michals-silly-game-frontend/src/app/mario/mario.component.ts

154 lines
5.3 KiB
TypeScript
Raw Normal View History

2025-04-07 09:20:49 +00:00
import { Component, HostListener, OnInit } from '@angular/core';
@Component({
selector: 'app-mario',
standalone: true,
imports: [],
templateUrl: './mario.component.html',
styleUrl: './mario.component.scss'
})
export class MarioComponent implements OnInit {
mario = {
x: 50,
y: 0,
width: 40,
height: 60,
dx: 0,
dy: 0,
speed: 4,
jumpStrength: 25,
gravity: 0.5,
onGround: false,
color: 'red'
};
private canvas!: HTMLCanvasElement;
private ctx!: CanvasRenderingContext2D;
private canvasWidth = window.innerWidth * 5;
private canvasHeight = window.innerHeight;
private platforms = [
{ x: 300, y: 500, width: 100, height: 10 },
{ x: 600, y: 400, width: 150, height: 10 },
{ x: 900, y: 350, width: 200, height: 10 },
{ x: 1300, y: 450, width: 120, height: 10 },
{ x: 1600, y: 380, width: 180, height: 10 },
{ x: 1900, y: 320, width: 120, height: 10 },
{ x: 2200, y: 300, width: 150, height: 10 },
{ x: 2500, y: 350, width: 180, height: 10 },
{ x: 2800, y: 400, width: 120, height: 10 },
{ x: 3100, y: 450, width: 150, height: 10 },
{ x: 3400, y: 390, width: 130, height: 10 },
{ x: 3700, y: 350, width: 200, height: 10 },
{ x: 4000, y: 300, width: 140, height: 10 },
{ x: 4300, y: 330, width: 180, height: 10 },
{ x: 4600, y: 380, width: 120, height: 10 },
{ x: 4900, y: 350, width: 160, height: 10 },
{ x: 5200, y: 420, width: 180, height: 10 },
{ x: 5500, y: 460, width: 120, height: 10 },
{ x: 5800, y: 500, width: 160, height: 10 },
{ x: 6100, y: 400, width: 140, height: 10 }
];
ngOnInit(): void {
this.canvas = document.getElementById('gameCanvas') as HTMLCanvasElement;
this.ctx = this.canvas.getContext('2d')!;
this.canvas.width = this.canvasWidth;
this.canvas.height = this.canvasHeight;
this.mario.y = this.canvasHeight - 100;
this.loop();
}
@HostListener('window:keydown', ['$event'])
handleKeyDown(event: KeyboardEvent) {
if (event.key === 'ArrowRight') this.mario.dx = this.mario.speed;
if (event.key === 'ArrowLeft') this.mario.dx = -this.mario.speed;
if (event.key === ' ' && this.mario.onGround) {
this.mario.dy = -this.mario.jumpStrength;
this.mario.onGround = false;
}
}
@HostListener('window:keyup', ['$event'])
handleKeyUp(event: KeyboardEvent) {
if (['ArrowRight', 'ArrowLeft'].includes(event.key)) this.mario.dx = 0;
}
private loop = () => {
this.update();
this.draw();
this.scrollToMario();
requestAnimationFrame(this.loop);
};
private update() {
this.mario.x += this.mario.dx;
this.mario.y += this.mario.dy;
// Gravity
this.mario.dy += this.mario.gravity;
this.mario.onGround = false;
const groundY = this.canvasHeight - 60;
if (this.mario.y + this.mario.height >= groundY) {
this.mario.y = groundY - this.mario.height;
this.mario.dy = 0;
this.mario.onGround = true;
}
for (const platform of this.platforms) {
if (
this.mario.x + this.mario.width > platform.x &&
this.mario.x < platform.x + platform.width &&
this.mario.y + this.mario.height <= platform.y &&
this.mario.y + this.mario.height + this.mario.dy >= platform.y
) {
this.mario.y = platform.y - this.mario.height;
this.mario.dy = 0;
this.mario.onGround = true;
}
}
// Boundaries
if (this.mario.x < 0) this.mario.x = 0;
if (this.mario.x + this.mario.width > this.canvasWidth)
this.mario.x = this.canvasWidth - this.mario.width;
}
private draw() {
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
// Ground
this.ctx.fillStyle = '#654321';
this.ctx.fillRect(0, this.canvas.height - 60, this.canvas.width, 60);
// Platforms
this.ctx.fillStyle = '#888';
for (const p of this.platforms) {
this.ctx.fillRect(p.x, p.y, p.width, p.height);
}
// Mario
this.ctx.fillStyle = this.mario.color;
this.ctx.fillRect(this.mario.x, this.mario.y, this.mario.width, this.mario.height);
this.ctx.fillStyle = 'white';
this.ctx.fillRect(this.mario.x + 8, this.mario.y + 15, 8, 8);
this.ctx.fillRect(this.mario.x + 24, this.mario.y + 15, 8, 8);
this.ctx.fillStyle = 'black';
this.ctx.fillRect(this.mario.x + 10, this.mario.y + 17, 4, 4);
this.ctx.fillRect(this.mario.x + 26, this.mario.y + 17, 4, 4);
this.ctx.fillStyle = 'yellow';
this.ctx.fillRect(this.canvasWidth - 60, this.canvas.height - 160, 20, 100);
this.ctx.fillStyle = 'black';
this.ctx.fillText('🏁', this.canvasWidth - 60, this.canvas.height - 170);
}
private scrollToMario() {
const scrollContainer = this.canvas.parentElement!;
const centerOffset = scrollContainer.clientWidth / 2;
scrollContainer.scrollLeft = this.mario.x - centerOffset;
}
}