164 lines
5.3 KiB
TypeScript
Raw Normal View History

2022-08-26 16:48:17 +08:00
import { CoroutineV2 } from "../../CatanEngine/CoroutineV2/CoroutineV2";
import { RandomEx } from "../../Utils/Number/RandomEx";
const { ccclass, property, requireComponent, menu } = cc._decorator;
@ccclass("State")
export class State {
@property()
public name: string = "";
@property({ type: cc.AnimationClip })
public clip: cc.AnimationClip = null;
@property()
public transitionTo: string = "";
}
@ccclass
@menu("Plug-in/Animation/Animator")
@requireComponent(cc.Animation)
export class Animator extends cc.Component {
@property({ displayName: "Default State" })
public defaultState: string = "";
@property({ type: State })
public states: State[] = [];
public nowPlayName: string = "";
_animation: cc.Animation = null;
/** 動畫速度 */
private _speed: number = 1;
get animation(): cc.Animation {
if (this._animation == null) {
this._animation = this.node.getComponent(cc.Animation);
}
return this._animation;
}
onLoad(): void {
if (CC_DEV) {
let animationClip: cc.AnimationClip[] = this.animation.getClips();
for (let s of this.states) {
let state: State = null;
for (let i: number = 0; i < animationClip.length; i++) {
const clip: cc.AnimationClip = animationClip[i];
if (s.clip != null && s.clip.name === clip.name) {
state = s;
break;
}
}
if (state === null) {
console.error(`node: ${this.node.name}, anim: ${s.clip?.name}, 動畫沒有掛在Animation上面`);
}
}
}
}
onEnable(): void {
this.stopState();
if (this.defaultState !== "") {
this.playState(this.defaultState);
}
}
onDisable(): void {
this.stopState();
}
/**
* runStateAndWait()
* @param name State的名稱
* @param callback callback transitionTo才會觸發
*/
public *runStateAndWait(name: string, callback: Function = null): any {
if (name === "") {
return;
}
this.animation.stop();
this.nowPlayName = name;
let state: State = null;
for (let s of this.states) {
if (s.name === name && s.clip != null && s.clip.isValid) {
state = s;
break;
}
}
if (state == null) {
return;
}
let animationState: cc.AnimationState = this.animation.play(state.clip.name);
animationState.speed = this.animation.currentClip.speed * this._speed;
this.animation.sample(animationState.name);
if (animationState.duration) {
yield CoroutineV2.WaitTime(animationState.duration);
}
if (callback && !state.transitionTo) {
callback();
}
yield* this.runStateAndWait(state.transitionTo, callback);
}
/** playState(動作機只會接一次動畫) */
public playState(name: string, callback: Function = null): void {
if (!this.node?.activeInHierarchy) {
cc.warn(`Animator error name: ${this.node.name}, activeInHierarchy: ${this.node.activeInHierarchy}`);
}
CoroutineV2.Single(this.runStateAndWait(name, callback)).Start(this);
}
/** playState(隨機) */
public playRandomState(callback: Function = null): void {
let random: number = RandomEx.GetInt(0, this.states.length);
let state: State = this.states[random];
this.playState(state.name, callback);
}
public stopState(): void {
CoroutineV2.StopCoroutinesBy(this);
this.nowPlayName = "";
}
/**
* (Speed在乘上倍率)
* @param speed
*/
public SetSpeed(speed: number): void {
this._speed = speed;
}
public getAnimTime(name: string, isGetNext: boolean = false): number {
for (let s of this.states) {
if (s.name === name && s.clip != null) {
let time: number = s.clip.duration / this._speed;
if (isGetNext && s.transitionTo !== "") {
time += this.getAnimTime(s.transitionTo, true);
}
return time;
}
}
return null;
}
/**
*
* @param name Animator設定的動畫名稱
* @param time
* @example
* this._anim.GotoTimeAndPause(name, 0);
*/
public GotoTimeAndPause(name: string, time: number = 0): void {
let clipName: string = null;
for (let s of this.states) {
if (s.name === name && s.clip != null) {
clipName = s.clip.name;
}
}
if (!clipName) {
cc.error(`GotoFrameAndPause get clip error: ${name}`);
return;
}
this.animation.play(clipName);
this.animation.stop(clipName);
this.animation.setCurrentTime(time);
this.animation.sample(clipName);
}
}