83 lines
4.1 KiB
TypeScript
83 lines
4.1 KiB
TypeScript
const { ccclass, property, executeInEditMode } = cc._decorator;
|
|
|
|
/** Bundle-local adaptation of NumberToImage: explicit frames, outlined digits and a centered coin row. */
|
|
@ccclass
|
|
@executeInEditMode
|
|
export default class CloudRiseNumberToImage extends cc.Component {
|
|
@property([cc.SpriteFrame]) digits: cc.SpriteFrame[] = [];
|
|
@property(cc.Material) glyphMaterial: cc.Material = null;
|
|
@property(cc.Node) coin: cc.Node = null;
|
|
@property(cc.Node) content: cc.Node = null;
|
|
@property value = 10000;
|
|
@property digitHeight = 62;
|
|
@property widthScale = 0.83;
|
|
@property spacing = 1;
|
|
@property coinGap = 10;
|
|
@property sourceArt = false;
|
|
@property showCoin = true;
|
|
private rendered = "";
|
|
private renderedDigits: cc.SpriteFrame[] = [];
|
|
private renderedMaterial: cc.Material = null;
|
|
|
|
setValue(value: number): void {
|
|
this.value = Number.isFinite(value) ? Math.max(0, Math.floor(value)) : 0;
|
|
this.update();
|
|
}
|
|
onEnable(): void { this.rendered = ""; this.update(); }
|
|
update(): void {
|
|
if (!this.content || !this.coin || this.digits.length !== 10 || this.digits.some(frame => !frame)) return;
|
|
const text = String(Math.max(0, Math.floor(this.value)));
|
|
if (text.length > 5) {
|
|
this.rendered = "";
|
|
this.content.active = false;
|
|
this.coin.active = false;
|
|
return;
|
|
}
|
|
this.content.active = true;
|
|
const key = [text, this.digitHeight, this.widthScale, this.spacing, this.coinGap, this.node.width, this.sourceArt, this.showCoin].join(":");
|
|
if (key === this.rendered && this.renderedMaterial === this.glyphMaterial
|
|
&& this.digits.every((frame, i) => frame === this.renderedDigits[i])) return;
|
|
this.rendered = key;
|
|
this.renderedDigits = this.digits.slice();
|
|
this.renderedMaterial = this.glyphMaterial;
|
|
const outlined = !!this.glyphMaterial && !this.sourceArt;
|
|
const digitMaterial = outlined ? this.glyphMaterial : cc.Material.getBuiltinMaterial("2d-sprite");
|
|
const widths = text.split("").map(char => {
|
|
const rect = this.digits[Number(char)].getRect();
|
|
return this.digitHeight * rect.width / rect.height * this.widthScale;
|
|
});
|
|
const numberWidth = widths.reduce((sum, width) => sum + width, 0) + (text.length - 1) * this.spacing + 16;
|
|
this.coin.active = this.showCoin;
|
|
const coinWidth = this.showCoin ? this.coin.width + this.coinGap : 0;
|
|
const total = coinWidth + numberWidth;
|
|
const scale = Math.min(1, this.node.width / total);
|
|
this.coin.scale = scale; this.content.scale = scale;
|
|
this.coin.x = (-total / 2 + this.coin.width / 2) * scale;
|
|
this.content.x = (-total / 2 + coinWidth + 8) * scale;
|
|
let x = 0;
|
|
this.content.children.forEach((node, index) => {
|
|
node.active = index < text.length;
|
|
if (!node.active) return;
|
|
const frame = this.digits[Number(text[index])], width = widths[index];
|
|
const sprite = node.getComponent(cc.Sprite);
|
|
sprite.spriteFrame = frame;
|
|
sprite.sizeMode = cc.Sprite.SizeMode.CUSTOM;
|
|
const padding = outlined ? 8 : 0;
|
|
node.setContentSize(width + padding * 2, this.digitHeight + padding * 2);
|
|
node.color = cc.Color.WHITE;
|
|
node.setPosition(x + width / 2, 0);
|
|
// Source artwork must also replace a previously assigned outline material.
|
|
sprite.setMaterial(0, digitMaterial);
|
|
if (outlined) {
|
|
const material = sprite.getMaterial(0);
|
|
const rect = frame.getRect(), texture = frame.getTexture();
|
|
// The bundle atlas is not packable/rotated. Pass its bounds so outlines cannot sample adjacent digits.
|
|
material.setProperty("glyphRect", new cc.Vec4(rect.x / texture.width, rect.y / texture.height,
|
|
rect.width / texture.width, rect.height / texture.height));
|
|
material.setProperty("glyphSize", cc.v2(width, this.digitHeight));
|
|
}
|
|
x += width + this.spacing;
|
|
});
|
|
}
|
|
}
|