蹬墙跑、滑铲与攀爬翻越是现代竞技跑酷游戏的核心魅力。如果仅依赖默认 Humanoid 状态机,往往会导致角色卡在墙壁表面或出现动量丢失等严重物理毛病。
通过结合最新 Luau 物理约束(`LinearVelocity` 与 `VectorForce`)与方向性射线检测(Raycast),开发者能够构建出兼顾 PC、移动端与主机的高水准物理移动手感。
1. 重写默认 Humanoid 状态以实现流畅跑酷
自定义移动状态机的架构规范:
- 禁用冲突状态:进入蹬墙跑时立即调用
Humanoid:SetStateEnabled(Enum.HumanoidStateType.Climbing, false)并禁用自由落体,防止默认下落动作打断物理运算。 - 网络所有权确认:服务端必须对角色执行
rootPart:SetNetworkOwner(player),确保客户端拥有完全的物理计算权以消除操作延迟。 - 有限状态机(FSM):将移动解耦为行走、冲刺、蹬墙跑、悬挂和翻越,并定义清晰的进出回调函数。
2. 方向性射线检测与墙面法线计算
检测可跑墙面与推进方向的几何数学:
- 多重射线探针:从角色臀部和肩部向左右两侧各发射长约 3.5 螺栓的水平射线,精准检测墙面。
- 法线垂直度校验:提取碰撞法线向量,确保墙面坡度接近垂直(与世界向上向量夹角在 75° 到 105° 之间),过滤掉斜坡。
- 切线推进向量:通过法线与向上向量的外积计算(
wallNormal:Cross(Vector3.yAxis)),得出紧贴墙面行进的矢量。
3. 现代物理约束:LinearVelocity 与 VectorForce
淘汰过时的 BodyMovers 并转向现代物理架构:
- LinearVelocity 恒速推进:挂载至 HumanoidRootPart,提供充足的推力克服侧向摩擦并保持恒定前冲速度。
- VectorForce 抗重力悬浮:根据角色总质量计算向上推力,抵消世界基础重力加速度(196.2 studs/s²)。
- 动态衰减机制:在 1.5 至 2.5 秒内逐渐衰减向上的悬浮力,形成自然的抛物线下滑,提升跑酷操作门槛。
ReplicatedStorage.Controllers.WallRunController
-- 基于物理约束的客户端蹬墙跑控制器
local RunService = game:GetService("RunService")
local WallRun = {}
WallRun.__index = WallRun
function WallRun.new(character)
local self = setmetatable({}, WallRun)
self.Char = character
self.RootPart = character:WaitForChild("HumanoidRootPart")
self.Humanoid = character:WaitForChild("Humanoid")
local att = Instance.new("Attachment", self.RootPart)
local lv = Instance.new("LinearVelocity")
lv.Attachment0 = att
lv.MaxForce = 15000
lv.RelativeTo = Enum.ActuatorRelativeTo.World
lv.Enabled = false
lv.Parent = self.RootPart
self.Attachment = att
self.LinearVelocity = lv
self.IsRunning = false
return self
end
function WallRun:CheckWall(side)
local rightVec = self.RootPart.CFrame.RightVector
local rayDir = (side == "Left" and -rightVec or rightVec) * 3.5
local params = RaycastParams.new()
params.FilterDescendantsInstances = {self.Char}
params.FilterType = Enum.RaycastFilterType.Exclude
return workspace:Raycast(self.RootPart.Position, rayDir, params)
end
function WallRun:Start(hitNormal, forwardDir)
self.IsRunning = true
self.LinearVelocity.VectorVelocity = forwardDir * 32 + Vector3.new(0, 4, 0)
self.LinearVelocity.Enabled = true
self.Humanoid:ChangeState(Enum.HumanoidStateType.Physics)
end
function WallRun:Stop()
self.IsRunning = false
self.LinearVelocity.Enabled = false
self.Humanoid:ChangeState(Enum.HumanoidStateType.Freefall)
end
return WallRun
4. 程序化悬崖抓取与翻越(Mantle)
精准检测边缘并平滑衔接攀爬动画:
- 双层射线检测:胸部水平射线确认前方墙面,配合上方下向下射的垂直射线计算出台阶顶部的精准边缘。
- 位置吸附固定:抓住边缘瞬间启用
AlignPosition将角色牢牢吸附在边缘进入悬挂状态。 - 贝塞尔曲线翻越:玩家按下跳跃键时,播放攀登动作并使用二次贝塞尔曲线平滑抬升角色翻越至平台上。
5. 移动端触控优化与服务端防作弊
兼顾跨平台可用性与服务器安全防护:
- 触控按键映射:使用 ContextActionService 将蹬墙跳与攀爬映射至屏幕显眼的大尺寸按钮(至少 48x48 像素)。
- 服务端速度截断:根据网络复制 Tick 计算位移差,严厉限制非蹬墙状态下的超常移动速度。
- 物理墙面核验:当客户端上报蹬墙状态时,服务端需发射射线核实玩家身旁 4 螺栓内是否真实存在有效实体墙。
Frequently Asked Questions
为什么应当使用 LinearVelocity 而非传统的 BodyVelocity?
BodyVelocity 已被官方废弃。LinearVelocity 采用全新约束求解器,质量计算严密,在多平台多人网络环境下表现更加稳定。
如何防止角色在蹬墙跑过程中沿墙面下滑?
施加向上的 VectorForce 或在 LinearVelocity 的 Y 轴设定向上推力以抵消世界重力(质量 * 196.2),并通过倒计时平滑减小推力。
如何精确计算贴墙前进的动力向量?
使用 Raycast 返回的碰撞表面法线与世界向上向量 (0, 1, 0) 进行叉乘运算,即可得出与墙面完全平行的水平前冲向量。
服务端如何防范利用蹬墙机制实现的飞行外挂?
服务端独立发射射线核查玩家当前位置身侧是否存在真实墙体,并严格校验位移距离,超出速度阈值直接执行位置回滚。