Skip to content

Commit 6196347

Browse files
authored
Merge pull request #376 from Apptive-Game-Team/main
Deploy main to deploy
2 parents 7d0ea57 + 1619118 commit 6196347

26 files changed

Lines changed: 979 additions & 80 deletions

.github/workflows/deploy-itch.yml

Lines changed: 1 addition & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -65,38 +65,5 @@ jobs:
6565
- name: Push to Itch.io
6666
env:
6767
BUTLER_API_KEY: ${{ secrets.BUTLER_CREDENTIALS }}
68-
ITCH_USER_RAW: ${{ secrets.ITCH_USER }}
69-
ITCH_GAME_RAW: ${{ secrets.ITCH_GAME }}
7068
run: |
71-
itch_user="${ITCH_USER_RAW#https://}"
72-
itch_user="${itch_user#http://}"
73-
itch_user="${itch_user%%\?*}"
74-
itch_user="${itch_user%%\#*}"
75-
itch_user="${itch_user%/}"
76-
77-
if [[ "$itch_user" == itch.io/profile/* ]]; then
78-
itch_user="${itch_user#itch.io/profile/}"
79-
elif [[ "$itch_user" == *".itch.io"* ]]; then
80-
itch_user="${itch_user%%.itch.io*}"
81-
fi
82-
itch_user="${itch_user%%/*}"
83-
84-
itch_game="${ITCH_GAME_RAW#https://}"
85-
itch_game="${itch_game#http://}"
86-
itch_game="${itch_game%%\?*}"
87-
itch_game="${itch_game%%\#*}"
88-
itch_game="${itch_game%/}"
89-
90-
if [[ "$itch_game" == *".itch.io/"* ]]; then
91-
itch_game="${itch_game#*.itch.io/}"
92-
elif [[ "$itch_game" == */* ]]; then
93-
itch_game="${itch_game##*/}"
94-
fi
95-
itch_game="${itch_game%%:*}"
96-
97-
if [[ -z "$itch_user" || -z "$itch_game" ]]; then
98-
echo "Failed to parse ITCH_USER/ITCH_GAME secrets into a valid itch.io target."
99-
exit 1
100-
fi
101-
102-
./butler push build/WebGL/WebGL "${itch_user}/${itch_game}:webgl"
69+
./butler push build/WebGL/WebGL ${{ secrets.ITCH_USER }}/${{ secrets.ITCH_GAME }}:webgl
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
# 2026-07-13 — Fix 2.5D Selectable Hit Testing and Ground Projection Indicator
2+
3+
- Date: 2026-07-13
4+
- GitHub Issue: https://github.com/Apptive-Game-Team/WordOnlineClient/issues/350
5+
- Status: Implemented; manual Play Mode verification pending
6+
7+
## Goal
8+
9+
- Make pointer hover/click follow the visible `SpriteRenderer` reliably under the 2.5D camera, including rotated, scaled, animated, and y != 0 objects.
10+
- Make selection collider ownership deterministic instead of accidentally reusing an unrelated child collider.
11+
- Visualize the selected world position and its ground projection `(x, 0, z)` with a vertical line so the selected x/z coordinate is unambiguous.
12+
- Preserve the existing `CardInputSender` protocol while sending an object's actual world position and using `(x, 0, z)` only for ground visualization.
13+
14+
## Non-goals
15+
16+
- Do not change server DTOs, STOMP destinations, range calculations, or magic validation behavior.
17+
- Do not redesign the 2.5D camera or mass-rewrite prefab hierarchies.
18+
- Do not replace every gameplay collider with a new physics abstraction.
19+
- Do not change unrelated `feature/345` audio work or carry its dirty workspace changes into this branch.
20+
21+
## Context / Constraints
22+
23+
- Owning repository: `WordOnlineClient` (`client/`); no cross-repository contract change is expected.
24+
- Work branch: `fix/350`, based on local `main` at `70ce401`, in a dedicated Git worktree.
25+
- Current `Selectable.Awake()` finds a `SpriteRenderer`, creates or reuses a `BoxCollider` on the renderer GameObject, sizes it from `sprite.bounds`, and adds a `PhysicsRaycaster` to the camera.
26+
- Attaching the collider to the renderer GameObject should share rotation, so different tilt is a hypothesis, not a confirmed root cause. Actual ray hits, hierarchy, layer masks, animation, scale, and existing collider ownership must be observed before choosing a fix.
27+
- Existing dirty changes in the primary `feature/345` worktree overlap `Selectable.cs` and `FieldSelector.cs`; they are user-owned and must remain untouched.
28+
- Procedural indicators are rendered through `SkillIndicatorShapeRenderer`; reuse its material/sorting behavior where practical rather than restoring deleted indicator prefabs.
29+
- Main, tutorial, and debug/secondary selection flows must be checked. Only change secondary flows that reproduce or consume the same contract.
30+
- Unity versions documented in repo files disagree (`2022.3.34f1` in `AGENTS.md`, `2022.3.22f1` in `CLAUDE.md`); validate against `ProjectSettings/ProjectVersion.txt` before Editor testing.
31+
32+
## Approach (Checklist)
33+
34+
- [x] **Step 0: Recon** (Inspect existing code, locate files)
35+
- [ ] Reproduce in `GameScene` with at least one ground object and one y != 0 object; record which sprite regions miss hover/click. (Play Mode pending)
36+
- [x] Inspect `PhysicsRaycaster` setup, prefab object layers, renderer hierarchy, and current input boundary.
37+
- [x] Compare `Selectable`, `SpriteRenderer`, existing `BoxCollider2D`, and motion transforms in representative prefab YAML.
38+
- [x] Identify the unstable contract: a separate runtime `BoxCollider` used `sprite.bounds` once and did not track renderer-local bounds changes.
39+
- [x] Inspect `FieldSelector`, `TutorialFieldSelector`, `SkillIndicatorShapeRenderer`, and `LineSkillIndicator` to preserve existing procedural visual conventions.
40+
- [x] Confirm field clicks already produce ground coordinates while object clicks must preserve the selected object's world y.
41+
- [x] **Step 1: Implementation** (Code changes, file paths)
42+
- [x] Refactor `Assets/Scripts/GameScene/ServedObjectComponent/Selectable.cs` to own a dedicated `SelectableHitbox` child under the selected renderer.
43+
- [x] Synchronize collider center/size from `SpriteRenderer.localBounds` only when bounds change.
44+
- [x] Keep `PhysicsRaycaster` setup deterministic and warn when no renderer exists.
45+
- [x] Add `Assets/Scripts/GameScene/SelectionGroundIndicator.cs` to draw the selected position to `(x, 0, z)`.
46+
- [x] Connect hover lifecycle and hide the line on exit, click, disable, destroy, or owner replacement.
47+
- [ ] If the field aim cursor can represent y != 0, update `Assets/Scripts/GameScene/FieldSelector.cs` to keep world cursor and ground projection as separate values. Mirror only contract-equivalent behavior in `Assets/Scripts/TutorialScene/Battle/TutorialFieldSelector.cs`.
48+
- [x] Preserve actual object `(x, y, z)` input and keep ground projection local to the indicator.
49+
- [x] Restrict UI blocking to `GraphicRaycaster` hits so the `PhysicsRaycaster` ground hit does not disable field selection.
50+
- [x] Give `Selectable.OnPointerClick` priority over `FieldSelector` ground input when the pointer raycast hits a selectable object.
51+
- [ ] **Step 2: Tests** (Unit tests, manual verification steps)
52+
- [ ] Add Edit Mode tests under `Assets/Tests/` for collider target resolution and renderer-local bounds mapping if those rules can be isolated without scene dependencies.
53+
- [ ] Add tests for projection endpoints: `(x, y, z)` maps to `(x, 0, z)`, y == 0 hides or collapses the vertical line, and cleanup is idempotent.
54+
- [ ] In Unity, verify top/center/bottom clicks on rotated ground and airborne sprites, including during idle animation and after sprite replacement.
55+
- [ ] Verify overlapping sprite/collider cases select the expected object and UI pointer blocking remains unchanged.
56+
- [ ] Verify indicator endpoints retain identical x/z, correct world-space y, visible sorting, and cleanup across hover/click/cancel/disable/destroy.
57+
- [ ] Smoke-test tutorial/debug selection paths if recon finds shared behavior.
58+
- [ ] **Step 3: Rollout / Rollback** (Feature flags, migration steps)
59+
- [ ] Ship as client-only behavior with no migration or server release ordering.
60+
- [ ] Capture before/after Scene/Game view evidence for the PR and document tested prefab types.
61+
- [ ] Keep collider fix and indicator addition as separable commits when implementation boundaries remain clean.
62+
- [ ] Roll back through commit revert; no persistent data or protocol rollback required.
63+
64+
## Validation
65+
66+
- **Commands to run:**
67+
- `cat ProjectSettings/ProjectVersion.txt`
68+
- Unity Editor compile using the version declared by `ProjectSettings/ProjectVersion.txt`
69+
- Unity Test Runner Edit Mode/Play Mode tests added for issue #350
70+
- `git diff --check`
71+
- `git status --short`
72+
- **Expected output:**
73+
- Unity scripts compile without errors.
74+
- Added tests pass; no existing test regression.
75+
- Visible sprite regions reliably produce hover/click events in all listed manual cases.
76+
- y != 0 selection shows one line with endpoints `(x, y, z)` and `(x, 0, z)`; line disappears on every exit path.
77+
- Input payload remains compatible with existing `CardInputSender`/server expectations.
78+
- `git diff --check` prints no errors and diff contains only issue #350 files.
79+
80+
## Risks & Rollback
81+
82+
- **Risks:**
83+
- Enlarging or thickening colliders can make transparent sprite regions or overlapping objects capture unintended clicks.
84+
- Reusing gameplay colliders may change physics behavior; selection collider must remain trigger-only and ownership-specific.
85+
- Rebuilding bounds too often can add avoidable per-frame allocations/CPU cost across many objects.
86+
- LineRenderer/procedural indicator sorting may place the vertical line behind sprites or range meshes in the 2.5D camera.
87+
- Flattening `y` at the wrong boundary can change server targeting semantics; recon must confirm current contract.
88+
- Tutorial/debug flows may have different cursor lifecycles and should not be mechanically coupled without evidence.
89+
- **Rollback steps:**
90+
- Revert indicator commit independently if visual lifecycle/sorting regresses while keeping validated hit-testing fix.
91+
- Revert collider commit if click ordering or prefab physics regress.
92+
- No database, server, asset migration, or feature-flag cleanup is required.
93+
94+
## Open Questions
95+
96+
- Resolved: show the vertical line while hovering the active target, before input is committed.
97+
- Resolved: project to world `y = 0`, matching the requested acceptance baseline.
98+
- Resolved: object selection sends actual world y; only field selection and the visual ground endpoint use ground coordinates.
99+
- Which prefabs reproduce the miss most consistently? Record them during Editor recon and use them as the PR manual test matrix.
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
# 2026-07-17 — 서버 생성 오브젝트 등장 연출
2+
3+
- Date: 2026-07-17
4+
- GitHub Issue: #373
5+
- Status: Implemented (Play Mode visual QA pending)
6+
7+
## Goal
8+
9+
- 서버의 신규 생성 이벤트로 나타난 전투 오브젝트가 카메라 화면의 가로축(screen X)을 경첩 축으로 삼아, 팝업북 그림처럼 바닥에 납작하게 누운 상태에서 빠르게 일어나는 DOTween 등장 연출을 재생한다.
10+
- 건물류는 일어나는 동작의 착지 시점에 짧은 먼지구름을 생성하고 자연스럽게 확산·페이드아웃한다.
11+
- 기존 이동, 공격, idle DOTween 및 서버 동기화용 루트 Transform과 충돌하지 않도록 시각 계층만 애니메이션한다.
12+
13+
## Non-goals
14+
15+
- 투사체, Drop/Explode/Field/Rune 같은 단발성 마법 오브젝트에 등장 회전을 일괄 적용하지 않는다.
16+
- 서버 판정, 충돌체, 실제 위치 동기화, HP 바와 팀 표시의 좌표를 등장 연출에 종속시키지 않는다.
17+
- 이번 작업에서 전체 이펙트 풀링 시스템을 새로 만들지 않는다. 대량 생성 성능 문제가 측정되면 먼지 프리팹 풀링을 후속 작업으로 분리한다.
18+
19+
## Context / Constraints
20+
21+
- 신규 생성 경로는 `DeltaFrameHandler -> ObjectSpawner.SpawnObject`이며, 스냅샷 복원도 `ObjectSyncer -> ObjectSpawner.SpawnObject`를 공유한다.
22+
- `ObjectSpawner`는 이미 `PopupBookVisualPresenter.Attach(servedObject)`를 호출한다. 이 presenter는 `LateUpdate`에서 visual root를 `Camera.main.transform.rotation`으로 맞추므로, 그 하위 피벗의 `local X`는 항상 카메라 화면의 가로축과 일치한다.
23+
- `GameScene` 카메라는 Perspective이며 월드 X축 기준 약 45도 기울어져 있다. 따라서 월드 X 회전을 직접 적용하기보다 카메라 정렬 presenter 아래에서 local X를 회전해야 카메라 변경에도 팝업북 방향이 유지된다.
24+
- `ObjectSpawner``Resources/Prefabs/{type}`을 로드하며 유닛/건물 이외의 서버 오브젝트도 생성하므로 스포너에서 타입 문자열로 건물을 추측하면 유지보수가 어렵다.
25+
- 유닛과 건물 대부분은 `AbstractMeleeMob`, `AbstractRangeMob`, `AbstractAerialMob`, `AbstractSlime`, `AbstractBuild` 프리팹 Variant 계층을 사용한다. 연출 대상 여부와 세부 설정은 프리팹 컴포넌트가 명시하는 편이 현재 구조와 맞다.
26+
- 기존 `HoppingMotionController`, `CrawlMotionController` 등은 자식 Sprite Transform을 `Awake`부터 계속 움직인다. 등장 연출은 별도의 지면 피벗 wrapper를 회전해 같은 Transform 프로퍼티에 대한 tween 충돌을 피해야 한다.
27+
- 현재 Sprite 피벗은 대표적으로 중앙(`ElectricTower`: 0.5, 0.5)이어서 Sprite 자체를 90도 회전하면 지면에 눕는 대신 중앙을 축으로 돈다.
28+
- `SpawnPopInEffect`는 일부 마법 프리팹에서 `Awake` 재생되는 기존 스케일 연출이다. 이 동작은 유지하고 신규 유닛/건물 등장 연출과 대상을 분리한다.
29+
30+
## Approach (Checklist)
31+
32+
- [x] **Step 0: Recon**
33+
- [x] 실제 `AbstractBuild` 파생 prefab 목록을 기준으로 먼지 대상 건물 타입을 확정한다.
34+
- [x] 추상 프리팹의 visual root, shadow, HP bar 계층과 기존 `PopupBookVisualPresenter` 삽입 구조를 확인한다.
35+
- [x] 델타 신규 생성에서만 연출하고 스냅샷 복원은 생략한다.
36+
37+
- [x] **Step 1: Implementation**
38+
- [x] `PopupBookVisualPresenter.Attach``SpawnPresentationPivot`을 생성하고 actual visual을 그 아래에 배치하도록 확장했다.
39+
- [x] `logical root -> PopupBookVisualPresenter(camera aligned) -> SpawnPresentationPivot(local X tween) -> actual visual` 계층을 구성했다.
40+
- [x] `Attach`가 presenter를 반환하고 `ObjectSpawner` 등록 성공 뒤 `PlaySpawnPresentation`을 호출한다.
41+
- [x] Sprite local bounds 하단으로 경첩 오프셋을 계산하고 upright 위치가 보존되도록 상쇄한다.
42+
- [x] 카메라 정렬 root와 local X 등장 pivot을 분리했다.
43+
- [x] 카메라 기준 local X `-84° -> 0°`, 0.42초 `Ease.OutBack` 등장 연출을 구현했다.
44+
- [x] Tween 중단/파괴/완료 시 pivot 회전과 World Space UI 활성 상태를 복원한다.
45+
- [x] `BuildingSpawnDustEffect`가 런타임 소프트 원형 Sprite를 한 번 생성하고 6개 먼지 조각을 좌우 확산·확대·페이드한 뒤 스스로 제거한다.
46+
- [x] `SpawnPresentationTypeCatalog`에 현재 `AbstractBuild` 파생 타입과 구조물 형태의 `FrenzyTotem`, `RallyingTorch`를 격리했다.
47+
- [x] `ObjectSpawner.SpawnObject``playSpawnPresentation` 인자를 추가하고 `ObjectSyncer` 복원 경로는 false로 호출한다.
48+
49+
- [ ] **Step 2: Tests**
50+
- [x] `dotnet build Assembly-CSharp.csproj -v minimal` 컴파일 성공 및 Unity Asset Pipeline 스크립트 컴파일 오류 없음 확인.
51+
- [ ] Unity Edit Mode 테스트를 추가할 경우 `Assets/Tests/EditMode`에서 생성 사유에 따른 재생 여부와 컴포넌트 미존재 fallback을 검증한다.
52+
- [ ] Play Mode/Editor에서 근접, 원거리, 공중, slime, 건물, PvE 예외 프리팹을 좌/우 master 각각 생성한다.
53+
- [ ] 생성 직후 같은 프레임에 위치/HP/status 업데이트가 와도 루트 위치, collider, HP bar가 흔들리지 않는지 확인한다.
54+
- [ ] 공격/idle tween이 등장 연출 도중 시작돼도 자식 visual의 Z 회전·이동과 부모 pivot의 X 회전이 자연스럽게 합성되는지 확인한다.
55+
- [ ] 카메라가 45도인 현재 GameScene에서 화면 가로축을 따라 접히며, 월드축 기준 옆으로 쓰러지는 모션처럼 보이지 않는지 확인한다.
56+
- [ ] reconnect/스냅샷 복원 시 기존 전장 전체가 동시에 일어나는 연출을 하지 않는지 확인한다.
57+
- [ ] 건물을 연속 생성하고 파괴해 먼지 오브젝트와 DOTween sequence가 Hierarchy에 남지 않는지, WebGL 프레임 드롭이 없는지 확인한다.
58+
59+
- [ ] **Step 3: Rollout / Rollback**
60+
- [ ] 연출 파라미터는 프리팹 직렬화 값으로 조절 가능하게 해 코드 변경 없이 튜닝한다.
61+
- [ ] 문제 발생 시 각 프리팹의 `SpawnRiseEffect`를 disable하면 생성/동기화 로직은 그대로 동작하게 유지한다.
62+
- [ ] 최종 구현 전 GitHub issue를 생성하고 저장소 규칙에 맞는 `<issue-label>/<issue-num>` 브랜치로 작업한다.
63+
64+
## Validation
65+
66+
- **Commands to run:**
67+
- `dotnet build Assembly-CSharp.csproj -v minimal`
68+
- Unity Editor Play Mode 수동 생성 테스트(Delta create, Snapshot restore, 좌/우 master, 연속 건물 생성)
69+
- 가능하면 WebGL Development Build에서 먼지 sorting/성능 확인
70+
- **Expected output:**
71+
- C# 컴파일 오류 없음.
72+
- 선택된 유닛/건물만 1회 일어나며, 건물만 착지 먼지가 재생됨.
73+
- 서버 좌표, collider, shadow, HP bar 및 지속 idle tween에 시각적/기능적 회귀가 없음.
74+
75+
## Risks & Rollback
76+
77+
- **Risks:**
78+
- 중앙 피벗 Sprite를 직접 회전하면 바닥에서 미끄러지거나 공중에서 회전해 보일 수 있다.
79+
- 기존 무한 tween과 동일 Transform을 제어하면 DOTween 값 경쟁으로 점프/회전이 튈 수 있다.
80+
- Snapshot restore까지 자동 재생하면 입장/재접속 때 모든 오브젝트가 동시에 애니메이션된다.
81+
- Variant가 아닌 예외 prefab을 누락하면 일부 서버 오브젝트만 연출이 없다.
82+
- 먼지 sorting layer 또는 scale이 건물 크기별로 맞지 않을 수 있다.
83+
- **Rollback steps:** `SpawnRiseEffect` 컴포넌트를 프리팹에서 비활성화하거나 관련 커밋을 revert한다. 스포너는 컴포넌트 미존재를 정상 경로로 처리한다.
84+
85+
## Open Questions
86+
87+
- 스냅샷에서 처음 발견된 오브젝트도 등장 연출을 보여줄지, 오직 delta `objects.create`만 보여줄지 결정이 필요하다. 권장안은 delta만 재생이다.
88+
- 카메라 쪽으로 넘어져 있다가 뒤로 일어날지(`+X`), 카메라 반대쪽에서 앞으로 일어날지(`-X`) 결정이 필요하다. 팝업북 느낌에는 카메라 쪽으로 누웠다가 일어나는 한 방향 고정안을 권장한다.
89+
- 먼지 색상/크기를 모든 건물 공통으로 할지, 건물별 override가 필요한지 아트 방향 확인이 필요하다.
90+
- 최종 대상에 건물/소환수 외 `Rune`, `Field`, `Drop` 계열도 포함되는지 범위를 확정해야 한다.

