feat: Add rabbit entity with dynamic movement and sprite rendering
This commit is contained in:
154
js/entities/entity.js
Normal file
154
js/entities/entity.js
Normal file
@@ -0,0 +1,154 @@
|
||||
// Base entity system
|
||||
const ENTITY_TYPES = {
|
||||
RABBIT: 'rabbit'
|
||||
};
|
||||
|
||||
// Store all entities
|
||||
const entities = [];
|
||||
|
||||
// Base Entity class
|
||||
class Entity {
|
||||
constructor(type, x, y, options = {}) {
|
||||
this.type = type;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.vx = 0;
|
||||
this.vy = 0;
|
||||
this.width = options.width || 10;
|
||||
this.height = options.height || 10;
|
||||
this.rotation = 0;
|
||||
this.sprite = null;
|
||||
this.flipped = false;
|
||||
this.isStatic = false;
|
||||
this.lastUpdate = performance.now();
|
||||
this.id = Entity.nextId++;
|
||||
}
|
||||
|
||||
static nextId = 1;
|
||||
|
||||
update() {
|
||||
// Override in subclasses
|
||||
return false;
|
||||
}
|
||||
|
||||
render(ctx, offsetX, offsetY) {
|
||||
// Default rendering - override in subclasses
|
||||
const screenX = (this.x - offsetX) * PIXEL_SIZE;
|
||||
const screenY = (this.y - offsetY) * PIXEL_SIZE;
|
||||
|
||||
ctx.save();
|
||||
ctx.translate(screenX, screenY);
|
||||
ctx.rotate(this.rotation);
|
||||
|
||||
if (this.sprite && this.sprite.complete) {
|
||||
const width = this.width * PIXEL_SIZE;
|
||||
const height = this.height * PIXEL_SIZE;
|
||||
|
||||
if (this.flipped) {
|
||||
ctx.scale(-1, 1);
|
||||
ctx.drawImage(this.sprite, -width/2, -height/2, width, height);
|
||||
} else {
|
||||
ctx.drawImage(this.sprite, -width/2, -height/2, width, height);
|
||||
}
|
||||
} else {
|
||||
// Fallback if sprite not loaded
|
||||
ctx.fillStyle = '#FF00FF';
|
||||
ctx.fillRect(
|
||||
-this.width/2 * PIXEL_SIZE,
|
||||
-this.height/2 * PIXEL_SIZE,
|
||||
this.width * PIXEL_SIZE,
|
||||
this.height * PIXEL_SIZE
|
||||
);
|
||||
}
|
||||
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
checkCollisions(newX, newY) {
|
||||
const result = {
|
||||
collision: false,
|
||||
horizontal: false,
|
||||
vertical: false,
|
||||
ground: false
|
||||
};
|
||||
|
||||
// Check points around the entity
|
||||
const halfWidth = this.width / 2;
|
||||
const halfHeight = this.height / 2;
|
||||
|
||||
// Check bottom points for ground collision
|
||||
const bottomLeft = { x: newX - halfWidth * 0.8, y: newY + halfHeight };
|
||||
const bottomRight = { x: newX + halfWidth * 0.8, y: newY + halfHeight };
|
||||
|
||||
if (this.isPixelSolid(bottomLeft.x, bottomLeft.y) ||
|
||||
this.isPixelSolid(bottomRight.x, bottomRight.y)) {
|
||||
result.collision = true;
|
||||
result.vertical = true;
|
||||
result.ground = true;
|
||||
}
|
||||
|
||||
// Check side points for horizontal collision
|
||||
const leftMiddle = { x: newX - halfWidth, y: newY };
|
||||
const rightMiddle = { x: newX + halfWidth, y: newY };
|
||||
|
||||
if (this.isPixelSolid(leftMiddle.x, leftMiddle.y)) {
|
||||
result.collision = true;
|
||||
result.horizontal = true;
|
||||
}
|
||||
|
||||
if (this.isPixelSolid(rightMiddle.x, rightMiddle.y)) {
|
||||
result.collision = true;
|
||||
result.horizontal = true;
|
||||
}
|
||||
|
||||
// Check top for ceiling collision
|
||||
const topMiddle = { x: newX, y: newY - halfHeight };
|
||||
if (this.isPixelSolid(topMiddle.x, topMiddle.y)) {
|
||||
result.collision = true;
|
||||
result.vertical = true;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
isPixelSolid(x, y) {
|
||||
const pixel = getPixel(Math.floor(x), Math.floor(y));
|
||||
return pixel !== EMPTY &&
|
||||
pixel !== WATER &&
|
||||
pixel !== FIRE &&
|
||||
pixel !== SQUARE &&
|
||||
pixel !== CIRCLE &&
|
||||
pixel !== TRIANGLE;
|
||||
}
|
||||
}
|
||||
|
||||
// Function to create and register an entity
|
||||
function createEntity(type, x, y, options = {}) {
|
||||
let entity;
|
||||
|
||||
switch(type) {
|
||||
case ENTITY_TYPES.RABBIT:
|
||||
entity = new Rabbit(x, y, options);
|
||||
break;
|
||||
default:
|
||||
console.error(`Unknown entity type: ${type}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
entities.push(entity);
|
||||
return entity;
|
||||
}
|
||||
|
||||
// Update all entities
|
||||
function updateEntities() {
|
||||
for (let i = entities.length - 1; i >= 0; i--) {
|
||||
entities[i].update();
|
||||
}
|
||||
}
|
||||
|
||||
// Render all entities
|
||||
function renderEntities(ctx, offsetX, offsetY) {
|
||||
for (const entity of entities) {
|
||||
entity.render(ctx, offsetX, offsetY);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user