164 lines
5.3 KiB
TypeScript
164 lines
5.3 KiB
TypeScript
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);
|
|
}
|
|
}
|