壁走り、スライディング、崖登りなどの高速移動アクションは、現代の対戦ゲームで高い爽快感を生み出します。デフォルトのHumanoid状態だけで作ろうとすると、壁に引っかかったり減速したりするバグが多発します。
最新のLuau物理制約である `LinearVelocity` や `VectorForce`、そして方向性Raycastを組み合わせることで、PC・スマホ・家庭用ゲーム機すべてで快適に動く高品質な移動システムを構築できます。
1. パルクールのためのHumanoid状態のオーバーライド
カスタム移動ステートマシンの設計手法:
- 競合状態の無効化:壁走りの開始と同時に
Humanoid:SetStateEnabled(Enum.HumanoidStateType.Climbing, false)を呼び出し、落下アニメーションが物理挙動を上書きするのを防ぎます。 - ネットワーク所有権:操作遅延をゼロにするため、サーバー側で
rootPart:SetNetworkOwner(player)を設定して物理制御権をクライアントに委譲します。 - 状態遷移のモジュール化:歩行、ダッシュ、壁走り、崖ぶら下がりなどの状態を有限状態機械(FSM)として設計します。
2. 方向性Raycastと壁面法線の計算
壁面の検出と走行ベクトルの算出方法:
- 多重Raycastプローブ:腰と肩の位置から左右へ3.5スタッドのRayを発射し、走れる垂直な壁面を検知します。
- 法線ベクトルの検証:衝突面の法線角度が垂直(上向きベクトルに対して75°〜105°)であることを確認し、緩やかな斜面を除外します。
- 進行ベクトルの外積算出:壁面法線と上向きベクトルの外積(
wallNormal:Cross(Vector3.yAxis))により、壁に沿って前進するベクトルを求めます。
3. 最新物理制約:LinearVelocityとVectorForce
廃止されたBodyMoversに代わる現代的な制約活用:
- LinearVelocityによる等速推進:HumanoidRootPartに制約を追加し、十分な力で壁面摩擦に抗して一定速度を維持します。
- VectorForceによる反重力制御:ゲーム内重力(196.2 studs/s²)を相殺するため、キャラクターの質量に応じた上向きの力を加えます。
- 滞空時間の減衰:1.5〜2秒かけて上向きの力を徐々に弱めることで、自然な放物線を描いて落下させ、テクニカルなジャンプを要求します。
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)
段差の検知と滑らかなアニメーション連携:
- 二段階Raycast検知:胸の高さの前方Ray(壁の確認)と、上部から真下へのRay(崖上の正確な高さを測定)を組み合わせます。
- 位置スナップ固定:崖を掴んだ瞬間に
AlignPositionで位置を固定し、ぶら下がり状態へ遷移させます。 - ベジェ曲線よじ登り:ジャンプ入力時に、2次ベジェ曲線を使ってRootPartを崖上へ滑らかに持ち上げて前進させます。
5. モバイル対応とサーバー側の不正対策
操作性の向上とチート防止の両立:
- タッチUIバインド:壁ジャンプや登り入力を、画面上の押しやすいボタン(48x48px以上)に割り当てます。
- サーバー移動速度制限:フレーム間の移動距離をサーバー側で検証し、異常な瞬間移動や加速チートを遮断します。
- 壁存在の再検証:クライアントが壁走りを申告した際、実際にその位置に壁が存在していたかをサーバー側で再判定します。
Frequently Asked Questions
BodyVelocityではなくLinearVelocityを使うべき理由は何ですか?
BodyVelocityは非推奨の古いインスタンスです。LinearVelocityは最新の物理ソルバーと統合されており、質量計算が正確で安定しています。
壁走り中に下にずり落ちないようにするにはどうすればよいですか?
重力を打ち消す上向きの力をVectorForceなどで加えます。ゲーム性を高めるため、数秒後に上向きの力を減衰させて落下させます。
壁に沿って走るベクトルはどのように計算しますか?
Raycastで取得した壁面法線とワールド上向きベクトル(0, 1, 0)の外積を計算することで、壁面に平行な前進ベクトルを求めます。
壁走りを使ったチートをサーバー側で防ぐには?
サーバー側でRaycastを実行して実際に壁が存在するかを確認し、速度上限を超えていないかを常時チェックします。