궁병 — 조준 방향에 적이 없으면 목표점까지 이동(PD 2026-11-04 2차 지시 · 「행동 취소」 폐기) · 에디터 그래픽 API D3D11 우선(oCam 훅 D3D12 크래시 우회) · 테스트 17/17
- ActionResolver.ArcherRoutine: 복도(당긴 거리 + 사거리) 안에 적 없음 → FreeTravel 로 목표점까지 이동 · MovedOnly · ActionOutcome.Cancelled 삭제 - SlingshotInput: archerBlocked(놓기 취소 · 회색 목표 링 · HUD 안내 문구) 삭제 · BattleManager.Describe 「사거리 안에 적이 없어 대기」 삭제 · AI 적 궁병도 전진 - 테스트: Archer_CannotAct_… → Archer_MovesToTarget_WhenNoEnemyWithinSlingPlusRatio (사본 배치 error CS 0 · 17/17) - ProjectSettings: WindowsStandaloneSupport m_APIs D3D11 → D3D12 (PD 에디터 15:04 크래시 2회 = oCam HookSurfaceDLL → D3D12SwapChain::Present · 사본 GUI 실측 Direct3D 11.0 · oCam 켜진 채 자동 플레이 1턴 정상) - CLAUDE.md(궁병 규칙 · 크래시 진단 절차) · README 병종 표 갱신 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
bc8e571eea
commit
645f8646cf
|
|
@ -12,7 +12,6 @@ namespace TKS
|
||||||
public bool Overwatch; // 창병: 최종 목표점 명중 → 대기(견제)
|
public bool Overwatch; // 창병: 최종 목표점 명중 → 대기(견제)
|
||||||
public bool Crit; // 궁병: 최종 목표점 명중 → 크리티컬
|
public bool Crit; // 궁병: 최종 목표점 명중 → 크리티컬
|
||||||
public bool MovedOnly; // 공격 없이 이동만
|
public bool MovedOnly; // 공격 없이 이동만
|
||||||
public bool Cancelled; // 궁병: 조준 방향 슬링샷 범위 + 사거리 안에 적이 없어 행동하지 않음
|
|
||||||
public int DamageDealt;
|
public int DamageDealt;
|
||||||
public int CounterTaken;
|
public int CounterTaken;
|
||||||
public List<Unit> Attacked = new List<Unit>();
|
public List<Unit> Attacked = new List<Unit>();
|
||||||
|
|
@ -25,8 +24,9 @@ namespace TKS
|
||||||
/// ① 보병: 경로 위 첫 적까지 이동 후 공격 · 목표점 명중 시 추가 이동 1회
|
/// ① 보병: 경로 위 첫 적까지 이동 후 공격 · 목표점 명중 시 추가 이동 1회
|
||||||
/// ② 기병: 경로 위 적을 모두 관통 피해 · 목표점까지 이동 · 목표점 명중 시 제자리 복귀
|
/// ② 기병: 경로 위 적을 모두 관통 피해 · 목표점까지 이동 · 목표점 명중 시 제자리 복귀
|
||||||
/// ③ 창병: 보병과 동일 · 목표점 명중 시 대기 → 반경 안에서 움직이는 적에게 피해
|
/// ③ 창병: 보병과 동일 · 목표점 명중 시 대기 → 반경 안에서 움직이는 적에게 피해
|
||||||
/// ④ 궁병: 조준 방향 슬링샷 거리 + 사거리(슬링샷 최대 × archerRangeRatio) 안에 적이 있을 때만 행동 —
|
/// ④ 궁병: 조준 방향 슬링샷 거리 + 사거리(슬링샷 최대 × archerRangeRatio) 안에 적이 있으면
|
||||||
/// 적 가장자리가 사거리에 들어올 때까지만 이동 후 사격(반격 없음) · 목표점 명중 시 크리티컬 · 적이 없으면 취소(PD 2026-11-04)
|
/// 적 가장자리가 사거리에 들어올 때까지만 이동 후 사격(반격 없음) · 목표점 명중 시 크리티컬 ·
|
||||||
|
/// 적이 없으면 목표점까지 이동만(PD 2026-11-04 2차 지시 · 1차의 「행동 취소」 폐기)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class ActionResolver
|
public class ActionResolver
|
||||||
{
|
{
|
||||||
|
|
@ -251,23 +251,18 @@ namespace TKS
|
||||||
|
|
||||||
IEnumerator ArcherRoutine(Unit u, Vector2 origin, Vector2 dir, float dist, Vector2 target, bool moveOnly, ActionOutcome o)
|
IEnumerator ArcherRoutine(Unit u, Vector2 origin, Vector2 dir, float dist, Vector2 target, bool moveOnly, ActionOutcome o)
|
||||||
{
|
{
|
||||||
if (moveOnly)
|
// 조준 방향으로 슬링샷 거리 + 사거리 안의 첫 적. hitDist = 그 적 가장자리에 닿기까지의 이동 거리.
|
||||||
|
float hitDist = 0f;
|
||||||
|
Unit first = moveOnly ? null : Combat.FirstEnemyOnPath(u, origin, dir, dist + u.AttackRange, out hitDist);
|
||||||
|
if (first == null)
|
||||||
{
|
{
|
||||||
|
// 적이 없으면 목표점까지 이동(PD 2026-11-04 2차: 「슬링샷 범위 내 적이 없을 경우 해당 위치까지 이동」)
|
||||||
float free = Combat.FreeTravel(u, origin, dir, dist, true);
|
float free = Combat.FreeTravel(u, origin, dir, dist, true);
|
||||||
yield return Move(u, origin + dir * free, o);
|
yield return Move(u, origin + dir * free, o);
|
||||||
o.MovedOnly = true;
|
o.MovedOnly = true;
|
||||||
yield break;
|
yield break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 조준 방향으로 슬링샷 거리 + 사거리 안의 첫 적. hitDist = 그 적 가장자리에 닿기까지의 이동 거리.
|
|
||||||
float hitDist;
|
|
||||||
Unit first = Combat.FirstEnemyOnPath(u, origin, dir, dist + u.AttackRange, out hitDist);
|
|
||||||
if (first == null)
|
|
||||||
{
|
|
||||||
o.Cancelled = true; // PD 2026-11-04: 슬링샷 범위 + 비율 안에 적이 있을 때만 행동 가능
|
|
||||||
yield break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 적 가장자리까지 남은 간격이 사거리 안에 들어올 때까지만 이동(슬링샷 거리 상한)
|
// 적 가장자리까지 남은 간격이 사거리 안에 들어올 때까지만 이동(슬링샷 거리 상한)
|
||||||
float need = Mathf.Clamp(hitDist - u.AttackRange, 0f, dist);
|
float need = Mathf.Clamp(hitDist - u.AttackRange, 0f, dist);
|
||||||
float freeMove = Combat.FreeTravel(u, origin, dir, need, true);
|
float freeMove = Combat.FreeTravel(u, origin, dir, need, true);
|
||||||
|
|
|
||||||
|
|
@ -383,7 +383,6 @@ namespace TKS
|
||||||
static string Describe(Unit u, ActionOutcome o, bool moveOnly)
|
static string Describe(Unit u, ActionOutcome o, bool moveOnly)
|
||||||
{
|
{
|
||||||
string who = u.DisplayName + "(" + UnitTypeInfo.Korean(u.Type) + ")";
|
string who = u.DisplayName + "(" + UnitTypeInfo.Korean(u.Type) + ")";
|
||||||
if (o.Cancelled) return who + " 사거리 안에 적이 없어 대기";
|
|
||||||
if (o.Attacked.Count == 0)
|
if (o.Attacked.Count == 0)
|
||||||
{
|
{
|
||||||
return who + (moveOnly ? " 추가 이동" : " 이동");
|
return who + (moveOnly ? " 추가 이동" : " 이동");
|
||||||
|
|
|
||||||
|
|
@ -7,13 +7,12 @@ namespace TKS
|
||||||
/// 슬링샷 조작: 아군 유닛을 잡고(누름) 뒤로 당긴 뒤(드래그) 놓으면(드롭) 반대 방향으로 이동·공격.
|
/// 슬링샷 조작: 아군 유닛을 잡고(누름) 뒤로 당긴 뒤(드래그) 놓으면(드롭) 반대 방향으로 이동·공격.
|
||||||
/// 유닛 탭(아군·적군) = 그 유닛의 사거리 표시(RangeIndicator) · 빈 곳 드래그 = 전장 스크롤 · 휠 = 줌 · 미니맵 탭 = 그 지점으로 이동.
|
/// 유닛 탭(아군·적군) = 그 유닛의 사거리 표시(RangeIndicator) · 빈 곳 드래그 = 전장 스크롤 · 휠 = 줌 · 미니맵 탭 = 그 지점으로 이동.
|
||||||
/// 버튼 없음. 결과 화면에서는 탭으로 계속. 추가 이동만 남았을 때 빈 곳 탭 = 건너뛰기.
|
/// 버튼 없음. 결과 화면에서는 탭으로 계속. 추가 이동만 남았을 때 빈 곳 탭 = 건너뛰기.
|
||||||
/// 궁병(PD 2026-11-04): 조준 방향 슬링샷 거리 + 사거리 안에 적이 없으면 놓아도 행동하지 않는다(선택 유지 · 안내 문구).
|
/// 궁병도 다른 병종과 같이 놓으면 바로 행동한다(조준 방향에 적이 없으면 목표점까지 이동 · PD 2026-11-04 2차 지시).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class SlingshotInput : MonoBehaviour
|
public class SlingshotInput : MonoBehaviour
|
||||||
{
|
{
|
||||||
static readonly Color TargetFreeColor = Color.white;
|
static readonly Color TargetFreeColor = Color.white;
|
||||||
static readonly Color TargetHitColor = new Color(1f, 0.6f, 0.1f);
|
static readonly Color TargetHitColor = new Color(1f, 0.6f, 0.1f);
|
||||||
static readonly Color TargetBlockedColor = new Color(0.65f, 0.65f, 0.65f, 0.7f);
|
|
||||||
|
|
||||||
BattleManager battle;
|
BattleManager battle;
|
||||||
Camera cam;
|
Camera cam;
|
||||||
|
|
@ -26,7 +25,6 @@ namespace TKS
|
||||||
bool panning;
|
bool panning;
|
||||||
bool emptyPress;
|
bool emptyPress;
|
||||||
bool previewValid;
|
bool previewValid;
|
||||||
bool archerBlocked;
|
|
||||||
Vector2 lastScreen;
|
Vector2 lastScreen;
|
||||||
float panPixels;
|
float panPixels;
|
||||||
Vector2 currentTarget;
|
Vector2 currentTarget;
|
||||||
|
|
@ -124,7 +122,6 @@ namespace TKS
|
||||||
panning = false;
|
panning = false;
|
||||||
emptyPress = false;
|
emptyPress = false;
|
||||||
previewValid = false;
|
previewValid = false;
|
||||||
archerBlocked = false;
|
|
||||||
|
|
||||||
Vector2 mapWorld;
|
Vector2 mapWorld;
|
||||||
if (hud != null && camCtrl != null && hud.TryMinimapTap(screen, out mapWorld))
|
if (hud != null && camCtrl != null && hud.TryMinimapTap(screen, out mapWorld))
|
||||||
|
|
@ -170,43 +167,24 @@ namespace TKS
|
||||||
{
|
{
|
||||||
HidePreview();
|
HidePreview();
|
||||||
previewValid = false;
|
previewValid = false;
|
||||||
archerBlocked = false;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (mag > selected.SlingMaxDistance) launch = launch / mag * selected.SlingMaxDistance;
|
if (mag > selected.SlingMaxDistance) launch = launch / mag * selected.SlingMaxDistance;
|
||||||
currentTarget = battle.Board.ClampInside(selected.Position + launch, selected.Radius);
|
currentTarget = battle.Board.ClampInside(selected.Position + launch, selected.Radius);
|
||||||
previewValid = true;
|
previewValid = true;
|
||||||
int targets = ShowPreview(selected, currentTarget, w);
|
ShowPreview(selected, currentTarget, w);
|
||||||
archerBlocked = selected.Type == UnitType.Archer && !selected.BonusMoveAvailable && targets == 0;
|
|
||||||
if (archerBlocked)
|
|
||||||
{
|
|
||||||
targetRing.color = TargetBlockedColor;
|
|
||||||
targetRing.transform.localScale = new Vector3(0.7f, 0.7f, 1f);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnRelease(Vector2 screen)
|
void OnRelease(Vector2 screen)
|
||||||
{
|
{
|
||||||
if (dragging && selected != null && previewValid && battle.CanAct(selected))
|
if (dragging && selected != null && previewValid && battle.CanAct(selected))
|
||||||
{
|
{
|
||||||
if (archerBlocked)
|
var t = currentTarget;
|
||||||
{
|
var u = selected;
|
||||||
// 궁병: 조준 방향 슬링샷 범위 + 비율 안에 적 없음 → 행동 취소(선택·사거리 표시 유지)
|
HidePreview();
|
||||||
HidePreview();
|
if (u.View != null) u.View.SetAiming(false);
|
||||||
if (selected.View != null) selected.View.SetAiming(false);
|
Select(null);
|
||||||
int pct = Mathf.RoundToInt(battle.Config.archerRangeRatio * 100f);
|
battle.RequestPlayerAction(u, t);
|
||||||
battle.LastMessage = selected.DisplayName + "(궁병) — 슬링샷 범위 +" + pct + " % 안에 적이 없어 행동할 수 없습니다";
|
|
||||||
battle.NotifyChanged();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var t = currentTarget;
|
|
||||||
var u = selected;
|
|
||||||
HidePreview();
|
|
||||||
if (u.View != null) u.View.SetAiming(false);
|
|
||||||
Select(null);
|
|
||||||
battle.RequestPlayerAction(u, t);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (emptyPress && panPixels <= TapPixels)
|
else if (emptyPress && panPixels <= TapPixels)
|
||||||
{
|
{
|
||||||
|
|
@ -221,7 +199,6 @@ namespace TKS
|
||||||
panning = false;
|
panning = false;
|
||||||
emptyPress = false;
|
emptyPress = false;
|
||||||
previewValid = false;
|
previewValid = false;
|
||||||
archerBlocked = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>유닛 선택(아군·적군 · null = 해제): 선택 링 + HUD 정보 + 사거리 표시(궁병은 도넛 · 범위 안 적 유무로 색).</summary>
|
/// <summary>유닛 선택(아군·적군 · null = 해제): 선택 링 + HUD 정보 + 사거리 표시(궁병은 도넛 · 범위 안 적 유무로 색).</summary>
|
||||||
|
|
|
||||||
|
|
@ -138,7 +138,6 @@ namespace TKS.Tests
|
||||||
var foe = bm.SpawnUnit(UnitType.Infantry, Faction.Enemy, new Vector2(0f, -0.2f), "적보병");
|
var foe = bm.SpawnUnit(UnitType.Infantry, Faction.Enemy, new Vector2(0f, -0.2f), "적보병");
|
||||||
var o = new ActionOutcome();
|
var o = new ActionOutcome();
|
||||||
yield return bm.ResolveForTest(arc, new Vector2(0f, -1f), false, o);
|
yield return bm.ResolveForTest(arc, new Vector2(0f, -1f), false, o);
|
||||||
Assert.IsFalse(o.Cancelled);
|
|
||||||
Assert.Contains(foe, o.Attacked, "조준 방향의 적을 사거리까지 이동 후 사격");
|
Assert.Contains(foe, o.Attacked, "조준 방향의 적을 사거리까지 이동 후 사격");
|
||||||
Assert.Less(foe.Hp, foe.MaxHp);
|
Assert.Less(foe.Hp, foe.MaxHp);
|
||||||
Assert.AreEqual(arc.MaxHp, arc.Hp, "궁병은 반격을 받지 않는다");
|
Assert.AreEqual(arc.MaxHp, arc.Hp, "궁병은 반격을 받지 않는다");
|
||||||
|
|
@ -149,19 +148,20 @@ namespace TKS.Tests
|
||||||
}
|
}
|
||||||
|
|
||||||
[UnityTest]
|
[UnityTest]
|
||||||
public IEnumerator Archer_CannotAct_WhenNoEnemyWithinSlingPlusRatio()
|
public IEnumerator Archer_MovesToTarget_WhenNoEnemyWithinSlingPlusRatio()
|
||||||
{
|
{
|
||||||
yield return Fresh();
|
yield return Fresh();
|
||||||
var arc = bm.SpawnUnit(UnitType.Archer, Faction.Player, new Vector2(0f, -5f), "궁병");
|
var arc = bm.SpawnUnit(UnitType.Archer, Faction.Player, new Vector2(0f, -5f), "궁병");
|
||||||
// 적 가장자리까지 6.0 − 반지름 합 ≈ 5.2 > 4.0 + 0.8 → 행동 불가(이동도 없음)
|
// 적 가장자리까지 6.0 − 반지름 합 ≈ 5.2 > 슬링샷 + 사거리 → 공격 없이 목표점까지 이동(PD 2026-11-04 2차 지시)
|
||||||
var foe = bm.SpawnUnit(UnitType.Infantry, Faction.Enemy, new Vector2(0f, 1f), "적보병");
|
var foe = bm.SpawnUnit(UnitType.Infantry, Faction.Enemy, new Vector2(0f, 1f), "적보병");
|
||||||
Assert.IsFalse(bm.AnyEnemyWithinArcherReach(arc), "공격 가능 범위 안에 적 없음(도넛 회색)");
|
Assert.IsFalse(bm.AnyEnemyWithinArcherReach(arc), "공격 가능 범위 안에 적 없음(도넛 회색)");
|
||||||
var o = new ActionOutcome();
|
var o = new ActionOutcome();
|
||||||
yield return bm.ResolveForTest(arc, new Vector2(0f, -1f), false, o);
|
var target = new Vector2(0f, -1f);
|
||||||
Assert.IsTrue(o.Cancelled, "슬링샷 범위 + 비율 안에 적이 없으면 궁병은 행동하지 않는다");
|
yield return bm.ResolveForTest(arc, target, false, o);
|
||||||
|
Assert.IsTrue(o.MovedOnly, "슬링샷 범위 + 비율 안에 적이 없으면 궁병은 목표점까지 이동만 한다");
|
||||||
Assert.AreEqual(0, o.Attacked.Count);
|
Assert.AreEqual(0, o.Attacked.Count);
|
||||||
Assert.AreEqual(foe.MaxHp, foe.Hp);
|
Assert.AreEqual(foe.MaxHp, foe.Hp);
|
||||||
Assert.Less(Vector2.Distance(arc.Position, new Vector2(0f, -5f)), 0.01f, "이동도 하지 않는다");
|
Assert.Less(Vector2.Distance(arc.Position, target), 0.05f, "목표 지점까지 이동");
|
||||||
|
|
||||||
var near = bm.SpawnUnit(UnitType.Infantry, Faction.Enemy, new Vector2(0f, -0.4f), "가까운적");
|
var near = bm.SpawnUnit(UnitType.Infantry, Faction.Enemy, new Vector2(0f, -0.4f), "가까운적");
|
||||||
Assert.IsTrue(bm.AnyEnemyWithinArcherReach(arc), "범위 안에 적이 생기면 도넛 붉게");
|
Assert.IsTrue(bm.AnyEnemyWithinArcherReach(arc), "범위 안에 적이 생기면 도넛 붉게");
|
||||||
|
|
|
||||||
|
|
@ -39,13 +39,14 @@ Unity.exe -projectPath <proj> -executeMethod TKS.EditorTools.ProjectSetup.AutoPl
|
||||||
|
|
||||||
- `Unity.exe` = `C:\Program Files\Unity\Hub\Editor\6000.3.23f1\Editor\Unity.exe`. 에디터가 같은 프로젝트를 열고 있으면 배치 실행은 못 한다(`Temp/UnityLockfile`) — 먼저 `unity status` 로 확인.
|
- `Unity.exe` = `C:\Program Files\Unity\Hub\Editor\6000.3.23f1\Editor\Unity.exe`. 에디터가 같은 프로젝트를 열고 있으면 배치 실행은 못 한다(`Temp/UnityLockfile`) — 먼저 `unity status` 로 확인.
|
||||||
- 배치 로그 판정 = `error CS` 0 · 테스트 XML `failed="0"`.
|
- 배치 로그 판정 = `error CS` 0 · 테스트 XML `failed="0"`.
|
||||||
|
- **에디터가 열리면서 크래시하면 oCam(화면 녹화)을 의심**: `%LOCALAPPDATA%\Unity\Editor\Editor.log` 꼬리의 스택에 `HookSurfaceDLL` → `D3D12SwapChain::Present` 가 있으면 oCam 의 D3D12 훅이 원인(2026-11-04 15:04 × 2 실측). 이 프로젝트는 그래서 Windows Graphics API 를 **D3D11 우선**으로 고정했다(`ProjectSettings.asset` `m_BuildTargetGraphicsAPIs` · 에디터도 이 설정을 따름). 그래도 크래시하면 `-force-d3d11` 인수 또는 oCam 종료. 배치 `-nographics` 는 영향 없음.
|
||||||
|
|
||||||
## 코드 규칙
|
## 코드 규칙
|
||||||
|
|
||||||
- 수치·영웅·스테이지 SOT = `Assets/Resources/BalanceConfig.asset`(Inspector 조정 · 최초 시드만 `Assets/Scripts/Editor/BalanceDefaults.cs`). 코드·테스트에 수치를 직접 적지 않는다(C45).
|
- 수치·영웅·스테이지 SOT = `Assets/Resources/BalanceConfig.asset`(Inspector 조정 · 최초 시드만 `Assets/Scripts/Editor/BalanceDefaults.cs`). 코드·테스트에 수치를 직접 적지 않는다(C45).
|
||||||
- 규칙 해석 변경 = `Assets/Scripts/Runtime/ActionResolver.cs`(병종 4종) · `BattleManager.cs`(턴·승패·창병 대기) · `Combat.cs`(피해 공식·경로 질의). 변경 시 `Assets/Tests/PlayMode/BattleFlowTests.cs` 에 테스트 추가 후 14종 이상 전부 통과 확인.
|
- 규칙 해석 변경 = `Assets/Scripts/Runtime/ActionResolver.cs`(병종 4종) · `BattleManager.cs`(턴·승패·창병 대기) · `Combat.cs`(피해 공식·경로 질의). 변경 시 `Assets/Tests/PlayMode/BattleFlowTests.cs` 에 테스트 추가 후 14종 이상 전부 통과 확인.
|
||||||
- 콜라이더 질의 전 `Physics2D.SyncTransforms()` · 유닛 제거는 `SetActive(false)` 후 `Destroy` · 자식 렌더러가 있는 표시물은 GameObject 통째로 끈다.
|
- 콜라이더 질의 전 `Physics2D.SyncTransforms()` · 유닛 제거는 `SetActive(false)` 후 `Destroy` · 자식 렌더러가 있는 표시물은 GameObject 통째로 끈다.
|
||||||
- **궁병 규칙(PD 2026-11-04)**: 사거리 = `slingMaxDistance × archerRangeRatio`(자산 · 기본 0.2 = 슬링샷 범위 +20 %). 조준 방향 「슬링샷 거리 + 사거리」 안에 적이 없으면 행동 불가(`SlingshotInput` 이 놓기 자체를 취소 · AI 는 `ActionOutcome.Cancelled` 로 대기) · 적 **가장자리**가 사거리에 들어올 때까지만 이동 후 사격. `UnitStats.attackRange` 는 창병 견제 반경만.
|
- **궁병 규칙(PD 2026-11-04)**: 사거리 = `slingMaxDistance × archerRangeRatio`(자산 · 기본 0.2 = 슬링샷 범위 +20 %). 조준 방향 「슬링샷 거리 + 사거리」 안에 적이 있으면 적 **가장자리**가 사거리에 들어올 때까지만 이동 후 사격 · 적이 없으면 **목표점까지 이동만**(PD 2026-11-04 2차 지시 · 1차의 「행동 취소」 폐기 · AI 적 궁병도 같음). `UnitStats.attackRange` 는 창병 견제 반경만.
|
||||||
- **사거리 표시(PD 2026-11-04)**: 유닛 탭(아군·적군) → `RangeIndicator`(슬링샷 최대 거리 = 얇은 반투명 원 · 궁병 = 슬링샷 범위 ~ ×(1+비율) 채워진 붉은 도넛 · 범위 안 적이 없으면 회색).
|
- **사거리 표시(PD 2026-11-04)**: 유닛 탭(아군·적군) → `RangeIndicator`(슬링샷 최대 거리 = 얇은 반투명 원 · 궁병 = 슬링샷 범위 ~ ×(1+비율) 채워진 붉은 도넛 · 범위 안 적이 없으면 회색).
|
||||||
- 보고는 실측·추정·미확인을 분리한다(C23 · C5). 커밋은 의도 변경 파일만.
|
- 보고는 실측·추정·미확인을 분리한다(C23 · C5). 커밋은 의도 변경 파일만.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -395,7 +395,10 @@ PlayerSettings:
|
||||||
m_BuildTargetShaderSettings: []
|
m_BuildTargetShaderSettings: []
|
||||||
m_BuildTargetGraphicsJobs: []
|
m_BuildTargetGraphicsJobs: []
|
||||||
m_BuildTargetGraphicsJobMode: []
|
m_BuildTargetGraphicsJobMode: []
|
||||||
m_BuildTargetGraphicsAPIs: []
|
m_BuildTargetGraphicsAPIs:
|
||||||
|
- m_BuildTarget: WindowsStandaloneSupport
|
||||||
|
m_APIs: 0200000012000000
|
||||||
|
m_Automatic: 0
|
||||||
m_BuildTargetVRSettings: []
|
m_BuildTargetVRSettings: []
|
||||||
m_DefaultShaderChunkSizeInMB: 16
|
m_DefaultShaderChunkSizeInMB: 16
|
||||||
m_DefaultShaderChunkCount: 0
|
m_DefaultShaderChunkCount: 0
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ Unity 6000.3.23f1 · 2D · 도형 플레이스홀더(아트 리소스는 별도
|
||||||
| 보병 | 첫 적까지 이동 후 공격(반격 받음) | 추가 이동 1회(이동만) |
|
| 보병 | 첫 적까지 이동 후 공격(반격 받음) | 추가 이동 1회(이동만) |
|
||||||
| 기병 | 경로 위 모든 적 관통 피해 · 목표점까지 이동 | 제자리로 복귀 |
|
| 기병 | 경로 위 모든 적 관통 피해 · 목표점까지 이동 | 제자리로 복귀 |
|
||||||
| 창병 | 보병과 동일 | 대기 — 적군 턴에 반경 안에서 움직이는 적에게 피해 |
|
| 창병 | 보병과 동일 | 대기 — 적군 턴에 반경 안에서 움직이는 적에게 피해 |
|
||||||
| 궁병 | 목표 방향의 적을 향해 사거리까지 이동 후 사격(반격 없음) | 무조건 크리티컬 |
|
| 궁병 | 목표 방향 「슬링샷 거리 + 사거리」 안의 적을 향해 사거리까지 이동 후 사격(반격 없음) · 적이 없으면 목표점까지 이동 | 무조건 크리티컬 |
|
||||||
|
|
||||||
상성: 보병 > 창병 > 기병 > 보병. 궁병은 모두에게 약함(받는 피해 1.5배).
|
상성: 보병 > 창병 > 기병 > 보병. 궁병은 모두에게 약함(받는 피해 1.5배).
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue