Change hammer mechanic from throw to hit

This commit is contained in:
2026-06-22 16:34:46 +03:00
parent 21682f6af2
commit 5fbe7391d3
15 changed files with 39263 additions and 1328 deletions
File diff suppressed because it is too large Load Diff
@@ -1,7 +1,8 @@
fileFormatVersion: 2
guid: d17a53f2be7d3024099b8777f2cad4f3
TextScriptImporter:
guid: 338fe03d4e50c43408a233110291c45e
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 7400000
userData:
assetBundleName:
assetBundleVariant:
+6 -7
View File
@@ -8,7 +8,7 @@ The goal is to determine whether the current structure supports the intended gam
- tactical movement instead of combat
- puzzle-focused level design
- a **magical throwing hammer** as the main tool
- a **magical hammer hit** as the main tool
- trolls as slow, persistent pressure enemies
- treasure, chests, keys, and exit-based progression
- rising tension through timed troll spawns
@@ -373,19 +373,18 @@ or embed a clearly defined state enum and transitions into `TrollController`.
---
## 5.7 Hammer Logic Should Be More Than a Simple Throw Script
The current `AxeThrower.cs` / future `HammerThrower.cs` likely handles only basic projectile logic.
## 5.7 Hammer Logic Should Be More Than a Simple Hit Script
The current `AxeThrower.cs` / `HammerThrower.cs` likely handles only basic hit logic.
But the concept needs more behavior:
- cooldown after throw
- cooldown after use
- collision with walls
- collision with trolls
- destruction of fragile blocks
- impact feedback
- optional light knockback
- disappearance on impact
- return availability after cooldown
- availability after cooldown
### Required fix
The hammer should be treated as a gameplay subsystem, not just a small helper script.
@@ -394,7 +393,7 @@ Possible structure:
```text
Scripts/Player/HammerThrower.cs
Scripts/Projectiles/HammerProjectile.cs
Scripts/Combat/Hammer.cs
```
---
-681
View File
@@ -1,681 +0,0 @@
# Gnomes Bounty — Architecture Migration Plan for Coding Model
## 1. Purpose
This document is written for a coding model / AI coding agent that will work on the current Unity project for **Gnomes Bounty**.
The purpose is to guide the model through a **safe, incremental architecture migration** based on the **current project structure**, not an idealized rewrite.
The agent should use this file as an execution plan.
---
## 2. Main Goal
Refactor and extend the current Unity project so it better matches the intended game concept:
- 2D puzzle-platformer
- player uses a **throwing hammer**
- hammer can **stun enemies** and **break certain walls**
- player opens chests, finds a key, and exits through a door
- enemies should create pressure over time
- enemy AI should eventually react to **noise**
- the project structure should become cleaner without requiring a full rewrite
The goal is **not** to rebuild the game from scratch.
The goal is to **improve the current project safely**.
---
## 3. Current Project Context
The current Unity project already contains the following relevant files and systems:
- `PlayerController.cs`
- `Hammer.cs`
- `HammerThrower.cs`
- `EnemyAI.cs`
- `CharacterSpawner.cs`
- `BreakableWall.cs`
- `Chest.cs`
- `KeyChest.cs`
- `Door.cs`
- `DoorInteract.cs`
- `InputManager.cs`
- `UiManager.cs`
- `Character.cs`
- `CameraFollow.cs`
- `TreasureSO.cs`
- `MapElementSO.cs`
The project also includes folders and assets related to:
- controllers
- environment objects
- managers
- map elements
- scriptable objects
- prefabs
- scenes
- animations
- sprites
This means the project already has a prototype-level gameplay foundation.
---
## 4. Important Constraints for the Coding Model
### 4.1 General Rules
- Do **not** rewrite unrelated systems.
- Do **not** perform a full architecture rewrite.
- Prefer **small, safe, incremental changes**.
- Keep the project **Unity-friendly** and inspector-friendly.
- Preserve existing gameplay where possible.
- Keep naming consistent and readable.
- Avoid unnecessary abstractions.
- Use simple `MonoBehaviour` patterns unless something more advanced is clearly required.
### 4.2 Refactoring Rules
- If renaming files/classes, clearly list all renamed files.
- If Unity references may break because of renaming or moving scripts, explicitly mention required Unity setup steps.
- If a class is currently too generic, improve it gradually instead of replacing it immediately.
- Prefer **adding missing gameplay systems** over rewriting existing ones.
- If a task is risky, choose the safest implementation that preserves compatibility.
### 4.3 Output Rules
For each task, the coding model must return:
1. A short summary of what was changed
2. A list of files changed
3. Full code for every **new file**
4. Full code for every **modified file**
5. Unity setup instructions (if required)
6. Notes about assumptions, risks, or possible scene/prefab reference issues
---
## 5. Architecture Issues to Solve
The current project already aligns with the concept in several areas, but the following architectural gaps should be addressed:
1. `UiManager.cs` should be normalized to `UIManager.cs`
2. `HammerThrower.cs` and `Hammer.cs` should have clearly separated responsibilities
3. There is no explicit `GameManager.cs`
4. There is no explicit `LevelManager.cs`
5. There is no dedicated `NoiseSystem.cs`
6. `EnemyAI.cs` is likely too generic and should gradually support enemy states
7. Hammer gameplay should clearly support:
- cooldown
- projectile behavior
- stun
- destructible wall interaction
- self-destruction on impact
8. The chest → key → door → exit loop should be centralized and explicit
---
## 6. High-Level Migration Phases
---
## Phase 1 — Safe Cleanup and Structural Preparation
### Objective
Improve naming and separate responsibilities without changing game behavior too much.
### Tasks
1. Rename `UiManager.cs` to `UIManager.cs`
2. Review `Hammer.cs` and `HammerThrower.cs`
3. Ensure:
- `HammerThrower` handles throw input, direction, cooldown, and projectile spawn
- `Hammer` handles projectile movement, collision detection, stun, breakable wall interaction, and self-destruction
4. Review `EnemyAI.cs`
5. Do **not** deeply refactor `EnemyAI` yet
6. Keep all current gameplay working
### Expected Result
- cleaner naming
- clearer hammer responsibilities
- no major gameplay breakage
### Acceptance Criteria
- project compiles
- hammer still works
- no scene references are silently broken
---
## Phase 2 — Add Core Gameplay Managers
### Objective
Introduce missing orchestration for game state and level progression.
### Tasks
1. Create `GameManager.cs`
2. Create `LevelManager.cs`
3. `GameManager` should manage:
- whether the player has the key
- treasure count
- simple level complete action
- future extensibility for restart, game over, or scene transition
4. `LevelManager` should manage:
- key collection flow
- door unlock flow
- level completion request when player reaches exit
5. Integrate current `Door`, `DoorInteract`, `Chest`, and `KeyChest` systems with these managers
### Expected Result
The gameplay loop becomes explicit:
1. open chest
2. get key
3. unlock door
4. reach exit
5. complete level
### Acceptance Criteria
- when the player gets the key, game state updates correctly
- door unlocking is controlled through level logic
- reaching exit with the key triggers level completion logic
- project compiles without removing current gameplay
---
## Phase 3 — Implement Noise System
### Objective
Add one of the missing core mechanics from the design: **enemy reaction to noise**.
### Tasks
1. Create `NoiseSystem.cs`
2. Add a public API such as:
- `Emit(Vector3 position, float radius)`
3. Update `BreakableWall.cs` so breaking a wall emits noise
4. Update `Hammer.cs` so impact can emit noise
5. Update `EnemyAI.cs` so it can respond to noise events via a method such as:
- `OnNoise(Vector3 sourcePosition)`
6. Keep the implementation simple and compatible with the current project
### Expected Result
Enemy behavior becomes influenced by player actions.
### Acceptance Criteria
- breaking a wall emits noise
- hammer impact emits noise
- nearby enemies are notified
- enemies change behavior when hearing noise
- exposed tuning fields are available in Inspector where useful
---
## Phase 4 — Improve EnemyAI with States
### Objective
Make enemy behavior clearer, more predictable, and closer to the intended troll-like pressure behavior.
### Tasks
1. Update `EnemyAI.cs` to support a simple internal state model
2. Add states such as:
- `Patrol`
- `Investigate`
- `Chase`
- `Stunned`
3. Add logic for:
- reacting to noise
- changing target position when investigating
- entering a stunned state when hit by the hammer
- recovering from stun after a delay
4. Keep implementation simple
5. Do **not** create an overcomplicated AI framework
### Expected Result
Enemies behave in a controlled and readable way.
### Acceptance Criteria
- enemy can be stunned
- enemy can recover from stun
- enemy can investigate noise
- enemy can still chase or patrol if such behavior already exists
- code is maintainable and understandable
---
## Phase 5 — Finalize Hammer Gameplay Logic
### Objective
Make the hammer fully aligned with the intended design.
### Tasks
1. Update `HammerThrower.cs` to include:
- cooldown
- throw permission checks
- projectile spawning
2. Update `Hammer.cs` to include:
- collision with breakable walls
- collision with `EnemyAI`
- self-destruction on impact
- optional impact noise emission
3. Ensure the hammer:
- does not kill enemies
- can stun enemies
- can break only destructible walls
- disappears on impact
- becomes available again after cooldown
### Expected Result
The hammer becomes a proper tactical tool rather than a generic projectile.
### Acceptance Criteria
- hammer cannot be spammed without cooldown
- hammer breaks only valid destructible objects
- hammer stuns enemies
- hammer disappears on impact
- gameplay remains responsive
---
## Phase 6 — Integrate Chest → Key → Door Loop Properly
### Objective
Make the main level progression loop robust and explicit.
### Tasks
1. Review `Chest.cs` and `KeyChest.cs`
2. Ensure `KeyChest.cs` informs `GameManager` and/or `LevelManager` when key is collected
3. Update `Door.cs` so it clearly supports locked/unlocked state
4. Update `DoorInteract.cs` so player interaction is separate from progression logic
5. Ensure door completion checks are performed through `GameManager` and/or `LevelManager`
### Expected Result
The core objective loop becomes stable:
- open chest
- obtain key
- unlock or enable exit
- reach exit
- complete level
### Acceptance Criteria
- opening key chest grants key
- door unlocks only when appropriate
- player can finish level through door flow
- project compiles and current gameplay remains functional
---
## Phase 7 — Optional Folder and Naming Cleanup
### Objective
Improve long-term maintainability without breaking the current project.
### Tasks
1. Consider moving scripts into clearer folders, for example:
- `HammerThrower.cs` → player-related folder
- `Hammer.cs` → projectile or combat-related folder
- `EnemyAI.cs` → enemy-related folder
- `BreakableWall.cs` → environment or world-related folder
2. Rename `CharacterSpawner.cs` to `EnemySpawner.cs` **only if** it is truly enemy-specific
3. Do not move or rename files unless the required Unity follow-up steps are clearly documented
### Expected Result
The project structure becomes easier to understand and extend.
### Acceptance Criteria
- structure becomes clearer
- moved scripts still work in Unity
- all renames/moves are documented
---
## 7. Detailed Task Backlog for the Coding Model
Each task below should preferably be executed in a separate interaction or small batch.
---
## Task 1 — Normalize UI Manager Naming
### Instruction
Rename `UiManager.cs` to `UIManager.cs` and update the class name accordingly.
Check for all references in the project and update them if necessary.
Do not change any unrelated logic unless required for compilation.
### Deliverables
- renamed file
- updated class name
- note about scene/prefab references if Unity reattachment is required
### Definition of Done
- project compiles
- no broken references remain undocumented
---
## Task 2 — Separate Hammer Throw Logic and Hammer Projectile Logic
### Instruction
Review `HammerThrower.cs` and `Hammer.cs`.
Refactor them so:
- `HammerThrower` manages player input integration, throw direction, throw permission checks, cooldown, and projectile spawn
- `Hammer` manages projectile movement, collision detection, stun application, destructible wall interaction, optional noise emission, and self-destruction on impact
Do not remove existing functionality unless replacing it with equivalent or better behavior.
### Deliverables
- full updated `HammerThrower.cs`
- full updated `Hammer.cs`
- short explanation of each class responsibility
### Definition of Done
- hammer still throws correctly
- responsibilities are clearly separated
---
## Task 3 — Add GameManager
### Instruction
Create a new `GameManager.cs` MonoBehaviour singleton.
It should manage:
- whether the player has the key
- treasure count
- a basic level completion action
- future-friendly structure for restart or scene transition
Keep implementation simple and compatible with the current project.
### Deliverables
- new `GameManager.cs`
- explanation of where to place it in the scene
- any required setup steps
### Definition of Done
- other systems can check key ownership via `GameManager`
- treasure is tracked centrally
---
## Task 4 — Add LevelManager
### Instruction
Create a new `LevelManager.cs` MonoBehaviour.
It should manage:
- response to key collection
- door unlock flow
- level completion request when the player reaches exit
Integrate it with `Door.cs`, `DoorInteract.cs`, `Chest.cs`, and/or `KeyChest.cs` as needed.
### Deliverables
- new `LevelManager.cs`
- updated related files if required
- integration notes
### Definition of Done
- level progression logic is centralized
- door flow is no longer spread across unrelated classes
---
## Task 5 — Add Noise System
### Instruction
Create a simple `NoiseSystem.cs` singleton for noise events in the world.
Requirements:
- expose a public method to emit noise using world position and radius
- detect nearby enemies
- notify them through a method like `OnNoise(Vector3 sourcePosition)`
Integrate the system with:
- `Hammer.cs`
- `BreakableWall.cs`
- `EnemyAI.cs`
### Deliverables
- new `NoiseSystem.cs`
- updated `Hammer.cs`
- updated `BreakableWall.cs`
- updated `EnemyAI.cs` with a basic noise reaction entry point
### Definition of Done
- gameplay events can emit noise
- nearby enemies can react to that noise
---
## Task 6 — Add Enemy States to EnemyAI
### Instruction
Refactor `EnemyAI.cs` to support a simple internal state system.
Required states:
- `Patrol`
- `Investigate`
- `Chase`
- `Stunned`
Required behaviors:
- change target or focus when hearing a noise
- enter stunned state when hit by hammer
- recover from stun after delay
Do not overengineer.
### Deliverables
- full updated `EnemyAI.cs`
- brief explanation of state transitions
### Definition of Done
- enemies respond to noise
- enemies can be stunned and recover
- code stays readable
---
## Task 7 — Add Hammer Cooldown and Tactical Rules
### Instruction
Update hammer gameplay to fully support the intended tactical role.
Requirements:
- player cannot throw continuously without cooldown
- hammer disappears on impact
- hammer stuns enemies but does not kill them
- hammer breaks only destructible walls
- hammer impact may emit noise
- all tunable values should be inspector-friendly where appropriate
### Deliverables
- updated `HammerThrower.cs`
- updated `Hammer.cs`
- notes about tuning values exposed in Inspector
### Definition of Done
- hammer behavior matches design
- gameplay is tunable without code changes
---
## Task 8 — Integrate KeyChest with Game State
### Instruction
Update `KeyChest.cs` so that opening it informs `GameManager` and/or `LevelManager` that the key has been collected.
This must trigger:
- key ownership update
- door unlock flow or unlock eligibility
Do not break the existing chest interaction flow.
### Deliverables
- updated `KeyChest.cs`
- updated `Chest.cs` if needed
- scene setup notes if references are required
### Definition of Done
- opening the key chest updates game state correctly
---
## Task 9 — Improve Door Flow
### Instruction
Refactor `Door.cs` and `DoorInteract.cs` so that:
- `Door` owns locked/unlocked state
- `DoorInteract` handles player interaction only
- final completion checks are performed through `GameManager` and/or `LevelManager`
### Deliverables
- updated `Door.cs`
- updated `DoorInteract.cs`
- explanation of class responsibilities
### Definition of Done
- door lock state is explicit
- player interaction is separated from progression logic
---
## Task 10 — Optional Folder Cleanup Plan
### Instruction
Propose and optionally apply a safer folder structure cleanup for the current scripts.
Suggested direction:
- player-related scripts in a player or controllers folder
- enemy scripts in an enemies folder
- hammer/projectile scripts in a combat or projectiles folder
- managers in a managers folder
- environment/world scripts in an environment or world folder
Do not move files blindly.
If there is risk to Unity references, provide a move plan instead of applying changes immediately.
### Deliverables
- either actual safe file moves
- or a documented migration plan for folder cleanup
### Definition of Done
- the structure becomes clearer or a safe future move plan is documented
---
## 8. Recommended Execution Order
The coding model should process the tasks in the following order:
1. Task 1 — Normalize UI manager naming
2. Task 2 — Separate hammer throw logic and projectile logic
3. Task 3 — Add `GameManager`
4. Task 4 — Add `LevelManager`
5. Task 8 — Integrate `KeyChest` with game state
6. Task 9 — Improve door flow
7. Task 5 — Add `NoiseSystem`
8. Task 6 — Add enemy states to `EnemyAI`
9. Task 7 — Finalize hammer cooldown and tactical rules
10. Task 10 — Optional folder cleanup
This order is intentionally incremental and low risk.
---
## 9. Prompt Template for the Coding Model
Use the following prompt template when sending a single task to a coding model:
```text
You are working on an existing Unity 2D project called Gnomes Bounty.
Important constraints:
- Do not rewrite the entire architecture.
- Make only focused, safe, incremental changes.
- Keep existing gameplay working if possible.
- Return full code for every changed file.
- Return full code for every new file.
- Clearly list renamed files and any Unity Inspector setup steps.
- Prefer simple MonoBehaviour-based design over overengineering.
Current project context:
- The player uses a throwing hammer.
- The hammer can stun enemies and break certain walls.
- The level loop is chest -> key -> door -> exit.
- Enemies should later react to noise.
- Existing files include PlayerController, Hammer, HammerThrower, EnemyAI, BreakableWall, Chest, KeyChest, Door, DoorInteract, InputManager, UiManager, CharacterSpawner.
Task:
[INSERT ONE TASK FROM THIS DOCUMENT]
Expected output:
1. Summary of changes
2. List of files changed
3. Full code for each new file
4. Full code for each updated file
5. Unity setup instructions
6. Notes about any risks or assumptions
```
---
## 10. Recommended AI Session Breakdown
To reduce risk, do **not** ask the coding model to execute the whole migration in one response.
Preferred breakdown:
### Session 1
- Task 1
- Task 2
### Session 2
- Task 3
- Task 4
- Task 8
- Task 9
### Session 3
- Task 5
- Task 6
- Task 7
### Session 4
- Task 10
This keeps refactoring controlled and easier to review.
---
## 11. Final Guidance for the Coding Model
When performing these tasks, always prefer:
- compatibility over elegance
- safe migration over aggressive refactor
- explicit inspector setup over hidden assumptions
- readable MonoBehaviour code over advanced patterns
The project already has a working prototype base.
The purpose of this plan is to evolve it into a cleaner architecture that matches the game concept more closely.
Do not replace the existing project with a theoretical “perfect architecture”.
Improve the real project incrementally.
---
## 12. Desired End State
After completing the migration, the project should have:
- clean hammer throw + projectile separation
- a central `GameManager`
- a central `LevelManager`
- a simple but functional `NoiseSystem`
- enemies that can react to noise and become stunned
- a stable chest → key → door → exit flow
- clearer responsibilities between gameplay systems
- a cleaner and more maintainable Unity architecture
This is the target state for the current project.
@@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: 690a6b37d2b97044c8a4ecda9dce6e90
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+5 -5
View File
@@ -29,16 +29,16 @@ Each level is a compact, self-contained challenge where the player must collect
---
### 3.2 Magical Throwing Hammer
### 3.2 Magical Hammer Strike
The gnome wields a **runic enchanted throwing hammer**.
The gnome wields a **runic enchanted hammer** used to strike enemies and fragile blocks.
**Properties:**
- Thrown in a straight line
- Hits in a forward direction
- Stuns trolls on impact
- Breaks fragile blocks
- Disappears on impact
- Returns after a cooldown
- Applies impact feedback on contact
- Has a cooldown before the next hit
- Cannot kill enemies
**Impact Effects:**
-309
View File
@@ -1,309 +0,0 @@
# Architecture Migration - Completion Summary
## Overview
All 10 tasks from the ArchitectureMigration.md plan have been successfully completed. The Gnome's Bounty project now has cleaner architecture, improved separation of concerns, and new core gameplay systems.
---
## Changes Made by Task
### Task 1 ✓ - Normalize UI Manager Naming
**Files Changed:**
- `UiManager.cs``UIManager.cs` (file and class renamed)
**What Changed:**
- Renamed file and class to follow proper C# naming convention (UIManager instead of UiManager)
- No breaking changes - internal logic untouched
---
### Task 2 ✓ - Separate Hammer Throw Logic and Hammer Projectile Logic
**Files Changed:**
- `HammerThrower.cs` (refactored)
- `Hammer.cs` (refactored)
**What Changed:**
- **HammerThrower.cs**:
- Added cooldown system (`_throwCooldown`, `_cooldownTimer`)
- Added `CanThrow` property to check if ready
- New `TryThrowHammer()` method that respects cooldown
- Calls `Hammer.Initialize()` to set direction and speed
- Default cooldown: 1.5 seconds
- **Hammer.cs**:
- Added `Initialize(bool facingRight, float speed)` method
- Improved collision detection with different object types
- Stuns enemies on collision (`OnHitByHammer`)
- Breaks BreakableWall objects
- Emits noise on impact (integrates with NoiseSystem)
- Self-destructs on any collision
- Default lifespan: 5 seconds
---
### Task 3 ✓ - Add GameManager
**File Created:**
- `Managers/GameManager.cs`
**Functionality:**
- Singleton managing global game state
- Tracks key ownership: `HasKey`, `SetKeyState(bool)`
- Tracks treasure count: `TreasureCount`, `AddTreasure(int)`
- Events: `OnKeyStateChanged`, `OnTreasureCountChanged`, `OnLevelComplete`
- `CompletLevel()` method for level completion
- `Reset()` for resetting game state
- Debug mode for logging
**Setup Required:**
- Add empty GameObject with GameManager script to scene
- Component will auto-persist with `DontDestroyOnLoad`
---
### Task 4 ✓ - Add LevelManager
**File Created:**
- `Managers/LevelManager.cs`
**Functionality:**
- Singleton managing level progression
- Tracks key collection and door unlock
- Subscribes to GameManager key state changes
- `NotifyKeyCollected()` - triggered when player gets key
- `NotifyLevelComplete()` - triggered when player exits with key
- Events: `OnKeyCollected`, `OnDoorUnlocked`, `OnLevelComplete`
- `Reset()` for restarting level
- Debug mode for logging
**Setup Required:**
- Add empty GameObject with LevelManager script to scene
- In Inspector: Assign Door reference to the `_doorReference` field
---
### Task 5 ✓ - Add NoiseSystem
**Files Created/Modified:**
- `Managers/NoiseSystem.cs` (new)
- `EnvironmentObjects/BreakableWall.cs` (updated)
- `Hammer.cs` (already includes noise emission)
**Functionality:**
- Singleton noise emission system
- `Emit(Vector3 position, float radius)` - emits noise and alerts nearby enemies
- Automatically detects enemies on "Enemy" layer
- Calls `OnNoise(Vector3 position)` on all nearby enemies
- Event: `OnNoiseEmitted` for external systems
**Setup Required:**
- Add empty GameObject with NoiseSystem script to scene
- Ensure all enemy GameObject have "Enemy" layer assigned
---
### Task 6 ✓ - Add Enemy States to EnemyAI
**File Modified:**
- `EnemyAI.cs` (completely refactored)
**Functionality:**
- State machine with 4 states: `Patrol`, `Investigate`, `Chase`, `Stunned`
- Automatic state transitions based on distance to player
- `OnNoise(Vector3 position)` - enters Investigate state
- `OnHitByHammer(float stunDuration)` - enters Stunned state and recovers after duration
- Configurable ranges and speeds via Inspector
- Inspector fields:
- `_patrolSpeed`: Speed during patrol
- `_patrolRange`: How far to patrol
- `_investigateRange`: Range to trigger investigate
- `_chaseRange`: Range to trigger chase
- `_stunDuration`: How long to stay stunned (default: 1 second)
**Behavior:**
- Patrols back and forth when at peace
- Investigates noise sources when hearing sound
- Chases player when within range
- Recovers from stun and resumes normal behavior
---
### Task 7 ✓ - Finalize Hammer Cooldown and Tactical Rules
**Files Modified:**
- `PlayerController.cs` (updated to use new hammer API)
- `HammerThrower.cs` (already includes cooldown)
- `Hammer.cs` (already includes tactical rules)
**What Changed:**
- PlayerController now checks `_hammerThrower.CanThrow` before playing throw animation
- Calls `TryThrowHammer()` instead of `ThrowHammer()`
- Hammer impacts emit noise automatically (configurable)
- Hammer stuns enemies without killing them
- Hammer breaks only BreakableWall objects
- Hammer disappears on impact
**Tunable Values (Inspector):**
- HammerThrower: `_throwCooldown`, `_throwSpeed`
- Hammer: `_lifespan`, `_stunDuration`, `_impactNoiseRadius`, `_emitNoiseOnImpact`
---
### Task 8 ✓ - Integrate KeyChest with Game State
**Files Modified:**
- `KeyChest.cs` (updated)
- `Chest.cs` (updated)
**What Changed:**
- KeyChest now notifies GameManager when key collected
- KeyChest triggers LevelManager to unlock door
- Chest properly handles coin and key treasures
- Both systems integrate with GameManager for centralized state tracking
- Events flow: Chest/KeyChest → GameManager → LevelManager → Door
---
### Task 9 ✓ - Improve Door Flow
**Files Modified:**
- `Door.cs` (refactored)
- `DoorInteract.cs` (refactored)
**What Changed:**
- **Door.cs**:
- Added explicit `IsLocked` state property
- `OpenDoor()` now updates lock state, visuals, and collision
- `LockDoor()` can re-lock the door if needed
- Event: `OnDoorOpened`
- **DoorInteract.cs**:
- Checks GameManager.HasKey instead of PlayerState
- Triggers LevelManager.NotifyLevelComplete() when player exits with key
- Prevents multiple completions with `_hasTriggered`
- Debug logging for testing
**Progression Flow:**
1. Player collects key → KeyChest → GameManager.SetKeyState(true)
2. GameManager triggers → LevelManager.NotifyKeyCollected()
3. LevelManager unlocks → Door.OpenDoor()
4. Player reaches exit → DoorInteract triggers
5. DoorInteract → LevelManager.NotifyLevelComplete()
---
### Task 10 ✓ - Folder Cleanup
**Folders Created:**
- `Assets/Scripts/Player/` - Player-related scripts
- `Assets/Scripts/Combat/` - Combat/projectile scripts
- `Assets/Scripts/Enemies/` - Enemy AI and spawning
- `Assets/Scripts/Environment/` - Environmental objects
- `Assets/Scripts/Utilities/` - Helper and utility classes
**Files Moved:**
- **Player/**: PlayerController, HammerThrower, PlayerState
- **Combat/**: Hammer
- **Enemies/**: EnemyAI, EnemySpawner (renamed from CharacterSpawner)
- **Environment/**: BreakableWall, Chest, Door, DoorInteract, KeyChest, MapElements/
- **Managers/**: GameManager, LevelManager, NoiseSystem, UIManager, InputManager (unchanged location)
- **ScriptableObject/**: TreasureSO, MapElementSO (unchanged location)
- **Utilities/**: Character, CameraFollow, IDoor, Enums, InputActions, MainMenu
---
## Scene Setup Checklist
To get the project running after these changes:
- [ ] **Add GameManager to Scene**
- Create empty GameObject
- Add `GameManager` component
- Leave Debug Mode off for release
- [ ] **Add LevelManager to Scene**
- Create empty GameObject
- Add `LevelManager` component
- **In Inspector:** Assign the Door GameObject to `_doorReference` field
- Leave Debug Mode off for release
- [ ] **Add NoiseSystem to Scene**
- Create empty GameObject
- Add `NoiseSystem` component
- Ensure all enemy GameObjects have "Enemy" layer assigned
- Leave Debug Mode off for release
- [ ] **Update Enemy Layer**
- Select all enemy GameObjects
- Set Layer to "Enemy" (create if doesn't exist)
- This is critical for NoiseSystem to detect enemies
- [ ] **Update Scene References**
- Check for any scripts that directly reference moved classes
- Most will auto-resolve if using GetComponent<>()
- Update Inspector if scripts are assigned as references
- [ ] **Test the Flow:**
1. Player throws hammer (with cooldown)
2. Hammer hits enemy - enemy should stun and nearby enemies should hear noise
3. Player collects key - GameManager updates, door should unlock
4. Player exits through door - level completion triggered
---
## Key Architectural Improvements
1. **Clear Separation of Concerns**
- Hammer throwing vs. hammer behavior
- Player controller vs. player state vs. hammer equipment
- Enemy AI vs. enemy spawning
2. **Centralized Game State**
- GameManager handles all global state
- LevelManager orchestrates progression
- No scattered state management
3. **Event-Driven Architecture**
- Systems communicate via events
- Loose coupling between systems
- Easy to add new features
4. **Extensible Gameplay Systems**
- Noise system can be expanded for more sounds
- Enemy states make new behaviors easy to add
- Hammer can be enhanced with additional effects
5. **Inspector-Friendly**
- Tunable parameters for game feel
- Debug modes for testing
- Clear assignment fields
---
## Files Summary
**New Files (3):**
- `Managers/GameManager.cs`
- `Managers/LevelManager.cs`
- `Managers/NoiseSystem.cs`
**Modified Files (9):**
- `Managers/UIManager.cs` (renamed from UiManager)
- `Player/HammerThrower.cs` (refactored)
- `Combat/Hammer.cs` (refactored)
- `Enemies/EnemyAI.cs` (refactored)
- `Environment/BreakableWall.cs` (updated)
- `Environment/Chest.cs` (updated)
- `Environment/KeyChest.cs` (updated)
- `Environment/Door.cs` (refactored)
- `Environment/DoorInteract.cs` (refactored)
**Renamed:**
- `CharacterSpawner.cs``EnemySpawner.cs`
**Moved (folder reorganization only, no content changes):**
- Multiple scripts distributed into logical folders
---
## Notes
- All existing gameplay is preserved
- The changes follow Unity best practices
- Systems are designed to be maintainable and extensible
- Debug modes can be disabled before release
- No major breaking changes - mostly additive improvements
**Status:****All tasks complete and ready for testing**
@@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: ef7a187ce6bfdac4981745e075218235
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
-142
View File
@@ -1,142 +0,0 @@
# Quick Setup Guide - Post Migration
## Immediate Next Steps
### 1. Refresh Unity Project
- Open the Gnome's Bounty project in Unity
- Wait for asset import to complete
- Check Console for any errors related to moved files
### 2. Create Required GameObjects in Scene
**Add these to your main gameplay scene:**
#### GameManager
1. Right-click in Hierarchy → Create Empty
2. Name it "GameManager"
3. Drag GameManager script from `Assets/Scripts/Managers/GameManager.cs` onto it
4. Leave settings at defaults (debug mode optional)
#### LevelManager
1. Right-click in Hierarchy → Create Empty
2. Name it "LevelManager"
3. Drag LevelManager script from `Assets/Scripts/Managers/LevelManager.cs` onto it
4. **IMPORTANT:** In Inspector, drag your Door GameObject to the `_doorReference` field
5. Leave settings at defaults (debug mode optional)
#### NoiseSystem
1. Right-click in Hierarchy → Create Empty
2. Name it "NoiseSystem"
3. Drag NoiseSystem script from `Assets/Scripts/Managers/NoiseSystem.cs` onto it
4. Enemy Layer should be set to "Enemy" automatically
5. Leave settings at defaults (debug mode optional)
### 3. Configure Layers
1. In Hierarchy, select all enemy GameObjects
2. In Inspector, change Layer to "Enemy"
3. Create the layer if it doesn't exist (Layer dropdown → Add Layer → "Enemy")
### 4. Testing Checklist
After setup, test these features:
- [ ] **Hammer Throw**: Throw hammer and verify cooldown works (should delay next throw by ~1.5 sec)
- [ ] **Enemy Stun**: Hit an enemy with hammer, it should freeze for 1 second
- [ ] **Wall Break**: Break a breakable wall with hammer
- [ ] **Noise System**: Break a wall or hit enemy near another enemy - second enemy should investigate
- [ ] **Key Collection**: Pick up key, GameManager should show `HasKey = true`
- [ ] **Door Unlock**: After picking up key, door should open automatically
- [ ] **Level Complete**: Exit through open door with key - should trigger level completion
### 5. If Compilation Errors Occur
**Most likely causes:**
1. **"Cannot find type 'GameManager'"** - Make sure GameManager component is added to scene
2. **"Missing script references"** - Delete and re-add the script component
3. **"Cannot find EnemyAI"** - Verify EnemyAI.cs moved to `Enemies/` folder correctly
**Quick fix:**
- In Unity, go to Assets → Reimport All
- Wait for compilation to complete
- Check Console for remaining errors
### 6. Debug Features
To enable debug logging in managers:
1. Select GameManager in Hierarchy
2. In Inspector, check `Debug Mode`
3. Repeat for LevelManager and NoiseSystem
4. Play game - console will show state changes
5. **Remember to disable before final build**
---
## What If You Encounter Errors?
### Scenes Won't Load
- Unity may need to reload scenes
- Close the scene, delete from Recent, and re-open
### "CharacterSpawner" not found
- This class was renamed to `EnemySpawner`
- Update any scene prefabs or scripts that reference it
- Check `Enemies/EnemySpawner.cs`
### PlayerController Errors
- Check that `GetComponent<HammerThrower>()` can still find it
- If not, re-add PlayerController script to Player GameObject
### Missing References in Inspector
- Right-click the field → "Try Find Component"
- Or manually drag the object from scene/hierarchy
---
## Performance Notes
The new systems have minimal performance impact:
- **GameManager**: O(1) operations, just stores state
- **LevelManager**: Minimal, just tracks key/completion
- **NoiseSystem**: O(n) where n = enemies in range (typically small)
- **EnemyAI States**: Simple state switching, no AI overhead
No performance concerns with default settings.
---
## Next Development Steps
With the architecture in place, you can now easily:
1. **Add new hammer effects** - Modify Hammer.cs
2. **Add new enemy behaviors** - Add states to EnemyAI.cs
3. **Add puzzles** - Create new interaction systems using GameManager
4. **Add levels** - Use GameManager/LevelManager for progression
5. **Add UI** - Hook into GameManager events for HUD updates
All systems use events for loose coupling, making extensions clean and safe.
---
## File Location Reference
| What | Location |
|------|----------|
| Player Controller | `Assets/Scripts/Player/PlayerController.cs` |
| Hammer Mechanics | `Assets/Scripts/Combat/Hammer.cs` |
| Hammer Throwing | `Assets/Scripts/Player/HammerThrower.cs` |
| Enemy AI | `Assets/Scripts/Enemies/EnemyAI.cs` |
| Enemy Spawner | `Assets/Scripts/Enemies/EnemySpawner.cs` |
| Game State | `Assets/Scripts/Managers/GameManager.cs` |
| Level Control | `Assets/Scripts/Managers/LevelManager.cs` |
| Noise System | `Assets/Scripts/Managers/NoiseSystem.cs` |
| UI Manager | `Assets/Scripts/Managers/UIManager.cs` |
| Environment | `Assets/Scripts/Environment/*.cs` |
---
**Status:** Ready to import into Unity and test! 🎮
+51 -53
View File
@@ -2,79 +2,85 @@ using UnityEngine;
public class Hammer : MonoBehaviour
{
[SerializeField] private float _lifespan = 5f;
[SerializeField] private float _stunDuration = 1f;
[SerializeField] private float _impactNoiseRadius = 10f;
[SerializeField] private bool _emitNoiseOnImpact = true;
public event System.Action OnReturnedToHand;
private float _lifeTimer;
private Vector2 _velocity;
private bool _facingRight;
private Rigidbody2D _rigidbody;
private bool _hasCollided = false;
private bool _isThrown = false;
private Rigidbody2D _rigidbody;
private Collider2D _collider;
private void Awake()
{
_rigidbody = GetComponent<Rigidbody2D>();
_lifeTimer = _lifespan;
}
public void Initialize(bool facingRight, float speed)
{
_facingRight = facingRight;
_velocity = new Vector2(facingRight ? speed : -speed, 0);
_isThrown = true;
if (_rigidbody != null)
{
_rigidbody.linearVelocity = _velocity;
_rigidbody.angularVelocity = 0f;
}
}
private void Update()
{
// Self-destruct after lifespan expires
_lifeTimer -= Time.deltaTime;
if (_lifeTimer <= 0)
{
Destroy(gameObject);
}
_collider = GetComponent<Collider2D>();
}
private void OnCollisionEnter2D(Collision2D collision)
{
Debug.Log($"Hammer collided with {collision.gameObject.name} via OnCollisionEnter2D");
if (_hasCollided)
return;
_hasCollided = true;
ProcessHit(collision.collider, collision.relativeVelocity, collision.GetContact(0).point);
}
private void OnTriggerEnter2D(Collider2D collider)
{
Debug.Log($"Hammer collided with {collider.gameObject.name} via OnTriggerEnter2D");
if (_hasCollided)
return;
_hasCollided = true;
ProcessHit(collider, Vector2.zero, collider.transform.position);
}
private void FixedUpdate()
{
if (_hasCollided || _rigidbody == null || _collider == null)
return;
var hits = Physics2D.OverlapBoxAll(_collider.bounds.center, _collider.bounds.size, 0f);
foreach (var hit in hits)
{
if (hit == _collider)
continue;
var wall = hit.GetComponent<BreakableWall>();
if (wall != null)
{
Debug.Log($"Hammer overlapped with {hit.gameObject.name} in FixedUpdate");
_hasCollided = true;
ProcessHit(hit, Vector2.zero, hit.bounds.center);
break;
}
}
}
private void ProcessHit(Collider2D collider, Vector2 impactVelocity, Vector2 contactPoint)
{
// Check for enemy collision (stun)
var enemy = collision.gameObject.GetComponent<Character>();
var enemy = collider.gameObject.GetComponent<Character>();
if (enemy != null)
{
HandleEnemyCollision(enemy, collision.relativeVelocity);
EmitImpactNoise(collision.GetContact(0).point);
ReturnToHand();
HandleEnemyCollision(enemy, impactVelocity);
EmitImpactNoise(contactPoint);
return;
}
// Check for breakable wall collision
var mapElement = collision.collider.GetComponent<MapElement>();
var mapElement = collider.GetComponent<MapElement>();
if (mapElement != null && mapElement is BreakableWall)
{
mapElement.Hit();
EmitImpactNoise(collision.GetContact(0).point);
ReturnToHand();
EmitImpactNoise(contactPoint);
return;
}
// Fallback: return hammer on any collision
EmitImpactNoise(collision.GetContact(0).point);
ReturnToHand();
// Fallback: hit something else
EmitImpactNoise(contactPoint);
}
private void HandleEnemyCollision(Character enemy, Vector2 impactVelocity)
@@ -83,7 +89,7 @@ public class Hammer : MonoBehaviour
var enemyAI = enemy as EnemyAI;
if (enemyAI != null)
{
enemyAI.OnHitByHammer(_stunDuration);
// enemyAI.OnHitByHammer(_stunDuration);
}
}
@@ -99,13 +105,5 @@ public class Hammer : MonoBehaviour
noiseSystem.Emit(position, _impactNoiseRadius);
}
}
private void ReturnToHand()
{
_isThrown = false;
if (OnReturnedToHand != null)
{
OnReturnedToHand.Invoke();
}
}
}
+1 -11
View File
@@ -3,7 +3,6 @@ using UnityEngine;
public class PlayerController : Character
{
[SerializeField] private HammerThrower _hammerThrower;
private GameObject _hammer;
private bool _isHoldingHammer = true;
@@ -61,19 +60,10 @@ public class PlayerController : Character
private void OnFireButtonPressed()
{
if (_hammerThrower.CanThrow)
{
_animator.SetTrigger("Body_ThrowHammer");
}
_animator.SetTrigger("Body_ThrowHammer");
}
// Animation event
public void ThrowHammerObject()
{
_hammerThrower.TryThrowHammer();
}
protected override void SetWalkingAnimation(bool isWalking)
{
_animator.SetBool("Legs_Walk", isWalking);
-102
View File
@@ -1,102 +0,0 @@
using System;
using UnityEngine;
public class HammerThrower : MonoBehaviour
{
[SerializeField] private Transform _spawnPoint;
[SerializeField] private GameObject hammerInHand;
[SerializeField] private float _throwSpeed = 5f;
[SerializeField] private float _throwCooldown = 1.5f;
[SerializeField] private Collider2D _playerCollider;
private GameObject _currentHammer;
private bool _hasHammer = true;
private bool _facingRight = true;
private float _cooldownTimer = 0f;
public bool HasHammer => _hasHammer;
public bool CanThrow => _hasHammer && _cooldownTimer <= 0f;
public float CooldownRemaining => Mathf.Max(0f, _cooldownTimer);
private void Awake()
{
_currentHammer = hammerInHand;
}
public void SetFacingDirection(bool facingRight)
{
_facingRight = facingRight;
}
public bool TryThrowHammer()
{
if (!CanThrow)
return false;
ThrowHammer();
return true;
}
private void ThrowHammer()
{
if (hammerInHand == null || _spawnPoint == null)
return;
_currentHammer = hammerInHand;
_currentHammer.transform.SetParent(null);
_currentHammer.transform.position = _spawnPoint.position;
_currentHammer.transform.rotation = _spawnPoint.rotation;
_hasHammer = false;
_cooldownTimer = _throwCooldown;
var hammer = _currentHammer.GetComponent<Hammer>();
if (hammer != null)
{
hammer.OnReturnedToHand += ReturnHammerToHand;
hammer.Initialize(_facingRight, _throwSpeed);
}
if (_playerCollider != null)
{
var hammerCollider = _currentHammer.GetComponent<Collider2D>();
if (hammerCollider != null)
{
Physics2D.IgnoreCollision(_playerCollider, hammerCollider);
}
}
}
private void Update()
{
if (_cooldownTimer > 0f)
{
_cooldownTimer -= Time.deltaTime;
}
}
private void ReturnHammerToHand()
{
if (_currentHammer == null || _spawnPoint == null)
return;
_currentHammer.transform.SetParent(_spawnPoint);
_currentHammer.transform.localPosition = Vector3.zero;
_currentHammer.transform.localRotation = Quaternion.identity;
var rb = _currentHammer.GetComponent<Rigidbody2D>();
if (rb != null)
{
rb.linearVelocity = Vector2.zero;
rb.angularVelocity = 0f;
}
var hammer = _currentHammer.GetComponent<Hammer>();
if (hammer != null)
{
hammer.OnReturnedToHand -= ReturnHammerToHand;
}
_hasHammer = true;
_cooldownTimer = 0f;
}
}
@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: 59cb4a98d6866124793e8758b2ec958a

Before

Width:  |  Height:  |  Size: 2.7 MiB

After

Width:  |  Height:  |  Size: 2.7 MiB