mirror of
https://github.com/unanmed/HumanBreak.git
synced 2025-01-19 12:49:25 +08:00
feat: 平滑视角
This commit is contained in:
parent
ef50b985cb
commit
cd9c95d24d
@ -23,8 +23,12 @@ export class Container<E extends EContainerEvent = EContainerEvent>
|
|||||||
* @param type 渲染模式,absolute表示绝对位置,static表示跟随摄像机移动
|
* @param type 渲染模式,absolute表示绝对位置,static表示跟随摄像机移动
|
||||||
* @param cache 是否启用缓存机制
|
* @param cache 是否启用缓存机制
|
||||||
*/
|
*/
|
||||||
constructor(type: RenderItemPosition = 'static', cache: boolean = true) {
|
constructor(
|
||||||
super(type, cache);
|
type: RenderItemPosition = 'static',
|
||||||
|
cache: boolean = true,
|
||||||
|
fall: boolean = false
|
||||||
|
) {
|
||||||
|
super(type, cache, fall);
|
||||||
this.type = type;
|
this.type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ import { LayerGroupFilter } from '@/plugin/fx/gameCanvas';
|
|||||||
import { LayerGroupAnimate } from './preset/animate';
|
import { LayerGroupAnimate } from './preset/animate';
|
||||||
import { LayerGroupPortal } from '@/plugin/fx/portal';
|
import { LayerGroupPortal } from '@/plugin/fx/portal';
|
||||||
import { LayerGroupHalo } from '@/plugin/fx/halo';
|
import { LayerGroupHalo } from '@/plugin/fx/halo';
|
||||||
|
import { FloorViewport } from './preset/viewport';
|
||||||
|
|
||||||
let main: MotaRenderer;
|
let main: MotaRenderer;
|
||||||
|
|
||||||
@ -37,6 +38,7 @@ Mota.require('var', 'loading').once('loaded', () => {
|
|||||||
const animate = new LayerGroupAnimate();
|
const animate = new LayerGroupAnimate();
|
||||||
const portal = new LayerGroupPortal();
|
const portal = new LayerGroupPortal();
|
||||||
const halo = new LayerGroupHalo();
|
const halo = new LayerGroupHalo();
|
||||||
|
const viewport = new FloorViewport();
|
||||||
layer.extends(damage);
|
layer.extends(damage);
|
||||||
layer.extends(detail);
|
layer.extends(detail);
|
||||||
layer.extends(filter);
|
layer.extends(filter);
|
||||||
@ -46,6 +48,7 @@ Mota.require('var', 'loading').once('loaded', () => {
|
|||||||
layer.getLayer('event')?.extends(door);
|
layer.getLayer('event')?.extends(door);
|
||||||
layer.getLayer('event')?.extends(shadow);
|
layer.getLayer('event')?.extends(shadow);
|
||||||
layer.extends(animate);
|
layer.extends(animate);
|
||||||
|
layer.extends(viewport);
|
||||||
|
|
||||||
render.appendChild(layer);
|
render.appendChild(layer);
|
||||||
// console.log(render);
|
// console.log(render);
|
||||||
|
@ -119,6 +119,7 @@ export interface ERenderItemEvent {
|
|||||||
|
|
||||||
interface TickerDelegation {
|
interface TickerDelegation {
|
||||||
fn: TickerFn;
|
fn: TickerFn;
|
||||||
|
timeout?: number;
|
||||||
endFn?: () => void;
|
endFn?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -179,11 +180,18 @@ export abstract class RenderItem<E extends ERenderItemEvent = ERenderItemEvent>
|
|||||||
protected cacheDirty: boolean = true;
|
protected cacheDirty: boolean = true;
|
||||||
/** 是否启用缓存机制 */
|
/** 是否启用缓存机制 */
|
||||||
readonly enableCache: boolean = true;
|
readonly enableCache: boolean = true;
|
||||||
|
/** 是否启用transform下穿机制 */
|
||||||
|
readonly transformFallThrough: boolean = false;
|
||||||
|
|
||||||
constructor(type: RenderItemPosition, enableCache: boolean = true) {
|
constructor(
|
||||||
|
type: RenderItemPosition,
|
||||||
|
enableCache: boolean = true,
|
||||||
|
transformFallThrough: boolean = false
|
||||||
|
) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.enableCache = enableCache;
|
this.enableCache = enableCache;
|
||||||
|
this.transformFallThrough = transformFallThrough;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
|
|
||||||
this.cache.withGameScale(true);
|
this.cache.withGameScale(true);
|
||||||
@ -222,7 +230,7 @@ export abstract class RenderItem<E extends ERenderItemEvent = ERenderItemEvent>
|
|||||||
if (this.hidden) return;
|
if (this.hidden) return;
|
||||||
this.emit('beforeRender', transform);
|
this.emit('beforeRender', transform);
|
||||||
this.needUpdate = false;
|
this.needUpdate = false;
|
||||||
const tran = this.transform;
|
const tran = this.transformFallThrough ? transform : this.transform;
|
||||||
|
|
||||||
const ax = -this.anchorX * this.width;
|
const ax = -this.anchorX * this.width;
|
||||||
const ay = -this.anchorY * this.height;
|
const ay = -this.anchorY * this.height;
|
||||||
@ -320,7 +328,7 @@ export abstract class RenderItem<E extends ERenderItemEvent = ERenderItemEvent>
|
|||||||
RenderItem.tickerMap.set(id, delegation);
|
RenderItem.tickerMap.set(id, delegation);
|
||||||
RenderItem.ticker.add(fn);
|
RenderItem.ticker.add(fn);
|
||||||
if (typeof time === 'number' && time < 2147438647 && time > 0) {
|
if (typeof time === 'number' && time < 2147438647 && time > 0) {
|
||||||
setTimeout(() => {
|
delegation.timeout = window.setTimeout(() => {
|
||||||
RenderItem.ticker.remove(fn);
|
RenderItem.ticker.remove(fn);
|
||||||
end?.();
|
end?.();
|
||||||
}, time);
|
}, time);
|
||||||
@ -332,6 +340,7 @@ export abstract class RenderItem<E extends ERenderItemEvent = ERenderItemEvent>
|
|||||||
const delegation = RenderItem.tickerMap.get(id);
|
const delegation = RenderItem.tickerMap.get(id);
|
||||||
if (!delegation) return false;
|
if (!delegation) return false;
|
||||||
RenderItem.ticker.remove(delegation.fn);
|
RenderItem.ticker.remove(delegation.fn);
|
||||||
|
window.clearTimeout(delegation.timeout);
|
||||||
if (callEnd) delegation.endFn?.();
|
if (callEnd) delegation.endFn?.();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -165,7 +165,7 @@ export class Damage extends Sprite<EDamageEvent> {
|
|||||||
private needUpdateBlocks: Set<number> = new Set();
|
private needUpdateBlocks: Set<number> = new Set();
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super('absolute', false);
|
super('absolute', false, true);
|
||||||
|
|
||||||
this.block = new BlockCacher(0, 0, core._WIDTH_, 1);
|
this.block = new BlockCacher(0, 0, core._WIDTH_, 1);
|
||||||
this.type = 'absolute';
|
this.type = 'absolute';
|
||||||
@ -175,11 +175,11 @@ export class Damage extends Sprite<EDamageEvent> {
|
|||||||
this.damageMap.setAntiAliasing(true);
|
this.damageMap.setAntiAliasing(true);
|
||||||
this.damageMap.size(core._PX_, core._PY_);
|
this.damageMap.size(core._PX_, core._PY_);
|
||||||
|
|
||||||
this.setRenderFn((canvas, camera) => {
|
this.setRenderFn((canvas, transform) => {
|
||||||
const { ctx } = canvas;
|
const { ctx } = canvas;
|
||||||
const { width, height } = canvas;
|
const { width, height } = canvas;
|
||||||
ctx.imageSmoothingEnabled = false;
|
ctx.imageSmoothingEnabled = false;
|
||||||
this.renderDamage(camera);
|
this.renderDamage(transform);
|
||||||
ctx.drawImage(this.damageMap.canvas, 0, 0, width, height);
|
ctx.drawImage(this.damageMap.canvas, 0, 0, width, height);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -107,11 +107,14 @@ export class LayerGroup extends Container implements IAnimateFrame {
|
|||||||
/** 地图显示层 */
|
/** 地图显示层 */
|
||||||
layers: Map<FloorLayer, Layer> = new Map();
|
layers: Map<FloorLayer, Layer> = new Map();
|
||||||
|
|
||||||
|
/** 这个地图组的摄像机 */
|
||||||
|
camera: Transform = new Transform();
|
||||||
|
|
||||||
private needRender?: NeedRenderData;
|
private needRender?: NeedRenderData;
|
||||||
private extend: Map<string, ILayerGroupRenderExtends> = new Map();
|
private extend: Map<string, ILayerGroupRenderExtends> = new Map();
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super('static');
|
super('static', true);
|
||||||
|
|
||||||
this.setHD(true);
|
this.setHD(true);
|
||||||
this.setAntiAliasing(false);
|
this.setAntiAliasing(false);
|
||||||
@ -128,6 +131,17 @@ export class LayerGroup extends Container implements IAnimateFrame {
|
|||||||
binder.bindThis();
|
binder.bindThis();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected render(canvas: MotaOffscreenCanvas2D): void {
|
||||||
|
const { ctx } = canvas;
|
||||||
|
|
||||||
|
this.sortedChildren.forEach(v => {
|
||||||
|
if (v.hidden) return;
|
||||||
|
ctx.save();
|
||||||
|
v.renderContent(canvas, this.camera);
|
||||||
|
ctx.restore();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 添加渲染拓展,可以将渲染拓展理解为一类插件,通过指定的函数在对应时刻执行一些函数,
|
* 添加渲染拓展,可以将渲染拓展理解为一类插件,通过指定的函数在对应时刻执行一些函数,
|
||||||
* 来达到执行自己想要的功能的效果。例如样板自带的勇士渲染、伤害渲染等都由此实现。
|
* 来达到执行自己想要的功能的效果。例如样板自带的勇士渲染、伤害渲染等都由此实现。
|
||||||
@ -633,7 +647,7 @@ export class Layer extends Container {
|
|||||||
protected backMap: MotaOffscreenCanvas2D = new MotaOffscreenCanvas2D();
|
protected backMap: MotaOffscreenCanvas2D = new MotaOffscreenCanvas2D();
|
||||||
|
|
||||||
/** 最终渲染至的Sprite */
|
/** 最终渲染至的Sprite */
|
||||||
main: Sprite = new Sprite('static', false);
|
main: Sprite = new Sprite('absolute', false, true);
|
||||||
|
|
||||||
/** 渲染的层 */
|
/** 渲染的层 */
|
||||||
layer?: FloorLayer;
|
layer?: FloorLayer;
|
||||||
@ -670,7 +684,7 @@ export class Layer extends Container {
|
|||||||
moving: Set<LayerMovingRenderable> = new Set();
|
moving: Set<LayerMovingRenderable> = new Set();
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super('absolute', false);
|
super('absolute', false, true);
|
||||||
|
|
||||||
// this.setHD(false);
|
// this.setHD(false);
|
||||||
this.setAntiAliasing(false);
|
this.setAntiAliasing(false);
|
||||||
|
390
src/core/render/preset/viewport.ts
Normal file
390
src/core/render/preset/viewport.ts
Normal file
@ -0,0 +1,390 @@
|
|||||||
|
import { logger } from '@/core/common/logger';
|
||||||
|
import { HeroRenderer } from './hero';
|
||||||
|
import { ILayerGroupRenderExtends, LayerGroup } from './layer';
|
||||||
|
import { Transform } from '../transform';
|
||||||
|
import { LayerGroupFloorBinder } from './floor';
|
||||||
|
import { hyper, TimingFn } from 'mutate-animate';
|
||||||
|
import { RenderAdapter } from '../adapter';
|
||||||
|
|
||||||
|
export class FloorViewport implements ILayerGroupRenderExtends {
|
||||||
|
id: string = 'viewport';
|
||||||
|
|
||||||
|
group!: LayerGroup;
|
||||||
|
hero!: HeroRenderer;
|
||||||
|
transform!: Transform;
|
||||||
|
binder!: LayerGroupFloorBinder;
|
||||||
|
|
||||||
|
/** 是否启用视角控制拓展 */
|
||||||
|
enabled: boolean = true;
|
||||||
|
|
||||||
|
/** 渐变的速率曲线 */
|
||||||
|
transitionFn: TimingFn = hyper('sin', 'out');
|
||||||
|
/** 加减速的速率曲线 */
|
||||||
|
movingEaseFn: TimingFn = t => t ** 2;
|
||||||
|
|
||||||
|
/** 突变时的渐变时长 */
|
||||||
|
transitionTime: number = 600;
|
||||||
|
|
||||||
|
/** 当前视角移动速度 */
|
||||||
|
private speedX: number = 0;
|
||||||
|
private speedY: number = 0;
|
||||||
|
/** X方向移动状态,0表示静止,1表示加速过程,2表示匀速过程,3表示减速过程 */
|
||||||
|
private movingStatusX: 0 | 1 | 2 | 3 = 0;
|
||||||
|
/** X方向加减速过程进度 */
|
||||||
|
private movingEaseProgressX: number = 0;
|
||||||
|
/** Y方向移动状态,0表示静止,1表示加速过程,2表示匀速过程,3表示减速过程 */
|
||||||
|
private movingStatusY: 0 | 1 | 2 | 3 = 0;
|
||||||
|
/** Y方向加减速过程进度 */
|
||||||
|
private movingEaseProgressY: number = 0;
|
||||||
|
/** X方向移动结束坐标 */
|
||||||
|
private movingEndX: number = 0;
|
||||||
|
/** X方向移动结束坐标 */
|
||||||
|
private movingEndY: number = 0;
|
||||||
|
/** X方向进入第一阶段的时刻 */
|
||||||
|
private movingAccX: number = 0;
|
||||||
|
/** Y方向进入第一阶段的时刻 */
|
||||||
|
private movingAccY: number = 0;
|
||||||
|
/** X方向进入第二阶段的时刻 */
|
||||||
|
private movingConstantX: number = 0;
|
||||||
|
/** Y方向进入第二阶段的时刻 */
|
||||||
|
private movingConstantY: number = 0;
|
||||||
|
/** X方向进入第三阶段的时刻 */
|
||||||
|
private movingDeaccX: number = 0;
|
||||||
|
/** Y方向进入第三阶段的时刻 */
|
||||||
|
private movingDeaccY: number = 0;
|
||||||
|
/** X方向进入第一阶段的横坐标 */
|
||||||
|
private movingAccPosX: number = 0;
|
||||||
|
/** Y方向进入第一阶段的横坐标 */
|
||||||
|
private movingAccPosY: number = 0;
|
||||||
|
/** X方向进入第二阶段的横坐标 */
|
||||||
|
private movingConstantPosX: number = 0;
|
||||||
|
/** Y方向进入第二阶段的横坐标 */
|
||||||
|
private movingConstantPosY: number = 0;
|
||||||
|
/** X方向进入第三阶段的横坐标 */
|
||||||
|
private movingDeaccPosX: number = 0;
|
||||||
|
/** Y方向进入第三阶段的横坐标 */
|
||||||
|
private movingDeaccPosY: number = 0;
|
||||||
|
/** X方向进入第一阶段的进度 */
|
||||||
|
private movingAccProgressX: number = 0;
|
||||||
|
/** Y方向进入第一阶段的进度 */
|
||||||
|
private movingAccProgressY: number = 0;
|
||||||
|
/** X方向进入第三阶段的进度 */
|
||||||
|
private movingDeaccProgressX: number = 0;
|
||||||
|
/** Y方向进入第三阶段的进度 */
|
||||||
|
private movingDeaccProgressY: number = 0;
|
||||||
|
/** 移动的加减速时长 */
|
||||||
|
private movingEaseTime: number = 0;
|
||||||
|
|
||||||
|
/** 当前视角位置 */
|
||||||
|
private nx: number = 0;
|
||||||
|
private ny: number = 0;
|
||||||
|
|
||||||
|
/** 委托ticker */
|
||||||
|
private delegation: number = -1;
|
||||||
|
/** 渐变委托ticker */
|
||||||
|
private transition: number = -1;
|
||||||
|
/** 移动委托ticker */
|
||||||
|
private moving: number = -1;
|
||||||
|
/** 是否在渐变过程中 */
|
||||||
|
private inTransition: boolean = false;
|
||||||
|
/** 是否在移动过程中 */
|
||||||
|
private inMoving: boolean = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 禁用自动视角控制
|
||||||
|
*/
|
||||||
|
disable() {
|
||||||
|
this.enabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 启用自动视角控制
|
||||||
|
*/
|
||||||
|
enable() {
|
||||||
|
this.enabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 传入视角的目标位置,将其限定在地图范围内后返回
|
||||||
|
* @param x 图格横坐标
|
||||||
|
* @param y 图格纵坐标
|
||||||
|
*/
|
||||||
|
getBoundedPosition(x: number, y: number) {
|
||||||
|
const width = core._WIDTH_;
|
||||||
|
const height = core._HEIGHT_;
|
||||||
|
const minX = (width - 1) / 2;
|
||||||
|
const minY = (height - 1) / 2;
|
||||||
|
const floor = core.status.maps[this.binder.getFloor()];
|
||||||
|
const maxX = floor.width - minX - 1;
|
||||||
|
const maxY = floor.height - minY - 1;
|
||||||
|
|
||||||
|
// return { x, y };
|
||||||
|
return { x: core.clamp(x, minX, maxX), y: core.clamp(y, minY, maxY) };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置视角位置
|
||||||
|
* @param x 目标图格横坐标
|
||||||
|
* @param y 目标图格纵坐标
|
||||||
|
*/
|
||||||
|
setPosition(x: number, y: number) {
|
||||||
|
const { x: nx, y: ny } = this.getBoundedPosition(x, y);
|
||||||
|
this.group.removeTicker(this.transition, false);
|
||||||
|
this.nx = nx;
|
||||||
|
this.ny = ny;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当勇士通过移动改变至指定位置时移动视角
|
||||||
|
* @param x 目标图格横坐标
|
||||||
|
* @param y 目标图格纵坐标
|
||||||
|
*/
|
||||||
|
moveTo(x: number, y: number) {
|
||||||
|
const { x: nx, y: ny } = this.getBoundedPosition(x, y);
|
||||||
|
if (this.inTransition) {
|
||||||
|
const distance = Math.hypot(this.nx - nx, this.ny - ny);
|
||||||
|
const time = core.clamp(distance * 200, 200, 600);
|
||||||
|
this.createTransition(nx, ny, time);
|
||||||
|
} else {
|
||||||
|
this.createTransition(nx, ny, 200);
|
||||||
|
// const moveSpeed = 1000 / this.hero.speed;
|
||||||
|
// this.speedX = moveSpeed;
|
||||||
|
// this.speedY = moveSpeed;
|
||||||
|
// this.movingEndX = x;
|
||||||
|
// this.movingEndY = y;
|
||||||
|
// this.movingEaseTime = moveSpeed * 2;
|
||||||
|
// this.processMoving(x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private processMoving(x: number, y: number) {
|
||||||
|
this.movingEndX = x;
|
||||||
|
this.movingEndY = y;
|
||||||
|
if (!this.inMoving) {
|
||||||
|
this.movingStatusX = 0;
|
||||||
|
this.movingEaseProgressX = 0;
|
||||||
|
this.movingStatusY = 0;
|
||||||
|
this.movingEaseProgressY = 0;
|
||||||
|
this.inMoving = true;
|
||||||
|
} else {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当勇士位置突变至指定位置时移动视角
|
||||||
|
* @param x 目标图格横坐标
|
||||||
|
* @param y 目标图格纵坐标
|
||||||
|
*/
|
||||||
|
mutateTo(x: number, y: number) {
|
||||||
|
const { x: nx, y: ny } = this.getBoundedPosition(x, y);
|
||||||
|
this.createTransition(nx, ny, this.transitionTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
private createTransition(x: number, y: number, time: number) {
|
||||||
|
const start = Date.now();
|
||||||
|
const end = start + time;
|
||||||
|
const sx = this.nx;
|
||||||
|
const sy = this.ny;
|
||||||
|
const dx = x - sx;
|
||||||
|
const dy = y - sy;
|
||||||
|
this.speedX = 0;
|
||||||
|
this.speedY = 0;
|
||||||
|
|
||||||
|
this.inTransition = true;
|
||||||
|
this.group.removeTicker(this.transition, false);
|
||||||
|
this.transition = this.group.delegateTicker(
|
||||||
|
() => {
|
||||||
|
const now = Date.now();
|
||||||
|
if (now >= end) {
|
||||||
|
this.group.removeTicker(this.transition, true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const progress = this.transitionFn((now - start) / time);
|
||||||
|
const tx = dx * progress;
|
||||||
|
const ty = dy * progress;
|
||||||
|
this.nx = tx + sx;
|
||||||
|
this.ny = ty + sy;
|
||||||
|
},
|
||||||
|
time,
|
||||||
|
() => {
|
||||||
|
this.nx = x;
|
||||||
|
this.ny = y;
|
||||||
|
this.inTransition = false;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private createMoving() {
|
||||||
|
this.moving = this.group.delegateTicker(() => {
|
||||||
|
const nx = this.nx;
|
||||||
|
const ny = this.ny;
|
||||||
|
const now = Date.now();
|
||||||
|
const dx = Math.sign(this.movingEndX - nx);
|
||||||
|
const dy = Math.sign(this.movingEndY - ny);
|
||||||
|
const fn = this.movingEaseFn;
|
||||||
|
// 进行进度判断
|
||||||
|
if (this.movingEndX !== nx && this.movingStatusX === 0) {
|
||||||
|
this.movingStatusX = 1;
|
||||||
|
this.movingAccX = now;
|
||||||
|
this.movingAccProgressX = this.movingEaseProgressX;
|
||||||
|
this.movingAccPosX = nx - fn(this.movingAccProgressX) * dx;
|
||||||
|
}
|
||||||
|
if (this.movingEndY !== ny && this.movingStatusY === 0) {
|
||||||
|
this.movingEaseProgressY = 1;
|
||||||
|
this.movingAccY = now;
|
||||||
|
this.movingAccProgressY = this.movingEaseProgressY;
|
||||||
|
this.movingAccPosY = ny - fn(this.movingAccProgressY) * dy;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
Math.abs(this.movingEndX - nx) <= 1 &&
|
||||||
|
this.movingStatusX !== 3
|
||||||
|
) {
|
||||||
|
this.movingStatusX = 3;
|
||||||
|
this.movingDeaccX = now;
|
||||||
|
this.movingDeaccProgressX = this.movingEaseProgressX;
|
||||||
|
this.movingDeaccPosX =
|
||||||
|
nx - (fn(this.movingDeaccProgressX) + 1) * dx;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
Math.abs(this.movingEndY - ny) <= 1 &&
|
||||||
|
this.movingStatusY !== 3
|
||||||
|
) {
|
||||||
|
this.movingStatusY = 3;
|
||||||
|
this.movingDeaccY = now;
|
||||||
|
this.movingDeaccProgressY = this.movingEaseProgressY;
|
||||||
|
this.movingDeaccPosY =
|
||||||
|
ny - (fn(this.movingDeaccProgressY) + 1) * dy;
|
||||||
|
}
|
||||||
|
if (this.movingEaseProgressX >= 1 && this.movingStatusX === 1) {
|
||||||
|
this.movingStatusX = 2;
|
||||||
|
this.movingConstantX = now;
|
||||||
|
this.movingConstantPosX = nx;
|
||||||
|
}
|
||||||
|
if (this.movingEaseProgressY >= 1 && this.movingStatusY === 1) {
|
||||||
|
this.movingStatusY = 2;
|
||||||
|
this.movingConstantY = now;
|
||||||
|
this.movingConstantPosY = ny;
|
||||||
|
}
|
||||||
|
if (this.movingEaseProgressX <= 0 && this.movingStatusX === 3) {
|
||||||
|
this.nx = this.movingEndX;
|
||||||
|
this.movingStatusX = 0;
|
||||||
|
this.movingEaseProgressX = 0;
|
||||||
|
}
|
||||||
|
if (this.movingEaseProgressY <= 0 && this.movingStatusY === 3) {
|
||||||
|
this.ny = this.movingEndY;
|
||||||
|
this.movingStatusY = 0;
|
||||||
|
this.movingEaseProgressY = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 平滑视角位置计算
|
||||||
|
if (this.movingEaseProgressX === 1) {
|
||||||
|
// 加速阶段
|
||||||
|
this.movingEaseProgressX =
|
||||||
|
(now - this.movingAccX) / this.movingEaseTime;
|
||||||
|
const tx = fn(this.movingEaseProgressX) * dx;
|
||||||
|
this.nx = this.movingAccPosX + tx;
|
||||||
|
} else if (this.movingEaseProgressX === 2) {
|
||||||
|
// 匀速阶段
|
||||||
|
const time = now - this.movingConstantX;
|
||||||
|
this.nx = this.movingConstantPosX + time * this.speedX * dx;
|
||||||
|
} else if (this.movingEaseProgressX === 3) {
|
||||||
|
// 减速阶段
|
||||||
|
this.movingEaseProgressX =
|
||||||
|
1 - (now - this.movingDeaccX) / this.movingEaseTime;
|
||||||
|
const tx = fn(this.movingEaseProgressX) * dx;
|
||||||
|
this.nx = this.movingDeaccPosX + tx;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.movingEaseProgressY === 1) {
|
||||||
|
// 加速阶段
|
||||||
|
this.movingEaseProgressY =
|
||||||
|
(now - this.movingAccY) / this.movingEaseTime;
|
||||||
|
const ty = fn(this.movingEaseProgressY) * dy;
|
||||||
|
this.ny = this.movingAccPosY + ty;
|
||||||
|
} else if (this.movingEaseProgressY === 2) {
|
||||||
|
// 匀速阶段
|
||||||
|
const time = now - this.movingConstantY;
|
||||||
|
this.ny = this.movingConstantPosY + time * this.speedY * dy;
|
||||||
|
} else if (this.movingEaseProgressY === 3) {
|
||||||
|
// 减速阶段
|
||||||
|
this.movingEaseProgressY =
|
||||||
|
1 - (now - this.movingDeaccY) / this.movingEaseTime;
|
||||||
|
const ty = fn(this.movingEaseProgressY) * dy;
|
||||||
|
this.ny = this.movingDeaccPosY + ty;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private create() {
|
||||||
|
let nx = this.nx;
|
||||||
|
let ny = this.ny;
|
||||||
|
const halfWidth = core._PX_ / 2;
|
||||||
|
const halfHeight = core._PY_ / 2;
|
||||||
|
this.delegation = this.group.delegateTicker(() => {
|
||||||
|
if (this.nx === nx && this.ny === ny) return;
|
||||||
|
const cell = this.group.cellSize;
|
||||||
|
const half = cell / 2;
|
||||||
|
nx = this.nx;
|
||||||
|
ny = this.ny;
|
||||||
|
const ox = nx * cell;
|
||||||
|
const oy = ny * cell;
|
||||||
|
core.bigmap.offsetX = ox;
|
||||||
|
core.bigmap.offsetY = oy;
|
||||||
|
|
||||||
|
this.group.camera.setTranslate(
|
||||||
|
-ox + halfWidth - half,
|
||||||
|
-oy + halfHeight - half
|
||||||
|
);
|
||||||
|
this.group.update(this.group);
|
||||||
|
});
|
||||||
|
// this.createMoving();
|
||||||
|
}
|
||||||
|
|
||||||
|
awake(group: LayerGroup): void {
|
||||||
|
this.group = group;
|
||||||
|
this.transform = group.transform;
|
||||||
|
const ex1 = group.getLayer('event')?.getExtends('floor-hero');
|
||||||
|
const ex2 = group.getExtends('floor-binder');
|
||||||
|
if (
|
||||||
|
ex1 instanceof HeroRenderer &&
|
||||||
|
ex2 instanceof LayerGroupFloorBinder
|
||||||
|
) {
|
||||||
|
this.hero = ex1;
|
||||||
|
this.binder = ex2;
|
||||||
|
this.create();
|
||||||
|
adapter.add(this);
|
||||||
|
} else {
|
||||||
|
logger.error(
|
||||||
|
15,
|
||||||
|
`Viewport extends needs 'floor-hero' extends as dependency.`
|
||||||
|
);
|
||||||
|
group.removeExtends('viewport');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onDestroy(group: LayerGroup): void {
|
||||||
|
group.removeTicker(this.delegation);
|
||||||
|
group.removeTicker(this.transition);
|
||||||
|
group.removeTicker(this.moving);
|
||||||
|
adapter.remove(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const adapter = new RenderAdapter<FloorViewport>('viewport');
|
||||||
|
adapter.receive('mutateTo', (item, x, y) => {
|
||||||
|
item.mutateTo(x, y);
|
||||||
|
return Promise.resolve();
|
||||||
|
});
|
||||||
|
adapter.receive('moveTo', (item, x, y) => {
|
||||||
|
item.moveTo(x, y);
|
||||||
|
return Promise.resolve();
|
||||||
|
});
|
||||||
|
adapter.receive('setPosition', (item, x, y) => {
|
||||||
|
item.setPosition(x, y);
|
||||||
|
return Promise.resolve();
|
||||||
|
});
|
||||||
|
|
||||||
|
const hook = Mota.require('var', 'hook');
|
||||||
|
hook.on('changingFloor', (_, loc) => {
|
||||||
|
adapter.all('setPosition', loc.x, loc.y);
|
||||||
|
});
|
@ -19,8 +19,12 @@ export class Sprite<E extends ESpriteEvent = ESpriteEvent> extends RenderItem<
|
|||||||
* @param type 渲染模式,absolute表示绝对位置,不会跟随自身的Transform改变
|
* @param type 渲染模式,absolute表示绝对位置,不会跟随自身的Transform改变
|
||||||
* @param cache 是否启用缓存机制
|
* @param cache 是否启用缓存机制
|
||||||
*/
|
*/
|
||||||
constructor(type: RenderItemPosition = 'static', cache: boolean = true) {
|
constructor(
|
||||||
super(type, cache);
|
type: RenderItemPosition = 'static',
|
||||||
|
cache: boolean = true,
|
||||||
|
fall: boolean = false
|
||||||
|
) {
|
||||||
|
super(type, cache, fall);
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.renderFn = () => {};
|
this.renderFn = () => {};
|
||||||
}
|
}
|
||||||
|
@ -95,7 +95,7 @@ export class Portal extends Sprite {
|
|||||||
private delegation: number;
|
private delegation: number;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super('static', false);
|
super('static', false, true);
|
||||||
|
|
||||||
this.particleSetting = mainSetting.getSetting('fx.portalParticle')!;
|
this.particleSetting = mainSetting.getSetting('fx.portalParticle')!;
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@ import { BluePalace } from '@/game/mechanism/misc';
|
|||||||
import { backDir } from './utils';
|
import { backDir } from './utils';
|
||||||
import type { TimingFn } from 'mutate-animate';
|
import type { TimingFn } from 'mutate-animate';
|
||||||
import EventEmitter from 'eventemitter3';
|
import EventEmitter from 'eventemitter3';
|
||||||
|
import type { FloorViewport } from '@/core/render/preset/viewport';
|
||||||
|
|
||||||
// 向后兼容用,会充当两个版本间过渡的作用
|
// 向后兼容用,会充当两个版本间过渡的作用
|
||||||
|
|
||||||
@ -18,6 +19,7 @@ interface Adapters {
|
|||||||
'door-animate'?: RenderAdapter<LayerDoorAnimate>;
|
'door-animate'?: RenderAdapter<LayerDoorAnimate>;
|
||||||
animate?: RenderAdapter<LayerGroupAnimate>;
|
animate?: RenderAdapter<LayerGroupAnimate>;
|
||||||
layer?: RenderAdapter<Layer>;
|
layer?: RenderAdapter<Layer>;
|
||||||
|
viewport?: RenderAdapter<FloorViewport>;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface MoveEvent {
|
interface MoveEvent {
|
||||||
@ -38,11 +40,13 @@ export function init() {
|
|||||||
const doorAnimate = Adapter.get<LayerDoorAnimate>('door-animate');
|
const doorAnimate = Adapter.get<LayerDoorAnimate>('door-animate');
|
||||||
const animate = Adapter.get<LayerGroupAnimate>('animate');
|
const animate = Adapter.get<LayerGroupAnimate>('animate');
|
||||||
const layer = Adapter.get<Layer>('layer');
|
const layer = Adapter.get<Layer>('layer');
|
||||||
|
const viewport = Adapter.get<FloorViewport>('viewport');
|
||||||
|
|
||||||
adapters['hero-adapter'] = hero;
|
adapters['hero-adapter'] = hero;
|
||||||
adapters['door-animate'] = doorAnimate;
|
adapters['door-animate'] = doorAnimate;
|
||||||
adapters['animate'] = animate;
|
adapters['animate'] = animate;
|
||||||
adapters['layer'] = layer;
|
adapters['layer'] = layer;
|
||||||
|
adapters['viewport'] = viewport;
|
||||||
}
|
}
|
||||||
|
|
||||||
let moving: boolean = false;
|
let moving: boolean = false;
|
||||||
@ -193,6 +197,17 @@ export function init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
stepEnding = adapter.all('move', moveDir);
|
stepEnding = adapter.all('move', moveDir);
|
||||||
|
if (portal && portalData) {
|
||||||
|
adapters.viewport?.all(
|
||||||
|
'mutateTo',
|
||||||
|
portalData.x,
|
||||||
|
portalData.y
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
const { nx, ny } = getNextLoc();
|
||||||
|
adapters.viewport?.all('moveTo', nx, ny);
|
||||||
|
}
|
||||||
|
|
||||||
await stepEnding;
|
await stepEnding;
|
||||||
|
|
||||||
moveEmit.emit('stepEnd');
|
moveEmit.emit('stepEnd');
|
||||||
@ -260,11 +275,13 @@ export function init() {
|
|||||||
callback?: () => void
|
callback?: () => void
|
||||||
) {
|
) {
|
||||||
if (portal && portalData) {
|
if (portal && portalData) {
|
||||||
|
const before = moveDir;
|
||||||
const { x, y, dir } = portalData;
|
const { x, y, dir } = portalData;
|
||||||
core.setHeroLoc('x', x);
|
core.setHeroLoc('x', x);
|
||||||
core.setHeroLoc('y', y);
|
core.setHeroLoc('y', y);
|
||||||
core.setHeroLoc('direction', dir);
|
core.setHeroLoc('direction', dir);
|
||||||
portal = false;
|
portal = false;
|
||||||
|
moveDir = before;
|
||||||
} else if (!noPass) {
|
} else if (!noPass) {
|
||||||
const { nx, ny } = getNextLoc();
|
const { nx, ny } = getNextLoc();
|
||||||
core.setHeroLoc('x', nx, true);
|
core.setHeroLoc('x', nx, true);
|
||||||
@ -929,6 +946,7 @@ export function init() {
|
|||||||
if (moving) return;
|
if (moving) return;
|
||||||
const sx = core.getHeroLoc('x');
|
const sx = core.getHeroLoc('x');
|
||||||
const sy = core.getHeroLoc('y');
|
const sy = core.getHeroLoc('y');
|
||||||
|
adapters.viewport?.all('mutateTo', ex, ey);
|
||||||
|
|
||||||
const locked = core.status.lockControl;
|
const locked = core.status.lockControl;
|
||||||
core.lockControl();
|
core.lockControl();
|
||||||
@ -956,6 +974,18 @@ export function init() {
|
|||||||
core.setHeroLoc('y', ey);
|
core.setHeroLoc('y', ey);
|
||||||
callback?.();
|
callback?.();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ----- 视角处理
|
||||||
|
|
||||||
|
////// 瞬间移动 //////
|
||||||
|
control.prototype.moveDirectly = function (
|
||||||
|
destX: number,
|
||||||
|
destY: number,
|
||||||
|
ignoreSteps: number
|
||||||
|
) {
|
||||||
|
adapters.viewport?.all('mutateTo', destX, destY);
|
||||||
|
return this.controldata.moveDirectly(destX, destY, ignoreSteps);
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
loading.once('loaded', () => {
|
loading.once('loaded', () => {
|
||||||
|
Loading…
Reference in New Issue
Block a user