From ff97b227c23ff1829868e003e8074eba4cb74136 Mon Sep 17 00:00:00 2001 From: Pierre Wessman <4029607+pierrewessman@users.noreply.github.com> Date: Thu, 2 Oct 2025 10:08:00 +0200 Subject: [PATCH] Add player enhancements and debug features MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 --- src/config/constants.ts | 4 +- src/entities/Player.ts | 89 +++++++++++++++++++++++++++++++++++++++-- src/game/GameScene.ts | 15 ++++++- 3 files changed, 101 insertions(+), 7 deletions(-) diff --git a/src/config/constants.ts b/src/config/constants.ts index 5b4984a..2633a85 100644 --- a/src/config/constants.ts +++ b/src/config/constants.ts @@ -25,12 +25,12 @@ export const COLOR_GOAL_CREASE = 0x87ceeb; // Game settings export const FPS = 60; -export const DEBUG = true; +export const DEBUG = false; // Player constants export const PLAYER_RADIUS_GOALIE = 12; // 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 GOAL_DECELERATION_RATE = 0.9; // Speed multiplier reduction after goal diff --git a/src/entities/Player.ts b/src/entities/Player.ts index e2666da..ee58b2e 100644 --- a/src/entities/Player.ts +++ b/src/entities/Player.ts @@ -6,7 +6,8 @@ import { PLAYER_RADIUS_SKATER, SPEED_SCALE_FACTOR, MOVEMENT_STOP_THRESHOLD, - GOAL_DECELERATION_RATE + GOAL_DECELERATION_RATE, + DEBUG } from '../config/constants'; import { CoordinateUtils } from '../utils/coordinates'; import { MathUtils } from '../utils/math'; @@ -38,6 +39,13 @@ export class Player extends Phaser.GameObjects.Container { // Rotation (angle in radians, 0 = facing right) private currentAngle: number = 0; + // Debug visualizations + private debugTargetGraphics?: Phaser.GameObjects.Graphics; + private debugLineGraphics?: Phaser.GameObjects.Graphics; + + // Direction indicator + private directionIndicator!: Phaser.GameObjects.Graphics; + constructor( scene: Phaser.Scene, id: string, @@ -62,6 +70,10 @@ export class Player extends Phaser.GameObjects.Container { this.attributes = attributes; 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 this.scene.events.on('goal', this.onGoal, this); @@ -78,6 +90,12 @@ export class Player extends Phaser.GameObjects.Container { this.body.setCollideWorldBounds(true); this.createSprite(); + + // Create debug graphics if DEBUG is enabled + if (DEBUG) { + this.debugTargetGraphics = scene.add.graphics(); + this.debugLineGraphics = scene.add.graphics(); + } } private createSprite() { @@ -105,7 +123,28 @@ export class Player extends Phaser.GameObjects.Container { }); 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); // Smoothly rotate toward target angle + // Scale rotation speed inversely with current velocity (faster = wider turns) 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 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); this.gameX = gamePos.x; 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 + ); } } diff --git a/src/game/GameScene.ts b/src/game/GameScene.ts index 645ef14..1ddfe4f 100644 --- a/src/game/GameScene.ts +++ b/src/game/GameScene.ts @@ -49,11 +49,22 @@ export class GameScene extends Phaser.Scene { 'home', 'C', -10, - -10, + -5, { 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() {