f8b21af4be
10_Wiki/Topics 대규모 정리: - 오류 캡처/미완성 stub 문서 227개 제거 - 교차폴더 중복 43클러스터 병합 (63파일 → redirect) - 링크명 정규화: 깨진 링크 수정·redirect 직결·개념 매핑 ~2,400건 - 카테고리 MOC 6개 신규 생성 - Graph 섹션 미해결 related-keyword 링크 10,058건 제거 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
198 lines
6.9 KiB
Markdown
198 lines
6.9 KiB
Markdown
---
|
|
id: wiki-2026-0508-이동-속도-movement-speed
|
|
title: 이동 속도 (Movement Speed)
|
|
category: 10_Wiki/Topics
|
|
status: verified
|
|
canonical_id: self
|
|
aliases: [Movement Speed, Locomotion Tuning, Avatar Speed]
|
|
duplicate_of: none
|
|
source_trust_level: B
|
|
confidence_score: 0.85
|
|
verification_status: applied
|
|
tags: [game-architecture, simulation, physics, ux]
|
|
raw_sources: []
|
|
last_reinforced: 2026-05-10
|
|
github_commit: pending
|
|
tech_stack:
|
|
language: csharp
|
|
framework: unity
|
|
---
|
|
|
|
# 이동 속도 (Movement Speed)
|
|
|
|
## 매 한 줄
|
|
> **"매 속도는 단순 숫자가 아니라 시스템"**. 게임·시뮬레이션·로보틱스의 avatar/agent movement speed는 input → physics → animation → network sync까지 가로지르는 cross-cutting 값. 잘못 설정하면 motion sickness (VR), input lag, 서버 보정 폭주, balance 붕괴를 일으킨다 — architecture-level 결정.
|
|
|
|
## 매 핵심
|
|
|
|
### 매 layered model
|
|
- **Logical speed** (units/s): 게임 규칙·서버 검증의 단위.
|
|
- **Physics speed**: 충돌·중력 step과 통합되는 값 (rigidbody.velocity).
|
|
- **Animation speed**: 발 미끄러짐(foot sliding) 방지 위해 logical에 동기화.
|
|
- **Camera/feel speed**: 체감용 오프셋 (FOV, head bob).
|
|
|
|
### 매 핵심 트레이드오프
|
|
- **빠를수록 즐거움 ↑** (Doom-feel) but **레벨 design density ↓** (필요 공간 ↑).
|
|
- **Network sync 비용**: 빠른 client = 더 자주 보정. lag compensation 부담 ↑.
|
|
- **VR/모션**: 1.4 m/s (보행)을 넘으면 vection-induced motion sickness ↑.
|
|
|
|
### 매 응용
|
|
1. **FPS**: walk/run/sprint/crouch 4-tier + lateral penalty.
|
|
2. **MMO**: rubber-banding 방지를 위한 server reconciliation.
|
|
3. **VR**: teleport 또는 snap-turn 옵션, continuous는 vignette + 저속 default.
|
|
4. **자율주행 시뮬레이션**: scenario별 speed parameterization.
|
|
|
|
## 💻 패턴
|
|
|
|
### Unity character controller — 4-tier movement
|
|
```csharp
|
|
public sealed class PlayerLocomotion : MonoBehaviour {
|
|
[SerializeField] float walkSpeed = 2.5f;
|
|
[SerializeField] float runSpeed = 5.0f;
|
|
[SerializeField] float sprintSpeed = 7.5f;
|
|
[SerializeField] float lateralPenalty = 0.85f;
|
|
[SerializeField] float acceleration = 30f;
|
|
|
|
CharacterController cc;
|
|
Vector3 velocity;
|
|
|
|
void Awake() => cc = GetComponent<CharacterController>();
|
|
|
|
void Update() {
|
|
var input = new Vector2(Input.GetAxis("Horizontal"),
|
|
Input.GetAxis("Vertical"));
|
|
var target = ComputeTargetSpeed(input);
|
|
velocity = Vector3.MoveTowards(velocity, target,
|
|
acceleration * Time.deltaTime);
|
|
cc.Move(velocity * Time.deltaTime);
|
|
}
|
|
|
|
Vector3 ComputeTargetSpeed(Vector2 input) {
|
|
if (input.sqrMagnitude < 0.01f) return Vector3.zero;
|
|
var dir = transform.TransformDirection(new Vector3(input.x, 0, input.y));
|
|
var tier = Input.GetKey(KeyCode.LeftShift) ? sprintSpeed
|
|
: Input.GetKey(KeyCode.LeftControl) ? walkSpeed
|
|
: runSpeed;
|
|
var sideMod = Mathf.Lerp(1f, lateralPenalty, Mathf.Abs(input.x));
|
|
return dir.normalized * tier * sideMod;
|
|
}
|
|
}
|
|
```
|
|
|
|
### Animation speed sync (foot-sliding 방지)
|
|
```csharp
|
|
void LateUpdate() {
|
|
var horizontal = new Vector3(velocity.x, 0, velocity.z).magnitude;
|
|
animator.SetFloat("Speed", horizontal);
|
|
animator.speed = Mathf.Clamp(horizontal / runSpeed, 0.5f, 1.5f);
|
|
}
|
|
```
|
|
|
|
### Server-authoritative movement (FPS/MMO)
|
|
```typescript
|
|
// Client: input prediction
|
|
const pendingInputs: Input[] = [];
|
|
function tick(input: Input, dt: number) {
|
|
pendingInputs.push(input);
|
|
predictedPos = simulate(predictedPos, input, dt);
|
|
socket.send({ seq: input.seq, dx: input.dx, dy: input.dy, dt });
|
|
}
|
|
|
|
// Server: validate speed
|
|
function applyInput(state: PlayerState, input: ClientInput) {
|
|
const distance = Math.hypot(input.dx, input.dy);
|
|
const maxAllowed = MAX_SPEED * input.dt * SPEED_TOLERANCE; // 1.05x
|
|
if (distance > maxAllowed) {
|
|
metrics.inc('speedhack.suspect', { player: state.id });
|
|
return state; // 거부
|
|
}
|
|
state.x += input.dx; state.y += input.dy;
|
|
state.lastSeq = input.seq;
|
|
return state;
|
|
}
|
|
|
|
// Client: reconciliation on server snapshot
|
|
function onSnapshot(snap: ServerSnapshot) {
|
|
authoritativePos = snap.pos;
|
|
pendingInputs.splice(0, pendingInputs.findIndex(i => i.seq > snap.lastSeq));
|
|
predictedPos = pendingInputs.reduce(
|
|
(p, i) => simulate(p, i, i.dt), authoritativePos);
|
|
}
|
|
```
|
|
|
|
### VR motion sickness mitigation
|
|
```csharp
|
|
// Unity XR Interaction Toolkit
|
|
public sealed class VrComfortLocomotion : MonoBehaviour {
|
|
[SerializeField] float comfortSpeed = 1.4f; // human walking
|
|
[SerializeField] float vignetteThreshold = 0.7f;
|
|
[SerializeField] CanvasGroup vignette;
|
|
|
|
void Update() {
|
|
var v = controller.velocity.magnitude;
|
|
// 빠를수록 vignette 깊게
|
|
vignette.alpha = Mathf.Clamp01(v / comfortSpeed - vignetteThreshold);
|
|
}
|
|
}
|
|
```
|
|
|
|
### Difficulty/scenario-based parameterization
|
|
```yaml
|
|
# scenarios/highway-merge.yaml — 자율주행 sim
|
|
ego:
|
|
max_speed: 30 # m/s = 108 km/h
|
|
comfort_accel: 2.0
|
|
npcs:
|
|
- kind: car
|
|
behavior: aggressive
|
|
max_speed: 35
|
|
target_gap_s: 0.8
|
|
- kind: car
|
|
behavior: cautious
|
|
max_speed: 25
|
|
target_gap_s: 2.0
|
|
```
|
|
|
|
### Speed curve (acceleration profile)
|
|
```python
|
|
# 선형 가속은 부자연 — easing 적용
|
|
def smooth_accel(current: float, target: float, dt: float, tau: float = 0.15):
|
|
"""exponential approach — feels organic"""
|
|
alpha = 1 - math.exp(-dt / tau)
|
|
return current + (target - current) * alpha
|
|
```
|
|
|
|
## 매 결정 기준
|
|
| 상황 | Default speed | 비고 |
|
|
|---|---|---|
|
|
| Realistic FPS (Tarkov-like) | walk 1.5 / run 4.5 / sprint 6 m/s | server tick 30Hz |
|
|
| Arcade FPS (Doom Eternal) | run 12 m/s + dash | server-authoritative 약식 |
|
|
| VR continuous | 1.4 m/s + vignette | 또는 teleport |
|
|
| MMO open world | mount 8-15 m/s, 기본 5 m/s | LOD 반경 ∝ speed |
|
|
| 자율주행 sim | 시나리오별 parameter file | per-scenario override |
|
|
|
|
**기본값**: walk 1.5 / run 5 / sprint 7.5 m/s. VR은 1.4 m/s. 모두 server-authoritative validation.
|
|
|
|
## 🔗 Graph
|
|
|
|
## 🤖 LLM 활용
|
|
**언제**: 새 게임 prototyping 시 baseline tuning, VR comfort 검토.
|
|
**언제 X**: 정밀 물리 시뮬레이션 (CFD, FEM) — 별도 도메인.
|
|
|
|
## ❌ 안티패턴
|
|
- **Animation speed = `Time.deltaTime` 미곱**: 프레임률에 따라 속도 변동.
|
|
- **Client-authoritative speed**: speedhack 무방비.
|
|
- **단일 speed value**: walk/run/sprint 구분 없으면 UX 빈약.
|
|
- **VR에서 5 m/s 연속 이동**: 멀미 보장.
|
|
- **가속 0 (instant)**: 캐릭터가 미끄러지는 느낌.
|
|
|
|
## 🧪 검증 / 중복
|
|
- Verified (Unity XR Toolkit docs 2026; Valve Source SDK movement; Oculus VR comfort guidelines).
|
|
- 신뢰도 B (도메인이 architecture 가장자리).
|
|
|
|
## 🕓 Changelog
|
|
| 날짜 | 변경 |
|
|
|---|---|
|
|
| 2026-05-08 | Phase 1 |
|
|
| 2026-05-10 | Manual cleanup — layered speed model·server validation·VR comfort |
|