Compare commits

..

1 Commits

Author SHA1 Message Date
AncTe
5fa05a6faa
Merge ac260e3ab3 into 820dc5bf4c 2025-11-24 08:38:29 +00:00
26 changed files with 180 additions and 301 deletions

View File

@ -105,8 +105,6 @@ class TrackedAssetData
private originSourceMap: Map<number, SizedCanvasImageSource> = new Map(); private originSourceMap: Map<number, SizedCanvasImageSource> = new Map();
private promises: Set<Promise<ImageBitmap>> = new Set();
constructor( constructor(
readonly materials: IMaterialGetter, readonly materials: IMaterialGetter,
readonly builder: AssetBuilder readonly builder: AssetBuilder
@ -139,10 +137,7 @@ class TrackedAssetData
this.sourceList.set(index, source); this.sourceList.set(index, source);
this.skipRef.set(source, index); this.skipRef.set(source, index);
} else { } else {
const promise = createImageBitmap(source); const bitmap = await createImageBitmap(source);
this.promises.add(promise);
const bitmap = await promise;
this.promises.delete(promise);
this.sourceList.set(index, bitmap); this.sourceList.set(index, bitmap);
this.skipRef.set(bitmap, index); this.skipRef.set(bitmap, index);
// 要把源也加到映射中,因为这里的 bitmap 与外部源并不同引用 // 要把源也加到映射中,因为这里的 bitmap 与外部源并不同引用
@ -151,9 +146,5 @@ class TrackedAssetData
this.dirty(index); this.dirty(index);
} }
async then(): Promise<void> {
await Promise.all([...this.promises]);
}
close(): void {} close(): void {}
} }

View File

@ -6,7 +6,6 @@ export function createMaterial() {
createAutotile(); createAutotile();
loading.once('loaded', () => { loading.once('loaded', () => {
fallbackLoad(); fallbackLoad();
loading.emit('assetBuilt');
}); });
} }

View File

@ -442,7 +442,6 @@ export class MaterialManager implements IMaterialManager {
store: this.assetStore store: this.assetStore
}; };
this.checkAssetDirty(data); this.checkAssetDirty(data);
texture.toAsset(data);
return assetData; return assetData;
} }
@ -568,10 +567,10 @@ export class MaterialManager implements IMaterialManager {
} }
assetContainsTexture(texture: ITexture): boolean { assetContainsTexture(texture: ITexture): boolean {
return this.trackedAsset.skipRef.has(texture.source); return this.assetMap.has(texture);
} }
getTextureAsset(texture: ITexture): number | undefined { getTextureAsset(texture: ITexture): number | undefined {
return this.trackedAsset.skipRef.get(texture.source); return this.assetMap.get(texture);
} }
} }

View File

@ -505,9 +505,4 @@ export interface ITrackedAssetData extends IDirtyTracker<Set<number>> {
* 使 * 使
*/ */
close(): void; close(): void;
/**
*
*/
then(): Promise<void>;
} }

View File

@ -1,21 +1,8 @@
import { state } from '@user/data-state'; import { state } from '@user/data-state';
import { MapRenderer } from './map/renderer';
import { materials } from '@user/client-base'; import { materials } from '@user/client-base';
import { MapRenderer, MapExtensionManager } from './map';
/** 主地图渲染器,用于渲染游戏画面 */ /** 主地图渲染器,用于渲染游戏画面 */
export const mainMapRenderer = new MapRenderer(materials, state.layer); export const mainMapRenderer = new MapRenderer(materials, state.layer);
/** 主地图渲染器拓展 */
export const mainMapExtension = new MapExtensionManager(mainMapRenderer);
/** 副地图渲染器,用于渲染缩略图、浏览地图等 */ /** 副地图渲染器,用于渲染缩略图、浏览地图等 */
// export const expandMapRenderer = new MapRenderer(materials, state.layer); // 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);
}
}

View File

