101 lines
4.0 KiB
TypeScript
101 lines
4.0 KiB
TypeScript
/** 清洗状态由 Block 立即更新;这里只有逐格泥巴渐隐及气泡尾效。 */
|
|
export default class WashingEffect {
|
|
private layer = new cc.Node("washingEffect");
|
|
private bubbles = new cc.Node("washingBubbles");
|
|
private pending = 0;
|
|
private callbacks: (() => void)[] = [];
|
|
private disposed = false;
|
|
|
|
constructor(private parent: cc.Node) {
|
|
parent.addChild(this.layer);
|
|
this.bubbles.zIndex = 1000;
|
|
const icon = parent.getChildByName("icon");
|
|
if (icon) {
|
|
this.layer.setSiblingIndex(icon.getSiblingIndex() + 1);
|
|
this.layer.zIndex = icon.zIndex;
|
|
}
|
|
// 单色地块以 simple 格图替代 icon。
|
|
parent.children.filter(child => child.name === "simple").forEach(child => {
|
|
this.layer.zIndex = Math.max(this.layer.zIndex, child.zIndex + 1);
|
|
});
|
|
}
|
|
|
|
get isCleaning(): boolean { return this.pending > 0; }
|
|
|
|
play(cells: { x: number; y: number; frame: number }[], dirtFrames: cc.SpriteFrame[], bubbleFrame: cc.SpriteFrame): void {
|
|
if (this.disposed || !cc.isValid(this.layer, true)) return;
|
|
for (const cell of cells) {
|
|
const x = -65 + cell.x * 120, y = 60 + cell.y * 120;
|
|
const frame = dirtFrames[cell.frame];
|
|
if (frame) {
|
|
const dirt = this.sprite("washingDirt", frame, x, y, 120);
|
|
this.pending++;
|
|
cc.tween(dirt).to(0.2, { opacity: 0 }).call(() => {
|
|
if (this.disposed) return;
|
|
this.remove(dirt);
|
|
this.pending--;
|
|
this.flush();
|
|
}).start();
|
|
}
|
|
if (!bubbleFrame || !this.parent.parent) continue;
|
|
if (!this.bubbles.parent) this.parent.parent.addChild(this.bubbles);
|
|
// 气泡留在洗澡地形上,方块移走时不跟随。
|
|
const center = this.bubbles.convertToNodeSpaceAR(this.parent.convertToWorldSpaceAR(cc.v2(x, y)));
|
|
for (let i = 0; i < 12; i++) {
|
|
const bubble = this.sprite("washingBubble", bubbleFrame,
|
|
center.x + (i % 3 - 1) * 28 + Math.random() * 12 - 6,
|
|
center.y - 42 + Math.floor(i / 3) * 24 + Math.random() * 12, 18 + Math.random() * 18, this.bubbles);
|
|
bubble.opacity = 210;
|
|
bubble.color = i % 3 === 1 ? cc.color(220, 249, 255) : cc.Color.WHITE;
|
|
cc.tween(bubble).to(0.8 + Math.random() * 0.2, {
|
|
x: bubble.x + Math.random() * 28 - 14,
|
|
y: bubble.y + 126 + Math.random() * 72,
|
|
opacity: 0,
|
|
}, { easing: "sineOut" }).call(() => {
|
|
this.remove(bubble);
|
|
if (!this.bubbles.children.length) this.bubbles.removeFromParent();
|
|
}).start();
|
|
}
|
|
}
|
|
}
|
|
|
|
whenClean(callback: () => void): void {
|
|
if (this.disposed) return;
|
|
this.callbacks.push(callback);
|
|
this.flush();
|
|
}
|
|
|
|
private flush(): void {
|
|
while (!this.disposed && this.pending === 0 && this.callbacks.length) this.callbacks.shift()();
|
|
}
|
|
|
|
private sprite(name: string, frame: cc.SpriteFrame, x: number, y: number, size: number, layer = this.layer): cc.Node {
|
|
const node = new cc.Node(name);
|
|
const sprite = node.addComponent(cc.Sprite);
|
|
sprite.spriteFrame = frame;
|
|
sprite.sizeMode = cc.Sprite.SizeMode.CUSTOM;
|
|
sprite.trim = false;
|
|
node.setContentSize(size, size);
|
|
node.setPosition(x, y);
|
|
layer.addChild(node);
|
|
return node;
|
|
}
|
|
|
|
private remove(node: cc.Node): void {
|
|
if (!cc.isValid(node, true)) return;
|
|
node.removeFromParent();
|
|
node.destroy();
|
|
}
|
|
|
|
dispose(): void {
|
|
if (this.disposed) return;
|
|
this.disposed = true;
|
|
this.callbacks = [];
|
|
this.pending = 0;
|
|
[this.layer, this.bubbles].forEach(layer => {
|
|
layer.children.slice().forEach(node => cc.Tween.stopAllByTarget(node));
|
|
this.remove(layer);
|
|
});
|
|
}
|
|
}
|