mirror of
https://github.com/unanmed/HumanBreak.git
synced 2025-02-28 17:37:07 +08:00
(feat: refactor hero renderer and remove unused code from layer)
This commit is contained in:
parent
852a6667dc
commit
3ecf3a0d67
1
idea.md
1
idea.md
@ -117,3 +117,4 @@ dam4.png ---- 存档 59
|
|||||||
[x] 复写 api,rewrite()
|
[x] 复写 api,rewrite()
|
||||||
[x] 对 vnode 进行简单的包装,提供出显示文字、显示图片等 api 以及修改 css 的 api
|
[x] 对 vnode 进行简单的包装,提供出显示文字、显示图片等 api 以及修改 css 的 api
|
||||||
[] mapDamage 注册
|
[] mapDamage 注册
|
||||||
|
[] Box 组件右下角添加 resize 按钮
|
||||||
|
88
src/core/render/adapter.ts
Normal file
88
src/core/render/adapter.ts
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
@ -44,7 +44,7 @@ interface TextureRequire {
|
|||||||
interface RenderableDataBase {
|
interface RenderableDataBase {
|
||||||
/** 图块的总帧数 */
|
/** 图块的总帧数 */
|
||||||
frame: number;
|
frame: number;
|
||||||
/** 对应图块属性的动画帧数,-1表示没有设定 */
|
/** 对应图块属性的动画帧数,-1表示没有设定,0表示第一帧 */
|
||||||
animate: number;
|
animate: number;
|
||||||
/** 是否是大怪物 */
|
/** 是否是大怪物 */
|
||||||
bigImage: boolean;
|
bigImage: boolean;
|
||||||
@ -76,6 +76,15 @@ class TextureCache extends EventEmitter<TextureCacheEvent> {
|
|||||||
renderable: Map<number, RenderableData | AutotileRenderable> = new Map();
|
renderable: Map<number, RenderableData | AutotileRenderable> = new Map();
|
||||||
/** 自动元件额外连接信息,用于对非自身图块进行连接 */
|
/** 自动元件额外连接信息,用于对非自身图块进行连接 */
|
||||||
autoConn: Map<number, Set<number>> = 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() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
@ -2,7 +2,7 @@ import { isNil } from 'lodash-es';
|
|||||||
import { EventEmitter } from '../common/eventEmitter';
|
import { EventEmitter } from '../common/eventEmitter';
|
||||||
import { MotaOffscreenCanvas2D } from '../fx/canvas2d';
|
import { MotaOffscreenCanvas2D } from '../fx/canvas2d';
|
||||||
import { Camera } from './camera';
|
import { Camera } from './camera';
|
||||||
import { Ticker } from 'mutate-animate';
|
import { Ticker, TickerFn } from 'mutate-animate';
|
||||||
|
|
||||||
export type RenderFunction = (
|
export type RenderFunction = (
|
||||||
canvas: MotaOffscreenCanvas2D,
|
canvas: MotaOffscreenCanvas2D,
|
||||||
@ -123,6 +123,25 @@ interface IRenderFrame {
|
|||||||
requestRenderFrame(fn: () => void): void;
|
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 {
|
interface RenderItemEvent {
|
||||||
beforeUpdate: (item?: RenderItem) => void;
|
beforeUpdate: (item?: RenderItem) => void;
|
||||||
afterUpdate: (item?: RenderItem) => void;
|
afterUpdate: (item?: RenderItem) => void;
|
||||||
@ -130,10 +149,16 @@ interface RenderItemEvent {
|
|||||||
afterRender: () => void;
|
afterRender: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface TickerDelegation {
|
||||||
|
fn: TickerFn;
|
||||||
|
endFn?: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
const beforeFrame: (() => void)[] = [];
|
const beforeFrame: (() => void)[] = [];
|
||||||
const afterFrame: (() => void)[] = [];
|
const afterFrame: (() => void)[] = [];
|
||||||
const renderFrame: (() => void)[] = [];
|
const renderFrame: (() => void)[] = [];
|
||||||
|
|
||||||
|
// todo: 添加模型变换
|
||||||
export abstract class RenderItem
|
export abstract class RenderItem
|
||||||
extends EventEmitter<RenderItemEvent>
|
extends EventEmitter<RenderItemEvent>
|
||||||
implements
|
implements
|
||||||
@ -141,12 +166,17 @@ export abstract class RenderItem
|
|||||||
IRenderUpdater,
|
IRenderUpdater,
|
||||||
IRenderAnchor,
|
IRenderAnchor,
|
||||||
IRenderConfig,
|
IRenderConfig,
|
||||||
IRenderFrame
|
IRenderFrame,
|
||||||
|
IRenderTickerSupport
|
||||||
{
|
{
|
||||||
/** 渲染的全局ticker */
|
/** 渲染的全局ticker */
|
||||||
static ticker: Ticker = new Ticker();
|
static ticker: Ticker = new Ticker();
|
||||||
/** 包括但不限于怪物、npc、自动元件的动画帧数 */
|
/** 包括但不限于怪物、npc、自动元件的动画帧数 */
|
||||||
static animatedFrame: number = 0;
|
static animatedFrame: number = 0;
|
||||||
|
/** ticker委托映射 */
|
||||||
|
static tickerMap: Map<number, TickerDelegation> = new Map();
|
||||||
|
/** ticker委托id */
|
||||||
|
static tickerId: number = 0;
|
||||||
|
|
||||||
zIndex: number = 0;
|
zIndex: number = 0;
|
||||||
|
|
||||||
@ -263,6 +293,32 @@ export abstract class RenderItem
|
|||||||
renderFrame.push(fn);
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 隐藏这个元素
|
* 隐藏这个元素
|
||||||
*/
|
*/
|
||||||
|
@ -37,7 +37,6 @@ export class FloorDamageExtends implements ILayerGroupRenderExtends {
|
|||||||
this.sprite.setMapSize(map.width, map.height);
|
this.sprite.setMapSize(map.width, map.height);
|
||||||
ensureFloorDamage(floor);
|
ensureFloorDamage(floor);
|
||||||
const enemy = core.status.maps[floor].enemy;
|
const enemy = core.status.maps[floor].enemy;
|
||||||
console.log(enemy);
|
|
||||||
|
|
||||||
this.sprite.updateCollection(enemy);
|
this.sprite.updateCollection(enemy);
|
||||||
}
|
}
|
||||||
@ -386,7 +385,7 @@ export class Damage extends Sprite {
|
|||||||
* @param camera 摄像机
|
* @param camera 摄像机
|
||||||
*/
|
*/
|
||||||
renderDamage(camera: Camera) {
|
renderDamage(camera: Camera) {
|
||||||
console.time('damage');
|
// console.time('damage');
|
||||||
const { ctx } = this.damageMap;
|
const { ctx } = this.damageMap;
|
||||||
ctx.save();
|
ctx.save();
|
||||||
transformCanvas(this.damageMap, camera, true);
|
transformCanvas(this.damageMap, camera, true);
|
||||||
@ -435,6 +434,6 @@ export class Damage extends Sprite {
|
|||||||
block.cache.set(v, temp.canvas);
|
block.cache.set(v, temp.canvas);
|
||||||
});
|
});
|
||||||
ctx.restore();
|
ctx.restore();
|
||||||
console.timeEnd('damage');
|
// console.timeEnd('damage');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,7 @@ hook.on('changingFloor', floor => {
|
|||||||
interface LayerGroupBinderEvent {
|
interface LayerGroupBinderEvent {
|
||||||
update: [floor: FloorIds];
|
update: [floor: FloorIds];
|
||||||
setBlock: [x: number, y: number, floor: FloorIds, block: AllNumbers];
|
setBlock: [x: number, y: number, floor: FloorIds, block: AllNumbers];
|
||||||
|
floorChange: [floor: FloorIds];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -86,7 +87,7 @@ export class LayerGroupFloorBinder
|
|||||||
* 在下一帧进行绑定数据更新
|
* 在下一帧进行绑定数据更新
|
||||||
*/
|
*/
|
||||||
updateBind() {
|
updateBind() {
|
||||||
if (this.needUpdate) return;
|
if (this.needUpdate || !this.group) return;
|
||||||
this.needUpdate = true;
|
this.needUpdate = true;
|
||||||
this.group.requestBeforeFrame(() => {
|
this.group.requestBeforeFrame(() => {
|
||||||
this.needUpdate = false;
|
this.needUpdate = false;
|
||||||
@ -182,6 +183,7 @@ export class LayerFloorBinder implements ILayerRenderExtends {
|
|||||||
bindThis() {
|
bindThis() {
|
||||||
this.floor = void 0;
|
this.floor = void 0;
|
||||||
this.bindThisFloor = true;
|
this.bindThisFloor = true;
|
||||||
|
this.updateBind();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -191,6 +193,7 @@ export class LayerFloorBinder implements ILayerRenderExtends {
|
|||||||
bindFloor(floorId: FloorIds) {
|
bindFloor(floorId: FloorIds) {
|
||||||
this.bindThisFloor = false;
|
this.bindThisFloor = false;
|
||||||
this.floor = floorId;
|
this.floor = floorId;
|
||||||
|
this.updateBind();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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();
|
||||||
|
});
|
||||||
|
@ -168,29 +168,6 @@ export class LayerGroup extends Container {
|
|||||||
this.cellSize = size;
|
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 摄像机
|
* @param camera 摄像机
|
||||||
@ -348,37 +286,6 @@ export class LayerGroup extends Container {
|
|||||||
this.needRender = void 0;
|
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');
|
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.
|
// todo: animate frame.
|
||||||
// renderEmits.on('animateFrame', () => {
|
// renderEmits.on('animateFrame', () => {
|
||||||
// LayerGroup.list.forEach(v => {
|
// LayerGroup.list.forEach(v => {
|
||||||
@ -662,10 +544,6 @@ export interface ILayerRenderExtends {
|
|||||||
autotiles: Record<number, number>
|
autotiles: Record<number, number>
|
||||||
): void;
|
): void;
|
||||||
|
|
||||||
// onDataBind?: (layer: Layer, floor: FloorIds, name?: FloorLayer) => void;
|
|
||||||
// onLayerBind?: (layer: Layer, name: FloorLayer) => void;
|
|
||||||
// onDataUpdate?: (layer: Layer, data: number[]) => void;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 当地图大小修改时执行的函数
|
* 当地图大小修改时执行的函数
|
||||||
* @param layer 目标Layer实例
|
* @param layer 目标Layer实例
|
||||||
@ -695,7 +573,7 @@ export interface ILayerRenderExtends {
|
|||||||
/**
|
/**
|
||||||
* 当更新移动层的渲染信息是执行的函数
|
* 当更新移动层的渲染信息是执行的函数
|
||||||
* @param layer 目标Layer实例
|
* @param layer 目标Layer实例
|
||||||
* @param renderable 移动层的渲染信息(包含大怪物)
|
* @param renderable 移动层的渲染信息(包含大怪物),未排序
|
||||||
*/
|
*/
|
||||||
onMovingUpdate?(layer: Layer, renderable: LayerMovingRenderable[]): void;
|
onMovingUpdate?(layer: Layer, renderable: LayerMovingRenderable[]): void;
|
||||||
|
|
||||||
@ -728,7 +606,7 @@ interface LayerCacheItem {
|
|||||||
canvas: HTMLCanvasElement;
|
canvas: HTMLCanvasElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface LayerMovingRenderable extends RenderableData {
|
export interface LayerMovingRenderable extends RenderableData {
|
||||||
zIndex: number;
|
zIndex: number;
|
||||||
x: number;
|
x: number;
|
||||||
y: number;
|
y: number;
|
||||||
@ -800,6 +678,7 @@ export class Layer extends Container {
|
|||||||
floorId?: FloorIds;
|
floorId?: FloorIds;
|
||||||
/** 渲染的层 */
|
/** 渲染的层 */
|
||||||
layer?: FloorLayer;
|
layer?: FloorLayer;
|
||||||
|
// todo: renderable分块存储,优化循环绘制性能
|
||||||
/** 渲染数据 */
|
/** 渲染数据 */
|
||||||
renderData: number[] = [];
|
renderData: number[] = [];
|
||||||
/** 自动元件的连接信息,键表示图块在渲染数据中的索引,值表示连接信息,是个8位二进制 */
|
/** 自动元件的连接信息,键表示图块在渲染数据中的索引,值表示连接信息,是个8位二进制 */
|
||||||
@ -823,6 +702,7 @@ export class Layer extends Container {
|
|||||||
moving: MovingBlock[] = [];
|
moving: MovingBlock[] = [];
|
||||||
/** 大怪物渲染信息 */
|
/** 大怪物渲染信息 */
|
||||||
bigImages: Map<number, LayerMovingRenderable> = new Map();
|
bigImages: Map<number, LayerMovingRenderable> = new Map();
|
||||||
|
// todo: 是否需要桶排?
|
||||||
/** 移动层的渲染信息 */
|
/** 移动层的渲染信息 */
|
||||||
movingRenderable: LayerMovingRenderable[] = [];
|
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,以保证渲染正常进行
|
* 设置地图大小,会清空渲染数据(且丢失引用),因此后面应当紧跟 putRenderData,以保证渲染正常进行
|
||||||
* @param width 地图宽度
|
* @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()) {
|
for (const ex of this.extend.values()) {
|
||||||
ex.onMovingUpdate?.(this, this.movingRenderable);
|
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.setTransform(1, 0, 0, 1, 0, 0);
|
||||||
ctx.translate(core._PX_ / 2, core._PY_ / 2);
|
ctx.translate(core._PX_ / 2, core._PY_ / 2);
|
||||||
ctx.transform(a, b, c, d, e, f);
|
ctx.transform(a, b, c, d, e, f);
|
||||||
const r =
|
const max1 = Math.max(a, b, c, d) ** 2;
|
||||||
Math.max(a, b, c, d) ** 2 * Math.max(core._PX_, core._PY_) * 2;
|
const max2 = Math.max(core._PX_, core._PY_) * 2;
|
||||||
|
const r = (max1 * max2) ** 2;
|
||||||
|
|
||||||
this.movingRenderable.forEach(v => {
|
this.movingRenderable.forEach(v => {
|
||||||
const { x, y, image, frame: blockFrame, render } = v;
|
const { x, y, image, frame: blockFrame, render, animate } = v;
|
||||||
const f = frame % 4;
|
const ff = frame % 4;
|
||||||
const i = frame === 4 && blockFrame === 3 ? 1 : f;
|
const i =
|
||||||
|
animate === -1
|
||||||
|
? frame === 4 && blockFrame === 3
|
||||||
|
? 1
|
||||||
|
: ff
|
||||||
|
: animate;
|
||||||
const [sx, sy, w, h] = render[i];
|
const [sx, sy, w, h] = render[i];
|
||||||
const px = x * cell - w / 2 + halfCell;
|
const px = x * cell - w / 2 + halfCell;
|
||||||
const py = y * cell - h + cell;
|
const py = y * cell - h + cell;
|
||||||
const ex = px + w;
|
const ex = px + w;
|
||||||
const ey = py + h;
|
const ey = py + h;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
(px - e) ** 2 > r ||
|
(px + e) ** 2 > r ||
|
||||||
(py - f) ** 2 > r ||
|
(py + f) ** 2 > r ||
|
||||||
(ex - e) ** 2 > r ||
|
(ex + e) ** 2 > r ||
|
||||||
(ey - f) ** 2 > r
|
(ey + f) ** 2 > r
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.drawImage(image, sx, sy, w, h, px, py, w, h);
|
ctx.drawImage(image, sx, sy, w, h, px, py, w, h);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1578,339 +1427,3 @@ export class Layer extends Container {
|
|||||||
super.destroy();
|
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();
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
@ -6,6 +6,7 @@ import { RenderItem, transformCanvas, withCacheRender } from './item';
|
|||||||
import { FloorLayer, Layer, LayerGroup } from './preset/layer';
|
import { FloorLayer, Layer, LayerGroup } from './preset/layer';
|
||||||
import { LayerGroupFloorBinder } from './preset/floor';
|
import { LayerGroupFloorBinder } from './preset/floor';
|
||||||
import { FloorDamageExtends } from './preset/damage';
|
import { FloorDamageExtends } from './preset/damage';
|
||||||
|
import { HeroRenderer } from './preset/hero';
|
||||||
|
|
||||||
export class MotaRenderer extends Container {
|
export class MotaRenderer extends Container {
|
||||||
static list: Set<MotaRenderer> = new Set();
|
static list: Set<MotaRenderer> = new Set();
|
||||||
@ -67,7 +68,7 @@ export class MotaRenderer extends Container {
|
|||||||
* 渲染游戏画面
|
* 渲染游戏画面
|
||||||
*/
|
*/
|
||||||
render() {
|
render() {
|
||||||
console.time();
|
// console.time();
|
||||||
const { canvas, ctx } = this.target;
|
const { canvas, ctx } = this.target;
|
||||||
const camera = this.camera;
|
const camera = this.camera;
|
||||||
this.emit('beforeRender');
|
this.emit('beforeRender');
|
||||||
@ -94,7 +95,7 @@ export class MotaRenderer extends Container {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
this.emit('afterRender');
|
this.emit('afterRender');
|
||||||
console.timeEnd();
|
// console.timeEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
update(item?: RenderItem) {
|
update(item?: RenderItem) {
|
||||||
@ -152,11 +153,20 @@ Mota.require('var', 'hook').once('reset', () => {
|
|||||||
|
|
||||||
const binder = new LayerGroupFloorBinder();
|
const binder = new LayerGroupFloorBinder();
|
||||||
const damage = new FloorDamageExtends();
|
const damage = new FloorDamageExtends();
|
||||||
|
const hero = new HeroRenderer();
|
||||||
layer.extends(binder);
|
layer.extends(binder);
|
||||||
layer.extends(damage);
|
layer.extends(damage);
|
||||||
|
layer.getLayer('event')?.extends(hero);
|
||||||
binder.bindThis();
|
binder.bindThis();
|
||||||
render.appendChild(layer);
|
render.appendChild(layer);
|
||||||
|
|
||||||
|
layer.requestAfterFrame(() => {
|
||||||
|
hero.setImage(core.material.images.images['hero2.png']);
|
||||||
|
});
|
||||||
|
layer.delegateTicker(() => {
|
||||||
|
hero.turn();
|
||||||
|
}, 10000);
|
||||||
|
|
||||||
camera.move(240, 240);
|
camera.move(240, 240);
|
||||||
render.update();
|
render.update();
|
||||||
|
|
||||||
|
@ -365,6 +365,7 @@ export class Hero<T extends object = IHeroStatusDefault>
|
|||||||
x: number;
|
x: number;
|
||||||
y: number;
|
y: number;
|
||||||
floorId: FloorIds;
|
floorId: FloorIds;
|
||||||
|
dir: Dir2;
|
||||||
|
|
||||||
readonly items: Map<AllIdsOf<'items'>, number> = new Map();
|
readonly items: Map<AllIdsOf<'items'>, number> = new Map();
|
||||||
readonly id: string;
|
readonly id: string;
|
||||||
@ -384,6 +385,7 @@ export class Hero<T extends object = IHeroStatusDefault>
|
|||||||
this.y = y;
|
this.y = y;
|
||||||
this.floorId = floorId;
|
this.floorId = floorId;
|
||||||
this.state = state;
|
this.state = state;
|
||||||
|
this.dir = 'down';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
9
src/types/control.d.ts
vendored
9
src/types/control.d.ts
vendored
@ -294,12 +294,14 @@ interface Control {
|
|||||||
setAutomaticRoute(destX: number, destY: number, stepPostfix: Loc[]): void;
|
setAutomaticRoute(destX: number, destY: number, stepPostfix: Loc[]): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @deprecated
|
||||||
* 连续行走
|
* 连续行走
|
||||||
* @param steps 压缩的步伐数组,每项表示朝某方向走多少步
|
* @param steps 压缩的步伐数组,每项表示朝某方向走多少步
|
||||||
*/
|
*/
|
||||||
setAutoHeroMove(steps: CompressedStep[]): void;
|
setAutoHeroMove(steps: CompressedStep[]): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @deprecated
|
||||||
* 设置行走的效果动画
|
* 设置行走的效果动画
|
||||||
*/
|
*/
|
||||||
setHeroMoveInterval(callback?: () => any): void;
|
setHeroMoveInterval(callback?: () => any): void;
|
||||||
@ -310,6 +312,7 @@ interface Control {
|
|||||||
moveOneStep(callback?: () => any): void;
|
moveOneStep(callback?: () => any): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @deprecated
|
||||||
* 尝试前进一步,如果面前不可被踏入就会直接触发该点事件
|
* 尝试前进一步,如果面前不可被踏入就会直接触发该点事件
|
||||||
* @example core.moveAction(core.doAction); // 尝试前进一步,然后继续事件处理
|
* @example core.moveAction(core.doAction); // 尝试前进一步,然后继续事件处理
|
||||||
* @param callback 走一步后的回调函数
|
* @param callback 走一步后的回调函数
|
||||||
@ -317,6 +320,7 @@ interface Control {
|
|||||||
moveAction(callback?: () => void): void;
|
moveAction(callback?: () => void): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @deprecated
|
||||||
* 连续前进,不撞南墙不回头
|
* 连续前进,不撞南墙不回头
|
||||||
* @example core.moveHero(); // 连续前进
|
* @example core.moveHero(); // 连续前进
|
||||||
* @param direction 移动的方向,不设置就是勇士当前的方向
|
* @param direction 移动的方向,不设置就是勇士当前的方向
|
||||||
@ -325,11 +329,13 @@ interface Control {
|
|||||||
moveHero(direction?: Dir, callback?: () => void): void;
|
moveHero(direction?: Dir, callback?: () => void): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @deprecated
|
||||||
* 当前是否正在移动
|
* 当前是否正在移动
|
||||||
*/
|
*/
|
||||||
isMoving(): boolean;
|
isMoving(): boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @deprecated
|
||||||
* 停止勇士的一切行动并等待勇士停下
|
* 停止勇士的一切行动并等待勇士停下
|
||||||
* @example core.waitHeroToStop(core.vibrate); // 等待勇士停下,然后视野左右抖动1秒
|
* @example core.waitHeroToStop(core.vibrate); // 等待勇士停下,然后视野左右抖动1秒
|
||||||
* @param callback 勇士停止后的回调函数
|
* @param callback 勇士停止后的回调函数
|
||||||
@ -337,6 +343,7 @@ interface Control {
|
|||||||
waitHeroToStop(callback?: () => void): void;
|
waitHeroToStop(callback?: () => void): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @deprecated
|
||||||
* 主角转向并计入录像,不会导致跟随者聚集,会导致视野重置到以主角为中心
|
* 主角转向并计入录像,不会导致跟随者聚集,会导致视野重置到以主角为中心
|
||||||
* @example core.turnHero(); // 主角顺时针旋转,即单击主角或按下Z键的效果
|
* @example core.turnHero(); // 主角顺时针旋转,即单击主角或按下Z键的效果
|
||||||
* @param direction 主角的新朝向,可为up, down, left, right, :left, :right, :back七种之一,不填视为:right
|
* @param direction 主角的新朝向,可为up, down, left, right, :left, :right, :back七种之一,不填视为:right
|
||||||
@ -360,6 +367,7 @@ interface Control {
|
|||||||
tryMoveDirectly(destX: number, destY: number): boolean;
|
tryMoveDirectly(destX: number, destY: number): boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @deprecated
|
||||||
* 绘制主角和跟随者并重置视野到以主角为中心
|
* 绘制主角和跟随者并重置视野到以主角为中心
|
||||||
* @example core.drawHero(); // 原地绘制主角的静止帧
|
* @example core.drawHero(); // 原地绘制主角的静止帧
|
||||||
* @param status 绘制状态
|
* @param status 绘制状态
|
||||||
@ -373,6 +381,7 @@ interface Control {
|
|||||||
): void;
|
): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @deprecated
|
||||||
* 改变勇士的不透明度
|
* 改变勇士的不透明度
|
||||||
* @param opacity 要设置成的不透明度
|
* @param opacity 要设置成的不透明度
|
||||||
* @param moveMode 动画的缓动模式
|
* @param moveMode 动画的缓动模式
|
||||||
|
Loading…
Reference in New Issue
Block a user