HumanBreak/src/core/render/render.ts
2024-05-10 17:33:27 +08:00

158 lines
4.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { isNil } from 'lodash-es';
import { MotaCanvas2D, MotaOffscreenCanvas2D } from '../fx/canvas2d';
import { Camera } from './camera';
import { Container } from './container';
import { RenderItem, withCacheRender } from './item';
import { Image, Text } from './preset/misc';
import { Animation, hyper } from 'mutate-animate';
export class MotaRenderer extends Container {
canvas: MotaOffscreenCanvas2D;
camera: Camera;
/** 摄像机缓存,如果是需要快速切换摄像机的场景,使用缓存可以大幅提升性能表现 */
cameraCache: Map<Camera, MotaOffscreenCanvas2D> = new Map();
target: MotaCanvas2D;
private needUpdate: boolean = false;
constructor() {
super();
this.canvas = new MotaOffscreenCanvas2D();
this.camera = new Camera();
this.target = new MotaCanvas2D(`render-main`);
this.width = 480;
this.height = 480;
this.target.withGameScale(true);
this.target.size(480, 480);
this.canvas.withGameScale(true);
this.canvas.size(480, 480);
this.target.css(`z-index: 100`);
}
/**
* 使用某个摄像机
* @param camera 要使用的摄像机
* @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);
}
/**
* 删除某个摄像机的画面缓存
* @param camera 要删除的缓存对应的摄像机
*/
freeCameraCache(camera: Camera) {
this.cameraCache.delete(camera);
}
/**
* 渲染游戏画面
*/
render() {
const { canvas, ctx } = this.target;
const camera = this.camera;
ctx.clearRect(0, 0, canvas.width, canvas.height);
withCacheRender(this, canvas, ctx, camera, canvas => {
const { canvas: ca, ctx: ct, scale } = canvas;
const mat = camera.mat;
const a = mat[0] * scale;
const b = mat[1] * scale;
const c = mat[3] * scale;
const d = mat[4] * scale;
const e = mat[6] * scale;
const f = mat[7] * scale;
this.sortedChildren.forEach(v => {
if (v.type === 'absolute') {
ct.transform(scale, 0, 0, scale, 0, 0);
} else {
ct.setTransform(1, 0, 0, 1, 0, 0);
ct.translate(ca.width / 2, ca.height / 2);
ct.transform(a, b, c, d, e, f);
}
v.render(ca, ct, camera);
});
});
}
/**
* 更新渲染在下一个tick更新
*/
update(item?: RenderItem) {
if (this.needUpdate) return;
this.needUpdate = true;
requestAnimationFrame(() => {
this.cache(this.writing);
this.needUpdate = false;
this.emit('beforeUpdate', item);
this.render();
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);
}
/**
* 添加至游戏画面
*/
mount() {
this.target.mount();
}
}
Mota.require('var', 'hook').once('reset', () => {
const render = new MotaRenderer();
const con = new Container('static');
const camera = render.camera;
render.mount();
const testText = new Text();
testText.setText('测试测试');
testText.pos(240, 240);
testText.setFont('32px normal');
testText.setStyle('#fff');
con.size(480, 480);
con.pos(-240, -240);
const testImage = new Image(core.material.images.images['arrow.png']);
testImage.pos(240, 240);
con.appendChild([testText, testImage]);
render.appendChild([con]);
render.update(render);
const ani = new Animation();
ani.mode(hyper('sin', 'in-out'))
.time(10000)
.absolute()
.rotate(360)
.scale(0.7)
.move(100, 100);
ani.ticker.add(() => {
render.cache('@default');
camera.reset();
camera.move(ani.x, ani.y);
camera.scale(ani.size);
camera.rotate((ani.angle / 180) * Math.PI);
render.update(render);
});
setTimeout(() => ani.ticker.destroy(), 10000);
});