From 27923851bd93037b35cbebd5b9a666a64861a877 Mon Sep 17 00:00:00 2001 From: unanmed <1319491857@qq.com> Date: Mon, 26 Aug 2024 23:59:58 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=85=89=E5=BD=B1=E6=95=88=E6=9E=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core/fx/canvas2d.ts | 78 +++++++++++++++++- src/core/fx/shadow.ts | 141 +++++++++++++++++++++++++-------- src/core/render/gl.ts | 40 ++++++++++ src/core/render/index.ts | 3 + src/core/render/preset/hero.ts | 5 ++ src/plugin/game/fallback.ts | 24 ------ 6 files changed, 230 insertions(+), 61 deletions(-) create mode 100644 src/core/render/gl.ts diff --git a/src/core/fx/canvas2d.ts b/src/core/fx/canvas2d.ts index 5a7d3e5..3ec7d60 100644 --- a/src/core/fx/canvas2d.ts +++ b/src/core/fx/canvas2d.ts @@ -1,6 +1,7 @@ import { parseCss } from '@/plugin/utils'; import { EventEmitter } from 'eventemitter3'; import { CSSObj } from '../interface'; +import { isWebGL2Supported } from './webgl'; interface OffscreenCanvasEvent { /** 当被动触发resize时(例如core.domStyle.scale变化、窗口大小变化)时触发,使用size函数并不会触发 */ @@ -36,8 +37,6 @@ export class MotaOffscreenCanvas2D extends EventEmitter { this.width = this.canvas.width / devicePixelRatio; this.height = this.canvas.height / devicePixelRatio; - this.canvas.style.position = 'absolute'; - MotaOffscreenCanvas2D.list.add(this); } @@ -121,6 +120,74 @@ export class MotaOffscreenCanvas2D extends EventEmitter { } } +export class MotaOffscreenCanvasGL2 extends EventEmitter { + static support: boolean = isWebGL2Supported(); + static list: Set = new Set(); + + canvas: HTMLCanvasElement; + gl: WebGL2RenderingContext; + + width: number; + height: number; + + /** 是否自动跟随样板的core.domStyle.scale进行缩放 */ + autoScale: boolean = false; + /** 是否是高清画布 */ + highResolution: boolean = true; + + scale: number = 1; + + /** 更新标识符,如果发生变化则说明画布被动清空 */ + symbol: number = 0; + + constructor() { + super(); + + this.canvas = document.createElement('canvas'); + this.gl = this.canvas.getContext('webgl2')!; + this.width = this.canvas.width / devicePixelRatio; + this.height = this.canvas.height / devicePixelRatio; + } + + /** + * 设置画布的大小 + */ + size(width: number, height: number) { + let ratio = this.highResolution ? devicePixelRatio : 1; + if (this.autoScale && this.highResolution) { + ratio *= core.domStyle.scale; + } + this.scale = ratio; + this.canvas.width = width * ratio; + this.canvas.height = height * ratio; + this.width = width; + this.height = height; + } + + /** + * 设置当前画布是否跟随样板的 core.domStyle.scale 一同进行缩放 + */ + withGameScale(auto: boolean) { + this.autoScale = auto; + this.size(this.width, this.height); + } + + /** + * 设置当前画布是否为高清画布 + */ + setHD(hd: boolean) { + this.highResolution = hd; + this.size(this.width, this.height); + } + + /** + * 删除这个画布 + */ + delete() { + MotaOffscreenCanvasGL2.list.delete(this); + } +} + export class MotaCanvas2D extends MotaOffscreenCanvas2D { static map: Map = new Map(); @@ -255,5 +322,12 @@ window.addEventListener('resize', () => { v.emit('resize'); } }); + MotaOffscreenCanvasGL2.list.forEach(v => { + if (v.autoScale) { + v.size(v.width, v.height); + v.symbol++; + v.emit('resize'); + } + }); }); }); diff --git a/src/core/fx/shadow.ts b/src/core/fx/shadow.ts index 800f25e..c89d715 100644 --- a/src/core/fx/shadow.ts +++ b/src/core/fx/shadow.ts @@ -6,6 +6,9 @@ import { isWebGL2Supported } from './webgl'; import { setCanvasFilterByFloorId } from '@/plugin/fx/gameCanvas'; +import { ILayerRenderExtends, Layer } from '../render/preset/layer'; +import { HeroRenderer } from '../render/preset/hero'; +import { Sprite } from '../render/sprite'; /** * 最大光源数量,必须设置,且光源数不能超过这个值,这个值决定了会预留多少的缓冲区,因此最好尽可能小,同时游戏过程中不可修改 @@ -70,7 +73,9 @@ function addLightFromBlock(floors: FloorIds[], block: number, config: LightConfi }) } -Mota.require('var', 'hook').once('reset', () => { +const hook = Mota.require('var', 'hook'); + +hook.once('reset', () => { Shadow.init(); addLightFromBlock( core.floorIds.slice(61, 70).concat(core.floorIds.slice(72, 81)).concat(core.floorIds.slice(85, 103)), @@ -85,48 +90,62 @@ Mota.require('var', 'hook').once('reset', () => { { decay: 20, r: 150, color: [0.9333, 0.6, 0.333, 0.4], noShelter: true }, { background: [0, 0, 0, 0.4] } ); - Shadow.mount(); + // Shadow.mount(); // 勇士身上的光源 - Mota.rewrite(core.control, 'drawHero', 'add', () => { - if (core.getFlag('__heroOpacity__') !== 0) { - const shadow = Shadow.now(); - if (shadow) { - shadow.followHero.forEach(v => { - shadow.modifyLight(v, { - x: core.status.heroCenter.px, - y: core.status.heroCenter.py + 8 - }); - }); - if (shadow.followHero.size > 0) shadow.requestRefresh(); - } - } - }); + // Mota.rewrite(core.control, 'drawHero', 'add', () => { + // if (core.getFlag('__heroOpacity__') !== 0) { + // const shadow = Shadow.now(); + // if (shadow) { + // shadow.followHero.forEach(v => { + // shadow.modifyLight(v, { + // x: core.status.heroCenter.px, + // y: core.status.heroCenter.py + 8 + // }); + // }); + // if (shadow.followHero.size > 0) shadow.requestRefresh(); + // } + // } + // }); // 更新地形数据 - Mota.rewrite(core.maps, 'removeBlock', 'add', success => { - if (success && !main.replayChecking) { - Shadow.update(true); - } - return success; - }); - Mota.rewrite(core.maps, 'setBlock', 'add', () => { - if (!main.replayChecking) { - Shadow.update(true); - } - }); + // Mota.rewrite(core.maps, 'removeBlock', 'add', success => { + // if (success && !main.replayChecking) { + // Shadow.update(true); + // } + // return success; + // }); + // Mota.rewrite(core.maps, 'setBlock', 'add', () => { + // if (!main.replayChecking) { + // Shadow.update(true); + // } + // }); Mota.rewrite(core.control, 'loadData', 'add', () => { if (!main.replayChecking) { Shadow.update(true); } }); - Mota.require('var', 'hook').on('changingFloor', (floorId) => { - if (!main.replayChecking) { - Shadow.clearBuffer(); - Shadow.update(); - setCanvasFilterByFloorId(floorId); - } - }) + // Mota.require('var', 'hook').on('changingFloor', (floorId) => { + // if (!main.replayChecking) { + // Shadow.clearBuffer(); + // Shadow.update(); + // setCanvasFilterByFloorId(floorId); + // } + // }) }); +hook.on('reset', () => { + Shadow.update(true); + LayerShadowExtends.shadowList.forEach(v => v.sprite.update(v.sprite)); +}) +hook.on('setBlock', () => { + Shadow.update(true); + LayerShadowExtends.shadowList.forEach(v => v.sprite.update(v.sprite)); +}) +hook.on('changingFloor', floorId => { + Shadow.clearBuffer(); + Shadow.update(); + setCanvasFilterByFloorId(floorId); + LayerShadowExtends.shadowList.forEach(v => v.sprite.update(v.sprite)); +}) // 深度测试着色器 @@ -977,6 +996,7 @@ export class Shadow { color: this.create2DTexture(core._PX_), blur: this.create2DTexture(core._PX_) } + this.resizeCanvas(); } static resize() { @@ -1007,7 +1027,6 @@ export class Shadow { } static mount() { - this.resizeCanvas(); core.dom.gameDraw.appendChild(this.canvas); } @@ -1281,3 +1300,55 @@ export function calMapWalls(floor: FloorIds, nocache: boolean = false) { } } } + +export class LayerShadowExtends implements ILayerRenderExtends { + static shadowList: Set = new Set(); + id: string = 'shadow'; + + hero!: HeroRenderer + sprite!: Sprite; + + private onMoveTick = (x: number, y: number) => { + const now = Shadow.now(); + if (!now) return; + if (now.followHero.size === 0) return; + now.followHero.forEach(v => { + now.modifyLight(v, { + x: x * 32 + 16, + y: y * 32 + 16 + }); + }); + now.requestRefresh(); + this.sprite.update(this.sprite); + } + + private listen() { + this.hero.on('moveTick', this.onMoveTick); + } + + awake(layer: Layer): void { + const ex = layer.getExtends('floor-hero'); + if (!ex) { + layer.removeExtends('shadow'); + logger.error(1101, `Shadow extends needs 'floor-hero' extends as dependency.`); + return; + } + this.hero = ex as HeroRenderer; + this.listen(); + LayerShadowExtends.shadowList.add(this); + this.sprite = new Sprite('static', false); + this.sprite.setHD(true); + this.sprite.size(layer.width, layer.height); + this.sprite.setRenderFn((canvas, transform) => { + canvas.ctx.drawImage(Shadow.canvas, 0, 0, layer.width, layer.height); + }); + + layer.appendChild(this.sprite); + } + + onDestroy(layer: Layer): void { + this.hero.off('moveTick', this.onMoveTick); + this.sprite.destroy(); + LayerShadowExtends.shadowList.delete(this); + } +} diff --git a/src/core/render/gl.ts b/src/core/render/gl.ts new file mode 100644 index 0000000..15a2bf5 --- /dev/null +++ b/src/core/render/gl.ts @@ -0,0 +1,40 @@ +import { MotaOffscreenCanvas2D, MotaOffscreenCanvasGL2 } from '../fx/canvas2d'; +import { RenderItem } from './item'; +import { Transform } from './transform'; + +type GL2RenderFunc = ( + canvas: MotaOffscreenCanvasGL2, + transform: Transform +) => void; + +export class GL2 extends RenderItem { + canvas: MotaOffscreenCanvasGL2 = new MotaOffscreenCanvasGL2(); + + /** 渲染函数 */ + private renderFn: GL2RenderFunc = () => {}; + + protected render( + canvas: MotaOffscreenCanvas2D, + transform: Transform + ): void { + const gl = this.canvas.gl; + gl.viewport(0, 0, this.canvas.width, this.canvas.height); + + gl.clearColor(0, 0, 0, 0); + gl.clearDepth(1); + gl.enable(gl.DEPTH_TEST); + gl.depthFunc(gl.LEQUAL); + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + + this.renderFn(this.canvas, transform); + canvas.ctx.drawImage(this.canvas.canvas, 0, 0, this.width, this.height); + } + + /** + * 设置这个gl2元素的渲染函数 + * @param fn 渲染函数 + */ + setRenderFn(fn: GL2RenderFunc) { + this.renderFn = fn; + } +} diff --git a/src/core/render/index.ts b/src/core/render/index.ts index 4749343..57d1d83 100644 --- a/src/core/render/index.ts +++ b/src/core/render/index.ts @@ -4,6 +4,7 @@ import { LayerDoorAnimate, LayerGroupFloorBinder } from './preset/floor'; import { HeroRenderer } from './preset/hero'; import { LayerGroup, FloorLayer } from './preset/layer'; import { MotaRenderer } from './render'; +import { LayerShadowExtends } from '../fx/shadow'; let main: MotaRenderer; @@ -27,10 +28,12 @@ Mota.require('var', 'loading').once('loaded', () => { const hero = new HeroRenderer(); const detail = new FloorItemDetail(); const door = new LayerDoorAnimate(); + const shadow = new LayerShadowExtends(); layer.extends(damage); layer.extends(detail); layer.getLayer('event')?.extends(hero); layer.getLayer('event')?.extends(door); + layer.getLayer('event')?.extends(shadow); render.appendChild(layer); }); diff --git a/src/core/render/preset/hero.ts b/src/core/render/preset/hero.ts index 1bb1b5a..feb44ef 100644 --- a/src/core/render/preset/hero.ts +++ b/src/core/render/preset/hero.ts @@ -12,6 +12,7 @@ type HeroMovingStatus = 'stop' | 'moving' | 'moving-as'; interface HeroRenderEvent { stepEnd: []; + moveTick: [x: number, y: number]; } export class HeroRenderer @@ -170,6 +171,7 @@ export class HeroRenderer this.renderable.x = rx; this.renderable.y = ry; } + this.emit('moveTick', this.renderable.x, this.renderable.y); this.renderable.animate = this.movingFrame; this.layer.update(this.layer); } @@ -272,6 +274,7 @@ export class HeroRenderer if (!isNil(y)) { this.renderable.y = y; } + this.emit('moveTick', this.renderable.x, this.renderable.y); this.layer.update(this.layer); } @@ -306,6 +309,7 @@ export class HeroRenderer (a, b) => a.zIndex - b.zIndex ); } + this.emit('moveTick', this.renderable.x, this.renderable.y); this.layer.update(this.layer); }, time, @@ -315,6 +319,7 @@ export class HeroRenderer this.renderable.animate = 0; this.renderable.x = x; this.renderable.y = y; + this.emit('moveTick', this.renderable.x, this.renderable.y); this.layer.update(this.layer); res(); } diff --git a/src/plugin/game/fallback.ts b/src/plugin/game/fallback.ts index f187180..ea98060 100644 --- a/src/plugin/game/fallback.ts +++ b/src/plugin/game/fallback.ts @@ -390,30 +390,6 @@ export function init() { core.animateFrame.asyncId[animate] = cb; this._openDoor_animate(block, x, y, callback); } - - // var blockInfo = core.getBlockInfo(block); - // var speed = (doorInfo.time || 160) / 4; - // blockInfo.posX = 3; - // core.maps._drawBlockInfo(blockInfo, x, y); - - // var animate = window.setInterval( - // function () { - // blockInfo.posX--; - // if (blockInfo.posX < 0) { - // clearInterval(animate); - // delete core.animateFrame.asyncId[animate]; - // cb(); - // return; - // } - // core.maps._drawBlockInfo(blockInfo, x, y); - // }, - // core.status.replay.speed == 24 - // ? 1 - // : speed / Math.max(core.status.replay.speed, 1) - // ); - - // core.animateFrame.lastAsyncId = animate; - // core.animateFrame.asyncId[animate] = cb; }; });