mirror of
https://github.com/unanmed/HumanBreak.git
synced 2025-04-19 17:16:08 +08:00
feat: 渲染系统接入vue渲染器
This commit is contained in:
parent
4073db5b04
commit
ac68d64f5f
7901
pnpm-lock.yaml
7901
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
@ -278,9 +278,9 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = {
|
|||||||
// 要存档的内容
|
// 要存档的内容
|
||||||
var data = {
|
var data = {
|
||||||
floorId: core.status.floorId,
|
floorId: core.status.floorId,
|
||||||
hero: core.clone(core.status.hero, name => name !== 'chase'),
|
hero: core.status.hero,
|
||||||
hard: core.status.hard,
|
hard: core.status.hard,
|
||||||
maps: core.clone(core.maps.saveMap()),
|
maps: core.maps.saveMap(),
|
||||||
route: core.encodeRoute(core.status.route, !fromAutosave),
|
route: core.encodeRoute(core.status.route, !fromAutosave),
|
||||||
values: values,
|
values: values,
|
||||||
version: core.firstData.version,
|
version: core.firstData.version,
|
||||||
@ -291,7 +291,7 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = {
|
|||||||
skill: HeroSkill.saveSkill()
|
skill: HeroSkill.saveSkill()
|
||||||
};
|
};
|
||||||
|
|
||||||
return data;
|
return structuredClone(data);
|
||||||
},
|
},
|
||||||
loadData: function (data, callback) {
|
loadData: function (data, callback) {
|
||||||
// 读档操作;从存储中读取了内容后的行为
|
// 读档操作;从存储中读取了内容后的行为
|
||||||
|
@ -134,6 +134,7 @@ export class MotaOffscreenCanvas2D extends EventEmitter<OffscreenCanvasEvent> {
|
|||||||
this.canvas.remove();
|
this.canvas.remove();
|
||||||
this.ctx.reset();
|
this.ctx.reset();
|
||||||
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
||||||
|
this._freezed = true;
|
||||||
MotaOffscreenCanvas2D.list.delete(this);
|
MotaOffscreenCanvas2D.list.delete(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,6 +138,10 @@ export class HeroKeyMover {
|
|||||||
private onStepEnd = () => {
|
private onStepEnd = () => {
|
||||||
const con = this.controller;
|
const con = this.controller;
|
||||||
if (!con) return;
|
if (!con) return;
|
||||||
|
if (core.status.lockControl) {
|
||||||
|
con.stop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!this.moving) {
|
if (!this.moving) {
|
||||||
con.stop();
|
con.stop();
|
||||||
return;
|
return;
|
||||||
|
@ -71,6 +71,7 @@ export class Container<E extends EContainerEvent = EContainerEvent>
|
|||||||
removeChild(...child: RenderItem<any>[]): void {
|
removeChild(...child: RenderItem<any>[]): void {
|
||||||
let changed = false;
|
let changed = false;
|
||||||
child.forEach(v => {
|
child.forEach(v => {
|
||||||
|
if (v.parent !== this) return;
|
||||||
const success = v.remove();
|
const success = v.remove();
|
||||||
if (success) {
|
if (success) {
|
||||||
changed = true;
|
changed = true;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import EventEmitter from 'eventemitter3';
|
import EventEmitter from 'eventemitter3';
|
||||||
import { logger } from '../common/logger';
|
import { logger } from '../common/logger';
|
||||||
import { MotaOffscreenCanvas2D } from '../fx/canvas2d';
|
import { MotaOffscreenCanvas2D } from '../fx/canvas2d';
|
||||||
import { RenderItem, RenderItemPosition } from './item';
|
import { ERenderItemEvent, RenderItem, RenderItemPosition } from './item';
|
||||||
import { Transform } from './transform';
|
import { Transform } from './transform';
|
||||||
import { isWebGL2Supported } from '../fx/webgl';
|
import { isWebGL2Supported } from '../fx/webgl';
|
||||||
|
|
||||||
@ -91,7 +91,11 @@ export type ProgramConstructor<T extends GL2Program> = new (
|
|||||||
fs?: string
|
fs?: string
|
||||||
) => T;
|
) => T;
|
||||||
|
|
||||||
export abstract class GL2 extends RenderItem {
|
export interface EGL2Event extends ERenderItemEvent {}
|
||||||
|
|
||||||
|
export abstract class GL2<E extends EGL2Event = EGL2Event> extends RenderItem<
|
||||||
|
EGL2Event | E
|
||||||
|
> {
|
||||||
/** 是否支持此组件 */
|
/** 是否支持此组件 */
|
||||||
static readonly support: boolean = isWebGL2Supported();
|
static readonly support: boolean = isWebGL2Supported();
|
||||||
|
|
||||||
|
@ -1,81 +0,0 @@
|
|||||||
import { FloorItemDetail } from '@/plugin/fx/itemDetail';
|
|
||||||
import { FloorDamageExtends } from './preset/damage';
|
|
||||||
import { LayerDoorAnimate } from './preset/floor';
|
|
||||||
import { HeroRenderer } from './preset/hero';
|
|
||||||
import { LayerGroup, FloorLayer } from './preset/layer';
|
|
||||||
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';
|
|
||||||
import { LayerGroupHalo } from '@/plugin/fx/halo';
|
|
||||||
import { FloorViewport } from './preset/viewport';
|
|
||||||
import { Container } from './container';
|
|
||||||
import { PopText } from '@/plugin/fx/pop';
|
|
||||||
import { FloorChange } from '@/plugin/fallback';
|
|
||||||
import tips from '@/data/tips.json';
|
|
||||||
|
|
||||||
let main: MotaRenderer;
|
|
||||||
|
|
||||||
Mota.require('var', 'loading').once('coreInit', () => {
|
|
||||||
const render = new MotaRenderer();
|
|
||||||
main = render;
|
|
||||||
render.hide();
|
|
||||||
|
|
||||||
const mapDraw = new Container();
|
|
||||||
const layer = new LayerGroup();
|
|
||||||
const pop = new PopText('static');
|
|
||||||
const floorChange = new FloorChange('static');
|
|
||||||
mapDraw.id = 'map-draw';
|
|
||||||
layer.id = 'layer-main';
|
|
||||||
pop.id = 'pop-main';
|
|
||||||
floorChange.id = 'floor-change';
|
|
||||||
|
|
||||||
mapDraw.setHD(true);
|
|
||||||
mapDraw.setAntiAliasing(false);
|
|
||||||
mapDraw.size(core._PX_, core._PY_);
|
|
||||||
floorChange.size(480, 480);
|
|
||||||
floorChange.setHD(true);
|
|
||||||
floorChange.setZIndex(50);
|
|
||||||
floorChange.setTips(tips);
|
|
||||||
pop.setZIndex(80);
|
|
||||||
|
|
||||||
['bg', 'bg2', 'event', 'fg', 'fg2'].forEach(v => {
|
|
||||||
layer.addLayer(v as FloorLayer);
|
|
||||||
});
|
|
||||||
|
|
||||||
const damage = new FloorDamageExtends();
|
|
||||||
const hero = new HeroRenderer();
|
|
||||||
const detail = new FloorItemDetail();
|
|
||||||
const door = new LayerDoorAnimate();
|
|
||||||
const shadow = new LayerShadowExtends();
|
|
||||||
const filter = new LayerGroupFilter();
|
|
||||||
const animate = new LayerGroupAnimate();
|
|
||||||
const portal = new LayerGroupPortal();
|
|
||||||
const halo = new LayerGroupHalo();
|
|
||||||
const viewport = new FloorViewport();
|
|
||||||
layer.extends(damage);
|
|
||||||
layer.extends(detail);
|
|
||||||
layer.extends(filter);
|
|
||||||
layer.extends(portal);
|
|
||||||
layer.extends(halo);
|
|
||||||
layer.getLayer('event')?.extends(hero);
|
|
||||||
layer.getLayer('event')?.extends(door);
|
|
||||||
layer.getLayer('event')?.extends(shadow);
|
|
||||||
layer.extends(animate);
|
|
||||||
layer.extends(viewport);
|
|
||||||
|
|
||||||
render.appendChild(mapDraw);
|
|
||||||
mapDraw.appendChild(layer);
|
|
||||||
layer.appendChild(pop);
|
|
||||||
mapDraw.appendChild(floorChange);
|
|
||||||
console.log(render);
|
|
||||||
});
|
|
||||||
|
|
||||||
Mota.require('var', 'hook').on('reset', () => {
|
|
||||||
main.show();
|
|
||||||
});
|
|
||||||
|
|
||||||
Mota.require('var', 'hook').on('restart', () => {
|
|
||||||
main.hide();
|
|
||||||
});
|
|
145
src/core/render/index.tsx
Normal file
145
src/core/render/index.tsx
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
import { FloorItemDetail } from '@/plugin/fx/itemDetail';
|
||||||
|
import { FloorDamageExtends } from './preset/damage';
|
||||||
|
import { LayerDoorAnimate } from './preset/floor';
|
||||||
|
import { HeroRenderer } from './preset/hero';
|
||||||
|
import { LayerGroup, FloorLayer } from './preset/layer';
|
||||||
|
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';
|
||||||
|
import { LayerGroupHalo } from '@/plugin/fx/halo';
|
||||||
|
import { FloorViewport } from './preset/viewport';
|
||||||
|
import { Container } from './container';
|
||||||
|
import { PopText } from '@/plugin/fx/pop';
|
||||||
|
import { FloorChange } from '@/plugin/fallback';
|
||||||
|
import { onTick, render } from './renderer';
|
||||||
|
import { MotaOffscreenCanvas2D } from '../fx/canvas2d';
|
||||||
|
import { SpriteComponent } from './renderer/elements';
|
||||||
|
import { defineComponent, onActivated, onMounted, ref } from 'vue';
|
||||||
|
import { Ticker } from 'mutate-animate';
|
||||||
|
import { Sprite } from './sprite';
|
||||||
|
|
||||||
|
let main: MotaRenderer;
|
||||||
|
|
||||||
|
Mota.require('var', 'loading').once('coreInit', () => {
|
||||||
|
main = new MotaRenderer();
|
||||||
|
|
||||||
|
const Com = defineComponent(props => {
|
||||||
|
const group = ref<LayerGroup>();
|
||||||
|
|
||||||
|
return () => (
|
||||||
|
<container
|
||||||
|
id="map-draw"
|
||||||
|
hd
|
||||||
|
antiAliasing={false}
|
||||||
|
width={core._PX_}
|
||||||
|
height={core._PY_}
|
||||||
|
>
|
||||||
|
<layer-group
|
||||||
|
id="layer-main"
|
||||||
|
ref={group}
|
||||||
|
ex={[
|
||||||
|
new FloorDamageExtends(),
|
||||||
|
new FloorItemDetail(),
|
||||||
|
new LayerGroupFilter(),
|
||||||
|
new LayerGroupPortal(),
|
||||||
|
new LayerGroupHalo(),
|
||||||
|
new LayerGroupAnimate(),
|
||||||
|
new FloorViewport()
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<layer layer="bg" zIndex={10}></layer>
|
||||||
|
<layer layer="bg2" zIndex={20}></layer>
|
||||||
|
<layer
|
||||||
|
layer="event"
|
||||||
|
zIndex={30}
|
||||||
|
ex={[
|
||||||
|
new HeroRenderer(),
|
||||||
|
new LayerDoorAnimate(),
|
||||||
|
new LayerShadowExtends()
|
||||||
|
]}
|
||||||
|
></layer>
|
||||||
|
<layer layer="fg" zIndex={40}></layer>
|
||||||
|
<layer layer="fg2" zIndex={50}></layer>
|
||||||
|
<PopText id="pop-main" zIndex={80}></PopText>
|
||||||
|
</layer-group>
|
||||||
|
<FloorChange id="floor-change" zIndex={50}></FloorChange>
|
||||||
|
</container>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
main.hide();
|
||||||
|
render(<Com></Com>, main);
|
||||||
|
|
||||||
|
// const mapDraw = new Container();
|
||||||
|
// const layer = new LayerGroup();
|
||||||
|
// const pop = new PopText('static');
|
||||||
|
// const floorChange = new FloorChange('static');
|
||||||
|
// mapDraw.id = 'map-draw';
|
||||||
|
// layer.id = 'layer-main';
|
||||||
|
// pop.id = 'pop-main';
|
||||||
|
// floorChange.id = 'floor-change';
|
||||||
|
|
||||||
|
// mapDraw.setHD(true);
|
||||||
|
// mapDraw.setAntiAliasing(false);
|
||||||
|
// mapDraw.size(core._PX_, core._PY_);
|
||||||
|
// floorChange.size(480, 480);
|
||||||
|
// floorChange.setHD(true);
|
||||||
|
// floorChange.setZIndex(50);
|
||||||
|
// floorChange.setTips(tips);
|
||||||
|
// pop.setZIndex(80);
|
||||||
|
|
||||||
|
// ['bg', 'bg2', 'event', 'fg', 'fg2'].forEach(v => {
|
||||||
|
// layer.addLayer(v as FloorLayer);
|
||||||
|
// });
|
||||||
|
|
||||||
|
// const damage = new FloorDamageExtends();
|
||||||
|
// const hero = new HeroRenderer();
|
||||||
|
// const detail = new FloorItemDetail();
|
||||||
|
// const door = new LayerDoorAnimate();
|
||||||
|
// const shadow = new LayerShadowExtends();
|
||||||
|
// const filter = new LayerGroupFilter();
|
||||||
|
// const animate = new LayerGroupAnimate();
|
||||||
|
// const portal = new LayerGroupPortal();
|
||||||
|
// const halo = new LayerGroupHalo();
|
||||||
|
// const viewport = new FloorViewport();
|
||||||
|
// layer.extends(damage);
|
||||||
|
// layer.extends(detail);
|
||||||
|
// layer.extends(filter);
|
||||||
|
// layer.extends(portal);
|
||||||
|
// layer.extends(halo);
|
||||||
|
// layer.getLayer('event')?.extends(hero);
|
||||||
|
// layer.getLayer('event')?.extends(door);
|
||||||
|
// layer.getLayer('event')?.extends(shadow);
|
||||||
|
// layer.extends(animate);
|
||||||
|
// layer.extends(viewport);
|
||||||
|
|
||||||
|
// main.appendChild(mapDraw);
|
||||||
|
// mapDraw.appendChild(layer);
|
||||||
|
// layer.appendChild(pop);
|
||||||
|
// mapDraw.appendChild(floorChange);
|
||||||
|
console.log(main);
|
||||||
|
});
|
||||||
|
|
||||||
|
Mota.require('var', 'hook').on('reset', () => {
|
||||||
|
main.show();
|
||||||
|
});
|
||||||
|
|
||||||
|
Mota.require('var', 'hook').on('restart', () => {
|
||||||
|
main.hide();
|
||||||
|
});
|
||||||
|
|
||||||
|
export * from './preset';
|
||||||
|
export * from './renderer';
|
||||||
|
export * from './adapter';
|
||||||
|
export * from './cache';
|
||||||
|
export * from './camera';
|
||||||
|
export * from './container';
|
||||||
|
export * from './gl2';
|
||||||
|
export * from './item';
|
||||||
|
export * from './render';
|
||||||
|
export * from './shader';
|
||||||
|
export * from './sprite';
|
||||||
|
export * from './transform';
|
||||||
|
export * from './utils';
|
@ -4,6 +4,7 @@ import { MotaOffscreenCanvas2D } from '../fx/canvas2d';
|
|||||||
import { Ticker, TickerFn } from 'mutate-animate';
|
import { Ticker, TickerFn } from 'mutate-animate';
|
||||||
import { Transform } from './transform';
|
import { Transform } from './transform';
|
||||||
import { logger } from '../common/logger';
|
import { logger } from '../common/logger';
|
||||||
|
import { ElementNamespace, ComponentInternalInstance } from 'vue';
|
||||||
|
|
||||||
export type RenderFunction = (
|
export type RenderFunction = (
|
||||||
canvas: MotaOffscreenCanvas2D,
|
canvas: MotaOffscreenCanvas2D,
|
||||||
@ -117,6 +118,24 @@ interface IRenderTickerSupport {
|
|||||||
hasTicker(id: number): boolean;
|
hasTicker(id: number): boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface IRenderVueSupport {
|
||||||
|
/**
|
||||||
|
* 在 jsx, vue 中当属性改变后触发此函数,用于处理响应式等情况
|
||||||
|
* @param key 属性键名
|
||||||
|
* @param prevValue 该属性先前的数值
|
||||||
|
* @param nextValue 该属性当前的数值
|
||||||
|
* @param namespace 元素命名空间
|
||||||
|
* @param parentComponent 元素的父组件
|
||||||
|
*/
|
||||||
|
patchProp(
|
||||||
|
key: string,
|
||||||
|
prevValue: any,
|
||||||
|
nextValue: any,
|
||||||
|
namespace?: ElementNamespace,
|
||||||
|
parentComponent?: ComponentInternalInstance | null
|
||||||
|
): void;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ERenderItemEvent {
|
export interface ERenderItemEvent {
|
||||||
beforeUpdate: [item?: RenderItem];
|
beforeUpdate: [item?: RenderItem];
|
||||||
afterUpdate: [item?: RenderItem];
|
afterUpdate: [item?: RenderItem];
|
||||||
@ -144,7 +163,9 @@ export abstract class RenderItem<E extends ERenderItemEvent = ERenderItemEvent>
|
|||||||
IRenderAnchor,
|
IRenderAnchor,
|
||||||
IRenderConfig,
|
IRenderConfig,
|
||||||
IRenderFrame,
|
IRenderFrame,
|
||||||
IRenderTickerSupport
|
IRenderTickerSupport,
|
||||||
|
IRenderChildable,
|
||||||
|
IRenderVueSupport
|
||||||
{
|
{
|
||||||
/** 渲染的全局ticker */
|
/** 渲染的全局ticker */
|
||||||
static ticker: Ticker = new Ticker();
|
static ticker: Ticker = new Ticker();
|
||||||
@ -194,16 +215,34 @@ export abstract class RenderItem<E extends ERenderItemEvent = ERenderItemEvent>
|
|||||||
hidden: boolean = false;
|
hidden: boolean = false;
|
||||||
/** 滤镜 */
|
/** 滤镜 */
|
||||||
filter: string = 'none';
|
filter: string = 'none';
|
||||||
|
/** 混合方式 */
|
||||||
|
composite: GlobalCompositeOperation = 'source-over';
|
||||||
|
/** 不透明度 */
|
||||||
|
alpha: number = 1;
|
||||||
|
|
||||||
|
private _parent?: RenderItem;
|
||||||
/** 当前元素的父元素 */
|
/** 当前元素的父元素 */
|
||||||
parent?: RenderItem & IRenderChildable;
|
get parent() {
|
||||||
|
return this._parent;
|
||||||
|
}
|
||||||
/** 当前元素是否为根元素 */
|
/** 当前元素是否为根元素 */
|
||||||
readonly isRoot: boolean = false;
|
readonly isRoot: boolean = false;
|
||||||
|
|
||||||
protected needUpdate: boolean = false;
|
protected needUpdate: boolean = false;
|
||||||
|
|
||||||
/** 该渲染元素的模型变换矩阵 */
|
private _transform: Transform = new Transform();
|
||||||
transform: Transform = new Transform();
|
/** 设置该渲染元素的模型变换矩阵 */
|
||||||
|
set transform(value: Transform) {
|
||||||
|
this._transform = value;
|
||||||
|
this.update();
|
||||||
|
}
|
||||||
|
/** 获取该渲染元素的模型变换矩阵 */
|
||||||
|
get transform() {
|
||||||
|
this.update();
|
||||||
|
return this._transform;
|
||||||
|
}
|
||||||
|
/** 该渲染元素的子元素 */
|
||||||
|
children: Set<RenderItem<ERenderItemEvent>> = new Set();
|
||||||
|
|
||||||
/** 渲染缓存信息 */
|
/** 渲染缓存信息 */
|
||||||
protected cache: MotaOffscreenCanvas2D = new MotaOffscreenCanvas2D();
|
protected cache: MotaOffscreenCanvas2D = new MotaOffscreenCanvas2D();
|
||||||
@ -278,10 +317,13 @@ export abstract class RenderItem<E extends ERenderItemEvent = ERenderItemEvent>
|
|||||||
const ax = -this.anchorX * this.width;
|
const ax = -this.anchorX * this.width;
|
||||||
const ay = -this.anchorY * this.height;
|
const ay = -this.anchorY * this.height;
|
||||||
|
|
||||||
canvas.ctx.save();
|
const ctx = canvas.ctx;
|
||||||
|
ctx.save();
|
||||||
canvas.setAntiAliasing(this.antiAliasing);
|
canvas.setAntiAliasing(this.antiAliasing);
|
||||||
if (this.enableCache) canvas.ctx.filter = this.filter;
|
if (this.enableCache) canvas.ctx.filter = this.filter;
|
||||||
if (this.type === 'static') transformCanvas(canvas, tran);
|
if (this.type === 'static') transformCanvas(canvas, tran);
|
||||||
|
ctx.globalAlpha = this.alpha;
|
||||||
|
ctx.globalCompositeOperation = this.composite;
|
||||||
if (this.enableCache) {
|
if (this.enableCache) {
|
||||||
const { width, height, ctx } = this.cache;
|
const { width, height, ctx } = this.cache;
|
||||||
if (this.cacheDirty) {
|
if (this.cacheDirty) {
|
||||||
@ -319,6 +361,24 @@ export abstract class RenderItem<E extends ERenderItemEvent = ERenderItemEvent>
|
|||||||
this.update(this);
|
this.update(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置本元素渲染时的混合方式
|
||||||
|
* @param composite 混合方式
|
||||||
|
*/
|
||||||
|
setComposite(composite: GlobalCompositeOperation) {
|
||||||
|
this.composite = composite;
|
||||||
|
this.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置本元素的不透明度
|
||||||
|
* @param alpha 不透明度
|
||||||
|
*/
|
||||||
|
setAlpha(alpha: number) {
|
||||||
|
this.alpha = alpha;
|
||||||
|
this.update();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取当前元素的绝对位置(不建议使用,因为应当很少会有获取绝对位置的需求)
|
* 获取当前元素的绝对位置(不建议使用,因为应当很少会有获取绝对位置的需求)
|
||||||
*/
|
*/
|
||||||
@ -426,10 +486,10 @@ export abstract class RenderItem<E extends ERenderItemEvent = ERenderItemEvent>
|
|||||||
* 将这个渲染元素添加到其他父元素上
|
* 将这个渲染元素添加到其他父元素上
|
||||||
* @param parent 父元素
|
* @param parent 父元素
|
||||||
*/
|
*/
|
||||||
append(parent: IRenderChildable & RenderItem) {
|
append(parent: RenderItem) {
|
||||||
this.remove();
|
this.remove();
|
||||||
parent.children.add(this);
|
parent.children.add(this);
|
||||||
this.parent = parent;
|
this._parent = parent;
|
||||||
parent.requestSort();
|
parent.requestSort();
|
||||||
this.needUpdate = false;
|
this.needUpdate = false;
|
||||||
this.update();
|
this.update();
|
||||||
@ -447,7 +507,7 @@ export abstract class RenderItem<E extends ERenderItemEvent = ERenderItemEvent>
|
|||||||
if (!this.parent) return false;
|
if (!this.parent) return false;
|
||||||
const parent = this.parent;
|
const parent = this.parent;
|
||||||
const success = parent.children.delete(this);
|
const success = parent.children.delete(this);
|
||||||
this.parent = void 0;
|
this._parent = void 0;
|
||||||
parent.requestSort();
|
parent.requestSort();
|
||||||
parent.update();
|
parent.update();
|
||||||
if (!success) return false;
|
if (!success) return false;
|
||||||
@ -455,6 +515,179 @@ export abstract class RenderItem<E extends ERenderItemEvent = ERenderItemEvent>
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加子元素,默认没有任何行为且会抛出警告,你需要在自己的RenderItem继承类中复写它,才可以使用
|
||||||
|
* @param child 子元素
|
||||||
|
*/
|
||||||
|
appendChild(...child: RenderItem<any>[]): void {
|
||||||
|
logger.warn(35);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除子元素,默认没有任何行为且会抛出警告,你需要在自己的RenderItem继承类中复写它,才可以使用
|
||||||
|
* @param child 子元素
|
||||||
|
*/
|
||||||
|
removeChild(...child: RenderItem<any>[]): void {
|
||||||
|
logger.warn(36);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 申请对元素进行排序,默认没有任何行为且会抛出警告,你需要在自己的RenderItem继承类中复写它,才可以使用
|
||||||
|
*/
|
||||||
|
requestSort(): void {
|
||||||
|
logger.warn(37);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断一个prop是否是期望类型
|
||||||
|
* @param value 实际值
|
||||||
|
* @param expected 期望类型
|
||||||
|
* @param key 键名
|
||||||
|
*/
|
||||||
|
protected assertType(
|
||||||
|
value: any,
|
||||||
|
expected: string | (new () => any),
|
||||||
|
key: string
|
||||||
|
) {
|
||||||
|
if (typeof expected === 'string') {
|
||||||
|
const type = typeof value;
|
||||||
|
if (type !== expected) {
|
||||||
|
logger.warn(21, key, expected, type);
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (value instanceof expected) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
logger.warn(
|
||||||
|
21,
|
||||||
|
key,
|
||||||
|
expected.name,
|
||||||
|
value?.constructor?.name ?? typeof value
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析事件key
|
||||||
|
* @param key 键名
|
||||||
|
* @returns 返回字符串表示解析后的键名,返回布尔值表示不是事件
|
||||||
|
*/
|
||||||
|
protected parseEvent(key: string): string | false {
|
||||||
|
if (key.startsWith('on')) {
|
||||||
|
const code = key.charCodeAt(2);
|
||||||
|
if (code >= 65 && code <= 90) {
|
||||||
|
return key[2].toLowerCase() + key.slice(3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
patchProp(
|
||||||
|
key: string,
|
||||||
|
prevValue: any,
|
||||||
|
nextValue: any,
|
||||||
|
namespace?: ElementNamespace,
|
||||||
|
parentComponent?: ComponentInternalInstance | null
|
||||||
|
): void {
|
||||||
|
switch (key) {
|
||||||
|
case 'x': {
|
||||||
|
if (!this.assertType(nextValue, 'number', key)) return;
|
||||||
|
this.pos(nextValue, this.transform.y);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case 'y': {
|
||||||
|
if (!this.assertType(nextValue, 'number', key)) return;
|
||||||
|
this.pos(this.transform.x, nextValue);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case 'anchorX': {
|
||||||
|
if (!this.assertType(nextValue, 'number', key)) return;
|
||||||
|
this.setAnchor(nextValue, this.anchorY);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case 'anchorY': {
|
||||||
|
if (!this.assertType(nextValue, 'number', key)) return;
|
||||||
|
this.setAnchor(this.anchorX, nextValue);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case 'zIndex': {
|
||||||
|
if (!this.assertType(nextValue, 'number', key)) return;
|
||||||
|
this.setZIndex(nextValue);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case 'width': {
|
||||||
|
if (!this.assertType(nextValue, 'number', key)) return;
|
||||||
|
this.size(nextValue, this.height);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case 'height': {
|
||||||
|
if (!this.assertType(nextValue, 'number', key)) return;
|
||||||
|
this.size(this.width, nextValue);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case 'filter': {
|
||||||
|
if (!this.assertType(nextValue, 'string', key)) return;
|
||||||
|
this.setFilter(this.filter);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case 'hd': {
|
||||||
|
if (!this.assertType(nextValue, 'boolean', key)) return;
|
||||||
|
this.setHD(nextValue);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case 'antiAliasing': {
|
||||||
|
if (!this.assertType(nextValue, 'boolean', key)) return;
|
||||||
|
this.setAntiAliasing(nextValue);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case 'hidden': {
|
||||||
|
if (!this.assertType(nextValue, 'boolean', key)) return;
|
||||||
|
if (nextValue) this.hide();
|
||||||
|
else this.show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case 'transform': {
|
||||||
|
if (!this.assertType(nextValue, Transform, key)) return;
|
||||||
|
this.transform = nextValue;
|
||||||
|
this.update();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case 'type': {
|
||||||
|
if (!this.assertType(nextValue, 'string', key)) return;
|
||||||
|
this.type = nextValue;
|
||||||
|
this.update();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case 'id': {
|
||||||
|
if (!this.assertType(nextValue, 'string', key)) return;
|
||||||
|
this.id = nextValue;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case 'alpha': {
|
||||||
|
if (!this.assertType(nextValue, 'number', key)) return;
|
||||||
|
this.setAlpha(nextValue);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case 'composite': {
|
||||||
|
if (!this.assertType(nextValue, 'string', key)) return;
|
||||||
|
this.setComposite(nextValue);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const ev = this.parseEvent(key);
|
||||||
|
if (ev) {
|
||||||
|
if (prevValue) {
|
||||||
|
this.off(ev as keyof ERenderItemEvent, prevValue);
|
||||||
|
}
|
||||||
|
this.on(ev as keyof ERenderItemEvent, nextValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 摧毁这个渲染元素,摧毁后不应继续使用
|
* 摧毁这个渲染元素,摧毁后不应继续使用
|
||||||
*/
|
*/
|
||||||
|
@ -4,14 +4,15 @@ import { Sprite } from '../sprite';
|
|||||||
import { HeroRenderer } from './hero';
|
import { HeroRenderer } from './hero';
|
||||||
import { ILayerGroupRenderExtends, LayerGroup } from './layer';
|
import { ILayerGroupRenderExtends, LayerGroup } from './layer';
|
||||||
import { MotaOffscreenCanvas2D } from '@/core/fx/canvas2d';
|
import { MotaOffscreenCanvas2D } from '@/core/fx/canvas2d';
|
||||||
import { transformCanvas } from '../item';
|
import { ERenderItemEvent, RenderItem, transformCanvas } from '../item';
|
||||||
|
import { Transform } from '../transform';
|
||||||
|
|
||||||
export class LayerGroupAnimate implements ILayerGroupRenderExtends {
|
export class LayerGroupAnimate implements ILayerGroupRenderExtends {
|
||||||
static animateList: Set<LayerGroupAnimate> = new Set();
|
static animateList: Set<LayerGroupAnimate> = new Set();
|
||||||
id: string = 'animate';
|
id: string = 'animate';
|
||||||
|
|
||||||
group!: LayerGroup;
|
group!: LayerGroup;
|
||||||
hero!: HeroRenderer;
|
hero?: HeroRenderer;
|
||||||
animate!: Animate;
|
animate!: Animate;
|
||||||
|
|
||||||
private animation: Set<AnimateData> = new Set();
|
private animation: Set<AnimateData> = new Set();
|
||||||
@ -28,7 +29,8 @@ export class LayerGroupAnimate implements ILayerGroupRenderExtends {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private updatePosition(animate: AnimateData) {
|
private updatePosition(animate: AnimateData) {
|
||||||
if (!this.hero.renderable) return;
|
if (!this.checkHero()) return;
|
||||||
|
if (!this.hero?.renderable) return;
|
||||||
const { x, y } = this.hero.renderable;
|
const { x, y } = this.hero.renderable;
|
||||||
const cell = this.group.cellSize;
|
const cell = this.group.cellSize;
|
||||||
const half = cell / 2;
|
const half = cell / 2;
|
||||||
@ -48,30 +50,37 @@ export class LayerGroupAnimate implements ILayerGroupRenderExtends {
|
|||||||
};
|
};
|
||||||
|
|
||||||
private listen() {
|
private listen() {
|
||||||
this.hero.on('moveTick', this.onMoveTick);
|
if (this.checkHero()) {
|
||||||
|
this.hero!.on('moveTick', this.onMoveTick);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private checkHero() {
|
||||||
|
if (this.hero) return true;
|
||||||
|
const ex = this.group.getLayer('event')?.getExtends('floor-hero');
|
||||||
|
if (ex instanceof HeroRenderer) {
|
||||||
|
this.hero = ex;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
awake(group: LayerGroup): void {
|
awake(group: LayerGroup): void {
|
||||||
this.group = group;
|
this.group = group;
|
||||||
const ex = group.getLayer('event')?.getExtends('floor-hero');
|
this.animate = new Animate();
|
||||||
if (ex instanceof HeroRenderer) {
|
this.animate.size(group.width, group.height);
|
||||||
this.hero = ex;
|
this.animate.setHD(true);
|
||||||
this.animate = new Animate();
|
this.animate.setZIndex(100);
|
||||||
this.animate.size(group.width, group.height);
|
group.appendChild(this.animate);
|
||||||
this.animate.setHD(true);
|
LayerGroupAnimate.animateList.add(this);
|
||||||
this.animate.setZIndex(100);
|
this.listen();
|
||||||
group.appendChild(this.animate);
|
|
||||||
LayerGroupAnimate.animateList.add(this);
|
|
||||||
this.listen();
|
|
||||||
} else {
|
|
||||||
logger.error(14);
|
|
||||||
group.removeExtends('animate');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onDestroy(group: LayerGroup): void {
|
onDestroy(group: LayerGroup): void {
|
||||||
this.hero.off('moveTick', this.onMoveTick);
|
if (this.checkHero()) {
|
||||||
LayerGroupAnimate.animateList.delete(this);
|
this.hero!.off('moveTick', this.onMoveTick);
|
||||||
|
LayerGroupAnimate.animateList.delete(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,7 +98,9 @@ interface AnimateData {
|
|||||||
readonly absolute: boolean;
|
readonly absolute: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Animate extends Sprite {
|
export interface EAnimateEvent extends ERenderItemEvent {}
|
||||||
|
|
||||||
|
export class Animate extends RenderItem<EAnimateEvent> {
|
||||||
/** 绝对位置的动画 */
|
/** 绝对位置的动画 */
|
||||||
private absoluteAnimates: Set<AnimateData> = new Set();
|
private absoluteAnimates: Set<AnimateData> = new Set();
|
||||||
/** 静态位置的动画 */
|
/** 静态位置的动画 */
|
||||||
@ -102,18 +113,6 @@ export class Animate extends Sprite {
|
|||||||
constructor() {
|
constructor() {
|
||||||
super('absolute', false, true);
|
super('absolute', false, true);
|
||||||
|
|
||||||
this.setRenderFn((canvas, transform) => {
|
|
||||||
if (
|
|
||||||
this.absoluteAnimates.size === 0 &&
|
|
||||||
this.staticAnimates.size === 0
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.drawAnimates(this.absoluteAnimates, canvas);
|
|
||||||
transformCanvas(canvas, transform);
|
|
||||||
this.drawAnimates(this.staticAnimates, canvas);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.delegation = this.delegateTicker(time => {
|
this.delegation = this.delegateTicker(time => {
|
||||||
if (time - this.lastTime < 50) return;
|
if (time - this.lastTime < 50) return;
|
||||||
this.lastTime = time;
|
this.lastTime = time;
|
||||||
@ -129,6 +128,21 @@ export class Animate extends Sprite {
|
|||||||
adapter.add(this);
|
adapter.add(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected render(
|
||||||
|
canvas: MotaOffscreenCanvas2D,
|
||||||
|
transform: Transform
|
||||||
|
): void {
|
||||||
|
if (
|
||||||
|
this.absoluteAnimates.size === 0 &&
|
||||||
|
this.staticAnimates.size === 0
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.drawAnimates(this.absoluteAnimates, canvas);
|
||||||
|
transformCanvas(canvas, transform);
|
||||||
|
this.drawAnimates(this.staticAnimates, canvas);
|
||||||
|
}
|
||||||
|
|
||||||
private drawAnimates(
|
private drawAnimates(
|
||||||
data: Set<AnimateData>,
|
data: Set<AnimateData>,
|
||||||
canvas: MotaOffscreenCanvas2D
|
canvas: MotaOffscreenCanvas2D
|
||||||
|
@ -21,7 +21,7 @@ import type {
|
|||||||
import { MotaOffscreenCanvas2D } from '@/core/fx/canvas2d';
|
import { MotaOffscreenCanvas2D } from '@/core/fx/canvas2d';
|
||||||
import { isNil } from 'lodash-es';
|
import { isNil } from 'lodash-es';
|
||||||
import { getDamageColor } from '@/plugin/utils';
|
import { getDamageColor } from '@/plugin/utils';
|
||||||
import { RenderItem, transformCanvas } from '../item';
|
import { ERenderItemEvent, RenderItem, transformCanvas } from '../item';
|
||||||
import EventEmitter from 'eventemitter3';
|
import EventEmitter from 'eventemitter3';
|
||||||
import { Transform } from '../transform';
|
import { Transform } from '../transform';
|
||||||
|
|
||||||
@ -123,7 +123,7 @@ export interface DamageRenderable {
|
|||||||
strokeWidth?: number;
|
strokeWidth?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface EDamageEvent extends ESpriteEvent {
|
export interface EDamageEvent extends ERenderItemEvent {
|
||||||
setMapSize: [width: number, height: number];
|
setMapSize: [width: number, height: number];
|
||||||
beforeDamageRender: [need: Set<number>, transform: Transform];
|
beforeDamageRender: [need: Set<number>, transform: Transform];
|
||||||
updateBlocks: [blocks: Set<number>];
|
updateBlocks: [blocks: Set<number>];
|
||||||
|
82
src/core/render/preset/graphics.ts
Normal file
82
src/core/render/preset/graphics.ts
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
import { MotaOffscreenCanvas2D } from '@/core/fx/canvas2d';
|
||||||
|
import { RenderItem } from '../item';
|
||||||
|
import { Transform } from '../transform';
|
||||||
|
|
||||||
|
type DrawType = 'fill' | 'stroke';
|
||||||
|
|
||||||
|
interface IGraphicProperty {
|
||||||
|
/** 渲染方式,是描边还是填充 */
|
||||||
|
mode: DrawType;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Graphics extends RenderItem {
|
||||||
|
protected render(
|
||||||
|
canvas: MotaOffscreenCanvas2D,
|
||||||
|
transform: Transform
|
||||||
|
): void {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Rect extends RenderItem implements IGraphicProperty {
|
||||||
|
mode: DrawType = 'fill';
|
||||||
|
|
||||||
|
rectX: number = 0;
|
||||||
|
rectY: number = 0;
|
||||||
|
rectWidth: number = 100;
|
||||||
|
rectHeight: number = 100;
|
||||||
|
|
||||||
|
protected render(
|
||||||
|
canvas: MotaOffscreenCanvas2D,
|
||||||
|
transform: Transform
|
||||||
|
): void {
|
||||||
|
const ctx = canvas.ctx;
|
||||||
|
ctx.rect(this.rectX, this.rectY, this.rectWidth, this.rectHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
setPos(x: number, y: number) {
|
||||||
|
this.rectX = x;
|
||||||
|
this.rectY = y;
|
||||||
|
this.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
setSize(w: number, h: number) {
|
||||||
|
this.rectWidth = w;
|
||||||
|
this.rectHeight = h;
|
||||||
|
this.update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Circle extends RenderItem implements IGraphicProperty {
|
||||||
|
mode: DrawType = 'fill';
|
||||||
|
|
||||||
|
protected render(
|
||||||
|
canvas: MotaOffscreenCanvas2D,
|
||||||
|
transform: Transform
|
||||||
|
): void {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Ellipse extends RenderItem implements IGraphicProperty {
|
||||||
|
mode: DrawType = 'fill';
|
||||||
|
|
||||||
|
protected render(
|
||||||
|
canvas: MotaOffscreenCanvas2D,
|
||||||
|
transform: Transform
|
||||||
|
): void {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Line extends RenderItem implements IGraphicProperty {
|
||||||
|
mode: DrawType = 'fill';
|
||||||
|
|
||||||
|
protected render(
|
||||||
|
canvas: MotaOffscreenCanvas2D,
|
||||||
|
transform: Transform
|
||||||
|
): void {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Path extends RenderItem implements IGraphicProperty {
|
||||||
|
mode: DrawType = 'fill';
|
||||||
|
|
||||||
|
protected render(
|
||||||
|
canvas: MotaOffscreenCanvas2D,
|
||||||
|
transform: Transform
|
||||||
|
): void {}
|
||||||
|
}
|
8
src/core/render/preset/index.ts
Normal file
8
src/core/render/preset/index.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
export * from './animate';
|
||||||
|
export * from './block';
|
||||||
|
export * from './damage';
|
||||||
|
export * from './floor';
|
||||||
|
export * from './hero';
|
||||||
|
export * from './layer';
|
||||||
|
export * from './misc';
|
||||||
|
export * from './viewport';
|
@ -1,8 +1,13 @@
|
|||||||
import { MotaOffscreenCanvas2D } from '@/core/fx/canvas2d';
|
import { MotaOffscreenCanvas2D } from '@/core/fx/canvas2d';
|
||||||
import { Container } from '../container';
|
import { Container, EContainerEvent } from '../container';
|
||||||
import { Sprite } from '../sprite';
|
import { Sprite } from '../sprite';
|
||||||
import { TimingFn } from 'mutate-animate';
|
import { TimingFn } from 'mutate-animate';
|
||||||
import { IAnimateFrame, renderEmits, RenderItem } from '../item';
|
import {
|
||||||
|
ERenderItemEvent,
|
||||||
|
IAnimateFrame,
|
||||||
|
renderEmits,
|
||||||
|
RenderItem
|
||||||
|
} from '../item';
|
||||||
import { logger } from '@/core/common/logger';
|
import { logger } from '@/core/common/logger';
|
||||||
import { RenderableData, texture } from '../cache';
|
import { RenderableData, texture } from '../cache';
|
||||||
import {
|
import {
|
||||||
@ -14,6 +19,7 @@ import {
|
|||||||
import { Transform } from '../transform';
|
import { Transform } from '../transform';
|
||||||
import { LayerFloorBinder, LayerGroupFloorBinder } from './floor';
|
import { LayerFloorBinder, LayerGroupFloorBinder } from './floor';
|
||||||
import { RenderAdapter } from '../adapter';
|
import { RenderAdapter } from '../adapter';
|
||||||
|
import { ElementNamespace, ComponentInternalInstance } from 'vue';
|
||||||
|
|
||||||
export interface ILayerGroupRenderExtends {
|
export interface ILayerGroupRenderExtends {
|
||||||
/** 拓展的唯一标识符 */
|
/** 拓展的唯一标识符 */
|
||||||
@ -95,7 +101,12 @@ const layerZIndex: Record<FloorLayer, number> = {
|
|||||||
fg2: 50
|
fg2: 50
|
||||||
};
|
};
|
||||||
|
|
||||||
export class LayerGroup extends Container implements IAnimateFrame {
|
export interface ELayerGroupEvent extends EContainerEvent {}
|
||||||
|
|
||||||
|
export class LayerGroup
|
||||||
|
extends Container<ELayerGroupEvent>
|
||||||
|
implements IAnimateFrame
|
||||||
|
{
|
||||||
/** 地图组列表 */
|
/** 地图组列表 */
|
||||||
// static list: Set<LayerGroup> = new Set();
|
// static list: Set<LayerGroup> = new Set();
|
||||||
|
|
||||||
@ -115,7 +126,7 @@ export class LayerGroup extends Container implements IAnimateFrame {
|
|||||||
camera: Transform = new Transform();
|
camera: Transform = new Transform();
|
||||||
|
|
||||||
private needRender?: Set<number>;
|
private needRender?: Set<number>;
|
||||||
private extend: Map<string, ILayerGroupRenderExtends> = new Map();
|
readonly extend: Map<string, ILayerGroupRenderExtends> = new Map();
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super('static', true);
|
super('static', true);
|
||||||
@ -212,18 +223,28 @@ export class LayerGroup extends Container implements IAnimateFrame {
|
|||||||
* 添加显示层
|
* 添加显示层
|
||||||
* @param layer 显示层
|
* @param layer 显示层
|
||||||
*/
|
*/
|
||||||
addLayer(layer: FloorLayer) {
|
addLayer(layer: FloorLayer | Layer) {
|
||||||
const l = new Layer();
|
if (typeof layer === 'string') {
|
||||||
l.layer = layer;
|
const l = new Layer();
|
||||||
this.layers.set(layer, l);
|
l.layer = layer;
|
||||||
l.setZIndex(layerZIndex[layer]);
|
this.layers.set(layer, l);
|
||||||
this.appendChild(l);
|
l.setZIndex(layerZIndex[layer]);
|
||||||
|
this.appendChild(l);
|
||||||
|
|
||||||
for (const ex of this.extend.values()) {
|
for (const ex of this.extend.values()) {
|
||||||
ex.onLayerAdd?.(this, l);
|
ex.onLayerAdd?.(this, l);
|
||||||
|
}
|
||||||
|
|
||||||
|
return l;
|
||||||
|
} else {
|
||||||
|
if (layer.layer) {
|
||||||
|
this.layers.set(layer.layer, layer);
|
||||||
|
for (const ex of this.extend.values()) {
|
||||||
|
ex.onLayerAdd?.(this, layer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return layer;
|
||||||
}
|
}
|
||||||
|
|
||||||
return l;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -516,7 +537,9 @@ export interface LayerMovingRenderable extends RenderableData {
|
|||||||
alpha: number;
|
alpha: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Layer extends Container {
|
export interface ELayerEvent extends EContainerEvent {}
|
||||||
|
|
||||||
|
export class Layer extends Container<ELayerEvent> {
|
||||||
// 一些会用到的常量
|
// 一些会用到的常量
|
||||||
static readonly FRAME_0 = 1;
|
static readonly FRAME_0 = 1;
|
||||||
static readonly FRAME_1 = 2;
|
static readonly FRAME_1 = 2;
|
||||||
@ -1384,6 +1407,55 @@ export class Layer extends Container {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
patchProp(
|
||||||
|
key: string,
|
||||||
|
prevValue: any,
|
||||||
|
nextValue: any,
|
||||||
|
namespace?: ElementNamespace,
|
||||||
|
parentComponent?: ComponentInternalInstance | null
|
||||||
|
): void {
|
||||||
|
switch (key) {
|
||||||
|
case 'layer':
|
||||||
|
if (!this.assertType(nextValue, 'string', key)) return;
|
||||||
|
const parent = this.parent;
|
||||||
|
if (parent instanceof LayerGroup) {
|
||||||
|
parent.removeLayer(this);
|
||||||
|
this.layer = nextValue;
|
||||||
|
parent.addLayer(this);
|
||||||
|
} else {
|
||||||
|
this.layer = nextValue;
|
||||||
|
}
|
||||||
|
this.update();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private addToGroup(group: LayerGroup) {
|
||||||
|
if (this.layer) {
|
||||||
|
group.addLayer(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private removeFromGroup(group: LayerGroup) {
|
||||||
|
if (this.layer) {
|
||||||
|
group.removeLayer(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
append(parent: RenderItem): void {
|
||||||
|
super.append(parent);
|
||||||
|
if (parent instanceof LayerGroup) {
|
||||||
|
this.addToGroup(parent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
remove(): boolean {
|
||||||
|
if (this.parent instanceof LayerGroup) {
|
||||||
|
this.removeFromGroup(this.parent);
|
||||||
|
}
|
||||||
|
return super.remove();
|
||||||
|
}
|
||||||
|
|
||||||
destroy(): void {
|
destroy(): void {
|
||||||
for (const ex of this.extend.values()) {
|
for (const ex of this.extend.values()) {
|
||||||
ex.onDestroy?.(this);
|
ex.onDestroy?.(this);
|
||||||
|
@ -1,9 +1,14 @@
|
|||||||
import { MotaOffscreenCanvas2D } from '@/core/fx/canvas2d';
|
import { MotaOffscreenCanvas2D } from '@/core/fx/canvas2d';
|
||||||
import { Sprite } from '../sprite';
|
import { Sprite } from '../sprite';
|
||||||
|
import { ERenderItemEvent, RenderItem, RenderItemPosition } from '../item';
|
||||||
|
import { Transform } from '../transform';
|
||||||
|
import { ElementNamespace, ComponentInternalInstance } from 'vue';
|
||||||
|
|
||||||
type CanvasStyle = string | CanvasGradient | CanvasPattern;
|
type CanvasStyle = string | CanvasGradient | CanvasPattern;
|
||||||
|
|
||||||
export class Text extends Sprite {
|
export interface ETextEvent extends ERenderItemEvent {}
|
||||||
|
|
||||||
|
export class Text extends RenderItem<ETextEvent> {
|
||||||
text: string;
|
text: string;
|
||||||
|
|
||||||
fillStyle?: CanvasStyle = '#fff';
|
fillStyle?: CanvasStyle = '#fff';
|
||||||
@ -16,26 +21,30 @@ export class Text extends Sprite {
|
|||||||
|
|
||||||
private static measureCanvas = new MotaOffscreenCanvas2D();
|
private static measureCanvas = new MotaOffscreenCanvas2D();
|
||||||
|
|
||||||
constructor(text: string = '') {
|
constructor(text: string = '', type: RenderItemPosition = 'static') {
|
||||||
super('static', false);
|
super(type, false);
|
||||||
|
|
||||||
this.text = text;
|
this.text = text;
|
||||||
if (text.length > 0) this.calBox();
|
if (text.length > 0) this.calBox();
|
||||||
|
}
|
||||||
|
|
||||||
this.renderFn = ({ canvas, ctx }) => {
|
protected render(
|
||||||
ctx.textBaseline = 'bottom';
|
canvas: MotaOffscreenCanvas2D,
|
||||||
ctx.fillStyle = this.fillStyle ?? 'transparent';
|
transform: Transform
|
||||||
ctx.strokeStyle = this.strokeStyle ?? 'transparent';
|
): void {
|
||||||
ctx.font = this.font ?? '';
|
const ctx = canvas.ctx;
|
||||||
ctx.lineWidth = this.strokeWidth;
|
ctx.textBaseline = 'bottom';
|
||||||
|
ctx.fillStyle = this.fillStyle ?? 'transparent';
|
||||||
|
ctx.strokeStyle = this.strokeStyle ?? 'transparent';
|
||||||
|
ctx.font = this.font ?? '';
|
||||||
|
ctx.lineWidth = this.strokeWidth;
|
||||||
|
|
||||||
if (this.strokeStyle) {
|
if (this.strokeStyle) {
|
||||||
ctx.strokeText(this.text, 0, this.descent);
|
ctx.strokeText(this.text, 0, this.descent);
|
||||||
}
|
}
|
||||||
if (this.fillStyle) {
|
if (this.fillStyle) {
|
||||||
ctx.fillText(this.text, 0, this.descent);
|
ctx.fillText(this.text, 0, this.descent);
|
||||||
}
|
}
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -96,6 +105,22 @@ export class Text extends Sprite {
|
|||||||
this.descent = fontBoundingBoxAscent;
|
this.descent = fontBoundingBoxAscent;
|
||||||
this.size(width, fontBoundingBoxAscent);
|
this.size(width, fontBoundingBoxAscent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
patchProp(
|
||||||
|
key: string,
|
||||||
|
prevValue: any,
|
||||||
|
nextValue: any,
|
||||||
|
namespace?: ElementNamespace,
|
||||||
|
parentComponent?: ComponentInternalInstance | null
|
||||||
|
): void {
|
||||||
|
switch (key) {
|
||||||
|
case 'font':
|
||||||
|
if (!this.assertType(nextValue, 'string', key)) return;
|
||||||
|
this.setFont(nextValue);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
super.patchProp(key, prevValue, nextValue, namespace, parentComponent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SizedCanvasImageSource = Exclude<
|
export type SizedCanvasImageSource = Exclude<
|
||||||
@ -103,26 +128,47 @@ export type SizedCanvasImageSource = Exclude<
|
|||||||
VideoFrame | SVGElement
|
VideoFrame | SVGElement
|
||||||
>;
|
>;
|
||||||
|
|
||||||
export class Image extends Sprite {
|
export interface EImageEvent extends ERenderItemEvent {}
|
||||||
image: SizedCanvasImageSource;
|
|
||||||
|
|
||||||
constructor(image: SizedCanvasImageSource) {
|
export class Image extends RenderItem<EImageEvent> {
|
||||||
super();
|
image: CanvasImageSource;
|
||||||
|
|
||||||
|
constructor(image: CanvasImageSource, type: RenderItemPosition = 'static') {
|
||||||
|
super(type);
|
||||||
this.image = image;
|
this.image = image;
|
||||||
this.size(image.width, image.height);
|
if (image instanceof VideoFrame || image instanceof SVGElement) {
|
||||||
|
this.size(200, 200);
|
||||||
|
} else {
|
||||||
|
this.size(image.width, image.height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.renderFn = ({ canvas, ctx }) => {
|
protected render(
|
||||||
ctx.drawImage(this.image, 0, 0, canvas.width, canvas.height);
|
canvas: MotaOffscreenCanvas2D,
|
||||||
};
|
transform: Transform
|
||||||
|
): void {
|
||||||
|
const ctx = canvas.ctx;
|
||||||
|
ctx.drawImage(this.image, 0, 0, canvas.width, canvas.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置图片资源
|
* 设置图片资源
|
||||||
* @param image 图片资源
|
* @param image 图片资源
|
||||||
*/
|
*/
|
||||||
setImage(image: SizedCanvasImageSource) {
|
setImage(image: CanvasImageSource) {
|
||||||
this.image = image;
|
this.image = image;
|
||||||
this.size(image.width, image.height);
|
this.update();
|
||||||
this.update(this);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class Comment extends RenderItem {
|
||||||
|
constructor(public text: string = '') {
|
||||||
|
super('static');
|
||||||
|
this.hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render(
|
||||||
|
canvas: MotaOffscreenCanvas2D,
|
||||||
|
transform: Transform
|
||||||
|
): void {}
|
||||||
|
}
|
||||||
|
@ -9,8 +9,8 @@ export class FloorViewport implements ILayerGroupRenderExtends {
|
|||||||
id: string = 'viewport';
|
id: string = 'viewport';
|
||||||
|
|
||||||
group!: LayerGroup;
|
group!: LayerGroup;
|
||||||
hero!: HeroRenderer;
|
hero?: HeroRenderer;
|
||||||
binder!: LayerGroupFloorBinder;
|
binder?: LayerGroupFloorBinder;
|
||||||
|
|
||||||
/** 是否启用视角控制拓展 */
|
/** 是否启用视角控制拓展 */
|
||||||
enabled: boolean = true;
|
enabled: boolean = true;
|
||||||
@ -91,12 +91,13 @@ export class FloorViewport implements ILayerGroupRenderExtends {
|
|||||||
* @param y 图格纵坐标
|
* @param y 图格纵坐标
|
||||||
*/
|
*/
|
||||||
getBoundedPosition(x: number, y: number) {
|
getBoundedPosition(x: number, y: number) {
|
||||||
|
if (!this.checkDependency()) return { x, y };
|
||||||
if (!this.boundX && !this.boundY) return { x, y };
|
if (!this.boundX && !this.boundY) return { x, y };
|
||||||
const width = core._WIDTH_;
|
const width = core._WIDTH_;
|
||||||
const height = core._HEIGHT_;
|
const height = core._HEIGHT_;
|
||||||
const minX = (width - 1) / 2;
|
const minX = (width - 1) / 2;
|
||||||
const minY = (height - 1) / 2;
|
const minY = (height - 1) / 2;
|
||||||
const floor = core.status.maps[this.binder.getFloor()];
|
const floor = core.status.maps[this.binder!.getFloor()];
|
||||||
const maxX = floor.width - minX - 1;
|
const maxX = floor.width - minX - 1;
|
||||||
const maxY = floor.height - minY - 1;
|
const maxY = floor.height - minY - 1;
|
||||||
|
|
||||||
@ -151,6 +152,7 @@ export class FloorViewport implements ILayerGroupRenderExtends {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private createMoveTransition() {
|
private createMoveTransition() {
|
||||||
|
if (!this.checkDependency()) return;
|
||||||
let xTarget: number = 0;
|
let xTarget: number = 0;
|
||||||
let yTarget: number = 0;
|
let yTarget: number = 0;
|
||||||
let xStart: number = this.ox;
|
let xStart: number = this.ox;
|
||||||
@ -159,7 +161,7 @@ export class FloorViewport implements ILayerGroupRenderExtends {
|
|||||||
let yStartTime: number = Date.now();
|
let yStartTime: number = Date.now();
|
||||||
let ending: boolean = false;
|
let ending: boolean = false;
|
||||||
// 这个数等于 sinh(2),用这个数的话,可以正好在刚开始移动的时候达到1的斜率,效果会比较好
|
// 这个数等于 sinh(2),用这个数的话,可以正好在刚开始移动的时候达到1的斜率,效果会比较好
|
||||||
let transitionTime = this.hero.speed * 3.626860407847019;
|
let transitionTime = this.hero!.speed * 3.626860407847019;
|
||||||
|
|
||||||
const setTargetX = (x: number, time: number) => {
|
const setTargetX = (x: number, time: number) => {
|
||||||
if (x === xTarget) return;
|
if (x === xTarget) return;
|
||||||
@ -175,7 +177,7 @@ export class FloorViewport implements ILayerGroupRenderExtends {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (this.movingFramer) {
|
if (this.movingFramer) {
|
||||||
this.hero.off('moveTick', this.movingFramer);
|
this.hero!.off('moveTick', this.movingFramer);
|
||||||
}
|
}
|
||||||
this.movingFramer = () => {
|
this.movingFramer = () => {
|
||||||
if (this.inTransition) return;
|
if (this.inTransition) return;
|
||||||
@ -186,22 +188,22 @@ export class FloorViewport implements ILayerGroupRenderExtends {
|
|||||||
ending = true;
|
ending = true;
|
||||||
}
|
}
|
||||||
if (!ending) {
|
if (!ending) {
|
||||||
const dir = this.hero.stepDir;
|
const dir = this.hero!.stepDir;
|
||||||
const { x, y } = core.utils.scan2[dir];
|
const { x, y } = core.utils.scan2[dir];
|
||||||
setTargetX(-x * this.maxOffset, now);
|
setTargetX(-x * this.maxOffset, now);
|
||||||
setTargetY(-y * this.maxOffset, now);
|
setTargetY(-y * this.maxOffset, now);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.hero.renderable) return;
|
if (!this.hero!.renderable) return;
|
||||||
|
|
||||||
const { x, y } = this.hero.renderable;
|
const { x, y } = this.hero!.renderable;
|
||||||
const { x: nx, y: ny } = this.getBoundedPosition(x, y);
|
const { x: nx, y: ny } = this.getBoundedPosition(x, y);
|
||||||
this.nx = nx;
|
this.nx = nx;
|
||||||
this.ny = ny;
|
this.ny = ny;
|
||||||
|
|
||||||
if (ending) {
|
if (ending) {
|
||||||
if (this.ox === xTarget && this.oy == yTarget) {
|
if (this.ox === xTarget && this.oy == yTarget) {
|
||||||
this.hero.off('moveTick', this.movingFramer);
|
this.hero!.off('moveTick', this.movingFramer);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -228,7 +230,7 @@ export class FloorViewport implements ILayerGroupRenderExtends {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
this.hero.on('moveTick', this.movingFramer);
|
this.hero!.on('moveTick', this.movingFramer);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -299,8 +301,9 @@ export class FloorViewport implements ILayerGroupRenderExtends {
|
|||||||
// this.createMoving();
|
// this.createMoving();
|
||||||
}
|
}
|
||||||
|
|
||||||
awake(group: LayerGroup): void {
|
private checkDependency() {
|
||||||
this.group = group;
|
if (this.hero && this.binder) return true;
|
||||||
|
const group = this.group;
|
||||||
const ex1 = group.getLayer('event')?.getExtends('floor-hero');
|
const ex1 = group.getLayer('event')?.getExtends('floor-hero');
|
||||||
const ex2 = group.getExtends('floor-binder');
|
const ex2 = group.getExtends('floor-binder');
|
||||||
if (
|
if (
|
||||||
@ -309,12 +312,15 @@ export class FloorViewport implements ILayerGroupRenderExtends {
|
|||||||
) {
|
) {
|
||||||
this.hero = ex1;
|
this.hero = ex1;
|
||||||
this.binder = ex2;
|
this.binder = ex2;
|
||||||
this.create();
|
return true;
|
||||||
adapter.add(this);
|
|
||||||
} else {
|
|
||||||
logger.error(15);
|
|
||||||
group.removeExtends('viewport');
|
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
awake(group: LayerGroup): void {
|
||||||
|
this.group = group;
|
||||||
|
this.create();
|
||||||
|
adapter.add(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
onDestroy(group: LayerGroup): void {
|
onDestroy(group: LayerGroup): void {
|
||||||
|
@ -67,11 +67,12 @@ export class MotaRenderer extends Container {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private searchElement(ele: Container, id: string): RenderItem | null {
|
private searchElement(ele: RenderItem, id: string): RenderItem | null {
|
||||||
for (const child of ele.children) {
|
for (const child of ele.children) {
|
||||||
if (child.id === id) return child;
|
if (child.id === id) return child;
|
||||||
if (child instanceof Container) {
|
else {
|
||||||
return this.searchElement(child, id);
|
const ele = this.searchElement(child, id);
|
||||||
|
if (ele) return ele;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
119
src/core/render/renderer/elements.tsx
Normal file
119
src/core/render/renderer/elements.tsx
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
import {
|
||||||
|
ComponentOptionsMixin,
|
||||||
|
defineComponent,
|
||||||
|
DefineComponent,
|
||||||
|
h,
|
||||||
|
ReservedProps,
|
||||||
|
VNodeProps
|
||||||
|
} from 'vue';
|
||||||
|
import EventEmitter from 'eventemitter3';
|
||||||
|
import {
|
||||||
|
AnimateProps,
|
||||||
|
BaseProps,
|
||||||
|
CommentProps,
|
||||||
|
ContainerProps,
|
||||||
|
CustomProps,
|
||||||
|
DamageProps,
|
||||||
|
GL2Props,
|
||||||
|
ImageProps,
|
||||||
|
LayerGroupProps,
|
||||||
|
LayerProps,
|
||||||
|
ShaderProps,
|
||||||
|
SpriteProps,
|
||||||
|
TextProps
|
||||||
|
} from './props';
|
||||||
|
import { ERenderItemEvent, RenderItem } from '../item';
|
||||||
|
import { ESpriteEvent, Sprite } from '../sprite';
|
||||||
|
import { EContainerEvent } from '../container';
|
||||||
|
import { EGL2Event } from '../gl2';
|
||||||
|
import { EImageEvent, ETextEvent } from '../preset/misc';
|
||||||
|
import { ELayerEvent, ELayerGroupEvent } from '../preset/layer';
|
||||||
|
import { EAnimateEvent } from '../preset/animate';
|
||||||
|
import { EDamageEvent } from '../preset/damage';
|
||||||
|
import { EShaderEvent } from '../shader';
|
||||||
|
|
||||||
|
export type WrapEventEmitterEvents<T extends EventEmitter.ValidEventTypes> =
|
||||||
|
T extends string | symbol
|
||||||
|
? T
|
||||||
|
: {
|
||||||
|
[P in keyof T]: T[P] extends any[]
|
||||||
|
? (...args: T[P]) => void
|
||||||
|
: (...args: any[]) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
type MappingEvent<E extends ERenderItemEvent> = {
|
||||||
|
[P in keyof WrapEventEmitterEvents<E> as P extends string
|
||||||
|
? `on${Capitalize<P>}`
|
||||||
|
: never]?: WrapEventEmitterEvents<E>[P];
|
||||||
|
};
|
||||||
|
|
||||||
|
type _Define<P extends BaseProps, E extends ERenderItemEvent> = DefineComponent<
|
||||||
|
P,
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
ComponentOptionsMixin,
|
||||||
|
ComponentOptionsMixin,
|
||||||
|
WrapEventEmitterEvents<E>,
|
||||||
|
Exclude<keyof WrapEventEmitterEvents<E>, number | symbol>,
|
||||||
|
VNodeProps,
|
||||||
|
Readonly<P & MappingEvent<E>>
|
||||||
|
>;
|
||||||
|
|
||||||
|
type TagDefine<T extends object, E extends ERenderItemEvent> = T &
|
||||||
|
MappingEvent<E> &
|
||||||
|
ReservedProps;
|
||||||
|
|
||||||
|
export type RenderItemComponent = _Define<BaseProps, ERenderItemEvent>;
|
||||||
|
export type SpriteComponent = _Define<SpriteProps, ESpriteEvent>;
|
||||||
|
export type ContainerComponent = _Define<ContainerProps, EContainerEvent>;
|
||||||
|
export type GL2Component = _Define<GL2Props, EGL2Event>;
|
||||||
|
export type ShaderComponent = _Define<ShaderProps, EShaderEvent>;
|
||||||
|
export type TextComponent = _Define<TextProps, ETextEvent>;
|
||||||
|
export type ImageComponent = _Define<ImageProps, EImageEvent>;
|
||||||
|
export type CommentComponent = _Define<CommentProps, ERenderItemEvent>;
|
||||||
|
export type LayerGroupComponent = _Define<LayerGroupProps, ELayerGroupEvent>;
|
||||||
|
export type LayerComponent = _Define<LayerProps, ELayerEvent>;
|
||||||
|
export type AnimateComponent = _Define<AnimateProps, EAnimateEvent>;
|
||||||
|
export type DamageComponent = _Define<DamageProps, EDamageEvent>;
|
||||||
|
|
||||||
|
declare module 'vue/jsx-runtime' {
|
||||||
|
namespace JSX {
|
||||||
|
export interface IntrinsicElements {
|
||||||
|
sprite: TagDefine<SpriteProps, ESpriteEvent>;
|
||||||
|
container: TagDefine<ContainerProps, EContainerEvent>;
|
||||||
|
shader: TagDefine<ShaderProps, EShaderEvent>;
|
||||||
|
text: TagDefine<TextProps, ETextEvent>;
|
||||||
|
image: TagDefine<ImageProps, EImageEvent>;
|
||||||
|
comment: TagDefine<CommentProps, ERenderItemEvent>;
|
||||||
|
custom: TagDefine<CustomProps, ERenderItemEvent>;
|
||||||
|
layer: TagDefine<LayerProps, ELayerEvent>;
|
||||||
|
'layer-group': TagDefine<LayerGroupProps, ELayerGroupEvent>;
|
||||||
|
damage: TagDefine<DamageProps, EDamageEvent>;
|
||||||
|
animate: TagDefine<AnimateProps, EAnimateEvent>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface InstancedElementProp {
|
||||||
|
item: RenderItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function wrapInstancedComponent<
|
||||||
|
P extends BaseProps = BaseProps,
|
||||||
|
E extends ERenderItemEvent = ERenderItemEvent,
|
||||||
|
C extends RenderItem = RenderItem
|
||||||
|
>(onCreate: (props: P) => C): _Define<P, E> {
|
||||||
|
const Com = defineComponent((props, ctx) => {
|
||||||
|
return () => {
|
||||||
|
const p = {
|
||||||
|
...props,
|
||||||
|
...ctx.attrs,
|
||||||
|
_item: onCreate
|
||||||
|
};
|
||||||
|
return h('custom', p, ctx.slots);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
return Com as _Define<P, E>;
|
||||||
|
}
|
99
src/core/render/renderer/index.ts
Normal file
99
src/core/render/renderer/index.ts
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
import {
|
||||||
|
ComponentInternalInstance,
|
||||||
|
createRenderer,
|
||||||
|
ElementNamespace,
|
||||||
|
VNodeProps
|
||||||
|
} from 'vue';
|
||||||
|
import { ERenderItemEvent, RenderItem } from '../item';
|
||||||
|
import { tagMap } from './map';
|
||||||
|
import { logger } from '@/core/common/logger';
|
||||||
|
import { Comment, Text } from '../preset/misc';
|
||||||
|
|
||||||
|
export const { createApp, render } = createRenderer<RenderItem, RenderItem>({
|
||||||
|
patchProp: function (
|
||||||
|
el: RenderItem,
|
||||||
|
key: string,
|
||||||
|
prevValue: any,
|
||||||
|
nextValue: any,
|
||||||
|
namespace?: ElementNamespace,
|
||||||
|
parentComponent?: ComponentInternalInstance | null
|
||||||
|
): void {
|
||||||
|
el.patchProp(key, prevValue, nextValue, namespace, parentComponent);
|
||||||
|
},
|
||||||
|
|
||||||
|
insert: function (
|
||||||
|
el: RenderItem<ERenderItemEvent>,
|
||||||
|
parent: RenderItem,
|
||||||
|
anchor?: RenderItem<ERenderItemEvent> | null
|
||||||
|
): void {
|
||||||
|
parent.appendChild(el);
|
||||||
|
},
|
||||||
|
|
||||||
|
remove: function (el: RenderItem<ERenderItemEvent>): void {
|
||||||
|
el.remove();
|
||||||
|
el.destroy();
|
||||||
|
},
|
||||||
|
|
||||||
|
createElement: function (
|
||||||
|
type: string,
|
||||||
|
namespace?: ElementNamespace,
|
||||||
|
isCustomizedBuiltIn?: string,
|
||||||
|
vnodeProps?: (VNodeProps & { [key: string]: any }) | null
|
||||||
|
): RenderItem {
|
||||||
|
const onCreate = tagMap.get(type);
|
||||||
|
if (!onCreate) {
|
||||||
|
logger.error(20, type);
|
||||||
|
throw new Error(`Cannot create element '${type}'`);
|
||||||
|
}
|
||||||
|
return onCreate(namespace, isCustomizedBuiltIn, vnodeProps);
|
||||||
|
},
|
||||||
|
|
||||||
|
createText: function (text: string): RenderItem<ERenderItemEvent> {
|
||||||
|
logger.warn(38);
|
||||||
|
return new Text(text);
|
||||||
|
},
|
||||||
|
|
||||||
|
createComment: function (text: string): RenderItem<ERenderItemEvent> {
|
||||||
|
return new Comment(text);
|
||||||
|
},
|
||||||
|
|
||||||
|
setText: function (node: RenderItem<ERenderItemEvent>, text: string): void {
|
||||||
|
if (node instanceof Text) {
|
||||||
|
node.setText(text);
|
||||||
|
} else {
|
||||||
|
logger.warn(39);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
setElementText: function (node: RenderItem, text: string): void {
|
||||||
|
if (node instanceof Text) {
|
||||||
|
node.setText(text);
|
||||||
|
} else {
|
||||||
|
logger.warn(39);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
parentNode: function (
|
||||||
|
node: RenderItem<ERenderItemEvent>
|
||||||
|
): RenderItem | null {
|
||||||
|
return node.parent ?? null;
|
||||||
|
},
|
||||||
|
|
||||||
|
nextSibling: function (
|
||||||
|
node: RenderItem<ERenderItemEvent>
|
||||||
|
): RenderItem<ERenderItemEvent> | null {
|
||||||
|
if (!node.parent) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
const parent = node.parent;
|
||||||
|
const list = [...parent.children];
|
||||||
|
const index = list.indexOf(node);
|
||||||
|
return list[index] ?? null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export * from './elements';
|
||||||
|
export * from './map';
|
||||||
|
export * from './props';
|
||||||
|
export * from './use';
|
161
src/core/render/renderer/map.ts
Normal file
161
src/core/render/renderer/map.ts
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
import { logger } from '@/core/common/logger';
|
||||||
|
import { ERenderItemEvent, RenderItem } from '../item';
|
||||||
|
import { ElementNamespace, VNodeProps } from 'vue';
|
||||||
|
import { Container } from '../container';
|
||||||
|
import { MotaRenderer } from '../render';
|
||||||
|
import { Sprite } from '../sprite';
|
||||||
|
import { Comment, Image, Text } from '../preset/misc';
|
||||||
|
import { Shader } from '../shader';
|
||||||
|
import { Animate, Damage, EDamageEvent, Layer, LayerGroup } from '../preset';
|
||||||
|
|
||||||
|
type OnItemCreate<
|
||||||
|
E extends ERenderItemEvent = ERenderItemEvent,
|
||||||
|
T extends RenderItem<E> = RenderItem<E>
|
||||||
|
> = (
|
||||||
|
namespace?: ElementNamespace,
|
||||||
|
isCustomizedBuiltIn?: string,
|
||||||
|
vnodeProps?: (VNodeProps & { [key: string]: any }) | null
|
||||||
|
) => T;
|
||||||
|
|
||||||
|
class RenderTagMap {
|
||||||
|
private map: Map<string, OnItemCreate> = new Map();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注册一个标签,每个标签对应一类元素,重复注册会覆盖之前的
|
||||||
|
* @param tag 标签名称
|
||||||
|
* @param ele 对应的元素类或其构造器
|
||||||
|
*/
|
||||||
|
register<E extends ERenderItemEvent, T extends RenderItem<E>>(
|
||||||
|
tag: string,
|
||||||
|
onCreate: OnItemCreate<E, T>
|
||||||
|
) {
|
||||||
|
if (this.map.has(tag)) {
|
||||||
|
logger.warn(34, tag);
|
||||||
|
}
|
||||||
|
this.map.set(tag, onCreate);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取一个标签对应的元素构造器
|
||||||
|
* @param tag 标签名
|
||||||
|
*/
|
||||||
|
get<E extends ERenderItemEvent, T extends RenderItem<E>>(
|
||||||
|
tag: string
|
||||||
|
): OnItemCreate<E, T> | undefined {
|
||||||
|
return this.map.get(tag) as OnItemCreate<E, T>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const tagMap = new RenderTagMap();
|
||||||
|
|
||||||
|
// Default elements
|
||||||
|
tagMap.register('container', (_0, _1, props) => {
|
||||||
|
if (!props) return new Container();
|
||||||
|
else {
|
||||||
|
const {
|
||||||
|
type = 'static',
|
||||||
|
enableCache = true,
|
||||||
|
fallthrough = false
|
||||||
|
} = props;
|
||||||
|
return new Container(type, enableCache, fallthrough);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
tagMap.register('mota-renderer', (_0, _1, props) => {
|
||||||
|
return new MotaRenderer(props?.id);
|
||||||
|
});
|
||||||
|
tagMap.register('sprite', (_0, _1, props) => {
|
||||||
|
if (!props) return new Sprite();
|
||||||
|
else {
|
||||||
|
const {
|
||||||
|
type = 'static',
|
||||||
|
enableCache = true,
|
||||||
|
fallthrough = false
|
||||||
|
} = props;
|
||||||
|
return new Sprite(type, enableCache, fallthrough);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
tagMap.register('text', (_0, _1, props) => {
|
||||||
|
if (!props) return new Text();
|
||||||
|
else {
|
||||||
|
const { type = 'static', text = '' } = props;
|
||||||
|
return new Text(text, type);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
tagMap.register('image', (_0, _1, props) => {
|
||||||
|
if (!props) return new Image(core.material.images.images['bg.jpg']);
|
||||||
|
else {
|
||||||
|
const {
|
||||||
|
image = core.material.images.images['bg.jpg'],
|
||||||
|
type = 'static'
|
||||||
|
} = props;
|
||||||
|
return new Image(image, type);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
tagMap.register('comment', (_0, _1, props) => {
|
||||||
|
if (!props) return new Comment();
|
||||||
|
else {
|
||||||
|
const { text = '' } = props;
|
||||||
|
return new Comment(text);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
tagMap.register('shader', (_0, _1, props) => {
|
||||||
|
if (!props) return new Shader();
|
||||||
|
else {
|
||||||
|
const { type = 'static' } = props;
|
||||||
|
return new Shader(type);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
tagMap.register('custom', (_0, _1, props) => {
|
||||||
|
if (!props) {
|
||||||
|
logger.error(22);
|
||||||
|
throw new Error('Cannot create custom element.');
|
||||||
|
} else {
|
||||||
|
const item = props._item;
|
||||||
|
if (!item) {
|
||||||
|
logger.error(22);
|
||||||
|
throw new Error('Cannot create custom element.');
|
||||||
|
}
|
||||||
|
return item(props);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
tagMap.register('layer', (_0, _1, props) => {
|
||||||
|
if (!props) return new Layer();
|
||||||
|
else {
|
||||||
|
const { ex } = props;
|
||||||
|
const l = new Layer();
|
||||||
|
|
||||||
|
if (ex) {
|
||||||
|
(ex as any[]).forEach(v => {
|
||||||
|
l.extends(v);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
tagMap.register('layer-group', (_0, _1, props) => {
|
||||||
|
if (!props) return new LayerGroup();
|
||||||
|
else {
|
||||||
|
const { ex, layers } = props;
|
||||||
|
const l = new LayerGroup();
|
||||||
|
|
||||||
|
if (ex) {
|
||||||
|
(ex as any[]).forEach(v => {
|
||||||
|
l.extends(v);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (layers) {
|
||||||
|
(layers as any[]).forEach(v => {
|
||||||
|
l.addLayer(v);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
tagMap.register<EDamageEvent, Damage>('damage', (_0, _1, props) => {
|
||||||
|
return new Damage();
|
||||||
|
});
|
||||||
|
tagMap.register('animate', (_0, _1, props) => {
|
||||||
|
return new Animate();
|
||||||
|
});
|
89
src/core/render/renderer/props.ts
Normal file
89
src/core/render/renderer/props.ts
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
import { RenderFunction, RenderItem, RenderItemPosition } from '../item';
|
||||||
|
import { Transform } from '../transform';
|
||||||
|
import {
|
||||||
|
FloorLayer,
|
||||||
|
ILayerGroupRenderExtends,
|
||||||
|
ILayerRenderExtends
|
||||||
|
} from '../preset/layer';
|
||||||
|
import type { EnemyCollection } from '@/game/enemy/damage';
|
||||||
|
|
||||||
|
export interface CustomProps {
|
||||||
|
_item: (props: BaseProps) => RenderItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BaseProps {
|
||||||
|
x?: number;
|
||||||
|
y?: number;
|
||||||
|
anchorX?: number;
|
||||||
|
anchorY?: number;
|
||||||
|
zIndex?: number;
|
||||||
|
width?: number;
|
||||||
|
height?: number;
|
||||||
|
filter?: string;
|
||||||
|
hd?: boolean;
|
||||||
|
antiAliasing?: boolean;
|
||||||
|
hidden?: boolean;
|
||||||
|
transform?: Transform;
|
||||||
|
type?: RenderItemPosition;
|
||||||
|
id?: string;
|
||||||
|
alpha?: number;
|
||||||
|
composite?: GlobalCompositeOperation;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SpriteProps extends BaseProps {
|
||||||
|
render?: RenderFunction;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ContainerProps extends BaseProps {}
|
||||||
|
|
||||||
|
export interface GL2Props extends BaseProps {}
|
||||||
|
|
||||||
|
export interface ShaderProps extends BaseProps {}
|
||||||
|
|
||||||
|
export interface TextProps extends BaseProps {
|
||||||
|
text?: string;
|
||||||
|
fillStyle?: CanvasStyle;
|
||||||
|
strokeStyle?: CanvasStyle;
|
||||||
|
font?: string;
|
||||||
|
strokeWidth?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ImageProps extends BaseProps {
|
||||||
|
image: CanvasImageSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CommentProps extends BaseProps {
|
||||||
|
text?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LayerGroupProps extends BaseProps {
|
||||||
|
cellSize?: number;
|
||||||
|
blockSize?: number;
|
||||||
|
floorId?: FloorIds;
|
||||||
|
bindThisFloor?: boolean;
|
||||||
|
camera?: Transform;
|
||||||
|
ex?: readonly ILayerGroupRenderExtends[];
|
||||||
|
layers?: readonly FloorLayer[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LayerProps extends BaseProps {
|
||||||
|
layer?: FloorLayer;
|
||||||
|
mapWidth?: number;
|
||||||
|
mapHeight?: number;
|
||||||
|
cellSize?: number;
|
||||||
|
background?: AllNumbers;
|
||||||
|
floorImage?: FloorAnimate[];
|
||||||
|
ex?: readonly ILayerRenderExtends[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AnimateProps extends BaseProps {}
|
||||||
|
|
||||||
|
export interface DamageProps extends BaseProps {
|
||||||
|
mapWidth?: number;
|
||||||
|
mapHeight?: number;
|
||||||
|
cellSize?: number;
|
||||||
|
enemy?: EnemyCollection;
|
||||||
|
font?: string;
|
||||||
|
strokeStyle?: string;
|
||||||
|
strokeWidth?: number;
|
||||||
|
}
|
57
src/core/render/renderer/use.ts
Normal file
57
src/core/render/renderer/use.ts
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import { gameKey, Hotkey } from '@/core/main/custom/hotkey';
|
||||||
|
import { Animation, Ticker, Transition } from 'mutate-animate';
|
||||||
|
import { onMounted, onUnmounted } from 'vue';
|
||||||
|
|
||||||
|
const ticker = new Ticker();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在组件中每帧执行一次函数
|
||||||
|
* @param fn 每帧执行的函数
|
||||||
|
*/
|
||||||
|
export function onTick(fn: (time: number) => void) {
|
||||||
|
onMounted(() => {
|
||||||
|
ticker.add(fn);
|
||||||
|
});
|
||||||
|
onUnmounted(() => {
|
||||||
|
ticker.remove(fn);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
type AnimationUsing = [Animation];
|
||||||
|
type TransitionUsing = [Transition];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在组件中创建一个动画实例
|
||||||
|
*/
|
||||||
|
export function useAnimation(): AnimationUsing {
|
||||||
|
const ani = new Animation();
|
||||||
|
onUnmounted(() => {
|
||||||
|
ani.ticker.destroy();
|
||||||
|
});
|
||||||
|
return [ani];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在组件中创建一个渐变实例
|
||||||
|
*/
|
||||||
|
export function useTransition(): TransitionUsing {
|
||||||
|
const tran = new Transition();
|
||||||
|
onUnmounted(() => {
|
||||||
|
tran.ticker.destroy();
|
||||||
|
});
|
||||||
|
return [tran];
|
||||||
|
}
|
||||||
|
|
||||||
|
type KeyUsing = [Hotkey, symbol];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在组件中定义按键操作
|
||||||
|
*/
|
||||||
|
export function useKey(): KeyUsing {
|
||||||
|
const sym = Symbol();
|
||||||
|
gameKey.use(sym);
|
||||||
|
onUnmounted(() => {
|
||||||
|
gameKey.dispose();
|
||||||
|
});
|
||||||
|
return [gameKey, sym];
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
import { MotaOffscreenCanvas2D } from '../fx/canvas2d';
|
import { MotaOffscreenCanvas2D } from '../fx/canvas2d';
|
||||||
import { ERenderItemEvent, RenderItem, RenderItemPosition } from './item';
|
import { ERenderItemEvent, RenderItem, RenderItemPosition } from './item';
|
||||||
import { Transform } from './transform';
|
import { Transform } from './transform';
|
||||||
import { GL2, GL2Program, IGL2ProgramPrefix } from './gl2';
|
import { EGL2Event, GL2, GL2Program, IGL2ProgramPrefix } from './gl2';
|
||||||
|
|
||||||
const SHADER_PREFIX: IGL2ProgramPrefix = {
|
const SHADER_PREFIX: IGL2ProgramPrefix = {
|
||||||
VERTEX: /* glsl */ `#version 300 es
|
VERTEX: /* glsl */ `#version 300 es
|
||||||
@ -33,7 +33,11 @@ void main() {
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export class Shader extends GL2 {
|
export interface EShaderEvent extends EGL2Event {}
|
||||||
|
|
||||||
|
export class Shader<E extends EShaderEvent = EShaderEvent> extends GL2<
|
||||||
|
EShaderEvent | E
|
||||||
|
> {
|
||||||
setHD(hd: boolean): void {
|
setHD(hd: boolean): void {
|
||||||
super.setHD(hd);
|
super.setHD(hd);
|
||||||
this.sizeGL(this.width, this.height);
|
this.sizeGL(this.width, this.height);
|
||||||
|
@ -6,6 +6,8 @@ import {
|
|||||||
} from './item';
|
} from './item';
|
||||||
import { MotaOffscreenCanvas2D } from '../fx/canvas2d';
|
import { MotaOffscreenCanvas2D } from '../fx/canvas2d';
|
||||||
import { Transform } from './transform';
|
import { Transform } from './transform';
|
||||||
|
import { ElementNamespace, ComponentInternalInstance } from 'vue';
|
||||||
|
import { logger } from '../common/logger';
|
||||||
|
|
||||||
export interface ESpriteEvent extends ERenderItemEvent {}
|
export interface ESpriteEvent extends ERenderItemEvent {}
|
||||||
|
|
||||||
@ -40,4 +42,24 @@ export class Sprite<
|
|||||||
this.renderFn = fn;
|
this.renderFn = fn;
|
||||||
this.update(this);
|
this.update(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
patchProp(
|
||||||
|
key: string,
|
||||||
|
prevValue: any,
|
||||||
|
nextValue: any,
|
||||||
|
namespace?: ElementNamespace,
|
||||||
|
parentComponent?: ComponentInternalInstance | null
|
||||||
|
): void {
|
||||||
|
super.patchProp(key, prevValue, nextValue, namespace, parentComponent);
|
||||||
|
const type = typeof nextValue;
|
||||||
|
switch (key) {
|
||||||
|
case 'render':
|
||||||
|
if (type !== 'function') {
|
||||||
|
logger.error(21, key, 'function', type);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.setRenderFn(nextValue);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { TimingFn } from 'mutate-animate';
|
import { Ticker, TimingFn } from 'mutate-animate';
|
||||||
import { RenderAdapter } from './adapter';
|
import { RenderAdapter } from './adapter';
|
||||||
import { FloorViewport } from './preset/viewport';
|
import { FloorViewport } from './preset/viewport';
|
||||||
|
|
||||||
|
@ -13,12 +13,15 @@
|
|||||||
"11": "Cache depth cannot larger than 31.",
|
"11": "Cache depth cannot larger than 31.",
|
||||||
"12": "Cannot move while status is not 'moving'. Call 'readyMove' first.",
|
"12": "Cannot move while status is not 'moving'. Call 'readyMove' first.",
|
||||||
"13": "Cannot compile $1 shader. Error info: $2",
|
"13": "Cannot compile $1 shader. Error info: $2",
|
||||||
"14": "Animate extension needs 'floor-hero' extension as dependency.",
|
"14": "",
|
||||||
"15": "Viewport extension needs 'floor-hero' extension as dependency.",
|
"15": "",
|
||||||
"16": "Cannot find log message for $1 code $2.",
|
"16": "Cannot find log message for $1 code $2.",
|
||||||
"17": "Cannot use shader program for shader element that does not belong to it.",
|
"17": "Cannot use shader program for shader element that does not belong to it.",
|
||||||
"18": "Cannot delete shader program for shader element that does not belong to it.",
|
"18": "Cannot delete shader program for shader element that does not belong to it.",
|
||||||
"19": "Cannot create MotaRenderer instance for nonexistent canvas.",
|
"19": "Cannot create MotaRenderer instance for nonexistent canvas.",
|
||||||
|
"20": "Cannot create render element for tag '$1', since there's no registration for it.",
|
||||||
|
"21": "Incorrect render prop type is delivered. key: '$1', expected type: '$2', delivered type: '$3'",
|
||||||
|
"22": "Incorrect props for custom tag. Please ensure you have delivered 'item' prop and other required props.",
|
||||||
"1101": "Shadow extension needs 'floor-hero' extension as dependency.",
|
"1101": "Shadow extension needs 'floor-hero' extension as dependency.",
|
||||||
"1201": "Floor-damage extension needs 'floor-binder' extension as dependency.",
|
"1201": "Floor-damage extension needs 'floor-binder' extension as dependency.",
|
||||||
"1301": "Portal extension need 'floor-binder' extension as dependency.",
|
"1301": "Portal extension need 'floor-binder' extension as dependency.",
|
||||||
@ -58,6 +61,12 @@
|
|||||||
"31": "Cannot use indices since the indices instance is not belong to the program.",
|
"31": "Cannot use indices since the indices instance is not belong to the program.",
|
||||||
"32": "Sub-image exceeds texture dimensions, auto adjusting size.",
|
"32": "Sub-image exceeds texture dimensions, auto adjusting size.",
|
||||||
"33": "Cannot modify MotaOffscreenCanvas2D that is freezed.",
|
"33": "Cannot modify MotaOffscreenCanvas2D that is freezed.",
|
||||||
|
"34": "Repeated render tag registration: '$1'.",
|
||||||
|
"35": "Cannot append child on plain render item, please ensure you have overrided 'appendChild' method in your own element.",
|
||||||
|
"36": "Cannot remove child on plain render item, please ensure you have overrided 'removeChild' method in your own element.",
|
||||||
|
"37": "Cannot execute 'requestSort' on plain render item, please ensure you have overrided 'requestSort' method in your own element.",
|
||||||
|
"38": "Using plain text in jsx is strongly not recommended, since you can hardly modify its attributes. Consider using Text element instead.",
|
||||||
|
"39": "Plain text is not supported outside Text element.",
|
||||||
"1001": "Item-detail extension needs 'floor-binder' and 'floor-damage' extension as dependency.",
|
"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."
|
"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."
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,15 @@
|
|||||||
import { MotaOffscreenCanvas2D } from '@/core/fx/canvas2d';
|
import { MotaOffscreenCanvas2D } from '@/core/fx/canvas2d';
|
||||||
|
import { wrapInstancedComponent } from '@/core/render';
|
||||||
import { RenderItem, RenderItemPosition } from '@/core/render/item';
|
import { RenderItem, RenderItemPosition } from '@/core/render/item';
|
||||||
import { Transform } from '@/core/render/transform';
|
import { Transform } from '@/core/render/transform';
|
||||||
|
|
||||||
// 渲染端的向后兼容用,会充当两个版本间过渡的作用
|
// 渲染端的向后兼容用,会充当两个版本间过渡的作用
|
||||||
export class FloorChange extends RenderItem {
|
class Change extends RenderItem {
|
||||||
private tips: string[] = [];
|
private tips: string[] = [];
|
||||||
/** 当前小贴士 */
|
/** 当前小贴士 */
|
||||||
private usingTip: string = '';
|
private usingTip: string = '';
|
||||||
/** 透明度 */
|
/** 透明度 */
|
||||||
private alpha: number = 0;
|
private backAlpha: number = 0;
|
||||||
private title: string = '';
|
private title: string = '';
|
||||||
|
|
||||||
constructor(type: RenderItemPosition) {
|
constructor(type: RenderItemPosition) {
|
||||||
@ -45,10 +46,10 @@ export class FloorChange extends RenderItem {
|
|||||||
const dt = Date.now() - start;
|
const dt = Date.now() - start;
|
||||||
const progress = dt / time;
|
const progress = dt / time;
|
||||||
if (progress > 1) {
|
if (progress > 1) {
|
||||||
this.alpha = 1;
|
this.backAlpha = 1;
|
||||||
this.removeTicker(id);
|
this.removeTicker(id);
|
||||||
} else {
|
} else {
|
||||||
this.alpha = progress;
|
this.backAlpha = progress;
|
||||||
}
|
}
|
||||||
this.update();
|
this.update();
|
||||||
},
|
},
|
||||||
@ -71,9 +72,9 @@ export class FloorChange extends RenderItem {
|
|||||||
const progress = dt / time;
|
const progress = dt / time;
|
||||||
if (progress > 1) {
|
if (progress > 1) {
|
||||||
this.removeTicker(id);
|
this.removeTicker(id);
|
||||||
this.alpha = 0;
|
this.backAlpha = 0;
|
||||||
} else {
|
} else {
|
||||||
this.alpha = 1 - progress;
|
this.backAlpha = 1 - progress;
|
||||||
}
|
}
|
||||||
this.update();
|
this.update();
|
||||||
},
|
},
|
||||||
@ -87,9 +88,9 @@ export class FloorChange extends RenderItem {
|
|||||||
canvas: MotaOffscreenCanvas2D,
|
canvas: MotaOffscreenCanvas2D,
|
||||||
transform: Transform
|
transform: Transform
|
||||||
): void {
|
): void {
|
||||||
if (this.alpha === 0) return;
|
if (this.backAlpha === 0) return;
|
||||||
const ctx = canvas.ctx;
|
const ctx = canvas.ctx;
|
||||||
ctx.globalAlpha = this.alpha;
|
ctx.globalAlpha = this.backAlpha;
|
||||||
ctx.fillStyle = '#000';
|
ctx.fillStyle = '#000';
|
||||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||||
ctx.textAlign = 'center';
|
ctx.textAlign = 'center';
|
||||||
@ -106,3 +107,5 @@ export class FloorChange extends RenderItem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const FloorChange = wrapInstancedComponent(() => new Change('static'));
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { MotaOffscreenCanvas2D } from '@/core/fx/canvas2d';
|
import { MotaOffscreenCanvas2D } from '@/core/fx/canvas2d';
|
||||||
|
import { wrapInstancedComponent } from '@/core/render';
|
||||||
import { RenderItem, RenderItemPosition } from '@/core/render/item';
|
import { RenderItem, RenderItemPosition } from '@/core/render/item';
|
||||||
import { Transform } from '@/core/render/transform';
|
import { Transform } from '@/core/render/transform';
|
||||||
import { TimingFn } from 'mutate-animate';
|
import { TimingFn } from 'mutate-animate';
|
||||||
@ -18,7 +19,7 @@ function parabola(input: number): [number, number] {
|
|||||||
return [x, x ** 2 / 20 - 3 * x];
|
return [x, x ** 2 / 20 - 3 * x];
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PopText extends RenderItem {
|
class Pop extends RenderItem {
|
||||||
private popList: Set<PopData> = new Set();
|
private popList: Set<PopData> = new Set();
|
||||||
|
|
||||||
private delegation: number = 0;
|
private delegation: number = 0;
|
||||||
@ -94,3 +95,5 @@ export class PopText extends RenderItem {
|
|||||||
this.removeTicker(this.delegation);
|
this.removeTicker(this.delegation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const PopText = wrapInstancedComponent(() => new Pop('static'));
|
||||||
|
27
src/plugin/fx/shadow.ts
Normal file
27
src/plugin/fx/shadow.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { MotaOffscreenCanvas2D } from '@/core/fx/canvas2d';
|
||||||
|
import { GL2, GL2Program } from '@/core/render/gl2';
|
||||||
|
import { Transform } from '@/core/render/transform';
|
||||||
|
|
||||||
|
const MAX_COUNT = 5;
|
||||||
|
|
||||||
|
export class ShadowEffect extends GL2 {
|
||||||
|
protected preDraw(
|
||||||
|
canvas: MotaOffscreenCanvas2D,
|
||||||
|
transform: Transform,
|
||||||
|
gl: WebGL2RenderingContext,
|
||||||
|
program: GL2Program
|
||||||
|
): boolean {
|
||||||
|
throw new Error('Method not implemented.');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected postDraw(
|
||||||
|
canvas: MotaOffscreenCanvas2D,
|
||||||
|
transform: Transform,
|
||||||
|
gl: WebGL2RenderingContext,
|
||||||
|
program: GL2Program
|
||||||
|
): void {
|
||||||
|
throw new Error('Method not implemented.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ShadowProgam extends GL2Program {}
|
@ -9,11 +9,22 @@ import postcssPresetEnv from 'postcss-preset-env';
|
|||||||
|
|
||||||
const FSHOST = 'http://127.0.0.1:3000/';
|
const FSHOST = 'http://127.0.0.1:3000/';
|
||||||
|
|
||||||
|
const custom = [
|
||||||
|
'container', 'image', 'sprite', 'shader', 'text', 'comment', 'custom',
|
||||||
|
'layer', 'layer-group', 'animate', 'damage'
|
||||||
|
]
|
||||||
|
|
||||||
// https://vitejs.dev/config/
|
// https://vitejs.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [
|
plugins: [
|
||||||
vue(),
|
vue({
|
||||||
vuejsx(),
|
customElement: custom
|
||||||
|
}),
|
||||||
|
vuejsx({
|
||||||
|
isCustomElement: (tag) => {
|
||||||
|
return custom.includes(tag)
|
||||||
|
}
|
||||||
|
}),
|
||||||
legacy({
|
legacy({
|
||||||
targets: ['defaults', 'not IE 11'],
|
targets: ['defaults', 'not IE 11'],
|
||||||
polyfills: true,
|
polyfills: true,
|
||||||
|
Loading…
Reference in New Issue
Block a user