import { Application, Container } from 'pixi.js';
/**
* Рендерер: виртуальное «пиксельное» разрешение, растянутое целым числом на экран
* с image-rendering: pixelated — это и есть pixel-perfect для пиксель-арта.
*/
export interface RendererOptions {
/** Виртуальная ширина (например 480). */
virtualWidth: number;
/** Виртуальная высота (например 270). */
virtualHeight: number;
/** Целочисленный множитель масштаба. */
scale: number;
/** Цвет фона. */
background: number;
/** Родительский элемент для канваса. */
parent: HTMLElement;
}
export class Renderer {
readonly app: Application;
/** Корневой контейнер мира (к нему применяется камера). */
readonly worldRoot: Container;
/** Контейнер UI поверх мира (камерой не двигается). */
readonly uiRoot: Container;
readonly virtualWidth: number;
readonly virtualHeight: number;
readonly scale: number;
private readonly parentEl: HTMLElement;
constructor(options: RendererOptions) {
this.virtualWidth = options.virtualWidth;
this.virtualHeight = options.virtualHeight;
this.scale = options.scale;
this.parentEl = options.parent;
this.app = new Application();
void this.app.init({
width: options.virtualWidth,
height: options.virtualHeight,
backgroundColor: options.background,
antialias: false,
roundPixels: true,
autoStart: false,
sharedTicker: false
});
this.worldRoot = new Container();
this.uiRoot = new Container();
}
/** Дождаться инициализации WebGL и добавить канвас на страницу. */
async setup(): Promise<void> {
await this.app.renderer.init;
this.app.stage.addChild(this.worldRoot, this.uiRoot);
const canvas = this.app.canvas;
canvas.style.imageRendering = 'pixelated';
canvas.style.width = `${this.virtualWidth * this.scale}px`;
canvas.style.height = `${this.virtualHeight * this.scale}px`;
this.parentEl.appendChild(canvas);
}
/** Рендер кадра (вызывается игровым циклом). */
render(): void {
this.app.renderer.render(this.app.stage);
}
}