From 4f3154d497b3d358c5e80535e51b98221451f6ec Mon Sep 17 00:00:00 2001 From: unanmed <1319491857@qq.com> Date: Mon, 24 Nov 2025 23:53:10 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=8B=87=E5=A3=AB=E7=A7=BB=E5=8A=A8?= =?UTF-8?q?=E5=85=BC=E5=AE=B9=E4=B8=8E=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../client-base/src/material/builder.ts | 11 +- .../client-base/src/material/index.ts | 1 + .../client-base/src/material/manager.ts | 5 +- .../client-base/src/material/types.ts | 5 + .../client-modules/src/render/commonIns.ts | 15 +- .../client-modules/src/render/index.tsx | 7 +- .../client-modules/src/render/map/element.ts | 4 +- .../src/render/map/extension/hero.ts | 55 +++-- .../src/render/map/extension/index.ts | 1 + .../src/render/map/extension/manager.ts | 28 +++ .../src/render/map/extension/types.ts | 22 +- .../client-modules/src/render/map/index.ts | 2 + .../client-modules/src/render/map/moving.ts | 3 + .../client-modules/src/render/map/renderer.ts | 18 +- .../src/render/map/shader/map.vert | 2 +- .../client-modules/src/render/map/types.ts | 11 +- .../client-modules/src/render/map/vertex.ts | 27 +-- packages-user/data-base/src/game.ts | 2 + packages-user/data-state/src/hero/state.ts | 3 +- packages-user/data-state/src/legacy/move.ts | 220 +++++++++--------- packages-user/data-state/src/shared.ts | 2 + packages-user/entry-client/src/create.ts | 4 +- packages/common/src/logger.json | 1 + packages/render-assets/src/splitter.ts | 13 +- packages/render-assets/src/streamComposer.ts | 1 - packages/render-assets/src/texture.ts | 18 +- 26 files changed, 301 insertions(+), 180 deletions(-) create mode 100644 packages-user/client-modules/src/render/map/extension/manager.ts create mode 100644 packages-user/data-state/src/shared.ts diff --git a/packages-user/client-base/src/material/builder.ts b/packages-user/client-base/src/material/builder.ts index 1b3f73b..29b4335 100644 --- a/packages-user/client-base/src/material/builder.ts +++ b/packages-user/client-base/src/material/builder.ts @@ -105,6 +105,8 @@ class TrackedAssetData private originSourceMap: Map = new Map(); + private promises: Set> = new Set(); + constructor( readonly materials: IMaterialGetter, readonly builder: AssetBuilder @@ -137,7 +139,10 @@ class TrackedAssetData this.sourceList.set(index, source); this.skipRef.set(source, index); } else { - const bitmap = await createImageBitmap(source); + const promise = createImageBitmap(source); + this.promises.add(promise); + const bitmap = await promise; + this.promises.delete(promise); this.sourceList.set(index, bitmap); this.skipRef.set(bitmap, index); // 要把源也加到映射中,因为这里的 bitmap 与外部源并不同引用 @@ -146,5 +151,9 @@ class TrackedAssetData this.dirty(index); } + async then(): Promise { + await Promise.all([...this.promises]); + } + close(): void {} } diff --git a/packages-user/client-base/src/material/index.ts b/packages-user/client-base/src/material/index.ts index aadd600..8f74adc 100644 --- a/packages-user/client-base/src/material/index.ts +++ b/packages-user/client-base/src/material/index.ts @@ -6,6 +6,7 @@ export function createMaterial() { createAutotile(); loading.once('loaded', () => { fallbackLoad(); + loading.emit('assetBuilt'); }); } diff --git a/packages-user/client-base/src/material/manager.ts b/packages-user/client-base/src/material/manager.ts index fa84c28..36b4c5e 100644 --- a/packages-user/client-base/src/material/manager.ts +++ b/packages-user/client-base/src/material/manager.ts @@ -442,6 +442,7 @@ export class MaterialManager implements IMaterialManager { store: this.assetStore }; this.checkAssetDirty(data); + texture.toAsset(data); return assetData; } @@ -567,10 +568,10 @@ export class MaterialManager implements IMaterialManager { } assetContainsTexture(texture: ITexture): boolean { - return this.assetMap.has(texture); + return this.trackedAsset.skipRef.has(texture.source); } getTextureAsset(texture: ITexture): number | undefined { - return this.assetMap.get(texture); + return this.trackedAsset.skipRef.get(texture.source); } } diff --git a/packages-user/client-base/src/material/types.ts b/packages-user/client-base/src/material/types.ts index 2d0ce45..4903795 100644 --- a/packages-user/client-base/src/material/types.ts +++ b/packages-user/client-base/src/material/types.ts @@ -505,4 +505,9 @@ export interface ITrackedAssetData extends IDirtyTracker> { * 取消使用此图集,释放相关资源 */ close(): void; + + /** + * 等待所有打包操作结束 + */ + then(): Promise; } diff --git a/packages-user/client-modules/src/render/commonIns.ts b/packages-user/client-modules/src/render/commonIns.ts index 79395d2..9da3d48 100644 --- a/packages-user/client-modules/src/render/commonIns.ts +++ b/packages-user/client-modules/src/render/commonIns.ts @@ -1,8 +1,21 @@ import { state } from '@user/data-state'; -import { MapRenderer } from './map/renderer'; import { materials } from '@user/client-base'; +import { MapRenderer, MapExtensionManager } from './map'; /** 主地图渲染器,用于渲染游戏画面 */ export const mainMapRenderer = new MapRenderer(materials, state.layer); +/** 主地图渲染器拓展 */ +export const mainMapExtension = new MapExtensionManager(mainMapRenderer); /** 副地图渲染器,用于渲染缩略图、浏览地图等 */ // export const expandMapRenderer = new MapRenderer(materials, state.layer); + +export async function createMainExtension() { + // 算是一种妥协吧,等之后加载系统重构之后应该会清晰很多 + await materials.trackedAsset.then(); + + mainMapRenderer.useAsset(materials.trackedAsset); + const layer = state.layer.getLayerByAlias('event'); + if (layer) { + mainMapExtension.addHero(state.hero, layer); + } +} diff --git a/packages-user/client-modules/src/render/index.tsx b/packages-user/client-modules/src/render/index.tsx index 816c02f..086f307 100644 --- a/packages-user/client-modules/src/render/index.tsx +++ b/packages-user/client-modules/src/render/index.tsx @@ -11,6 +11,7 @@ import { createLegacy } from './legacy'; import { sceneController } from './scene'; import { GameTitleUI } from './ui/title'; import { createWeather } from './weather'; +import { createMainExtension } from './commonIns'; export function createGameRenderer() { const App = defineComponent(_props => { @@ -33,11 +34,15 @@ export function createRender() { createLoopMap(); createWeather(); - loading.on('loaded', () => { + loading.once('loaded', () => { sceneController.open(GameTitleUI, {}); mainRenderer.show(); }); + loading.once('assetBuilt', () => { + createMainExtension(); + }); + hook.on('restart', () => { sceneController.closeAll(); sceneController.open(GameTitleUI, {}); diff --git a/packages-user/client-modules/src/render/map/element.ts b/packages-user/client-modules/src/render/map/element.ts index 33357ae..e929aae 100644 --- a/packages-user/client-modules/src/render/map/element.ts +++ b/packages-user/client-modules/src/render/map/element.ts @@ -1,7 +1,6 @@ import { MotaOffscreenCanvas2D, RenderItem } from '@motajs/render-core'; import { ILayerState } from '@user/data-state'; import { IMapRenderer } from './types'; -import { materials } from '@user/client-base'; import { ElementNamespace, ComponentInternalInstance } from 'vue'; import { CELL_HEIGHT, CELL_WIDTH, MAP_HEIGHT, MAP_WIDTH } from '../shared'; @@ -14,10 +13,9 @@ export class MapRender extends RenderItem { readonly layerState: ILayerState, readonly renderer: IMapRenderer ) { - super('static'); + super('static', false, false); this.renderer.setLayerState(layerState); - this.renderer.useAsset(materials.trackedAsset); this.renderer.setCanvasSize(this.width, this.height); this.renderer.setCellSize(CELL_WIDTH, CELL_HEIGHT); this.renderer.setRenderSize(MAP_WIDTH, MAP_HEIGHT); diff --git a/packages-user/client-modules/src/render/map/extension/hero.ts b/packages-user/client-modules/src/render/map/extension/hero.ts index b017ead..efbe92d 100644 --- a/packages-user/client-modules/src/render/map/extension/hero.ts +++ b/packages-user/client-modules/src/render/map/extension/hero.ts @@ -127,7 +127,9 @@ export class MapHeroRenderer implements IMapHeroRenderer { if (!tex) { return renderer.addMovingBlock(layer, 0, hero.x, hero.y); } - return renderer.addMovingBlock(layer, tex, hero.x, hero.y); + const block = renderer.addMovingBlock(layer, tex, hero.x, hero.y); + block.useSpecifiedFrame(0); + return block; } /** @@ -162,23 +164,26 @@ export class MapHeroRenderer implements IMapHeroRenderer { private tick(time: number) { this.entities.forEach(v => { - if (!v.animating) { - v.animateFrame = 0; - return; - } - const dt = time - v.lastAnimateTime; - if (dt > v.animateInterval) { - if (v.animateDirection === HeroAnimateDirection.Forward) { - v.animateFrame++; - } else { - v.animateFrame--; - if (v.animateFrame < 0) { - // 小于 0,则加上帧数的整数倍,就写个 10000 倍吧 - v.animateFrame += v.block.texture.frames * 10000; + if (v.animating) { + const dt = time - v.lastAnimateTime; + if (dt > v.animateInterval) { + if (v.animateDirection === HeroAnimateDirection.Forward) { + v.animateFrame++; + } else { + v.animateFrame--; + if (v.animateFrame < 0) { + // 小于 0,则加上帧数的整数倍,就写个 10000 倍吧 + v.animateFrame += v.block.texture.frames * 10000; + } } + v.lastAnimateTime = time; + v.block.useSpecifiedFrame(v.animateFrame); + } + } else { + if (v.animateFrame !== 0) { + v.animateFrame = 0; + v.block.useSpecifiedFrame(0); } - v.lastAnimateTime = time; - v.block.useSpecifiedFrame(v.animateFrame); } }); } @@ -221,14 +226,13 @@ export class MapHeroRenderer implements IMapHeroRenderer { const nextTex = this.renderer.manager.getIfBigImage( nextTile?.identifier ?? block.tile ); + entity.animateInterval = time; entity.promise = entity.promise.then(async () => { entity.moving = true; entity.animating = true; entity.direction = direction; if (nextTex) block.setTexture(nextTex); await block.lineTo(tx, ty, time); - entity.moving = false; - entity.animating = false; entity.nextDirection = entity.direction; }); } @@ -272,34 +276,41 @@ export class MapHeroRenderer implements IMapHeroRenderer { entity.animating = false; entity.animateFrame = 0; await block.moveRelative(fn, time); - entity.moving = false; - entity.animating = false; }); } startMove(): void { this.heroEntity.moving = true; this.heroEntity.animating = true; - this.heroEntity.animateFrame = 1; this.heroEntity.lastAnimateTime = this.ticker.timestamp; this.heroEntity.block.useSpecifiedFrame(1); } + private endEntityMoving(entity: HeroRenderEntity) { + entity.moving = false; + entity.animating = false; + entity.animateFrame = 0; + entity.block.useSpecifiedFrame(0); + } + async waitMoveEnd(waitFollower: boolean): Promise { if (waitFollower) { await Promise.all(this.entities.map(v => v.promise)); return; } - return this.heroEntity.promise; + await this.heroEntity.promise; + this.entities.forEach(v => this.endEntityMoving(v)); } stopMove(stopFollower: boolean): void { if (stopFollower) { this.entities.forEach(v => { v.block.endMoving(); + this.endEntityMoving(v); }); } else { this.heroEntity.block.endMoving(); + this.endEntityMoving(this.heroEntity); } } diff --git a/packages-user/client-modules/src/render/map/extension/index.ts b/packages-user/client-modules/src/render/map/extension/index.ts index c494129..59af7b0 100644 --- a/packages-user/client-modules/src/render/map/extension/index.ts +++ b/packages-user/client-modules/src/render/map/extension/index.ts @@ -1,2 +1,3 @@ export * from './hero'; +export * from './manager'; export * from './types'; diff --git a/packages-user/client-modules/src/render/map/extension/manager.ts b/packages-user/client-modules/src/render/map/extension/manager.ts new file mode 100644 index 0000000..70d628d --- /dev/null +++ b/packages-user/client-modules/src/render/map/extension/manager.ts @@ -0,0 +1,28 @@ +import { IHeroState, IMapLayer } from '@user/data-state'; +import { IMapExtensionManager, IMapHeroRenderer } from './types'; +import { IMapRenderer } from '../types'; +import { MapHeroRenderer } from './hero'; +import { logger } from '@motajs/common'; + +export class MapExtensionManager implements IMapExtensionManager { + /** 勇士状态至勇士渲染器的映射 */ + readonly heroMap: WeakMap = new WeakMap(); + + constructor(readonly renderer: IMapRenderer) {} + + addHero(state: IHeroState, layer: IMapLayer): void { + if (this.heroMap.has(state)) { + logger.error(45); + return; + } + const heroRenderer = new MapHeroRenderer(this.renderer, layer, state); + this.heroMap.set(state, heroRenderer); + } + + removeHero(state: IHeroState): void { + const renderer = this.heroMap.get(state); + if (!renderer) return; + renderer.destroy(); + this.heroMap.delete(state); + } +} diff --git a/packages-user/client-modules/src/render/map/extension/types.ts b/packages-user/client-modules/src/render/map/extension/types.ts index c393fa6..5f5f83c 100644 --- a/packages-user/client-modules/src/render/map/extension/types.ts +++ b/packages-user/client-modules/src/render/map/extension/types.ts @@ -1,5 +1,25 @@ import { ITexture } from '@motajs/render-assets'; -import { FaceDirection, HeroAnimateDirection } from '@user/data-state'; +import { + FaceDirection, + HeroAnimateDirection, + IHeroState, + IMapLayer +} from '@user/data-state'; + +export interface IMapExtensionManager { + /** + * 添加勇士渲染拓展 + * @param state 勇士状态 + * @param layer 勇士所在图层 + */ + addHero(state: IHeroState, layer: IMapLayer): void; + + /** + * 移除勇士渲染拓展 + * @param state 勇士状态 + */ + removeHero(state: IHeroState): void; +} export interface IMapHeroRenderer { /** diff --git a/packages-user/client-modules/src/render/map/index.ts b/packages-user/client-modules/src/render/map/index.ts index 0aa67e7..a19d767 100644 --- a/packages-user/client-modules/src/render/map/index.ts +++ b/packages-user/client-modules/src/render/map/index.ts @@ -1,3 +1,5 @@ +export * from './extension'; + export * from './block'; export * from './constant'; export * from './element'; diff --git a/packages-user/client-modules/src/render/map/moving.ts b/packages-user/client-modules/src/render/map/moving.ts index b805c41..89b5635 100644 --- a/packages-user/client-modules/src/render/map/moving.ts +++ b/packages-user/client-modules/src/render/map/moving.ts @@ -121,6 +121,7 @@ export class MovingBlock extends DynamicBlockStatus implements IMovingBlock { this.dy = y - this.y; this.time = time; this.relative = false; + this.startTime = this.renderer.getTimestamp(); if (time === 0) { this.x = x; this.y = y; @@ -142,6 +143,7 @@ export class MovingBlock extends DynamicBlockStatus implements IMovingBlock { this.relative = false; this.startX = this.x; this.startY = this.y; + this.startTime = this.renderer.getTimestamp(); if (time === 0) { const [tx, ty] = curve(1); this.x = tx; @@ -168,6 +170,7 @@ export class MovingBlock extends DynamicBlockStatus implements IMovingBlock { this.relative = false; this.startX = this.x; this.startY = this.y; + this.startTime = this.renderer.getTimestamp(); if (time === 0) { const [tx, ty] = curve(1); this.x = tx + this.startX; diff --git a/packages-user/client-modules/src/render/map/renderer.ts b/packages-user/client-modules/src/render/map/renderer.ts index 479b627..4a37dd7 100644 --- a/packages-user/client-modules/src/render/map/renderer.ts +++ b/packages-user/client-modules/src/render/map/renderer.ts @@ -196,6 +196,9 @@ export class MapRenderer readonly manager: IMaterialManager, layerState: ILayerState ) { + this.movingIndexPool.push( + ...Array.from({ length: this.movingCount }, (_, i) => i).reverse() + ); this.canvas = document.createElement('canvas'); this.gl = this.canvas.getContext('webgl2')!; this.transform = new Transform(); @@ -476,6 +479,7 @@ export class MapRenderer //#region 渲染设置 useAsset(asset: ITrackedAssetData): void { + if (this.assetData === asset) return; this.assetData = asset; this.sortedLayers.forEach(v => { this.updateLayerArea(v, 0, 0, v.width, v.height); @@ -483,6 +487,7 @@ export class MapRenderer } setRenderSize(width: number, height: number): void { + if (width === this.renderWidth && height === this.renderHeight) return; this.renderWidth = width; this.renderHeight = height; this.sortedLayers.forEach(v => { @@ -491,6 +496,7 @@ export class MapRenderer } setCellSize(width: number, height: number): void { + if (width === this.cellWidth && height === this.cellHeight) return; this.cellWidth = width; this.cellHeight = height; this.sortedLayers.forEach(v => { @@ -544,6 +550,13 @@ export class MapRenderer const offset = data.texture.width / data.frames; pool.add(offset); } + // 还有勇士图片 + for (const tex of this.manager.imageStore.values()) { + if (!this.manager.assetContainsTexture(tex)) continue; + const { w } = tex.render().rect; + pool.add(w / 4); + } + // 其他判断 if (pool.size > 64 && import.meta.env.DEV) { logger.warn(82); } @@ -633,7 +646,7 @@ export class MapRenderer gl.disable(gl.CULL_FACE); gl.enable(gl.DEPTH_TEST); gl.enable(gl.BLEND); - gl.depthFunc(gl.LESS); + gl.depthFunc(gl.LEQUAL); gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); const data: IContextData = { @@ -1238,6 +1251,7 @@ export class MapRenderer v.data.render(); } }); + this.vertex.renderDynamic(); if (area.dirty.length > 0) { // 如果需要更新顶点数组... @@ -1491,6 +1505,8 @@ export class MapRenderer this.needUpdateFrameCounter = true; } + this.tickers.forEach(v => void v.fn(timestamp)); + // 图块移动 if (this.movingBlock.size > 0) { const toUpdate: IMovingBlock[] = []; diff --git a/packages-user/client-modules/src/render/map/shader/map.vert b/packages-user/client-modules/src/render/map/shader/map.vert index 82ab309..a13554f 100644 --- a/packages-user/client-modules/src/render/map/shader/map.vert +++ b/packages-user/client-modules/src/render/map/shader/map.vert @@ -26,7 +26,7 @@ void main() { vec2 pos = a_position.xy * a_tilePos.zw + a_tilePos.xy; vec2 texCoord = a_position.zw * a_texCoord.zw + a_texCoord.xy; // 偏移量 - float offset = a_texData.x < 0.0 ? mod(u_nowFrame, a_texData.y) : a_texData.x; + float offset = mod(a_texData.x < 0.0 ? u_nowFrame : a_texData.x, a_texData.y); int offsetIndex = int(a_texData.z); // 贴图偏移 texCoord.x += u_offsetPool[offsetIndex] * offset; diff --git a/packages-user/client-modules/src/render/map/types.ts b/packages-user/client-modules/src/render/map/types.ts index 4efe3ab..77ad5e2 100644 --- a/packages-user/client-modules/src/render/map/types.ts +++ b/packages-user/client-modules/src/render/map/types.ts @@ -1,5 +1,8 @@ import { IDirtyMark, IDirtyTracker } from '@motajs/common'; -import { ITextureRenderable } from '@motajs/render-assets'; +import { + ITextureRenderable, + SizedCanvasImageSource +} from '@motajs/render-assets'; import { Transform } from '@motajs/render-core'; import { IAutotileProcessor, @@ -373,6 +376,12 @@ export interface IMapRenderer { */ getLayerIndex(layer: IMapLayer): number; + /** + * 根据图集的图像源获取其索引 + * @param source 图像源 + */ + getAssetSourceIndex(source: SizedCanvasImageSource): number; + /** * 获取指定偏移值在偏移池中的索引 * @param offset 原始偏移值,非归一化偏移值 diff --git a/packages-user/client-modules/src/render/map/vertex.ts b/packages-user/client-modules/src/render/map/vertex.ts index f437369..86b0b60 100644 --- a/packages-user/client-modules/src/render/map/vertex.ts +++ b/packages-user/client-modules/src/render/map/vertex.ts @@ -21,7 +21,7 @@ import { DYNAMIC_RESERVE, MAP_BLOCK_HEIGHT, MAP_BLOCK_WIDTH } from '../shared'; import { BlockSplitter } from './block'; import { clamp, isNil } from 'lodash-es'; import { BlockCls, IMaterialFramedData } from '@user/client-base'; -import { IRect, SizedCanvasImageSource } from '@motajs/render-assets'; +import { IRect } from '@motajs/render-assets'; import { INSTANCED_COUNT } from './constant'; export interface IMapDataGetter { @@ -36,12 +36,6 @@ export interface IMapDataGetter { /** 图块大小与格子大小判断方式 */ readonly tileTestMode: MapTileSizeTestMode; - /** - * 根据图集的图像源获取其索引 - * @param source 图像源 - */ - getAssetSourceIndex(source: SizedCanvasImageSource): number; - /** * 渲染器是否包含指定的移动图块对象 * @param moving 移动图块对象 @@ -159,6 +153,11 @@ export class MapVertexGenerator staticCount * INSTANCED_COUNT, count * INSTANCED_COUNT ); + for (let i = 0; i < count; i++) { + const start = i * INSTANCED_COUNT; + this.instancedArray[start + 9] = 1; + this.instancedArray[start + 12] = -1; + } } private splitBlock() { @@ -320,12 +319,12 @@ export class MapVertexGenerator } case MapTileAlign.Center: { // 左右居中对齐 - left = cl + cwu - twu; + left = cl - cwu + twu; break; } case MapTileAlign.End: { // 右对齐 - left = cl + cw - tw; + left = cl - cw + tw; break; } } @@ -337,12 +336,12 @@ export class MapVertexGenerator } case MapTileAlign.Center: { // 上下居中对齐 - top = ct + chu - thu; + top = ct - chu + thu; break; } case MapTileAlign.End: { // 下对齐 - top = ct + ch - th; + top = ct - ch + th; } } return { x: left, y: top, w: tw, h: th }; @@ -378,7 +377,8 @@ export class MapVertexGenerator const layerIndex = this.renderer.getLayerIndex(index.layer); // 避免 z 坐标是 1 的时候被裁剪,因此范围选择 [-0.9, 0.9] const layerStart = (layerIndex / layerCount) * 1.8 - 0.9; - const zIndex = -layerStart - index.mapY / this.mapHeight; + const zIndex = + -layerStart - index.mapY / this.mapHeight / layerCount; const { x, y, w, h } = this.getTilePosition(index, width, height); // 图块位置 instancedArray[startIndex] = x; @@ -398,10 +398,7 @@ export class MapVertexGenerator instancedArray[startIndex + 5] = texY; instancedArray[startIndex + 6] = texWidth; instancedArray[startIndex + 7] = texHeight; - // 不透明度 - instancedArray[startIndex + 9] = 1; // 帧数、偏移、纹理索引 - instancedArray[startIndex + 12] = -1; instancedArray[startIndex + 13] = frames; instancedArray[startIndex + 14] = offsetIndex; instancedArray[startIndex + 15] = assetIndex; diff --git a/packages-user/data-base/src/game.ts b/packages-user/data-base/src/game.ts index 51f0f50..d02ccd5 100644 --- a/packages-user/data-base/src/game.ts +++ b/packages-user/data-base/src/game.ts @@ -11,6 +11,8 @@ interface GameLoadEvent { coreInit: []; /** 当所有启动必要资源加载完毕后触发 */ loaded: []; + /** 当资源构建完毕后触发,后续需要用新的加载系统替代 */ + assetBuilt: []; /** 当客户端(渲染端)和数据端都挂载完毕后触发 */ registered: []; /** 当数据端挂载完毕后触发 */ diff --git a/packages-user/data-state/src/hero/state.ts b/packages-user/data-state/src/hero/state.ts index bfc31e4..cf02be2 100644 --- a/packages-user/data-state/src/hero/state.ts +++ b/packages-user/data-state/src/hero/state.ts @@ -2,12 +2,13 @@ import { Hookable, HookController, IHookController } from '@motajs/common'; import { IHeroFollower, IHeroState, IHeroStateHooks } from './types'; import { FaceDirection, getFaceMovement, nextFaceDirection } from '../common'; import { isNil } from 'lodash-es'; +import { DEFAULT_HERO_IMAGE } from '../shared'; export class HeroState extends Hookable implements IHeroState { x: number = 0; y: number = 0; direction: FaceDirection = FaceDirection.Down; - image?: ImageIds; + image: ImageIds = DEFAULT_HERO_IMAGE; /** 当前勇士是否正在移动 */ moving: boolean = false; diff --git a/packages-user/data-state/src/legacy/move.ts b/packages-user/data-state/src/legacy/move.ts index 137f7e5..216ad3d 100644 --- a/packages-user/data-state/src/legacy/move.ts +++ b/packages-user/data-state/src/legacy/move.ts @@ -14,6 +14,7 @@ import type { } from '@user/client-modules'; import { BluePalace, MiscData } from '../mechanism/misc'; import { sleep } from '@motajs/common'; +import { fromDirectionString, state } from '..'; // todo: 转身功能 @@ -437,8 +438,6 @@ const enum HeroMoveCode { } export class HeroMover extends ObjectMoverBase { - /** 勇士渲染适配器,用于等待动画等操作 */ - static adapter?: RenderAdapter; /** 视角适配器 */ static viewport?: RenderAdapter; @@ -485,12 +484,10 @@ export class HeroMover extends ObjectMoverBase { protected async onMoveStart(controller: IMoveController): Promise { this.beforeMoveSpeed = this.moveSpeed; - const adapter = HeroMover.adapter; const viewport = HeroMover.viewport; - if (!adapter || !viewport) return; - if (!core.isReplaying() || core.status.replay.speed <= 3) { - adapter.sync('startAnimate'); - await adapter.all('readyMove'); + if (!viewport) return; + if (!core.isReplaying() || core.status.replay.speed <= 12) { + state.hero.startMove(); } // 这里要检查前面那一格能不能走,不能走则不触发平滑视角,以避免撞墙上视角卡住 if (!this.ignoreTerrain) { @@ -512,11 +509,9 @@ export class HeroMover extends ObjectMoverBase { protected async onMoveEnd(controller: IMoveController): Promise { this.moveSpeed = this.beforeMoveSpeed; this.onSetMoveSpeed(this.moveSpeed, controller); - const adapter = HeroMover.adapter; const viewport = HeroMover.viewport; - if (!adapter || !viewport) return; - adapter.sync('endAnimate'); - await adapter.all('endMove'); + if (!viewport) return; + await state.hero.endMove(); viewport.sync('endMove'); core.clearContinueAutomaticRoute(); core.stopAutomaticRoute(); @@ -667,40 +662,43 @@ export class HeroMover extends ObjectMoverBase { speed: number, _controller: IMoveController ): void { - const adapter = HeroMover.adapter; - if (!adapter) return; - adapter.sync('setMoveSpeed', speed); + this.moveSpeed = speed; } /** * 移动动画 * @param x 目标横坐标 * @param y 目标纵坐标 - * @param showDir 显示方向 + * @param _showDir 显示方向 * @param moveDir 移动方向 */ private async moveAnimate( x: number, y: number, - showDir: Dir, + _showDir: Dir, moveDir: Dir2 ) { - const adapter = HeroMover.adapter; const viewport = HeroMover.viewport; - if (!adapter || !viewport) return; + if (!viewport) return; const replay = core.status.replay.speed; const speed = replay === 24 ? 1 : this.moveSpeed / replay; viewport.all('moveTo', x, y, speed * 1.6); - adapter.sync('setAnimateDir', showDir); - if (core.isReplaying() && core.status.replay.speed > 3) { - adapter.sync('endAnimate'); - await sleep(speed); - await adapter.all('setHeroLoc', x, y); - } else { - if (core.isReplaying()) { - adapter.sync('startAnimate'); + const replaying = core.isReplaying(); + if (replaying) { + if (core.status.replay.speed > 12) { + await state.hero.endMove(); + await sleep(speed); + state.hero.setPosition(x, y); + } else { + state.hero.startMove(); + await state.hero.move( + fromDirectionString(moveDir), + this.moveSpeed / core.status.replay.speed + ); } - await adapter.all('move', moveDir); + } else { + state.hero.startMove(); + await state.hero.move(fromDirectionString(moveDir), this.moveSpeed); } } @@ -784,101 +782,99 @@ export class HeroMover extends ObjectMoverBase { return { portal: false }; } - private renderHeroSwap(data: BluePalace.PortalTo) { - const adapter = HeroMover.adapter; - if (!adapter) return; - const list = adapter.items; - const { x: tx, y: ty, dir: toDir } = data; - const { x, y, direction } = core.status.hero.loc; - const { x: dx } = core.utils.scan[direction]; - const { x: tdx } = core.utils.scan[toDir]; + private renderHeroSwap(_data: BluePalace.PortalTo) { + // todo: 传送门 + // const list = adapter.items; + // const { x: tx, y: ty, dir: toDir } = data; + // const { x, y, direction } = core.status.hero.loc; + // const { x: dx } = core.utils.scan[direction]; + // const { x: tdx } = core.utils.scan[toDir]; - const promises = [...list].map(v => { - if (!v.renderable) return; - const renderable = { ...v.renderable }; - renderable.render = v.getRenderFromDir(toDir); - renderable.zIndex = ty; - const heroDir = v.moveDir; + // const promises = [...list].map(v => { + // if (!v.renderable) return; + // const renderable = { ...v.renderable }; + // renderable.render = v.getRenderFromDir(toDir); + // renderable.zIndex = ty; + // const heroDir = v.moveDir; - const width = v.renderable.render[0][2]; - const height = v.renderable.render[0][3]; - const cell = v.layer.cellSize; - const restHeight = height - cell; - if (!width || !height) return; + // const width = v.renderable.render[0][2]; + // const height = v.renderable.render[0][3]; + // const cell = v.layer.cellSize; + // const restHeight = height - cell; + // if (!width || !height) return; - const originFrom = structuredClone(v.renderable.render); - const originTo = structuredClone(renderable.render); - v.layer.moving.add(renderable); - v.layer.requestUpdateMoving(); + // const originFrom = structuredClone(v.renderable.render); + // const originTo = structuredClone(renderable.render); + // v.layer.moving.add(renderable); + // v.layer.requestUpdateMoving(); - const start = Date.now(); - return new Promise(res => { - const tick = () => { - const now = Date.now(); - const progress = (now - start) / this.moveSpeed; - const clipWidth = cell * progress; - const clipHeight = cell * progress; - const beforeWidth = width - clipWidth; - const beforeHeight = height - clipHeight; + // const start = Date.now(); + // return new Promise(res => { + // const tick = () => { + // const now = Date.now(); + // const progress = (now - start) / this.moveSpeed; + // const clipWidth = cell * progress; + // const clipHeight = cell * progress; + // const beforeWidth = width - clipWidth; + // const beforeHeight = height - clipHeight; - v.renderable!.x = x; - v.renderable!.y = y; - if (heroDir === 'left' || heroDir === 'right') { - v.renderable!.x = x + (clipWidth / 2 / cell) * dx; - v.renderable!.render.forEach((v, i) => { - v[2] = beforeWidth; - if (heroDir === 'left') { - v[0] = originFrom[i][0] + clipWidth; - } - }); - } else { - v.renderable!.render.forEach((v, i) => { - v[3] = beforeHeight; - if (heroDir === 'up') { - v[1] = - originFrom[i][1] + clipHeight + restHeight; - } - }); - } + // v.renderable!.x = x; + // v.renderable!.y = y; + // if (heroDir === 'left' || heroDir === 'right') { + // v.renderable!.x = x + (clipWidth / 2 / cell) * dx; + // v.renderable!.render.forEach((v, i) => { + // v[2] = beforeWidth; + // if (heroDir === 'left') { + // v[0] = originFrom[i][0] + clipWidth; + // } + // }); + // } else { + // v.renderable!.render.forEach((v, i) => { + // v[3] = beforeHeight; + // if (heroDir === 'up') { + // v[1] = + // originFrom[i][1] + clipHeight + restHeight; + // } + // }); + // } - renderable.x = tx; - renderable.y = ty; - if (toDir === 'left' || toDir === 'right') { - renderable.x = tx + (clipWidth / 2 / cell - 0.5) * tdx; - renderable.render.forEach((v, i) => { - v[2] = clipWidth; - if (toDir === 'right') { - v[0] = originTo[i][0] + beforeWidth; - } - }); - } else { - if (toDir === 'down') renderable.y = ty - 1 + progress; - renderable.render.forEach((v, i) => { - v[3] = clipHeight + restHeight; - if (toDir === 'down') { - v[1] = originTo[i][1] + clipHeight + restHeight; - v[3] = clipHeight; - } - }); - } - }; - v.layer.delegateTicker(tick, this.moveSpeed, () => { - v.renderable!.render = originFrom; - v.setAnimateDir(data.dir); - v.layer.moving.delete(renderable); - v.layer.requestUpdateMoving(); - res(); - }); - }); - }); + // renderable.x = tx; + // renderable.y = ty; + // if (toDir === 'left' || toDir === 'right') { + // renderable.x = tx + (clipWidth / 2 / cell - 0.5) * tdx; + // renderable.render.forEach((v, i) => { + // v[2] = clipWidth; + // if (toDir === 'right') { + // v[0] = originTo[i][0] + beforeWidth; + // } + // }); + // } else { + // if (toDir === 'down') renderable.y = ty - 1 + progress; + // renderable.render.forEach((v, i) => { + // v[3] = clipHeight + restHeight; + // if (toDir === 'down') { + // v[1] = originTo[i][1] + clipHeight + restHeight; + // v[3] = clipHeight; + // } + // }); + // } + // }; + // v.layer.delegateTicker(tick, this.moveSpeed, () => { + // v.renderable!.render = originFrom; + // v.setAnimateDir(data.dir); + // v.layer.moving.delete(renderable); + // v.layer.requestUpdateMoving(); + // res(); + // }); + // }); + // }); - return Promise.all(promises); + return Promise.all([]); } private renderHeroLoop() { - const adapter = HeroMover.adapter; const viewport = HeroMover.viewport; - if (!adapter || !viewport) return; + if (!viewport) return; const MotaRenderer = Mota.require('@motajs/render').MotaRenderer; const render = MotaRenderer.get('render-main'); const group = render?.getElementById('layer-loop') as LayerGroup; @@ -952,10 +948,8 @@ loading.once('coreInit', () => { loading.once('coreInit', () => { if (main.replayChecking || main.mode === 'editor') return; const Adapter = Mota.require('@motajs/render').RenderAdapter; - const adapter = Adapter.get('hero-adapter'); const viewport = Adapter.get('viewport'); const layerAdapter = Adapter.get('layer'); - HeroMover.adapter = adapter; HeroMover.viewport = viewport; BlockMover.adapter = layerAdapter; }); diff --git a/packages-user/data-state/src/shared.ts b/packages-user/data-state/src/shared.ts new file mode 100644 index 0000000..8da4926 --- /dev/null +++ b/packages-user/data-state/src/shared.ts @@ -0,0 +1,2 @@ +/** 默认的勇士图片 */ +export const DEFAULT_HERO_IMAGE: ImageIds = 'hero1.png'; diff --git a/packages-user/entry-client/src/create.ts b/packages-user/entry-client/src/create.ts index 10c9d6d..d641901 100644 --- a/packages-user/entry-client/src/create.ts +++ b/packages-user/entry-client/src/create.ts @@ -49,9 +49,9 @@ export function create() { } async function createModule() { - LegacyUI.create(); - ClientModules.create(); UserClientBase.create(); + ClientModules.create(); + LegacyUI.create(); await import('ant-design-vue/dist/antd.dark.css'); main.renderLoaded = true; diff --git a/packages/common/src/logger.json b/packages/common/src/logger.json index ada5d74..9eb2f33 100644 --- a/packages/common/src/logger.json +++ b/packages/common/src/logger.json @@ -44,6 +44,7 @@ "42": "The '$1' property of map-render element is required.", "43": "Cannot bind face direction to main block $1, please call malloc in advance.", "44": "Cannot bind face direction to main block $1, since main direction cannot be override.", + "45": "Cannot add hero renderer, sincehero renderer already exists for the given state.", "1101": "Shadow extension needs 'floor-hero' extension as dependency.", "1201": "Floor-damage extension needs 'floor-binder' extension as dependency.", "1301": "Portal extension need 'floor-binder' extension as dependency.", diff --git a/packages/render-assets/src/splitter.ts b/packages/render-assets/src/splitter.ts index 8fdb4b6..0c6f805 100644 --- a/packages/render-assets/src/splitter.ts +++ b/packages/render-assets/src/splitter.ts @@ -8,9 +8,10 @@ import { ITexture, ITextureSplitter, IRect } from './types'; export class TextureRowSplitter implements ITextureSplitter { *split(texture: ITexture, data: number): Generator { const lines = Math.ceil(texture.height / data); + const { x, y } = texture.render().rect; for (let i = 0; i < lines; i++) { const tex = new Texture(texture.source); - tex.clip(0, i * data, texture.width, data); + tex.clip(x, y + i * data, texture.width, data); yield tex; } } @@ -23,9 +24,10 @@ export class TextureRowSplitter implements ITextureSplitter { export class TextureColumnSplitter implements ITextureSplitter { *split(texture: ITexture, data: number): Generator { const lines = Math.ceil(texture.width / data); + const { x, y } = texture.render().rect; for (let i = 0; i < lines; i++) { const tex = new Texture(texture.source); - tex.clip(i * data, 0, data, texture.height); + tex.clip(x + i * data, y, data, texture.height); yield tex; } } @@ -40,10 +42,11 @@ export class TextureGridSplitter implements ITextureSplitter<[number, number]> { const [w, h] = data; const rows = Math.ceil(texture.width / w); const lines = Math.ceil(texture.height / h); - for (let y = 0; y < lines; y++) { - for (let x = 0; x < rows; x++) { + const { x, y } = texture.render().rect; + for (let ny = 0; ny < lines; ny++) { + for (let nx = 0; nx < rows; nx++) { const tex = new Texture(texture.source); - tex.clip(x * w, y * h, w, h); + tex.clip(x + nx * w, y + ny * h, w, h); yield tex; } } diff --git a/packages/render-assets/src/streamComposer.ts b/packages/render-assets/src/streamComposer.ts index cf01f34..d8f6e7a 100644 --- a/packages/render-assets/src/streamComposer.ts +++ b/packages/render-assets/src/streamComposer.ts @@ -140,7 +140,6 @@ export class TextureMaxRectsStreamComposer private nextCanvas() { this.nowCanvas = document.createElement('canvas'); - this.nowCanvas.id = `${Math.random()}`; this.nowCtx = this.nowCanvas.getContext('2d')!; this.nowCanvas.width = this.maxWidth; this.nowCanvas.height = this.maxHeight; diff --git a/packages/render-assets/src/texture.ts b/packages/render-assets/src/texture.ts index 16b25d8..0eaf627 100644 --- a/packages/render-assets/src/texture.ts +++ b/packages/render-assets/src/texture.ts @@ -30,8 +30,8 @@ export class Texture implements ITexture { this.height = source.height; this.cl = 0; this.ct = 0; - this.cr = 0; - this.cb = 0; + this.cr = source.width; + this.cb = source.height; } /** @@ -42,16 +42,14 @@ export class Texture implements ITexture { * @param h 裁剪高度 */ clip(x: number, y: number, w: number, h: number) { - const r = x + w; - const b = y + h; - if (x > this.width || y > this.height || r < 0 || b < 0) { + const left = clamp(this.cl + x, this.cl, this.cr); + const top = clamp(this.ct + y, this.ct, this.cb); + const right = clamp(this.cl + x + w, this.cl, this.cr); + const bottom = clamp(this.ct + y + h, this.ct, this.cb); + if (left === right || top === bottom) { logger.warn(69); return; } - const left = Math.max(0, x); - const top = Math.max(0, y); - const right = Math.min(this.width, r); - const bottom = Math.min(this.height, b); const width = right - left; const height = bottom - top; if (width <= 0 || height <= 0) { @@ -60,6 +58,8 @@ export class Texture implements ITexture { } this.cl = left; this.ct = top; + this.cr = right; + this.cb = bottom; this.width = right - left; this.height = bottom - top; }