feat: 宝石血瓶显伤

This commit is contained in:
unanmed 2024-08-26 21:10:56 +08:00
parent 57760d501e
commit 1c369251b1
11 changed files with 218 additions and 144 deletions

View File

@ -69,6 +69,7 @@ import { Image, Text } from './render/preset/misc';
import { RenderItem } from './render/item'; import { RenderItem } from './render/item';
import { texture } from './render/cache'; import { texture } from './render/cache';
import { RenderAdapter } from './render/adapter'; import { RenderAdapter } from './render/adapter';
import { getMainRenderer } from './render';
// ----- 类注册 // ----- 类注册
Mota.register('class', 'AudioPlayer', AudioPlayer); Mota.register('class', 'AudioPlayer', AudioPlayer);
@ -148,6 +149,7 @@ Mota.register('module', 'Effect', {
}); });
Mota.register('module', 'Render', { Mota.register('module', 'Render', {
texture, texture,
getMainRenderer: getMainRenderer,
MotaRenderer, MotaRenderer,
Container, Container,
Sprite, Sprite,

42
src/core/render/index.ts Normal file
View File

@ -0,0 +1,42 @@
import { FloorItemDetail } from '@/plugin/fx/itemDetail';
import { FloorDamageExtends } from './preset/damage';
import { LayerGroupFloorBinder } from './preset/floor';
import { HeroRenderer } from './preset/hero';
import { LayerGroup, FloorLayer } from './preset/layer';
import { MotaRenderer } from './render';
let main: MotaRenderer;
export function getMainRenderer() {
return main;
}
Mota.require('var', 'loading').once('loaded', () => {
const render = new MotaRenderer();
main = render;
render.mount();
render.hide();
const layer = new LayerGroup();
['bg', 'bg2', 'event', 'fg', 'fg2'].forEach(v => {
layer.addLayer(v as FloorLayer);
});
const damage = new FloorDamageExtends();
const hero = new HeroRenderer();
const detail = new FloorItemDetail();
layer.extends(damage);
layer.extends(detail);
layer.getLayer('event')?.extends(hero);
render.appendChild(layer);
});
Mota.require('var', 'hook').on('reset', () => {
main.show();
});
Mota.require('var', 'hook').on('restart', () => {
main.hide();
});

View File

@ -40,7 +40,7 @@ export class FloorDamageExtends
* *
*/ */
update(floor: FloorIds) { update(floor: FloorIds) {
if (!this.sprite) return; if (!this.sprite || !floor) return;
const map = core.status.maps[floor]; const map = core.status.maps[floor];
this.sprite.setMapSize(map.width, map.height); this.sprite.setMapSize(map.width, map.height);
ensureFloorDamage(floor); ensureFloorDamage(floor);
@ -89,7 +89,6 @@ export class FloorDamageExtends
} }
awake(group: LayerGroup): void { awake(group: LayerGroup): void {
group.requestBeforeFrame(() => {
const ex = group.getExtends('floor-binder'); const ex = group.getExtends('floor-binder');
if (ex instanceof LayerGroupFloorBinder) { if (ex instanceof LayerGroupFloorBinder) {
this.floorBinder = ex; this.floorBinder = ex;
@ -103,7 +102,6 @@ export class FloorDamageExtends
); );
group.removeExtends('floor-damage'); group.removeExtends('floor-damage');
} }
});
} }
onDestroy(group: LayerGroup): void { onDestroy(group: LayerGroup): void {
@ -132,6 +130,7 @@ interface DamageCache {
interface EDamageEvent extends ESpriteEvent { interface EDamageEvent extends ESpriteEvent {
setMapSize: [width: number, height: number]; setMapSize: [width: number, height: number];
beforeDamageRender: [need: Set<number>, transform: Transform]; beforeDamageRender: [need: Set<number>, transform: Transform];
updateBlocks: [blocks: Set<number>];
} }
export class Damage extends Sprite<EDamageEvent> { export class Damage extends Sprite<EDamageEvent> {
@ -241,13 +240,16 @@ export class Damage extends Sprite<EDamageEvent> {
/** /**
* *
* @param blocks * @param blocks
* @param map
*/ */
updateBlocks(blocks?: Set<number>) { updateBlocks(blocks?: Set<number>, map: boolean = true) {
if (blocks) { if (blocks) {
blocks.forEach(v => this.updateBlock(v)); blocks.forEach(v => this.updateBlock(v, map));
this.emit('updateBlocks', blocks);
} else { } else {
this.blockData.forEach((_, k) => this.updateBlock(k, false)); this.blockData.forEach((_, k) => this.updateBlock(k, false));
this.extractAllMapDamage(); if (map) this.extractAllMapDamage();
this.emit('updateBlocks', new Set(this.blockData.keys()));
} }
this.update(this); this.update(this);
} }
@ -274,7 +276,9 @@ export class Damage extends Sprite<EDamageEvent> {
this.needUpdateBlocks.add(block); this.needUpdateBlocks.add(block);
this.requestBeforeFrame(() => { this.requestBeforeFrame(() => {
this.needUpdateBlock = false; this.needUpdateBlock = false;
this.needUpdateBlocks.forEach(v => this.updateBlock(v, false)); this.updateBlocks(this.needUpdateBlocks, false);
this.needUpdateBlocks.clear();
// this.needUpdateBlocks.forEach(v => this.updateBlock(v, false));
// todo: 阻击夹域等地图伤害检测是否必要更新,例如不包含阻击夹域的怪就不必要更新这个怪物信息 // todo: 阻击夹域等地图伤害检测是否必要更新,例如不包含阻击夹域的怪就不必要更新这个怪物信息
// this.extractAllMapDamage(); // this.extractAllMapDamage();
}); });
@ -423,6 +427,7 @@ export class Damage extends Sprite<EDamageEvent> {
ctx.save(); ctx.save();
this.damageMap.clear(); this.damageMap.clear();
transformCanvas(this.damageMap, transform); transformCanvas(this.damageMap, transform);
// console.trace();
const { res: render } = this.calNeedRender(transform); const { res: render } = this.calNeedRender(transform);
const block = this.block; const block = this.block;

View File

@ -149,9 +149,9 @@ export class LayerGroupFloorBinder
LayerGroupFloorBinder.activedBinder.add(this); LayerGroupFloorBinder.activedBinder.add(this);
} }
onLayerAdd(group: LayerGroup, layer: Layer): void { // onLayerAdd(group: LayerGroup, layer: Layer): void {
this.checkLayerExtends(layer); // this.checkLayerExtends(layer);
} // }
onDestroy(group: LayerGroup) { onDestroy(group: LayerGroup) {
LayerGroupFloorBinder.activedBinder.delete(this); LayerGroupFloorBinder.activedBinder.delete(this);

View File

@ -145,7 +145,10 @@ export class HeroRenderer
*/ */
private moveTick(time: number) { private moveTick(time: number) {
if (this.status !== 'moving') return; if (this.status !== 'moving') return;
if (!this.renderable) return; if (!this.renderable) {
this.emit('stepEnd');
return;
}
if (time - this.lastFrameTime > this.speed) { if (time - this.lastFrameTime > this.speed) {
this.lastFrameTime = time; this.lastFrameTime = time;
@ -379,6 +382,10 @@ adapter.recieve('turn', (item, dir: Dir2) => {
item.turn(dir); item.turn(dir);
return Promise.resolve(); return Promise.resolve();
}); });
adapter.recieve('setImage', (item, image: SizedCanvasImageSource) => {
item.setImage(image);
return Promise.resolve();
});
// 同步fallback用于适配现在的样板之后会删除 // 同步fallback用于适配现在的样板之后会删除
adapter.recieveSync('setHeroLoc', (item, x?: number, y?: number) => { adapter.recieveSync('setHeroLoc', (item, x?: number, y?: number) => {
item.setHeroLoc(x, y); item.setHeroLoc(x, y);

View File

@ -8,6 +8,7 @@ import { RenderableData, texture } from '../cache';
import { glMatrix } from 'gl-matrix'; import { glMatrix } from 'gl-matrix';
import { BlockCacher } from './block'; import { BlockCacher } from './block';
import { Transform } from '../transform'; import { Transform } from '../transform';
import { LayerFloorBinder, LayerGroupFloorBinder } from './floor';
export interface ILayerGroupRenderExtends { export interface ILayerGroupRenderExtends {
/** 拓展的唯一标识符 */ /** 拓展的唯一标识符 */
@ -60,6 +61,18 @@ export interface ILayerGroupRenderExtends {
*/ */
onFrameUpdate?(group: LayerGroup, frame: number): void; onFrameUpdate?(group: LayerGroup, frame: number): void;
/**
*
* @param group LayerGroup实例
*/
onBeforeRender?(group: LayerGroup): void;
/**
*
* @param group LayerGroup实例
*/
onAfterRender?(group: LayerGroup): void;
/** /**
* LayerGroup被销毁 * LayerGroup被销毁
* @param group LayerGroup实例 * @param group LayerGroup实例
@ -100,6 +113,10 @@ export class LayerGroup extends Container implements IAnimateFrame {
}); });
renderEmits.addFramer(this); renderEmits.addFramer(this);
const binder = new LayerGroupFloorBinder();
this.extends(binder);
binder.bindThis();
} }
/** /**
@ -675,6 +692,8 @@ export class Layer extends Container {
ctx.drawImage(this.staticMap.canvas, 0, 0, width, height); ctx.drawImage(this.staticMap.canvas, 0, 0, width, height);
ctx.drawImage(this.movingMap.canvas, 0, 0, width, height); ctx.drawImage(this.movingMap.canvas, 0, 0, width, height);
}); });
this.extends(new LayerFloorBinder());
} }
/** /**

View File

@ -1,14 +1,7 @@
import { Animation, hyper, linear, sleep } from 'mutate-animate'; import { MotaCanvas2D } from '../fx/canvas2d';
import { MotaCanvas2D, MotaOffscreenCanvas2D } from '../fx/canvas2d';
import { Container } from './container'; import { Container } from './container';
import { RenderItem, transformCanvas } from './item'; import { RenderItem } from './item';
import { FloorLayer, Layer, LayerGroup } from './preset/layer';
import { LayerGroupFloorBinder } from './preset/floor';
import { FloorDamageExtends } from './preset/damage';
import { HeroRenderer } from './preset/hero';
import { Transform } from './transform'; import { Transform } from './transform';
import { Text } from './preset/misc';
import { Shader } from './shader';
export class MotaRenderer extends Container { export class MotaRenderer extends Container {
static list: Set<MotaRenderer> = new Set(); static list: Set<MotaRenderer> = new Set();
@ -69,65 +62,3 @@ export class MotaRenderer extends Container {
window.addEventListener('resize', () => { window.addEventListener('resize', () => {
MotaRenderer.list.forEach(v => v.update(v)); MotaRenderer.list.forEach(v => v.update(v));
}); });
Mota.require('var', 'hook').once('reset', () => {
const render = new MotaRenderer();
const transform = render.transform;
render.mount();
const ani = new Animation();
const shader = new Shader();
const layer = new LayerGroup();
['bg', 'bg2', 'event', 'fg', 'fg2'].forEach(v => {
layer.addLayer(v as FloorLayer);
});
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(shader);
shader.appendChild(layer);
shader.size(480, 480);
shader.fs(`
uniform float u_offset;
void main() {
// 计算当前像素到爆炸中心的距离
vec2 coordToCenter = v_texCoord - vec2(0.5, 0.5);
float distance = length(coordToCenter);
// 根据时间计算当前距离上的波动相位
float wavePhase = 100.0 * (distance - u_offset);
// 计算波动的强度(正弦波)并结合衰减
float wave = sin(wavePhase) * 0.02 / (1.0 + 10.0 * distance);
// 将波动效果应用到纹理坐标上,造成扭曲
vec2 warpedCoords = v_texCoord + normalize(coordToCenter) * wave;
// 采样纹理并输出颜色
gl_FragColor = texture2D(u_sampler, warpedCoords);
}
`);
shader.compileShader();
const offset = shader.getUniform('u_offset');
shader.delegateTicker(() => {
shader.gl.uniform1f(offset, ani.value.offset);
shader.update();
}, 20000);
ani.value.offset = 0;
ani.mode(linear()).time(10000).absolute().apply('offset', 1);
layer.requestAfterFrame(() => {
hero.setImage(core.material.images.images['hero2.png']);
});
console.log(render);
});

View File

@ -31,7 +31,6 @@ import type { texture } from '@/core/render/cache';
import type { MotaRenderer } from '@/core/render/render'; import type { MotaRenderer } from '@/core/render/render';
import type { Container } from '@/core/render/container'; import type { Container } from '@/core/render/container';
import type { Sprite } from '@/core/render/sprite'; import type { Sprite } from '@/core/render/sprite';
import type { Camera } from '@/core/render/camera';
import type { Image, Text } from '@/core/render/preset/misc'; import type { Image, Text } from '@/core/render/preset/misc';
import type { RenderItem } from '@/core/render/item'; import type { RenderItem } from '@/core/render/item';
import type { RenderAdapter } from '@/core/render/adapter'; import type { RenderAdapter } from '@/core/render/adapter';
@ -107,10 +106,10 @@ interface ModuleInterface {
}; };
Render: { Render: {
texture: typeof texture; texture: typeof texture;
main: MotaRenderer;
MotaRenderer: typeof MotaRenderer; MotaRenderer: typeof MotaRenderer;
Container: typeof Container; Container: typeof Container;
Sprite: typeof Sprite; Sprite: typeof Sprite;
Camera: typeof Camera;
Text: typeof Text; Text: typeof Text;
Image: typeof Image; Image: typeof Image;
RenderItem: typeof RenderItem; RenderItem: typeof RenderItem;

View File

@ -10,7 +10,6 @@ import {
ILayerGroupRenderExtends, ILayerGroupRenderExtends,
LayerGroup LayerGroup
} from '@/core/render/preset/layer'; } from '@/core/render/preset/layer';
import { cloneDeep } from 'lodash-es';
interface ItemDetailData { interface ItemDetailData {
x: number; x: number;
@ -25,8 +24,13 @@ interface ItemData {
} }
const ItemState = Mota.require('module', 'State').ItemState; const ItemState = Mota.require('module', 'State').ItemState;
Mota.require('var', 'hook').on('setBlock', (x, y, floorId, block) => {
FloorItemDetail.listened.forEach(v => {
v.setBlock(block, x, y);
});
});
class FloorItemDetail implements ILayerGroupRenderExtends { export class FloorItemDetail implements ILayerGroupRenderExtends {
id: string = 'item-detail'; id: string = 'item-detail';
group!: LayerGroup; group!: LayerGroup;
@ -35,7 +39,12 @@ class FloorItemDetail implements ILayerGroupRenderExtends {
sprite!: Damage; sprite!: Damage;
/** 每个分块中包含的物品信息 */ /** 每个分块中包含的物品信息 */
blockData: Map<number, Set<ItemData>> = new Map(); blockData: Map<number, Map<number, ItemData>> = new Map();
/** 需要更新的分块 */
private dirtyBlock: Set<number> = new Set();
/** 道具详细信息 */
private detailData: Map<number, Map<number, ItemDetailData>> = new Map();
static detailColor: Record<string, CanvasStyle> = { static detailColor: Record<string, CanvasStyle> = {
atk: '#FF7A7A', atk: '#FF7A7A',
@ -51,8 +60,15 @@ class FloorItemDetail implements ILayerGroupRenderExtends {
manaper: '#c66' manaper: '#c66'
}; };
private onBeforeRender = (need: Set<number>) => { static listened: Set<FloorItemDetail> = new Set();
private onBeforeDamageRender = (need: Set<number>) => {
if (!mainSetting.getValue('screen.itemDetail')) return; if (!mainSetting.getValue('screen.itemDetail')) return;
need.forEach(v => {
if (this.dirtyBlock.has(v)) {
this.sprite.block.clearCache(v, 1);
}
});
this.render(need); this.render(need);
}; };
@ -60,9 +76,21 @@ class FloorItemDetail implements ILayerGroupRenderExtends {
this.updateMapSize(width, height); this.updateMapSize(width, height);
}; };
private onUpdate = () => {
this.updateItems();
};
private onUpdateBlocks = (blocks: Set<number>) => {
blocks.forEach(v => {
this.dirtyBlock.add(v);
});
};
private listen() { private listen() {
this.sprite.on('beforeDamageRender', this.onBeforeRender); this.sprite.on('beforeDamageRender', this.onBeforeDamageRender);
this.sprite.on('setMapSize', this.onUpdateMapSize); this.sprite.on('setMapSize', this.onUpdateMapSize);
this.sprite.on('updateBlocks', this.onUpdateBlocks);
this.damage.on('update', this.onUpdate);
} }
/** /**
@ -72,9 +100,12 @@ class FloorItemDetail implements ILayerGroupRenderExtends {
this.blockData.clear(); this.blockData.clear();
// 预留blockData // 预留blockData
const num = width * height; const [x, y] = this.sprite.block.getBlockXY(width, height);
const num = x * y;
for (let i = 0; i < num; i++) { for (let i = 0; i < num; i++) {
this.blockData.set(i, new Set()); this.blockData.set(i, new Map());
this.detailData.set(i, new Map());
this.dirtyBlock.add(i);
} }
} }
@ -83,6 +114,7 @@ class FloorItemDetail implements ILayerGroupRenderExtends {
*/ */
updateItems() { updateItems() {
const floor = this.floorBinder.getFloor(); const floor = this.floorBinder.getFloor();
if (!floor) return;
core.extractBlocks(floor); core.extractBlocks(floor);
core.status.maps[floor].blocks.forEach(v => { core.status.maps[floor].blocks.forEach(v => {
@ -93,18 +125,46 @@ class FloorItemDetail implements ILayerGroupRenderExtends {
const x = v.x; const x = v.x;
const y = v.y; const y = v.y;
const block = this.sprite.block.getIndexByLoc(x, y); const block = this.sprite.block.getIndexByLoc(x, y);
this.blockData.get(block)?.add({ x, y, id }); const index = x + y * this.sprite.mapWidth;
const blockData = this.blockData.get(block);
blockData?.set(index, { x, y, id });
}); });
} }
/**
*
* @param block
* @param x
* @param y
*/
setBlock(block: AllNumbers, x: number, y: number) {
const map = maps_90f36752_8815_4be8_b32b_d7fad1d0542e;
const index = this.sprite.block.getIndexByLoc(x, y);
const itemIndex = x + y * this.sprite.mapWidth;
const blockData = this.blockData.get(index);
this.dirtyBlock.add(index);
if (block === 0) {
blockData?.delete(itemIndex);
return;
}
const cls = map[block].cls;
if (cls !== 'items') {
blockData?.delete(itemIndex);
return;
}
const id = map[block].id;
blockData?.set(itemIndex, { x, y, id });
}
/** /**
* *
* @param block * @param block
*/ */
calAllItems(block: Set<number>) { calAllItems(block: Set<number>) {
if (this.dirtyBlock.size === 0 || block.size === 0) return;
let diff: Record<string | symbol, number | undefined> = {}; let diff: Record<string | symbol, number | undefined> = {};
const before = core.status.hero; const before = core.status.hero;
const hero = cloneDeep(core.status.hero); const hero = structuredClone(core.status.hero);
const handler: ProxyHandler<any> = { const handler: ProxyHandler<any> = {
set(target, key, v) { set(target, key, v) {
diff[key] = v - (target[key] || 0); diff[key] = v - (target[key] || 0);
@ -114,14 +174,16 @@ class FloorItemDetail implements ILayerGroupRenderExtends {
}; };
core.status.hero = new Proxy(hero, handler); core.status.hero = new Proxy(hero, handler);
const res: Map<number, Set<ItemDetailData>> = new Map();
core.setFlag('__statistics__', true); core.setFlag('__statistics__', true);
block.forEach(v => { block.forEach(v => {
if (!this.dirtyBlock.has(v)) return;
const data = this.blockData.get(v); const data = this.blockData.get(v);
const detail = this.detailData.get(v);
detail?.clear();
if (!data) return; if (!data) return;
const info: Set<ItemDetailData> = new Set();
data.forEach(v => { data.forEach(v => {
const { x, y, id } = v; const { id, x, y } = v;
const index = x + y * this.sprite.mapWidth;
diff = {}; diff = {};
const item = core.material.items[id]; const item = core.material.items[id];
@ -135,21 +197,18 @@ class FloorItemDetail implements ILayerGroupRenderExtends {
const n = name as SelectKey<HeroStatus, number>; const n = name as SelectKey<HeroStatus, number>;
diff[name + 'per'] = per[n].toString() + '%'; diff[name + 'per'] = per[n].toString() + '%';
} }
info.add({ x, y, diff }); detail?.set(index, { x, y, diff });
return; return;
} }
// @ts-ignore // @ts-ignore
ItemState.item(id)?.itemEffectFn(); ItemState.item(id)?.itemEffectFn();
info.add({ x, y, diff }); detail?.set(index, { x, y, diff });
}); });
res.set(v, info);
}); });
core.status.hero = before; core.status.hero = before;
window.hero = before; window.hero = before;
window.flags = before.flags; window.flags = before.flags;
return res;
} }
/** /**
@ -157,8 +216,13 @@ class FloorItemDetail implements ILayerGroupRenderExtends {
* @param block * @param block
*/ */
render(block: Set<number>) { render(block: Set<number>) {
const data = this.calAllItems(block); this.calAllItems(block);
data.forEach((info, k) => { const data = this.detailData;
block.forEach(v => {
if (!this.dirtyBlock.has(v)) return;
this.dirtyBlock.delete(v);
const info = data.get(v);
if (!info) return;
info.forEach(({ x, y, diff }) => { info.forEach(({ x, y, diff }) => {
let n = 0; let n = 0;
for (const [key, value] of Object.entries(diff)) { for (const [key, value] of Object.entries(diff)) {
@ -173,7 +237,7 @@ class FloorItemDetail implements ILayerGroupRenderExtends {
align: 'left', align: 'left',
baseline: 'alphabetic' baseline: 'alphabetic'
}; };
this.sprite.renderable.get(k)?.add(renderable); this.sprite.renderable.get(v)?.add(renderable);
n++; n++;
} }
}); });
@ -181,8 +245,10 @@ class FloorItemDetail implements ILayerGroupRenderExtends {
} }
awake(group: LayerGroup): void { awake(group: LayerGroup): void {
console.log(this);
this.group = group; this.group = group;
group.requestBeforeFrame(() => {
const binder = group.getExtends('floor-binder'); const binder = group.getExtends('floor-binder');
const damage = group.getExtends('floor-damage'); const damage = group.getExtends('floor-damage');
if ( if (
@ -191,10 +257,9 @@ class FloorItemDetail implements ILayerGroupRenderExtends {
) { ) {
this.floorBinder = binder; this.floorBinder = binder;
this.damage = damage; this.damage = damage;
group.requestAfterFrame(() => {
this.sprite = damage.sprite; this.sprite = damage.sprite;
this.listen(); this.listen();
}); FloorItemDetail.listened.add(this);
} else { } else {
logger.warn( logger.warn(
1001, 1001,
@ -202,11 +267,11 @@ class FloorItemDetail implements ILayerGroupRenderExtends {
); );
group.removeExtends('item-detail'); group.removeExtends('item-detail');
} }
});
} }
onDestroy(group: LayerGroup): void { onDestroy(group: LayerGroup): void {
this.sprite.off('beforeDamageRender', this.onBeforeRender); this.sprite.off('beforeDamageRender', this.onBeforeDamageRender);
this.sprite.off('setMapSize', this.onUpdateMapSize); this.sprite.off('setMapSize', this.onUpdateMapSize);
FloorItemDetail.listened.delete(this);
} }
} }

View File

@ -267,9 +267,12 @@ export function init() {
stopChian = true; stopChian = true;
callback?.(); callback?.();
};
// if (callback) return this.moveAction(callback); events.prototype.setHeroIcon = function (name: ImageIds) {
// this._moveHero_moving(); const img = core.material.images.images[name];
if (!img) return;
adapters['hero-adapter']?.all('setImage', img);
}; };
hook.on('reset', () => { hook.on('reset', () => {

View File

@ -312,7 +312,8 @@ interface Control {
moveOneStep(callback?: () => any): void; moveOneStep(callback?: () => any): void;
/** /**
* @deprecated * @deprecated 使使使 `eventMoveHero`
* 使 `moveHero`
* *
* @example core.moveAction(core.doAction); // 尝试前进一步,然后继续事件处理 * @example core.moveAction(core.doAction); // 尝试前进一步,然后继续事件处理
* @param callback * @param callback
@ -320,8 +321,8 @@ interface Control {
moveAction(callback?: () => void): void; moveAction(callback?: () => void): void;
/** /**
* @deprecated * @deprecated 使使使 `eventMoveHero`
* *
* @example core.moveHero(); // 连续前进 * @example core.moveHero(); // 连续前进
* @param direction * @param direction
* @param callback * @param callback