FAZA 2: Player entity with WASD movement, walking animation, and camera follow - Ready for testing

This commit is contained in:
2025-12-06 18:00:59 +01:00
parent 7e6cc85a6f
commit 3086356171
6 changed files with 641 additions and 47 deletions

163
src/entities/Player.js Normal file
View File

@@ -0,0 +1,163 @@
// Player Entity
// Igralec z WASD kontrolami in isometrično podporo
class Player {
constructor(scene, gridX = 50, gridY = 50) {
this.scene = scene;
this.gridX = gridX;
this.gridY = gridY;
this.iso = new IsometricUtils(48, 24);
// Hitrost gibanja
this.moveSpeed = 150; // px/s
this.gridMoveTime = 200; // ms za premik na eno kocko
// Stanje
this.isMoving = false;
this.direction = 'down';
// Kreira sprite
this.createSprite();
// Setup kontrole
this.setupControls();
// Začetna pozicija
this.updatePosition();
}
createSprite() {
// Generiraj player teksturo
TextureGenerator.createPlayerSprite(this.scene, 'player');
TextureGenerator.createPlayerWalkSprite(this.scene, 'player_walk');
// Kreira sprite
const screenPos = this.iso.toScreen(this.gridX, this.gridY);
this.sprite = this.scene.add.sprite(screenPos.x, screenPos.y, 'player');
this.sprite.setOrigin(0.5, 1); // Anchor na dnu sprite-a
// Depth sorting
this.updateDepth();
// Dodaj walking animacijo
if (!this.scene.anims.exists('player_walk_anim')) {
this.scene.anims.create({
key: 'player_walk_anim',
frames: this.scene.anims.generateFrameNumbers('player_walk', {
start: 0,
end: 3
}),
frameRate: 8,
repeat: -1
});
}
}
setupControls() {
// WASD kontrole
this.keys = this.scene.input.keyboard.addKeys({
up: Phaser.Input.Keyboard.KeyCodes.W,
down: Phaser.Input.Keyboard.KeyCodes.S,
left: Phaser.Input.Keyboard.KeyCodes.A,
right: Phaser.Input.Keyboard.KeyCodes.D
});
}
update(delta) {
if (!this.isMoving) {
this.handleInput();
}
}
handleInput() {
let targetX = this.gridX;
let targetY = this.gridY;
let moved = false;
// WASD za isometric movement
if (this.keys.up.isDown) {
// W = North-West v isometric view
targetX--;
moved = true;
this.direction = 'up';
} else if (this.keys.down.isDown) {
// S = South-East v isometric view
targetX++;
moved = true;
this.direction = 'down';
}
if (this.keys.left.isDown) {
// A = South-West v isometric view
targetY--;
moved = true;
this.direction = 'left';
} else if (this.keys.right.isDown) {
// D = North-East v isometric view
targetY++;
moved = true;
this.direction = 'right';
}
// Preveri kolizijo z robovi mape
const terrainSystem = this.scene.terrainSystem;
if (moved && terrainSystem) {
if (this.iso.isInBounds(targetX, targetY, terrainSystem.width, terrainSystem.height)) {
this.moveToGrid(targetX, targetY);
}
}
}
moveToGrid(targetX, targetY) {
this.isMoving = true;
this.gridX = targetX;
this.gridY = targetY;
const targetScreen = this.iso.toScreen(targetX, targetY);
// Animacija hoje
this.sprite.play('player_walk_anim', true);
// Tween za smooth gibanje
this.scene.tweens.add({
targets: this.sprite,
x: targetScreen.x,
y: targetScreen.y,
duration: this.gridMoveTime,
ease: 'Linear',
onComplete: () => {
this.isMoving = false;
this.sprite.stop();
this.sprite.setFrame(0); // Idle frame
}
});
// Posodobi depth
this.updateDepth();
}
updatePosition() {
const screenPos = this.iso.toScreen(this.gridX, this.gridY);
this.sprite.setPosition(screenPos.x, screenPos.y);
this.updateDepth();
}
updateDepth() {
const depth = this.iso.getDepth(this.gridX, this.gridY);
this.sprite.setDepth(depth + 1000); // +1000 da je nad terenom
}
getPosition() {
return { x: this.gridX, y: this.gridY };
}
getScreenPosition() {
return { x: this.sprite.x, y: this.sprite.y };
}
destroy() {
if (this.sprite) {
this.sprite.destroy();
}
}
}

