Add player enhancements and debug features

- Add away team defender (LD) for testing behaviors
- Add direction indicator (white line) showing player facing direction
- Initialize player facing angle based on team (home faces right, away faces left)
- Implement speed-based turning physics (faster = wider turns, rotation drops to 30% at max speed)
- Add debug visualizations for player targets (line and X marker) when DEBUG is true
- Increase rotation speed constant from 1 to 3 rad/s for more responsive turning

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Pierre Wessman 2025-10-02 10:08:00 +02:00
parent a3ffc94916
commit ff97b227c2
3 changed files with 101 additions and 7 deletions

View File

@ -25,12 +25,12 @@ export const COLOR_GOAL_CREASE = 0x87ceeb;
// Game settings // Game settings
export const FPS = 60; export const FPS = 60;
export const DEBUG = true; export const DEBUG = false;
// Player constants // Player constants
export const PLAYER_RADIUS_GOALIE = 12; // pixels export const PLAYER_RADIUS_GOALIE = 12; // pixels
export const PLAYER_RADIUS_SKATER = 10; // pixels export const PLAYER_RADIUS_SKATER = 10; // pixels
export const PLAYER_ROTATION_SPEED = 1; // radians per second export const PLAYER_ROTATION_SPEED = 3; // radians per second
export const SPEED_SCALE_FACTOR = 10; // speed attribute (0-100) / 10 = m/s export const SPEED_SCALE_FACTOR = 10; // speed attribute (0-100) / 10 = m/s
export const GOAL_DECELERATION_RATE = 0.9; // Speed multiplier reduction after goal export const GOAL_DECELERATION_RATE = 0.9; // Speed multiplier reduction after goal

View File

