(feat: refactor hero renderer and remove unused code from layer)

This commit is contained in:
unanmed 2024-08-19 22:21:36 +08:00
parent 852a6667dc
commit 3ecf3a0d67
11 changed files with 473 additions and 520 deletions

View File

@ -117,3 +117,4 @@ dam4.png ---- 存档 59
[x] 复写 apirewrite()
[x] 对 vnode 进行简单的包装,提供出显示文字、显示图片等 api 以及修改 css 的 api
[] mapDamage 注册
[] Box 组件右下角添加 resize 按钮

View File

@ -0,0 +1,88 @@
type AdapterFunction<T> = (item: T, ...params: any[]) => Promise<any>;
/**
*
*
*
*/
export class RenderAdapter<T> {
static adapters: Map<string, RenderAdapter<any>> = new Map();
/** 所有元素的集合 */
items: Set<T> = new Set();
/** 适配器的id */
id: string;
private execute: Map<string, AdapterFunction<T>> = new Map();
constructor(id: string) {
this.id = id;
RenderAdapter.adapters.set(id, this);
}
/**
*
*/
add(item: T) {
this.items.add(item);
}
/**
*
*/
remove(item: T) {
this.items.delete(item);
}
/**
*
* @param fn
*/
recieve(id: string, fn: AdapterFunction<T>): void {
this.execute.set(id, fn);
}
/**
* Promise.all
* @returns
*/
all<R = any>(fn: string, ...params: any[]): Promise<R[]> {
const execute = this.execute.get(fn);
if (!execute) {
return Promise.reject();
} else {
return Promise.all(
[...this.items].map(v => execute!(v, ...params))
);
}
}
/**
* Promise.any
* @returns
*/
any<R = any>(fn: string, ...params: any[]): Promise<R> {
const execute = this.execute.get(fn);
if (!execute) {
return Promise.reject();
} else {
return Promise.any(
[...this.items].map(v => execute!(v, ...params))
);
}
}
/**
* adapter
*/
destroy() {
RenderAdapter.adapters.delete(this.id);
}
/**
*
*/
static get<T>(id: string): RenderAdapter<T> | undefined {
return this.adapters.get(id);
}
}

View File

