mirror of
https://github.com/unanmed/HumanBreak.git
synced 2025-01-31 15:09:26 +08:00
refactor: 优化渲染缓存 & 添加每个元素的Transform
This commit is contained in:
parent
3f500e35ca
commit
fc05ddec9b
@ -1,10 +1,10 @@
|
||||
import { parseCss } from '@/plugin/utils';
|
||||
import { EventEmitter } from '../common/eventEmitter';
|
||||
import { EventEmitter } from 'eventemitter3';
|
||||
import { CSSObj } from '../interface';
|
||||
|
||||
interface OffscreenCanvasEvent {
|
||||
/** 当被动触发resize时(例如core.domStyle.scale变化、窗口大小变化)时触发,使用size函数并不会触发 */
|
||||
resize: () => void;
|
||||
resize: [];
|
||||
}
|
||||
|
||||
export class MotaOffscreenCanvas2D extends EventEmitter<OffscreenCanvasEvent> {
|
||||
|
@ -65,7 +65,6 @@ import * as portal from './fx/portal';
|
||||
import { MotaRenderer } from './render/render';
|
||||
import { Container } from './render/container';
|
||||
import { Sprite } from './render/sprite';
|
||||
import { Camera } from './render/camera';
|
||||
import { Image, Text } from './render/preset/misc';
|
||||
import { RenderItem } from './render/item';
|
||||
import { texture } from './render/cache';
|
||||
@ -152,7 +151,6 @@ Mota.register('module', 'Render', {
|
||||
MotaRenderer,
|
||||
Container,
|
||||
Sprite,
|
||||
Camera,
|
||||
Text,
|
||||
Image,
|
||||
RenderItem,
|
||||
|
@ -422,7 +422,9 @@ document.addEventListener('keyup', e => {
|
||||
const code = keycode(e.keyCode);
|
||||
if (gameKey.emitKey(code, assist, 'up', e)) {
|
||||
e.preventDefault();
|
||||
deleteWith(core.status.holdingKeys, e.keyCode);
|
||||
if (core.status.holdingKeys) {
|
||||
deleteWith(core.status.holdingKeys, e.keyCode);
|
||||
}
|
||||
} else {
|
||||
// polyfill样板
|
||||
if (
|
||||
|
@ -1,95 +1,51 @@
|
||||
import { MotaOffscreenCanvas2D } from '../fx/canvas2d';
|
||||
import { Camera } from './camera';
|
||||
import {
|
||||
ICanvasCachedRenderItem,
|
||||
IRenderChildable,
|
||||
RenderItem,
|
||||
RenderItemPosition,
|
||||
withCacheRender
|
||||
} from './item';
|
||||
import { IRenderChildable, RenderItem, RenderItemPosition } from './item';
|
||||
import { Transform } from './transform';
|
||||
|
||||
export class Container
|
||||
extends RenderItem
|
||||
implements ICanvasCachedRenderItem, IRenderChildable
|
||||
{
|
||||
export class Container extends RenderItem implements IRenderChildable {
|
||||
children: RenderItem[] = [];
|
||||
sortedChildren: RenderItem[] = [];
|
||||
|
||||
canvas: MotaOffscreenCanvas2D;
|
||||
|
||||
/** 是否启用缓存机制,对于特殊场景,内部已经包含了缓存机制,这时就不需要启用了 */
|
||||
private readonly enableCache: boolean;
|
||||
|
||||
/**
|
||||
* 创建一个容器,容器中可以包含其他渲染对象
|
||||
* @param type 渲染模式,absolute表示绝对位置,static表示跟随摄像机移动,只对顶层元素有效
|
||||
* @param type 渲染模式,absolute表示绝对位置,static表示跟随摄像机移动
|
||||
* @param cache 是否启用缓存机制
|
||||
*/
|
||||
constructor(type: RenderItemPosition = 'static', cache: boolean = true) {
|
||||
super();
|
||||
this.canvas = new MotaOffscreenCanvas2D();
|
||||
super(cache);
|
||||
this.type = type;
|
||||
this.canvas.withGameScale(true);
|
||||
this.enableCache = cache;
|
||||
}
|
||||
|
||||
private renderTo(canvas: MotaOffscreenCanvas2D, camera: Camera) {
|
||||
protected render(
|
||||
canvas: MotaOffscreenCanvas2D,
|
||||
transform: Transform
|
||||
): void {
|
||||
const { ctx } = canvas;
|
||||
this.sortedChildren.forEach(v => {
|
||||
if (v.hidden) return;
|
||||
ctx.save();
|
||||
if (!v.antiAliasing) {
|
||||
ctx.imageSmoothingEnabled = false;
|
||||
} else {
|
||||
if (v.antiAliasing) {
|
||||
ctx.imageSmoothingEnabled = true;
|
||||
} else {
|
||||
ctx.imageSmoothingEnabled = false;
|
||||
}
|
||||
v.render(canvas, camera);
|
||||
v.renderContent(canvas, transform);
|
||||
ctx.restore();
|
||||
});
|
||||
}
|
||||
|
||||
render(canvas: MotaOffscreenCanvas2D, camera: Camera): void {
|
||||
this.emit('beforeRender');
|
||||
if (this.needUpdate) {
|
||||
this.cache(this.using);
|
||||
this.needUpdate = false;
|
||||
}
|
||||
if (this.enableCache) {
|
||||
withCacheRender(this, canvas.canvas, canvas.ctx, camera, c => {
|
||||
this.renderTo(c, camera);
|
||||
});
|
||||
} else {
|
||||
this.renderTo(canvas, camera);
|
||||
}
|
||||
this.writing = void 0;
|
||||
this.emit('afterRender');
|
||||
}
|
||||
|
||||
size(width: number, height: number) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.canvas.size(width, height);
|
||||
this.writing = this.using;
|
||||
this.update(this);
|
||||
}
|
||||
|
||||
pos(x: number, y: number) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加子元素到这个容器上,然后在下一个tick执行更新
|
||||
* @param children 要添加的子元素
|
||||
*/
|
||||
appendChild(...children: RenderItem[]) {
|
||||
appendChild(...children: RenderItem<any>[]) {
|
||||
children.forEach(v => (v.parent = this));
|
||||
this.children.push(...children);
|
||||
this.sortChildren();
|
||||
this.update(this);
|
||||
}
|
||||
|
||||
removeChild(...child: RenderItem[]): void {
|
||||
removeChild(...child: RenderItem<any>[]): void {
|
||||
child.forEach(v => {
|
||||
const index = this.children.indexOf(v);
|
||||
if (index === -1) return;
|
||||
@ -105,18 +61,6 @@ export class Container
|
||||
.sort((a, b) => a.zIndex - b.zIndex);
|
||||
}
|
||||
|
||||
setHD(hd: boolean): void {
|
||||
this.highResolution = hd;
|
||||
this.canvas.setHD(hd);
|
||||
this.update(this);
|
||||
}
|
||||
|
||||
setAntiAliasing(anti: boolean): void {
|
||||
this.antiAliasing = anti;
|
||||
this.canvas.setAntiAliasing(anti);
|
||||
this.update(this);
|
||||
}
|
||||
|
||||
destroy(): void {
|
||||
super.destroy();
|
||||
this.children.forEach(v => {
|
||||
|
@ -1,43 +1,16 @@
|
||||
import { isNil } from 'lodash-es';
|
||||
import { EventEmitter } from 'eventemitter3';
|
||||
import { MotaOffscreenCanvas2D } from '../fx/canvas2d';
|
||||
import { Camera } from './camera';
|
||||
import { Ticker, TickerFn } from 'mutate-animate';
|
||||
import { Transform } from './transform';
|
||||
|
||||
export type RenderFunction = (
|
||||
canvas: MotaOffscreenCanvas2D,
|
||||
camera: Camera
|
||||
transform: Transform
|
||||
) => void;
|
||||
|
||||
export type RenderItemPosition = 'absolute' | 'static';
|
||||
|
||||
interface IRenderCache {
|
||||
/** 缓存列表 */
|
||||
cacheList: Map<string, MotaOffscreenCanvas2D>;
|
||||
/** 当前正在使用的缓存 */
|
||||
using?: string;
|
||||
/** 下次绘制需要写入的缓存 */
|
||||
writing?: string;
|
||||
|
||||
/**
|
||||
* 在之后的绘制中使用缓存,如果缓存不存在,那么就不使用缓存,并在下一次绘制结束后写入
|
||||
* @param index 缓存的索引,不填时表示不使用缓存
|
||||
*/
|
||||
useCache(index?: string): void;
|
||||
|
||||
/**
|
||||
* 将下次绘制写入缓存并使用
|
||||
* @param index 缓存的索引,不填时表示不进行缓存
|
||||
*/
|
||||
cache(index?: string): void;
|
||||
|
||||
/**
|
||||
* 清除指定或所有缓存
|
||||
* @param index 要清除的缓存,不填代表清除所有
|
||||
*/
|
||||
clearCache(index?: string): void;
|
||||
}
|
||||
|
||||
export interface IRenderUpdater {
|
||||
/**
|
||||
* 更新这个渲染元素
|
||||
@ -46,11 +19,6 @@ export interface IRenderUpdater {
|
||||
update(item?: RenderItem): void;
|
||||
}
|
||||
|
||||
export interface ICanvasCachedRenderItem {
|
||||
/** 离屏画布,首先渲染到它上面,然后由Renderer渲染到最终画布上 */
|
||||
canvas: MotaOffscreenCanvas2D;
|
||||
}
|
||||
|
||||
interface IRenderAnchor {
|
||||
anchorX: number;
|
||||
anchorY: number;
|
||||
@ -89,13 +57,13 @@ export interface IRenderChildable {
|
||||
* 向这个元素添加子元素
|
||||
* @param child 添加的元素
|
||||
*/
|
||||
appendChild(...child: RenderItem[]): void;
|
||||
appendChild(...child: RenderItem<any>[]): void;
|
||||
|
||||
/**
|
||||
* 移除这个元素中的某个子元素
|
||||
* @param child 要移除的元素
|
||||
*/
|
||||
removeChild(...child: RenderItem[]): void;
|
||||
removeChild(...child: RenderItem<any>[]): void;
|
||||
|
||||
/**
|
||||
* 对子元素进行排序
|
||||
@ -142,11 +110,11 @@ interface IRenderTickerSupport {
|
||||
removeTicker(id: number, callEnd?: boolean): boolean;
|
||||
}
|
||||
|
||||
interface RenderItemEvent {
|
||||
beforeUpdate: (item?: RenderItem) => void;
|
||||
afterUpdate: (item?: RenderItem) => void;
|
||||
beforeRender: () => void;
|
||||
afterRender: () => void;
|
||||
export interface ERenderItemEvent {
|
||||
beforeUpdate: [item?: RenderItem];
|
||||
afterUpdate: [item?: RenderItem];
|
||||
beforeRender: [transform: Transform];
|
||||
afterRender: [transform: Transform];
|
||||
}
|
||||
|
||||
interface TickerDelegation {
|
||||
@ -158,11 +126,9 @@ const beforeFrame: (() => void)[] = [];
|
||||
const afterFrame: (() => void)[] = [];
|
||||
const renderFrame: (() => void)[] = [];
|
||||
|
||||
// todo: 添加模型变换
|
||||
export abstract class RenderItem
|
||||
extends EventEmitter<RenderItemEvent>
|
||||
export abstract class RenderItem<E extends ERenderItemEvent = ERenderItemEvent>
|
||||
extends EventEmitter<ERenderItemEvent | E>
|
||||
implements
|
||||
IRenderCache,
|
||||
IRenderUpdater,
|
||||
IRenderAnchor,
|
||||
IRenderConfig,
|
||||
@ -178,22 +144,17 @@ export abstract class RenderItem
|
||||
/** ticker委托id */
|
||||
static tickerId: number = 0;
|
||||
|
||||
/** 元素纵深,表示了遮挡关系 */
|
||||
zIndex: number = 0;
|
||||
|
||||
x: number = 0;
|
||||
y: number = 0;
|
||||
width: number = 200;
|
||||
height: number = 200;
|
||||
|
||||
cacheList: Map<string, MotaOffscreenCanvas2D> = new Map();
|
||||
|
||||
using?: string;
|
||||
writing?: string;
|
||||
|
||||
// 渲染锚点,(0,0)表示左上角,(1,1)表示右下角
|
||||
anchorX: number = 0;
|
||||
anchorY: number = 0;
|
||||
|
||||
/** 渲染模式,absolute表示绝对位置,static表示跟随摄像机移动,只对顶层元素有效 */
|
||||
/** 渲染模式,absolute表示绝对位置,static表示跟随摄像机移动 */
|
||||
type: 'absolute' | 'static' = 'static';
|
||||
/** 是否是高清画布 */
|
||||
highResolution: boolean = true;
|
||||
@ -202,57 +163,92 @@ export abstract class RenderItem
|
||||
/** 是否被隐藏 */
|
||||
hidden: boolean = false;
|
||||
|
||||
/** 当前元素的父元素 */
|
||||
parent?: RenderItem & IRenderChildable;
|
||||
|
||||
protected needUpdate: boolean = false;
|
||||
|
||||
constructor() {
|
||||
/** 该渲染元素的模型变换矩阵 */
|
||||
transform: Transform = new Transform();
|
||||
|
||||
/** 渲染缓存信息 */
|
||||
private cache: MotaOffscreenCanvas2D = new MotaOffscreenCanvas2D();
|
||||
/** 是否需要更新缓存 */
|
||||
private cacheDirty: boolean = true;
|
||||
/** 是否启用缓存机制 */
|
||||
readonly enableCache: boolean = true;
|
||||
|
||||
constructor(enableCache: boolean = true) {
|
||||
super();
|
||||
|
||||
// this.using = '@default';
|
||||
this.enableCache = enableCache;
|
||||
this.cache.withGameScale(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 渲染这个对象
|
||||
* 渲染函数
|
||||
* @param canvas 渲染至的画布
|
||||
* @param camera 渲染时使用的摄像机
|
||||
* @param transform 当前变换矩阵的,渲染时已经进行变换处理,不需要对画布再次进行变换处理
|
||||
* 此参数可用于自己对元素进行变换处理,也会用于对子元素的处理。
|
||||
* 例如对于`absolute`类型的元素,同时有对视角改变的需求,就可以通过此参数进行变换。
|
||||
* 样板内置的`Layer`及`Damage`元素就是通过此方式实现的
|
||||
*/
|
||||
abstract render(canvas: MotaOffscreenCanvas2D, camera: Camera): void;
|
||||
protected abstract render(
|
||||
canvas: MotaOffscreenCanvas2D,
|
||||
transform: Transform
|
||||
): void;
|
||||
|
||||
/**
|
||||
* 修改这个对象的大小
|
||||
*/
|
||||
abstract size(width: number, height: number): void;
|
||||
size(width: number, height: number): void {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.cache.size(width, height);
|
||||
this.cacheDirty = true;
|
||||
this.update(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改这个对象的位置
|
||||
* 渲染当前对象
|
||||
* @param canvas 渲染至的画布
|
||||
* @param transform 父元素的变换矩阵
|
||||
*/
|
||||
abstract pos(x: number, y: number): void;
|
||||
renderContent(canvas: MotaOffscreenCanvas2D, transform: Transform) {
|
||||
if (this.hidden) return;
|
||||
this.emit('beforeRender', transform);
|
||||
this.needUpdate = false;
|
||||
const tran = transform.multiply(this.transform);
|
||||
const ax = -this.anchorX * this.width;
|
||||
const ay = -this.anchorY * this.height;
|
||||
if (this.enableCache) {
|
||||
if (this.cacheDirty) {
|
||||
const cache = this.cache;
|
||||
this.render(cache, tran);
|
||||
this.cache = cache;
|
||||
this.cacheDirty = false;
|
||||
}
|
||||
|
||||
useCache(index?: string): void {
|
||||
if (isNil(index)) {
|
||||
this.using = void 0;
|
||||
return;
|
||||
}
|
||||
if (!this.cacheList.has(index)) {
|
||||
this.writing = index;
|
||||
}
|
||||
this.using = index;
|
||||
}
|
||||
|
||||
cache(index?: string): void {
|
||||
this.writing = index;
|
||||
this.using = index;
|
||||
}
|
||||
|
||||
clearCache(index?: string): void {
|
||||
if (isNil(index)) {
|
||||
this.writing = void 0;
|
||||
this.using = void 0;
|
||||
this.cacheList.clear();
|
||||
canvas.ctx.save();
|
||||
if (this.type === 'static') transformCanvas(canvas, tran);
|
||||
canvas.ctx.drawImage(this.cache.canvas, ax, ay);
|
||||
canvas.ctx.restore();
|
||||
} else {
|
||||
this.cacheList.delete(index);
|
||||
canvas.ctx.save();
|
||||
if (this.type === 'static') transformCanvas(canvas, tran);
|
||||
this.render(canvas, tran);
|
||||
canvas.ctx.restore();
|
||||
}
|
||||
this.emit('afterRender', transform);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置这个元素的位置,等效于`transform.setTranslate(x, y)`
|
||||
* @param x 横坐标
|
||||
* @param y 纵坐标
|
||||
*/
|
||||
pos(x: number, y: number) {
|
||||
this.transform.setTranslate(x, y);
|
||||
}
|
||||
|
||||
setAnchor(x: number, y: number): void {
|
||||
@ -260,19 +256,22 @@ export abstract class RenderItem
|
||||
this.anchorY = y;
|
||||
}
|
||||
|
||||
update(item?: RenderItem): void {
|
||||
update(item?: RenderItem<any>): void {
|
||||
if (this.needUpdate) return;
|
||||
this.needUpdate = true;
|
||||
this.cacheDirty = true;
|
||||
this.parent?.update(item);
|
||||
}
|
||||
|
||||
setHD(hd: boolean): void {
|
||||
this.highResolution = hd;
|
||||
this.cache.setHD(hd);
|
||||
this.update(this);
|
||||
}
|
||||
|
||||
setAntiAliasing(anti: boolean): void {
|
||||
this.antiAliasing = anti;
|
||||
this.cache.setAntiAliasing(anti);
|
||||
this.update(this);
|
||||
}
|
||||
|
||||
@ -429,54 +428,13 @@ Mota.require('var', 'hook').once('reset', () => {
|
||||
});
|
||||
});
|
||||
|
||||
export function withCacheRender(
|
||||
item: RenderItem & ICanvasCachedRenderItem,
|
||||
_canvas: HTMLCanvasElement,
|
||||
ctx: CanvasRenderingContext2D,
|
||||
camera: Camera,
|
||||
fn: RenderFunction
|
||||
) {
|
||||
const { width, height } = item;
|
||||
const ax = width * item.anchorX;
|
||||
const ay = height * item.anchorY;
|
||||
let write = item.writing;
|
||||
if (!isNil(item.using) && isNil(item.writing)) {
|
||||
const cache = item.cacheList.get(item.using);
|
||||
if (cache) {
|
||||
ctx.drawImage(
|
||||
cache.canvas,
|
||||
item.x - ax,
|
||||
item.y - ay,
|
||||
item.width,
|
||||
item.height
|
||||
);
|
||||
return;
|
||||
}
|
||||
write = item.using;
|
||||
}
|
||||
const { canvas: c, ctx: ct } = item.canvas;
|
||||
ct.clearRect(0, 0, c.width, c.height);
|
||||
fn(item.canvas, camera);
|
||||
if (!isNil(write)) {
|
||||
const cache = item.cacheList.get(write);
|
||||
if (cache) {
|
||||
const { canvas, ctx } = cache;
|
||||
ctx.drawImage(c, 0, 0, canvas.width, canvas.height);
|
||||
} else {
|
||||
item.cacheList.set(write, MotaOffscreenCanvas2D.clone(item.canvas));
|
||||
}
|
||||
}
|
||||
|
||||
ctx.drawImage(c, item.x - ax, item.y - ay, item.width, item.height);
|
||||
}
|
||||
|
||||
export function transformCanvas(
|
||||
canvas: MotaOffscreenCanvas2D,
|
||||
camera: Camera,
|
||||
transform: Transform,
|
||||
clear: boolean = false
|
||||
) {
|
||||
const { canvas: ca, ctx, scale } = canvas;
|
||||
const mat = camera.mat;
|
||||
const mat = transform.mat;
|
||||
const a = mat[0] * scale;
|
||||
const b = mat[1] * scale;
|
||||
const c = mat[3] * scale;
|
||||
@ -487,6 +445,5 @@ export function transformCanvas(
|
||||
if (clear) {
|
||||
ctx.clearRect(0, 0, ca.width, ca.height);
|
||||
}
|
||||
ctx.translate(ca.width / 2, ca.height / 2);
|
||||
ctx.transform(a, b, c, d, e, f);
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ import {
|
||||
Layer,
|
||||
LayerGroup
|
||||
} from './layer';
|
||||
import { Sprite } from '../sprite';
|
||||
import { ESpriteEvent, Sprite } from '../sprite';
|
||||
import { BlockCacher } from './block';
|
||||
import type {
|
||||
DamageEnemy,
|
||||
@ -14,22 +14,28 @@ import type {
|
||||
MapDamage
|
||||
} from '@/game/enemy/damage';
|
||||
import { MotaOffscreenCanvas2D } from '@/core/fx/canvas2d';
|
||||
import { Camera } from '../camera';
|
||||
import { isNil } from 'lodash-es';
|
||||
import { getDamageColor } from '@/plugin/utils';
|
||||
import { transformCanvas } from '../item';
|
||||
import EventEmitter from 'eventemitter3';
|
||||
import { Transform } from '../transform';
|
||||
|
||||
const ensureFloorDamage = Mota.require('fn', 'ensureFloorDamage');
|
||||
|
||||
export class FloorDamageExtends implements ILayerGroupRenderExtends {
|
||||
interface EFloorDamageEvent {
|
||||
update: [floor: FloorIds];
|
||||
}
|
||||
|
||||
export class FloorDamageExtends
|
||||
extends EventEmitter<EFloorDamageEvent>
|
||||
implements ILayerGroupRenderExtends
|
||||
{
|
||||
id: string = 'floor-damage';
|
||||
|
||||
floorBinder!: LayerGroupFloorBinder;
|
||||
group!: LayerGroup;
|
||||
sprite!: Damage;
|
||||
|
||||
static listenedDamage: Set<FloorDamageExtends> = new Set();
|
||||
|
||||
/**
|
||||
* 立刻刷新伤害渲染
|
||||
*/
|
||||
@ -41,6 +47,7 @@ export class FloorDamageExtends implements ILayerGroupRenderExtends {
|
||||
const enemy = core.status.maps[floor].enemy;
|
||||
|
||||
this.sprite.updateCollection(enemy);
|
||||
this.emit('update', floor);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -89,18 +96,16 @@ export class FloorDamageExtends implements ILayerGroupRenderExtends {
|
||||
);
|
||||
group.removeExtends('floor-damage');
|
||||
}
|
||||
FloorDamageExtends.listenedDamage.add(this);
|
||||
});
|
||||
}
|
||||
|
||||
onDestroy(group: LayerGroup): void {
|
||||
this.floorBinder.off('update', this.onUpdate);
|
||||
this.floorBinder.off('setBlock', this.onSetBlock);
|
||||
FloorDamageExtends.listenedDamage.delete(this);
|
||||
}
|
||||
}
|
||||
|
||||
interface DamageRenderable {
|
||||
export interface DamageRenderable {
|
||||
x: number;
|
||||
y: number;
|
||||
align: CanvasTextAlign;
|
||||
@ -117,7 +122,12 @@ interface DamageCache {
|
||||
symbol: number;
|
||||
}
|
||||
|
||||
export class Damage extends Sprite {
|
||||
interface EDamageEvent extends ESpriteEvent {
|
||||
setMapSize: [width: number, height: number];
|
||||
beforeDamageRender: [need: Set<number>, transform: Transform];
|
||||
}
|
||||
|
||||
export class Damage extends Sprite<EDamageEvent> {
|
||||
mapWidth: number = 0;
|
||||
mapHeight: number = 0;
|
||||
|
||||
@ -188,6 +198,8 @@ export class Damage extends Sprite {
|
||||
this.blockData.set(i, new Map());
|
||||
this.renderable.set(i, new Set());
|
||||
}
|
||||
|
||||
this.emit('setMapSize', width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -385,32 +397,34 @@ export class Damage extends Sprite {
|
||||
/**
|
||||
* 计算需要渲染哪些块
|
||||
*/
|
||||
calNeedRender(camera: Camera) {
|
||||
calNeedRender(transform: Transform) {
|
||||
if (this.parent instanceof LayerGroup) {
|
||||
// 如果处于地图组中,每个地图的渲染区域应该是一样的,因此可以缓存优化
|
||||
return this.parent.cacheNeedRender(camera, this.block);
|
||||
return this.parent.cacheNeedRender(transform, this.block);
|
||||
} else if (this.parent instanceof Layer) {
|
||||
// 如果是地图的子元素,直接调用Layer的计算函数
|
||||
return this.parent.calNeedRender(camera);
|
||||
return this.parent.calNeedRender(transform);
|
||||
} else {
|
||||
return calNeedRenderOf(camera, this.cellSize, this.block);
|
||||
return calNeedRenderOf(transform, this.cellSize, this.block);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 渲染伤害层
|
||||
* @param camera 摄像机
|
||||
* @param transform 变换矩阵
|
||||
*/
|
||||
renderDamage(camera: Camera) {
|
||||
renderDamage(transform: Transform) {
|
||||
// console.time('damage');
|
||||
const { ctx } = this.damageMap;
|
||||
ctx.save();
|
||||
transformCanvas(this.damageMap, camera, true);
|
||||
transformCanvas(this.damageMap, transform, true);
|
||||
|
||||
const { res: render } = this.calNeedRender(camera);
|
||||
const { res: render } = this.calNeedRender(transform);
|
||||
const block = this.block;
|
||||
const cell = this.cellSize;
|
||||
const size = cell * block.blockSize;
|
||||
|
||||
this.emit('beforeDamageRender', render, transform);
|
||||
render.forEach(v => {
|
||||
const [x, y] = block.getBlockXYByIndex(v);
|
||||
const bx = x * block.blockSize;
|
||||
|
@ -107,6 +107,10 @@ export class LayerGroupFloorBinder
|
||||
this.emit('update', floor);
|
||||
}
|
||||
|
||||
getFloor() {
|
||||
return this.bindThisFloor ? core.status.floorId : this.floor!;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置图块
|
||||
*/
|
||||
|
@ -338,7 +338,6 @@ export class HeroRenderer
|
||||
this.moveId = layer.delegateTicker(() => {
|
||||
this.moveTick(Date.now());
|
||||
});
|
||||
console.log(this);
|
||||
}
|
||||
|
||||
onDestroy(layer: Layer): void {
|
||||
|
@ -1,18 +1,13 @@
|
||||
import { MotaOffscreenCanvas2D } from '@/core/fx/canvas2d';
|
||||
import { Container } from '../container';
|
||||
import { Sprite } from '../sprite';
|
||||
import { Camera } from '../camera';
|
||||
import { TimingFn } from 'mutate-animate';
|
||||
import {
|
||||
IAnimateFrame,
|
||||
renderEmits,
|
||||
RenderItem,
|
||||
transformCanvas
|
||||
} from '../item';
|
||||
import { IAnimateFrame, renderEmits, RenderItem } from '../item';
|
||||
import { logger } from '@/core/common/logger';
|
||||
import { AutotileRenderable, RenderableData, texture } from '../cache';
|
||||
import { RenderableData, texture } from '../cache';
|
||||
import { glMatrix } from 'gl-matrix';
|
||||
import { BlockCacher } from './block';
|
||||
import { Transform } from '../transform';
|
||||
|
||||
export interface ILayerGroupRenderExtends {
|
||||
/** 拓展的唯一标识符 */
|
||||
@ -58,13 +53,6 @@ export interface ILayerGroupRenderExtends {
|
||||
*/
|
||||
onEmptyLayer?(group: LayerGroup): void;
|
||||
|
||||
/**
|
||||
* 当渲染数据更新时执行的函数
|
||||
* @param group 目标LayerGroup实例
|
||||
* @param floor 数据更新的楼层
|
||||
*/
|
||||
// onDataUpdate?: (group: LayerGroup, floor: FloorIds) => void;
|
||||
|
||||
/**
|
||||
* 当帧动画更新时执行的函数,例如从第一帧变成第二帧时
|
||||
* @param group 目标LayerGroup实例
|
||||
@ -72,13 +60,6 @@ export interface ILayerGroupRenderExtends {
|
||||
*/
|
||||
onFrameUpdate?(group: LayerGroup, frame: number): void;
|
||||
|
||||
/**
|
||||
* 当绑定楼层时执行的函数
|
||||
* @param group 目标LayerGroup实例
|
||||
* @param floor 绑定的楼层id,为空时表示绑定为当前楼层
|
||||
*/
|
||||
// onBindFloor?: (group: LayerGroup, floor?: FloorIds) => void;
|
||||
|
||||
/**
|
||||
* 当拓展被取消挂载时执行的函数(LayerGroup被销毁,拓展被移除等)
|
||||
* @param group 目标LayerGroup实例
|
||||
@ -267,13 +248,13 @@ export class LayerGroup extends Container implements IAnimateFrame {
|
||||
|
||||
/**
|
||||
* 缓存计算应该渲染的块
|
||||
* @param camera 摄像机
|
||||
* @param transform 变换矩阵
|
||||
* @param blockData 分块信息
|
||||
*/
|
||||
cacheNeedRender(camera: Camera, block: BlockCacher<any>) {
|
||||
cacheNeedRender(transform: Transform, block: BlockCacher<any>) {
|
||||
return (
|
||||
this.needRender ??
|
||||
(this.needRender = calNeedRenderOf(camera, this.cellSize, block))
|
||||
(this.needRender = calNeedRenderOf(transform, this.cellSize, block))
|
||||
);
|
||||
}
|
||||
|
||||
@ -288,9 +269,6 @@ export class LayerGroup extends Container implements IAnimateFrame {
|
||||
* 更新动画帧
|
||||
*/
|
||||
updateFrameAnimate() {
|
||||
this.layers.forEach(v => {
|
||||
v.cache(v.using);
|
||||
});
|
||||
this.update(this);
|
||||
|
||||
for (const ex of this.extend.values()) {
|
||||
@ -308,7 +286,7 @@ export class LayerGroup extends Container implements IAnimateFrame {
|
||||
}
|
||||
|
||||
export function calNeedRenderOf(
|
||||
camera: Camera,
|
||||
transform: Transform,
|
||||
cell: number,
|
||||
block: BlockCacher<any>
|
||||
): NeedRenderData {
|
||||
@ -317,10 +295,10 @@ export function calNeedRenderOf(
|
||||
const size = block.blockSize;
|
||||
|
||||
// -1是因为宽度是core._PX_,从0开始的话,末尾索引就是core._PX_ - 1
|
||||
const [x1, y1] = Camera.untransformed(camera, -w, -h);
|
||||
const [x2, y2] = Camera.untransformed(camera, w - 1, -h);
|
||||
const [x3, y3] = Camera.untransformed(camera, w - 1, h - 1);
|
||||
const [x4, y4] = Camera.untransformed(camera, -w, h - 1);
|
||||
const [x1, y1] = Transform.untransformed(transform, -w, -h);
|
||||
const [x2, y2] = Transform.untransformed(transform, w - 1, -h);
|
||||
const [x3, y3] = Transform.untransformed(transform, w - 1, h - 1);
|
||||
const [x4, y4] = Transform.untransformed(transform, -w, h - 1);
|
||||
|
||||
const res: Set<number> = new Set();
|
||||
/** 一个纵坐标对应的所有横坐标,用于填充 */
|
||||
@ -469,8 +447,6 @@ export interface ILayerRenderExtends {
|
||||
*/
|
||||
onBackgroundSet?(layer: Layer, background: AllNumbers): void;
|
||||
|
||||
// onBackgroundBind?: (layer: Layer, floorId: FloorIds) => void;
|
||||
|
||||
/**
|
||||
* 当背景图块图片被生成时执行的函数
|
||||
* @param layer 目标Layer实例
|
||||
@ -568,18 +544,26 @@ export interface ILayerRenderExtends {
|
||||
/**
|
||||
* 在地图渲染之前执行的函数
|
||||
* @param layer 目标Layer实例
|
||||
* @param camera 渲染的摄像头信息
|
||||
* @param transform 渲染的变换矩阵
|
||||
* @param need 需要渲染的分块信息
|
||||
*/
|
||||
onBeforeRender?(layer: Layer, camera: Camera, need: NeedRenderData): void;
|
||||
onBeforeRender?(
|
||||
layer: Layer,
|
||||
transform: Transform,
|
||||
need: NeedRenderData
|
||||
): void;
|
||||
|
||||
/**
|
||||
* 在地图渲染之后执行的函数
|
||||
* @param layer 目标Layer实例
|
||||
* @param camera 渲染的摄像头信息
|
||||
* @param transform 渲染的变换矩阵
|
||||
* @param need 需要渲染的分块信息
|
||||
*/
|
||||
onAfterRender?(layer: Layer, camera: Camera, need: NeedRenderData): void;
|
||||
onAfterRender?(
|
||||
layer: Layer,
|
||||
transform: Transform,
|
||||
need: NeedRenderData
|
||||
): void;
|
||||
|
||||
/**
|
||||
* 当拓展被取消挂载时执行的函数(Layer被销毁,拓展被移除等)
|
||||
@ -606,38 +590,6 @@ interface NeedRenderData {
|
||||
back: [x: number, y: number][];
|
||||
}
|
||||
|
||||
interface MovingStepLinearSwap {
|
||||
/** 线性差值移动(也就是平移)或者是瞬移 */
|
||||
type: 'linear' | 'swap';
|
||||
x: number;
|
||||
y: number;
|
||||
/** 这次移动的总时长,不是每格时长 */
|
||||
time?: number;
|
||||
}
|
||||
|
||||
interface MovingStepFunction {
|
||||
/** 自定义移动方式 */
|
||||
type: 'fn';
|
||||
/**
|
||||
* 移动函数,返回一个三元素数组,表示当前所在格子数,以及在纵向上的深度(一般图块的深度就是它的纵坐标),
|
||||
* 注意不是像素数,可以是小数
|
||||
*/
|
||||
fn: TimingFn<3>;
|
||||
time?: number;
|
||||
relative?: boolean;
|
||||
}
|
||||
|
||||
interface MovingBlock {
|
||||
/** 当前横坐标 */
|
||||
x: number;
|
||||
/** 当前纵坐标 */
|
||||
y: number;
|
||||
/** 渲染信息 */
|
||||
render: RenderableData | AutotileRenderable;
|
||||
/** 当前的纵深 */
|
||||
nowZ: number;
|
||||
}
|
||||
|
||||
export class Layer extends Container {
|
||||
// 一些会用到的常量
|
||||
static readonly FRAME_0 = 1;
|
||||
@ -654,7 +606,7 @@ export class Layer extends Container {
|
||||
protected backMap: MotaOffscreenCanvas2D = new MotaOffscreenCanvas2D();
|
||||
|
||||
/** 最终渲染至的Sprite */
|
||||
main: Sprite = new Sprite();
|
||||
main: Sprite = new Sprite('static', false);
|
||||
|
||||
/** 渲染的层 */
|
||||
layer?: FloorLayer;
|
||||
@ -714,13 +666,13 @@ export class Layer extends Container {
|
||||
this.main.size(core._PX_, core._PY_);
|
||||
|
||||
this.appendChild(this.main);
|
||||
this.main.setRenderFn((canvas, camera) => {
|
||||
this.main.setRenderFn((canvas, transform) => {
|
||||
const { ctx } = canvas;
|
||||
const { width, height } = canvas.canvas;
|
||||
ctx.save();
|
||||
ctx.imageSmoothingEnabled = false;
|
||||
const need = this.calNeedRender(camera);
|
||||
this.renderMap(camera, need);
|
||||
const need = this.calNeedRender(transform);
|
||||
this.renderMap(transform, need);
|
||||
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
||||
ctx.drawImage(this.backMap.canvas, 0, 0, width, height);
|
||||
ctx.drawImage(this.staticMap.canvas, 0, 0, width, height);
|
||||
@ -930,8 +882,6 @@ export class Layer extends Container {
|
||||
this.updateBlocks(x, y, width, height);
|
||||
this.updateBigImages(x, y, width, height);
|
||||
|
||||
// this.update(this);
|
||||
|
||||
for (const ex of this.extend.values()) {
|
||||
ex.onDataPut?.(this, data, width, x, y, calAutotile);
|
||||
}
|
||||
@ -1109,15 +1059,15 @@ export class Layer extends Container {
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算在传入的摄像机的视角下,应该渲染哪些内容
|
||||
* @param camera 摄像机
|
||||
* 计算在传入的变换矩阵下,应该渲染哪些内容
|
||||
* @param transform 变换矩阵
|
||||
*/
|
||||
calNeedRender(camera: Camera): NeedRenderData {
|
||||
calNeedRender(transform: Transform): NeedRenderData {
|
||||
if (this.parent instanceof LayerGroup) {
|
||||
// 如果处于地图组中,每个地图的渲染区域应该是一样的,因此可以缓存优化
|
||||
return this.parent.cacheNeedRender(camera, this.block);
|
||||
return this.parent.cacheNeedRender(transform, this.block);
|
||||
} else {
|
||||
return calNeedRenderOf(camera, this.cellSize, this.block);
|
||||
return calNeedRenderOf(transform, this.cellSize, this.block);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1138,7 +1088,7 @@ export class Layer extends Container {
|
||||
/**
|
||||
* 渲染当前地图
|
||||
*/
|
||||
renderMap(camera: Camera, need: NeedRenderData) {
|
||||
renderMap(transform: Transform, need: NeedRenderData) {
|
||||
this.staticMap.clear();
|
||||
this.movingMap.clear();
|
||||
this.backMap.clear();
|
||||
@ -1146,32 +1096,31 @@ export class Layer extends Container {
|
||||
if (this.needUpdateMoving) this.updateMovingRenderable();
|
||||
|
||||
for (const ex of this.extend.values()) {
|
||||
ex.onBeforeRender?.(this, camera, need);
|
||||
ex.onBeforeRender?.(this, transform, need);
|
||||
}
|
||||
this.renderBack(camera, need);
|
||||
this.renderStatic(camera, need);
|
||||
this.renderMoving(camera);
|
||||
this.renderBack(transform, need);
|
||||
this.renderStatic(transform, need);
|
||||
this.renderMoving(transform);
|
||||
for (const ex of this.extend.values()) {
|
||||
ex.onAfterRender?.(this, camera, need);
|
||||
ex.onAfterRender?.(this, transform, need);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 渲染背景图
|
||||
* @param camera 摄像机
|
||||
* @param transform 变换矩阵
|
||||
* @param need 需要渲染的块
|
||||
*/
|
||||
protected renderBack(camera: Camera, need: NeedRenderData) {
|
||||
protected renderBack(transform: Transform, need: NeedRenderData) {
|
||||
const cell = this.cellSize;
|
||||
const frame = (RenderItem.animatedFrame % 4) + 1;
|
||||
const blockSize = this.block.blockSize;
|
||||
const { back } = need;
|
||||
const { ctx } = this.backMap;
|
||||
|
||||
const mat = camera.mat;
|
||||
const mat = transform.mat;
|
||||
const [a, b, , c, d, , e, f] = mat;
|
||||
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
||||
ctx.translate(core._PX_ / 2, core._PY_ / 2);
|
||||
ctx.transform(a, b, c, d, e, f);
|
||||
|
||||
if (this.background !== 0) {
|
||||
@ -1195,7 +1144,7 @@ export class Layer extends Container {
|
||||
/**
|
||||
* 渲染静态层
|
||||
*/
|
||||
protected renderStatic(camera: Camera, need: NeedRenderData) {
|
||||
protected renderStatic(transform: Transform, need: NeedRenderData) {
|
||||
const cell = this.cellSize;
|
||||
const frame = RenderItem.animatedFrame % 4;
|
||||
const { width } = this.block.blockData;
|
||||
@ -1205,7 +1154,9 @@ export class Layer extends Container {
|
||||
ctx.save();
|
||||
|
||||
const { res: render } = need;
|
||||
transformCanvas(this.staticMap, camera, false);
|
||||
const [a, b, , c, d, , e, f] = transform.mat;
|
||||
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
||||
ctx.transform(a, b, c, d, e, f);
|
||||
|
||||
render.forEach(v => {
|
||||
const x = v % width;
|
||||
@ -1277,17 +1228,16 @@ export class Layer extends Container {
|
||||
/**
|
||||
* 渲染移动/大怪物层
|
||||
*/
|
||||
protected renderMoving(camera: Camera) {
|
||||
protected renderMoving(transform: Transform) {
|
||||
const frame = (RenderItem.animatedFrame % 4) + 1;
|
||||
const cell = this.cellSize;
|
||||
const halfCell = cell / 2;
|
||||
const { ctx } = this.movingMap;
|
||||
|
||||
ctx.save();
|
||||
const mat = camera.mat;
|
||||
const mat = transform.mat;
|
||||
const [a, b, , c, d, , e, f] = mat;
|
||||
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
||||
ctx.translate(core._PX_ / 2, core._PY_ / 2);
|
||||
ctx.transform(a, b, c, d, e, f);
|
||||
const max1 = Math.max(a, b, c, d) ** 2;
|
||||
const max2 = Math.max(core._PX_, core._PY_) * 2;
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { MotaOffscreenCanvas2D } from '@/core/fx/canvas2d';
|
||||
import { Sprite } from '../sprite';
|
||||
|
||||
type CanvasStyle = string | CanvasGradient | CanvasPattern;
|
||||
@ -12,6 +13,8 @@ export class Text extends Sprite {
|
||||
private length: number = 0;
|
||||
private descent: number = 0;
|
||||
|
||||
private static measureCanvas = new MotaOffscreenCanvas2D();
|
||||
|
||||
constructor(text: string = '') {
|
||||
super();
|
||||
|
||||
@ -39,11 +42,10 @@ export class Text extends Sprite {
|
||||
* 获取文字的长度
|
||||
*/
|
||||
measure() {
|
||||
this.canvas.ctx.save();
|
||||
this.canvas.ctx.textBaseline = 'bottom';
|
||||
this.canvas.ctx.font = this.font ?? '';
|
||||
const res = this.canvas.ctx.measureText(this.text);
|
||||
this.canvas.ctx.restore();
|
||||
const ctx = Text.measureCanvas.ctx;
|
||||
ctx.textBaseline = 'bottom';
|
||||
ctx.font = this.font ?? '';
|
||||
const res = ctx.measureText(this.text);
|
||||
return res;
|
||||
}
|
||||
|
||||
@ -53,8 +55,6 @@ export class Text extends Sprite {
|
||||
*/
|
||||
setText(text: string) {
|
||||
this.text = text;
|
||||
this.writing = this.using;
|
||||
this.using = void 0;
|
||||
this.calBox();
|
||||
if (this.parent) this.update(this);
|
||||
}
|
||||
@ -101,8 +101,6 @@ export class Image extends Sprite {
|
||||
constructor(image: SizedCanvasImageSource) {
|
||||
super();
|
||||
this.image = image;
|
||||
this.canvas.withGameScale(false);
|
||||
this.canvas.setHD(false);
|
||||
this.size(image.width, image.height);
|
||||
|
||||
this.renderFn = ({ canvas, ctx }) => {
|
||||
@ -117,8 +115,6 @@ export class Image extends Sprite {
|
||||
setImage(image: SizedCanvasImageSource) {
|
||||
this.image = image;
|
||||
this.size(image.width, image.height);
|
||||
this.writing = this.using;
|
||||
this.using = void 0;
|
||||
this.update(this);
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,7 @@
|
||||
import { Animation, hyper, sleep } from 'mutate-animate';
|
||||
import { MotaCanvas2D, MotaOffscreenCanvas2D } from '../fx/canvas2d';
|
||||
import { Camera } from './camera';
|
||||
import { Container } from './container';
|
||||
import { RenderItem, transformCanvas, withCacheRender } from './item';
|
||||
import { RenderItem, transformCanvas } from './item';
|
||||
import { FloorLayer, Layer, LayerGroup } from './preset/layer';
|
||||
import { LayerGroupFloorBinder } from './preset/floor';
|
||||
import { FloorDamageExtends } from './preset/damage';
|
||||
@ -11,11 +10,6 @@ import { HeroRenderer } from './preset/hero';
|
||||
export class MotaRenderer extends Container {
|
||||
static list: Set<MotaRenderer> = new Set();
|
||||
|
||||
canvas: MotaOffscreenCanvas2D;
|
||||
camera: Camera;
|
||||
|
||||
/** 摄像机缓存,如果是需要快速切换摄像机的场景,使用缓存可以大幅提升性能表现 */
|
||||
cameraCache: Map<Camera, MotaOffscreenCanvas2D> = new Map();
|
||||
target: MotaCanvas2D;
|
||||
/** 这个渲染对象的id */
|
||||
id: string;
|
||||
@ -23,19 +17,14 @@ export class MotaRenderer extends Container {
|
||||
protected needUpdate: boolean = false;
|
||||
|
||||
constructor(id: string = 'render-main') {
|
||||
super();
|
||||
super('static', false);
|
||||
|
||||
this.id = id;
|
||||
|
||||
this.canvas = new MotaOffscreenCanvas2D();
|
||||
this.camera = new Camera();
|
||||
this.target = new MotaCanvas2D(id);
|
||||
this.width = core._PX_;
|
||||
this.height = core._PY_;
|
||||
this.size(core._PX_, core._PY_);
|
||||
this.target.withGameScale(true);
|
||||
this.target.size(core._PX_, core._PY_);
|
||||
this.canvas.withGameScale(true);
|
||||
this.canvas.size(core._PX_, core._PY_);
|
||||
this.target.css(`z-index: 100`);
|
||||
|
||||
MotaRenderer.list.add(this);
|
||||
@ -47,62 +36,61 @@ export class MotaRenderer extends Container {
|
||||
* @param noCache 是否不使用缓存,当切换至的目标摄像机相比切走时发生了例如位置的变化时,一般需要设置为true,
|
||||
* 否则会使用上一次被切换走时的缓存
|
||||
*/
|
||||
useCamera(camera: Camera, noCache: boolean = false) {
|
||||
const cache = MotaOffscreenCanvas2D.clone(this.canvas);
|
||||
this.cameraCache.set(this.camera, cache);
|
||||
this.camera = camera;
|
||||
const nowCache = this.cameraCache.get(camera);
|
||||
if (!nowCache || !noCache) this.render();
|
||||
else this.renderCache(nowCache);
|
||||
}
|
||||
// useCamera(camera: Camera, noCache: boolean = false) {
|
||||
// const cache = MotaOffscreenCanvas2D.clone(this.canvas);
|
||||
// this.cameraCache.set(this.camera, cache);
|
||||
// this.camera = camera;
|
||||
// const nowCache = this.cameraCache.get(camera);
|
||||
// if (!nowCache || !noCache) this.render();
|
||||
// else this.renderCache(nowCache);
|
||||
// }
|
||||
|
||||
/**
|
||||
* 删除某个摄像机的画面缓存
|
||||
* @param camera 要删除的缓存对应的摄像机
|
||||
*/
|
||||
freeCameraCache(camera: Camera) {
|
||||
this.cameraCache.delete(camera);
|
||||
}
|
||||
// freeCameraCache(camera: Camera) {
|
||||
// this.cameraCache.delete(camera);
|
||||
// }
|
||||
|
||||
/**
|
||||
* 渲染游戏画面
|
||||
*/
|
||||
render() {
|
||||
// console.time();
|
||||
const { canvas, ctx } = this.target;
|
||||
const camera = this.camera;
|
||||
this.emit('beforeRender');
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
withCacheRender(this, canvas, ctx, camera, canvas => {
|
||||
const { canvas: ca, ctx: ct, scale } = canvas;
|
||||
this.sortedChildren.forEach(v => {
|
||||
if (v.hidden) return;
|
||||
if (v.type === 'absolute') {
|
||||
ct.setTransform(scale, 0, 0, scale, 0, 0);
|
||||
} else {
|
||||
transformCanvas(canvas, camera, false);
|
||||
}
|
||||
ct.save();
|
||||
if (!v.antiAliasing) {
|
||||
ctx.imageSmoothingEnabled = false;
|
||||
ct.imageSmoothingEnabled = false;
|
||||
} else {
|
||||
ctx.imageSmoothingEnabled = true;
|
||||
ct.imageSmoothingEnabled = true;
|
||||
}
|
||||
v.render(this.target, camera);
|
||||
ct.restore();
|
||||
});
|
||||
});
|
||||
this.emit('afterRender');
|
||||
// console.timeEnd();
|
||||
}
|
||||
// protected render(canvas: MotaOffscreenCanvas2D, transform: Transform) {
|
||||
// console.time();
|
||||
// const { canvas, ctx } = this.target;
|
||||
// const camera = this.camera;
|
||||
// this.emit('beforeRender', camera);
|
||||
// ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
// withCacheRender(this, canvas, ctx, camera, canvas => {
|
||||
// const { canvas: ca, ctx: ct, scale } = canvas;
|
||||
// this.sortedChildren.forEach(v => {
|
||||
// if (v.hidden) return;
|
||||
// if (v.type === 'absolute') {
|
||||
// ct.setTransform(scale, 0, 0, scale, 0, 0);
|
||||
// } else {
|
||||
// transformCanvas(canvas, camera, false);
|
||||
// }
|
||||
// ct.save();
|
||||
// if (!v.antiAliasing) {
|
||||
// ctx.imageSmoothingEnabled = false;
|
||||
// ct.imageSmoothingEnabled = false;
|
||||
// } else {
|
||||
// ctx.imageSmoothingEnabled = true;
|
||||
// ct.imageSmoothingEnabled = true;
|
||||
// }
|
||||
// v.renderContent(this.target, camera);
|
||||
// ct.restore();
|
||||
// });
|
||||
// });
|
||||
// this.emit('afterRender', camera);
|
||||
// console.timeEnd();
|
||||
// }
|
||||
|
||||
update(item?: RenderItem) {
|
||||
if (this.needUpdate) return;
|
||||
this.needUpdate = true;
|
||||
this.requestRenderFrame(() => {
|
||||
this.cache(this.using);
|
||||
this.needUpdate = false;
|
||||
this.refresh(item);
|
||||
});
|
||||
@ -110,20 +98,12 @@ export class MotaRenderer extends Container {
|
||||
|
||||
protected refresh(item?: RenderItem): void {
|
||||
this.emit('beforeUpdate', item);
|
||||
this.render();
|
||||
// console.time();
|
||||
this.render(this.target, this.transform);
|
||||
// console.timeEnd();
|
||||
this.emit('afterUpdate', item);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将缓存内容渲染至画面
|
||||
* @param cache 渲染缓存,是一个离屏Canvas2D对象
|
||||
*/
|
||||
renderCache(cache: MotaOffscreenCanvas2D) {
|
||||
const { canvas, ctx } = this.canvas;
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.width);
|
||||
ctx.drawImage(cache.canvas, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加至游戏画面
|
||||
*/
|
||||
@ -142,7 +122,7 @@ window.addEventListener('resize', () => {
|
||||
|
||||
Mota.require('var', 'hook').once('reset', () => {
|
||||
const render = new MotaRenderer();
|
||||
const camera = render.camera;
|
||||
const transform = render.transform;
|
||||
render.mount();
|
||||
|
||||
const layer = new LayerGroup();
|
||||
@ -174,8 +154,8 @@ Mota.require('var', 'hook').once('reset', () => {
|
||||
// }
|
||||
// );
|
||||
|
||||
camera.move(240, 240);
|
||||
transform.move(240, 240);
|
||||
render.update();
|
||||
|
||||
console.log(layer);
|
||||
console.log(render);
|
||||
});
|
||||
|
@ -1,76 +1,39 @@
|
||||
import { Camera } from './camera';
|
||||
import {
|
||||
ICanvasCachedRenderItem,
|
||||
ERenderItemEvent,
|
||||
RenderFunction,
|
||||
RenderItem,
|
||||
RenderItemPosition,
|
||||
withCacheRender
|
||||
RenderItemPosition
|
||||
} from './item';
|
||||
import { MotaOffscreenCanvas2D } from '../fx/canvas2d';
|
||||
import { Transform } from './transform';
|
||||
|
||||
export class Sprite extends RenderItem implements ICanvasCachedRenderItem {
|
||||
export interface ESpriteEvent extends ERenderItemEvent {}
|
||||
|
||||
export class Sprite<E extends ESpriteEvent = ESpriteEvent> extends RenderItem<
|
||||
E | ESpriteEvent
|
||||
> {
|
||||
renderFn: RenderFunction;
|
||||
|
||||
canvas: MotaOffscreenCanvas2D;
|
||||
|
||||
private readonly enableCache: boolean;
|
||||
|
||||
/**
|
||||
* 创建一个精灵,可以自由在上面渲染内容
|
||||
* @param type 渲染模式,absolute表示绝对位置,static表示跟随摄像机移动,只对顶层元素有效
|
||||
* @param cache 是否启用缓存机制
|
||||
*/
|
||||
constructor(type: RenderItemPosition = 'static', cache: boolean = true) {
|
||||
super();
|
||||
super(cache);
|
||||
this.type = type;
|
||||
this.enableCache = cache;
|
||||
this.renderFn = () => {};
|
||||
this.canvas = new MotaOffscreenCanvas2D();
|
||||
this.canvas.withGameScale(true);
|
||||
}
|
||||
|
||||
render(canvas: MotaOffscreenCanvas2D, camera: Camera): void {
|
||||
this.emit('beforeRender');
|
||||
if (this.needUpdate) {
|
||||
this.cache(this.using);
|
||||
this.needUpdate = false;
|
||||
}
|
||||
if (this.enableCache) {
|
||||
withCacheRender(this, canvas.canvas, canvas.ctx, camera, canvas => {
|
||||
this.renderFn(canvas, camera);
|
||||
});
|
||||
} else {
|
||||
this.renderFn(canvas, camera);
|
||||
}
|
||||
this.writing = void 0;
|
||||
this.emit('afterRender');
|
||||
}
|
||||
|
||||
size(width: number, height: number) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.canvas.size(width, height);
|
||||
this.update(this);
|
||||
}
|
||||
|
||||
pos(x: number, y: number) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
protected render(
|
||||
canvas: MotaOffscreenCanvas2D,
|
||||
transform: Transform
|
||||
): void {
|
||||
this.renderFn(canvas, transform);
|
||||
}
|
||||
|
||||
setRenderFn(fn: RenderFunction) {
|
||||
this.renderFn = fn;
|
||||
}
|
||||
|
||||
setHD(hd: boolean): void {
|
||||
this.highResolution = hd;
|
||||
this.canvas.setHD(hd);
|
||||
this.update(this);
|
||||
}
|
||||
|
||||
setAntiAliasing(anti: boolean): void {
|
||||
this.antiAliasing = anti;
|
||||
this.canvas.setAntiAliasing(anti);
|
||||
this.update(this);
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,6 @@
|
||||
import { ReadonlyMat3, ReadonlyVec3, mat3, vec2, vec3 } from 'gl-matrix';
|
||||
import { EventEmitter } from '../common/eventEmitter';
|
||||
import { mat3, ReadonlyMat3, ReadonlyVec3, vec2, vec3 } from 'gl-matrix';
|
||||
|
||||
interface CameraEvent {}
|
||||
|
||||
export class Camera extends EventEmitter<CameraEvent> {
|
||||
export class Transform {
|
||||
mat: mat3 = mat3.create();
|
||||
|
||||
x: number = 0;
|
||||
@ -12,10 +9,11 @@ export class Camera extends EventEmitter<CameraEvent> {
|
||||
scaleY: number = 1;
|
||||
rad: number = 0;
|
||||
|
||||
private saveStack: number[][] = [];
|
||||
/** 有没有对这个Transform进行过修改,用于优化常规表现 */
|
||||
private modified: boolean = false;
|
||||
|
||||
/**
|
||||
* 重设摄像机的所有参数
|
||||
* 重设所有参数
|
||||
*/
|
||||
reset() {
|
||||
mat3.identity(this.mat);
|
||||
@ -24,28 +22,31 @@ export class Camera extends EventEmitter<CameraEvent> {
|
||||
this.scaleX = 1;
|
||||
this.scaleY = 1;
|
||||
this.rad = 0;
|
||||
this.modified = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改摄像机的缩放,叠加关系
|
||||
* 修改缩放,叠加关系
|
||||
*/
|
||||
scale(x: number, y: number = x) {
|
||||
mat3.scale(this.mat, this.mat, [x, y]);
|
||||
this.scaleX *= x;
|
||||
this.scaleY *= y;
|
||||
this.modified = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 移动摄像机,叠加关系
|
||||
* 移动,叠加关系
|
||||
*/
|
||||
move(x: number, y: number) {
|
||||
mat3.translate(this.mat, this.mat, [-x, -y]);
|
||||
this.x += x;
|
||||
this.y += y;
|
||||
this.modified = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 旋转摄像机,叠加关系
|
||||
* 旋转,叠加关系
|
||||
*/
|
||||
rotate(rad: number) {
|
||||
mat3.rotate(this.mat, this.mat, rad);
|
||||
@ -54,36 +55,40 @@ export class Camera extends EventEmitter<CameraEvent> {
|
||||
const n = Math.floor(this.rad / Math.PI / 2);
|
||||
this.rad -= n * Math.PI * 2;
|
||||
}
|
||||
this.modified = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置摄像机的缩放,非叠加关系
|
||||
* 设置缩放,非叠加关系
|
||||
*/
|
||||
setScale(x: number, y: number = x) {
|
||||
mat3.scale(this.mat, this.mat, [x / this.scaleX, y / this.scaleY]);
|
||||
this.scaleX = x;
|
||||
this.scaleY = y;
|
||||
this.modified = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置摄像机的位置,非叠加关系
|
||||
* 设置位置,非叠加关系
|
||||
*/
|
||||
setTranslate(x: number, y: number) {
|
||||
mat3.translate(this.mat, this.mat, [this.x - x, this.y - y]);
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.modified = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置摄像机的旋转,非叠加关系
|
||||
* 设置旋转,非叠加关系
|
||||
*/
|
||||
setRotate(rad: number) {
|
||||
mat3.rotate(this.mat, this.mat, rad - this.rad);
|
||||
this.rad = rad;
|
||||
this.modified = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置摄像机的变换矩阵,叠加模式
|
||||
* 设置变换矩阵,叠加模式
|
||||
* @param a 水平缩放
|
||||
* @param b 垂直倾斜
|
||||
* @param c 水平倾斜
|
||||
@ -108,7 +113,7 @@ export class Camera extends EventEmitter<CameraEvent> {
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置摄像机的变换矩阵,非叠加模式
|
||||
* 设置变换矩阵,非叠加模式
|
||||
* @param a 水平缩放
|
||||
* @param b 垂直倾斜
|
||||
* @param c 水平倾斜
|
||||
@ -140,46 +145,62 @@ export class Camera extends EventEmitter<CameraEvent> {
|
||||
this.scaleX = scaleX;
|
||||
this.scaleY = scaleY;
|
||||
this.rad = rad;
|
||||
if (x === 0 && y === 0 && scaleX === 1 && scaleY === 1 && rad === 0) {
|
||||
this.modified = false;
|
||||
} else {
|
||||
this.modified = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存当前摄像机状态
|
||||
* 与一个矩阵相乘,返回其计算结果(不改变原矩阵)
|
||||
* @param transform 变换矩阵
|
||||
*/
|
||||
save() {
|
||||
this.saveStack.push(Array.from(this.mat));
|
||||
multiply(transform: Transform): Transform {
|
||||
if (this.modified) {
|
||||
const res = new Transform();
|
||||
const mat = mat3.clone(this.mat);
|
||||
mat3.multiply(mat, mat, transform.mat);
|
||||
res.mat = mat;
|
||||
return res;
|
||||
} else {
|
||||
return transform.clone();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 回退当前摄像机状态
|
||||
* 复制这个变换矩阵
|
||||
*/
|
||||
restore() {
|
||||
const data = this.saveStack.pop();
|
||||
if (!data) return;
|
||||
const [a, b, c, d, e, f, g, h, i] = data;
|
||||
this.mat = mat3.fromValues(a, b, c, d, e, f, g, h, i);
|
||||
clone() {
|
||||
const transform = new Transform();
|
||||
transform.mat = mat3.clone(this.mat);
|
||||
return transform;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据摄像机的信息,将一个点转换为计算后的位置
|
||||
* @param camera 摄像机
|
||||
* @param transform 摄像机
|
||||
* @param x 横坐标
|
||||
* @param y 纵坐标
|
||||
*/
|
||||
static transformed(camera: Camera, x: number, y: number) {
|
||||
return multiplyVec3(camera.mat, [x, y, 1]);
|
||||
static transformed(transform: Transform, x: number, y: number) {
|
||||
return multiplyVec3(transform.mat, [x, y, 1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据摄像机的信息,将一个计算后的位置逆转换为原位置
|
||||
* @param camera 摄像机
|
||||
* @param transform 摄像机
|
||||
* @param x 横坐标
|
||||
* @param y 纵坐标
|
||||
*/
|
||||
static untransformed(camera: Camera, x: number, y: number) {
|
||||
static untransformed(transform: Transform, x: number, y: number) {
|
||||
const invert = mat3.create();
|
||||
mat3.invert(invert, camera.mat);
|
||||
mat3.invert(invert, transform.mat);
|
||||
return multiplyVec3(invert, [x, y, 1]);
|
||||
}
|
||||
|
||||
/** 单位矩阵 */
|
||||
static readonly identity = new Transform();
|
||||
}
|
||||
|
||||
function multiplyVec3(mat: ReadonlyMat3, vec: ReadonlyVec3) {
|
@ -10,6 +10,7 @@ import * as hero from './state/hero';
|
||||
import * as miscMechanism from './mechanism/misc';
|
||||
import * as study from './mechanism/study';
|
||||
import { registerPresetState } from './state/preset';
|
||||
import { ItemState } from './state/item';
|
||||
|
||||
// ----- 类注册
|
||||
Mota.register('class', 'DamageEnemy', damage.DamageEnemy);
|
||||
@ -32,6 +33,9 @@ Mota.register('module', 'Mechanism', {
|
||||
BluePalace: miscMechanism.BluePalace,
|
||||
Study: study
|
||||
});
|
||||
Mota.register('module', 'State', {
|
||||
ItemState
|
||||
});
|
||||
|
||||
main.loading = loading;
|
||||
|
||||
|
@ -35,6 +35,7 @@ import type { Camera } from '@/core/render/camera';
|
||||
import type { Image, Text } from '@/core/render/preset/misc';
|
||||
import type { RenderItem } from '@/core/render/item';
|
||||
import type { RenderAdapter } from '@/core/render/adapter';
|
||||
import type { ItemState } from './state/item';
|
||||
|
||||
interface ClassInterface {
|
||||
// 渲染进程与游戏进程通用
|
||||
@ -115,6 +116,9 @@ interface ModuleInterface {
|
||||
RenderItem: typeof RenderItem;
|
||||
RenderAdapter: typeof RenderAdapter;
|
||||
};
|
||||
State: {
|
||||
ItemState: typeof ItemState;
|
||||
};
|
||||
}
|
||||
|
||||
interface SystemInterfaceMap {
|
||||
|
@ -261,8 +261,6 @@ export function init() {
|
||||
core.autosave();
|
||||
}
|
||||
|
||||
console.trace();
|
||||
|
||||
moveDir = direction;
|
||||
stepDir = direction;
|
||||
await readyMove();
|
||||
|
Loading…
Reference in New Issue
Block a user