@ -6,7 +6,8 @@ import {
PLAYER_RADIUS_SKATER, PLAYER_RADIUS_SKATER,
SPEED_SCALE_FACTOR, SPEED_SCALE_FACTOR,
MOVEMENT_STOP_THRESHOLD, MOVEMENT_STOP_THRESHOLD,
GOAL_DECELERATION_RATE GOAL_DECELERATION_RATE,
DEBUG
} from '../config/constants'; } from '../config/constants';
import { CoordinateUtils } from '../utils/coordinates'; import { CoordinateUtils } from '../utils/coordinates';
import { MathUtils } from '../utils/math'; import { MathUtils } from '../utils/math';
@ -38,6 +39,13 @@ export class Player extends Phaser.GameObjects.Container {
// Rotation (angle in radians, 0 = facing right) // Rotation (angle in radians, 0 = facing right)
private currentAngle: number = 0; private currentAngle: number = 0;
// Debug visualizations
private debugTargetGraphics?: Phaser.GameObjects.Graphics;
private debugLineGraphics?: Phaser.GameObjects.Graphics;
// Direction indicator
private directionIndicator!: Phaser.GameObjects.Graphics;
constructor( constructor(
scene: Phaser.Scene, scene: Phaser.Scene,
id: string, id: string,
@ -62,6 +70,10 @@ export class Player extends Phaser.GameObjects.Container {
this.attributes = attributes; this.attributes = attributes;
this.state = 'defensive'; this.state = 'defensive';
// Initialize facing direction based on team
// Home team (left side) faces right, Away team (right side) faces left
this.currentAngle = this.team === 'home' ? 0 : Math.PI;
// Listen for goal events // Listen for goal events
this.scene.events.on('goal', this.onGoal, this); this.scene.events.on('goal', this.onGoal, this);
@ -78,6 +90,12 @@ export class Player extends Phaser.GameObjects.Container {
this.body.setCollideWorldBounds(true); this.body.setCollideWorldBounds(true);
this.createSprite(); this.createSprite();
// Create debug graphics if DEBUG is enabled
if (DEBUG) {
this.debugTargetGraphics = scene.add.graphics();
this.debugLineGraphics = scene.add.graphics();
}
} }
private createSprite() { private createSprite() {
@ -105,7 +123,28 @@ export class Player extends Phaser.GameObjects.Container {
}); });
label.setOrigin(0.5, 0.5); label.setOrigin(0.5, 0.5);
this.add([graphics, label]); // Create direction indicator (small line showing facing direction)
this.directionIndicator = this.scene.add.graphics();
this.updateDirectionIndicator();
this.add([graphics, label, this.directionIndicator]);
}
/**
* Update the direction indicator to show current facing angle
*/
private updateDirectionIndicator() {
this.directionIndicator.clear();
const radius = this.playerPosition === 'G' ? PLAYER_RADIUS_GOALIE : PLAYER_RADIUS_SKATER;
// Draw line pointing right (container rotation will orient it correctly)
// Line goes from behind center to near the edge (0=center, 1=edge)
const startX = -radius * -0.5; // Start
const endX = radius * 1.0; // End
this.directionIndicator.lineStyle(3, 0x999999, 1);
this.directionIndicator.lineBetween(startX, 0, endX, 0); // Horizontal line, rotation handled by container
} }
/** /**
@ -166,8 +205,13 @@ export class Player extends Phaser.GameObjects.Container {
const targetAngle = Math.atan2(dy, dx); const targetAngle = Math.atan2(dy, dx);
// Smoothly rotate toward target angle // Smoothly rotate toward target angle
// Scale rotation speed inversely with current velocity (faster = wider turns)
const deltaSeconds = delta / 1000; const deltaSeconds = delta / 1000;
const maxRotation = PLAYER_ROTATION_SPEED * deltaSeconds; const currentSpeed = Math.sqrt(this.body.velocity.x ** 2 + this.body.velocity.y ** 2);
const maxSpeed = (this.attributes.speed / SPEED_SCALE_FACTOR) * SCALE;
const speedRatio = maxSpeed > 0 ? currentSpeed / maxSpeed : 0;
const turnPenalty = 1 - (speedRatio * 0.7); // At max speed, rotation is 30% of base
const maxRotation = PLAYER_ROTATION_SPEED * turnPenalty * deltaSeconds;
// Calculate shortest angular difference // Calculate shortest angular difference
const angleDiff = MathUtils.angleDifference(this.currentAngle, targetAngle); const angleDiff = MathUtils.angleDifference(this.currentAngle, targetAngle);
@ -199,5 +243,44 @@ export class Player extends Phaser.GameObjects.Container {
const gamePos = CoordinateUtils.screenToGame(this.scene, bodyX, bodyY); const gamePos = CoordinateUtils.screenToGame(this.scene, bodyX, bodyY);
this.gameX = gamePos.x; this.gameX = gamePos.x;
this.gameY = gamePos.y; this.gameY = gamePos.y;
// Update debug visualizations
this.updateDebugVisuals();
}
/**
* Update debug visualizations (target position and path line)
*/
private updateDebugVisuals() {
if (!DEBUG || !this.debugTargetGraphics || !this.debugLineGraphics) return;
// Convert target position to screen coordinates
const targetScreen = CoordinateUtils.gameToScreen(this.scene, this.targetX, this.targetY);
const playerScreen = CoordinateUtils.gameToScreen(this.scene, this.gameX, this.gameY);
// Clear previous debug graphics
this.debugTargetGraphics.clear();
this.debugLineGraphics.clear();
// Draw line from player to target
const lineColor = this.team === 'home' ? 0x0000ff : 0xff0000;
this.debugLineGraphics.lineStyle(1, lineColor, 0.5);
this.debugLineGraphics.lineBetween(playerScreen.x, playerScreen.y, targetScreen.x, targetScreen.y);
// Draw target marker (X)
const markerSize = 5;
this.debugTargetGraphics.lineStyle(2, lineColor, 0.8);
this.debugTargetGraphics.lineBetween(
targetScreen.x - markerSize,
targetScreen.y - markerSize,
targetScreen.x + markerSize,
targetScreen.y + markerSize
);
this.debugTargetGraphics.lineBetween(
targetScreen.x + markerSize,
targetScreen.y - markerSize,
targetScreen.x - markerSize,
targetScreen.y + markerSize
);
} }
} }

View File

@ -49,11 +49,22 @@ export class GameScene extends Phaser.Scene {
'home', 'home',
'C', 'C',
-10, -10,
-10, -5,
{ speed: 80, skill: 75 } { speed: 80, skill: 75 }
); );
this.players.push(homeCenter); // Create one away defender at (15, 0) - right side for defending
const awayDefender = new Player(
this,
'away-LD',
'away',
'LD',
15,
0,
{ speed: 75, skill: 70 }
);
this.players.push(homeCenter, awayDefender);
} }
private setupEventListeners() { private setupEventListeners() {