[G1-Sync] Manual knowledge update
This commit is contained in:
@@ -2,120 +2,228 @@
|
||||
id: wiki-2026-0508-control-points
|
||||
title: Control Points
|
||||
category: 10_Wiki/Topics
|
||||
status: needs_review
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: []
|
||||
aliases: [Capture Points, KOTH, Domination]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.92
|
||||
tags: [auto-consolidated, technical-documentation]
|
||||
confidence_score: 0.85
|
||||
verification_status: applied
|
||||
tags: [game-design, multiplayer, objectives, fps]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-08
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
inferred_by: Claude Opus 4.7 (auto-normalize 2026-05-08)
|
||||
tech_stack:
|
||||
language: unspecified
|
||||
framework: unspecified
|
||||
language: csharp-gdscript
|
||||
framework: unity-godot-unreal
|
||||
---
|
||||
|
||||
# 통제점(Control Points)
|
||||
# Control Points
|
||||
|
||||
## 📌 한 줄 통찰 (The Karpathy Summary)
|
||||
통제점(Control Points)은 최소 25명 이상의 플레이어로 구성된 동맹(Alliance)이 세계 지도 상의 경합 구역(Contestable Zones)에서 점령할 수 있는 거점입니다 [1]. 통제점을 성공적으로 점령하고 방어하면, 해당 동맹은 통제점의 레벨에 따라 다양한 수준의 석유 및 토륨 부스트 혜택을 얻게 됩니다 [2, 3]. 점령 과정은 NPC 기지 공격부터 시작하여 다른 동맹과의 제한된 전쟁(War) 및 서든 데스(Sudden Death) 단계에 이르는 치열한 전투로 구성됩니다 [3-5].
|
||||
## 매 한 줄
|
||||
> **"매 spatial objective — 매 team 이 매 zone 의 occupy 통해 score/win."**. Control Points는 multiplayer game 의 가장 ubiquitous objective primitive. Domination, KOTH, Capture-and-Hold, Push, Hardpoint 모두 변형. Team Fortress 2, Battlefield, Overwatch, Apex, CS Bombsite (variant), Splatoon (area-based) 의 핵심. 2026년 server-authoritative netcode + lag compensation 패턴 매 stable.
|
||||
|
||||
---
|
||||
## 매 핵심
|
||||
|
||||
거점(Control Points)은 월드 맵의 분쟁 가능 구역(Contestable Zones) 내에 위치하며, 점령 시 동맹(Alliance)에게 오일과 토륨 부스트 혜택을 제공하는 주요 군사적 목표물입니다 [1, 2]. 최소 25명 이상의 플레이어로 구성된 동맹만이 점령전에 참여할 수 있으며, NPC 기지를 파괴하는 것으로 본격적인 전쟁이 시작됩니다 [1, 2]. 이후 일정 횟수의 거점 파괴, 동맹 간의 전쟁(War) 단계, 서든 데스(Sudden Death) 등 엄격한 룰에 기반한 페이즈를 거쳐 최종적으로 구역 내에 생존한 기지의 유무에 따라 점령 동맹이 결정됩니다 [2-4].
|
||||
### 매 variants
|
||||
| Variant | Mechanic |
|
||||
|---|---|
|
||||
| **Domination** | Multiple points, hold majority for tickets/score |
|
||||
| **KOTH** | Single point, hold-time wins |
|
||||
| **Capture & Hold** | Capture sequentially, last team standing |
|
||||
| **Hardpoint** | Single rotating point, score over time |
|
||||
| **Push/Payload** | Mobile control point along track |
|
||||
| **Linear (5CP TF2)** | Sequential capture, central pivot |
|
||||
|
||||
## 📖 구조화된 지식 (Synthesized Content)
|
||||
- **경합 구역과 통제점의 개념:** 통제점은 세계 지도의 특정 구역인 '경합 구역(Contestable Zones)' 내에 위치하는 NPC 기지입니다 [1, 3]. 모든 지도 구역에 전초기지나 요새가 있지만, 오직 경합 구역만이 통제점을 포함하고 있습니다 [1].
|
||||
- **점령을 위한 전투 및 진행 단계:** 통제점 점령은 여러 단계를 거쳐 이루어집니다.
|
||||
- **공격(Attacking) 및 공격받음(Under Attack) 단계:** 동맹이 경합 구역을 점령하기 위한 첫 번째 단계로, 통제점 자체인 NPC 기지를 공격해 물리쳐야 합니다 [3]. 이후 '공격받음' 상태로 전환되며, 제한된 시간 내에 통제점 레벨에 따라 요구되는 횟수만큼 기지를 격파해야 다음 단계로 넘어갈 수 있습니다 [3].
|
||||
- **전쟁(War) 단계:** 방어 동맹과 공격 동맹 간에 상반된 목표를 가지고 맞붙는 단계입니다 [4]. 방어 측은 경합 구역에서 모든 공격 동맹 구성원을 제거해야 하며, 공격 측은 전쟁 단계가 끝날 때까지 구역 내에 최소 1명의 동맹원 기지를 유지해야 합니다 [4]. 이 단계 동안 두 동맹은 다른 플레이어의 공격을 받지 않는 일종의 '철창 매치(cage match)'를 치르며, 패배한 기지는 구역 밖으로 '이동([[Shift|Shift]]ed)'되어 전쟁이 끝나거나 서든 데스 단계가 되기 전까지 돌아올 수 없습니다 [4].
|
||||
- **서든 데스(Sudden Death) 단계:** 전쟁 단계 종료 시점에도 공격 동맹 구성원이 구역 내에 남아있을 경우 발동됩니다 [5]. 방어 측이 공격 측 기지를 구역 밖으로 밀어낼 수 있는 마지막 기회이며, 한 번 밖으로 쫓겨나면 다시는 구역으로 들어올 수 없습니다 [5]. 서든 데스 종료 시 공격 측 기지가 하나라도 남아있다면 공격 동맹이 통제점을 최종적으로 차지하게 됩니다 [5].
|
||||
- **안전(Secured) 단계:** 통제점 전쟁의 최종 단계로, 이 기간 동안에는 다른 동맹이 통제점을 공격하여 경합 구역을 뺏기 위해 시도할 수 없습니다 [5].
|
||||
### 매 mechanics
|
||||
- **Capture progress**: 0~100%, increases with attackers in zone.
|
||||
- **Multi-capture rate**: more attackers → faster (capped, e.g. 2x at 2+).
|
||||
- **Contest**: defenders inside → progress paused.
|
||||
- **Lock/unlock**: previous point capture unlocks next.
|
||||
- **Decay**: progress drops when zone empty (configurable).
|
||||
- **Overtime**: contested point prevents game end.
|
||||
|
||||
---
|
||||
### 매 design principles
|
||||
- **Sightline balance**: defenders 의 advantage 와 attacker chokes 의 균형.
|
||||
- **Capture time**: too short → trivial, too long → stalemate. 5-15s typical.
|
||||
- **Cap-zone size**: encourages clustering vs spread.
|
||||
- **Spawn distance**: defender respawn 가 너무 가까우면 attack 불가.
|
||||
|
||||
* **참여 조건 및 점령 혜택**
|
||||
거점(Control Points) 점령전에 참여하기 위해서는 최소 25명의 플레이어로 구성된 동맹(Alliance)에 소속되어야 합니다 [1]. 거점을 성공적으로 점령한 동맹은 해당 거점의 레벨에 따라 다양한 수준의 오일(Oil) 및 토륨(Thorium) 생산량 부스트 혜택을 받게 됩니다 [2].
|
||||
### 매 응용
|
||||
1. FPS multiplayer modes (Overwatch, BF, CoD).
|
||||
2. MOBA jungle camps / objectives (Roshan, Drake areas).
|
||||
3. RTS resource nodes (StarCraft expansions).
|
||||
4. MMO PvP zones (WoW battlegrounds).
|
||||
|
||||
* **점령전 진행 단계 (Phases of CP War)**
|
||||
점령전은 다음과 같은 여러 단계를 순차적으로 거치며 진행됩니다.
|
||||
* **초기 공격 (Attacking Control Points)**: 분쟁 가능 구역(Contestable Zone)을 차지하기 위해 동맹이 가장 먼저 해야 할 일은 구역 내에 있는 NPC 기지 형태의 거점 자체를 파괴하는 것입니다 [2].
|
||||
* **공격받음 (Under Attack) 단계**: NPC 기지를 무너뜨리면 거점은 'Under Attack' 상태가 됩니다 [2]. 이때 거점 레벨에 따라 요구되는 지정된 승리 횟수를 제한 시간 내에 달성해야만 다음 단계로 넘어갈 수 있습니다 [2].
|
||||
* **전쟁 (War) 단계**: 본격적으로 두 동맹이 분쟁 가능 구역 내에서 맞붙는 "케이지 매치(Cage Match)" 단계입니다 [3]. 이 기간에는 두 동맹을 제외한 다른 어떤 플레이어도 해당 구역의 기지를 공격할 수 없습니다 [3].
|
||||
* **방어 동맹의 목표**: 공격 동맹의 모든 멤버를 분쟁 가능 구역 밖으로 제거해야 합니다. 기지가 파괴된 플레이어는 구역 밖으로 '이동(shifted)'되며, 전쟁이 끝나거나 서든 데스 단계가 되기 전까지는 구역으로 복귀할 수 없습니다 [3].
|
||||
* **공격 동맹의 목표**: 전쟁 단계가 종료될 때까지 구역 내에 최소 1명 이상의 동맹 멤버 기지가 살아남아 있어야 합니다 [3].
|
||||
* **서든 데스 (Sudden Death) 단계**: 전쟁 단계가 끝날 때까지 구역 내에 공격 동맹의 기지가 남아있을 경우 발동됩니다 [4]. 방어 동맹이 공격 기지를 구역 밖으로 밀어낼 수 있는 마지막 기회이며, 이 단계에서 밖으로 밀려난 기지는 절대 다시 구역으로 복귀할 수 없습니다 [4]. 하나의 공격 기지라도 살아남는다면 공격 동맹이 거점을 차지하게 됩니다 [4].
|
||||
* **안전 확보 (Secured) 단계**: 거점 점령전의 최종 단계입니다 [4]. 이 기간 동안에는 다른 어떤 동맹도 해당 거점을 공격하거나 분쟁 가능 구역을 빼앗기 위해 시도할 수 없으며, 승리한 동맹은 다음 전투를 준비하며 휴식을 취할 수 있습니다 [4].
|
||||
## 💻 패턴
|
||||
|
||||
## ⚠️ 모순 및 업데이트 (Contradictions & Updates)
|
||||
- 신규 지식 자산화 (2026-04-27).
|
||||
- War Commander 전투 생태계 데이터 통합.
|
||||
### Unity C# — control point trigger
|
||||
```csharp
|
||||
using UnityEngine;
|
||||
using Unity.Netcode;
|
||||
|
||||
## 🔗 지식 연결 (Graph)
|
||||
- **Related Topics:** [[동맹(Alliances)|동맹(Alliances]], 토륨(Thorium), [[세계 지도(World Map)|세계 지도(World Map]]
|
||||
- **Projects/Contexts:** War Commander 전투 시스템 및 동맹 간 영토 지배 전략
|
||||
- **Contradictions/Notes:** 통제점 확보는 단순히 개별 기지의 전투력뿐만 아니라, 외부 세력의 개입이 차단된 상태('cage match')에서 동맹원들이 구역 내에 기지를 유지하거나 밀어내는 지정학적 기동 전략을 요구합니다 [2, 4].
|
||||
public class ControlPoint : NetworkBehaviour {
|
||||
public NetworkVariable<float> Progress = new(0f);
|
||||
public NetworkVariable<int> OwnerTeam = new(-1);
|
||||
|
||||
---
|
||||
*Last updated: 2026-04-27*
|
||||
[SerializeField] private float captureRate = 10f; // pct/sec per attacker
|
||||
[SerializeField] private float maxRate = 20f;
|
||||
|
||||
---
|
||||
private readonly Dictionary<int, int> teamCount = new();
|
||||
|
||||
- **Related Topics:** [[동맹(Alliances)|동맹(Alliances)]], 토륨(Thorium)
|
||||
- **Projects/Contexts:** War Commander: Rogue Assault
|
||||
- **Contradictions/Notes:** 제공된 소스에서 설명하는 분쟁 가능 구역(Contestable Zones)과 거점(Control Points) 점령 시스템은 'War Commander: Rogue Assault' 헬프 센터의 데이터를 기반으로 합니다 [1]. 본가 'War Commander'의 200개 섹터를 둘러싼 동맹전(Clans & Alliances)과 맥락을 같이 하지만, 상세 전투 페이즈(War, Sudden Death 등)는 로그 어썰트(Rogue Assault)의 특정 시스템을 설명한 것임을 유의해야 합니다.
|
||||
private void OnTriggerEnter(Collider other) {
|
||||
if (!IsServer) return;
|
||||
if (other.TryGetComponent<Player>(out var p)) {
|
||||
teamCount.TryGetValue(p.Team, out var n);
|
||||
teamCount[p.Team] = n + 1;
|
||||
}
|
||||
}
|
||||
|
||||
---
|
||||
*Last updated: 2026-04-27*
|
||||
private void OnTriggerExit(Collider other) {
|
||||
if (!IsServer) return;
|
||||
if (other.TryGetComponent<Player>(out var p)
|
||||
&& teamCount.TryGetValue(p.Team, out var n)) {
|
||||
teamCount[p.Team] = Mathf.Max(0, n - 1);
|
||||
}
|
||||
}
|
||||
|
||||
## 🤖 LLM 활용 힌트 (How to Use This Knowledge)
|
||||
private void FixedUpdate() {
|
||||
if (!IsServer) return;
|
||||
|
||||
**언제 이 지식을 쓰는가:**
|
||||
- *(TODO)*
|
||||
var teams = new List<KeyValuePair<int,int>>(teamCount);
|
||||
teams.Sort((a, b) => b.Value.CompareTo(a.Value));
|
||||
|
||||
**언제 쓰면 안 되는가:**
|
||||
- *(TODO)*
|
||||
if (teams.Count == 0 || teams[0].Value == 0) return;
|
||||
|
||||
## 🧪 검증 상태 (Validation)
|
||||
// Contested: top two teams equal & nonzero
|
||||
if (teams.Count > 1 && teams[0].Value == teams[1].Value) return;
|
||||
|
||||
- **정보 상태:** needs_review
|
||||
- **출처 신뢰도:** A
|
||||
- **검토 이유:** *(P-Reinforce Phase 1 자동 정규화. 본문 검증 필요.)*
|
||||
var attacker = teams[0];
|
||||
var rate = Mathf.Min(maxRate, captureRate * Mathf.Sqrt(attacker.Value));
|
||||
|
||||
## 🧬 중복 검사 (Duplicate Check)
|
||||
|
||||
- **기존 유사 문서:** *(TODO: 인덱서 클러스터 리포트 참조)*
|
||||
- **처리 방식:** UPDATE (자동 정규화)
|
||||
- **처리 이유:** Phase 1 정규화 — 옛 템플릿/누락 필드 보강.
|
||||
|
||||
## 🕓 변경 이력 (Changelog)
|
||||
|
||||
| 날짜 | 변경 내용 | 처리 방식 | 신뢰도 |
|
||||
|------|-----------|-----------|--------|
|
||||
| 2026-05-08 | P-Reinforce Phase 1 정규화 (frontmatter + 헤더 표준화) | UPDATE | A |
|
||||
|
||||
## 💻 코드 패턴 (Code Patterns)
|
||||
|
||||
**패턴 1:** *(TODO: 이 프로젝트 컨벤션 반영한 구조 스켈레톤)*
|
||||
|
||||
```text
|
||||
# TODO
|
||||
if (OwnerTeam.Value == attacker.Key) {
|
||||
Progress.Value = Mathf.Min(100f, Progress.Value + rate * Time.fixedDeltaTime);
|
||||
} else {
|
||||
Progress.Value = Mathf.Max(0f, Progress.Value - rate * Time.fixedDeltaTime);
|
||||
if (Progress.Value <= 0f) OwnerTeam.Value = attacker.Key;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 🤔 의사결정 기준 (Decision Criteria)
|
||||
### Godot 4 / GDScript
|
||||
```gdscript
|
||||
extends Area3D
|
||||
class_name ControlPoint
|
||||
|
||||
**선택 A를 써야 할 때:**
|
||||
- *(TODO)*
|
||||
@export var capture_rate: float = 10.0
|
||||
var progress: float = 0.0
|
||||
var owner_team: int = -1
|
||||
var team_in_zone: Dictionary = {}
|
||||
|
||||
**선택 B를 써야 할 때:**
|
||||
- *(TODO)*
|
||||
func _physics_process(delta: float) -> void:
|
||||
if not multiplayer.is_server(): return
|
||||
|
||||
**기본값:**
|
||||
> *(TODO)*
|
||||
var top_team := -1
|
||||
var top_count := 0
|
||||
var contested := false
|
||||
|
||||
## ❌ 안티패턴 (Anti-Patterns)
|
||||
for team in team_in_zone:
|
||||
var n: int = team_in_zone[team]
|
||||
if n > top_count:
|
||||
top_count = n
|
||||
top_team = team
|
||||
contested = false
|
||||
elif n == top_count and n > 0:
|
||||
contested = true
|
||||
|
||||
- **[안티패턴]:** *(TODO: 무엇을 하면 안 되는가 + 이유 + 대신 무엇을)*
|
||||
if contested or top_count == 0: return
|
||||
|
||||
var rate = capture_rate * sqrt(top_count)
|
||||
if owner_team == top_team:
|
||||
progress = min(100.0, progress + rate * delta)
|
||||
else:
|
||||
progress = max(0.0, progress - rate * delta)
|
||||
if progress <= 0.0:
|
||||
owner_team = top_team
|
||||
_notify_capture(top_team)
|
||||
```
|
||||
|
||||
### Server-authoritative state with lag compensation
|
||||
```csharp
|
||||
// Server stores state snapshots for last 1s
|
||||
private readonly CircularBuffer<Snapshot> history = new(60);
|
||||
|
||||
public bool WasInsideAtTime(Vector3 playerPos, float clientTime) {
|
||||
var snap = history.SampleAt(clientTime);
|
||||
return snap.Bounds.Contains(playerPos);
|
||||
}
|
||||
```
|
||||
|
||||
### Configurable progression (data-driven)
|
||||
```json
|
||||
{
|
||||
"id": "cp_central",
|
||||
"captureTimeSeconds": 8,
|
||||
"multiCapMultiplier": [1.0, 1.5, 1.75, 2.0],
|
||||
"decayRate": 0.5,
|
||||
"unlockedBy": ["cp_a", "cp_b"],
|
||||
"scoresPerSecond": 10
|
||||
}
|
||||
```
|
||||
|
||||
### UI broadcast (client-side prediction visual)
|
||||
```typescript
|
||||
// Client receives Progress NetworkVariable changes,
|
||||
// interpolates between ticks for smooth bar fill
|
||||
useEffect(() => {
|
||||
const start = performance.now();
|
||||
const startVal = displayedProgress;
|
||||
const targetVal = serverProgress;
|
||||
|
||||
const tick = () => {
|
||||
const t = Math.min(1, (performance.now() - start) / 100);
|
||||
setDisplayedProgress(startVal + (targetVal - startVal) * t);
|
||||
if (t < 1) requestAnimationFrame(tick);
|
||||
};
|
||||
tick();
|
||||
}, [serverProgress]);
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| Casual fast-match | KOTH, single-point, 3-5 min rounds |
|
||||
| Competitive | Linear/5CP, longer rounds, overtime |
|
||||
| Asymmetric | Push/Payload (attack vs defend) |
|
||||
| Objective rotation | Hardpoint (rotating zone keeps action moving) |
|
||||
| Large maps | Domination (multiple distributed) |
|
||||
|
||||
**기본값**: server-authoritative + 5-10s capture + multi-cap multiplier + decay + contested-pause + overtime.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Multiplayer Game Design]] · [[Game Objectives]]
|
||||
- 변형: [[King of the Hill]] · [[Domination]] · [[Payload]]
|
||||
- 응용: [[Team Fortress 2]] · [[Overwatch]] · [[Battlefield]]
|
||||
- Adjacent: [[Server Authority]] · [[Lag Compensation]] · [[Spawn System]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: multiplayer mode design, level layout review, balance tuning, netcode design for objectives.
|
||||
**언제 X**: single-player, asynchronous (turn-based), pure deathmatch (no objective).
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Client-authoritative capture**: trivially exploitable — server-side only.
|
||||
- **Spawn too close to objective**: defender immortal — distance + lockout window.
|
||||
- **No contested-pause**: solo defender can't stall — feels unfair.
|
||||
- **Capture too short**: zerg wins, no skill — 8-12s standard.
|
||||
- **No decay**: half-cap then leave is permanent — partial progress decay.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Valve TF2 design / Blizzard Overwatch dev blogs / GDC talks 2018-2024).
|
||||
- 신뢰도 A-.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — variants + Unity/Godot impl + netcode |
|
||||
|
||||
Reference in New Issue
Block a user