[add] Engine

This commit is contained in:
2022-08-26 16:48:17 +08:00
parent f67e566f2a
commit d9c19f096c
197 changed files with 10626 additions and 0 deletions

View File

@@ -0,0 +1,16 @@
const { ccclass, requireComponent, menu } = cc._decorator;
@ccclass
@menu("Plug-in/Animation/AnimationAutoPlay")
@requireComponent(cc.Animation)
export default class AnimationAutoPlay extends cc.Component {
onEnable() {
let anim = this.getComponent(cc.Animation);
if (anim != null) {
let animationState = anim.play();
anim.sample(animationState.name);
}
}
}

View File

@@ -0,0 +1,10 @@
{
"ver": "1.1.0",
"uuid": "c51bb156-b283-4e24-a738-317650150b9d",
"importer": "typescript",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View File

@@ -0,0 +1,153 @@
const { ccclass, property, requireComponent, menu } = cc._decorator;
@ccclass("State_AnimationRandomPlay")
export class State_AnimationRandomPlay {
@property({ displayName: "最少時間", type: cc.Float })
public mintime: number = 0;
@property({ displayName: "最多時間", type: cc.Float })
public maxtime: number = 0;
@property({ displayName: "權重", type: cc.Integer })
public weight: number = 0;
@property({ displayName: "動畫", type: cc.AnimationClip })
public clip: cc.AnimationClip = null;
}
@ccclass
@menu("Plug-in/Animation/AnimationRandomPlay")
@requireComponent(cc.Animation)
/** 可以根據權重決定多久後隨機播放甚麼動畫 */
export class AnimationRandomPlay extends cc.Component {
//#region public 外調參數
@property({ type: State_AnimationRandomPlay })
public states: State_AnimationRandomPlay[] = [];
//#endregion
//#region public 屬性
public nowPlayName: string = "";
public nextPlayName: string = "";
public nextPlayTime: number = null;
//#endregion
//#region private 屬性
private _animation: cc.Animation = null;
private _weightAll: number[] = [];
private _weightAllNum: number = 0;
//#endregion
//#region get set
get animation(): cc.Animation {
if (this._animation == null) {
this._animation = this.node.getComponent(cc.Animation);
}
return this._animation;
}
//#endregion
//#region Lifecycle
onLoad(): void {
let self: this = this;
let weight: number = 0;
for (let i: number = 0; i < this.states.length; i++) {
weight += this.states[i].weight;
this._weightAll.push(weight);
this._weightAllNum += this.states[i].weight;
}
// 一般動畫
this.animation.on("finished", () => {
self.GetNextAnim();
}, this);
// 不一般動畫 (X
// Loop動畫
this.animation.on("lastframe", () => {
self.animation.setCurrentTime(0);
self.animation.stop();
self.GetNextAnim();
}, this);
}
onEnable(): void {
this.GetNextAnim();
}
onDisable(): void {
this.nextPlayName = "";
this.nextPlayTime = null;
this.animation.setCurrentTime(0);
this.animation.stop();
}
onDestroy(): void {
this.animation.targetOff(this);
// let self: this = this;
// this.animation.off("finished", () => {
// self.GetNextAnim();
// }, this);
// this.animation.off("lastframe", () => {
// self.animation.setCurrentTime(0);
// self.animation.stop();
// self.GetNextAnim();
// }, this);
}
update(dt: number): void {
let time: number = Date.now();
if (this.nextPlayTime && time >= this.nextPlayTime) {
this.nowPlayName = this.nextPlayName;
if (this.animation.getAnimationState(this.nextPlayName)) {
this.animation.play(this.nextPlayName);
} else {
console.error(`this node(${this.node.name}) not has animation(${this.nextPlayName})`);
this.animation.addClip(this.GetClip_From_states(this.nextPlayName));
if (this.animation.getAnimationState(this.nextPlayName)) {
console.warn(`this node(${this.node.name}) add animation(${this.nextPlayName})`);
this.animation.play(this.nextPlayName);
}
}
this.nextPlayName = "";
this.nextPlayTime = null;
}
}
//#endregion
//#region Custom Function
/** 取得下一隻動畫的時間&名稱 */
GetNextAnim(): void {
let random: number = Math.floor(Math.random() * this._weightAllNum) + 1;
for (let i: number = 0; i < this._weightAll.length; i++) {
if (random <= this._weightAll[i]) {
let time: number = Math.floor(Math.random() * (this.states[i].maxtime - this.states[i].mintime + 1)) + this.states[i].mintime;
this.nextPlayTime = Date.now() + (time * 1000);
this.nextPlayName = this.states[i].clip.name;
// if (CC_DEBUG) {
// let date: Date = new Date(this.nextPlayTime);
// console.log(`nextWaitTime: ${time}, nextPlayTime: ${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}, nextPlayName: ${this.nextPlayName}`);
// }
break;
}
}
}
/** 取得下一隻動畫的時間&名稱 */
GetClip_From_states(name: string): cc.AnimationClip {
for (let i: number = 0; i < this.states.length; i++) {
if (this.states[i].clip.name === name) {
return this.states[i].clip;
}
}
}
//#endregion
}

View File

@@ -0,0 +1,10 @@
{
"ver": "1.1.0",
"uuid": "66e6675c-c922-4952-9eb1-dc55aece8dc3",
"importer": "typescript",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View File

@@ -0,0 +1,163 @@
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);
}
}

