我的视角模型本地脚本在移动躯干时会让手臂抖动

后端开发 2026-07-11

当我以高速移动躯干时,手臂会抖动,理想的状态应该是手臂能立即旋转。据我所知,这只会在Z 轴和X 轴方向发生。

展示问题的媒体:

问题图片 1

问题图片 2

https://files.catbox.moe/402frc.mp4

现在我在视图模型中使用真实的手臂作为参考,我猜这是因为左右肩膀跟着它,但它应该正常跟随视图模型,完全没有抖动。

视图模型模块:

    ------------------------------------Settings\/


local invertedSway = false -- true means sway will smooth behind and false means it will predict
local thirdpersonArms = true -- should arms follow camera angle in thrid person
local swaySensitivity = 1 -- higher number = more sway
local UseOffset = true -- if true then it will use the torso orientation for viewbobbing instead of the preset positions
local offsetDiv = 2 -- how much the offset is divided by if useoffset is true

-- walk bob if useoffset is false
local period    = 2


local omega     = 5
local amplitude = .2   -- studs



------------------------------------Settings/\
--------------------------------------- Code\/
local RunService = game:GetService("RunService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ReplicatedFirst = game:GetService("ReplicatedFirst")

local ToolFramework = ReplicatedStorage:FindFirstChild("ToolFramework")

local Modules = ReplicatedStorage:WaitForChild("Modules")
local Misc = ReplicatedStorage.Miscs
local ViewmodelRigs = Misc.ViewmodelRigs
local viewmodelr6 = ViewmodelRigs.ViewModel


local ToolDataServer = require(ToolFramework:FindFirstChild("Registry"))
local ToolSettingsDefault = ToolDataServer.Ranged["ToolSettings"]

local Players = game:GetService("Players")
local uis = game:GetService("UserInputService")
local spring = require(script.spring)

local ViewModel = {}
ViewModel.__index = ViewModel

-- Constructor
function ViewModel.new(boundKey,offsetobject : Vector3Value, rotationobject : Vector3Value)
    local self = setmetatable({}, ViewModel)

    self.Seam = require(Modules.Seam)
    self.Scope = self.Seam.Scope(self.Seam)
    self.HoldingAim = self.Scope:Value(false)

    self.player       = Players.LocalPlayer
    self.camera       = workspace.CurrentCamera
    self.boundKey     = boundKey or "LeftControl"
    self.renderConn   = nil
    self.charConn     = nil
    self.dieConn      = nil
    self.sway = spring.create()
    self.offset = Instance.new("Vector3Value",self.camera)
    self.offset.Name = "Offset"

    self.rot_offset = Instance.new("Vector3Value",self.camera)
    self.rot_offset.Name = "Rot_Offset"

    -- build the invisible head+torso model
    --self.model        = Instance.new("Model")
    --self.model.Name   = "ViewModel"
    --self.model.Parent = self.camera
    self.humanoid = nil 
    self.characterRemoved = nil

    self.model        = viewmodelr6:Clone()
    self.model.Name   = "ViewModel"
    self.model.Parent = self.camera

    self.ViewmodelAimingVectorDefault = Vector3.zero
    self.ViewmodelNoAimingVectorDefault = Vector3.zero

    self.TargetAimingVector = nil   

    self.TargetAimingVector = self.Scope:Computed(function(Use) -- The speed based on whether we are sprinting
        if Use(self.HoldingAim) then
            return self.ViewmodelAimingVectorDefault
        else
            return self.ViewmodelNoAimingVectorDefault
        end
    end)    

    self.Scope:New(self.offset, {
        Value = self.Scope:Spring(self.TargetAimingVector, 24, 0.9) -- We use a spring so that it's not a sudden change
    })

    self:_createParts()
    return self
end

-- create head, torso, and weld
function ViewModel:_createParts()

    local head = self.model.Head
    local torso = self.model.Torso
    local weld = Instance.new("Weld")

    --local head  = Instance.new("Part")
    --local torso = Instance.new("Part")
    --local weld  = Instance.new("Weld")


    --[[
    head.Name               = "Head"
    head.Size               = Vector3.new(2, 1, 1)
    head.Anchored           = true
    head.Transparency       = 1
    head.CanCollide         = false
    head.CanQuery           = false
    head.Parent             = self.model
    head.CollisionGroup = "CannotCollide"

    torso.Name              = "Torso"
    torso.Size              = Vector3.new(2, 2, 1)
    torso.Transparency      = 1
    torso.CanCollide        = false
    torso.CanQuery          = false
    torso.Parent            = self.model
    torso.CollisionGroup = "CannotCollide"
    --]]
    weld.Part0              = head
    weld.Part1              = torso
    weld.C0                 = CFrame.new(0, -1.5, 0)
    weld.Parent             = head

    self.headPart  = head
    self.torsoPart = torso
    self.model.PrimaryPart = head
end

-- start listening for character and render steps
function ViewModel:Init()
    -- set the bound key for MouseLockController
    self.player.PlayerScripts:WaitForChild("PlayerModule")
        :WaitForChild("CameraModule")
        :WaitForChild("MouseLockController")
        :WaitForChild("BoundKeys").Value = self.boundKey

    -- connect CharacterAdded
    if self.player.Character:FindFirstChildWhichIsA("Humanoid") then
        self.humanoid = self.player.Character:FindFirstChildWhichIsA("Humanoid")
    end

    self.charConn = self.player.CharacterAdded:Connect(function(char)
        self:_onCharacter(char)
    end)

    self.dieConn = self.humanoid.Died:Connect(function()
        self:Destroy()
    end)

    self.characterRemoved = self.player.CharacterRemoving:Connect(function()
        self:Destroy()
    end)

    -- if character already loaded
    if self.player.Character then
        self:_onCharacter(self.player.Character)
    end
end

-- Handles character spawn and viewmodel updates
function ViewModel:_onCharacter(char)
    -- Disconnect previous RenderStepped connection
    if self.renderConn then
        self.renderConn:Disconnect()
    end

    -- References
    local animator: Animator = char:FindFirstChildWhichIsA("Animator") 
        or char:FindFirstChild("Humanoid") and char.Humanoid:FindFirstChildWhichIsA("Animator")
    local torso         = char:WaitForChild("Torso")
    local leftShoulder  = torso:WaitForChild("Left Shoulder")
    local rightShoulder = torso:WaitForChild("Right Shoulder")
    local leftArm       = char:WaitForChild("Left Arm")
    local rightArm      = char:WaitForChild("Right Arm")


    leftShoulder.Part0  = self.torsoPart
    rightShoulder.Part0 = self.torsoPart


    -- Bobbing and sway variables
    local bobbingVector = Vector3.new()
    local velocity = 0
    local mega = omega * math.pi / period

    -- Connect to RenderStepped
    RunService:BindToRenderStep("viewmodel", Enum.RenderPriority.Camera + 1, function(dt)
        local cam = self.camera
        local firstPerson = (cam.CFrame.Position - cam.Focus.Position).Magnitude < 0.75
        local t = tick()

        -- Smooth velocity tracking
        velocity = math.lerp(velocity, torso.Velocity.Magnitude, 0.1)

        -- Bobbing calculation
        local speedmod = velocity / 16 > 1.5 and 2 or 1
        local Bobx = -math.cos(t * mega * speedmod)
        local Boby = 1 - (Bobx * Bobx)

        Bobx = -Bobx * amplitude * (velocity / 16)
        Boby = -Boby * amplitude * (velocity / 16)

        -- Optional rotation offset based on torso vs root orientation
        local ROTOFFSET = CFrame.Angles(0, 0, 0)
        if UseOffset then
            local relative = torso.CFrame:ToObjectSpace(char.HumanoidRootPart.CFrame)
            local rotX, rotY, rotZ = relative:ToOrientation()
            ROTOFFSET = CFrame.Angles(
                -rotX / offsetDiv,
                -rotY / offsetDiv, 0)

        else
            bobbingVector = bobbingVector:Lerp(Vector3.new(Bobx, Boby, 0), 0.2)
        end

        -- Reparent shoulders to viewmodel torso
        --leftShoulder.Part0  = self.torsoPart
        --rightShoulder.Part0 = self.torsoPart

        -- Ensure arms are visible
        leftArm.LocalTransparencyModifier  = 0
        rightArm.LocalTransparencyModifier = 0

        -- Mouse sway
        local md = uis:GetMouseDelta()
        self.sway:shove(Vector3.new(md.Y, md.X))
        local sway = self.sway:update(dt)
        if not invertedSway then
            sway = -sway
        end

        -- First-person viewmodel positioning
        if firstPerson then
            self.model.PrimaryPart = self.headPart

            local camcf = cam.CFrame
            camcf += camcf.RightVector * self.offset.Value.X
            camcf += camcf.UpVector * self.offset.Value.Y
            camcf += camcf.LookVector * self.offset.Value.Z

            camcf *= CFrame.Angles(
                math.rad(bobbingVector.Y * 15),
                math.rad(bobbingVector.X * 15),
                0
            ) * ROTOFFSET * CFrame.Angles(

                math.rad(self.rot_offset.Value.X),
                math.rad(self.rot_offset.Value.Y),
                math.rad(self.rot_offset.Value.Z))

            camcf += camcf.LookVector * 4
            camcf *= CFrame.Angles(
                math.rad(sway.X / (25 / swaySensitivity)),
                math.rad(sway.Y / (25 / swaySensitivity)),
                0
            )

            self.model:PivotTo(camcf - camcf.LookVector * 4)

            -- Third-person fallback
        else
            self.model.PrimaryPart = self.torsoPart
            local cf = torso.CFrame + Vector3.new(0, 0.5, 0)

            if char:FindFirstChildOfClass("Tool") and thirdpersonArms then
                local x = select(1, cam.CFrame:ToOrientation())
                cf *= CFrame.fromOrientation(x, 0, 0)
            end

            cf -= cf.UpVector * 0.5
            self.model:PivotTo(cf)
        end
    end)
    self.renderConn = RunService.RenderStepped:Connect(function(dt)

    end)
end


-- disconnect and clean up
function ViewModel:Destroy()

    if self.charConn then
        self.charConn:Disconnect()
        self.charConn = nil
    end
    if self.renderConn then
        self.renderConn:Disconnect()
        self.renderConn = nil
    end
    if self.model then
        self.model:Destroy()
        self.model = nil
    end
    if self.offset then
        self.offset:Destroy()
        self.offset = nil
    end
    if self.rot_offset then
        self.rot_offset:Destroy()
        self.rot_offset = nil
    end
    RunService:UnbindFromRenderStep("viewmodel")
    self.TargetAimingVector = nil   
end

return ViewModel

解决方案

我也不知道自己在做什么,但这可能能解决你的问题。基本上把整个 _onCharacter 函数替换为这个

function ViewModel:_onCharacter(char)
    if self.renderConn then self.renderConn:Disconnect() end

    local torso = char:WaitForChild("Torso")
    local root = char:WaitForChild("HumanoidRootPart")
    local leftShoulder = torso:WaitForChild("Left Shoulder")
    local rightShoulder = torso:WaitForChild("Right Shoulder")
    local leftArm = char:WaitForChild("Left Arm")
    local rightArm = char:WaitForChild("Right Arm")

    leftShoulder.Part0 = self.torsoPart
    rightShoulder.Part0 = self.torsoPart

    local bobbingVector = Vector3.zero
    local mega = omega * math.pi / period

    RunService:BindToRenderStep("viewmodel", Enum.RenderPriority.Camera.Value + 1, function(dt)
        local cam = self.camera
        local firstPerson = (cam.CFrame.Position - cam.Focus.Position).Magnitude < 0.75
        local t = tick()

        local currentVelocity = torso.AssemblyLinearVelocity.Magnitude
        local speedmod = currentVelocity / 16 > 1.5 and 2 or 1

        local Bobx = -math.cos(t * mega * speedmod)
        local Boby = 1 - (Bobx * Bobx)

        Bobx = -Bobx * amplitude * (currentVelocity / 16)
        Boby = -Boby * amplitude * (currentVelocity / 16)

        local ROTOFFSET = CFrame.identity
        if UseOffset then
            local relative = torso.CFrame:ToObjectSpace(root.CFrame)
            local rotX, rotY, rotZ = relative:ToOrientation()
            ROTOFFSET = CFrame.Angles(-rotX / offsetDiv, -rotY / offsetDiv, 0)
        else
            bobbingVector = bobbingVector:Lerp(Vector3.new(Bobx, Boby, 0), 0.2)
        end

        leftArm.LocalTransparencyModifier = 0
        rightArm.LocalTransparencyModifier = 0

        local md = uis:GetMouseDelta()
        self.sway:shove(Vector3.new(md.Y, md.X))
        local sway = self.sway:update(dt)
        if not invertedSway then sway = -sway end

        if firstPerson then
            self.model.PrimaryPart = self.headPart
            local camcf = cam.CFrame

            camcf *= CFrame.new(self.offset.Value)
            camcf *= CFrame.Angles(
                math.rad(bobbingVector.Y * 15),
                math.rad(bobbingVector.X * 15),
                0
            ) * ROTOFFSET * CFrame.Angles(
                math.rad(self.rot_offset.Value.X),
                math.rad(self.rot_offset.Value.Y),
                math.rad(self.rot_offset.Value.Z)
            )

            local finalSway = CFrame.Angles(
                math.rad(sway.X / (25 / swaySensitivity)),
                math.rad(sway.Y / (25 / swaySensitivity)),
                0
            )

            self.model:PivotTo(camcf * finalSway)
        else
            self.model.PrimaryPart = self.torsoPart
            local cf = torso.CFrame
            if char:FindFirstChildOfClass("Tool") and thirdpersonArms then
                local x = select(1, cam.CFrame:ToOrientation())
                cf *= CFrame.fromOrientation(x, 0, 0)
            end
            self.model:PivotTo(cf)
        end
    end)
end

所以先从现在开始,我们改用 AssemblyLinearVelocity。主要是因为 Velocity 已被弃用。我还通过执行 CFrame.new(offset.Value) 来简化了CFrame的计算,代替 RightVector * Offset。另外,velocity 的lerp可能在快速转向或加速时导致你的手臂在躯干后方“漂移”,所以我把它去除了。这里还有一些其他的小改动,但不过是让你的代码多加了一点脑力的小改动。

希望这个系统对你的游戏要稳定得多 :]

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章