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:
parent
a3ffc94916
commit
ff97b227c2
@ -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
|
||||||
|
|
||||||
|
|||||||
@ -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
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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() {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user