mirror of
https://github.com/unanmed/HumanBreak.git
synced 2025-11-13 04:52:57 +08:00
190 lines
6.0 KiB
TypeScript
190 lines
6.0 KiB
TypeScript
import { logger } from '@motajs/common';
|
|
import {
|
|
IRect,
|
|
ITexture,
|
|
ITextureAnimater,
|
|
ITextureListedRenderable
|
|
} from './types';
|
|
|
|
/**
|
|
* 基于帧的动画控制器,创建时传入的参数代表帧数,生成动画时传入的参数自定义
|
|
*/
|
|
export abstract class FrameBasedAnimater<T>
|
|
implements ITextureAnimater<number, T>
|
|
{
|
|
texture: ITexture<number, T> | null = null;
|
|
|
|
/** 动画的总帧数 */
|
|
protected frames: number = 0;
|
|
|
|
create(texture: ITexture, data: number): void {
|
|
if (this.texture) {
|
|
logger.warn(70);
|
|
return;
|
|
}
|
|
this.texture = texture;
|
|
this.frames = data;
|
|
}
|
|
|
|
/**
|
|
* 判断当前动画控制器是否已经初始化完毕并开始生成动画
|
|
*/
|
|
protected check(): boolean {
|
|
if (!this.texture || this.frames === 0) {
|
|
logger.warn(71);
|
|
return false;
|
|
}
|
|
if (this.texture.height % this.frames !== 0) {
|
|
logger.warn(72);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
abstract open(init: T): Generator<ITextureListedRenderable> | null;
|
|
|
|
abstract cycled(init: T): Generator<ITextureListedRenderable> | null;
|
|
}
|
|
|
|
/**
|
|
* 行动画控制器,将贴图按照从上到下的顺序依次组成帧动画,创建时传入的参数代表帧数
|
|
*/
|
|
export class TextureRowAnimater extends FrameBasedAnimater<void> {
|
|
*open(): Generator<ITextureListedRenderable> | null {
|
|
if (!this.check()) return null;
|
|
const renderable = this.texture!.static();
|
|
const { x: ox, y: oy } = renderable.rect;
|
|
const { width: w, height } = this.texture!;
|
|
const h = height / this.frames;
|
|
for (let i = 0; i < this.frames; i++) {
|
|
const renderable: ITextureListedRenderable = {
|
|
source: this.texture!.source,
|
|
rect: [{ x: ox, y: i * h + oy, w, h }]
|
|
};
|
|
yield renderable;
|
|
}
|
|
}
|
|
|
|
*cycled(): Generator<ITextureListedRenderable> | null {
|
|
if (!this.check()) return null;
|
|
const renderable = this.texture!.static();
|
|
const { x: ox, y: oy } = renderable.rect;
|
|
const { width: w, height } = this.texture!;
|
|
const h = height / this.frames;
|
|
let i = 0;
|
|
while (true) {
|
|
if (i === this.frames) i = 0;
|
|
const renderable: ITextureListedRenderable = {
|
|
source: this.texture!.source,
|
|
rect: [{ x: ox, y: i * h + oy, w, h }]
|
|
};
|
|
yield renderable;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 列动画控制器,将贴图按照从左到右的顺序依次组成帧动画,创建时传入的参数代表帧数
|
|
*/
|
|
export class TextureColumnAnimater extends FrameBasedAnimater<void> {
|
|
*open(): Generator<ITextureListedRenderable> | null {
|
|
if (!this.check()) return null;
|
|
const renderable = this.texture!.static();
|
|
const { x: ox, y: oy } = renderable.rect;
|
|
const { width, height: h } = this.texture!;
|
|
const w = width / this.frames;
|
|
for (let i = 0; i < this.frames; i++) {
|
|
const renderable: ITextureListedRenderable = {
|
|
source: this.texture!.source,
|
|
rect: [{ x: i * width + ox, y: oy, w, h }]
|
|
};
|
|
yield renderable;
|
|
}
|
|
}
|
|
|
|
*cycled(): Generator<ITextureListedRenderable> | null {
|
|
if (!this.check()) return null;
|
|
const renderable = this.texture!.static();
|
|
const { x: ox, y: oy } = renderable.rect;
|
|
const { width, height: h } = this.texture!;
|
|
const w = width / this.frames;
|
|
let i = 0;
|
|
while (true) {
|
|
if (i === this.frames) i = 0;
|
|
const renderable: ITextureListedRenderable = {
|
|
source: this.texture!.source,
|
|
rect: [{ x: i * w + ox, y: oy, w, h }]
|
|
};
|
|
yield renderable;
|
|
}
|
|
}
|
|
}
|
|
|
|
export interface IAnimaterTranslatedInit<T> {
|
|
/** 以此矩形作为参考矩形 */
|
|
readonly rect: Readonly<IRect>;
|
|
/** 传递给原先的动画控制器的参数 */
|
|
readonly data: T;
|
|
/** 原本所属的纹理 */
|
|
readonly texture: ITexture<unknown, T>;
|
|
}
|
|
|
|
type AdderImplements<T> = ITextureAnimater<void, IAnimaterTranslatedInit<T>>;
|
|
|
|
type AdderTexture<T> = ITexture<void, IAnimaterTranslatedInit<T>>;
|
|
|
|
/**
|
|
* 对一个动画控制器执行偏移操作的控制器,一般用于图集上。
|
|
* 创建时传入的参数代表要执行偏移操作的动画控制器,动画参数包含两部分,一个是参考矩形,一个是传递给原先动画控制器的参数
|
|
*/
|
|
export class TextureAnimaterTranslated<T> implements AdderImplements<T> {
|
|
texture: AdderTexture<T> | null = null;
|
|
|
|
create(texture: ITexture): void {
|
|
this.texture = texture;
|
|
}
|
|
|
|
*output(
|
|
ani: Generator<ITextureListedRenderable>,
|
|
origin: Readonly<IRect>,
|
|
rect: Readonly<IRect>
|
|
) {
|
|
const { x: ox, y: oy } = origin;
|
|
const { x: nx, y: ny } = rect;
|
|
const source = this.texture!.source;
|
|
|
|
while (true) {
|
|
const next = ani.next();
|
|
if (next.done) break;
|
|
const renderable = next.value;
|
|
const list: IRect[] = [];
|
|
renderable.rect.forEach(({ x, y, w, h }) => {
|
|
list.push({ x: x - ox + nx, y: y - oy + ny, w, h });
|
|
});
|
|
const res: ITextureListedRenderable = {
|
|
source,
|
|
rect: list
|
|
};
|
|
yield res;
|
|
}
|
|
}
|
|
|
|
open(
|
|
init: IAnimaterTranslatedInit<T>
|
|
): Generator<ITextureListedRenderable> | null {
|
|
const ani = init.texture.dynamic(init.data);
|
|
const origin = init.texture.static().rect;
|
|
if (!ani || !origin) return null;
|
|
return this.output(ani, origin, init.rect);
|
|
}
|
|
|
|
cycled(
|
|
init: IAnimaterTranslatedInit<T>
|
|
): Generator<ITextureListedRenderable> | null {
|
|
const ani = init.texture.cycled(init.data);
|
|
const origin = init.texture.static().rect;
|
|
if (!ani || !origin) return null;
|
|
return this.output(ani, origin, init.rect);
|
|
}
|
|
}
|