402 lines
12 KiB
Markdown
402 lines
12 KiB
Markdown
# Hockey Match Engine MVP - Implementation Plan
|
|
|
|
## Overview
|
|
A minimal hockey match engine using PhaserJS with continuous positioning and behavior trees. The goal is to validate the architecture with the simplest working implementation.
|
|
|
|
## Technical Stack
|
|
- **Framework**: PhaserJS 3
|
|
- **Language**: TypeScript
|
|
- **Coordinate System**: International rink (60x30m), origin at center (0,0)
|
|
- **Update Rate**: 60 FPS (Phaser's default game loop)
|
|
|
|
## Core Architecture
|
|
|
|
### 1. Game State
|
|
```
|
|
GameState {
|
|
puck: { x, y, possession: PlayerID|null, carrier: PlayerID|null }
|
|
teams: [homeTeam, awayTeam]
|
|
score: { home: 0, away: 0 }
|
|
gameTime: elapsed seconds
|
|
}
|
|
```
|
|
|
|
### 2. Player Structure
|
|
```
|
|
Player {
|
|
id: string
|
|
team: 'home'|'away'
|
|
position: 'LW'|'C'|'RW'|'LD'|'RD'|'G'
|
|
x, y: float (current position)
|
|
targetX, targetY: float (desired position)
|
|
|
|
attributes: {
|
|
speed: 0-100 (movement speed)
|
|
skill: 0-100 (pass/shot accuracy, decision quality)
|
|
}
|
|
|
|
state: 'offensive'|'defensive'
|
|
}
|
|
```
|
|
|
|
### 3. Behavior Tree (Simplified)
|
|
```
|
|
Root
|
|
├─ Is Player Goalie?
|
|
│ └─ Goalie Behavior (stay near net, track puck)
|
|
│
|
|
├─ Does Player Have Puck?
|
|
│ ├─ Can Shoot? → Shoot
|
|
│ ├─ Can Pass? → Pass to best teammate
|
|
│ └─ Else → Skate toward opponent net
|
|
│
|
|
├─ Is Puck Loose?
|
|
│ └─ Move toward puck (chase)
|
|
│
|
|
└─ Team Has Puck? (offensive state)
|
|
├─ Move to offensive support position
|
|
└─ Else → Move to defensive coverage position
|
|
```
|
|
|
|
---
|
|
|
|
## Implementation Phases
|
|
|
|
### Phase 1: Environment Setup
|
|
**Goal**: Get PhaserJS running with a hockey rink displayed
|
|
|
|
**Tasks**:
|
|
1. Initialize npm project with package.json
|
|
2. Install dependencies: `phaser` (and optionally `typescript`, `vite` for dev server)
|
|
3. Create basic project structure:
|
|
```
|
|
/src
|
|
/game
|
|
main.js (Phaser game configuration)
|
|
GameScene.js (main game scene)
|
|
/entities
|
|
Player.js
|
|
Puck.js
|
|
/systems
|
|
BehaviorTree.js
|
|
PositioningSystem.js
|
|
PuckSystem.js
|
|
/config
|
|
constants.js (rink dimensions, game constants)
|
|
index.html
|
|
```
|
|
4. Draw the rink:
|
|
- Rectangle 60x30m (scale for display, e.g., 1m = 10px → 600x300px)
|
|
- Center red line
|
|
- Blue lines at ±18.3m
|
|
- Goal creases (simple rectangles at x=±26, y=0)
|
|
- Optional: boards/corners (rounded rectangle)
|
|
|
|
**Validation**: Browser displays a hockey rink with proper markings
|
|
|
|
---
|
|
|
|
### Phase 2: Player Entities & Basic Movement
|
|
**Goal**: Render 12 players (10 skaters + 2 goalies) and move them to target positions
|
|
|
|
**Tasks**:
|
|
1. Create Player class:
|
|
- Visual: colored circle (10px radius)
|
|
- Home team: blue circles
|
|
- Away team: red circles
|
|
- Goalies: larger circles or different shade
|
|
- Render at x,y coordinates
|
|
- Add text label with position (LW, C, RW, LD, RD, G)
|
|
|
|
2. Create initial formations (5v5 setup):
|
|
- **Home team** (left side, attacking right):
|
|
- G: (-26, 0)
|
|
- LD: (-15, -5), RD: (-15, 5)
|
|
- LW: (-10, -8), C: (-10, 0), RW: (-10, 8)
|
|
- **Away team** (right side, attacking left):
|
|
- G: (26, 0)
|
|
- LD: (15, 5), RD: (15, -5)
|
|
- LW: (10, 8), C: (10, 0), RW: (10, -8)
|
|
|
|
3. Implement interpolated movement:
|
|
- Each player has `targetX, targetY`
|
|
- Each frame: move toward target using easing (lerp or Phaser tweens)
|
|
- Movement speed based on `speed` attribute (e.g., speed=80 → 8 m/s)
|
|
|
|
4. Test: Set random target positions and watch players smoothly move
|
|
|
|
**Validation**: 12 colored circles on ice, moving smoothly when targets change
|
|
|
|
---
|
|
|
|
### Phase 3: Puck & Basic Possession
|
|
**Goal**: Add puck entity and implement possession mechanics
|
|
|
|
**Tasks**:
|
|
1. Create Puck class:
|
|
- Visual: small black circle (5px radius)
|
|
- Position: x, y
|
|
- State: `loose`, `carried`, `passing`, `shot`
|
|
- `possession`: which team has control
|
|
- `carrier`: specific PlayerID holding puck
|
|
|
|
2. Implement puck carrying:
|
|
- When player has puck: puck.x = player.x, puck.y = player.y
|
|
- Puck "sticks" to carrier position each frame
|
|
|
|
3. Implement loose puck pickup:
|
|
- Calculate distance from each player to puck
|
|
- If player within pickup radius (1.5m) and puck is loose:
|
|
- Pickup chance = f(player.skill, player.speed)
|
|
- Assign carrier, set possession
|
|
|
|
4. Faceoff system:
|
|
- Place puck at (0, 0)
|
|
- Set state = `loose`
|
|
- Both centers skate to center, first to arrive picks up
|
|
|
|
**Validation**: Puck spawns at center, players skate to it, one picks it up
|
|
|
|
---
|
|
|
|
### Phase 4: Basic Behavior Tree - Puck Carrier Actions
|
|
**Goal**: Player with puck makes decisions (shoot, pass, carry)
|
|
|
|
**Tasks**:
|
|
1. Create BehaviorTree evaluator:
|
|
- Input: player, gameState
|
|
- Output: action {type: 'move'|'pass'|'shoot', target: ...}
|
|
|
|
2. Implement carrier behaviors:
|
|
- **Shoot decision**:
|
|
- If within shooting range (x > 15m from opponent net) and clear lane:
|
|
- Shoot chance = f(skill, distance to net)
|
|
- Action: `{type: 'shoot', targetX: opponentNet.x, targetY: random(-2, 2)}`
|
|
|
|
- **Pass decision**:
|
|
- Find all teammates
|
|
- Filter: not covered by opponent (simple distance check)
|
|
- Rate by: distance to net, openness
|
|
- If good option exists: `{type: 'pass', target: teammateID}`
|
|
|
|
- **Carry decision** (default):
|
|
- Move toward opponent net
|
|
- Target: (opponentNet.x - 5, y + random(-3, 3))
|
|
|
|
3. Action execution:
|
|
- **Shoot**:
|
|
- Travel time = distance / shotSpeed (e.g., 30 m/s)
|
|
- Goalie save chance = f(goalie.skill, shot distance, shot accuracy)
|
|
- Outcome: goal (puck to net, score++, faceoff) or save (puck loose near net)
|
|
|
|
- **Pass**:
|
|
- Travel time = distance / passSpeed (15 m/s)
|
|
- Success chance = f(passer.skill, distance, pressure)
|
|
- If success: receiver becomes carrier
|
|
- If fail: puck becomes loose at midpoint
|
|
|
|
- **Carry**: player skates with puck
|
|
|
|
**Validation**: Player with puck skates toward net, occasionally passes or shoots
|
|
|
|
---
|
|
|
|
### Phase 5: Team Positioning System (Offensive)
|
|
**Goal**: Players without puck move to support positions
|
|
|
|
**Tasks**:
|
|
1. Create simple heat map / positioning zones:
|
|
- **Offensive formation** (team has puck):
|
|
- Wingers: spread wide (y = ±10)
|
|
- Center: support puck carrier (x = carrier.x - 5, y = 0)
|
|
- Defense: hold blue line (x = offensiveZone - 15)
|
|
|
|
2. Implement positioning logic:
|
|
- Each non-carrier calculates ideal position based on:
|
|
- Puck location
|
|
- Their position role (LW, C, RW, LD, RD)
|
|
- Teammate spacing (avoid clustering)
|
|
|
|
- Set targetX, targetY to ideal position
|
|
- Player moves there using Phase 2 movement system
|
|
|
|
3. Dynamic adjustment:
|
|
- If puck changes possession, recalculate all positions
|
|
|
|
**Validation**: When one team has puck, their players spread into offensive formation
|
|
|
|
---
|
|
|
|
### Phase 6: Defensive Behaviors
|
|
**Goal**: Defending team pressures puck carrier and covers passing lanes
|
|
|
|
**Tasks**:
|
|
1. Add defensive state detection:
|
|
- If opponent has puck: all players on team → state = 'defensive'
|
|
|
|
2. Implement defensive positioning:
|
|
- **Forecheck** (1 nearest forward):
|
|
- Chase puck carrier (targetX = carrier.x, targetY = carrier.y)
|
|
- Apply "pressure" when within 2m
|
|
|
|
- **Coverage** (other forwards):
|
|
- Cover closest opponent forward
|
|
- Stay between opponent and own net
|
|
|
|
- **Defensive zone** (defense):
|
|
- Protect net-front (x = -20 to -25, y = ±4)
|
|
- Box out opponent forwards
|
|
|
|
3. Pressure mechanic:
|
|
- When defender within 2m of carrier:
|
|
- Reduce carrier's pass/shot success chance
|
|
- Increase turnover chance (puck becomes loose)
|
|
|
|
**Validation**: Defending team chases puck carrier and covers opponents
|
|
|
|
---
|
|
|
|
### Phase 7: Goalie Behavior
|
|
**Goal**: Goalies track puck and make saves
|
|
|
|
**Tasks**:
|
|
1. Goalie positioning:
|
|
- Stay in crease: x = ±27 (near net)
|
|
- Track puck Y position: targetY = clamp(puck.y, -3, 3)
|
|
- Interpolate to target (slower than skaters)
|
|
|
|
2. Save mechanics:
|
|
- On shot toward net:
|
|
- Calculate if shot trajectory intersects goalie position (±1m tolerance)
|
|
- Save chance = f(goalie.skill, shot speed, shot accuracy)
|
|
- Save: puck becomes loose in crease
|
|
- Goal: puck "enters net", score increments
|
|
|
|
3. Visual feedback:
|
|
- Flash goalie when making save
|
|
- Puck bounce animation on save
|
|
|
|
**Validation**: Goalies move laterally to track puck, stop some shots
|
|
|
|
---
|
|
|
|
### Phase 8: Game Flow & Reset
|
|
**Goal**: Handle goals and faceoffs, basic game loop
|
|
|
|
**Tasks**:
|
|
1. Goal detection:
|
|
- Check if puck crosses goal line (x > 29.5 or x < -29.5, y within net width)
|
|
- Increment score
|
|
- Trigger celebration pause (1 second)
|
|
- Reset to faceoff
|
|
|
|
2. Faceoff logic:
|
|
- Reset all players to formation positions (Phase 2)
|
|
- Place puck at (0, 0)
|
|
- Set puck.state = 'loose'
|
|
- Resume game
|
|
|
|
3. Game clock (optional for MVP):
|
|
- Display elapsed time
|
|
- No periods/end game for now (continuous)
|
|
|
|
4. Simple HUD:
|
|
- Score display: "Home 2 - 1 Away"
|
|
- Optional: possession indicator
|
|
|
|
**Validation**: Full game loop - faceoff → play → goal → faceoff → repeat
|
|
|
|
---
|
|
|
|
### Phase 9: Polish & Tuning
|
|
**Goal**: Make it feel like hockey
|
|
|
|
**Tasks**:
|
|
1. Tune movement speeds:
|
|
- Skater speed: ~8-10 m/s
|
|
- Puck pass speed: 15 m/s
|
|
- Puck shot speed: 30 m/s
|
|
|
|
2. Tune AI probabilities:
|
|
- Shot frequency: should see ~5-15 shots per team per minute
|
|
- Pass success: ~70-80% for uncontested passes
|
|
- Turnover frequency: balance offense/defense
|
|
|
|
3. Visual polish:
|
|
- Puck trail (line showing recent path)
|
|
- Player orientation (rotate sprite toward movement direction)
|
|
- Smooth animations for passes/shots
|
|
|
|
4. Balancing:
|
|
- Adjust skill/speed ranges so games are competitive
|
|
- Test with different attribute values
|
|
|
|
**Validation**: Game looks and feels reasonably like hockey
|
|
|
|
---
|
|
|
|
## Success Criteria for MVP
|
|
|
|
- ✅ 12 players (10 skaters + 2 goalies) on ice
|
|
- ✅ Players move smoothly in formations
|
|
- ✅ Puck possession and carrying works
|
|
- ✅ Players pass, shoot, and score goals
|
|
- ✅ Basic offensive positioning (spread out, support)
|
|
- ✅ Basic defensive pressure (chase carrier)
|
|
- ✅ Goalies make saves
|
|
- ✅ Goals trigger faceoffs and score updates
|
|
- ✅ Game runs continuously without crashes
|
|
|
|
---
|
|
|
|
## Future Iterations (Post-MVP)
|
|
|
|
**Phase 10+**: Additional features to consider after MVP validation:
|
|
- Offsides/icing rules
|
|
- Line changes and stamina system
|
|
- More strategies (1-2-2 forecheck, trap, etc.)
|
|
- Penalties and power plays
|
|
- More player attributes (checking, passing accuracy, shot power)
|
|
- Better AI decision trees (risk assessment, situational awareness)
|
|
- Replay system
|
|
- Match statistics tracking
|
|
- 3D visualization or better sprites
|
|
|
|
---
|
|
|
|
## Development Tips
|
|
|
|
1. **Test incrementally**: After each phase, test thoroughly before moving on
|
|
2. **Use constants file**: Store all magic numbers (rink size, speeds, probabilities) in one place
|
|
3. **Debug visualization**: Add toggleable overlays (target positions, pressure zones, etc.)
|
|
4. **Start simple**: Don't add complexity until basic mechanics work
|
|
5. **Iterate on feel**: The numbers in this plan are starting points - tune them based on what looks/feels right
|
|
|
|
---
|
|
|
|
## Estimated Timeline (Solo Developer)
|
|
|
|
- Phase 1: 2-4 hours (setup + rink rendering)
|
|
- Phase 2: 3-4 hours (player entities + movement)
|
|
- Phase 3: 2-3 hours (puck + possession)
|
|
- Phase 4: 4-6 hours (behavior tree + carrier actions)
|
|
- Phase 5: 3-4 hours (offensive positioning)
|
|
- Phase 6: 4-5 hours (defensive behaviors)
|
|
- Phase 7: 2-3 hours (goalie AI)
|
|
- Phase 8: 2-3 hours (game flow)
|
|
- Phase 9: 4-6 hours (tuning + polish)
|
|
|
|
**Total: ~26-38 hours** for a working MVP
|
|
|
|
---
|
|
|
|
## Next Steps
|
|
|
|
1. Set up development environment (Phase 1)
|
|
2. Get rink rendering working
|
|
3. Proceed through phases sequentially
|
|
4. Test and validate each phase before moving forward
|
|
5. Document any architectural changes or insights in NOTES.md
|
|
|
|
Good luck! 🏒
|