import { Text, TextStyleOptions } from 'pixi.js';
/**
* Пиксельный шрифт движка: VT323 (OFL, есть кириллица).
* Загружается через FontFace API; PixelText — обёртка над Pixi Text
* с этим шрифтом. Если игры понадобится свой шрифт — меняется источник,
* обёртка остаётся.
*/
export const PIXEL_FONT_FAMILY = 'VT323';
/** Фолбэк, если шрифт ещё не загрузился или не поддерживается. */
export const PIXEL_FONT_FALLBACK = 'monospace';
let fontLoaded: Promise<void> | null = null;
/**
* Resolution растеризации текста (пикселей устройства на виртуальный пиксель).
* Задаётся Renderer'ом при смене масштаба; до этого тексты чуть мягче — не критично.
*/
let textResolution = 1;
/** Задать resolution растеризации PixelText (вызывается движком при ресайзе). */
export function setPixelTextResolution(resolution: number): void {
textResolution = Math.max(1, Math.round(resolution));
}
/**
* Загрузить пиксельный шрифт. url указывает на TTF (приложение решает, где он лежит).
* Повторные вызовы возвращают уже начатую загрузку. Безопасно вызывать до Pixi.
*/
export function ensurePixelFont(url: string, family: string = PIXEL_FONT_FAMILY): Promise<void> {
if (fontLoaded) return fontLoaded;
fontLoaded = (async () => {
try {
const face = new FontFace(family, `url(${url})`);
await face.load();
document.fonts.add(face);
} catch (err) {
// Не критично: PixelText останется с фолбэком monospace.
console.warn(`[engine] пиксельный шрифт не загрузился (${url}):`, err);
}
})();
return fontLoaded;
}
export interface PixelTextOptions {
text: string;
/** Размер в виртуальных пикселях (шрифт растровый — целые числа). */
size: number;
/** Цвет (числом, 0xRRGGBB). */
color?: number;
/** Перенос по ширине (виртуальные пиксели). */
wordWrapWidth?: number;
lineHeight?: number;
align?: 'left' | 'center' | 'right';
letterSpacing?: number;
fontFamily?: string;
}
/** Текст пиксельным шрифтом движка. */
export class PixelText extends Text {
constructor(options: PixelTextOptions) {
const family = `${options.fontFamily ?? PIXEL_FONT_FAMILY}, ${PIXEL_FONT_FALLBACK}`;
const style: TextStyleOptions = {
fontFamily: family,
fontSize: options.size,
fill: options.color ?? 0xffffff,
letterSpacing: options.letterSpacing ?? 0,
lineHeight: options.lineHeight
};
if (options.wordWrapWidth !== undefined) {
style.wordWrap = true;
style.wordWrapWidth = options.wordWrapWidth;
style.breakWords = true;
}
if (options.align !== undefined) {
style.align = options.align;
}
// resolution > 1: глифы растеризуются 1:1 с пикселями устройства — резкий текст.
super({ text: options.text, style, resolution: textResolution });
}
}