How would I add a `gun sway` like feature to an NPC (a stand from an anime in my case)

Hello, so in this small project I’m working on I need to make a stand (checkout the anime Jojo’s Bizarre Adventure) which I’ve easily made, but when trying to implement a gun sway like feature to the stand I just can’t seem to get it working. By gun sway I mean the stand doesn’t update its CFrame instantly, but rather has a delay. For more realism I’d say.

I’ve tried Lerping, using tweens with TweenService, trigonometric functions sine/cosine etc.

If looking over the code helps then here’s the code in both Script and LocalScript. There are 4 RemoteEvents as well, all under ReplicatedStorage.

Script

local standModel = game:GetService('ReplicatedStorage'):WaitForChild('Stand')

local idleEvent = game:GetService('ReplicatedStorage'):WaitForChild('RE'):WaitForChild('MovementEvents'):WaitForChild('StandIdleEvent')
local walkEvent = game:GetService('ReplicatedStorage'):WaitForChild('RE'):WaitForChild('MovementEvents'):WaitForChild('StandWalkEvent')
local outEvent = game:GetService('ReplicatedStorage'):WaitForChild('RE'):WaitForChild('In/Out'):WaitForChild('StandOutEvent')
local inEvent = game:GetService('ReplicatedStorage'):WaitForChild('RE'):WaitForChild('In/Out'):WaitForChild('StandInEvent')

local ts = game:GetService('TweenService')

game:GetService('Players').PlayerAdded:Connect(function(plr)
	local char = plr.Character or plr.CharacterAdded:Wait()
	local stand = standModel:Clone()
	local primary = stand.PrimaryPart
	
	stand.Parent = workspace
	local weld = Instance.new('Weld')
	weld.Parent = char
	weld.Part0 = char:WaitForChild('HumanoidRootPart')
	weld.Part1 = stand:WaitForChild('HumanoidRootPart')
	
	local idleAnimation = script.Idle
	local walkAnimation = script.Walk
	local Idle = stand:WaitForChild('AnimationController'):LoadAnimation(idleAnimation)
	local Walk = stand:FindFirstChild('AnimationController'):LoadAnimation(walkAnimation)
	Idle.Priority = Enum.AnimationPriority.Idle
	Walk.Priority = Enum.AnimationPriority.Movement
	
	idleEvent.OnServerEvent:Connect(function(obj)
		Idle:Play()
		Walk:Stop()
	end)
	walkEvent.OnServerEvent:Connect(function(obj)
		Walk:Play()
		Idle:Stop()
	end)
	outEvent.OnServerEvent:Connect(function(obj)
		for _,v in pairs(stand:GetChildren()) do
			if v.ClassName == "Part" and v.Name ~= "HumanoidRootPart" then
				ts:Create(v, TweenInfo.new(.5,Enum.EasingStyle.Quad,Enum.EasingDirection.Out), {Transparency = 0}):Play()
			end
			if v.Name == "Head" then
				ts:Create(v:FindFirstChild('face'), TweenInfo.new(.5,Enum.EasingStyle.Quad,Enum.EasingDirection.Out), {Transparency = 0}):Play()
			end
		end
		ts:Create(weld,TweenInfo.new(.5,Enum.EasingStyle.Quad,Enum.EasingDirection.Out),{C0 = CFrame.new(2.69,1.8,4)}):Play()
	end)
	inEvent.OnServerEvent:Connect(function(obj)
		for _,v in pairs(stand:GetChildren()) do
			if v.ClassName == "Part" and v.Name ~= "HumanoidRootPart" then
				ts:Create(v, TweenInfo.new(.5,Enum.EasingStyle.Quad,Enum.EasingDirection.Out), {Transparency = 1}):Play()
			end
			if v.Name == "Head" then
				ts:Create(v:FindFirstChild('face'), TweenInfo.new(.5,Enum.EasingStyle.Quad,Enum.EasingDirection.Out), {Transparency = 1}):Play()
			end
		end
		ts:Create(weld,TweenInfo.new(.5,Enum.EasingStyle.Quad,Enum.EasingDirection.Out),{C0 = CFrame.new(0,0,0)}):Play()
	end)
end)

LocalScript

local uis = game:GetService('UserInputService')

local idleEvent = game:GetService('ReplicatedStorage'):WaitForChild('RE'):WaitForChild('MovementEvents'):WaitForChild('StandIdleEvent')
local walkEvent = game:GetService('ReplicatedStorage'):WaitForChild('RE'):WaitForChild('MovementEvents'):WaitForChild('StandWalkEvent')
local outEvent = game:GetService('ReplicatedStorage'):WaitForChild('RE'):WaitForChild('In/Out'):WaitForChild('StandOutEvent')
local inEvent = game:GetService('ReplicatedStorage'):WaitForChild('RE'):WaitForChild('In/Out'):WaitForChild('StandInEvent')

local c = 0
local debounce = false

local plr = game:GetService('Players').LocalPlayer
local char = plr.Character or plr.CharacterAdded:Wait()

uis.InputBegan:Connect(function(input)
	if input.KeyCode == Enum.KeyCode.Q and debounce == false and c == 0 then
		debounce = true
		c = c + 1
		outEvent:FireServer()
		wait(2.5)
		debounce = false
	elseif input.KeyCode == Enum.KeyCode.Q and debounce == false and c == 1 then
		debounce = true
		c = 0
		inEvent:FireServer()
		wait(2.5)
		debounce = false
	end
end)

local a = 0

game:GetService('RunService').RenderStepped:Connect(function()
	if char.Humanoid.MoveDirection.Magnitude == 0 and a == 0 then
		a = 1
		idleEvent:FireServer()
	elseif char.Humanoid.MoveDirection.Magnitude ~= 0 and a ~= 0 then
		a = 0
		walkEvent:FireServer()
	end
end)
1 Like