Assets/Scenes/GameScene.unity

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4563,7 +4563,7 @@ Camera:
45634563
height: 1
45644564
near clip plane: 0.1
45654565
far clip plane: 1000
4566-
field of view: 35
4566+
field of view: 25
45674567
orthographic: 0
45684568
orthographic size: 5
45694569
m_Depth: -1
@@ -4590,7 +4590,7 @@ Transform:
45904590
m_GameObject: {fileID: 700072844}
45914591
serializedVersion: 2
45924592
m_LocalRotation: {x: 0.38268343, y: 0, z: 0, w: 0.92387956}
4593-
m_LocalPosition: {x: 9, y: 13.08, z: -8.9}
4593+
m_LocalPosition: {x: 9, y: 21, z: -16}
45944594
m_LocalScale: {x: 1, y: 1, z: 1}
45954595
m_ConstrainProportionsScale: 0
45964596
m_Children: []

Assets/Scripts/Admin/DebugPanelController.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
using GameScene;
44
using Global;
55
using UnityEngine;
6-
using UnityEngine.EventSystems;
76

87
namespace Admin
98
{
@@ -140,7 +139,7 @@ private void Update()
140139
if (Input.GetMouseButtonDown(0))
141140
{
142141
// Check if clicking on UI
143-
if (EventSystem.current.IsPointerOverGameObject()) return;
142+
if (PointerInputUtility.IsPointerOverUi()) return;
144143

145144
Vector3 worldPos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
146145
worldPos.z = 0;

0 commit comments

Comments
 (0)