refactor: 部分渲染系统的结构

This commit is contained in:
unanmed 2025-01-29 16:04:54 +08:00
parent 19a0c29015
commit b9f4804c8c
15 changed files with 220 additions and 169 deletions

View File

@ -97,10 +97,10 @@ class TextureCache extends EventEmitter<TextureCacheEvent> {
Mota.require('var', 'loading').once('loaded', () => {
const map = maps_90f36752_8815_4be8_b32b_d7fad1d0542e;
// @ts-ignore
// @ts-expect-error 无法推导
this.idNumberMap = {};
for (const [key, { id }] of Object.entries(map)) {
// @ts-ignore
// @ts-expect-error 无法推导
this.idNumberMap[id] = parseInt(key) as AllNumbers;
}
this.tileset = core.material.images.tilesets;
@ -128,7 +128,7 @@ class TextureCache extends EventEmitter<TextureCacheEvent> {
*/
private calRenderable() {
const map = maps_90f36752_8815_4be8_b32b_d7fad1d0542e;
for (const [key, data] of Object.entries(map)) {
for (const key of Object.keys(map)) {
this.calRenderableByNum(parseInt(key));
}
}
@ -167,7 +167,8 @@ class TextureCache extends EventEmitter<TextureCacheEvent> {
const data = map[num as Exclude<AllNumbers, 0>];
// 地狱般的分支if
if (data) {
let { cls, faceIds, bigImage, id, animate } = data;
let { faceIds, bigImage } = data;
const { cls, id, animate } = data;
if (cls === 'enemys' || cls === 'enemy48') {
// 怪物需要特殊处理,因为它的大怪物信息不在 maps 里面
({ bigImage, faceIds } = enemys[id as EnemyIds]);
@ -212,7 +213,7 @@ class TextureCache extends EventEmitter<TextureCacheEvent> {
if (cls === 'enemy48' || cls === 'npc48') {
const img = core.material.images[cls];
if (!img) return null;
// @ts-ignore
// @ts-expect-error 无法推导
const line = icons[cls][id];
const w = 32;
const h = 48;
@ -269,7 +270,7 @@ class TextureCache extends EventEmitter<TextureCacheEvent> {
if (!image) return null;
const frame = core.getAnimateFrames(cls);
const cell = 32;
// @ts-ignore
// @ts-expect-error 无法推导
const offset = (icons[cls][id] as number) * cell;
const render: [number, number, number, number][] = [
[0, offset, cell, cell]

52
src/core/render/frame.ts Normal file
View File

@ -0,0 +1,52 @@
import EventEmitter from 'eventemitter3';
import { RenderItem } from './item';
export interface IAnimateFrame {
updateFrameAnimate(frame: number, time: number): void;
}
interface RenderEvent {
animateFrame: [frame: number, time: number];
}
class RenderEmits extends EventEmitter<RenderEvent> {
private framer: Set<IAnimateFrame> = new Set();
/**
*
*/
addFramer(framer: IAnimateFrame) {
this.framer.add(framer);
}
/**
*
*/
removeFramer(framer: IAnimateFrame) {
this.framer.delete(framer);
}
/**
*
* @param frame
* @param time
*/
emitAnimateFrame(frame: number, time: number) {
this.framer.forEach(v => v.updateFrameAnimate(frame, time));
this.emit('animateFrame', frame, time);
}
}
export const renderEmits = new RenderEmits();
Mota.require('var', 'hook').once('reset', () => {
let lastTime = 0;
RenderItem.ticker.add(time => {
if (!core.isPlaying()) return;
if (time - lastTime > core.values.animateSpeed) {
RenderItem.animatedFrame++;
lastTime = time;
renderEmits.emitAnimateFrame(RenderItem.animatedFrame, time);
}
});
});

View File

@ -101,4 +101,4 @@ export * from './shader';
export * from './sprite';
export * from './transform';
export * from './utils';
export * from '../../module/ui/components';
export * from './event';

View File

@ -5,6 +5,7 @@ import { Ticker, TickerFn } from 'mutate-animate';
import { Transform } from './transform';
import { logger } from '../common/logger';
import { ElementNamespace, ComponentInternalInstance } from 'vue';
import { transformCanvas } from './utils';
export type RenderFunction = (
canvas: MotaOffscreenCanvas2D,
@ -21,7 +22,7 @@ export interface IRenderUpdater {
update(item?: RenderItem): void;
}
interface IRenderAnchor {
export interface IRenderAnchor {
/** 锚点横坐标0表示最左端1表示最右端 */
anchorX: number;
/** 锚点纵坐标0表示最上端1表示最下端 */
@ -35,7 +36,7 @@ interface IRenderAnchor {
setAnchor(x: number, y: number): void;
}
interface IRenderConfig {
export interface IRenderConfig {
/** 是否是高清画布 */
highResolution: boolean;
/** 是否启用抗锯齿 */
@ -76,7 +77,7 @@ export interface IRenderChildable {
requestSort(): void;
}
interface IRenderFrame {
export interface IRenderFrame {
/**
*
* @param fn
@ -96,7 +97,7 @@ interface IRenderFrame {
requestRenderFrame(fn: () => void): void;
}
interface IRenderTickerSupport {
export interface IRenderTickerSupport {
/**
* ticker
* @param fn
@ -121,7 +122,7 @@ interface IRenderTickerSupport {
hasTicker(id: number): boolean;
}
interface IRenderVueSupport {
export interface IRenderVueSupport {
/**
* jsx, vue
* @param key
@ -139,12 +140,114 @@ interface IRenderVueSupport {
): void;
}
export const enum MouseType {
/** 没有按键按下 */
None = 0,
/** 左键 */
Left = 1 << 0,
/** 中键,即按下滚轮 */
Middle = 1 << 1,
/** 右键 */
Right = 1 << 2,
/** 侧键后退 */
Back = 1 << 3,
/** 侧键前进 */
Forward = 1 << 4
}
export const enum WheelType {
None,
/** 以像素为单位 */
Pixel,
/** 以行为单位,每行长度视浏览器设置而定,约为 1rem */
Line,
/** 以页为单位,一般为一个屏幕高度 */
Page
}
export interface IActionEvent {
/** 当前事件是监听的哪个元素 */
readonly target: RenderItem;
/** 这次操作的标识符,在按下、移动、抬起阶段中保持不变 */
readonly identifier: number;
/** 相对于触发元素左上角的横坐标 */
readonly offsetX: number;
/** 相对于触发元素左上角的纵坐标 */
readonly offsetY: number;
/** 相对于整个画布左上角的横坐标 */
readonly absoluteX: number;
/** 相对于整个画布左上角的纵坐标 */
readonly absoluteY: number;
/**
* {@link MouseType.None}
* {@link MouseType}
*/
readonly type: MouseType;
/**
*
* `buttons & MouseType.Left`
*/
readonly buttons: number;
/** 触发时是否按下了 alt 键 */
readonly altKey: boolean;
/** 触发时是否按下了 shift 键 */
readonly shiftKey: boolean;
/** 触发时是否按下了 ctrl 键 */
readonly ctrlKey: boolean;
/** 触发时是否按下了 Windows(Windows) / Command(Mac) 键 */
readonly metaKey: boolean;
/**
*
*
*
*/
stopPropagation(): void;
}
export interface IWheelEvent extends IActionEvent {
/** 滚轮事件的鼠标横向滚动量 */
readonly wheelX: number;
/** 滚轮事件的鼠标纵向滚动量 */
readonly wheelY: number;
/** 滚轮事件的鼠标垂直屏幕的滚动量 */
readonly wheelZ: number;
/** 滚轮事件的滚轮类型,表示了对应值的单位 */
readonly wheelType: WheelType;
}
export interface ERenderItemEvent {
beforeRender: [transform: Transform];
afterRender: [transform: Transform];
destroy: [];
/** 当这个元素被点击时触发 */
clickCapture: [x: number, y: number, type: number, ev: MouseEvent];
/** 当这个元素被点击时的捕获阶段触发 */
clickCapture: [ev: IActionEvent];
/** 当这个元素被点击时的冒泡阶段触发 */
click: [ev: IActionEvent];
/** 当鼠标或手指在该元素上按下的捕获阶段触发 */
downCapture: [ev: IActionEvent];
/** 当鼠标或手指在该元素上按下的冒泡阶段触发 */
down: [ev: IActionEvent];
/** 当鼠标或手指在该元素上移动的捕获阶段触发 */
moveCapture: [ev: IActionEvent];
/** 当鼠标或手指在该元素上移动的冒泡阶段触发 */
move: [ev: IActionEvent];
/** 当鼠标或手指在该元素上抬起的捕获阶段触发 */
upCapture: [ev: IActionEvent];
/** 当鼠标或手指在该元素上抬起的冒泡阶段触发 */
up: [ev: IActionEvent];
/** 当鼠标或手指进入该元素的捕获阶段触发 */
enterCapture: [ev: IActionEvent];
/** 当鼠标或手指进入该元素的冒泡阶段触发 */
enter: [ev: IActionEvent];
/** 当鼠标或手指离开该元素的捕获阶段触发 */
leaveCapture: [ev: IActionEvent];
/** 当鼠标或手指离开该元素的冒泡阶段触发 */
leave: [ev: IActionEvent];
/** 当鼠标滚轮时的捕获阶段触发 */
wheelCapture: [ev: IWheelEvent];
/** 当鼠标滚轮时的冒泡阶段触发 */
wheel: [ev: IWheelEvent];
}
interface TickerDelegation {
@ -205,8 +308,9 @@ export abstract class RenderItem<E extends ERenderItemEvent = ERenderItemEvent>
width: number = 200;
height: number = 200;
// 渲染锚点,(0,0)表示左上角,(1,1)表示右下角
/** 渲染锚点,(0,0)表示左上角,(1,1)表示右下角 */
anchorX: number = 0;
/** 渲染锚点,(0,0)表示左上角,(1,1)表示右下角 */
anchorY: number = 0;
/** 渲染模式absolute表示绝对位置static表示跟随摄像机移动 */
@ -740,63 +844,3 @@ RenderItem.ticker.add(time => {
arr.forEach(v => v());
}
});
export interface IAnimateFrame {
updateFrameAnimate(frame: number, time: number): void;
}
interface RenderEvent {
animateFrame: [frame: number, time: number];
}
class RenderEmits extends EventEmitter<RenderEvent> {
private framer: Set<IAnimateFrame> = new Set();
/**
*
*/
addFramer(framer: IAnimateFrame) {
this.framer.add(framer);
}
/**
*
*/
removeFramer(framer: IAnimateFrame) {
this.framer.delete(framer);
}
/**
*
* @param frame
* @param time
*/
emitAnimateFrame(frame: number, time: number) {
this.framer.forEach(v => v.updateFrameAnimate(frame, time));
this.emit('animateFrame', frame, time);
}
}
export const renderEmits = new RenderEmits();
Mota.require('var', 'hook').once('reset', () => {
let lastTime = 0;
RenderItem.ticker.add(time => {
if (!core.isPlaying()) return;
if (time - lastTime > core.values.animateSpeed) {
RenderItem.animatedFrame++;
lastTime = time;
renderEmits.emitAnimateFrame(RenderItem.animatedFrame, time);
}
});
});
export function transformCanvas(
canvas: MotaOffscreenCanvas2D,
transform: Transform
) {
const { ctx } = canvas;
const mat = transform.mat;
const [a, b, , c, d, , e, f] = mat;
ctx.transform(a, b, c, d, e, f);
}

View File

@ -1,44 +0,0 @@
import { logger } from '../common/logger';
import { MotaOffscreenCanvas2D } from '../fx/canvas2d';
export class CanvasPool {
private pool: MotaOffscreenCanvas2D[] = [];
private requested: Set<MotaOffscreenCanvas2D> = new Set();
/**
*
* @param num
*/
requestCanvas(num: number): MotaOffscreenCanvas2D[] {
if (this.pool.length < num) {
const diff = num - this.pool.length;
for (let i = 0; i < diff; i++) {
this.pool.push(new MotaOffscreenCanvas2D(false));
}
}
const toProvide = this.pool.splice(0, num);
toProvide.forEach(v => this.requested.add(v));
return toProvide;
}
/**
* 退
* @param canvas 退
*/
returnCanvas(canvas: MotaOffscreenCanvas2D[]) {
canvas.forEach(v => {
if (!this.requested.has(v)) {
logger.warn(40);
return;
}
this.requested.delete(v);
this.pool.push(v);
v.clear();
});
}
destroy() {
this.pool.forEach(v => v.delete());
this.requested.forEach(v => v.delete());
}
}

View File

@ -1,11 +1,10 @@
import { logger } from '@/core/common/logger';
import { RenderAdapter } from '../adapter';
import { Sprite } from '../sprite';
import { HeroRenderer } from './hero';
import { ILayerGroupRenderExtends, LayerGroup } from './layer';
import { MotaOffscreenCanvas2D } from '@/core/fx/canvas2d';
import { ERenderItemEvent, RenderItem, transformCanvas } from '../item';
import { ERenderItemEvent, RenderItem } from '../item';
import { Transform } from '../transform';
import { transformCanvas } from '../utils';
export class LayerGroupAnimate implements ILayerGroupRenderExtends {
static animateList: Set<LayerGroupAnimate> = new Set();

View File

@ -6,13 +6,7 @@ import {
Layer,
LayerGroup
} from './layer';
import { ESpriteEvent, Sprite } from '../sprite';
import {
BlockCacher,
CanvasCacheItem,
IBlockCacheable,
ICanvasCacheItem
} from './block';
import { BlockCacher, CanvasCacheItem, ICanvasCacheItem } from './block';
import type {
DamageEnemy,
EnemyCollection,
@ -21,10 +15,11 @@ import type {
import { MotaOffscreenCanvas2D } from '@/core/fx/canvas2d';
import { isNil } from 'lodash-es';
import { getDamageColor } from '@/plugin/utils';
import { ERenderItemEvent, RenderItem, transformCanvas } from '../item';
import { ERenderItemEvent, RenderItem } from '../item';
import EventEmitter from 'eventemitter3';
import { Transform } from '../transform';
import { ElementNamespace, ComponentInternalInstance } from 'vue';
import { transformCanvas } from '../utils';
const ensureFloorDamage = Mota.require('fn', 'ensureFloorDamage');
@ -75,22 +70,22 @@ export class FloorDamageExtends
this.update(floor);
};
private onSetBlock = (x: number, y: number, floor: FloorIds) => {
// this.sprite.enemy?.once('extract', () => {
// if (floor !== this.sprite.enemy?.floorId) return;
// this.sprite.updateBlocks();
// });
// if (!this.floorBinder.bindThisFloor) {
// this.sprite.enemy?.extract();
// }
};
// private onSetBlock = (x: number, y: number, floor: FloorIds) => {
// this.sprite.enemy?.once('extract', () => {
// if (floor !== this.sprite.enemy?.floorId) return;
// this.sprite.updateBlocks();
// });
// if (!this.floorBinder.bindThisFloor) {
// this.sprite.enemy?.extract();
// }
// };
/**
*
*/
private listen() {
this.floorBinder.on('update', this.onUpdate);
this.floorBinder.on('setBlock', this.onSetBlock);
// this.floorBinder.on('setBlock', this.onSetBlock);
}
awake(group: LayerGroup): void {
@ -106,9 +101,9 @@ export class FloorDamageExtends
}
}
onDestroy(group: LayerGroup): void {
onDestroy(_group: LayerGroup): void {
this.floorBinder.off('update', this.onUpdate);
this.floorBinder.off('setBlock', this.onSetBlock);
// this.floorBinder.off('setBlock', this.onSetBlock);
}
}
@ -259,7 +254,7 @@ export class Damage extends RenderItem<EDamageEvent> {
blocks.forEach(v => this.dirtyBlocks.add(v));
this.emit('updateBlocks', blocks);
} else {
this.blockData.forEach((v, i) => {
this.blockData.forEach((_v, i) => {
this.dirtyBlocks.add(i);
});
this.emit('updateBlocks', new Set(this.blockData.keys()));
@ -473,7 +468,6 @@ export class Damage extends RenderItem<EDamageEvent> {
// console.time('damage');
const { ctx } = canvas;
transformCanvas(canvas, transform);
// console.trace();
const render = this.calNeedRender(transform);
const block = this.block;

View File

@ -170,7 +170,7 @@ export class LayerGroupFloorBinder
LayerGroupFloorBinder.activedBinder.add(this);
}
onLayerAdd(group: LayerGroup, layer: Layer): void {
onLayerAdd(_group: LayerGroup, layer: Layer): void {
this.checkLayerExtends(layer);
}
@ -300,7 +300,7 @@ export class LayerFloorBinder implements ILayerRenderExtends {
this.checkListen();
}
onDestroy(layer: Layer) {
onDestroy(_layer: Layer) {
LayerFloorBinder.listenedBinder.delete(this);
this.parent?.layerBinders.delete(this);
}
@ -397,11 +397,11 @@ export class LayerDoorAnimate implements ILayerRenderExtends {
doorAdapter.add(this);
}
onMovingUpdate(layer: Layer, renderable: LayerMovingRenderable[]): void {
onMovingUpdate(_layer: Layer, renderable: LayerMovingRenderable[]): void {
renderable.push(...this.moving);
}
onDestroy(layer: Layer): void {
onDestroy(_layer: Layer): void {
doorAdapter.remove(this);
}
}

View File

@ -354,8 +354,8 @@ export class HeroRenderer
moveAs(x: number, y: number, time: number, fn: TimingFn<3>): Promise<void> {
if (!this.moving) return Promise.resolve();
if (!this.renderable) return Promise.resolve();
let nowZIndex = fn(0)[2];
let startTime = Date.now();
const nowZIndex = fn(0)[2];
const startTime = Date.now();
return new Promise(res => {
this.layer.delegateTicker(
() => {
@ -414,7 +414,7 @@ export class HeroRenderer
layer.removeTicker(this.moveId);
}
onMovingUpdate(layer: Layer, renderable: LayerMovingRenderable[]): void {
onMovingUpdate(_layer: Layer, renderable: LayerMovingRenderable[]): void {
if (this.renderable) {
renderable.push(this.renderable);
this.emit('append', renderable);

View File

@ -2,7 +2,7 @@ import { MotaOffscreenCanvas2D } from '@/core/fx/canvas2d';
import { Container, EContainerEvent } from '../container';
import { Sprite } from '../sprite';
import { TimingFn } from 'mutate-animate';
import { IAnimateFrame, renderEmits, RenderItem } from '../item';
import { RenderItem } from '../item';
import { logger } from '@/core/common/logger';
import { RenderableData, texture } from '../cache';
import { BlockCacher, CanvasCacheItem, ICanvasCacheItem } from './block';
@ -11,6 +11,7 @@ import { LayerFloorBinder, LayerGroupFloorBinder } from './floor';
import { RenderAdapter } from '../adapter';
import { ElementNamespace, ComponentInternalInstance } from 'vue';
import { Camera } from '../camera';
import { IAnimateFrame, renderEmits } from '../frame';
export interface ILayerGroupRenderExtends {
/** 拓展的唯一标识符 */

View File

@ -1,17 +1,12 @@
import { MotaOffscreenCanvas2D } from '@/core/fx/canvas2d';
import {
ERenderItemEvent,
IAnimateFrame,
renderEmits,
RenderItem,
RenderItemPosition
} from '../item';
import { ERenderItemEvent, RenderItem, RenderItemPosition } from '../item';
import { Transform } from '../transform';
import { ElementNamespace, ComponentInternalInstance } from 'vue';
import { AutotileRenderable, RenderableData } from '../cache';
import { texture } from '../cache';
import { isNil } from 'lodash-es';
import { logger } from '@/core/common/logger';
import { IAnimateFrame, renderEmits } from '../frame';
type CanvasStyle = string | CanvasGradient | CanvasPattern;

View File

@ -1,4 +1,3 @@
import { logger } from '@/core/common/logger';
import { HeroRenderer } from './hero';
import { ILayerGroupRenderExtends, LayerGroup } from './layer';
import { LayerGroupFloorBinder } from './floor';
@ -162,7 +161,7 @@ export class FloorViewport implements ILayerGroupRenderExtends {
let yStartTime: number = Date.now();
let ending: boolean = false;
// 这个数等于 sinh(2)用这个数的话可以正好在刚开始移动的时候达到1的斜率效果会比较好
let transitionTime = this.hero!.speed * 3.626860407847019;
const transitionTime = this.hero!.speed * 3.626860407847019;
const setTargetX = (x: number, time: number) => {
if (x === xTarget) return;

View File

@ -1,5 +1,4 @@
import { MotaOffscreenCanvas2D } from '../fx/canvas2d';
import { ERenderItemEvent, RenderItem, RenderItemPosition } from './item';
import { Transform } from './transform';
import { EGL2Event, GL2, GL2Program, IGL2ProgramPrefix } from './gl2';
@ -57,8 +56,8 @@ export class Shader<E extends EShaderEvent = EShaderEvent> extends GL2<
protected preDraw(
canvas: MotaOffscreenCanvas2D,
transform: Transform,
gl: WebGL2RenderingContext,
_transform: Transform,
_gl: WebGL2RenderingContext,
program: GL2Program
): boolean {
if (!program.modified) return false;
@ -74,10 +73,10 @@ export class Shader<E extends EShaderEvent = EShaderEvent> extends GL2<
}
protected postDraw(
canvas: MotaOffscreenCanvas2D,
transform: Transform,
gl: WebGL2RenderingContext,
program: GL2Program
_canvas: MotaOffscreenCanvas2D,
_transform: Transform,
_gl: WebGL2RenderingContext,
_program: GL2Program
): void {}
}

View File

@ -7,7 +7,6 @@ import {
import { MotaOffscreenCanvas2D } from '../fx/canvas2d';
import { Transform } from './transform';
import { ElementNamespace, ComponentInternalInstance } from 'vue';
import { logger } from '../common/logger';
export interface ESpriteEvent extends ERenderItemEvent {}

View File

@ -3,6 +3,8 @@ import { RenderAdapter } from './adapter';
import { FloorViewport } from './preset/viewport';
import { JSX } from 'vue/jsx-runtime';
import { DefineComponent, DefineSetupFnComponent } from 'vue';
import { MotaOffscreenCanvas2D } from '../fx/canvas2d';
import { Transform } from './transform';
export type Props<
T extends
@ -50,3 +52,13 @@ export function isSetEqual<T>(set1: Set<T>, set2: Set<T>) {
if (set1 === set2) return true;
else return set1.size === set2.size && set1.isSubsetOf(set2);
}
export function transformCanvas(
canvas: MotaOffscreenCanvas2D,
transform: Transform
) {
const { ctx } = canvas;
const mat = transform.mat;
const [a, b, , c, d, , e, f] = mat;
ctx.transform(a, b, c, d, e, f);
}