mirror of
https://github.com/unanmed/HumanBreak.git
synced 2025-01-19 12:39:35 +08:00
fix: 离屏画布没有删除导致的内存泄漏
This commit is contained in:
parent
003369bed1
commit
f1b20450aa
@ -1,4 +1,5 @@
|
||||
import { EventEmitter } from 'eventemitter3';
|
||||
import { logger } from '../common/logger';
|
||||
|
||||
interface OffscreenCanvasEvent {
|
||||
/** 当被动触发resize时(例如core.domStyle.scale变化、窗口大小变化)时触发,使用size函数并不会触发 */
|
||||
@ -26,6 +27,12 @@ export class MotaOffscreenCanvas2D extends EventEmitter<OffscreenCanvasEvent> {
|
||||
/** 更新标识符,如果发生变化则说明画布被动清空 */
|
||||
symbol: number = 0;
|
||||
|
||||
private _freezed: boolean = false;
|
||||
/** 当前画布是否被冻结 */
|
||||
get freezed() {
|
||||
return this._freezed;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个新的离屏画布
|
||||
* @param alpha 是否启用透明度通道
|
||||
@ -33,6 +40,7 @@ export class MotaOffscreenCanvas2D extends EventEmitter<OffscreenCanvasEvent> {
|
||||
*/
|
||||
constructor(alpha: boolean = true, canvas?: HTMLCanvasElement) {
|
||||
super();
|
||||
// console.trace();
|
||||
|
||||
this.canvas = canvas ?? document.createElement('canvas');
|
||||
this.ctx = this.canvas.getContext('2d', { alpha })!;
|
||||
@ -46,6 +54,10 @@ export class MotaOffscreenCanvas2D extends EventEmitter<OffscreenCanvasEvent> {
|
||||
* 设置画布的大小
|
||||
*/
|
||||
size(width: number, height: number) {
|
||||
if (this._freezed) {
|
||||
logger.warn(33);
|
||||
return;
|
||||
}
|
||||
let ratio = this.highResolution ? devicePixelRatio : 1;
|
||||
const scale = core.domStyle.scale;
|
||||
if (this.autoScale) {
|
||||
@ -69,6 +81,10 @@ export class MotaOffscreenCanvas2D extends EventEmitter<OffscreenCanvasEvent> {
|
||||
* 设置当前画布是否跟随样板的 core.domStyle.scale 一同进行缩放
|
||||
*/
|
||||
withGameScale(auto: boolean) {
|
||||
if (this._freezed) {
|
||||
logger.warn(33);
|
||||
return;
|
||||
}
|
||||
this.autoScale = auto;
|
||||
this.size(this.width, this.height);
|
||||
}
|
||||
@ -77,6 +93,10 @@ export class MotaOffscreenCanvas2D extends EventEmitter<OffscreenCanvasEvent> {
|
||||
* 设置当前画布是否为高清画布
|
||||
*/
|
||||
setHD(hd: boolean) {
|
||||
if (this._freezed) {
|
||||
logger.warn(33);
|
||||
return;
|
||||
}
|
||||
this.highResolution = hd;
|
||||
this.size(this.width, this.height);
|
||||
}
|
||||
@ -85,6 +105,10 @@ export class MotaOffscreenCanvas2D extends EventEmitter<OffscreenCanvasEvent> {
|
||||
* 设置当前画布的抗锯齿设置
|
||||
*/
|
||||
setAntiAliasing(anti: boolean) {
|
||||
if (this._freezed) {
|
||||
logger.warn(33);
|
||||
return;
|
||||
}
|
||||
this.antiAliasing = anti;
|
||||
this.ctx.imageSmoothingEnabled = anti;
|
||||
}
|
||||
@ -93,6 +117,10 @@ export class MotaOffscreenCanvas2D extends EventEmitter<OffscreenCanvasEvent> {
|
||||
* 清空画布
|
||||
*/
|
||||
clear() {
|
||||
if (this._freezed) {
|
||||
logger.warn(33);
|
||||
return;
|
||||
}
|
||||
this.ctx.save();
|
||||
this.ctx.setTransform(1, 0, 0, 1, 0, 0);
|
||||
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
||||
@ -103,13 +131,21 @@ export class MotaOffscreenCanvas2D extends EventEmitter<OffscreenCanvasEvent> {
|
||||
* 删除这个画布
|
||||
*/
|
||||
delete() {
|
||||
this.canvas.remove();
|
||||
this.ctx.reset();
|
||||
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
||||
MotaOffscreenCanvas2D.list.delete(this);
|
||||
}
|
||||
|
||||
freeze() {
|
||||
this._freezed = true;
|
||||
MotaOffscreenCanvas2D.list.delete(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 复制一个离屏Canvas2D对象,一般用于缓存等操作
|
||||
* @param canvas 被复制的MotaOffscreenCanvas2D对象
|
||||
* @returns 复制结果
|
||||
* @returns 复制结果,注意复制结果是被冻结的,无法进行大小等的修改,但是可以继续绘制
|
||||
*/
|
||||
static clone(canvas: MotaOffscreenCanvas2D): MotaOffscreenCanvas2D {
|
||||
const newCanvas = new MotaOffscreenCanvas2D();
|
||||
@ -123,12 +159,13 @@ export class MotaOffscreenCanvas2D extends EventEmitter<OffscreenCanvasEvent> {
|
||||
canvas.width,
|
||||
canvas.height
|
||||
);
|
||||
newCanvas.freeze();
|
||||
return newCanvas;
|
||||
}
|
||||
|
||||
static refreshAll() {
|
||||
static refreshAll(force: boolean = false) {
|
||||
this.list.forEach(v => {
|
||||
if (v.autoScale) {
|
||||
if (force || v.autoScale) {
|
||||
v.size(v.width, v.height);
|
||||
v.symbol++;
|
||||
v.emit('resize');
|
||||
|
@ -480,6 +480,7 @@ function splitAutotiles(map: IdToNumber): AutotileCaches {
|
||||
master.size(32, 32);
|
||||
master.ctx.drawImage(img, 0, 0, 32, 32, 0, 0, 32, 32);
|
||||
masterMap[auto] = master.canvas.toDataURL('image/png');
|
||||
master.delete();
|
||||
|
||||
// 自动图块的绘制信息
|
||||
for (let i = 0; i <= 0b11111111; i++) {
|
||||
@ -504,6 +505,7 @@ function splitAutotiles(map: IdToNumber): AutotileCaches {
|
||||
canvas.setAntiAliasing(false);
|
||||
canvas.withGameScale(false);
|
||||
canvas.size(32 * frame, 32);
|
||||
canvas.freeze();
|
||||
const ctx = canvas.ctx;
|
||||
for (let i = 0; i < frame; i++) {
|
||||
const dx = 32 * i;
|
||||
@ -525,16 +527,17 @@ function splitAutotiles(map: IdToNumber): AutotileCaches {
|
||||
}
|
||||
}
|
||||
|
||||
// 进行父子关系判断
|
||||
for (const [key, img] of Object.entries(core.material.images.autotile)) {
|
||||
const auto = map[key as AllIdsOf<'autotile'>];
|
||||
|
||||
// 只针对3*4的图块进行,截取第一行中间的,然后判断
|
||||
const judge = new MotaOffscreenCanvas2D();
|
||||
judge.setHD(false);
|
||||
judge.setAntiAliasing(false);
|
||||
judge.withGameScale(false);
|
||||
judge.size(32, 32);
|
||||
// 进行父子关系判断
|
||||
for (const [key, img] of Object.entries(core.material.images.autotile)) {
|
||||
const auto = map[key as AllIdsOf<'autotile'>];
|
||||
|
||||
// 只针对3*4的图块进行,截取第一行中间的,然后判断
|
||||
judge.clear();
|
||||
judge.ctx.drawImage(img, 32, 0, 32, 32, 0, 0, 32, 32);
|
||||
const data = judge.canvas.toDataURL('image/png');
|
||||
|
||||
@ -547,6 +550,7 @@ function splitAutotiles(map: IdToNumber): AutotileCaches {
|
||||
}
|
||||
}
|
||||
}
|
||||
judge.delete();
|
||||
|
||||
return cache as AutotileCaches;
|
||||
}
|
||||
|
@ -4,28 +4,28 @@ import { logger } from '../common/logger';
|
||||
import { Transform } from './transform';
|
||||
import EventEmitter from 'eventemitter3';
|
||||
|
||||
export interface CameraTranslate {
|
||||
export interface ICameraTranslate {
|
||||
readonly type: 'translate';
|
||||
readonly from: Camera;
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
|
||||
export interface CameraRotate {
|
||||
export interface ICameraRotate {
|
||||
readonly type: 'rotate';
|
||||
readonly from: Camera;
|
||||
/** 旋转角,单位弧度 */
|
||||
angle: number;
|
||||
}
|
||||
|
||||
export interface CameraScale {
|
||||
export interface ICameraScale {
|
||||
readonly type: 'scale';
|
||||
readonly from: Camera;
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
|
||||
type CameraOperation = CameraTranslate | CameraScale | CameraRotate;
|
||||
type CameraOperation = ICameraTranslate | ICameraScale | ICameraRotate;
|
||||
|
||||
interface CameraEvent {
|
||||
destroy: [];
|
||||
@ -145,8 +145,8 @@ export class Camera extends EventEmitter<CameraEvent> {
|
||||
* 添加一个平移操作
|
||||
* @returns 添加的平移变换操作
|
||||
*/
|
||||
addTranslate(): CameraTranslate {
|
||||
const item: CameraTranslate = {
|
||||
addTranslate(): ICameraTranslate {
|
||||
const item: ICameraTranslate = {
|
||||
type: 'translate',
|
||||
x: 0,
|
||||
y: 0,
|
||||
@ -160,8 +160,8 @@ export class Camera extends EventEmitter<CameraEvent> {
|
||||
* 添加一个旋转操作
|
||||
* @returns 添加的旋转变换操作
|
||||
*/
|
||||
addRotate(): CameraRotate {
|
||||
const item: CameraRotate = {
|
||||
addRotate(): ICameraRotate {
|
||||
const item: ICameraRotate = {
|
||||
type: 'rotate',
|
||||
angle: 0,
|
||||
from: this
|
||||
@ -174,8 +174,8 @@ export class Camera extends EventEmitter<CameraEvent> {
|
||||
* 添加一个放缩操作
|
||||
* @returns 添加的放缩变换操作
|
||||
*/
|
||||
addScale(): CameraScale {
|
||||
const item: CameraScale = {
|
||||
addScale(): ICameraScale {
|
||||
const item: ICameraScale = {
|
||||
type: 'scale',
|
||||
x: 1,
|
||||
y: 1,
|
||||
@ -213,7 +213,7 @@ export class Camera extends EventEmitter<CameraEvent> {
|
||||
* @param time 动画时长
|
||||
*/
|
||||
applyTranslateAnimation(
|
||||
operation: CameraTranslate,
|
||||
operation: ICameraTranslate,
|
||||
animate: Animation,
|
||||
time: number
|
||||
) {
|
||||
@ -237,7 +237,7 @@ export class Camera extends EventEmitter<CameraEvent> {
|
||||
* @param time 动画时长
|
||||
*/
|
||||
applyRotateAnimation(
|
||||
operation: CameraRotate,
|
||||
operation: ICameraRotate,
|
||||
animate: Animation,
|
||||
time: number
|
||||
) {
|
||||
@ -260,7 +260,7 @@ export class Camera extends EventEmitter<CameraEvent> {
|
||||
* @param time 动画时长
|
||||
*/
|
||||
applyScaleAnimation(
|
||||
operation: CameraScale,
|
||||
operation: ICameraScale,
|
||||
animate: Animation,
|
||||
time: number
|
||||
) {
|
||||
@ -284,7 +284,7 @@ export class Camera extends EventEmitter<CameraEvent> {
|
||||
* @param time 渐变时长
|
||||
*/
|
||||
applyTranslateTransition(
|
||||
operation: CameraTranslate,
|
||||
operation: ICameraTranslate,
|
||||
animate: Transition,
|
||||
time: number
|
||||
) {
|
||||
@ -308,7 +308,7 @@ export class Camera extends EventEmitter<CameraEvent> {
|
||||
* @param time 渐变时长
|
||||
*/
|
||||
applyRotateTransition(
|
||||
operation: CameraRotate,
|
||||
operation: ICameraRotate,
|
||||
animate: Transition,
|
||||
time: number
|
||||
) {
|
||||
@ -331,7 +331,7 @@ export class Camera extends EventEmitter<CameraEvent> {
|
||||
* @param time 渐变时长
|
||||
*/
|
||||
applyScaleTransition(
|
||||
operation: CameraScale,
|
||||
operation: ICameraScale,
|
||||
animate: Transition,
|
||||
time: number
|
||||
) {
|
||||
@ -514,7 +514,7 @@ export class CameraAnimation extends EventEmitter<CameraAnimationEvent> {
|
||||
* @param timing 动画的缓动函数
|
||||
*/
|
||||
translate(
|
||||
operation: CameraTranslate,
|
||||
operation: ICameraTranslate,
|
||||
x: number,
|
||||
y: number,
|
||||
time: number,
|
||||
@ -542,7 +542,7 @@ export class CameraAnimation extends EventEmitter<CameraAnimationEvent> {
|
||||
* @param timing 动画的缓动函数
|
||||
*/
|
||||
rotate(
|
||||
operation: CameraRotate,
|
||||
operation: ICameraRotate,
|
||||
angle: number,
|
||||
time: number,
|
||||
start: number,
|
||||
@ -568,7 +568,7 @@ export class CameraAnimation extends EventEmitter<CameraAnimationEvent> {
|
||||
* @param timing 动画的缓动函数
|
||||
*/
|
||||
scale(
|
||||
operation: CameraScale,
|
||||
operation: ICameraScale,
|
||||
scale: number,
|
||||
time: number,
|
||||
start: number,
|
||||
|
@ -385,6 +385,7 @@ export abstract class GL2 extends RenderItem {
|
||||
|
||||
destroy(): void {
|
||||
this.programs.forEach(v => v.destroy());
|
||||
this.canvas.remove();
|
||||
super.destroy();
|
||||
}
|
||||
|
||||
|
@ -462,6 +462,7 @@ export abstract class RenderItem<E extends ERenderItemEvent = ERenderItemEvent>
|
||||
this.remove();
|
||||
this.emit('destroy');
|
||||
this.removeAllListeners();
|
||||
this.cache.delete();
|
||||
RenderItem.itemMap.delete(this._id);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { EventEmitter } from '@/core/common/eventEmitter';
|
||||
import { logger } from '@/core/common/logger';
|
||||
import { MotaOffscreenCanvas2D } from '@/core/fx/canvas2d';
|
||||
|
||||
interface BlockCacherEvent {
|
||||
split: () => void;
|
||||
@ -16,13 +17,22 @@ interface BlockData {
|
||||
restHeight: number;
|
||||
}
|
||||
|
||||
export interface IBlockCacheable {
|
||||
/**
|
||||
* 摧毁这个缓存元素
|
||||
*/
|
||||
destroy(): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* 简单分块缓存类,内容包含元素与分块两种,其中元素是最小单元,分块是缓存单元。
|
||||
* 拿楼层举例,假如我将楼层按照13x13划分缓存,那么元素就是每个图块,而分块就是这13x13的缓存分块。
|
||||
* 为方便区分,在相关函数的注释最后,都会有`xx -> yy`的说明,
|
||||
* 其中xx说明传入的数据是元素还是分块的数据,而yy表示其返回值或转换为的值
|
||||
*/
|
||||
export class BlockCacher<T> extends EventEmitter<BlockCacherEvent> {
|
||||
export class BlockCacher<
|
||||
T extends IBlockCacheable
|
||||
> extends EventEmitter<BlockCacherEvent> {
|
||||
/** 区域宽度 */
|
||||
width: number;
|
||||
/** 区域高度 */
|
||||
@ -118,7 +128,12 @@ export class BlockCacher<T> extends EventEmitter<BlockCacherEvent> {
|
||||
const depth = this.cacheDepth;
|
||||
for (let i = 0; i < depth; i++) {
|
||||
if (deep & (1 << i)) {
|
||||
this.cache.delete(index * this.cacheDepth + i);
|
||||
const nowIndex = index * this.cacheDepth + i;
|
||||
const item = this.cache.get(nowIndex);
|
||||
if (item) {
|
||||
item.destroy();
|
||||
this.cache.delete(nowIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -126,14 +141,19 @@ export class BlockCacher<T> extends EventEmitter<BlockCacherEvent> {
|
||||
/**
|
||||
* 清空指定索引的缓存,与 {@link clearCache} 不同的是,这里会直接清空对应索引的缓存,而不是指定分块的缓存(分块->void)
|
||||
*/
|
||||
clearCacheByIndex(index: number) {
|
||||
clearCacheByIndex(index: number, func: (item: T) => void) {
|
||||
const item = this.cache.get(index);
|
||||
if (item) {
|
||||
item.destroy();
|
||||
this.cache.delete(index);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空所有缓存
|
||||
*/
|
||||
clearAllCache() {
|
||||
this.cache.forEach(v => v.destroy());
|
||||
this.cache.clear();
|
||||
}
|
||||
|
||||
@ -272,4 +292,24 @@ export class BlockCacher<T> extends EventEmitter<BlockCacherEvent> {
|
||||
(y + 1) * this.blockSize
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 摧毁这个块缓存
|
||||
*/
|
||||
destroy() {
|
||||
this.clearAllCache();
|
||||
}
|
||||
}
|
||||
|
||||
export interface ICanvasCacheItem extends IBlockCacheable {
|
||||
readonly canvas: MotaOffscreenCanvas2D;
|
||||
symbol: number;
|
||||
}
|
||||
|
||||
export class CanvasCacheItem implements ICanvasCacheItem {
|
||||
constructor(public canvas: MotaOffscreenCanvas2D, public symbol: number) {}
|
||||
|
||||
destroy(): void {
|
||||
this.canvas.delete();
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,12 @@ import {
|
||||
LayerGroup
|
||||
} from './layer';
|
||||
import { ESpriteEvent, Sprite } from '../sprite';
|
||||
import { BlockCacher } from './block';
|
||||
import {
|
||||
BlockCacher,
|
||||
CanvasCacheItem,
|
||||
IBlockCacheable,
|
||||
ICanvasCacheItem
|
||||
} from './block';
|
||||
import type {
|
||||
DamageEnemy,
|
||||
EnemyCollection,
|
||||
@ -16,7 +21,7 @@ import type {
|
||||
import { MotaOffscreenCanvas2D } from '@/core/fx/canvas2d';
|
||||
import { isNil } from 'lodash-es';
|
||||
import { getDamageColor } from '@/plugin/utils';
|
||||
import { transformCanvas } from '../item';
|
||||
import { RenderItem, transformCanvas } from '../item';
|
||||
import EventEmitter from 'eventemitter3';
|
||||
import { Transform } from '../transform';
|
||||
|
||||
@ -120,11 +125,6 @@ export interface DamageRenderable {
|
||||
strokeWidth?: number;
|
||||
}
|
||||
|
||||
interface DamageCache {
|
||||
canvas: MotaOffscreenCanvas2D;
|
||||
symbol: number;
|
||||
}
|
||||
|
||||
interface EDamageEvent extends ESpriteEvent {
|
||||
setMapSize: [width: number, height: number];
|
||||
beforeDamageRender: [need: Set<number>, transform: Transform];
|
||||
@ -132,11 +132,11 @@ interface EDamageEvent extends ESpriteEvent {
|
||||
dirtyUpdate: [block: number];
|
||||
}
|
||||
|
||||
export class Damage extends Sprite<EDamageEvent> {
|
||||
export class Damage extends RenderItem<EDamageEvent> {
|
||||
mapWidth: number = 0;
|
||||
mapHeight: number = 0;
|
||||
|
||||
block: BlockCacher<DamageCache>;
|
||||
block: BlockCacher<ICanvasCacheItem>;
|
||||
/** 键表示分块索引,值表示在这个分块上的渲染信息(当然实际渲染位置可以不在这个分块上) */
|
||||
renderable: Map<number, Set<DamageRenderable>> = new Map();
|
||||
|
||||
@ -147,8 +147,6 @@ export class Damage extends Sprite<EDamageEvent> {
|
||||
/** 单元格大小 */
|
||||
cellSize: number = 32;
|
||||
|
||||
/** 伤害渲染层 */
|
||||
damageMap: MotaOffscreenCanvas2D = new MotaOffscreenCanvas2D();
|
||||
/** 默认伤害字体 */
|
||||
font: string = '300 9px Verdana';
|
||||
/** 默认描边样式,当伤害文字不存在描边属性时会使用此属性 */
|
||||
@ -165,18 +163,15 @@ export class Damage extends Sprite<EDamageEvent> {
|
||||
this.block = new BlockCacher(0, 0, core._WIDTH_, 1);
|
||||
this.type = 'absolute';
|
||||
this.size(core._PX_, core._PY_);
|
||||
this.damageMap.withGameScale(true);
|
||||
this.damageMap.setHD(true);
|
||||
this.damageMap.setAntiAliasing(true);
|
||||
this.damageMap.size(core._PX_, core._PY_);
|
||||
this.setHD(true);
|
||||
this.setAntiAliasing(true);
|
||||
}
|
||||
|
||||
this.setRenderFn((canvas, transform) => {
|
||||
const { ctx } = canvas;
|
||||
const { width, height } = canvas;
|
||||
ctx.imageSmoothingEnabled = false;
|
||||
this.renderDamage(transform);
|
||||
ctx.drawImage(this.damageMap.canvas, 0, 0, width, height);
|
||||
});
|
||||
protected render(
|
||||
canvas: MotaOffscreenCanvas2D,
|
||||
transform: Transform
|
||||
): void {
|
||||
this.renderDamage(canvas, transform);
|
||||
}
|
||||
|
||||
private onExtract = () => {
|
||||
@ -468,12 +463,10 @@ export class Damage extends Sprite<EDamageEvent> {
|
||||
* 渲染伤害层
|
||||
* @param transform 变换矩阵
|
||||
*/
|
||||
renderDamage(transform: Transform) {
|
||||
renderDamage(canvas: MotaOffscreenCanvas2D, transform: Transform) {
|
||||
// console.time('damage');
|
||||
const { ctx } = this.damageMap;
|
||||
ctx.save();
|
||||
this.damageMap.clear();
|
||||
transformCanvas(this.damageMap, transform);
|
||||
const { ctx } = canvas;
|
||||
transformCanvas(canvas, transform);
|
||||
// console.trace();
|
||||
|
||||
const render = this.calNeedRender(transform);
|
||||
@ -529,10 +522,7 @@ export class Damage extends Sprite<EDamageEvent> {
|
||||
});
|
||||
|
||||
ctx.drawImage(temp.canvas, px, py, size, size);
|
||||
block.cache.set(v, {
|
||||
canvas: temp,
|
||||
symbol: temp.symbol
|
||||
});
|
||||
block.cache.set(v, new CanvasCacheItem(temp, temp.symbol));
|
||||
});
|
||||
ctx.restore();
|
||||
// console.timeEnd('damage');
|
||||
@ -540,6 +530,7 @@ export class Damage extends Sprite<EDamageEvent> {
|
||||
|
||||
destroy(): void {
|
||||
super.destroy();
|
||||
this.block.destroy();
|
||||
this.enemy?.off('extract', this.onExtract);
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,12 @@ import { TimingFn } from 'mutate-animate';
|
||||
import { IAnimateFrame, renderEmits, RenderItem } from '../item';
|
||||
import { logger } from '@/core/common/logger';
|
||||
import { RenderableData, texture } from '../cache';
|
||||
import { BlockCacher } from './block';
|
||||
import {
|
||||
BlockCacher,
|
||||
CanvasCacheItem,
|
||||
IBlockCacheable,
|
||||
ICanvasCacheItem
|
||||
} from './block';
|
||||
import { Transform } from '../transform';
|
||||
import { LayerFloorBinder, LayerGroupFloorBinder } from './floor';
|
||||
import { RenderAdapter } from '../adapter';
|
||||
@ -388,7 +393,7 @@ export interface ILayerRenderExtends {
|
||||
* @param layer 目标Layer实例
|
||||
* @param images 生成出的背景图块的单个分块图像,数组是因为背景图块可能是多帧图块
|
||||
*/
|
||||
onBackgroundGenerated?(layer: Layer, images: HTMLCanvasElement[]): void;
|
||||
onBackgroundGenerated?(layer: Layer, images: MotaOffscreenCanvas2D[]): void;
|
||||
|
||||
/**
|
||||
* 当修改渲染数据时执行的函数,参见 {@link Layer.putRenderData}
|
||||
@ -504,11 +509,6 @@ export interface ILayerRenderExtends {
|
||||
onDestroy?(layer: Layer): void;
|
||||
}
|
||||
|
||||
interface LayerCacheItem {
|
||||
symbol: number;
|
||||
canvas: MotaOffscreenCanvas2D;
|
||||
}
|
||||
|
||||
export interface LayerMovingRenderable extends RenderableData {
|
||||
zIndex: number;
|
||||
x: number;
|
||||
@ -550,12 +550,17 @@ export class Layer extends Container {
|
||||
/** 背景图块 */
|
||||
background: AllNumbers = 0;
|
||||
/** 背景图块画布 */
|
||||
backImage: HTMLCanvasElement[] = [];
|
||||
backImage: MotaOffscreenCanvas2D[] = [];
|
||||
/** 背景贴图 */
|
||||
floorImage: FloorAnimate[] = [];
|
||||
|
||||
/** 分块信息 */
|
||||
block: BlockCacher<LayerCacheItem> = new BlockCacher(0, 0, core._WIDTH_, 4);
|
||||
block: BlockCacher<ICanvasCacheItem> = new BlockCacher(
|
||||
0,
|
||||
0,
|
||||
core._WIDTH_,
|
||||
4
|
||||
);
|
||||
|
||||
/** 大怪物渲染信息 */
|
||||
bigImages: Map<number, LayerMovingRenderable> = new Map();
|
||||
@ -717,13 +722,17 @@ export class Layer extends Container {
|
||||
const num = this.background;
|
||||
|
||||
const data = texture.getRenderable(num);
|
||||
this.backImage.forEach(v => v.delete());
|
||||
this.backImage = [];
|
||||
if (!data) return;
|
||||
|
||||
const frame = data.frame;
|
||||
const temp = new MotaOffscreenCanvas2D();
|
||||
temp.setHD(false);
|
||||
temp.setAntiAliasing(false);
|
||||
temp.withGameScale(false);
|
||||
for (let i = 0; i < frame; i++) {
|
||||
const canvas = new MotaOffscreenCanvas2D();
|
||||
const temp = new MotaOffscreenCanvas2D();
|
||||
const ctx = canvas.ctx;
|
||||
const tempCtx = temp.ctx;
|
||||
const [sx, sy, w, h] = data.render[i];
|
||||
@ -731,9 +740,6 @@ export class Layer extends Container {
|
||||
canvas.setAntiAliasing(false);
|
||||
canvas.withGameScale(false);
|
||||
canvas.size(core._PX_, core._PY_);
|
||||
temp.setHD(false);
|
||||
temp.setAntiAliasing(false);
|
||||
temp.withGameScale(false);
|
||||
temp.size(w, h);
|
||||
|
||||
const img = data.autotile ? data.image[0b11111111] : data.image;
|
||||
@ -743,8 +749,9 @@ export class Layer extends Container {
|
||||
ctx.fillStyle = pattern;
|
||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
this.backImage.push(canvas.canvas);
|
||||
this.backImage.push(canvas);
|
||||
}
|
||||
temp.delete();
|
||||
|
||||
for (const ex of this.extend.values()) {
|
||||
ex.onBackgroundGenerated?.(this, this.backImage);
|
||||
@ -1078,7 +1085,7 @@ export class Layer extends Container {
|
||||
const sx = x * blockSize;
|
||||
const sy = y * blockSize;
|
||||
ctx.drawImage(
|
||||
img,
|
||||
img.canvas,
|
||||
sx * cell,
|
||||
sy * cell,
|
||||
blockSize * cell,
|
||||
@ -1180,10 +1187,7 @@ export class Layer extends Container {
|
||||
blockSize * cell,
|
||||
blockSize * cell
|
||||
);
|
||||
this.block.cache.set(index, {
|
||||
canvas: temp,
|
||||
symbol: temp.symbol
|
||||
});
|
||||
this.block.cache.set(index, new CanvasCacheItem(temp, temp.symbol));
|
||||
});
|
||||
}
|
||||
|
||||
@ -1379,6 +1383,12 @@ export class Layer extends Container {
|
||||
ex.onDestroy?.(this);
|
||||
}
|
||||
super.destroy();
|
||||
this.staticMap.delete();
|
||||
this.movingMap.delete();
|
||||
this.backMap.delete();
|
||||
this.backImage.forEach(v => v.delete());
|
||||
this.block.destroy();
|
||||
this.main.destroy();
|
||||
layerAdapter.remove(this);
|
||||
}
|
||||
|
||||
|
@ -78,7 +78,9 @@ export class MotaRenderer extends Container {
|
||||
}
|
||||
|
||||
destroy() {
|
||||
super.destroy();
|
||||
MotaRenderer.list.delete(this.id);
|
||||
this.target.delete();
|
||||
}
|
||||
|
||||
static get(id: string) {
|
||||
|
@ -17,7 +17,7 @@ export function enableViewport() {
|
||||
/**
|
||||
* 将两个缓动函数做加法
|
||||
*/
|
||||
export function AddTiming(timing1: TimingFn, timing2: TimingFn): TimingFn {
|
||||
export function addTiming(timing1: TimingFn, timing2: TimingFn): TimingFn {
|
||||
return (p: number) => timing1(p) + timing2(p);
|
||||
}
|
||||
|
||||
|
@ -57,6 +57,7 @@
|
||||
"30": "Cannot use indices named $1 since no definition for it. Please define it in advance.",
|
||||
"31": "Cannot use indices since the indices instance is not belong to the program.",
|
||||
"32": "Sub-image exceeds texture dimensions, auto adjusting size.",
|
||||
"33": "Cannot modify MotaOffscreenCanvas2D that is freezed.",
|
||||
"1001": "Item-detail extension needs 'floor-binder' and 'floor-damage' extension as dependency.",
|
||||
"1101": "Cannot add new effect to point effect instance, for there's no more reserve space for it. Please increase the max count of the instance."
|
||||
}
|
||||
|
@ -2,9 +2,7 @@ import { Shader, ShaderProgram } from '@/core/render/shader';
|
||||
import { IWeather, WeatherController } from './weather';
|
||||
import { MotaRenderer } from '@/core/render/render';
|
||||
import { Container } from '@/core/render/container';
|
||||
import { GL2Program, IShaderUniform, UniformType } from '@/core/render/gl2';
|
||||
import { MotaOffscreenCanvas2D } from '@/core/fx/canvas2d';
|
||||
import { Transform } from '@/core/render/transform';
|
||||
import { IShaderUniform, UniformType } from '@/core/render/gl2';
|
||||
|
||||
const rainVs = /* glsl */ `
|
||||
in vec2 a_rainVertex;
|
||||
|
@ -163,8 +163,10 @@ export class ArrowProjectile extends Projectile<TowerBoss> {
|
||||
this.easing = void 0;
|
||||
this.dangerEasing = void 0;
|
||||
this.horizontal?.clear();
|
||||
this.horizontal?.delete();
|
||||
this.horizontal = null;
|
||||
this.vertical?.clear();
|
||||
this.vertical?.delete();
|
||||
this.vertical = null;
|
||||
}
|
||||
|
||||
@ -465,6 +467,7 @@ export class ThunderProjectile extends Projectile<TowerBoss> {
|
||||
|
||||
static end() {
|
||||
this.cache?.clear();
|
||||
this.cache?.delete();
|
||||
this.cache = null;
|
||||
}
|
||||
|
||||
@ -648,8 +651,10 @@ export class ThunderBallProjectile extends Projectile<TowerBoss> {
|
||||
static end() {
|
||||
this.dangerEasing = void 0;
|
||||
this.horizontal?.clear();
|
||||
this.horizontal?.delete();
|
||||
this.horizontal = null;
|
||||
this.vertical?.clear();
|
||||
this.vertical?.delete();
|
||||
this.vertical = null;
|
||||
}
|
||||
|
||||
|
@ -337,6 +337,8 @@ export class Chase extends EventEmitter<ChaseEvent> {
|
||||
Chase.shader.remove();
|
||||
this.emit('end', success);
|
||||
this.removeAllListeners();
|
||||
this.pathMap.forEach(v => v.delete());
|
||||
this.pathMap.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Animation, hyper, linear, power, sleep } from 'mutate-animate';
|
||||
import { Chase, ChaseData, IChaseController } from './chase';
|
||||
import { completeAchievement } from '../ui/achievement';
|
||||
import { Camera, CameraAnimation, CameraScale } from '@/core/render/camera';
|
||||
import { Camera, CameraAnimation, ICameraScale } from '@/core/render/camera';
|
||||
import { LayerGroup } from '@/core/render/preset/layer';
|
||||
import { MotaRenderer } from '@/core/render/render';
|
||||
import { Sprite } from '@/core/render/sprite';
|
||||
@ -272,7 +272,7 @@ function playAudio(from: number, chase: Chase) {
|
||||
function processScale(
|
||||
chase: Chase,
|
||||
ani: Animation,
|
||||
scale: CameraScale,
|
||||
scale: ICameraScale,
|
||||
camera: Camera
|
||||
) {
|
||||
chase.onceLoc(35, 3, 'MT15', () => {
|
||||
|
Loading…
Reference in New Issue
Block a user