@ -44,7 +44,7 @@ interface TextureRequire {
interface RenderableDataBase {
/** 图块的总帧数 */
frame: number;
/** 对应图块属性的动画帧数,-1表示没有设定 */
/** 对应图块属性的动画帧数,-1表示没有设定0表示第一帧 */
animate: number;
/** 是否是大怪物 */
bigImage: boolean;
@ -76,6 +76,15 @@ class TextureCache extends EventEmitter<TextureCacheEvent> {
renderable: Map<number, RenderableData | AutotileRenderable> = new Map();
/** 自动元件额外连接信息,用于对非自身图块进行连接 */
autoConn: Map<number, Set<number>> = new Map();
/** 行走图朝向绑定 */
characterDirection: Record<Dir, number> = {
down: 0,
left: 1,
right: 2,
up: 3
};
/** 行走图转向顺序 */
characterTurn: Dir[] = ['up', 'right', 'down', 'left'];
constructor() {
super();

View File

@ -2,7 +2,7 @@ import { isNil } from 'lodash-es';
import { EventEmitter } from '../common/eventEmitter';
import { MotaOffscreenCanvas2D } from '../fx/canvas2d';
import { Camera } from './camera';
import { Ticker } from 'mutate-animate';
import { Ticker, TickerFn } from 'mutate-animate';
export type RenderFunction = (
canvas: MotaOffscreenCanvas2D,
@ -123,6 +123,25 @@ interface IRenderFrame {
requestRenderFrame(fn: () => void): void;
}
interface IRenderTickerSupport {
/**
* ticker
* @param fn
* @param time
* @param end
* @returns id
*/
delegateTicker(fn: TickerFn, time?: number, end?: () => void): number;
/**
* ticker函数
* @param id id{@link IRenderTickerSupport.delegateTicker}
* @param callEnd {@link IRenderTickerSupport.delegateTicker}end参数
* @returns ticker不存在
*/
removeTicker(id: number, callEnd?: boolean): boolean;
}
interface RenderItemEvent {
beforeUpdate: (item?: RenderItem) => void;
afterUpdate: (item?: RenderItem) => void;
@ -130,10 +149,16 @@ interface RenderItemEvent {
afterRender: () => void;
}
interface TickerDelegation {
fn: TickerFn;
endFn?: () => void;
}
const beforeFrame: (() => void)[] = [];
const afterFrame: (() => void)[] = [];
const renderFrame: (() => void)[] = [];
// todo: 添加模型变换
export abstract class RenderItem
extends EventEmitter<RenderItemEvent>
implements
@ -141,12 +166,17 @@ export abstract class RenderItem
IRenderUpdater,
IRenderAnchor,
IRenderConfig,
IRenderFrame
IRenderFrame,
IRenderTickerSupport
{
/** 渲染的全局ticker */
static ticker: Ticker = new Ticker();
/** 包括但不限于怪物、npc、自动元件的动画帧数 */
static animatedFrame: number = 0;
/** ticker委托映射 */
static tickerMap: Map<number, TickerDelegation> = new Map();
/** ticker委托id */
static tickerId: number = 0;
zIndex: number = 0;
@ -263,6 +293,32 @@ export abstract class RenderItem
renderFrame.push(fn);
}
delegateTicker(fn: TickerFn, time?: number, end?: () => void): number {
const id = RenderItem.tickerId++;
if (typeof time === 'number' && time === 0) return id;
const delegation: TickerDelegation = {
fn,
endFn: end
};
RenderItem.tickerMap.set(id, delegation);
RenderItem.ticker.add(fn);
if (typeof time === 'number' && time < 2147438647 && time > 0) {
setTimeout(() => {
RenderItem.ticker.remove(fn);
end?.();
}, time);
}
return id;
}
removeTicker(id: number, callEnd: boolean = true): boolean {
const delegation = RenderItem.tickerMap.get(id);
if (!delegation) return false;
RenderItem.ticker.remove(delegation.fn);
if (callEnd) delegation.endFn?.();
return true;
}
/**
*
*/

View File

@ -37,7 +37,6 @@ export class FloorDamageExtends implements ILayerGroupRenderExtends {
this.sprite.setMapSize(map.width, map.height);
ensureFloorDamage(floor);
const enemy = core.status.maps[floor].enemy;
console.log(enemy);
this.sprite.updateCollection(enemy);
}
@ -386,7 +385,7 @@ export class Damage extends Sprite {
* @param camera
*/
renderDamage(camera: Camera) {
console.time('damage');
// console.time('damage');
const { ctx } = this.damageMap;
ctx.save();
transformCanvas(this.damageMap, camera, true);
@ -435,6 +434,6 @@ export class Damage extends Sprite {
block.cache.set(v, temp.canvas);
});
ctx.restore();
console.timeEnd('damage');
// console.timeEnd('damage');
}
}

View File

@ -39,6 +39,7 @@ hook.on('changingFloor', floor => {
interface LayerGroupBinderEvent {
update: [floor: FloorIds];
setBlock: [x: number, y: number, floor: FloorIds, block: AllNumbers];
floorChange: [floor: FloorIds];
}
/**
@ -86,7 +87,7 @@ export class LayerGroupFloorBinder
*
*/
updateBind() {
if (this.needUpdate) return;
if (this.needUpdate || !this.group) return;
this.needUpdate = true;
this.group.requestBeforeFrame(() => {
this.needUpdate = false;
@ -182,6 +183,7 @@ export class LayerFloorBinder implements ILayerRenderExtends {
bindThis() {
this.floor = void 0;
this.bindThisFloor = true;
this.updateBind();
}
/**
@ -191,6 +193,7 @@ export class LayerFloorBinder implements ILayerRenderExtends {
bindFloor(floorId: FloorIds) {
this.bindThisFloor = false;
this.floor = floorId;
this.updateBind();
}
/**

View File

@ -1,3 +1,266 @@
import { ILayerRenderExtends } from './layer';
import { ILayerRenderExtends, Layer, LayerMovingRenderable } from './layer';
import { SizedCanvasImageSource } from './misc';
import { RenderAdapter } from '../adapter';
import { logger } from '@/core/common/logger';
import EventEmitter from 'eventemitter3';
import { texture } from '../cache';
export class HeroRenderer implements ILayerRenderExtends {}
type HeroMovingStatus = 'stop' | 'moving';
interface HeroRenderEvent {
stepEnd: [];
}
export class HeroRenderer
extends EventEmitter<HeroRenderEvent>
implements ILayerRenderExtends
{
id: string = 'floor-hero';
/** 勇士的图片资源 */
image?: SizedCanvasImageSource;
cellWidth?: number;
cellHeight?: number;
/** 勇士的渲染信息 */
renderable?: LayerMovingRenderable;
layer!: Layer;
// hero?: Hero;
/** 勇士移动状态 */
status: HeroMovingStatus = 'stop';
/** 当前移动帧数 */
movingFrame: number = 0;
/** 勇士移动速度 */
speed: number = 100;
/** 勇士移动定时器id */
private moveId: number = -1;
/** 上一次帧数切换的时间 */
private lastFrameTime: number = 0;
/** 当前的移动方向 */
private moveDir: Dir = 'down';
/** 上一步走到格子上的时间 */
private lastStepTime: number = 0;
/** 是否已经执行了当前步移动 */
private moveDetached?: Promise<void>;
/**
* {@link moveDir}
* {@link moveDir}
*/
private stepDir: Dir = 'down';
/** 每步的格子增量 */
private stepDelta: Loc = { x: 0, y: 1 };
/**
*
* @param image
*/
setImage(image: SizedCanvasImageSource) {
this.image = image;
this.split();
this.layer.update(this.layer);
}
setMoveSpeed(speed: number) {
this.speed = speed;
}
/**
* renderable信息
*/
split() {
this.cellWidth = this.image!.width / 4;
this.cellHeight = this.image!.height / 4;
this.generateRenderable();
}
/**
*
*/
generateRenderable() {
if (!this.image) return;
this.renderable = {
image: this.image,
frame: 4,
x: core.status.hero.loc.x,
y: core.status.hero.loc.y,
zIndex: core.status.hero.loc.y,
autotile: false,
bigImage: true,
render: this.getRenderFromDir(this.moveDir),
animate: 0
};
}
/**
*
* @param dir
*/
getRenderFromDir(dir: Dir): [number, number, number, number][] {
if (!this.cellWidth || !this.cellHeight) return [];
const index = texture.characterDirection[dir];
const y = index * this.cellHeight;
return [0, 1, 2, 3].map(v => {
return [v * this.cellWidth!, y, this.cellWidth!, this.cellHeight!];
});
}
/**
*
* @param hero
*/
// setHero(hero: Hero) {
// this.hero = hero;
// }
/**
*
*/
readyMove() {
if (this.status !== 'stop') return;
this.status = 'moving';
this.lastFrameTime = Date.now();
}
/**
*
*/
private moveTick(time: number) {
if (this.status !== 'moving') return;
if (!this.renderable) return;
if (time - this.lastFrameTime > this.speed) {
this.lastFrameTime = time;
this.movingFrame++;
this.movingFrame %= 4;
}
const progress = (time - this.lastStepTime) / this.speed;
const { x: dx, y: dy } = this.stepDelta;
const { x, y } = core.status.hero.loc;
if (progress >= 1) {
this.renderable.x = x + dx;
this.renderable.y = y + dy;
this.emit('stepEnd');
} else {
const rx = dx * progress + x;
const ry = dy * progress + y;
this.renderable.x = rx;
this.renderable.y = ry;
}
this.renderable.animate = this.movingFrame;
this.layer.update(this.layer);
}
/**
*
*/
private step() {
this.stepDir = this.moveDir;
this.lastStepTime = Date.now();
this.stepDelta = core.utils.scan[this.stepDir];
this.turn(this.stepDir);
}
/**
*
*/
move(dir: Dir): Promise<void> {
if (this.status !== 'moving') {
logger.error(
12,
`Cannot move while status is not 'moving'. Call 'readyMove' first.`
);
return Promise.reject();
}
this.moveDir = dir;
if (this.moveDetached) return this.moveDetached;
else {
this.step();
return (this.moveDetached = new Promise<void>(resolve => {
this.moveDetached = void 0;
this.once('stepEnd', resolve);
}));
}
}
/**
*
*/
endMove(): Promise<void> {
if (this.status !== 'moving') return Promise.reject();
return new Promise<void>(resolve => {
this.once('stepEnd', () => {
this.status = 'stop';
this.movingFrame = 0;
this.layer.removeTicker(this.moveId);
this.render();
resolve();
});
});
}
/**
*
* @param dir
*/
turn(dir?: Dir): void {
if (!dir) {
const index = texture.characterTurn.indexOf(this.moveDir) + 1;
const length = texture.characterTurn.length;
const next = texture.characterTurn[index % length];
return this.turn(next);
}
this.moveDir = dir;
if (!this.renderable) return;
this.renderable.render = this.getRenderFromDir(this.moveDir);
this.layer.update(this.layer);
}
/**
*
*/
render() {
if (!this.renderable) return;
if (this.status === 'stop') {
this.renderable.animate = -1;
} else {
this.renderable.animate = this.movingFrame;
}
this.layer.update(this.layer);
}
awake(layer: Layer): void {
this.layer = layer;
adapter.add(this);
this.moveId = layer.delegateTicker(() => {
this.moveTick(Date.now());
});
}
onDestroy(layer: Layer): void {
adapter.remove(this);
layer.removeTicker(this.moveId);
}
onMovingUpdate(layer: Layer, renderable: LayerMovingRenderable[]): void {
if (this.renderable) renderable.push(this.renderable);
}
}
const adapter = new RenderAdapter<HeroRenderer>('hero-adapter');
adapter.recieve('readyMove', item => {
item.readyMove();
return Promise.resolve();
});
adapter.recieve('move', (item, dir: Dir) => {
return item.move(dir);
});
adapter.recieve('endMove', item => {
return item.endMove();
});

View File

@ -168,29 +168,6 @@ export class LayerGroup extends Container {
this.cellSize = size;
}
/**
* 使Layer实例会被摧毁
* @param preset
*/
// usePreset(preset: LayerGroupPreset) {
// this.emptyLayer();
// const child = layers.map((v, i) => {
// const layer = new Layer();
// layer.bindLayer(v);
// layer.setZIndex(i * 10);
// this.layers.set(v, layer);
// return layer;
// });
// this.appendChild(...child);
// if (preset === 'defaults') {
// const damage = new Damage(this.floorId);
// this.appendChild(damage);
// this.damage = damage;
// damage.setZIndex(60);
// }
// }
/**
*
*/
@ -290,45 +267,6 @@ export class LayerGroup extends Container {
}
}
/**
*
*/
// bindThis() {
// this.bindThisFloor = true;
// this.updateFloor();
// }
/**
*
* @param floor
*/
// bindFloor(floor?: FloorIds) {
// if (!floor) {
// this.bindThisFloor = true;
// } else {
// this.floorId = floor;
// }
// this.updateFloor();
// }
/**
*
*/
// updateFloor() {
// if (this.bindThisFloor) {
// this.floorId = core.status.floorId;
// }
// const floor = this.floorId;
// if (!floor) return;
// this.layers.forEach(v => {
// v.bindData(floor);
// if (v.layer === 'bg') {
// v.bindBackground(floor);
// }
// });
// // this.damage?.bindFloor(floor);
// }
/**
*
* @param camera
@ -348,37 +286,6 @@ export class LayerGroup extends Container {
this.needRender = void 0;
}
/**
*
*/
// addDamage() {
// if (!this.damage) {
// const damage = new Damage();
// this.appendChild(damage);
// this.damage = damage;
// if (this.floorId) damage.bindFloor(this.floorId);
// return damage;
// }
// return this.damage;
// }
/**
*
*/
// removeDamage() {
// if (this.damage) {
// this.removeChild(this.damage);
// this.damage = void 0;
// }
// }
/**
*
*/
// updateDamage(x: number, y: number, width: number, height: number) {
// this.damage?.updateRenderable(x, y, width, height);
// }
/**
*
*/
@ -405,31 +312,6 @@ export class LayerGroup extends Container {
const hook = Mota.require('var', 'hook');
// hook.on('changingFloor', floorId => {
// LayerGroup.list.forEach(v => {
// if (v.floorId === floorId || v.bindThisFloor) v.updateFloor();
// });
// });
// hook.on('setBlock', (x, y, floorId, block) => {
// LayerGroup.list.forEach(v => {
// if (v.floorId === floorId) {
// v.updateDamage(x, y, 1, 1);
// v.layers.forEach(v => {
// if (v.layer === 'event') {
// v.putRenderData([block], 1, x, y);
// }
// });
// }
// });
// });
// hook.on('statusBarUpdate', () => {
// LayerGroup.list.forEach(v => {
// if (v.floorId) {
// v.damage?.bindFloor(v.floorId);
// }
// });
// });
// todo: animate frame.
// renderEmits.on('animateFrame', () => {
// LayerGroup.list.forEach(v => {
@ -662,10 +544,6 @@ export interface ILayerRenderExtends {
autotiles: Record<number, number>
): void;
// onDataBind?: (layer: Layer, floor: FloorIds, name?: FloorLayer) => void;
// onLayerBind?: (layer: Layer, name: FloorLayer) => void;
// onDataUpdate?: (layer: Layer, data: number[]) => void;
/**
*
* @param layer Layer实例
@ -695,7 +573,7 @@ export interface ILayerRenderExtends {
/**
*
* @param layer Layer实例
* @param renderable
* @param renderable
*/
onMovingUpdate?(layer: Layer, renderable: LayerMovingRenderable[]): void;
@ -728,7 +606,7 @@ interface LayerCacheItem {
canvas: HTMLCanvasElement;
}
interface LayerMovingRenderable extends RenderableData {
export interface LayerMovingRenderable extends RenderableData {
zIndex: number;
x: number;
y: number;
@ -800,6 +678,7 @@ export class Layer extends Container {
floorId?: FloorIds;
/** 渲染的层 */
layer?: FloorLayer;
// todo: renderable分块存储优化循环绘制性能
/** 渲染数据 */
renderData: number[] = [];
/** 自动元件的连接信息键表示图块在渲染数据中的索引值表示连接信息是个8位二进制 */
@ -823,6 +702,7 @@ export class Layer extends Container {
moving: MovingBlock[] = [];
/** 大怪物渲染信息 */
bigImages: Map<number, LayerMovingRenderable> = new Map();
// todo: 是否需要桶排?
/** 移动层的渲染信息 */
movingRenderable: LayerMovingRenderable[] = [];
/** 下一此渲染时是否需要更新移动层的渲染信息 */
@ -1207,45 +1087,6 @@ export class Layer extends Container {
}
}
/**
*
* @param floor id
* @param layer
*/
// bindData(floor: FloorIds, layer?: FloorLayer) {
// this.floorId = floor;
// if (layer) this.layer = layer;
// const f = core.status.maps[floor];
// this.mapWidth = f.width;
// this.mapHeight = f.height;
// this.block.size(f.width, f.height);
// this.updateDataFromFloor();
// }
/**
*
* @param layer
*/
// bindLayer(layer: FloorLayer) {
// this.layer = layer;
// this.updateDataFromFloor();
// }
/**
*
*/
// updateDataFromFloor() {
// if (!this.floorId || !this.layer) return;
// const floor = core.status.maps[this.floorId];
// if (this.layer === 'event') {
// const map = floor.map;
// this.putRenderData(map.flat(), floor.width, 0, 0);
// } else {
// const map = core.maps._getBgFgMapArray(this.layer, this.floorId);
// this.putRenderData(map.flat(), floor.width, 0, 0);
// }
// }
/**
* putRenderData
* @param width
@ -1323,11 +1164,11 @@ export class Layer extends Container {
});
}
});
this.movingRenderable.sort((a, b) => a.zIndex - b.zIndex);
for (const ex of this.extend.values()) {
ex.onMovingUpdate?.(this, this.movingRenderable);
}
this.movingRenderable.sort((a, b) => a.zIndex - b.zIndex);
}
/**
@ -1499,26 +1340,34 @@ export class Layer extends Container {
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.translate(core._PX_ / 2, core._PY_ / 2);
ctx.transform(a, b, c, d, e, f);
const r =
Math.max(a, b, c, d) ** 2 * Math.max(core._PX_, core._PY_) * 2;
const max1 = Math.max(a, b, c, d) ** 2;
const max2 = Math.max(core._PX_, core._PY_) * 2;
const r = (max1 * max2) ** 2;
this.movingRenderable.forEach(v => {
const { x, y, image, frame: blockFrame, render } = v;
const f = frame % 4;
const i = frame === 4 && blockFrame === 3 ? 1 : f;
const { x, y, image, frame: blockFrame, render, animate } = v;
const ff = frame % 4;
const i =
animate === -1
? frame === 4 && blockFrame === 3
? 1
: ff
: animate;
const [sx, sy, w, h] = render[i];
const px = x * cell - w / 2 + halfCell;
const py = y * cell - h + cell;
const ex = px + w;
const ey = py + h;
if (
(px - e) ** 2 > r ||
(py - f) ** 2 > r ||
(ex - e) ** 2 > r ||
(ey - f) ** 2 > r
(px + e) ** 2 > r ||
(py + f) ** 2 > r ||
(ex + e) ** 2 > r ||
(ey + f) ** 2 > r
) {
return;
}
ctx.drawImage(image, sx, sy, w, h, px, py, w, h);
});
@ -1578,339 +1427,3 @@ export class Layer extends Container {
super.destroy();
}
}
// interface DamageRenderable {
// x: number;
// y: number;
// align: CanvasTextAlign;
// baseline: CanvasTextBaseline;
// text: string;
// color: CanvasStyle;
// }
// export class Damage extends Sprite {
// floorId?: FloorIds;
// mapWidth: number = 0;
// mapHeight: number = 0;
// /** 键表示格子索引,值表示在这个格子上的渲染信息(当然实际渲染位置可以不在这个格子上) */
// renderable: Map<number, DamageRenderable[]> = new Map();
// block: BlockCacher<LayerCacheItem>;
// /** 记录所有需要重新计算伤害的分块,这样可以不用一次性计算全地图的伤害,从而优化性能 */
// needUpdateBlock: Set<number> = new Set();
// cellSize: number = 32;
// /** 伤害渲染层,渲染至之后再渲染到目标层 */
// damageMap: MotaOffscreenCanvas2D = new MotaOffscreenCanvas2D();
// /** 字体 */
// font: string = "14px 'normal'";
// /** 描边样式 */
// strokeColor: CanvasStyle = '#000';
// /** 描边粗细 */
// strokeWidth: number = 2;
// constructor(floor?: FloorIds) {
// super();
// this.block = new BlockCacher(0, 0, core._WIDTH_, 1);
// this.type = 'absolute';
// if (floor) this.bindFloor(floor);
// this.size(core._PX_, core._PY_);
// this.damageMap.withGameScale(true);
// this.damageMap.setHD(true);
// this.damageMap.setAntiAliasing(true);
// this.damageMap.size(core._PX_, core._PY_);
// this.setRenderFn((canvas, camera) => {
// const { ctx } = canvas;
// const { width, height } = canvas.canvas;
// ctx.save();
// ctx.imageSmoothingEnabled = false;
// this.renderDamage(camera);
// ctx.setTransform(1, 0, 0, 1, 0, 0);
// ctx.drawImage(this.damageMap.canvas, 0, 0, width, height);
// ctx.restore();
// });
// }
// /**
// * 更新楼层信息
// */
// updateFloor(): void {
// const floor = this.floorId;
// if (!floor) return;
// core.extractBlocks(floor);
// const f = core.status.maps[floor];
// this.updateRenderable(0, 0, f.width, f.height);
// }
// /**
// * 绑定显示楼层
// * @param floor 绑定的楼层
// */
// bindFloor(floor: FloorIds) {
// this.floorId = floor;
// core.extractBlocks(this.floorId);
// const f = core.status.maps[this.floorId];
// this.mapWidth = f.width;
// this.mapHeight = f.height;
// this.block.size(f.width, f.height);
// this.updateFloor();
// }
// /**
// * 根据需要更新的区域更新显示信息,注意调用前需要保证怪物信息是最新的,也就是要在计算过怪物信息后才能调用这个
// * @param block 要更新的区域
// */
// updateRenderableBlock(block: Set<number>) {
// if (!this.floorId) return;
// const size = this.block.blockSize;
// Mota.require('fn', 'ensureFloorDamage')(this.floorId);
// const col = core.status.maps[this.floorId].enemy;
// const obj = core.getMapBlocksObj(this.floorId);
// if (block.size === 1) {
// // 如果是单个分块,直接进行更新
// const index = [...block.values()][0];
// if (!this.needUpdateBlock.has(index)) return;
// this.needUpdateBlock.delete(index);
// const [x, y] = this.block.getBlockXYByIndex(index);
// const sx = x * size;
// const sy = y * size;
// const ex = sx + size;
// const ey = sy + size;
// for (let ny = sy; ny < ey; ny++) {
// for (let nx = sx; nx < ex; nx++) {
// const index = nx + ny * this.mapWidth;
// this.renderable.delete(index);
// this.pushMapDamage(obj, col, nx, ny);
// }
// }
// col.range
// .scan('rect', { x: sx, y: sy, w: size, h: size })
// .forEach(v => {
// if (isNil(v.x) || isNil(v.y)) return;
// this.pushEnemyDamage(v, v.x, v.y);
// });
// } else {
// // 否则使用 X 扫描线的方式获取每个y坐标对应的最小最大x坐标从而可以更快地找出在范围内的怪物
// const xyMap: Map<number, LocArr> = new Map();
// const toEmitArea: [number, number, number, number][] = [];
// block.forEach(v => {
// if (!this.needUpdateBlock.has(v)) return;
// this.needUpdateBlock.delete(v);
// const [x, y] = this.block.getBlockXYByIndex(v);
// const sx = x * size;
// const sy = y * size;
// const ex = sx + size;
// const ey = sy + size;
// toEmitArea.push([sx, sy, size, size]);
// for (let ny = sy; ny < ey; ny++) {
// let arr = xyMap.get(ny);
// if (!arr) {
// arr = [sx, ex];
// xyMap.set(ny, arr);
// } else {
// if (sx < arr[0]) arr[0] = sx;
// if (ex > arr[1]) arr[1] = ex;
// }
// for (let nx = sx; nx < ex; nx++) {
// const index = nx + ny * this.mapWidth;
// this.renderable.delete(index);
// this.pushMapDamage(obj, col, x, y);
// }
// }
// });
// xyMap.forEach(([sx, ex], y) => {
// col.list.forEach(v => {
// if (isNil(v.x) || isNil(v.y)) return;
// if (v.y !== y || v.x < sx || v.x >= ex) return;
// this.pushEnemyDamage(v, v.x, v.y);
// });
// });
// toEmitArea.forEach(v => {
// this.emit('dataUpdate', v[0], v[1], v[2], v[3]);
// });
// this.update(this);
// }
// }
// /**
// * 更新指定区域内的渲染信息,注意调用前需要保证怪物信息是最新的,也就是要在计算过怪物信息后才能调用这个
// */
// updateRenderable(x: number, y: number, width: number, height: number) {
// if (!this.floorId) return;
// this.block.getIndexOf(x, y, width, height).forEach(v => {
// this.block.clearCache(v, 1);
// this.needUpdateBlock.add(v);
// });
// this.update(this);
// }
// /**
// * 向渲染列表添加渲染内容,应当在 `dataUpdate` 的事件中进行调用,其他位置不应当直接调用
// */
// pushDamageRenderable(x: number, y: number, ...data: DamageRenderable[]) {
// const index = x + y * this.mapWidth;
// let arr = this.renderable.get(index);
// if (!arr) {
// arr = [];
// this.renderable.set(index, arr);
// }
// arr.push(...data);
// }
// private pushMapDamage(
// obj: Record<LocString, Block>,
// col: EnemyCollection,
// x: number,
// y: number
// ) {
// const loc = `${x},${y}` as LocString;
// const dam = col.mapDamage[loc];
// if (!dam || obj[loc]?.event.noPass) return;
// let text = '';
// let color = '#fa3';
// if (dam.damage > 0) {
// text = core.formatBigNumber(dam.damage, true);
// } else if (dam.mockery) {
// dam.mockery.sort((a, b) =>
// a[0] === b[0] ? a[1] - b[1] : a[0] - b[0]
// );
// const [tx, ty] = dam.mockery[0];
// const dir = x > tx ? '←' : x < tx ? '→' : y > ty ? '↑' : '↓';
// text = '嘲' + dir;
// color = '#fd4';
// } else if (dam.hunt) {
// text = '猎';
// color = '#fd4';
// } else {
// return;
// }
// const mapDam: DamageRenderable = {
// align: 'center',
// baseline: 'middle',
// text,
// color,
// x: x * this.cellSize + this.cellSize / 2,
// y: y * this.cellSize + this.cellSize / 2
// };
// this.pushDamageRenderable(x, y, mapDam);
// }
// private pushEnemyDamage(enemy: DamageEnemy, x: number, y: number) {
// const dam = enemy.calDamage().damage;
// const cri = enemy.calCritical(1)[0]?.atkDelta ?? Infinity;
// const dam1: DamageRenderable = {
// align: 'left',
// baseline: 'alphabetic',
// text: !isFinite(dam) ? '???' : core.formatBigNumber(dam, true),
// color: getDamageColor(dam),
// x: x * this.cellSize + 1,
// y: y * this.cellSize + this.cellSize - 1
// };
// const dam2: DamageRenderable = {
// align: 'left',
// baseline: 'alphabetic',
// text: !isFinite(cri) ? '?' : core.formatBigNumber(cri, true),
// color: '#fff',
// x: x * this.cellSize + 1,
// y: y * this.cellSize + this.cellSize - 11
// };
// this.pushDamageRenderable(x, y, dam1, dam2);
// }
// /**
// * 计算需要渲染哪些块
// */
// calNeedRender(camera: Camera) {
// if (this.parent instanceof LayerGroup) {
// // 如果处于地图组中,每个地图的渲染区域应该是一样的,因此可以缓存优化
// return this.parent.cacheNeedRender(camera, this.block);
// } else if (this.parent instanceof Layer) {
// // 如果是地图的子元素直接调用Layer的计算函数
// return this.parent.calNeedRender(camera);
// } else {
// return calNeedRenderOf(camera, this.cellSize, this.block);
// }
// }
// /**
// * 渲染伤害值
// */
// renderDamage(camera: Camera) {
// if (!this.floorId) return;
// const { ctx } = this.damageMap;
// ctx.save();
// transformCanvas(this.damageMap, camera, true);
// const { res: render } = this.calNeedRender(camera);
// this.updateRenderableBlock(render);
// const block = this.block;
// const cell = this.cellSize;
// const size = cell * block.blockSize;
// render.forEach(v => {
// const [x, y] = block.getBlockXYByIndex(v);
// const bx = x * block.blockSize;
// const by = y * block.blockSize;
// const px = bx * cell;
// const py = by * cell;
// // 检查有没有缓存
// const cache = block.cache.get(v * block.cacheDepth);
// if (cache && cache.floorId === this.floorId) {
// ctx.drawImage(cache.canvas, px, py, size, size);
// return;
// }
// // 否则依次渲染并写入缓存
// const temp = new MotaOffscreenCanvas2D();
// temp.setHD(true);
// temp.setAntiAliasing(true);
// temp.withGameScale(true);
// temp.size(size, size);
// const { ctx: ct } = temp;
// ct.font = this.font;
// ct.strokeStyle = this.strokeColor;
// ct.lineWidth = this.strokeWidth;
// const ex = bx + block.blockSize;
// const ey = by + block.blockSize;
// for (let nx = bx; nx < ex; nx++) {
// for (let ny = by; ny < ey; ny++) {
// const index = nx + ny * block.blockSize;
// const render = this.renderable.get(index);
// render?.forEach(v => {
// if (!v) return;
// ct.fillStyle = v.color;
// ct.textAlign = v.align;
// ct.textBaseline = v.baseline;
// ct.strokeText(v.text, v.x, v.y);
// ct.fillText(v.text, v.x, v.y);
// });
// }
// }
// ct.drawImage(temp.canvas, px, py, size, size);
// block.cache.set(v * block.cacheDepth, {
// canvas: temp.canvas,
// floorId: this.floorId
// });
// });
// ctx.restore();
// }
// }

View File

@ -6,6 +6,7 @@ import { RenderItem, transformCanvas, withCacheRender } from './item';
import { FloorLayer, Layer, LayerGroup } from './preset/layer';
import { LayerGroupFloorBinder } from './preset/floor';
import { FloorDamageExtends } from './preset/damage';
import { HeroRenderer } from './preset/hero';
export class MotaRenderer extends Container {
static list: Set<MotaRenderer> = new Set();
@ -67,7 +68,7 @@ export class MotaRenderer extends Container {
*
*/
render() {
console.time();
// console.time();
const { canvas, ctx } = this.target;
const camera = this.camera;
this.emit('beforeRender');
@ -94,7 +95,7 @@ export class MotaRenderer extends Container {
});
});
this.emit('afterRender');
console.timeEnd();
// console.timeEnd();
}
update(item?: RenderItem) {
@ -152,11 +153,20 @@ Mota.require('var', 'hook').once('reset', () => {
const binder = new LayerGroupFloorBinder();
const damage = new FloorDamageExtends();
const hero = new HeroRenderer();
layer.extends(binder);
layer.extends(damage);
layer.getLayer('event')?.extends(hero);
binder.bindThis();
render.appendChild(layer);
layer.requestAfterFrame(() => {
hero.setImage(core.material.images.images['hero2.png']);
});
layer.delegateTicker(() => {
hero.turn();
}, 10000);
camera.move(240, 240);
render.update();

View File

@ -365,6 +365,7 @@ export class Hero<T extends object = IHeroStatusDefault>
x: number;
y: number;
floorId: FloorIds;
dir: Dir2;
readonly items: Map<AllIdsOf<'items'>, number> = new Map();
readonly id: string;
@ -384,6 +385,7 @@ export class Hero<T extends object = IHeroStatusDefault>
this.y = y;
this.floorId = floorId;
this.state = state;
this.dir = 'down';
}
/**

View File

@ -294,12 +294,14 @@ interface Control {
setAutomaticRoute(destX: number, destY: number, stepPostfix: Loc[]): void;
/**
* @deprecated
*
* @param steps
*/
setAutoHeroMove(steps: CompressedStep[]): void;
/**
* @deprecated
*
*/
setHeroMoveInterval(callback?: () => any): void;
@ -310,6 +312,7 @@ interface Control {
moveOneStep(callback?: () => any): void;
/**
* @deprecated
*
* @example core.moveAction(core.doAction); // 尝试前进一步,然后继续事件处理
* @param callback
@ -317,6 +320,7 @@ interface Control {
moveAction(callback?: () => void): void;
/**
* @deprecated
*
* @example core.moveHero(); // 连续前进
* @param direction
@ -325,11 +329,13 @@ interface Control {
moveHero(direction?: Dir, callback?: () => void): void;
/**
* @deprecated
*
*/
isMoving(): boolean;
/**
* @deprecated
*
* @example core.waitHeroToStop(core.vibrate); // 等待勇士停下然后视野左右抖动1秒
* @param callback
@ -337,6 +343,7 @@ interface Control {
waitHeroToStop(callback?: () => void): void;
/**
* @deprecated
*
* @example core.turnHero(); // 主角顺时针旋转即单击主角或按下Z键的效果
* @param direction up, down, left, right, :left, :right, :back七种之一不填视为:right
@ -360,6 +367,7 @@ interface Control {
tryMoveDirectly(destX: number, destY: number): boolean;
/**
* @deprecated
*
* @example core.drawHero(); // 原地绘制主角的静止帧
* @param status
@ -373,6 +381,7 @@ interface Control {
): void;
/**
* @deprecated
*
* @param opacity
* @param moveMode