Going to keep it short and sweet: working on a First Person Weapon System, and I’m attempting to get an Aim Down Sight working using CFrame:Lerp to go to a new position.
This isn’t working, however, because I’m constantly setting the CFrame of the PrimaryPrimary part of my Viewmodel to the camera’s CFrame. I have this code so far and I don’t have any idea on what to do.
RunService.RenderStepped:Connect(function(deltaTime)
this.DeltaTime = deltaTime
if this.Viewmodel then
-- viewmodel initialized, render stepped
if Character.Head.LocalTransparencyModifier == 1 and this.Weapon then
this.WeaponEnabled = true
this.Weapon.Parent = this.Camera
local CF = this.Camera.CFrame * CFrame.new(0.025, -0.25, -1.3)
-- Return updated CFrame with config
CF = this:ReturnCFrameFromConfiguration(CF)
if not this.ADS then
-- if ADS (bool) == false, if ADS is true that means we're holding right click and attempting to "ADS"
-- In this situation, we're not, so we can simply not do anything
this.ADS_Anim_Finished = false
this.Weapon:SetPrimaryPartCFrame(CF)
else
-- We're attempting to ADS, Lerp/Tween position to ADS Position
if this.ADS_Position then
-- If the ADS Position actually exists yet, we can go
if this.ADS_Anim_Finished == false then
-- insert animation
end
this.ADS_Anim_Finished = true
this.Weapon.PrimaryPart.CFrame = CF * this.ADS_Position
end
end
else
-- We're zoomed out or the weapon does not exist
if this.Viewmodel then
-- Viewmodel exists?
this.WeaponEnabled = false
this.Weapon.Parent = game.ReplicatedStorage
this.Weapon:SetPrimaryPartCFrame(this.Camera.CFrame * CFrame.new(0.025, -0.25, 1))
-- Pretty much "disable" the Viewmodel and put it in ReplicatedStorage
end
end
end
end)
I’ve used the Devforum to try and look for answers, Tweenservice, CFrame:Lerp, etc. It right now seems that the only way to get this working is by usin animations.
You generally want any effects on the viewmodel to affect it relative to the camera.
That means you can’t use TweenService on the view model CFrame because it sets the CFrame in world space, i.e. relative to the world i.e. not relative to the camera.
That also means that to interpolate some effect on the view model CFrame cannot directly interpolate it’s CFrame.
The solution is to work with an offset relative to the camera CFrame instead of directly working with the view model CFrame in world space. It can look like this:
--This CFrame is relative to the camera ("camera space")
local viewModelOffset = CFrame.new()
function updateViewModelCFrame(dt)
--This CFrame is relative to the world ("world space")
local viewModelCFrame = camera.CFrame * viewModelOffset
viewModel:PivotTo(viewModelCFrame)
end
RunS.RenderStepped:Connect(function(dt)
updateViewModelCFrame(dt)
end)
Now you’re free to animate viewModelOffset however you want, without worrying about the view model moving with the camera since that’s being handled. An example of how effects could be applied:
local t --The current time. Updated first thing every RenderStepped.
--Some basic examples of how this system allows several independent-ish animations to be applied on top of setting the viewmodel CFrame to the camera CFrame
local viewModelOffsets = {
base = CFrame.new(),
walkBobbing = CFrame.new(),
recoil = CFrame.new()
}
function applyRecoil() --Call when the gun is fired
viewModelOffset.recoil *= --Don't overwrite the old recoil
CFrame.new(0, 0.1, 0.1) * CFrame.Angles(0.1, 0, 0)
end
function animateRecoil(dt)
if not isReloading then
--Recoil recovery
viewModelOffsets.recoil =
viewModelOffset:Lerp(CFrame.new(), math.min(1, dt * 5))
end
end
function animateWalkBobbing(dt)
if isWalking and not isReloading then
viewModelOffsets.walkBobbing =
CFrame.Angles(0, math.sin(t*2)/10, 0) * CFrame.new(0, math.sin(t), 0)
else
viewModelOffsets.walkBobbing = CFrame.new()
end
end
function animateViewModel(dt)
animateRecoil(dt)
animateWalkBobbing(dt)
end
function updateViewModelCFrame(dt)
--This CFrame is relative to the camera ("camera space")
local viewModelOffset = multiply(viewModelOffsets)
--This CFrame is relative to the world ("world space")
local viewModelCFrame = camera.CFrame * viewModelOffset
viewModel:PivotTo(viewModelCFrame)
end
RunS.RenderStepped:Connect(function(dt)
t = tick()
animateViewModel(dt)
updateViewModelCFrame(dt)
end)
--Helper functions that I use all over my code, so I usually put them in a module so I can use them
function multiply(items)
return reduce(
items,
function(partialProduct, value) return partialProduct * value end
)
end
function reduce(items, reducer)
local result
for _, item in pairs(items) do
if not result then
result = item
continue
end
result = reducer(result, item)
end
return result
end
I understand that, and I’ve got that working properly, though how would I go about making a working animation that goes from Point A to Point B using Lerp? I’ve tried several ways though using RenderStepped is a pain with animating the Camera’s offset.
local playingAnimations = {}
function animateReloadAnimationTrack(animationTrack)
local progress = (t - animationTrack.startTick) / 2 --length of the animation
progress = math.min(1, progress)
viewModelOffsets.reload = RELOAD_TWEEN_POINT_1:Lerp(RELOAD_TWEEN_POINT_2, progress)
if progress == 1 then
table.remove(playingAnimations, table.find(playingAnimations, animationTrack))
end
end
function playReloadAnimation()
local animationTrack = {startTick = t, animate = animateReloadAnimationTrack}
table.insert(playingAnimations, animationTrack)
animationTrack:animate(0) --Start animating the same frame it's played
end
function animateAnimations(dt) --Probably shouldn't use the verb animate like this xD
for _, animationTrack in ipairs(playingAnimations) do
animationTrack:animate(dt)
end
end
function animateViewModel(dt)
animateRecoil(dt)
animateWalkBobbing(dt)
animateAnimations(dt)
end
I figured I’d use a completely new way of animating to animate the ADS, but I’m getting another issue where the animation gets a little wonky.
this.i = 10 * this.ADS_Speed -- 50
-- initialize the iterating temp variable
RunService.RenderStepped:Connect(function(deltaTime)
this.DeltaTime = deltaTime
if this.Viewmodel then
-- viewmodel initialized, render stepped
if Character.Head.LocalTransparencyModifier == 1 and this.Weapon then
this.WeaponEnabled = true
this.Weapon.Parent = this.Camera
local CF = this.Camera.CFrame * CFrame.new(0.025, -0.25, -1.3)
-- Return updated CFrame with config
CF = this:ReturnCFrameFromConfiguration(CF)
local x
local y
local z
-- x, y, z vectors
if this.ADS == true then
-- ads is on (user right-clicked)
if this.i > 1 then
-- if the iterating variable is greater than 1, subtract 1
this.i -= 1
x = this.ADS_Position.X / this.i
y = this.ADS_Position.Y / this.i
z = this.ADS_Position.Z / this.i
-- set x, y, and z vectors to the positions / the iterating variable
else
x = this.ADS_Position.X
y = this.ADS_Position.Y
z = this.ADS_Position.Z
-- last index, set it to its normal stationary position
end
else
x = 0
y = 0
z = 0
this.i = 10
-- ads not on (user stopped right-clicking), set everything back to zero
end
this.Weapon:SetPrimaryPartCFrame(CF * CFrame.new(Vector3.new(x, y, z)))
else
-- We're zoomed out or the weapon does not exist
if this.Viewmodel then
-- Viewmodel exists?
this.WeaponEnabled = false
this.Weapon.Parent = game.ReplicatedStorage
this.Weapon:SetPrimaryPartCFrame(this.Camera.CFrame * CFrame.new(0.025, -0.25, 1))
-- Pretty much "disable" the Viewmodel and put it in ReplicatedStorage
end
end
end
end)
As you can see, it gives me an exponential growth in speed instead of a constant speed.
The only other way I can think of is making an invisible part that would move to the specific position and then the Viewmodel would move to that part’s CFrame, but that’s messy and I’d rather not do that.