diff --git a/packages/common/src/logger.json b/packages/common/src/logger.json index 80d4ced..e2986b4 100644 --- a/packages/common/src/logger.json +++ b/packages/common/src/logger.json @@ -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." } diff --git a/packages/render-assets/src/animater.ts b/packages/render-assets/src/animater.ts index 276217f..5f399b2 100644 --- a/packages/render-assets/src/animater.ts +++ b/packages/render-assets/src/animater.ts @@ -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 return true; } - abstract open(init: T): Generator | null; + abstract open(init: T): Generator | null; - abstract cycled(init: T): Generator | null; + abstract cycled(init: T): Generator | null; } /** * 行动画控制器,将贴图按照从上到下的顺序依次组成帧动画,创建时传入的参数代表帧数 */ export class TextureRowAnimater extends FrameBasedAnimater { - *open(): Generator | null { + *open(): Generator | 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 | null { + *cycled(): Generator | 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 { 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 { * 列动画控制器,将贴图按照从左到右的顺序依次组成帧动画,创建时传入的参数代表帧数 */ export class TextureColumnAnimater extends FrameBasedAnimater { - *open(): Generator | null { + *open(): Generator | 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 | null { + *cycled(): Generator | 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 { 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 +{ + texture: ITexture | 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 | 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 | null { + throw new Error('Method not implemented.'); + } +} + export interface IAnimaterTranslatedInit { /** 以此矩形作为参考矩形 */ readonly rect: Readonly; @@ -141,11 +213,15 @@ export class TextureAnimaterTranslated implements AdderImplements { texture: AdderTexture | null = null; create(texture: ITexture): void { + if (this.texture) { + logger.warn(70); + return; + } this.texture = texture; } *output( - ani: Generator, + ani: Generator, origin: Readonly, rect: Readonly ) { @@ -157,13 +233,11 @@ export class TextureAnimaterTranslated implements AdderImplements { 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 implements AdderImplements { open( init: IAnimaterTranslatedInit - ): Generator | null { + ): Generator | 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 implements AdderImplements { cycled( init: IAnimaterTranslatedInit - ): Generator | null { + ): Generator | null { const ani = init.texture.cycled(init.data); const origin = init.texture.static().rect; if (!ani || !origin) return null; diff --git a/packages/render-assets/src/composer.ts b/packages/render-assets/src/composer.ts index 298a599..79cec35 100644 --- a/packages/render-assets/src/composer.ts +++ b/packages/render-assets/src/composer.ts @@ -55,7 +55,8 @@ interface GridNeededSize { } /** - * 网格组合器,将等大小的贴图组合成图集,要求每个贴图的尺寸一致 + * 网格组合器,将等大小的贴图组合成图集,要求每个贴图的尺寸一致。 + * 组合时按照先从左到右,再从上到下的顺序组合。 */ export class TextureGridComposer implements TranslatedComposer diff --git a/packages/render-assets/src/texture.ts b/packages/render-assets/src/texture.ts index ffd8e28..b778dfc 100644 --- a/packages/render-assets/src/texture.ts +++ b/packages/render-assets/src/texture.ts @@ -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 implements ITexture { 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 | null { + dynamic(data: A): Generator | null { if (!this.animater) return null; return this.animater.open(data); } - cycled(data: A): Generator | null { + cycled(data: A): Generator | null { if (!this.animater) return null; return this.animater.cycled(data); } diff --git a/packages/render-assets/src/types.ts b/packages/render-assets/src/types.ts index b1944c4..7c104a4 100644 --- a/packages/render-assets/src/types.ts +++ b/packages/render-assets/src/types.ts @@ -12,20 +12,13 @@ export interface IRect { h: number; } -export interface ITextureSingleRenderable { +export interface ITextureRenderable { /** 可渲染贴图对象的图像源 */ readonly source: SizedCanvasImageSource; /** 贴图裁剪区域 */ readonly rect: Readonly; } -export interface ITextureListedRenderable { - /** 可渲染贴图对象的图像源 */ - readonly source: SizedCanvasImageSource; - /** 贴图裁剪区域 */ - readonly rect: Readonly[]; -} - export interface ITextureComposedData { /** 这个纹理图集的贴图对象 */ readonly texture: ITexture; @@ -69,13 +62,13 @@ export interface ITextureAnimater { * 开始动画序列 * @param init 动画初始化参数 */ - open(init: I): Generator | null; + open(init: I): Generator | null; /** * 开始循环动画序列 * @param init 动画初始化参数 */ - cycled(init: I): Generator | null; + cycled(init: I): Generator | null; } export interface ITexture { @@ -111,19 +104,19 @@ export interface ITexture { /** * 获取整张图的可渲染对象 */ - static(): ITextureSingleRenderable; + static(): ITextureRenderable; /** * 获取一系列动画可渲染对象,不循环,按帧数依次排列 * @param data 传递给动画控制器的初始化参数 */ - dynamic(data: A): Generator | null; + dynamic(data: A): Generator | null; /** * 获取无限循环的动画可渲染对象 * @param data 传递给动画控制器的初始化参数 */ - cycled(data: A): Generator | null; + cycled(data: A): Generator | null; /** * 释放此贴图的资源,将不能再被使用