feat: scan animater & refactor: 动画输出类型

This commit is contained in:
unanmed 2025-10-20 12:18:37 +08:00
parent fc1c0b14d6
commit 77246b571a
5 changed files with 117 additions and 49 deletions

View File

@ -105,6 +105,7 @@
"71": "Cannot start animate when animater not created on texture.",
"72": "Frame count delivered to frame-based animater need to exactly divide texture's height.",
"73": "Consistent size is needed when using WebGL2 composer.",
"74": "Frame size not match texture's size, incomplete data will be skipped.",
"1001": "Item-detail extension needs 'floor-binder' and 'floor-damage' extension as dependency.",
"1101": "Cannot add new effect to point effect instance, for there's no more reserve space for it. Please increase the max count of the instance."
}

View File

@ -1,10 +1,5 @@
import { logger } from '@motajs/common';
import {
IRect,
ITexture,
ITextureAnimater,
ITextureListedRenderable
} from './types';
import { IRect, ITexture, ITextureAnimater, ITextureRenderable } from './types';
/**
*
@ -41,31 +36,31 @@ export abstract class FrameBasedAnimater<T>
return true;
}
abstract open(init: T): Generator<ITextureListedRenderable> | null;
abstract open(init: T): Generator<ITextureRenderable> | null;
abstract cycled(init: T): Generator<ITextureListedRenderable> | null;
abstract cycled(init: T): Generator<ITextureRenderable> | null;
}
/**
*
*/
export class TextureRowAnimater extends FrameBasedAnimater<void> {
*open(): Generator<ITextureListedRenderable> | null {
*open(): Generator<ITextureRenderable> | 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 = {
const renderable: ITextureRenderable = {
source: this.texture!.source,
rect: [{ x: ox, y: i * h + oy, w, h }]
rect: { x: ox, y: i * h + oy, w, h }
};
yield renderable;
}
}
*cycled(): Generator<ITextureListedRenderable> | null {
*cycled(): Generator<ITextureRenderable> | null {
if (!this.check()) return null;
const renderable = this.texture!.static();
const { x: ox, y: oy } = renderable.rect;
@ -74,9 +69,9 @@ export class TextureRowAnimater extends FrameBasedAnimater<void> {
let i = 0;
while (true) {
if (i === this.frames) i = 0;
const renderable: ITextureListedRenderable = {
const renderable: ITextureRenderable = {
source: this.texture!.source,
rect: [{ x: ox, y: i * h + oy, w, h }]
rect: { x: ox, y: i * h + oy, w, h }
};
yield renderable;
}
@ -87,22 +82,22 @@ export class TextureRowAnimater extends FrameBasedAnimater<void> {
*
*/
export class TextureColumnAnimater extends FrameBasedAnimater<void> {
*open(): Generator<ITextureListedRenderable> | null {
*open(): Generator<ITextureRenderable> | 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 = {
const renderable: ITextureRenderable = {
source: this.texture!.source,
rect: [{ x: i * width + ox, y: oy, w, h }]
rect: { x: i * width + ox, y: oy, w, h }
};
yield renderable;
}
}
*cycled(): Generator<ITextureListedRenderable> | null {
*cycled(): Generator<ITextureRenderable> | null {
if (!this.check()) return null;
const renderable = this.texture!.static();
const { x: ox, y: oy } = renderable.rect;
@ -111,15 +106,92 @@ export class TextureColumnAnimater extends FrameBasedAnimater<void> {
let i = 0;
while (true) {
if (i === this.frames) i = 0;
const renderable: ITextureListedRenderable = {
const renderable: ITextureRenderable = {
source: this.texture!.source,
rect: [{ x: i * w + ox, y: oy, w, h }]
rect: { x: i * w + ox, y: oy, w, h }
};
yield renderable;
}
}
}
export interface IScanAnimaterCreate {
/** 每帧的宽度 */
readonly width: number;
/** 每帧的高度 */
readonly height: number;
/** 总帧数 */
readonly frames: number;
}
/**
*
*/
export class TextureScanAnimater
implements ITextureAnimater<IScanAnimaterCreate, void>
{
texture: ITexture<IScanAnimaterCreate, void> | null = null;
private width: number = 0;
private height: number = 0;
private frames: number = 0;
private frameX: number = 0;
private frameY: number = 0;
create(texture: ITexture, data: IScanAnimaterCreate): void {
if (this.texture) {
logger.warn(70);
return;
}
this.texture = texture;
this.width = data.width;
this.height = data.height;
this.frames = data.frames;
// 如果尺寸不匹配
if (
texture.width % data.width !== 0 ||
texture.height % data.height !== 0
) {
logger.warn(74);
}
const frameX = Math.floor(texture.width / data.width);
const frameY = Math.floor(texture.height / data.height);
const possibleFrames = frameX * frameY;
// 如果传入的帧数超出了可能的帧数上限
if (this.frames > possibleFrames) {
this.frames = possibleFrames;
}
}
*open(): Generator<ITextureRenderable, void> | null {
const texture = this.texture;
if (!texture) return null;
const w = this.width;
const h = this.height;
for (let y = 0; y < this.frameY; y++) {
for (let x = 0; x < this.frameX; x++) {
const rect: IRect = { x: x * w, y: y * h, w, h };
const data: ITextureRenderable = {
source: texture.source,
rect
};
yield data;
}
}
}
cycled(): Generator<ITextureRenderable, void> | null {
throw new Error('Method not implemented.');
}
}
export interface IAnimaterTranslatedInit<T> {
/** 以此矩形作为参考矩形 */
readonly rect: Readonly<IRect>;
@ -141,11 +213,15 @@ export class TextureAnimaterTranslated<T> implements AdderImplements<T> {
texture: AdderTexture<T> | null = null;
create(texture: ITexture): void {
if (this.texture) {
logger.warn(70);
return;
}
this.texture = texture;
}
*output(
ani: Generator<ITextureListedRenderable>,
ani: Generator<ITextureRenderable>,
origin: Readonly<IRect>,
rect: Readonly<IRect>
) {
@ -157,13 +233,11 @@ export class TextureAnimaterTranslated<T> implements AdderImplements<T> {
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 = {
const { x, y, w, h } = renderable.rect;
const translated: IRect = { x: x - ox + nx, y: y - oy + ny, w, h };
const res: ITextureRenderable = {
source,
rect: list
rect: translated
};
yield res;
}
@ -171,7 +245,7 @@ export class TextureAnimaterTranslated<T> implements AdderImplements<T> {
open(
init: IAnimaterTranslatedInit<T>
): Generator<ITextureListedRenderable> | null {
): Generator<ITextureRenderable> | null {
const ani = init.texture.dynamic(init.data);
const origin = init.texture.static().rect;
if (!ani || !origin) return null;
@ -180,7 +254,7 @@ export class TextureAnimaterTranslated<T> implements AdderImplements<T> {
cycled(
init: IAnimaterTranslatedInit<T>
): Generator<ITextureListedRenderable> | null {
): Generator<ITextureRenderable> | null {
const ani = init.texture.cycled(init.data);
const origin = init.texture.static().rect;
if (!ani || !origin) return null;

View File

@ -55,7 +55,8 @@ interface GridNeededSize {
}
/**
*
*
*
*/
export class TextureGridComposer<T>
implements TranslatedComposer<IGridComposerData, T>

View File

@ -2,8 +2,7 @@ import { logger } from '@motajs/common';
import {
ITexture,
ITextureAnimater,
ITextureListedRenderable,
ITextureSingleRenderable,
ITextureRenderable,
ITextureSplitter,
SizedCanvasImageSource
} from './types';
@ -63,20 +62,20 @@ export class Texture<T = unknown, A = unknown> implements ITexture<T, A> {
animater.create(this, data);
}
static(): ITextureSingleRenderable {
const renderable: ITextureSingleRenderable = {
static(): ITextureRenderable {
const renderable: ITextureRenderable = {
source: this.source,
rect: { x: this.cx, y: this.cy, w: this.width, h: this.height }
};
return renderable;
}
dynamic(data: A): Generator<ITextureListedRenderable> | null {
dynamic(data: A): Generator<ITextureRenderable> | null {
if (!this.animater) return null;
return this.animater.open(data);
}
cycled(data: A): Generator<ITextureListedRenderable> | null {
cycled(data: A): Generator<ITextureRenderable> | null {
if (!this.animater) return null;
return this.animater.cycled(data);
}

View File

@ -12,20 +12,13 @@ export interface IRect {
h: number;
}
export interface ITextureSingleRenderable {
export interface ITextureRenderable {
/** 可渲染贴图对象的图像源 */
readonly source: SizedCanvasImageSource;
/** 贴图裁剪区域 */
readonly rect: Readonly<IRect>;
}
export interface ITextureListedRenderable {
/** 可渲染贴图对象的图像源 */
readonly source: SizedCanvasImageSource;
/** 贴图裁剪区域 */
readonly rect: Readonly<IRect>[];
}
export interface ITextureComposedData<T = unknown, A = unknown> {
/** 这个纹理图集的贴图对象 */
readonly texture: ITexture<T, A>;
@ -69,13 +62,13 @@ export interface ITextureAnimater<T, I> {
*
* @param init
*/
open(init: I): Generator<ITextureListedRenderable, void> | null;
open(init: I): Generator<ITextureRenderable, void> | null;
/**
*
* @param init
*/
cycled(init: I): Generator<ITextureListedRenderable, void> | null;
cycled(init: I): Generator<ITextureRenderable, void> | null;
}
export interface ITexture<T = unknown, A = unknown> {
@ -111,19 +104,19 @@ export interface ITexture<T = unknown, A = unknown> {
/**
*
*/
static(): ITextureSingleRenderable;
static(): ITextureRenderable;
/**
*
* @param data
*/
dynamic(data: A): Generator<ITextureListedRenderable, void> | null;
dynamic(data: A): Generator<ITextureRenderable, void> | null;
/**
*
* @param data
*/
cycled(data: A): Generator<ITextureListedRenderable, void> | null;
cycled(data: A): Generator<ITextureRenderable, void> | null;
/**
* 使