Hey, I am making a framework involving movement and viewmodels. I finished the movement part of it but viewmodel is unfinished. No errors, just checking if I could have done anything better in this module / local script
Framework
-- Thanks to @SnarlyZoo for the original script (I just added mobile support and changed it into a module)
-- Variables
-- Settings
local HOLD_JUMP_TO_BHOP = false
local DEBOUNCE = false
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local TweenService = game:GetService("TweenService")
local Players = game:GetService("Players")
local RunService = game:GetService('RunService')
local UserInputService = game:GetService("UserInputService")
local LocalPlayer = Players.LocalPlayer
local CharacterConnections: {RBXScriptConnection} = {}
local PlayerConnections: {RBXScriptConnection} = {}
local PlayerVelocity = Vector3.zero
local InputVelocity = Vector3.zero
local Humanoid: Humanoid?
local WishJump = false
local CanJump = true
-- TODO: Movement.
LocalPlayer:SetAttribute("MOVE_SPEED",24)
LocalPlayer:SetAttribute("RUN_ACCELERATION",3.2)
LocalPlayer:SetAttribute("RUN_DEACCELERATION",0.2)
LocalPlayer:SetAttribute("AIR_ACCELERATION",6)
LocalPlayer:SetAttribute("AIR_DEACCELERATION",2)
LocalPlayer:SetAttribute("SIDE_STRAFE_ACCELERATION",100)
LocalPlayer:SetAttribute("SIDE_STRAFE_SPEED",1)
LocalPlayer:SetAttribute("AIR_FRICTION",1)
LocalPlayer:SetAttribute("MAX_SPEED",46)
LocalPlayer:SetAttribute("JUMP_BUFFER",0.4)
LocalPlayer:SetAttribute("FRICTION",3)
local MOVE_SPEED = LocalPlayer:GetAttribute("MOVE_SPEED")
local RUN_ACCELERATION = LocalPlayer:GetAttribute("RUN_ACCELERATION")
local RUN_DEACCELERATION = LocalPlayer:GetAttribute("RUN_DEACCELERATION")
local AIR_ACCELERATION = LocalPlayer:GetAttribute("AIR_ACCELERATION")
local AIR_DEACCELERATION = LocalPlayer:GetAttribute("AIR_DEACCELERATION")
local SIDE_STRAFE_ACCELERATION = LocalPlayer:GetAttribute("SIDE_STRAFE_ACCELERATION")
local SIDE_STRAFE_SPEED = LocalPlayer:GetAttribute("SIDE_STRAFE_SPEED")
local FRICTION = LocalPlayer:GetAttribute("FRICTION")
local AIR_FRICTION = LocalPlayer:GetAttribute("AIR_FRICTION")
local MAX_SPEED = LocalPlayer:GetAttribute("MAX_SPEED")
local JUMP_BUFFER = LocalPlayer:GetAttribute("JUMP_BUFFER")
-- MODULES
local Modules = ReplicatedStorage.Modules
local ExtraModules = Modules.Extra
local ExtraFunctions = require(ExtraModules.ExtraFunctions)
local PlayerScripts = ExtraFunctions:WaitForChildWhichIsA(LocalPlayer, "PlayerScripts") :: PlayerScripts
local PlayerModule = require(PlayerScripts:WaitForChild("PlayerModule")) :: any
local Controls = PlayerModule:GetControls()
local Framework = {}
local FH = { __index = Framework }
-- Functions
local function bobbing(addition, speed, modifier)
return math.sin(tick() * addition * speed) * modifier
end
local function ThumbstickCurve(X: number): number
local K_CURVATURE = 2.0
local K_DEADZONE = 0.15
local function FCurve(X: number): number
return (math.exp(K_CURVATURE * X) - 1) / (math.exp(K_CURVATURE) - 1)
end
local function FDeadzone(X: number): number
return FCurve((X - K_DEADZONE) / (1 - K_DEADZONE))
end
return math.sign(X) * math.clamp(FDeadzone(math.abs(X)), 0, 1)
end
local function OnCharacterAdded(Character: Model)
local NewHumanoid = ExtraFunctions:WaitForChildWhichIsA(Character, "Humanoid", 5) :: Humanoid
if NewHumanoid then
table.insert(CharacterConnections, NewHumanoid:GetPropertyChangedSignal("Jump"):Connect(function()
if not CanJump then
NewHumanoid.Jump = false
end
end))
Humanoid = NewHumanoid
end
end
local function OnCharacterRemoving(_: Model)
Humanoid = nil
if #(CharacterConnections) > 0 then
for _, Connection in ipairs(CharacterConnections) do
if Connection.Connected then
Connection:Disconnect()
end
end
table.clear(CharacterConnections)
end
end
local function QueueJump()
if Humanoid then
local IsJumping = if (Controls.activeController and Controls.activeController.enabled and Controls.humanoid)
then (if (Controls.activeController:GetIsJumping() or (Controls.touchJumpController and Controls.touchJumpController:GetIsJumping()))
then true
else false)
else false
if not Humanoid:GetStateEnabled(Enum.HumanoidStateType.Jumping) and IsJumping then
IsJumping = false
end
local IsMobile = UserInputService.TouchEnabled
if HOLD_JUMP_TO_BHOP then
WishJump = IsJumping
else
if IsJumping and not WishJump then
WishJump = true
if Controls.activeController then
Controls.activeController.isJumping = false
elseif Controls.touchJumpController then
Controls.touchJumpController.isJumping = false
end
end
if not IsJumping then
WishJump = false
end
end
end
end
local function ApplyFriction(Time: number, InAir: boolean)
local Vector = Vector3.new(PlayerVelocity.X, 0, PlayerVelocity.Z)
local Speed = Vector.Magnitude
local Control = if Speed < RUN_DEACCELERATION then RUN_DEACCELERATION else Speed
local NewFriction = if InAir then AIR_FRICTION else FRICTION
local Drop = Control * NewFriction * RunService.Heartbeat:Wait() * Time
local NewSpeed = Speed - Drop
if NewSpeed < 0 then
NewSpeed = 0
end
if Speed > 0 then
NewSpeed /= Speed
end
local X = PlayerVelocity.X * NewSpeed
local Z = PlayerVelocity.Z * NewSpeed
PlayerVelocity = Vector3.new(X, 0, Z)
end
local function Accelerate(WishDirection: Vector3, WishSpeed: number, Acceleration: number)
local Currentspeed = PlayerVelocity:Dot(WishDirection)
local AddSpeed = WishSpeed - Currentspeed
if AddSpeed > 0 then
local AccelerationSpeed = Acceleration * RunService.Heartbeat:Wait() * WishSpeed
if AccelerationSpeed > AddSpeed then
AccelerationSpeed = AddSpeed
end
local X = PlayerVelocity.X + AccelerationSpeed * WishDirection.X
local Z = PlayerVelocity.Z + AccelerationSpeed * WishDirection.Z
PlayerVelocity = Vector3.new(X, 0, Z)
end
end
local function GroundMove(RelativeToCamera: boolean)
if not WishJump and CanJump then
ApplyFriction(1, false)
else
ApplyFriction(0, false)
end
local CoordinateFrame = if workspace.CurrentCamera
then workspace.CurrentCamera.CFrame
else CFrame.new()
local WishDirection = CoordinateFrame:VectorToWorldSpace(InputVelocity)
WishDirection = if RelativeToCamera
then Vector3.new(WishDirection.X, 0, WishDirection.Z)
else InputVelocity
if WishDirection ~= Vector3.zero then
WishDirection = WishDirection.Unit
end
local WishSpeed = WishDirection.Magnitude
WishSpeed *= MOVE_SPEED
Accelerate(
WishDirection,
WishSpeed,
RUN_ACCELERATION
)
PlayerVelocity = Vector3.new(
PlayerVelocity.X,
0,
PlayerVelocity.Z
)
if Humanoid and WishJump and CanJump then
Humanoid:ChangeState(Enum.HumanoidStateType.Jumping)
WishJump = false
CanJump = false
task.spawn(function()
local Begin = tick()
while tick() - Begin < JUMP_BUFFER do
RunService.Heartbeat:Wait()
end
CanJump = true
end)
end
end
local function AirMove(RelativeToCamera: boolean)
if InputVelocity.Z ~= 0 then
ApplyFriction(1, true)
end
local CoordinateFrame = if workspace.CurrentCamera
then workspace.CurrentCamera.CFrame
else CFrame.new()
local WishDirection = CoordinateFrame:VectorToWorldSpace(InputVelocity)
WishDirection = if RelativeToCamera
then Vector3.new(WishDirection.X, 0, WishDirection.Z)
else InputVelocity
local WishSpeed = WishDirection.Magnitude
WishSpeed *= MOVE_SPEED
if WishDirection ~= Vector3.zero then
WishDirection = WishDirection.Unit
end
local Acceleration: number = if PlayerVelocity:Dot(WishDirection) < 0
then AIR_DEACCELERATION
else AIR_ACCELERATION
if InputVelocity.Z == 0 and InputVelocity.X ~= 0 then
if WishSpeed > SIDE_STRAFE_SPEED then
WishSpeed = SIDE_STRAFE_SPEED
end
Acceleration = SIDE_STRAFE_ACCELERATION
end
Accelerate(WishDirection, WishSpeed, Acceleration)
end
--local Directions = {
-- [Vector3.new(0, 0, 0)] = "Not Moving";
-- [Vector3.new(0, 0, -1)] = "Foward";
-- [Vector3.new(0, 0, 1)] = "Backward";
-- [Vector3.new(-1, 0, 0)] = "Left";
-- [Vector3.new(1, 0, 0)] = "Right";
-- [Vector3.new(-1, 0, -1)] = "FowardLeft";
-- [Vector3.new(1, 0, -1)] = "FowardRight";
-- [Vector3.new(-1, 0, 1)] = "BackwardLeft";
-- [Vector3.new(1, 0, 1)] = "BackwardRight";
--}
if LocalPlayer.Character then
task.spawn(OnCharacterAdded, LocalPlayer.Character)
end
table.insert(PlayerConnections, LocalPlayer.CharacterAdded:Connect(OnCharacterAdded))
table.insert(PlayerConnections, LocalPlayer.CharacterRemoving:Connect(OnCharacterRemoving))
-- Framework
function Framework.new()
local self = {}
self.LoadingAnimations = {}
self.LerpValues = {}
self.LerpValues.Equip = Instance.new("NumberValue")
self.LerpValues.Equip.Value = 1
self.SwayCFrame = CFrame.new()
self.SwayAmount = -0.4
self.LastCameraCFrame = CFrame.new()
return setmetatable(self, FH)
end
function Framework:Equip()
if LocalPlayer and LocalPlayer.Character then
self.Viewmodel = ReplicatedStorage.Viewmodel:Clone() -- viewmodel
self.Camera = workspace.CurrentCamera
self.Character = Players.LocalPlayer.Character
self.Viewmodel["Left Arm"].Color = self.Character.Head.Color -- change left arm color
self.Viewmodel["Right Arm"].Color = self.Character.Head.Color -- change right arm color
self.Viewmodel.Parent = workspace.Camera
self.Settings = require(self.Viewmodel.Settings) -- settings gathered
self.LoadingAnimations.Walk = self.Viewmodel.AnimationController:LoadAnimation(
self.Settings.Animations.Viewmodel.Walk
)
local EquipTween = TweenInfo.new(0.5, Enum.EasingStyle.Quart, Enum.EasingDirection.Out) -- tween equip
TweenService:Create(self.LerpValues.Equip, EquipTween, { Value = 0 }):Play()
self.Equipped = true
end
end
function Framework:PlayerRemoving(Player: Player)
if Player == LocalPlayer then
LocalPlayer = nil
if #(PlayerConnections) > 0 then
for _, Connection in ipairs(PlayerConnections) do
if Connection.Connected then
Connection:Disconnect()
end
end
table.clear(PlayerConnections)
end
if Player.Character then
OnCharacterRemoving(Player.Character)
end
end
end
function Framework:PlayerMoved(Player: Player, WalkDirection: Vector3, RelativeToCamera: boolean)
RelativeToCamera = if Controls.activeController
then Controls.activeController:IsMoveVectorCameraRelative()
else RelativeToCamera
if Player == LocalPlayer then
local MoveVector = Controls:GetMoveVector()
MoveVector = Vector3.new(
ThumbstickCurve(MoveVector.X),
ThumbstickCurve(MoveVector.Y),
ThumbstickCurve(MoveVector.Z)
)
if MoveVector ~= InputVelocity then
InputVelocity = MoveVector
end
if Humanoid then
QueueJump()
local Grounded = Humanoid.FloorMaterial ~= Enum.Material.Air or Humanoid:GetState() == Enum.HumanoidStateType.Climbing
if Grounded then
GroundMove(RelativeToCamera)
else
AirMove(RelativeToCamera)
end
local NewDirection = Vector3.zero
if PlayerVelocity ~= Vector3.zero then
NewDirection = PlayerVelocity.Unit
end
local NewSpeed = PlayerVelocity.Magnitude
NewSpeed = math.clamp(NewSpeed, 0, MAX_SPEED)
if Humanoid and math.ceil(InputVelocity.Z) ~= 1 then
Humanoid.WalkSpeed = NewSpeed
Humanoid:Move(NewDirection, false)
elseif Humanoid and math.ceil(InputVelocity.Z) == 1 then
Humanoid.WalkSpeed = NewSpeed / 2
Humanoid:Move(NewDirection, false)
end
end
end
end
function Framework:Update(DT)
if self.Viewmodel then
local rotation = self.Camera.CFrame:ToObjectSpace(self.LastCameraCFrame)
local x,y,z = rotation:ToOrientation()
self.SwayCFrame = self.SwayCFrame:Lerp(CFrame.Angles(math.sin(x) * self.SwayAmount, math.sin(y) * self.SwayAmount,0),0.1)
self.LastCameraCFrame = self.Camera.CFrame
if self.Character:FindFirstChild("Humanoid") then
local Humanoid = self.Character.Humanoid
--local MoveVector = Controls:GetMoveVector()
--MoveVector = Vector3.new(
-- ThumbstickCurve(MoveVector.X),
-- ThumbstickCurve(MoveVector.Y),
-- ThumbstickCurve(MoveVector.Z)
--)
--print(MoveVector)
local Speed = Humanoid.WalkSpeed
self.Viewmodel:SetPrimaryPartCFrame(self.Camera.CFrame * self.SwayCFrame)
--TODO: Animations depending on your speed.
if Speed >= 8 and Speed <= 15 then -- walking
if not self.LoadingAnimations.Walk.IsPlaying then
self.LoadingAnimations.Walk:AdjustWeight(10,5)
self.LoadingAnimations.Walk:Play()
end
end
if Speed >= 16 then -- sprinting
if self.LoadingAnimations.Walk.IsPlaying then
self.LoadingAnimations.Walk:Stop()
end
end
if Speed < 3 then -- stopped moving
if self.LoadingAnimations.Walk.IsPlaying then
self.LoadingAnimations.Walk:Stop()
end
end
-- TODO: add walk cycle to viewmodel
end
end
end
return Framework
Framework_Client
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")
local Players = game:GetService("Players")
local Modules = ReplicatedStorage.Modules
local Framework = require(Modules.Framework)
local ExtraFunctions = require(Modules.Extra.ExtraFunctions)
local LocalPlayer = Players.LocalPlayer
if not LocalPlayer then
Players:GetPropertyChangedSignal("LocalPlayer"):Wait()
LocalPlayer = Players.LocalPlayer
end
local PlayerScripts = ExtraFunctions:WaitForChildWhichIsA(LocalPlayer, "PlayerScripts") :: PlayerScripts
local PlayerModule = require(PlayerScripts:WaitForChild("PlayerModule")) :: any
local Controls = PlayerModule:GetControls()
LocalPlayer.CharacterAdded:Wait()
repeat wait() until LocalPlayer.Character
local Viewmodel = Framework.new()
Viewmodel:Equip()
local function Update(DeltaTime)
Viewmodel:Update(DeltaTime)
end
RunService.RenderStepped:Connect(Update)
Players.PlayerRemoving:Connect(Framework.PlayerRemoving)
Controls.moveFunction = Framework.PlayerMoved