View File

@@ -4,6 +4,7 @@ class GameScene extends Phaser.Scene {
super({ key: 'GameScene' });
this.terrainSystem = null;
this.terrainContainer = null;
this.player = null;
}
create() {
@@ -22,6 +23,13 @@ class GameScene extends Phaser.Scene {
this.terrainSystem.generate();
this.terrainContainer = this.terrainSystem.render(width / 2, 100);
// Dodaj igralca - spawn na sredini mape
console.log('👤 Initializing player...');
this.player = new Player(this, 50, 50);
// Kamera sledi igralcu
this.cameras.main.startFollow(this.player.sprite, true, 0.1, 0.1);
// Kamera kontrole
this.setupCamera();
@@ -48,7 +56,7 @@ class GameScene extends Phaser.Scene {
this.fpsText.setScrollFactor(0);
this.fpsText.setDepth(1000);
console.log('✅ GameScene ready - FAZA 1!');
console.log('✅ GameScene ready - FAZA 2!');
}
setupCamera() {
@@ -65,20 +73,11 @@ class GameScene extends Phaser.Scene {
cam.setZoom(newZoom);
});
// Pan kontrole (Right click + drag)
this.input.on('pointermove', (pointer) => {
if (pointer.rightButtonDown()) {
cam.scrollX -= (pointer.x - pointer.prevPosition.x) / cam.zoom;
cam.scrollY -= (pointer.y - pointer.prevPosition.y) / cam.zoom;
}
});
// Pan kontrole (Right click + drag) - DISABLED za FAZA 2
// Player movement sedaj uporablja WASD
// WASD za kamera kontrolo (alternativa)
this.cursors = this.input.keyboard.addKeys({
up: Phaser.Input.Keyboard.KeyCodes.W,
down: Phaser.Input.Keyboard.KeyCodes.S,
left: Phaser.Input.Keyboard.KeyCodes.A,
right: Phaser.Input.Keyboard.KeyCodes.D,
// Q/E za zoom
this.zoomKeys = this.input.keyboard.addKeys({
zoomIn: Phaser.Input.Keyboard.KeyCodes.Q,
zoomOut: Phaser.Input.Keyboard.KeyCodes.E
});
@@ -88,7 +87,7 @@ class GameScene extends Phaser.Scene {
const width = this.cameras.main.width;
// Naslov
const title = this.add.text(width / 2, 20, 'FAZA 1: Generacija Terena', {
const title = this.add.text(width / 2, 20, 'FAZA 2: Igralec in Gibanje', {
fontFamily: 'Courier New',
fontSize: '20px',
fill: '#00ff41',
@@ -101,10 +100,9 @@ class GameScene extends Phaser.Scene {
// Kontrole info
const controlsText = this.add.text(width - 10, 10,
'Kontrole:\n' +
'WASD - Pan\n' +
'WASD - Gibanje igralca\n' +
'Q/E - Zoom\n' +
'Mouse Wheel - Zoom\n' +
'Right Click - Pan',
'Mouse Wheel - Zoom',
{
fontFamily: 'Courier New',
fontSize: '11px',
@@ -120,49 +118,37 @@ class GameScene extends Phaser.Scene {
}
update(time, delta) {
// Update player
if (this.player) {
this.player.update(delta);
}
// Update FPS
if (this.fpsText) {
this.fpsText.setText(`FPS: ${Math.round(this.game.loop.actualFps)}`);
}
// Kamera movement (WASD)
// Zoom controls
const cam = this.cameras.main;
const panSpeed = 5;
if (this.cursors) {
if (this.cursors.up.isDown) {
cam.scrollY -= panSpeed;
}
if (this.cursors.down.isDown) {
cam.scrollY += panSpeed;
}
if (this.cursors.left.isDown) {
cam.scrollX -= panSpeed;
}
if (this.cursors.right.isDown) {
cam.scrollX += panSpeed;
}
// Zoom
if (this.cursors.zoomIn.isDown) {
if (this.zoomKeys) {
if (this.zoomKeys.zoomIn.isDown) {
cam.setZoom(Phaser.Math.Clamp(cam.zoom + 0.01, 0.3, 2.0));
}
if (this.cursors.zoomOut.isDown) {
if (this.zoomKeys.zoomOut.isDown) {
cam.setZoom(Phaser.Math.Clamp(cam.zoom - 0.01, 0.3, 2.0));
}
}
// Debug info update
if (this.debugText) {
const pointer = this.input.activePointer;
const worldX = Math.round(pointer.worldX);
const worldY = Math.round(pointer.worldY);
if (this.debugText && this.player) {
const playerPos = this.player.getPosition();
const screenPos = this.player.getScreenPosition();
this.debugText.setText(
`FAZA 1 - Terrain System\n` +
`FAZA 2 - Player Movement\n` +
`Zoom: ${cam.zoom.toFixed(2)}\n` +
`Camera: (${Math.round(cam.scrollX)}, ${Math.round(cam.scrollY)})\n` +
`Mouse: (${worldX}, ${worldY})`
`Player Grid: (${playerPos.x}, ${playerPos.y})\n` +
`Player Screen: (${Math.round(screenPos.x)}, ${Math.round(screenPos.y)})`
);
}
}

View File

@@ -0,0 +1,200 @@
// Texture Generator
// Proceduralno generiranje tekstur in sprite-ov
class TextureGenerator {
// Generiraj player sprite (32x32px pixel art)
static createPlayerSprite(scene, key = 'player') {
const size = 32;
const canvas = scene.textures.createCanvas(key, size, size);
const ctx = canvas.getContext();
// Clear
ctx.clearRect(0, 0, size, size);
// Barve
const skinColor = '#FFDBAC';
const hatColor = '#F4E7C6';
const shirtColor = '#5CB85C';
const pantsColor = '#8B6F47';
const outlineColor = '#000000';
// Funkcija za risanje piksla
const pixel = (x, y, color) => {
ctx.fillStyle = color;
ctx.fillRect(x, y, 1, 1);
};
// Offset za centriranje
const ox = 12;
const oy = 4;
// Outline + Hat (8 pixel širok)
// Vrh klobuka
for (let x = 0; x < 8; x++) {
pixel(ox + x, oy + 0, outlineColor);
pixel(ox + x, oy + 1, hatColor);
pixel(ox + x, oy + 2, hatColor);
}
// Glava (6 pixel široka)
for (let y = 3; y < 6; y++) {
pixel(ox + 0, oy + y, outlineColor);
for (let x = 1; x < 7; x++) {
pixel(ox + x, oy + y, skinColor);
}
pixel(ox + 7, oy + y, outlineColor);
}
// Oči (črni piksli)
pixel(ox + 2, oy + 4, outlineColor);
pixel(ox + 5, oy + 4, outlineColor);
// Telo - srajca (6 pixel široka)
for (let y = 6; y < 11; y++) {
pixel(ox + 0, oy + y, outlineColor);
for (let x = 1; x < 7; x++) {
pixel(ox + x, oy + y, shirtColor);
}
pixel(ox + 7, oy + y, outlineColor);
}
// Roke (stranske)
for (let y = 7; y < 10; y++) {
// Leva roka
pixel(ox - 1, oy + y, skinColor);
pixel(ox - 2, oy + y, outlineColor);
// Desna roka
pixel(ox + 8, oy + y, skinColor);
pixel(ox + 9, oy + y, outlineColor);
}
// Noge - hlače (vsaka noga 3 piksle široka)
for (let y = 11; y < 16; y++) {
// Leva noga
pixel(ox + 0, oy + y, outlineColor);
pixel(ox + 1, oy + y, pantsColor);
pixel(ox + 2, oy + y, pantsColor);
pixel(ox + 3, oy + y, outlineColor);
// Desna noga
pixel(ox + 4, oy + y, outlineColor);
pixel(ox + 5, oy + y, pantsColor);
pixel(ox + 6, oy + y, pantsColor);
pixel(ox + 7, oy + y, outlineColor);
}
// Dno nog
for (let x = 0; x < 8; x++) {
pixel(ox + x, oy + 16, outlineColor);
}
canvas.refresh();
return canvas;
}
// Generiraj walking animacijo (4 frame-i)
static createPlayerWalkSprite(scene, key = 'player_walk') {
const frameWidth = 32;
const frameHeight = 32;
const frameCount = 4;
const canvas = scene.textures.createCanvas(key, frameWidth * frameCount, frameHeight);
const ctx = canvas.getContext();
// Frame 0: Idle (osnovni sprite)
this.drawPlayerFrame(ctx, 0, 0, 0);
// Frame 1: Left foot forward
this.drawPlayerFrame(ctx, frameWidth, 0, 1);
// Frame 2: Idle
this.drawPlayerFrame(ctx, frameWidth * 2, 0, 0);
// Frame 3: Right foot forward
this.drawPlayerFrame(ctx, frameWidth * 3, 0, 2);
canvas.refresh();
return canvas;
}
// Pomožna funkcija za risanje player frame-a
static drawPlayerFrame(ctx, offsetX, offsetY, frame) {
const size = 32;
// Barve
const skinColor = '#FFDBAC';
const hatColor = '#F4E7C6';
const shirtColor = '#5CB85C';
const pantsColor = '#8B6F47';
const outlineColor = '#000000';
const pixel = (x, y, color) => {
ctx.fillStyle = color;
ctx.fillRect(offsetX + x, offsetY + y, 1, 1);
};
const ox = 12;
const oy = 4;
// Klobuk
for (let x = 0; x < 8; x++) {
pixel(ox + x, oy + 0, outlineColor);
pixel(ox + x, oy + 1, hatColor);
pixel(ox + x, oy + 2, hatColor);
}
// Glava
for (let y = 3; y < 6; y++) {
pixel(ox + 0, oy + y, outlineColor);
for (let x = 1; x < 7; x++) {
pixel(ox + x, oy + y, skinColor);
}
pixel(ox + 7, oy + y, outlineColor);
}
pixel(ox + 2, oy + 4, outlineColor);
pixel(ox + 5, oy + 4, outlineColor);
// Telo
for (let y = 6; y < 11; y++) {
pixel(ox + 0, oy + y, outlineColor);
for (let x = 1; x < 7; x++) {
pixel(ox + x, oy + y, shirtColor);
}
pixel(ox + 7, oy + y, outlineColor);
}
// Roke
for (let y = 7; y < 10; y++) {
pixel(ox - 1, oy + y, skinColor);
pixel(ox - 2, oy + y, outlineColor);
pixel(ox + 8, oy + y, skinColor);
pixel(ox + 9, oy + y, outlineColor);
}
// Noge - prilagojeno glede na frame (walking animation)
let legOffset = 0;
if (frame === 1) legOffset = -1; // Left foot forward
if (frame === 2) legOffset = 1; // Right foot forward
for (let y = 11; y < 16; y++) {
const leftShift = (frame === 1) ? 0 : 0;
const rightShift = (frame === 2) ? 0 : 0;
// Leva noga
pixel(ox + 0, oy + y, outlineColor);
pixel(ox + 1, oy + y, pantsColor);
pixel(ox + 2, oy + y, pantsColor);
pixel(ox + 3, oy + y, outlineColor);
// Desna noga
pixel(ox + 4, oy + y, outlineColor);
pixel(ox + 5, oy + y, pantsColor);
pixel(ox + 6, oy + y, pantsColor);
pixel(ox + 7, oy + y, outlineColor);
}
for (let x = 0; x < 8; x++) {
pixel(ox + x, oy + 16, outlineColor);
}
}
}