feat: 传送门渲染

This commit is contained in:
unanmed 2024-08-27 21:30:10 +08:00
parent d0ff2f1dc5
commit 292e17ed53
6 changed files with 318 additions and 36 deletions

View File

@ -39,24 +39,24 @@ let ctx: CanvasRenderingContext2D;
let particleSetting: MotaSettingItem; let particleSetting: MotaSettingItem;
let lastTime = 0; let lastTime = 0;
Mota.require('var', 'loading').once('coreInit', () => { // Mota.require('var', 'loading').once('coreInit', () => {
canvas = MotaCanvas2D.for('@portal'); // canvas = MotaCanvas2D.for('@portal');
ctx = canvas.ctx; // ctx = canvas.ctx;
canvas.mount(); // canvas.mount();
canvas.css(`z-index: 51`); // canvas.css(`z-index: 51`);
canvas.withGameScale(true); // canvas.withGameScale(true);
canvas.pos(0, 0); // canvas.pos(0, 0);
canvas.size(480, 480); // canvas.size(480, 480);
canvas.on('resize', () => { // canvas.on('resize', () => {
canvas.css(`z-index: 51`); // canvas.css(`z-index: 51`);
}); // });
particleSetting = mainSetting.getSetting('fx.portalParticle')!; // particleSetting = mainSetting.getSetting('fx.portalParticle')!;
ticker.add(tickPortal); // ticker.add(tickPortal);
}); // });
Mota.require('var', 'hook').on('changingFloor', id => { // Mota.require('var', 'hook').on('changingFloor', id => {
drawPortals(id); // drawPortals(id);
}); // });
let needDraw = false; let needDraw = false;
function tickPortal(time: number) { function tickPortal(time: number) {

View File

@ -7,6 +7,7 @@ import { MotaRenderer } from './render';
import { LayerShadowExtends } from '../fx/shadow'; import { LayerShadowExtends } from '../fx/shadow';
import { LayerGroupFilter } from '@/plugin/fx/gameCanvas'; import { LayerGroupFilter } from '@/plugin/fx/gameCanvas';
import { LayerGroupAnimate } from './preset/animate'; import { LayerGroupAnimate } from './preset/animate';
import { LayerGroupPortal } from '@/plugin/fx/portal';
let main: MotaRenderer; let main: MotaRenderer;
@ -33,9 +34,11 @@ Mota.require('var', 'loading').once('loaded', () => {
const shadow = new LayerShadowExtends(); const shadow = new LayerShadowExtends();
const filter = new LayerGroupFilter(); const filter = new LayerGroupFilter();
const animate = new LayerGroupAnimate(); const animate = new LayerGroupAnimate();
const portal = new LayerGroupPortal();
layer.extends(damage); layer.extends(damage);
layer.extends(detail); layer.extends(detail);
layer.extends(filter); layer.extends(filter);
layer.extends(portal);
layer.getLayer('event')?.extends(hero); layer.getLayer('event')?.extends(hero);
layer.getLayer('event')?.extends(door); layer.getLayer('event')?.extends(door);
layer.getLayer('event')?.extends(shadow); layer.getLayer('event')?.extends(shadow);

View File

@ -16,7 +16,7 @@ export class Sprite<E extends ESpriteEvent = ESpriteEvent> extends RenderItem<
/** /**
* *
* @param type absolute表示绝对位置static表示跟随摄像机移动 * @param type absolute表示绝对位置Transform改变
* @param cache * @param cache
*/ */
constructor(type: RenderItemPosition = 'static', cache: boolean = true) { constructor(type: RenderItemPosition = 'static', cache: boolean = true) {

View File

@ -81,7 +81,7 @@ export namespace BluePalace {
// ---------- 传送门部分 // ---------- 传送门部分
interface Portal { export interface Portal {
fx: number; fx: number;
fy: number; fy: number;
dir: Dir; dir: Dir;

296
src/plugin/fx/portal.ts Normal file
View File

@ -0,0 +1,296 @@
import { logger } from '@/core/common/logger';
import { MotaOffscreenCanvas2D } from '@/core/fx/canvas2d';
import { mainSetting, MotaSettingItem } from '@/core/main/setting';
import { LayerGroupFloorBinder } from '@/core/render/preset/floor';
import {
ILayerGroupRenderExtends,
LayerGroup
} from '@/core/render/preset/layer';
import { Sprite } from '@/core/render/sprite';
import type { BluePalace } from '@/game/mechanism/misc';
/** 最大粒子数 */
const MAX_PARTICLES = 10;
/** 粒子持续时长 */
const PARTICLE_LAST = 2000;
/** 粒子产生间隔 */
const PARTICLE_INTERVAL = PARTICLE_LAST / MAX_PARTICLES;
export class LayerGroupPortal implements ILayerGroupRenderExtends {
id: string = 'portal';
group!: LayerGroup;
binder!: LayerGroupFloorBinder;
portal!: Portal;
private onFloorChange = (floor: FloorIds) => {
const data = Mota.require('module', 'Mechanism').BluePalace.portals;
this.portal.cellSize = this.group.cellSize;
this.portal.setData(data[floor] ?? []);
};
private listen() {
this.binder.on('floorChange', this.onFloorChange);
}
awake(group: LayerGroup): void {
this.group = group;
const ex = group.getExtends('floor-binder');
if (ex instanceof LayerGroupFloorBinder) {
this.binder = ex;
this.portal = new Portal();
this.portal.setHD(true);
this.portal.size(group.width, group.height);
group.getLayer('event')?.appendChild(this.portal);
this.listen();
} else {
logger.error(
1301,
`Portal extends need 'floor-binder' extends as dependency.`
);
group.removeExtends('portal');
}
}
onDestroy(group: LayerGroup): void {
this.binder.off('floorChange', this.onFloorChange);
}
}
interface DrawingPortal {
color: string;
x: number;
y: number;
particles: Set<PortalParticle>;
/** v表示竖向h表示横向 */
type: 'v' | 'h';
/** 上一次新增粒子的时间 */
lastParticle: number;
}
interface PortalParticle {
fx: number;
fy: number;
totalTime: number;
time: number;
tx: number;
ty: number;
r: number;
}
export class Portal extends Sprite {
static colors: string[] = ['#0f0', '#ff0', '#0ff', '#fff', '#f0f'];
cellSize: number = 32;
/** 当前的渲染数据 */
private renderData: BluePalace.Portal[] = [];
/** 渲染内容 */
private renderable: Set<DrawingPortal> = new Set();
/** 粒子开关设置 */
private particleSetting: MotaSettingItem;
/** 上一帧时刻 */
private lastTime: number = 0;
private delegation: number;
constructor() {
super('static', false);
this.particleSetting = mainSetting.getSetting('fx.portalParticle')!;
this.delegation = this.delegateTicker(() => {
if (this.particleSetting.value) this.update(this);
});
this.setRenderFn((canvas, transform) => {
// console.time();
this.renderPortal(canvas);
// console.timeEnd();
});
}
/**
*
*/
setData(data: BluePalace.Portal[]) {
this.renderData = data;
this.generateRenderable();
this.update(this);
}
private generateRenderable() {
this.renderable.clear();
if (this.renderData.length === 0) return;
const colorLength = Portal.colors.length;
const cell = this.cellSize;
this.renderData.forEach((v, i) => {
const c = Portal.colors[i % colorLength];
const { fx, fy, tx, ty, dir, toDir } = v;
let x1 = fx * cell;
let y1 = fy * cell;
let x2 = tx * cell;
let y2 = ty * cell;
if (dir === 'down') y1 += cell;
else if (dir === 'right') x1 += cell;
if (toDir === 'down') y2 += cell;
else if (toDir === 'right') x2 += cell;
this.renderable.add({
x: x1,
y: y1,
type: dir === 'left' || dir === 'right' ? 'v' : 'h',
color: c,
particles: new Set(),
lastParticle: Date.now()
});
this.renderable.add({
x: x2,
y: y2,
type: toDir === 'left' || toDir === 'right' ? 'v' : 'h',
color: c,
particles: new Set(),
lastParticle: Date.now()
});
});
}
private renderPortal(canvas: MotaOffscreenCanvas2D) {
const { ctx } = canvas;
const p = this.particleSetting.value;
ctx.lineCap = 'round';
ctx.lineWidth = 3;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
if (p) {
ctx.shadowBlur = 8;
} else {
ctx.shadowBlur = 0;
}
const time = Date.now();
const dt = time - this.lastTime;
this.lastTime = time;
this.renderable.forEach(v => {
const { color, x, y, type, lastParticle, particles } = v;
ctx.strokeStyle = color;
ctx.fillStyle = color;
ctx.globalAlpha = 1;
ctx.shadowColor = color;
if (type === 'v') {
ctx.beginPath();
ctx.moveTo(x, y - 14);
ctx.lineTo(x, y + 30);
ctx.stroke();
} else {
ctx.beginPath();
ctx.moveTo(x + 2, y);
ctx.lineTo(x + 30, y);
ctx.stroke();
}
if (!p) return;
const needDelete = new Set<PortalParticle>();
particles.forEach(v => {
const { fx, fy, tx, ty, time: t, totalTime, r } = v;
const progress = t / totalTime;
const nx = (tx - fx) * progress + fx;
const ny = (ty - fy) * progress + fy;
v.time += dt;
if (progress > 1) {
needDelete.add(v);
return;
} else if (progress > 0.75) {
ctx.globalAlpha = (1 - progress) * 4;
} else if (progress < 0.25) {
ctx.globalAlpha = progress * 4;
} else {
ctx.globalAlpha = 1;
}
ctx.beginPath();
ctx.arc(nx, ny, r, 0, Math.PI * 2);
ctx.closePath();
ctx.fill();
});
needDelete.forEach(v => {
particles.delete(v);
});
if (
time - lastParticle >= PARTICLE_INTERVAL &&
particles.size < MAX_PARTICLES
) {
// 添加新粒子
const direction = Math.random();
const k = Math.random() / 2 - 0.3;
const verticle = Math.floor(Math.random() * 8 + 8);
const r = Math.random() * 2;
v.lastParticle = time;
if (direction > 0.5) {
// 左边 | 上边
if (type === 'h') {
const fx = Math.floor(Math.random() * 24 + x + 4);
particles.add({
fx: fx,
fy: y - 1,
tx: verticle * k + fx + 4,
ty: -verticle + y - 1,
r: r,
time: 0,
totalTime: PARTICLE_LAST
});
} else {
const fy = Math.floor(Math.random() * 44 + y - 14);
particles.add({
fy: fy,
fx: x - 1,
ty: verticle * k + fy + 4,
tx: -verticle + x - 1,
r: r,
time: 0,
totalTime: PARTICLE_LAST
});
}
} else {
// 右边 | 下边
if (type === 'h') {
const fx = Math.floor(Math.random() * 24 + x + 4);
particles.add({
fx: fx,
fy: y + 1,
tx: verticle * k + fx + 4,
ty: verticle + y - 1,
r: r,
time: 0,
totalTime: PARTICLE_LAST
});
} else {
const fy = Math.floor(Math.random() * 44 + y - 14);
particles.add({
fy: fy,
fx: x + 1,
ty: verticle * k + fy + 4,
tx: verticle + x + 1,
r: r,
time: 0,
totalTime: PARTICLE_LAST
});
}
}
}
});
}
destroy(): void {
super.destroy();
this.removeTicker(this.delegation);
}
}

View File

@ -448,23 +448,6 @@ export function init() {
adapters.animate?.global('drawHeroAnimate', name).then(() => { adapters.animate?.global('drawHeroAnimate', name).then(() => {
callback?.(); callback?.();
}); });
// 开始绘制
// var animate = core.material.animates[name];
// animate.se = animate.se || {};
// if (typeof animate.se == 'string') animate.se = { 1: animate.se };
// var id = setTimeout(null);
// core.status.animateObjs.push({
// name: name,
// id: id,
// animate: animate,
// hero: true,
// index: 0,
// callback: callback
// });
// return id;
}; };
}); });