mirror of
https://github.com/unanmed/HumanBreak.git
synced 2025-01-31 23:29:27 +08:00
feat: 传送门渲染
This commit is contained in:
parent
d0ff2f1dc5
commit
292e17ed53
@ -39,24 +39,24 @@ let ctx: CanvasRenderingContext2D;
|
||||
let particleSetting: MotaSettingItem;
|
||||
|
||||
let lastTime = 0;
|
||||
Mota.require('var', 'loading').once('coreInit', () => {
|
||||
canvas = MotaCanvas2D.for('@portal');
|
||||
ctx = canvas.ctx;
|
||||
canvas.mount();
|
||||
canvas.css(`z-index: 51`);
|
||||
canvas.withGameScale(true);
|
||||
canvas.pos(0, 0);
|
||||
canvas.size(480, 480);
|
||||
canvas.on('resize', () => {
|
||||
canvas.css(`z-index: 51`);
|
||||
});
|
||||
particleSetting = mainSetting.getSetting('fx.portalParticle')!;
|
||||
ticker.add(tickPortal);
|
||||
});
|
||||
// Mota.require('var', 'loading').once('coreInit', () => {
|
||||
// canvas = MotaCanvas2D.for('@portal');
|
||||
// ctx = canvas.ctx;
|
||||
// canvas.mount();
|
||||
// canvas.css(`z-index: 51`);
|
||||
// canvas.withGameScale(true);
|
||||
// canvas.pos(0, 0);
|
||||
// canvas.size(480, 480);
|
||||
// canvas.on('resize', () => {
|
||||
// canvas.css(`z-index: 51`);
|
||||
// });
|
||||
// particleSetting = mainSetting.getSetting('fx.portalParticle')!;
|
||||
// ticker.add(tickPortal);
|
||||
// });
|
||||
|
||||
Mota.require('var', 'hook').on('changingFloor', id => {
|
||||
drawPortals(id);
|
||||
});
|
||||
// Mota.require('var', 'hook').on('changingFloor', id => {
|
||||
// drawPortals(id);
|
||||
// });
|
||||
|
||||
let needDraw = false;
|
||||
function tickPortal(time: number) {
|
||||
|
@ -7,6 +7,7 @@ import { MotaRenderer } from './render';
|
||||
import { LayerShadowExtends } from '../fx/shadow';
|
||||
import { LayerGroupFilter } from '@/plugin/fx/gameCanvas';
|
||||
import { LayerGroupAnimate } from './preset/animate';
|
||||
import { LayerGroupPortal } from '@/plugin/fx/portal';
|
||||
|
||||
let main: MotaRenderer;
|
||||
|
||||
@ -33,9 +34,11 @@ Mota.require('var', 'loading').once('loaded', () => {
|
||||
const shadow = new LayerShadowExtends();
|
||||
const filter = new LayerGroupFilter();
|
||||
const animate = new LayerGroupAnimate();
|
||||
const portal = new LayerGroupPortal();
|
||||
layer.extends(damage);
|
||||
layer.extends(detail);
|
||||
layer.extends(filter);
|
||||
layer.extends(portal);
|
||||
layer.getLayer('event')?.extends(hero);
|
||||
layer.getLayer('event')?.extends(door);
|
||||
layer.getLayer('event')?.extends(shadow);
|
||||
|
@ -16,7 +16,7 @@ export class Sprite<E extends ESpriteEvent = ESpriteEvent> extends RenderItem<
|
||||
|
||||
/**
|
||||
* 创建一个精灵,可以自由在上面渲染内容
|
||||
* @param type 渲染模式,absolute表示绝对位置,static表示跟随摄像机移动,只对顶层元素有效
|
||||
* @param type 渲染模式,absolute表示绝对位置,不会跟随自身的Transform改变
|
||||
* @param cache 是否启用缓存机制
|
||||
*/
|
||||
constructor(type: RenderItemPosition = 'static', cache: boolean = true) {
|
||||
|
@ -81,7 +81,7 @@ export namespace BluePalace {
|
||||
|
||||
// ---------- 传送门部分
|
||||
|
||||
interface Portal {
|
||||
export interface Portal {
|
||||
fx: number;
|
||||
fy: number;
|
||||
dir: Dir;
|
||||
|
296
src/plugin/fx/portal.ts
Normal file
296
src/plugin/fx/portal.ts
Normal 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);
|
||||
}
|
||||
}
|
@ -448,23 +448,6 @@ export function init() {
|
||||
adapters.animate?.global('drawHeroAnimate', name).then(() => {
|
||||
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;
|
||||
};
|
||||
});
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user