View File

@@ -0,0 +1,10 @@
{
"ver": "1.1.0",
"uuid": "e0690d25-55e6-4fb2-9932-2231d0125e60",
"importer": "typescript",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View File

@@ -0,0 +1,184 @@
import { CoroutineV2 } from "../../CatanEngine/CoroutineV2/CoroutineV2";
const { ccclass, property, requireComponent, menu, executeInEditMode } = cc._decorator;
// /** Clipname */
// export enum Clipname {
// None,
// }
@ccclass("SPState")
export class SPState {
@property()
public name: string = "";
public clip: cc.AnimationClip = null;
@property({ displayName: "Spine動畫名稱", tooltip: "Spine動畫名稱" })
public clipname: string = "";
// @property({ displayName: "clipname1", type: cc.Enum(Clipname) })
// public clipname1: Clipname = Clipname.None;
@property()
public isloop: boolean = false;
@property()
public transitionTo: string = "";
}
@ccclass
// @executeInEditMode
@menu("Plug-in/Animation/SPAnimator")
@requireComponent(sp.Skeleton)
export class SPAnimator extends cc.Component {
@property({ displayName: "Default State" })
public defaultState: string = "";
@property({ type: SPState })
public states: SPState[] = [];
public nowPlayName: string = "";
private _isInit: boolean = false;
_animation: sp.Skeleton = null;
/** 動畫速度 */
private _speed: number = 1;
get animation(): sp.Skeleton {
if (this._animation == null) {
this._animation = this.node.getComponent(sp.Skeleton);
}
return this._animation;
}
protected onLoad(): void {
if (this._isInit) {
return;
}
if (!this.node.activeInHierarchy) {
cc.error(`node: ${this.node.name}, activeInHierarchy: ${this.node.activeInHierarchy}, 動畫沒有打開無法初始化`);
return;
}
let animationClip: cc.AnimationClip[] = this.animation["skeletonData"]["_skeletonCache"].animations;
// if (CC_EDITOR) {
// for (let i: number = 0; i < animationClip.length; i++) {
// const clip: cc.AnimationClip = animationClip[i];
// Clipname[clip.name] = i;
// Clipname[i.toString()] = clip.name;
// cc.log(`[${i}] ${clip.name}`);
// }
// return;
// }
for (let s of this.states) {
let state: SPState = null;
for (let i: number = 0; i < animationClip.length; i++) {
const clip: cc.AnimationClip = animationClip[i];
if (s.clipname === clip.name && s.clipname != null) {
s.clip = clip;
state = s;
break;
}
}
if (CC_DEV) {
if (state === null) {
console.error(`node: ${this.node.name}, anim: ${s.clipname}, 動畫沒有掛在Animation上面`);
}
}
}
this._isInit = true;
}
protected onEnable(): void {
this.stopState();
if (this.defaultState !== "") {
this.playState(this.defaultState);
}
}
protected onDisable(): void {
this.stopState();
}
/**
* runStateAndWait(動作機只會接一次動畫)
* @param name 動畫State的名稱
* @param callback callback 沒有transitionTo才會觸發
*/
public *runStateAndWait(name: string, callback: Function = null): any {
if (!this._isInit) {
this.onLoad();
}
if (name === "") {
return;
}
this.animation.setToSetupPose();
let lastPlayName: string = "";
for (let s of this.states) {
if (s.name === this.nowPlayName && s.clipname != null) {
lastPlayName = s.clipname;
break;
}
}
this.nowPlayName = name;
let state: SPState = null;
for (let s of this.states) {
if (s.name === name && s.clipname != null) {
state = s;
break;
}
}
if (state == null) {
return;
}
// let animationState: cc.AnimationState = this.animation.play(state.clipname);
if (lastPlayName) {
this.animation.setMix(lastPlayName, state.clipname, 0.5);
}
this.animation.setAnimation(0, state.clipname, state.isloop);
// let animationState: sp.spine.TrackEntry = this.animation.setAnimation(0, state.clipname, state.isloop);
// animationState.speed = this.animation.currentClip.speed * this._speed;
// this.animation.sample(animationState.name);
if (state.clip.duration) {
yield CoroutineV2.WaitTime(state.clip.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(`SPAnimator 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: SPState = this.states[random];
// this.playState(state.name, callback);
// }
public stopState(): void {
CoroutineV2.StopCoroutinesBy(this);
this.nowPlayName = "";
this.animation.clearTracks();
}
// /**
// * 設定動畫速率(原有動畫的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.clipname != null) {
let time: number = s.clip.duration / this._speed;
if (isGetNext && s.transitionTo !== "") {
time += this.getAnimTime(s.transitionTo, true);
}
return time;
}
}
return null;
}
}

View File

@@ -0,0 +1,10 @@
{
"ver": "1.1.0",
"uuid": "636825f6-4e9a-4b5b-991d-8bc1afd3a1ca",
"importer": "typescript",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View File

@@ -0,0 +1,48 @@
cc.game.once(cc.game.EVENT_ENGINE_INITED, function () {
cc.js.mixin(sp.Skeleton.prototype, {
update(dt) {
// if (CC_EDITOR) return;
if (CC_EDITOR) {
cc.engine._animatingInEditMode = 1;
cc.engine.animatingInEditMode = 1;
}
if (this.paused) return;
dt *= this.timeScale * sp.timeScale;
if (this.isAnimationCached()) {
// Cache mode and has animation queue.
if (this._isAniComplete) {
if (this._animationQueue.length === 0 && !this._headAniInfo) {
let frameCache = this._frameCache;
if (frameCache && frameCache.isInvalid()) {
frameCache.updateToFrame();
let frames = frameCache.frames;
this._curFrame = frames[frames.length - 1];
}
return;
}
if (!this._headAniInfo) {
this._headAniInfo = this._animationQueue.shift();
}
this._accTime += dt;
if (this._accTime > this._headAniInfo.delay) {
let aniInfo = this._headAniInfo;
this._headAniInfo = null;
this.setAnimation(0, aniInfo.animationName, aniInfo.loop);
}
return;
}
this._updateCache(dt);
} else {
this._updateRealtime(dt);
}
}
});
});

View File

@@ -0,0 +1,10 @@
{
"ver": "1.1.0",
"uuid": "9d832050-308c-4cd9-87c6-d8542ea9c3f3",
"importer": "javascript",
"isPlugin": true,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": true,
"subMetas": {}
}