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 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) {
|
||||||
|
@ -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);
|
||||||
|
@ -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) {
|
||||||
|
@ -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
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(() => {
|
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;
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user