Files
GDevelop/Extensions/TextObject/textruntimeobject-pixi-renderer.ts
2025-08-07 18:47:02 +02:00

232 lines
6.7 KiB
TypeScript

namespace gdjs {
class TextRuntimeObjectPixiRenderer {
_object: gdjs.TextRuntimeObject;
_fontManager: any;
_text: PIXI.Text;
_justCreated: boolean = true;
constructor(
runtimeObject: gdjs.TextRuntimeObject,
instanceContainer: gdjs.RuntimeInstanceContainer
) {
this._object = runtimeObject;
this._fontManager = instanceContainer.getGame().getFontManager();
this._text = new PIXI.Text(' ', { align: 'left' });
this._text.anchor.x = 0.5;
this._text.anchor.y = 0.5;
instanceContainer
.getLayer('')
.getRenderer()
.addRendererObject(this._text, runtimeObject.getZOrder());
this._text.text =
runtimeObject._str.length === 0 ? ' ' : runtimeObject._str;
//Work around a PIXI.js bug. See updateTime method.
this.updateStyle();
this.updatePosition();
}
getRendererObject() {
return this._text;
}
ensureUpToDate() {
if (this._justCreated) {
//Work around a PIXI.js bug:
this._text.updateText(false);
//Width seems not to be correct when text is not rendered yet.
this.updatePosition();
this._justCreated = false;
}
}
updateStyle(): void {
const fontName =
'"' + this._fontManager.getFontFamily(this._object._fontName) + '"';
const style = this._text.style;
style.fontStyle = this._object._italic ? 'italic' : 'normal';
style.fontWeight = this._object._bold ? 'bold' : 'normal';
style.fontSize = this._object._characterSize;
style.fontFamily = fontName;
if (this._object._useGradient) {
style.fill = this._getGradientHex();
} else {
style.fill = this._getColorHex();
}
if (this._object._gradientType === 'LINEAR_VERTICAL') {
style.fillGradientType = PIXI.TEXT_GRADIENT.LINEAR_VERTICAL;
} else {
style.fillGradientType = PIXI.TEXT_GRADIENT.LINEAR_HORIZONTAL;
}
// @ts-ignore
style.align = this._object._textAlign;
style.wordWrap = this._object._wrapping;
style.wordWrapWidth = this._object._wrappingWidth;
style.breakWords = true;
style.stroke = gdjs.rgbToHexNumber(
this._object._outlineColor[0],
this._object._outlineColor[1],
this._object._outlineColor[2]
);
style.strokeThickness = this._object._isOutlineEnabled
? this._object._outlineThickness
: 0;
style.dropShadow = this._object._shadow;
style.dropShadowColor = gdjs.rgbToHexNumber(
this._object._shadowColor[0],
this._object._shadowColor[1],
this._object._shadowColor[2]
);
style.dropShadowAlpha = this._object._shadowOpacity / 255;
style.dropShadowBlur = this._object._shadowBlur;
style.dropShadowAngle = gdjs.toRad(this._object._shadowAngle);
style.dropShadowDistance = this._object._shadowDistance;
const extraPaddingForShadow = style.dropShadow
? style.dropShadowDistance + style.dropShadowBlur
: 0;
style.padding = Math.ceil(this._object._padding + extraPaddingForShadow);
style.lineHeight = this._object._lineHeight;
// Prevent spikey outlines by adding a miter limit
style.miterLimit = 3;
this.updatePosition();
// Manually ask the PIXI object to re-render as we changed a style property
// see http://www.html5gamedevs.com/topic/16924-change-text-style-post-render/
// @ts-ignore
this._text.dirty = true;
}
updatePosition(): void {
if (this._object.isWrapping() && this._text.width !== 0) {
const alignmentX =
this._object._textAlign === 'right'
? 1
: this._object._textAlign === 'center'
? 0.5
: 0;
const width = this._object.getWrappingWidth();
// A vector from the custom size center to the renderer center.
const centerToCenterX = (width - this._text.width) * (alignmentX - 0.5);
this._text.position.x = this._object.x + width / 2;
this._text.anchor.x = 0.5 - centerToCenterX / this._text.width;
} else {
this._text.position.x = this._object.x + this._text.width / 2;
this._text.anchor.x = 0.5;
}
const alignmentY =
this._object._verticalTextAlignment === 'bottom'
? 1
: this._object._verticalTextAlignment === 'center'
? 0.5
: 0;
this._text.position.y =
this._object.y + this._text.height * (0.5 - alignmentY);
this._text.anchor.y = 0.5;
}
updateAngle(): void {
this._text.rotation = gdjs.toRad(this._object.angle);
}
updateOpacity(): void {
this._text.alpha = this._object.opacity / 255;
}
updateString(): void {
this._text.text =
this._object._str.length === 0 ? ' ' : this._object._str;
//Work around a PIXI.js bug.
this._text.updateText(false);
}
getWidth(): float {
return this._text.width;
}
getHeight(): float {
return this._text.height;
}
_getColorHex() {
return gdjs.rgbToHexNumber(
this._object._color[0],
this._object._color[1],
this._object._color[2]
);
}
_getGradientHex() {
const gradient: Array<string> = [];
for (
let colorIndex = 0;
colorIndex < this._object._gradient.length;
colorIndex++
) {
gradient.push(
'#' +
gdjs.rgbToHex(
this._object._gradient[colorIndex][0],
this._object._gradient[colorIndex][1],
this._object._gradient[colorIndex][2]
)
);
}
return gradient;
}
/**
* Get x-scale of the text.
*/
getScaleX(): float {
return this._text.scale.x;
}
/**
* Get y-scale of the text.
*/
getScaleY(): float {
return this._text.scale.y;
}
/**
* Set the text object scale.
* @param newScale The new scale for the text object.
*/
setScale(newScale: float): void {
this._text.scale.x = newScale;
this._text.scale.y = newScale;
}
/**
* Set the text object x-scale.
* @param newScale The new x-scale for the text object.
*/
setScaleX(newScale: float): void {
this._text.scale.x = newScale;
}
/**
* Set the text object y-scale.
* @param newScale The new y-scale for the text object.
*/
setScaleY(newScale: float): void {
this._text.scale.y = newScale;
}
destroy() {
this._text.destroy(true);
}
}
// Register the class to let the engine use it.
export const TextRuntimeObjectRenderer = TextRuntimeObjectPixiRenderer;
export type TextRuntimeObjectRenderer = TextRuntimeObjectPixiRenderer;
}