feat(02-03): integrate GridManager and Renderer into Game class

- Add GridManager and Renderer imports
- Add gridManager and renderer fields to Game class
- Initialize GridManager and create grid in constructor
- Initialize Renderer with canvas reference
- Delegate render() to renderer.render()
- Add tilesSelected event listener for logging

Implements Task 1 of plan 02-03

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Claude Sonnet
2026-03-11 02:55:44 +00:00
parent 65db3a914b
commit 61a6d50dd9
+118 -103
View File
@@ -1,103 +1,118 @@
// src/game/Game.ts - Main game orchestrator class
/**
* Game is the main orchestrator that coordinates all game components.
* It manages the canvas, game loop, and event system.
*/
import { GameLoop } from './GameLoop';
import { TypedEventEmitter } from './EventEmitter';
import { GameEvents } from '../types';
import { CONFIG } from '../config';
export class Game {
readonly canvas: HTMLCanvasElement;
readonly ctx: CanvasRenderingContext2D;
readonly loop: GameLoop;
readonly events: TypedEventEmitter<GameEvents>;
constructor() {
// Get canvas element
this.canvas = document.getElementById('game') as HTMLCanvasElement;
if (!this.canvas) {
throw new Error('Canvas element with id "game" not found');
}
// Get 2D rendering context
this.ctx = this.canvas.getContext('2d')!;
if (!this.ctx) {
throw new Error('Could not get 2D context from canvas');
}
// Initialize event emitter
this.events = new TypedEventEmitter<GameEvents>();
// Setup canvas size and scale
this.setupCanvas();
// Create game loop with update callback
this.loop = new GameLoop(this.update.bind(this));
}
/**
* Sets up canvas dimensions and handles device pixel ratio for sharp rendering
*/
private setupCanvas(): void {
const { cols, rows } = CONFIG.grid;
const { size, gap } = CONFIG.tile;
const dpr = window.devicePixelRatio || 1;
// Calculate logical canvas size
const width = cols * (size + gap) + gap;
const height = rows * (size + gap) + gap;
// Set actual canvas size (accounting for device pixel ratio)
this.canvas.width = width * dpr;
this.canvas.height = height * dpr;
// Set display size (CSS)
this.canvas.style.width = `${width}px`;
this.canvas.style.height = `${height}px`;
// Scale context to account for device pixel ratio
this.ctx.scale(dpr, dpr);
}
/**
* Starts the game
*/
start(): void {
// Emit game:start event
this.events.emit('game:start', undefined as never);
// Start the game loop
this.loop.start();
}
/**
* Stops the game
*/
stop(): void {
this.loop.stop();
}
/**
* Update callback - called by GameLoop on each tick
* @param deltaTime - Time since last update in milliseconds
*/
private update(deltaTime: number): void {
// Emit game:tick event
this.events.emit('game:tick', { deltaTime });
// Render the current frame
this.render();
}
/**
* Renders the game state to the canvas
*/
private render(): void {
// Clear canvas with background color
this.ctx.fillStyle = CONFIG.colors.background;
this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
}
}
// src/game/Game.ts - Main game orchestrator class
/**
* Game is the main orchestrator that coordinates all game components.
* It manages the canvas, game loop, event system, grid, and rendering.
*/
import { GameLoop } from './GameLoop';
import { TypedEventEmitter } from './EventEmitter';
import { GameEvents } from '../types';
import { CONFIG } from '../config';
import { GridManager } from '../managers/GridManager';
import { Renderer } from '../rendering/Renderer';
export class Game {
readonly canvas: HTMLCanvasElement;
readonly ctx: CanvasRenderingContext2D;
readonly loop: GameLoop;
readonly events: TypedEventEmitter<GameEvents>;
readonly gridManager: GridManager;
readonly renderer: Renderer;
constructor() {
// Get canvas element
this.canvas = document.getElementById('game') as HTMLCanvasElement;
if (!this.canvas) {
throw new Error('Canvas element with id "game" not found');
}
// Get 2D rendering context
this.ctx = this.canvas.getContext('2d')!;
if (!this.ctx) {
throw new Error('Could not get 2D context from canvas');
}
// Initialize event emitter
this.events = new TypedEventEmitter<GameEvents>();
// Initialize grid manager and create grid
this.gridManager = new GridManager(this.events);
this.gridManager.initializeGrid();
// Setup canvas size and scale
this.setupCanvas();
// Initialize renderer with canvas reference
this.renderer = new Renderer(this.ctx, this.gridManager);
this.renderer.setCanvas(this.canvas);
// Create game loop with update callback
this.loop = new GameLoop(this.update.bind(this));
// Listen for tilesSelected event (log for now - Phase 3 will handle matching logic)
this.events.on('tilesSelected', ({ tile1, tile2 }) => {
console.log('Two tiles selected:', tile1.id, tile2.id);
});
}
/**
* Sets up canvas dimensions and handles device pixel ratio for sharp rendering
*/
private setupCanvas(): void {
const { cols, rows } = CONFIG.grid;
const { size, gap } = CONFIG.tile;
const dpr = window.devicePixelRatio || 1;
// Calculate logical canvas size
const width = cols * (size + gap) + gap;
const height = rows * (size + gap) + gap;
// Set actual canvas size (accounting for device pixel ratio)
this.canvas.width = width * dpr;
this.canvas.height = height * dpr;
// Set display size (CSS)
this.canvas.style.width = `${width}px`;
this.canvas.style.height = `${height}px`;
// Scale context to account for device pixel ratio
this.ctx.scale(dpr, dpr);
}
/**
* Starts the game
*/
start(): void {
// Emit game:start event
this.events.emit('game:start', undefined as never);
// Start the game loop
this.loop.start();
}
/**
* Stops the game
*/
stop(): void {
this.loop.stop();
}
/**
* Update callback - called by GameLoop on each tick
* @param deltaTime - Time since last update in milliseconds
*/
private update(deltaTime: number): void {
// Emit game:tick event
this.events.emit('game:tick', { deltaTime });
// Render the current frame
this.render();
}
/**
* Renders the game state to the canvas
*/
private render(): void {
this.renderer.render();
}
}