94 lines
3.6 KiB
TypeScript
94 lines
3.6 KiB
TypeScript
const { ccclass, property, requireComponent } = cc._decorator;
|
||
|
||
/** 挂到带 Sprite 的节点上,并在 Inspector 中指定 jungle_shine.effect。 */
|
||
@ccclass
|
||
@requireComponent(cc.Sprite)
|
||
export default class JungleShine extends cc.Component {
|
||
@property(cc.EffectAsset)
|
||
effectAsset: cc.EffectAsset = null;
|
||
|
||
private material: cc.Material = null;
|
||
private retainedEffect: cc.EffectAsset = null;
|
||
private delayRemaining: number = 0.15;
|
||
private elapsed: number = 0;
|
||
|
||
protected onLoad(): void {
|
||
let sprite = this.getComponent(cc.Sprite);
|
||
if (!sprite) return;
|
||
if (this.isEffectReady(this.effectAsset)) {
|
||
this.createMaterial(sprite, this.effectAsset);
|
||
} else {
|
||
// 旧预制体可能仍持有已释放的 EffectAsset,非空不代表可用。
|
||
cc.assetManager.loadBundle("publicUI", (error, bundle) => {
|
||
if (!cc.isValid(this, true)) return;
|
||
if (error) {
|
||
cc.error("[JungleShine] 加载 publicUI 失败", error);
|
||
return;
|
||
}
|
||
bundle.load("jungle_shine", cc.EffectAsset, (error, effect: cc.EffectAsset) => {
|
||
if (!cc.isValid(this, true)) return;
|
||
if (error || !this.isEffectReady(effect)) {
|
||
cc.error("[JungleShine] 加载扫光特效失败", error || "EffectAsset 数据无效");
|
||
return;
|
||
}
|
||
this.effectAsset = effect;
|
||
this.createMaterial(sprite, effect);
|
||
});
|
||
});
|
||
}
|
||
}
|
||
|
||
private isEffectReady(effect: cc.EffectAsset): boolean {
|
||
if (!cc.isValid(effect, true)) return false;
|
||
// Creator 2.4 的类型声明未包含 techniques。
|
||
const techniques = (effect as any).techniques;
|
||
return Array.isArray(techniques) && techniques.length > 0
|
||
&& techniques.every(technique => technique && Array.isArray(technique.passes) && technique.passes.length > 0);
|
||
}
|
||
|
||
private createMaterial(sprite: cc.Sprite, effect: cc.EffectAsset): void {
|
||
// 运行时创建的材质由本组件持有,同时保留其特效资源。
|
||
this.retainedEffect = effect.addRef();
|
||
let material = new cc.Material();
|
||
// @ts-ignore Cocos Creator 2.4 通过 effectAsset 创建运行时材质。
|
||
material.effectAsset = effect;
|
||
material.setProperty("shinePosition", -0.35);
|
||
material.setProperty("shineWidth", 0.075);
|
||
material.setProperty("shineStrength", 1.8);
|
||
material.setProperty("shineAngle", 0.2);
|
||
material.setProperty("shineColor", [1.0, 0.95, 0.68, 1.0]);
|
||
this.material = material;
|
||
sprite.setMaterial(0, material);
|
||
}
|
||
|
||
protected update(dt: number): void {
|
||
if (!this.material) {
|
||
return;
|
||
}
|
||
if (this.delayRemaining > 0) {
|
||
this.delayRemaining = Math.max(0, this.delayRemaining - dt);
|
||
return;
|
||
}
|
||
|
||
this.elapsed += dt;
|
||
let progress = Math.min(1, this.elapsed / 0.72);
|
||
this.material.setProperty("shinePosition", -0.35 + 1.7 * progress);
|
||
if (progress >= 1) {
|
||
this.elapsed = 0;
|
||
this.delayRemaining = 1.25;
|
||
this.material.setProperty("shinePosition", -0.35);
|
||
}
|
||
}
|
||
|
||
protected onDestroy(): void {
|
||
if (this.material && cc.isValid(this.material)) {
|
||
this.material.destroy();
|
||
}
|
||
this.material = null;
|
||
if (cc.isValid(this.retainedEffect, true)) {
|
||
this.retainedEffect.decRef();
|
||
}
|
||
this.retainedEffect = null;
|
||
}
|
||
}
|