mirror of
https://github.com/unanmed/HumanBreak.git
synced 2025-01-19 12:49:25 +08:00
feat: 图块移动
This commit is contained in:
parent
e0b54ecabb
commit
fdc08406b9
@ -1,175 +0,0 @@
|
|||||||
import { Ticker } from 'mutate-animate';
|
|
||||||
import { MotaCanvas2D } from '../fx/canvas2d';
|
|
||||||
import { EventEmitter } from '../common/eventEmitter';
|
|
||||||
import { debounce } from 'lodash-es';
|
|
||||||
|
|
||||||
// 重写样板的勇士绘制
|
|
||||||
|
|
||||||
const canvas = MotaCanvas2D.for('@hero', false);
|
|
||||||
|
|
||||||
Mota.require('var', 'loading').once('coreInit', () => {
|
|
||||||
canvas.withGameScale(true);
|
|
||||||
canvas.setHD(false);
|
|
||||||
canvas.size(480, 480);
|
|
||||||
canvas.pos(0, 0);
|
|
||||||
canvas.css(`z-index: 40`);
|
|
||||||
canvas.canvas.classList.add('no-anti-aliasing');
|
|
||||||
canvas.setTarget(core.dom.gameDraw);
|
|
||||||
canvas.mount();
|
|
||||||
});
|
|
||||||
|
|
||||||
const DIR_INDEX: Record<Dir, number> = {
|
|
||||||
down: 0,
|
|
||||||
left: 1,
|
|
||||||
right: 2,
|
|
||||||
up: 3
|
|
||||||
};
|
|
||||||
|
|
||||||
interface HeroDrawItem {
|
|
||||||
id: AllIds | 'hero';
|
|
||||||
image: HTMLCanvasElement;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface HeroDrawing extends HeroDrawItem {
|
|
||||||
x: number;
|
|
||||||
y: number;
|
|
||||||
dir: Dir;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface HeroRendererEvent {
|
|
||||||
beforeDraw: () => void;
|
|
||||||
afterDraw: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const resetMoving = debounce((render: HeroRenderer) => {
|
|
||||||
render.moving = 0;
|
|
||||||
}, 500);
|
|
||||||
|
|
||||||
export class HeroRenderer extends EventEmitter<HeroRendererEvent> {
|
|
||||||
followers: HeroDrawItem[] = [];
|
|
||||||
hero!: HeroDrawItem;
|
|
||||||
|
|
||||||
/** 移动状态 */
|
|
||||||
moving: number = 0;
|
|
||||||
/** 移动过程中的偏移 */
|
|
||||||
offset: number = 0;
|
|
||||||
/** 勇士绘制时的alpha通道 */
|
|
||||||
alpha: number = 1;
|
|
||||||
|
|
||||||
ticker: Ticker = new Ticker();
|
|
||||||
|
|
||||||
/** 是否是后退状态 */
|
|
||||||
private back: boolean = false;
|
|
||||||
/** 是否正在移动 */
|
|
||||||
private isMoving: boolean = false;
|
|
||||||
/** 上一次换腿(?的时间 */
|
|
||||||
private lastMoving: number = 0;
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
|
|
||||||
Mota.require('var', 'loading').once('coreInit', () => {
|
|
||||||
this.ticker.add(time => {
|
|
||||||
if (core.status.heroMoving < 0 || !this.isMoving) return;
|
|
||||||
if (time - this.lastMoving > core.values.moveSpeed) {
|
|
||||||
this.lastMoving = time;
|
|
||||||
this.moving++;
|
|
||||||
this.moving %= 4;
|
|
||||||
}
|
|
||||||
this.draw();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置勇士绘制信息
|
|
||||||
*/
|
|
||||||
setHero() {
|
|
||||||
const image = core.material.images.hero;
|
|
||||||
const canvas = new MotaCanvas2D();
|
|
||||||
canvas.setHD(false);
|
|
||||||
canvas.size(image.width, image.height);
|
|
||||||
canvas.ctx.drawImage(image, 0, 0);
|
|
||||||
this.hero = {
|
|
||||||
id: 'hero',
|
|
||||||
image: canvas.canvas
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 执行绘制
|
|
||||||
*/
|
|
||||||
draw() {
|
|
||||||
if (!core.isPlaying()) return;
|
|
||||||
return;
|
|
||||||
const { ctx, canvas: can } = canvas;
|
|
||||||
const { x, y, direction: dir } = core.status.hero.loc;
|
|
||||||
ctx.clearRect(0, 0, can.width, can.height);
|
|
||||||
ctx.globalAlpha = this.alpha;
|
|
||||||
|
|
||||||
this.emit('beforeDraw');
|
|
||||||
|
|
||||||
const data: HeroDrawing[] = [];
|
|
||||||
data.push({ ...this.hero, x, y, dir });
|
|
||||||
core.status.hero.followers.forEach((v, i) => {
|
|
||||||
const { id, image } = this.followers[i];
|
|
||||||
data.push({ x: v.x, y: v.y, dir: v.direction, id, image });
|
|
||||||
});
|
|
||||||
|
|
||||||
const { offsetX: ox, offsetY: oy } = core.bigmap;
|
|
||||||
const sgn = this.back ? -1 : 1;
|
|
||||||
const offset = this.offset * sgn;
|
|
||||||
|
|
||||||
const frame = this.isMoving ? this.moving % 4 : 0;
|
|
||||||
|
|
||||||
data.forEach(v => {
|
|
||||||
// hero offset x
|
|
||||||
const hox = offset * core.utils.scan[v.dir].x;
|
|
||||||
const hoy = offset * core.utils.scan[v.dir].y;
|
|
||||||
const cx = v.x * 32 + 16 - ox + hox;
|
|
||||||
const cy = v.y * 32 + 16 - oy + hoy;
|
|
||||||
const { width, height } = v.image;
|
|
||||||
const pw = width / 4;
|
|
||||||
const ph = height / 4;
|
|
||||||
const line = DIR_INDEX[v.dir];
|
|
||||||
|
|
||||||
const px = cx - pw / 2;
|
|
||||||
const py = cy - ph + 16;
|
|
||||||
|
|
||||||
const sx = frame * pw;
|
|
||||||
const sy = line * ph;
|
|
||||||
|
|
||||||
ctx.drawImage(v.image, sx, sy, pw, ph, px, py, pw, ph);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.emit('afterDraw');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置绘制状态为正在移动还是静止
|
|
||||||
*/
|
|
||||||
move(moving: boolean) {
|
|
||||||
this.isMoving = moving;
|
|
||||||
if (!moving) {
|
|
||||||
resetMoving(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置当前是否为后退状态
|
|
||||||
*/
|
|
||||||
backward(back: boolean) {
|
|
||||||
this.back = back;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置绘制时的alpha
|
|
||||||
*/
|
|
||||||
setAlpha(alpha: number) {
|
|
||||||
this.alpha = alpha;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const render = new HeroRenderer();
|
|
||||||
|
|
||||||
export { render as heroRender };
|
|
@ -233,12 +233,13 @@ export class HeroRenderer
|
|||||||
* 否则可能会导致移动过程中与大怪物的层级关系不正确,比如全在大怪物身后。注意不建议频繁改动这个值,
|
* 否则可能会导致移动过程中与大怪物的层级关系不正确,比如全在大怪物身后。注意不建议频繁改动这个值,
|
||||||
* 因为此举会导致层级的重新排序,降低渲染性能。
|
* 因为此举会导致层级的重新排序,降低渲染性能。
|
||||||
*/
|
*/
|
||||||
moveAs(x: number, y: number, time: number, fn: TimingFn<3>) {
|
moveAs(x: number, y: number, time: number, fn: TimingFn<3>): Promise<void> {
|
||||||
if (this.status !== 'stop') return;
|
if (this.status !== 'stop') return Promise.reject();
|
||||||
if (!this.renderable) return;
|
if (!this.renderable) return Promise.reject();
|
||||||
this.status = 'moving-as';
|
this.status = 'moving-as';
|
||||||
let nowZIndex = fn(0)[2];
|
let nowZIndex = fn(0)[2];
|
||||||
let startTime = Date.now();
|
let startTime = Date.now();
|
||||||
|
return new Promise(res => {
|
||||||
this.layer.delegateTicker(
|
this.layer.delegateTicker(
|
||||||
() => {
|
() => {
|
||||||
if (!this.renderable) return;
|
if (!this.renderable) return;
|
||||||
@ -258,13 +259,15 @@ export class HeroRenderer
|
|||||||
time,
|
time,
|
||||||
() => {
|
() => {
|
||||||
this.status = 'stop';
|
this.status = 'stop';
|
||||||
if (!this.renderable) return;
|
if (!this.renderable) return res();
|
||||||
this.renderable.animate = 0;
|
this.renderable.animate = 0;
|
||||||
this.renderable.x = x;
|
this.renderable.x = x;
|
||||||
this.renderable.y = y;
|
this.renderable.y = y;
|
||||||
this.layer.update(this.layer);
|
this.layer.update(this.layer);
|
||||||
|
res();
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -309,3 +312,9 @@ adapter.recieve('move', (item, dir: Dir) => {
|
|||||||
adapter.recieve('endMove', item => {
|
adapter.recieve('endMove', item => {
|
||||||
return item.endMove();
|
return item.endMove();
|
||||||
});
|
});
|
||||||
|
adapter.recieve(
|
||||||
|
'moveAs',
|
||||||
|
(item, x: number, y: number, time: number, fn: TimingFn<3>) => {
|
||||||
|
return item.moveAs(x, y, time, fn);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
@ -639,12 +639,7 @@ interface MovingStepFunction {
|
|||||||
relative?: boolean;
|
relative?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
type MovingStep = MovingStepFunction | MovingStepLinearSwap;
|
|
||||||
|
|
||||||
interface MovingBlock {
|
interface MovingBlock {
|
||||||
steps: MovingStep[];
|
|
||||||
/** 当前正在执行哪一步 */
|
|
||||||
index: number;
|
|
||||||
/** 当前横坐标 */
|
/** 当前横坐标 */
|
||||||
x: number;
|
x: number;
|
||||||
/** 当前纵坐标 */
|
/** 当前纵坐标 */
|
||||||
@ -695,8 +690,6 @@ export class Layer extends Container {
|
|||||||
/** 分块信息 */
|
/** 分块信息 */
|
||||||
block: BlockCacher<LayerCacheItem> = new BlockCacher(0, 0, core._WIDTH_, 4);
|
block: BlockCacher<LayerCacheItem> = new BlockCacher(0, 0, core._WIDTH_, 4);
|
||||||
|
|
||||||
/** 正在移动的图块 */
|
|
||||||
moving: MovingBlock[] = [];
|
|
||||||
/** 大怪物渲染信息 */
|
/** 大怪物渲染信息 */
|
||||||
bigImages: Map<number, LayerMovingRenderable> = new Map();
|
bigImages: Map<number, LayerMovingRenderable> = new Map();
|
||||||
// todo: 是否需要桶排?
|
// todo: 是否需要桶排?
|
||||||
@ -706,6 +699,8 @@ export class Layer extends Container {
|
|||||||
needUpdateMoving: boolean = false;
|
needUpdateMoving: boolean = false;
|
||||||
|
|
||||||
private extend: Map<string, ILayerRenderExtends> = new Map();
|
private extend: Map<string, ILayerRenderExtends> = new Map();
|
||||||
|
/** 正在移动的图块的渲染信息 */
|
||||||
|
private moving: Set<LayerMovingRenderable> = new Set();
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super('absolute', false);
|
super('absolute', false);
|
||||||
@ -1142,25 +1137,7 @@ export class Layer extends Container {
|
|||||||
updateMovingRenderable() {
|
updateMovingRenderable() {
|
||||||
this.movingRenderable = [];
|
this.movingRenderable = [];
|
||||||
this.movingRenderable.push(...this.bigImages.values());
|
this.movingRenderable.push(...this.bigImages.values());
|
||||||
this.moving.forEach(v => {
|
this.movingRenderable.push(...this.moving);
|
||||||
if (!v.render.autotile) {
|
|
||||||
this.movingRenderable.push({
|
|
||||||
...v.render,
|
|
||||||
x: v.x,
|
|
||||||
y: v.y,
|
|
||||||
zIndex: v.nowZ
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.movingRenderable.push({
|
|
||||||
...v.render,
|
|
||||||
x: v.x,
|
|
||||||
y: v.y,
|
|
||||||
zIndex: v.nowZ,
|
|
||||||
image: v.render.image[0b00000000],
|
|
||||||
autotile: false
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
for (const ex of this.extend.values()) {
|
for (const ex of this.extend.values()) {
|
||||||
ex.onMovingUpdate?.(this, this.movingRenderable);
|
ex.onMovingUpdate?.(this, this.movingRenderable);
|
||||||
@ -1202,12 +1179,7 @@ export class Layer extends Container {
|
|||||||
const { ctx } = this.backMap;
|
const { ctx } = this.backMap;
|
||||||
|
|
||||||
const mat = camera.mat;
|
const mat = camera.mat;
|
||||||
const a = mat[0];
|
const [a, b, , c, d, , e, f] = mat;
|
||||||
const b = mat[1];
|
|
||||||
const c = mat[3];
|
|
||||||
const d = mat[4];
|
|
||||||
const e = mat[6];
|
|
||||||
const f = mat[7];
|
|
||||||
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);
|
||||||
@ -1322,12 +1294,7 @@ export class Layer extends Container {
|
|||||||
|
|
||||||
ctx.save();
|
ctx.save();
|
||||||
const mat = camera.mat;
|
const mat = camera.mat;
|
||||||
const a = mat[0];
|
const [a, b, , c, d, , e, f] = mat;
|
||||||
const b = mat[1];
|
|
||||||
const c = mat[3];
|
|
||||||
const d = mat[4];
|
|
||||||
const e = mat[6];
|
|
||||||
const f = mat[7];
|
|
||||||
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);
|
||||||
@ -1336,7 +1303,7 @@ export class Layer extends Container {
|
|||||||
const r = (max1 * max2) ** 2;
|
const r = (max1 * max2) ** 2;
|
||||||
|
|
||||||
this.movingRenderable.forEach(v => {
|
this.movingRenderable.forEach(v => {
|
||||||
const { x, y, image, frame: blockFrame, render, animate } = v;
|
const { x, y, image, render, animate } = v;
|
||||||
const ff = frame % 4;
|
const ff = frame % 4;
|
||||||
const i = animate === -1 ? ff : animate;
|
const i = animate === -1 ? ff : animate;
|
||||||
const [sx, sy, w, h] = render[i];
|
const [sx, sy, w, h] = render[i];
|
||||||
@ -1376,7 +1343,31 @@ export class Layer extends Container {
|
|||||||
x: number,
|
x: number,
|
||||||
y: number,
|
y: number,
|
||||||
time?: number
|
time?: number
|
||||||
): Promise<void>;
|
): Promise<void> {
|
||||||
|
const block = this.renderData[index];
|
||||||
|
const fx = index % this.width;
|
||||||
|
const fy = Math.floor(index / this.width);
|
||||||
|
|
||||||
|
if (type === 'swap' || time === 0) {
|
||||||
|
this.putRenderData([0], 1, fx, fy);
|
||||||
|
this.putRenderData([block], 1, x, y);
|
||||||
|
return Promise.resolve();
|
||||||
|
} else {
|
||||||
|
if (!time) return Promise.reject();
|
||||||
|
const dx = x - fx;
|
||||||
|
const dy = y - fy;
|
||||||
|
return this.moveAs(
|
||||||
|
index,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
progress => {
|
||||||
|
return [dx * progress, dy * progress, Math.floor(dy + fy)];
|
||||||
|
},
|
||||||
|
time
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 让图块按照一个函数进行移动
|
* 让图块按照一个函数进行移动
|
||||||
* @param index 要移动的图块在渲染数据中的索引位置
|
* @param index 要移动的图块在渲染数据中的索引位置
|
||||||
@ -1388,22 +1379,68 @@ export class Layer extends Container {
|
|||||||
* @param time 移动总时长
|
* @param time 移动总时长
|
||||||
* @param relative 是否是相对模式
|
* @param relative 是否是相对模式
|
||||||
*/
|
*/
|
||||||
move(
|
moveAs(
|
||||||
index: number,
|
index: number,
|
||||||
type: 'fn',
|
x: number,
|
||||||
|
y: number,
|
||||||
fn: TimingFn<3>,
|
fn: TimingFn<3>,
|
||||||
time?: number,
|
time: number,
|
||||||
relative?: boolean
|
relative: boolean = true
|
||||||
): Promise<void>;
|
|
||||||
move(
|
|
||||||
index: number,
|
|
||||||
type: 'linear' | 'swap' | 'fn',
|
|
||||||
x: number | TimingFn<3>,
|
|
||||||
y?: number,
|
|
||||||
time?: number | boolean
|
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
// todo
|
const block = this.renderData[index];
|
||||||
return Promise.resolve();
|
const fx = index % this.width;
|
||||||
|
const fy = Math.floor(index / this.width);
|
||||||
|
const renderable = texture.getRenderable(block);
|
||||||
|
if (!renderable) return Promise.reject();
|
||||||
|
|
||||||
|
const image = renderable.autotile
|
||||||
|
? renderable.image[0]
|
||||||
|
: renderable.image;
|
||||||
|
|
||||||
|
const moving: LayerMovingRenderable = {
|
||||||
|
x: fx,
|
||||||
|
y: fy,
|
||||||
|
zIndex: fy,
|
||||||
|
image: image,
|
||||||
|
autotile: false,
|
||||||
|
frame: renderable.frame,
|
||||||
|
bigImage: renderable.bigImage,
|
||||||
|
animate: -1,
|
||||||
|
render: renderable.render
|
||||||
|
};
|
||||||
|
this.moving.add(moving);
|
||||||
|
|
||||||
|
// 删除原始位置的图块
|
||||||
|
this.putRenderData([0], 1, fx, fy);
|
||||||
|
|
||||||
|
let nowZ = fy;
|
||||||
|
const startTime = Date.now();
|
||||||
|
return new Promise<void>(resolve => {
|
||||||
|
this.delegateTicker(
|
||||||
|
() => {
|
||||||
|
const now = Date.now();
|
||||||
|
const progress = (now - startTime) / time;
|
||||||
|
const [nx, ny, nz] = fn(progress);
|
||||||
|
const tx = relative ? nx + fx : nx;
|
||||||
|
const ty = relative ? ny + fy : ny;
|
||||||
|
moving.x = tx;
|
||||||
|
moving.y = ty;
|
||||||
|
moving.zIndex = nz;
|
||||||
|
if (nz !== nowZ) {
|
||||||
|
this.movingRenderable.sort(
|
||||||
|
(a, b) => a.zIndex - b.zIndex
|
||||||
|
);
|
||||||
|
}
|
||||||
|
this.update(this);
|
||||||
|
},
|
||||||
|
time,
|
||||||
|
() => {
|
||||||
|
this.putRenderData([block], 1, x, y);
|
||||||
|
this.moving.delete(moving);
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy(): void {
|
destroy(): void {
|
||||||
|
Loading…
Reference in New Issue
Block a user