@ -11,7 +11,6 @@ import { createLegacy } from './legacy';
import { sceneController } from './scene'; import { sceneController } from './scene';
import { GameTitleUI } from './ui/title'; import { GameTitleUI } from './ui/title';
import { createWeather } from './weather'; import { createWeather } from './weather';
import { createMainExtension } from './commonIns';
export function createGameRenderer() { export function createGameRenderer() {
const App = defineComponent(_props => { const App = defineComponent(_props => {
@ -34,15 +33,11 @@ export function createRender() {
createLoopMap(); createLoopMap();
createWeather(); createWeather();
loading.once('loaded', () => { loading.on('loaded', () => {
sceneController.open(GameTitleUI, {}); sceneController.open(GameTitleUI, {});
mainRenderer.show(); mainRenderer.show();
}); });
loading.once('assetBuilt', () => {
createMainExtension();
});
hook.on('restart', () => { hook.on('restart', () => {
sceneController.closeAll(); sceneController.closeAll();
sceneController.open(GameTitleUI, {}); sceneController.open(GameTitleUI, {});

View File

@ -1,6 +1,7 @@
import { MotaOffscreenCanvas2D, RenderItem } from '@motajs/render-core'; import { MotaOffscreenCanvas2D, RenderItem } from '@motajs/render-core';
import { ILayerState } from '@user/data-state'; import { ILayerState } from '@user/data-state';
import { IMapRenderer } from './types'; import { IMapRenderer } from './types';
import { materials } from '@user/client-base';
import { ElementNamespace, ComponentInternalInstance } from 'vue'; import { ElementNamespace, ComponentInternalInstance } from 'vue';
import { CELL_HEIGHT, CELL_WIDTH, MAP_HEIGHT, MAP_WIDTH } from '../shared'; import { CELL_HEIGHT, CELL_WIDTH, MAP_HEIGHT, MAP_WIDTH } from '../shared';
@ -13,9 +14,10 @@ export class MapRender extends RenderItem {
readonly layerState: ILayerState, readonly layerState: ILayerState,
readonly renderer: IMapRenderer readonly renderer: IMapRenderer
) { ) {
super('static', false, false); super('static');
this.renderer.setLayerState(layerState); this.renderer.setLayerState(layerState);
this.renderer.useAsset(materials.trackedAsset);
this.renderer.setCanvasSize(this.width, this.height); this.renderer.setCanvasSize(this.width, this.height);
this.renderer.setCellSize(CELL_WIDTH, CELL_HEIGHT); this.renderer.setCellSize(CELL_WIDTH, CELL_HEIGHT);
this.renderer.setRenderSize(MAP_WIDTH, MAP_HEIGHT); this.renderer.setRenderSize(MAP_WIDTH, MAP_HEIGHT);

View File

@ -127,9 +127,7 @@ export class MapHeroRenderer implements IMapHeroRenderer {
if (!tex) { if (!tex) {
return renderer.addMovingBlock(layer, 0, hero.x, hero.y); return renderer.addMovingBlock(layer, 0, hero.x, hero.y);
} }
const block = renderer.addMovingBlock(layer, tex, hero.x, hero.y); return renderer.addMovingBlock(layer, tex, hero.x, hero.y);
block.useSpecifiedFrame(0);
return block;
} }
/** /**
@ -164,7 +162,10 @@ export class MapHeroRenderer implements IMapHeroRenderer {
private tick(time: number) { private tick(time: number) {
this.entities.forEach(v => { this.entities.forEach(v => {
if (v.animating) { if (!v.animating) {
v.animateFrame = 0;
return;
}
const dt = time - v.lastAnimateTime; const dt = time - v.lastAnimateTime;
if (dt > v.animateInterval) { if (dt > v.animateInterval) {
if (v.animateDirection === HeroAnimateDirection.Forward) { if (v.animateDirection === HeroAnimateDirection.Forward) {
@ -179,12 +180,6 @@ export class MapHeroRenderer implements IMapHeroRenderer {
v.lastAnimateTime = time; v.lastAnimateTime = time;
v.block.useSpecifiedFrame(v.animateFrame); v.block.useSpecifiedFrame(v.animateFrame);
} }
} else {
if (v.animateFrame !== 0) {
v.animateFrame = 0;
v.block.useSpecifiedFrame(0);
}
}
}); });
} }
@ -226,13 +221,14 @@ export class MapHeroRenderer implements IMapHeroRenderer {
const nextTex = this.renderer.manager.getIfBigImage( const nextTex = this.renderer.manager.getIfBigImage(
nextTile?.identifier ?? block.tile nextTile?.identifier ?? block.tile
); );
entity.animateInterval = time;
entity.promise = entity.promise.then(async () => { entity.promise = entity.promise.then(async () => {
entity.moving = true; entity.moving = true;
entity.animating = true; entity.animating = true;
entity.direction = direction; entity.direction = direction;
if (nextTex) block.setTexture(nextTex); if (nextTex) block.setTexture(nextTex);
await block.lineTo(tx, ty, time); await block.lineTo(tx, ty, time);
entity.moving = false;
entity.animating = false;
entity.nextDirection = entity.direction; entity.nextDirection = entity.direction;
}); });
} }
@ -276,41 +272,34 @@ export class MapHeroRenderer implements IMapHeroRenderer {
entity.animating = false; entity.animating = false;
entity.animateFrame = 0; entity.animateFrame = 0;
await block.moveRelative(fn, time); await block.moveRelative(fn, time);
entity.moving = false;
entity.animating = false;
}); });
} }
startMove(): void { startMove(): void {
this.heroEntity.moving = true; this.heroEntity.moving = true;
this.heroEntity.animating = true; this.heroEntity.animating = true;
this.heroEntity.animateFrame = 1;
this.heroEntity.lastAnimateTime = this.ticker.timestamp; this.heroEntity.lastAnimateTime = this.ticker.timestamp;
this.heroEntity.block.useSpecifiedFrame(1); 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<void> { async waitMoveEnd(waitFollower: boolean): Promise<void> {
if (waitFollower) { if (waitFollower) {
await Promise.all(this.entities.map(v => v.promise)); await Promise.all(this.entities.map(v => v.promise));
return; return;
} }
await this.heroEntity.promise; return this.heroEntity.promise;
this.entities.forEach(v => this.endEntityMoving(v));
} }
stopMove(stopFollower: boolean): void { stopMove(stopFollower: boolean): void {
if (stopFollower) { if (stopFollower) {
this.entities.forEach(v => { this.entities.forEach(v => {
v.block.endMoving(); v.block.endMoving();
this.endEntityMoving(v);
}); });
} else { } else {
this.heroEntity.block.endMoving(); this.heroEntity.block.endMoving();
this.endEntityMoving(this.heroEntity);
} }
} }

View File

@ -1,3 +1,2 @@
export * from './hero'; export * from './hero';
export * from './manager';
export * from './types'; export * from './types';

View File

@ -1,28 +0,0 @@
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<IHeroState, IMapHeroRenderer> = 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);
}
}

View File

@ -1,25 +1,5 @@
import { ITexture } from '@motajs/render-assets'; import { ITexture } from '@motajs/render-assets';
import { import { FaceDirection, HeroAnimateDirection } from '@user/data-state';
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 { export interface IMapHeroRenderer {
/** /**

View File

@ -1,5 +1,3 @@
export * from './extension';
export * from './block'; export * from './block';
export * from './constant'; export * from './constant';
export * from './element'; export * from './element';

View File

@ -121,7 +121,6 @@ export class MovingBlock extends DynamicBlockStatus implements IMovingBlock {
this.dy = y - this.y; this.dy = y - this.y;
this.time = time; this.time = time;
this.relative = false; this.relative = false;
this.startTime = this.renderer.getTimestamp();
if (time === 0) { if (time === 0) {
this.x = x; this.x = x;
this.y = y; this.y = y;
@ -143,7 +142,6 @@ export class MovingBlock extends DynamicBlockStatus implements IMovingBlock {
this.relative = false; this.relative = false;
this.startX = this.x; this.startX = this.x;
this.startY = this.y; this.startY = this.y;
this.startTime = this.renderer.getTimestamp();
if (time === 0) { if (time === 0) {
const [tx, ty] = curve(1); const [tx, ty] = curve(1);
this.x = tx; this.x = tx;
@ -170,7 +168,6 @@ export class MovingBlock extends DynamicBlockStatus implements IMovingBlock {
this.relative = false; this.relative = false;
this.startX = this.x; this.startX = this.x;
this.startY = this.y; this.startY = this.y;
this.startTime = this.renderer.getTimestamp();
if (time === 0) { if (time === 0) {
const [tx, ty] = curve(1); const [tx, ty] = curve(1);
this.x = tx + this.startX; this.x = tx + this.startX;

View File

@ -196,9 +196,6 @@ export class MapRenderer
readonly manager: IMaterialManager, readonly manager: IMaterialManager,
layerState: ILayerState layerState: ILayerState
) { ) {
this.movingIndexPool.push(
...Array.from({ length: this.movingCount }, (_, i) => i).reverse()
);
this.canvas = document.createElement('canvas'); this.canvas = document.createElement('canvas');
this.gl = this.canvas.getContext('webgl2')!; this.gl = this.canvas.getContext('webgl2')!;
this.transform = new Transform(); this.transform = new Transform();
@ -479,7 +476,6 @@ export class MapRenderer
//#region 渲染设置 //#region 渲染设置
useAsset(asset: ITrackedAssetData): void { useAsset(asset: ITrackedAssetData): void {
if (this.assetData === asset) return;
this.assetData = asset; this.assetData = asset;
this.sortedLayers.forEach(v => { this.sortedLayers.forEach(v => {
this.updateLayerArea(v, 0, 0, v.width, v.height); this.updateLayerArea(v, 0, 0, v.width, v.height);
@ -487,7 +483,6 @@ export class MapRenderer
} }
setRenderSize(width: number, height: number): void { setRenderSize(width: number, height: number): void {
if (width === this.renderWidth && height === this.renderHeight) return;
this.renderWidth = width; this.renderWidth = width;
this.renderHeight = height; this.renderHeight = height;
this.sortedLayers.forEach(v => { this.sortedLayers.forEach(v => {
@ -496,7 +491,6 @@ export class MapRenderer
} }
setCellSize(width: number, height: number): void { setCellSize(width: number, height: number): void {
if (width === this.cellWidth && height === this.cellHeight) return;
this.cellWidth = width; this.cellWidth = width;
this.cellHeight = height; this.cellHeight = height;
this.sortedLayers.forEach(v => { this.sortedLayers.forEach(v => {
@ -550,13 +544,6 @@ export class MapRenderer
const offset = data.texture.width / data.frames; const offset = data.texture.width / data.frames;
pool.add(offset); 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) { if (pool.size > 64 && import.meta.env.DEV) {
logger.warn(82); logger.warn(82);
} }
@ -646,7 +633,7 @@ export class MapRenderer
gl.disable(gl.CULL_FACE); gl.disable(gl.CULL_FACE);
gl.enable(gl.DEPTH_TEST); gl.enable(gl.DEPTH_TEST);
gl.enable(gl.BLEND); gl.enable(gl.BLEND);
gl.depthFunc(gl.LEQUAL); gl.depthFunc(gl.LESS);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
const data: IContextData = { const data: IContextData = {
@ -1251,7 +1238,6 @@ export class MapRenderer
v.data.render(); v.data.render();
} }
}); });
this.vertex.renderDynamic();
if (area.dirty.length > 0) { if (area.dirty.length > 0) {
// 如果需要更新顶点数组... // 如果需要更新顶点数组...
@ -1505,8 +1491,6 @@ export class MapRenderer
this.needUpdateFrameCounter = true; this.needUpdateFrameCounter = true;
} }
this.tickers.forEach(v => void v.fn(timestamp));
// 图块移动 // 图块移动
if (this.movingBlock.size > 0) { if (this.movingBlock.size > 0) {
const toUpdate: IMovingBlock[] = []; const toUpdate: IMovingBlock[] = [];

View File

@ -26,7 +26,7 @@ void main() {
vec2 pos = a_position.xy * a_tilePos.zw + a_tilePos.xy; vec2 pos = a_position.xy * a_tilePos.zw + a_tilePos.xy;
vec2 texCoord = a_position.zw * a_texCoord.zw + a_texCoord.xy; vec2 texCoord = a_position.zw * a_texCoord.zw + a_texCoord.xy;
// 偏移量 // 偏移量
float offset = mod(a_texData.x < 0.0 ? u_nowFrame : a_texData.x, a_texData.y); float offset = a_texData.x < 0.0 ? mod(u_nowFrame, a_texData.y) : a_texData.x;
int offsetIndex = int(a_texData.z); int offsetIndex = int(a_texData.z);
// 贴图偏移 // 贴图偏移
texCoord.x += u_offsetPool[offsetIndex] * offset; texCoord.x += u_offsetPool[offsetIndex] * offset;

View File

@ -1,8 +1,5 @@
import { IDirtyMark, IDirtyTracker } from '@motajs/common'; import { IDirtyMark, IDirtyTracker } from '@motajs/common';
import { import { ITextureRenderable } from '@motajs/render-assets';
ITextureRenderable,
SizedCanvasImageSource
} from '@motajs/render-assets';
import { Transform } from '@motajs/render-core'; import { Transform } from '@motajs/render-core';
import { import {
IAutotileProcessor, IAutotileProcessor,
@ -376,12 +373,6 @@ export interface IMapRenderer {
*/ */
getLayerIndex(layer: IMapLayer): number; getLayerIndex(layer: IMapLayer): number;
/**
*
* @param source
*/
getAssetSourceIndex(source: SizedCanvasImageSource): number;
/** /**
* *
* @param offset * @param offset

View File

@ -21,7 +21,7 @@ import { DYNAMIC_RESERVE, MAP_BLOCK_HEIGHT, MAP_BLOCK_WIDTH } from '../shared';
import { BlockSplitter } from './block'; import { BlockSplitter } from './block';
import { clamp, isNil } from 'lodash-es'; import { clamp, isNil } from 'lodash-es';
import { BlockCls, IMaterialFramedData } from '@user/client-base'; import { BlockCls, IMaterialFramedData } from '@user/client-base';
import { IRect } from '@motajs/render-assets'; import { IRect, SizedCanvasImageSource } from '@motajs/render-assets';
import { INSTANCED_COUNT } from './constant'; import { INSTANCED_COUNT } from './constant';
export interface IMapDataGetter { export interface IMapDataGetter {
@ -36,6 +36,12 @@ export interface IMapDataGetter {
/** 图块大小与格子大小判断方式 */ /** 图块大小与格子大小判断方式 */
readonly tileTestMode: MapTileSizeTestMode; readonly tileTestMode: MapTileSizeTestMode;
/**
*
* @param source
*/
getAssetSourceIndex(source: SizedCanvasImageSource): number;
/** /**
* *
* @param moving * @param moving
@ -153,11 +159,6 @@ export class MapVertexGenerator
staticCount * INSTANCED_COUNT, staticCount * INSTANCED_COUNT,
count * 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() { private splitBlock() {
@ -319,12 +320,12 @@ export class MapVertexGenerator
} }
case MapTileAlign.Center: { case MapTileAlign.Center: {
// 左右居中对齐 // 左右居中对齐
left = cl - cwu + twu; left = cl + cwu - twu;
break; break;
} }
case MapTileAlign.End: { case MapTileAlign.End: {
// 右对齐 // 右对齐
left = cl - cw + tw; left = cl + cw - tw;
break; break;
} }
} }
@ -336,12 +337,12 @@ export class MapVertexGenerator
} }
case MapTileAlign.Center: { case MapTileAlign.Center: {
// 上下居中对齐 // 上下居中对齐
top = ct - chu + thu; top = ct + chu - thu;
break; break;
} }
case MapTileAlign.End: { case MapTileAlign.End: {
// 下对齐 // 下对齐
top = ct - ch + th; top = ct + ch - th;
} }
} }
return { x: left, y: top, w: tw, h: th }; return { x: left, y: top, w: tw, h: th };
@ -377,8 +378,7 @@ export class MapVertexGenerator
const layerIndex = this.renderer.getLayerIndex(index.layer); const layerIndex = this.renderer.getLayerIndex(index.layer);
// 避免 z 坐标是 1 的时候被裁剪,因此范围选择 [-0.9, 0.9] // 避免 z 坐标是 1 的时候被裁剪,因此范围选择 [-0.9, 0.9]
const layerStart = (layerIndex / layerCount) * 1.8 - 0.9; const layerStart = (layerIndex / layerCount) * 1.8 - 0.9;
const zIndex = const zIndex = -layerStart - index.mapY / this.mapHeight;
-layerStart - index.mapY / this.mapHeight / layerCount;
const { x, y, w, h } = this.getTilePosition(index, width, height); const { x, y, w, h } = this.getTilePosition(index, width, height);
// 图块位置 // 图块位置
instancedArray[startIndex] = x; instancedArray[startIndex] = x;
@ -398,7 +398,10 @@ export class MapVertexGenerator
instancedArray[startIndex + 5] = texY; instancedArray[startIndex + 5] = texY;
instancedArray[startIndex + 6] = texWidth; instancedArray[startIndex + 6] = texWidth;
instancedArray[startIndex + 7] = texHeight; instancedArray[startIndex + 7] = texHeight;
// 不透明度
instancedArray[startIndex + 9] = 1;
// 帧数、偏移、纹理索引 // 帧数、偏移、纹理索引
instancedArray[startIndex + 12] = -1;
instancedArray[startIndex + 13] = frames; instancedArray[startIndex + 13] = frames;
instancedArray[startIndex + 14] = offsetIndex; instancedArray[startIndex + 14] = offsetIndex;
instancedArray[startIndex + 15] = assetIndex; instancedArray[startIndex + 15] = assetIndex;

View File

@ -11,8 +11,6 @@ interface GameLoadEvent {
coreInit: []; coreInit: [];
/** 当所有启动必要资源加载完毕后触发 */ /** 当所有启动必要资源加载完毕后触发 */
loaded: []; loaded: [];
/** 当资源构建完毕后触发,后续需要用新的加载系统替代 */
assetBuilt: [];
/** 当客户端(渲染端)和数据端都挂载完毕后触发 */ /** 当客户端(渲染端)和数据端都挂载完毕后触发 */
registered: []; registered: [];
/** 当数据端挂载完毕后触发 */ /** 当数据端挂载完毕后触发 */

View File

@ -2,13 +2,12 @@ import { Hookable, HookController, IHookController } from '@motajs/common';
import { IHeroFollower, IHeroState, IHeroStateHooks } from './types'; import { IHeroFollower, IHeroState, IHeroStateHooks } from './types';
import { FaceDirection, getFaceMovement, nextFaceDirection } from '../common'; import { FaceDirection, getFaceMovement, nextFaceDirection } from '../common';
import { isNil } from 'lodash-es'; import { isNil } from 'lodash-es';
import { DEFAULT_HERO_IMAGE } from '../shared';
export class HeroState extends Hookable<IHeroStateHooks> implements IHeroState { export class HeroState extends Hookable<IHeroStateHooks> implements IHeroState {
x: number = 0; x: number = 0;
y: number = 0; y: number = 0;
direction: FaceDirection = FaceDirection.Down; direction: FaceDirection = FaceDirection.Down;
image: ImageIds = DEFAULT_HERO_IMAGE; image?: ImageIds;
/** 当前勇士是否正在移动 */ /** 当前勇士是否正在移动 */
moving: boolean = false; moving: boolean = false;

View File

@ -14,7 +14,6 @@ import type {
} from '@user/client-modules'; } from '@user/client-modules';
import { BluePalace, MiscData } from '../mechanism/misc'; import { BluePalace, MiscData } from '../mechanism/misc';
import { sleep } from '@motajs/common'; import { sleep } from '@motajs/common';
import { fromDirectionString, state } from '..';
// todo: 转身功能 // todo: 转身功能
@ -438,6 +437,8 @@ const enum HeroMoveCode {
} }
export class HeroMover extends ObjectMoverBase { export class HeroMover extends ObjectMoverBase {
/** 勇士渲染适配器,用于等待动画等操作 */
static adapter?: RenderAdapter<HeroRenderer>;
/** 视角适配器 */ /** 视角适配器 */
static viewport?: RenderAdapter<FloorViewport>; static viewport?: RenderAdapter<FloorViewport>;
@ -484,10 +485,12 @@ export class HeroMover extends ObjectMoverBase {
protected async onMoveStart(controller: IMoveController): Promise<void> { protected async onMoveStart(controller: IMoveController): Promise<void> {
this.beforeMoveSpeed = this.moveSpeed; this.beforeMoveSpeed = this.moveSpeed;
const adapter = HeroMover.adapter;
const viewport = HeroMover.viewport; const viewport = HeroMover.viewport;
if (!viewport) return; if (!adapter || !viewport) return;
if (!core.isReplaying() || core.status.replay.speed <= 12) { if (!core.isReplaying() || core.status.replay.speed <= 3) {
state.hero.startMove(); adapter.sync('startAnimate');
await adapter.all('readyMove');
} }
// 这里要检查前面那一格能不能走,不能走则不触发平滑视角,以避免撞墙上视角卡住 // 这里要检查前面那一格能不能走,不能走则不触发平滑视角,以避免撞墙上视角卡住
if (!this.ignoreTerrain) { if (!this.ignoreTerrain) {
@ -509,9 +512,11 @@ export class HeroMover extends ObjectMoverBase {
protected async onMoveEnd(controller: IMoveController): Promise<void> { protected async onMoveEnd(controller: IMoveController): Promise<void> {
this.moveSpeed = this.beforeMoveSpeed; this.moveSpeed = this.beforeMoveSpeed;
this.onSetMoveSpeed(this.moveSpeed, controller); this.onSetMoveSpeed(this.moveSpeed, controller);
const adapter = HeroMover.adapter;
const viewport = HeroMover.viewport; const viewport = HeroMover.viewport;
if (!viewport) return; if (!adapter || !viewport) return;
await state.hero.endMove(); adapter.sync('endAnimate');
await adapter.all('endMove');
viewport.sync('endMove'); viewport.sync('endMove');
core.clearContinueAutomaticRoute(); core.clearContinueAutomaticRoute();
core.stopAutomaticRoute(); core.stopAutomaticRoute();
@ -662,43 +667,40 @@ export class HeroMover extends ObjectMoverBase {
speed: number, speed: number,
_controller: IMoveController _controller: IMoveController
): void { ): void {
this.moveSpeed = speed; const adapter = HeroMover.adapter;
if (!adapter) return;
adapter.sync('setMoveSpeed', speed);
} }
/** /**
* *
* @param x * @param x
* @param y * @param y
* @param _showDir * @param showDir
* @param moveDir * @param moveDir
*/ */
private async moveAnimate( private async moveAnimate(
x: number, x: number,
y: number, y: number,
_showDir: Dir, showDir: Dir,
moveDir: Dir2 moveDir: Dir2
) { ) {
const adapter = HeroMover.adapter;
const viewport = HeroMover.viewport; const viewport = HeroMover.viewport;
if (!viewport) return; if (!adapter || !viewport) return;
const replay = core.status.replay.speed; const replay = core.status.replay.speed;
const speed = replay === 24 ? 1 : this.moveSpeed / replay; const speed = replay === 24 ? 1 : this.moveSpeed / replay;
viewport.all('moveTo', x, y, speed * 1.6); viewport.all('moveTo', x, y, speed * 1.6);
const replaying = core.isReplaying(); adapter.sync('setAnimateDir', showDir);
if (replaying) { if (core.isReplaying() && core.status.replay.speed > 3) {
if (core.status.replay.speed > 12) { adapter.sync('endAnimate');
await state.hero.endMove();
await sleep(speed); await sleep(speed);
state.hero.setPosition(x, y); await adapter.all('setHeroLoc', x, y);
} else { } else {
state.hero.startMove(); if (core.isReplaying()) {
await state.hero.move( adapter.sync('startAnimate');
fromDirectionString(moveDir),
this.moveSpeed / core.status.replay.speed
);
} }
} else { await adapter.all('move', moveDir);
state.hero.startMove();
await state.hero.move(fromDirectionString(moveDir), this.moveSpeed);
} }
} }
@ -782,99 +784,101 @@ export class HeroMover extends ObjectMoverBase {
return { portal: false }; return { portal: false };
} }
private renderHeroSwap(_data: BluePalace.PortalTo) { private renderHeroSwap(data: BluePalace.PortalTo) {
// todo: 传送门 const adapter = HeroMover.adapter;
// const list = adapter.items; if (!adapter) return;
// const { x: tx, y: ty, dir: toDir } = data; const list = adapter.items;
// const { x, y, direction } = core.status.hero.loc; const { x: tx, y: ty, dir: toDir } = data;
// const { x: dx } = core.utils.scan[direction]; const { x, y, direction } = core.status.hero.loc;
// const { x: tdx } = core.utils.scan[toDir]; const { x: dx } = core.utils.scan[direction];
const { x: tdx } = core.utils.scan[toDir];
// const promises = [...list].map(v => { const promises = [...list].map(v => {
// if (!v.renderable) return; if (!v.renderable) return;
// const renderable = { ...v.renderable }; const renderable = { ...v.renderable };
// renderable.render = v.getRenderFromDir(toDir); renderable.render = v.getRenderFromDir(toDir);
// renderable.zIndex = ty; renderable.zIndex = ty;
// const heroDir = v.moveDir; const heroDir = v.moveDir;
// const width = v.renderable.render[0][2]; const width = v.renderable.render[0][2];
// const height = v.renderable.render[0][3]; const height = v.renderable.render[0][3];
// const cell = v.layer.cellSize; const cell = v.layer.cellSize;
// const restHeight = height - cell; const restHeight = height - cell;
// if (!width || !height) return; if (!width || !height) return;
// const originFrom = structuredClone(v.renderable.render); const originFrom = structuredClone(v.renderable.render);
// const originTo = structuredClone(renderable.render); const originTo = structuredClone(renderable.render);
// v.layer.moving.add(renderable); v.layer.moving.add(renderable);
// v.layer.requestUpdateMoving(); v.layer.requestUpdateMoving();
// const start = Date.now(); const start = Date.now();
// return new Promise<void>(res => { return new Promise<void>(res => {
// const tick = () => { const tick = () => {
// const now = Date.now(); const now = Date.now();
// const progress = (now - start) / this.moveSpeed; const progress = (now - start) / this.moveSpeed;
// const clipWidth = cell * progress; const clipWidth = cell * progress;
// const clipHeight = cell * progress; const clipHeight = cell * progress;
// const beforeWidth = width - clipWidth; const beforeWidth = width - clipWidth;
// const beforeHeight = height - clipHeight; const beforeHeight = height - clipHeight;
// v.renderable!.x = x; v.renderable!.x = x;
// v.renderable!.y = y; v.renderable!.y = y;
// if (heroDir === 'left' || heroDir === 'right') { if (heroDir === 'left' || heroDir === 'right') {
// v.renderable!.x = x + (clipWidth / 2 / cell) * dx; v.renderable!.x = x + (clipWidth / 2 / cell) * dx;
// v.renderable!.render.forEach((v, i) => { v.renderable!.render.forEach((v, i) => {
// v[2] = beforeWidth; v[2] = beforeWidth;
// if (heroDir === 'left') { if (heroDir === 'left') {
// v[0] = originFrom[i][0] + clipWidth; v[0] = originFrom[i][0] + clipWidth;
// } }
// }); });
// } else { } else {
// v.renderable!.render.forEach((v, i) => { v.renderable!.render.forEach((v, i) => {
// v[3] = beforeHeight; v[3] = beforeHeight;
// if (heroDir === 'up') { if (heroDir === 'up') {
// v[1] = v[1] =
// originFrom[i][1] + clipHeight + restHeight; originFrom[i][1] + clipHeight + restHeight;
// } }
// }); });
// } }
// renderable.x = tx; renderable.x = tx;
// renderable.y = ty; renderable.y = ty;
// if (toDir === 'left' || toDir === 'right') { if (toDir === 'left' || toDir === 'right') {
// renderable.x = tx + (clipWidth / 2 / cell - 0.5) * tdx; renderable.x = tx + (clipWidth / 2 / cell - 0.5) * tdx;
// renderable.render.forEach((v, i) => { renderable.render.forEach((v, i) => {
// v[2] = clipWidth; v[2] = clipWidth;
// if (toDir === 'right') { if (toDir === 'right') {
// v[0] = originTo[i][0] + beforeWidth; v[0] = originTo[i][0] + beforeWidth;
// } }
// }); });
// } else { } else {
// if (toDir === 'down') renderable.y = ty - 1 + progress; if (toDir === 'down') renderable.y = ty - 1 + progress;
// renderable.render.forEach((v, i) => { renderable.render.forEach((v, i) => {
// v[3] = clipHeight + restHeight; v[3] = clipHeight + restHeight;
// if (toDir === 'down') { if (toDir === 'down') {
// v[1] = originTo[i][1] + clipHeight + restHeight; v[1] = originTo[i][1] + clipHeight + restHeight;
// v[3] = clipHeight; v[3] = clipHeight;
// } }
// }); });
// } }
// }; };
// v.layer.delegateTicker(tick, this.moveSpeed, () => { v.layer.delegateTicker(tick, this.moveSpeed, () => {
// v.renderable!.render = originFrom; v.renderable!.render = originFrom;
// v.setAnimateDir(data.dir); v.setAnimateDir(data.dir);
// v.layer.moving.delete(renderable); v.layer.moving.delete(renderable);
// v.layer.requestUpdateMoving(); v.layer.requestUpdateMoving();
// res(); res();
// }); });
// }); });
// }); });
return Promise.all([]); return Promise.all(promises);
} }
private renderHeroLoop() { private renderHeroLoop() {
const adapter = HeroMover.adapter;
const viewport = HeroMover.viewport; const viewport = HeroMover.viewport;
if (!viewport) return; if (!adapter || !viewport) return;
const MotaRenderer = Mota.require('@motajs/render').MotaRenderer; const MotaRenderer = Mota.require('@motajs/render').MotaRenderer;
const render = MotaRenderer.get('render-main'); const render = MotaRenderer.get('render-main');
const group = render?.getElementById('layer-loop') as LayerGroup; const group = render?.getElementById('layer-loop') as LayerGroup;
@ -948,8 +952,10 @@ loading.once('coreInit', () => {
loading.once('coreInit', () => { loading.once('coreInit', () => {
if (main.replayChecking || main.mode === 'editor') return; if (main.replayChecking || main.mode === 'editor') return;
const Adapter = Mota.require('@motajs/render').RenderAdapter; const Adapter = Mota.require('@motajs/render').RenderAdapter;
const adapter = Adapter.get<HeroRenderer>('hero-adapter');
const viewport = Adapter.get<FloorViewport>('viewport'); const viewport = Adapter.get<FloorViewport>('viewport');
const layerAdapter = Adapter.get<Layer>('layer'); const layerAdapter = Adapter.get<Layer>('layer');
HeroMover.adapter = adapter;
HeroMover.viewport = viewport; HeroMover.viewport = viewport;
BlockMover.adapter = layerAdapter; BlockMover.adapter = layerAdapter;
}); });

View File

@ -1,2 +0,0 @@
/** 默认的勇士图片 */
export const DEFAULT_HERO_IMAGE: ImageIds = 'hero1.png';

View File

@ -49,9 +49,9 @@ export function create() {
} }
async function createModule() { async function createModule() {
UserClientBase.create();
ClientModules.create();
LegacyUI.create(); LegacyUI.create();
ClientModules.create();
UserClientBase.create();
await import('ant-design-vue/dist/antd.dark.css'); await import('ant-design-vue/dist/antd.dark.css');
main.renderLoaded = true; main.renderLoaded = true;

View File

@ -44,7 +44,6 @@
"42": "The '$1' property of map-render element is required.", "42": "The '$1' property of map-render element is required.",
"43": "Cannot bind face direction to main block $1, please call malloc in advance.", "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.", "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.", "1101": "Shadow extension needs 'floor-hero' extension as dependency.",
"1201": "Floor-damage extension needs 'floor-binder' extension as dependency.", "1201": "Floor-damage extension needs 'floor-binder' extension as dependency.",
"1301": "Portal extension need 'floor-binder' extension as dependency.", "1301": "Portal extension need 'floor-binder' extension as dependency.",

View File

@ -8,10 +8,9 @@ import { ITexture, ITextureSplitter, IRect } from './types';
export class TextureRowSplitter implements ITextureSplitter<number> { export class TextureRowSplitter implements ITextureSplitter<number> {
*split(texture: ITexture, data: number): Generator<ITexture> { *split(texture: ITexture, data: number): Generator<ITexture> {
const lines = Math.ceil(texture.height / data); const lines = Math.ceil(texture.height / data);
const { x, y } = texture.render().rect;
for (let i = 0; i < lines; i++) { for (let i = 0; i < lines; i++) {
const tex = new Texture(texture.source); const tex = new Texture(texture.source);
tex.clip(x, y + i * data, texture.width, data); tex.clip(0, i * data, texture.width, data);
yield tex; yield tex;
} }
} }
@ -24,10 +23,9 @@ export class TextureRowSplitter implements ITextureSplitter<number> {
export class TextureColumnSplitter implements ITextureSplitter<number> { export class TextureColumnSplitter implements ITextureSplitter<number> {
*split(texture: ITexture, data: number): Generator<ITexture> { *split(texture: ITexture, data: number): Generator<ITexture> {
const lines = Math.ceil(texture.width / data); const lines = Math.ceil(texture.width / data);
const { x, y } = texture.render().rect;
for (let i = 0; i < lines; i++) { for (let i = 0; i < lines; i++) {
const tex = new Texture(texture.source); const tex = new Texture(texture.source);
tex.clip(x + i * data, y, data, texture.height); tex.clip(i * data, 0, data, texture.height);
yield tex; yield tex;
} }
} }
@ -42,11 +40,10 @@ export class TextureGridSplitter implements ITextureSplitter<[number, number]> {
const [w, h] = data; const [w, h] = data;
const rows = Math.ceil(texture.width / w); const rows = Math.ceil(texture.width / w);
const lines = Math.ceil(texture.height / h); const lines = Math.ceil(texture.height / h);
const { x, y } = texture.render().rect; for (let y = 0; y < lines; y++) {
for (let ny = 0; ny < lines; ny++) { for (let x = 0; x < rows; x++) {
for (let nx = 0; nx < rows; nx++) {
const tex = new Texture(texture.source); const tex = new Texture(texture.source);
tex.clip(x + nx * w, y + ny * h, w, h); tex.clip(x * w, y * h, w, h);
yield tex; yield tex;
} }
} }

View File

@ -140,6 +140,7 @@ export class TextureMaxRectsStreamComposer
private nextCanvas() { private nextCanvas() {
this.nowCanvas = document.createElement('canvas'); this.nowCanvas = document.createElement('canvas');
this.nowCanvas.id = `${Math.random()}`;
this.nowCtx = this.nowCanvas.getContext('2d')!; this.nowCtx = this.nowCanvas.getContext('2d')!;
this.nowCanvas.width = this.maxWidth; this.nowCanvas.width = this.maxWidth;
this.nowCanvas.height = this.maxHeight; this.nowCanvas.height = this.maxHeight;

View File

@ -30,8 +30,8 @@ export class Texture implements ITexture {
this.height = source.height; this.height = source.height;
this.cl = 0; this.cl = 0;
this.ct = 0; this.ct = 0;
this.cr = source.width; this.cr = 0;
this.cb = source.height; this.cb = 0;
} }
/** /**
@ -42,14 +42,16 @@ export class Texture implements ITexture {
* @param h * @param h
*/ */
clip(x: number, y: number, w: number, h: number) { clip(x: number, y: number, w: number, h: number) {
const left = clamp(this.cl + x, this.cl, this.cr); const r = x + w;
const top = clamp(this.ct + y, this.ct, this.cb); const b = y + h;
const right = clamp(this.cl + x + w, this.cl, this.cr); if (x > this.width || y > this.height || r < 0 || b < 0) {
const bottom = clamp(this.ct + y + h, this.ct, this.cb);
if (left === right || top === bottom) {
logger.warn(69); logger.warn(69);
return; 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 width = right - left;
const height = bottom - top; const height = bottom - top;
if (width <= 0 || height <= 0) { if (width <= 0 || height <= 0) {
@ -58,8 +60,6 @@ export class Texture implements ITexture {
} }
this.cl = left; this.cl = left;
this.ct = top; this.ct = top;
this.cr = right;
this.cb = bottom;
this.width = right - left; this.width = right - left;
this.height = bottom - top; this.height = bottom - top;
} }