angular ui

This commit is contained in:
2025-05-10 11:33:42 +02:00
parent a2048718d8
commit a3a48cad40
120 changed files with 7600 additions and 475 deletions

View File

@@ -0,0 +1,154 @@
import { Component, ElementRef, OnInit, ViewChild } from '@angular/core';
import { OptionsComponent } from '../options/options.component';
import { GameState } from './gameState';
import { SimulationEngine } from './simulationEngine';
import { GameRules } from './gameRules';
import { Command } from './command';
@Component({
selector: 'app-game',
templateUrl: './game.component.html',
styleUrls: ['./game.component.css'],
imports: [OptionsComponent],
})
export class GameComponent implements OnInit {
engine!: SimulationEngine;
private optionsOpen = false;
private isPaused = false;
private lastFrameTime = 0;
@ViewChild('gameCanvas') canvasRef!: ElementRef<HTMLCanvasElement>;
@ViewChild('canvasWrapper') wrapperRef!: ElementRef<HTMLDivElement>;
private logicalWidth = 800;
private logicalHeight = 600;
scaleX: number = 1;
scaleY: number = 1;
pixelScale: number = 1;
async ngOnInit() {
const initialState: GameState = {
tick: 0,
units: [{ id: 'u1', x: 0, y: 0 }],
commandHistory: []
};
const json = await fetch('/assets/gameData.json').then(r => r.json());
this.engine = new SimulationEngine(initialState, GameRules, json);
requestAnimationFrame(this.gameLoop.bind(this));
}
start() {
this.engine.start();
}
stop() {
this.engine.stop();
}
step() {
this.engine.step();
}
rewind() {
this.engine.rewindToZero();
}
fastForward() {
this.engine.fastForwardToEnd();
}
async reloadGameData() {
const json = await fetch('/assets/gameData.json').then(r => r.json());
this.engine.reinitializeWithNewData(json);
}
issueMove() {
const cmd: Command = {
step: this.engine.currentStep + 1,
type: 'move',
payload: { id: 'u1', dx: 1, dy: 0 }
};
this.engine.issueCommand(cmd);
}
ngAfterViewInit(): void {
this.resizeCanvas();
window.addEventListener('resize', this.resizeCanvas.bind(this));
}
resizeCanvas(): void {
const wrapper = this.wrapperRef.nativeElement;
const canvas = this.canvasRef.nativeElement;
const targetAspect = this.logicalWidth / this.logicalHeight;
const maxW = wrapper.clientWidth;
const maxH = wrapper.clientHeight;
let width = maxW;
let height = width / targetAspect;
if (height > maxH) {
height = maxH;
width = height * targetAspect;
}
canvas.width = width;
canvas.height = height;
this.scaleX = canvas.width / this.logicalWidth; // e.g. 800
this.scaleY = canvas.height / this.logicalHeight; // e.g. 600
// Optionally use the smaller of the two for uniform scaling
this.pixelScale = Math.min(this.scaleX, this.scaleY);
}
toScreen(x: number, y: number): [number, number] {
return [x * this.pixelScale, y * this.pixelScale];
}
fromScreen(px: number, py: number): [number, number] {
return [px / this.pixelScale, py / this.pixelScale];
}
private gameLoop(currentTime: number): void {
if (this.lastFrameTime === 0) {
this.lastFrameTime = currentTime;
}
const deltaTime = (currentTime - this.lastFrameTime) / 1000;
this.lastFrameTime = currentTime;
// Only update the game state if not paused
if (!this.isPaused) {
this.updateGame(deltaTime);
requestAnimationFrame(this.gameLoop.bind(this));
}
}
private updateGame(deltaTime: number): void {
// Implement your game update logic here using deltaTime
}
// Called when opening options: pause the game
public openOptions(): void {
this.optionsOpen = true;
this.pauseGame();
}
// Called when closing options: resume the game
public closeOptions(): void {
this.optionsOpen = false;
this.resumeGame();
}
private pauseGame(): void {
this.isPaused = true;
}
private resumeGame(): void {
this.isPaused = false;
// Reset the lastFrameTime to avoid a huge delta on resume
this.lastFrameTime = performance.now();
requestAnimationFrame(this.gameLoop.bind(this));
}
}