mirror of
https://github.com/unanmed/HumanBreak.git
synced 2025-01-19 04:19:30 +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 = {
|
||||
floorId: core.status.floorId,
|
||||
hero: core.clone(core.status.hero, name => name !== 'chase'),
|
||||
hero: core.status.hero,
|
||||
hard: core.status.hard,
|
||||
maps: core.clone(core.maps.saveMap()),
|
||||
maps: core.maps.saveMap(),
|
||||
route: core.encodeRoute(core.status.route, !fromAutosave),
|
||||
values: values,
|
||||
version: core.firstData.version,
|
||||
@ -291,7 +291,7 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = {
|
||||
skill: HeroSkill.saveSkill()
|
||||
};
|
||||
|
||||
return data;
|
||||
return structuredClone(data);
|
||||
},
|
||||
loadData: function (data, callback) {
|
||||
// 读档操作;从存储中读取了内容后的行为
|
||||
|
@ -134,6 +134,7 @@ export class MotaOffscreenCanvas2D extends EventEmitter<OffscreenCanvasEvent> {
|
||||
this.canvas.remove();
|
||||
this.ctx.reset();
|
||||
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
||||
this._freezed = true;
|
||||
MotaOffscreenCanvas2D.list.delete(this);
|
||||
}
|
||||
|
||||
|
@ -138,6 +138,10 @@ export class HeroKeyMover {
|
||||
private onStepEnd = () => {
|
||||
const con = this.controller;
|
||||
if (!con) return;
|
||||
if (core.status.lockControl) {
|
||||
con.stop();
|
||||
return;
|
||||
}
|
||||
if (!this.moving) {
|
||||
con.stop();
|
||||
return;
|
||||
|
@ -71,6 +71,7 @@ export class Container<E extends EContainerEvent = EContainerEvent>
|
||||
removeChild(...child: RenderItem<any>[]): void {
|
||||
let changed = false;
|
||||
child.forEach(v => {
|
||||
if (v.parent !== this) return;
|
||||
const success = v.remove();
|
||||
if (success) {
|
||||
changed = true;
|
||||
|
@ -1,7 +1,7 @@
|
||||
import EventEmitter from 'eventemitter3';
|
||||
import { logger } from '../common/logger';
|
||||
import { MotaOffscreenCanvas2D } from '../fx/canvas2d';
|
||||
import { RenderItem, RenderItemPosition } from './item';
|
||||
import { ERenderItemEvent, RenderItem, RenderItemPosition } from './item';
|
||||
import { Transform } from './transform';
|
||||
import { isWebGL2Supported } from '../fx/webgl';
|
||||
|
||||
@ -91,7 +91,11 @@ export type ProgramConstructor<T extends GL2Program> = new (
|
||||
fs?: string
|
||||
) => 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();
|
||||
|
||||
|
@ -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 { Transform } from './transform';
|
||||
import { logger } from '../common/logger';
|
||||
import { ElementNamespace, ComponentInternalInstance } from 'vue';
|
||||
|
||||
export type RenderFunction = (
|
||||
canvas: MotaOffscreenCanvas2D,
|
||||
@ -117,6 +118,24 @@ interface IRenderTickerSupport {
|
||||
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 {
|
||||
beforeUpdate: [item?: RenderItem];
|
||||
afterUpdate: [item?: RenderItem];
|
||||
@ -144,7 +163,9 @@ export abstract class RenderItem<E extends ERenderItemEvent = ERenderItemEvent>
|
||||
IRenderAnchor,
|
||||
IRenderConfig,
|
||||
IRenderFrame,
|
||||
IRenderTickerSupport
|
||||
IRenderTickerSupport,
|
||||
IRenderChildable,
|
||||
IRenderVueSupport
|
||||
{
|
||||
/** 渲染的全局ticker */
|
||||
static ticker: Ticker = new Ticker();
|
||||
@ -194,16 +215,34 @@ export abstract class RenderItem<E extends ERenderItemEvent = ERenderItemEvent>
|
||||
hidden: boolean = false;
|
||||
/** 滤镜 */
|
||||
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;
|
||||
|
||||
protected needUpdate: boolean = false;
|
||||
|
||||
/** 该渲染元素的模型变换矩阵 */
|
||||
transform: Transform = new Transform();
|
||||
private _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();
|
||||
@ -278,10 +317,13 @@ export abstract class RenderItem<E extends ERenderItemEvent = ERenderItemEvent>
|
||||
const ax = -this.anchorX * this.width;
|
||||
const ay = -this.anchorY * this.height;
|
||||
|
||||
canvas.ctx.save();
|
||||
const ctx = canvas.ctx;
|
||||
ctx.save();
|
||||
canvas.setAntiAliasing(this.antiAliasing);
|
||||
if (this.enableCache) canvas.ctx.filter = this.filter;
|
||||
if (this.type === 'static') transformCanvas(canvas, tran);
|
||||
ctx.globalAlpha = this.alpha;
|
||||
ctx.globalCompositeOperation = this.composite;
|
||||
if (this.enableCache) {
|
||||
const { width, height, ctx } = this.cache;
|
||||
if (this.cacheDirty) {
|
||||
@ -319,6 +361,24 @@ export abstract class RenderItem<E extends ERenderItemEvent = ERenderItemEvent>
|
||||
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 父元素
|
||||
*/
|
||||
append(parent: IRenderChildable & RenderItem) {
|
||||
append(parent: RenderItem) {
|
||||
this.remove();
|
||||
parent.children.add(this);
|
||||
this.parent = parent;
|
||||
this._parent = parent;
|
||||
parent.requestSort();
|
||||
this.needUpdate = false;
|
||||
this.update();
|
||||
@ -447,7 +507,7 @@ export abstract class RenderItem<E extends ERenderItemEvent = ERenderItemEvent>
|
||||
if (!this.parent) return false;
|
||||
const parent = this.parent;
|
||||
const success = parent.children.delete(this);
|
||||
this.parent = void 0;
|
||||
this._parent = void 0;
|
||||
parent.requestSort();
|
||||
parent.update();
|
||||
if (!success) return false;
|
||||
@ -455,6 +515,179 @@ export abstract class RenderItem<E extends ERenderItemEvent = ERenderItemEvent>
|
||||
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 { ILayerGroupRenderExtends, LayerGroup } from './layer';
|
||||
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 {
|
||||
static animateList: Set<LayerGroupAnimate> = new Set();
|
||||
id: string = 'animate';
|
||||
|
||||
group!: LayerGroup;
|
||||
hero!: HeroRenderer;
|
||||
hero?: HeroRenderer;
|
||||
animate!: Animate;
|
||||
|
||||
private animation: Set<AnimateData> = new Set();
|
||||
@ -28,7 +29,8 @@ export class LayerGroupAnimate implements ILayerGroupRenderExtends {
|
||||
}
|
||||
|
||||
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 cell = this.group.cellSize;
|
||||
const half = cell / 2;
|
||||
@ -48,30 +50,37 @@ export class LayerGroupAnimate implements ILayerGroupRenderExtends {
|
||||
};
|
||||
|
||||
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 {
|
||||
this.group = group;
|
||||
const ex = group.getLayer('event')?.getExtends('floor-hero');
|
||||
if (ex instanceof HeroRenderer) {
|
||||
this.hero = ex;
|
||||
this.animate = new Animate();
|
||||
this.animate.size(group.width, group.height);
|
||||
this.animate.setHD(true);
|
||||
this.animate.setZIndex(100);
|
||||
group.appendChild(this.animate);
|
||||
LayerGroupAnimate.animateList.add(this);
|
||||
this.listen();
|
||||
} else {
|
||||
logger.error(14);
|
||||
group.removeExtends('animate');
|
||||
}
|
||||
this.animate = new Animate();
|
||||
this.animate.size(group.width, group.height);
|
||||
this.animate.setHD(true);
|
||||
this.animate.setZIndex(100);
|
||||
group.appendChild(this.animate);
|
||||
LayerGroupAnimate.animateList.add(this);
|
||||
this.listen();
|
||||
}
|
||||
|
||||
onDestroy(group: LayerGroup): void {
|
||||
this.hero.off('moveTick', this.onMoveTick);
|
||||
LayerGroupAnimate.animateList.delete(this);
|
||||
if (this.checkHero()) {
|
||||
this.hero!.off('moveTick', this.onMoveTick);
|
||||
LayerGroupAnimate.animateList.delete(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -89,7 +98,9 @@ interface AnimateData {
|
||||
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();
|
||||
/** 静态位置的动画 */
|
||||
@ -102,18 +113,6 @@ export class Animate extends Sprite {
|
||||
constructor() {
|
||||
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 => {
|
||||
if (time - this.lastTime < 50) return;
|
||||
this.lastTime = time;
|
||||
@ -129,6 +128,21 @@ export class Animate extends Sprite {
|
||||
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(
|
||||
data: Set<AnimateData>,
|
||||
canvas: MotaOffscreenCanvas2D
|
||||
|
@ -21,7 +21,7 @@ import type {
|
||||
import { MotaOffscreenCanvas2D } from '@/core/fx/canvas2d';
|
||||
import { isNil } from 'lodash-es';
|
||||
import { getDamageColor } from '@/plugin/utils';
|
||||
import { RenderItem, transformCanvas } from '../item';
|
||||
import { ERenderItemEvent, RenderItem, transformCanvas } from '../item';
|
||||
import EventEmitter from 'eventemitter3';
|
||||
import { Transform } from '../transform';
|
||||
|
||||
@ -123,7 +123,7 @@ export interface DamageRenderable {
|
||||
strokeWidth?: number;
|
||||
}
|
||||
|
||||
interface EDamageEvent extends ESpriteEvent {
|
||||
export interface EDamageEvent extends ERenderItemEvent {
|
||||
setMapSize: [width: number, height: number];
|
||||
beforeDamageRender: [need: Set<number>, transform: Transform];
|
||||
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 { Container } from '../container';
|
||||
import { Container, EContainerEvent } from '../container';
|
||||
import { Sprite } from '../sprite';
|
||||
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 { RenderableData, texture } from '../cache';
|
||||
import {
|
||||
@ -14,6 +19,7 @@ import {
|
||||
import { Transform } from '../transform';
|
||||
import { LayerFloorBinder, LayerGroupFloorBinder } from './floor';
|
||||
import { RenderAdapter } from '../adapter';
|
||||
import { ElementNamespace, ComponentInternalInstance } from 'vue';
|
||||
|
||||
export interface ILayerGroupRenderExtends {
|
||||
/** 拓展的唯一标识符 */
|
||||
@ -95,7 +101,12 @@ const layerZIndex: Record<FloorLayer, number> = {
|
||||
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();
|
||||
|
||||
@ -115,7 +126,7 @@ export class LayerGroup extends Container implements IAnimateFrame {
|
||||
camera: Transform = new Transform();
|
||||
|
||||
private needRender?: Set<number>;
|
||||
private extend: Map<string, ILayerGroupRenderExtends> = new Map();
|
||||
readonly extend: Map<string, ILayerGroupRenderExtends> = new Map();
|
||||
|
||||
constructor() {
|
||||
super('static', true);
|
||||
@ -212,18 +223,28 @@ export class LayerGroup extends Container implements IAnimateFrame {
|
||||
* 添加显示层
|
||||
* @param layer 显示层
|
||||
*/
|
||||
addLayer(layer: FloorLayer) {
|
||||
const l = new Layer();
|
||||
l.layer = layer;
|
||||
this.layers.set(layer, l);
|
||||
l.setZIndex(layerZIndex[layer]);
|
||||
this.appendChild(l);
|
||||
addLayer(layer: FloorLayer | Layer) {
|
||||
if (typeof layer === 'string') {
|
||||
const l = new Layer();
|
||||
l.layer = layer;
|
||||
this.layers.set(layer, l);
|
||||
l.setZIndex(layerZIndex[layer]);
|
||||
this.appendChild(l);
|
||||
|
||||
for (const ex of this.extend.values()) {
|
||||
ex.onLayerAdd?.(this, l);
|
||||
for (const ex of this.extend.values()) {
|
||||
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;
|
||||
}
|
||||
|
||||
export class Layer extends Container {
|
||||
export interface ELayerEvent extends EContainerEvent {}
|
||||
|
||||
export class Layer extends Container<ELayerEvent> {
|
||||
// 一些会用到的常量
|
||||
static readonly FRAME_0 = 1;
|
||||
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 {
|
||||
for (const ex of this.extend.values()) {
|
||||
ex.onDestroy?.(this);
|
||||
|
@ -1,9 +1,14 @@
|
||||
import { MotaOffscreenCanvas2D } from '@/core/fx/canvas2d';
|
||||
import { Sprite } from '../sprite';
|
||||
import { ERenderItemEvent, RenderItem, RenderItemPosition } from '../item';
|
||||
import { Transform } from '../transform';
|
||||
import { ElementNamespace, ComponentInternalInstance } from 'vue';
|
||||
|
||||
type CanvasStyle = string | CanvasGradient | CanvasPattern;
|
||||
|
||||
export class Text extends Sprite {
|
||||
export interface ETextEvent extends ERenderItemEvent {}
|
||||
|
||||
export class Text extends RenderItem<ETextEvent> {
|
||||
text: string;
|
||||
|
||||
fillStyle?: CanvasStyle = '#fff';
|
||||
@ -16,26 +21,30 @@ export class Text extends Sprite {
|
||||
|
||||
private static measureCanvas = new MotaOffscreenCanvas2D();
|
||||
|
||||
constructor(text: string = '') {
|
||||
super('static', false);
|
||||
constructor(text: string = '', type: RenderItemPosition = 'static') {
|
||||
super(type, false);
|
||||
|
||||
this.text = text;
|
||||
if (text.length > 0) this.calBox();
|
||||
}
|
||||
|
||||
this.renderFn = ({ canvas, ctx }) => {
|
||||
ctx.textBaseline = 'bottom';
|
||||
ctx.fillStyle = this.fillStyle ?? 'transparent';
|
||||
ctx.strokeStyle = this.strokeStyle ?? 'transparent';
|
||||
ctx.font = this.font ?? '';
|
||||
ctx.lineWidth = this.strokeWidth;
|
||||
protected render(
|
||||
canvas: MotaOffscreenCanvas2D,
|
||||
transform: Transform
|
||||
): void {
|
||||
const ctx = canvas.ctx;
|
||||
ctx.textBaseline = 'bottom';
|
||||
ctx.fillStyle = this.fillStyle ?? 'transparent';
|
||||
ctx.strokeStyle = this.strokeStyle ?? 'transparent';
|
||||
ctx.font = this.font ?? '';
|
||||
ctx.lineWidth = this.strokeWidth;
|
||||
|
||||
if (this.strokeStyle) {
|
||||
ctx.strokeText(this.text, 0, this.descent);
|
||||
}
|
||||
if (this.fillStyle) {
|
||||
ctx.fillText(this.text, 0, this.descent);
|
||||
}
|
||||
};
|
||||
if (this.strokeStyle) {
|
||||
ctx.strokeText(this.text, 0, this.descent);
|
||||
}
|
||||
if (this.fillStyle) {
|
||||
ctx.fillText(this.text, 0, this.descent);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -96,6 +105,22 @@ export class Text extends Sprite {
|
||||
this.descent = 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<
|
||||
@ -103,26 +128,47 @@ export type SizedCanvasImageSource = Exclude<
|
||||
VideoFrame | SVGElement
|
||||
>;
|
||||
|
||||
export class Image extends Sprite {
|
||||
image: SizedCanvasImageSource;
|
||||
export interface EImageEvent extends ERenderItemEvent {}
|
||||
|
||||
constructor(image: SizedCanvasImageSource) {
|
||||
super();
|
||||
export class Image extends RenderItem<EImageEvent> {
|
||||
image: CanvasImageSource;
|
||||
|
||||
constructor(image: CanvasImageSource, type: RenderItemPosition = 'static') {
|
||||
super(type);
|
||||
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 }) => {
|
||||
ctx.drawImage(this.image, 0, 0, canvas.width, canvas.height);
|
||||
};
|
||||
protected render(
|
||||
canvas: MotaOffscreenCanvas2D,
|
||||
transform: Transform
|
||||
): void {
|
||||
const ctx = canvas.ctx;
|
||||
ctx.drawImage(this.image, 0, 0, canvas.width, canvas.height);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置图片资源
|
||||
* @param image 图片资源
|
||||
*/
|
||||
setImage(image: SizedCanvasImageSource) {
|
||||
setImage(image: CanvasImageSource) {
|
||||
this.image = image;
|
||||
this.size(image.width, image.height);
|
||||
this.update(this);
|
||||
this.update();
|
||||
}
|
||||
}
|
||||
|
||||
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';
|
||||
|
||||
group!: LayerGroup;
|
||||
hero!: HeroRenderer;
|
||||
binder!: LayerGroupFloorBinder;
|
||||
hero?: HeroRenderer;
|
||||
binder?: LayerGroupFloorBinder;
|
||||
|
||||
/** 是否启用视角控制拓展 */
|
||||
enabled: boolean = true;
|
||||
@ -91,12 +91,13 @@ export class FloorViewport implements ILayerGroupRenderExtends {
|
||||
* @param y 图格纵坐标
|
||||
*/
|
||||
getBoundedPosition(x: number, y: number) {
|
||||
if (!this.checkDependency()) return { x, y };
|
||||
if (!this.boundX && !this.boundY) return { x, y };
|
||||
const width = core._WIDTH_;
|
||||
const height = core._HEIGHT_;
|
||||
const minX = (width - 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 maxY = floor.height - minY - 1;
|
||||
|
||||
@ -151,6 +152,7 @@ export class FloorViewport implements ILayerGroupRenderExtends {
|
||||
}
|
||||
|
||||
private createMoveTransition() {
|
||||
if (!this.checkDependency()) return;
|
||||
let xTarget: number = 0;
|
||||
let yTarget: number = 0;
|
||||
let xStart: number = this.ox;
|
||||
@ -159,7 +161,7 @@ export class FloorViewport implements ILayerGroupRenderExtends {
|
||||
let yStartTime: number = Date.now();
|
||||
let ending: boolean = false;
|
||||
// 这个数等于 sinh(2),用这个数的话,可以正好在刚开始移动的时候达到1的斜率,效果会比较好
|
||||
let transitionTime = this.hero.speed * 3.626860407847019;
|
||||
let transitionTime = this.hero!.speed * 3.626860407847019;
|
||||
|
||||
const setTargetX = (x: number, time: number) => {
|
||||
if (x === xTarget) return;
|
||||
@ -175,7 +177,7 @@ export class FloorViewport implements ILayerGroupRenderExtends {
|
||||
};
|
||||
|
||||
if (this.movingFramer) {
|
||||
this.hero.off('moveTick', this.movingFramer);
|
||||
this.hero!.off('moveTick', this.movingFramer);
|
||||
}
|
||||
this.movingFramer = () => {
|
||||
if (this.inTransition) return;
|
||||
@ -186,22 +188,22 @@ export class FloorViewport implements ILayerGroupRenderExtends {
|
||||
ending = true;
|
||||
}
|
||||
if (!ending) {
|
||||
const dir = this.hero.stepDir;
|
||||
const dir = this.hero!.stepDir;
|
||||
const { x, y } = core.utils.scan2[dir];
|
||||
setTargetX(-x * 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);
|
||||
this.nx = nx;
|
||||
this.ny = ny;
|
||||
|
||||
if (ending) {
|
||||
if (this.ox === xTarget && this.oy == yTarget) {
|
||||
this.hero.off('moveTick', this.movingFramer);
|
||||
this.hero!.off('moveTick', this.movingFramer);
|
||||
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();
|
||||
}
|
||||
|
||||
awake(group: LayerGroup): void {
|
||||
this.group = group;
|
||||
private checkDependency() {
|
||||
if (this.hero && this.binder) return true;
|
||||
const group = this.group;
|
||||
const ex1 = group.getLayer('event')?.getExtends('floor-hero');
|
||||
const ex2 = group.getExtends('floor-binder');
|
||||
if (
|
||||
@ -309,12 +312,15 @@ export class FloorViewport implements ILayerGroupRenderExtends {
|
||||
) {
|
||||
this.hero = ex1;
|
||||
this.binder = ex2;
|
||||
this.create();
|
||||
adapter.add(this);
|
||||
} else {
|
||||
logger.error(15);
|
||||
group.removeExtends('viewport');
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
awake(group: LayerGroup): void {
|
||||
this.group = group;
|
||||
this.create();
|
||||
adapter.add(this);
|
||||
}
|
||||
|
||||
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) {
|
||||
if (child.id === id) return child;
|
||||
if (child instanceof Container) {
|
||||
return this.searchElement(child, id);
|
||||
else {
|
||||
const ele = this.searchElement(child, id);
|
||||
if (ele) return ele;
|
||||
}
|
||||
}
|
||||
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 { ERenderItemEvent, RenderItem, RenderItemPosition } from './item';
|
||||
import { Transform } from './transform';
|
||||
import { GL2, GL2Program, IGL2ProgramPrefix } from './gl2';
|
||||
import { EGL2Event, GL2, GL2Program, IGL2ProgramPrefix } from './gl2';
|
||||
|
||||
const SHADER_PREFIX: IGL2ProgramPrefix = {
|
||||
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 {
|
||||
super.setHD(hd);
|
||||
this.sizeGL(this.width, this.height);
|
||||
|
@ -6,6 +6,8 @@ import {
|
||||
} from './item';
|
||||
import { MotaOffscreenCanvas2D } from '../fx/canvas2d';
|
||||
import { Transform } from './transform';
|
||||
import { ElementNamespace, ComponentInternalInstance } from 'vue';
|
||||
import { logger } from '../common/logger';
|
||||
|
||||
export interface ESpriteEvent extends ERenderItemEvent {}
|
||||
|
||||
@ -40,4 +42,24 @@ export class Sprite<
|
||||
this.renderFn = fn;
|
||||
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 { FloorViewport } from './preset/viewport';
|
||||
|
||||
|
@ -13,12 +13,15 @@
|
||||
"11": "Cache depth cannot larger than 31.",
|
||||
"12": "Cannot move while status is not 'moving'. Call 'readyMove' first.",
|
||||
"13": "Cannot compile $1 shader. Error info: $2",
|
||||
"14": "Animate extension needs 'floor-hero' extension as dependency.",
|
||||
"15": "Viewport extension needs 'floor-hero' extension as dependency.",
|
||||
"14": "",
|
||||
"15": "",
|
||||
"16": "Cannot find log message for $1 code $2.",
|
||||
"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.",
|
||||
"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.",
|
||||
"1201": "Floor-damage extension needs '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.",
|
||||
"32": "Sub-image exceeds texture dimensions, auto adjusting size.",
|
||||
"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.",
|
||||
"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 { wrapInstancedComponent } from '@/core/render';
|
||||
import { RenderItem, RenderItemPosition } from '@/core/render/item';
|
||||
import { Transform } from '@/core/render/transform';
|
||||
|
||||
// 渲染端的向后兼容用,会充当两个版本间过渡的作用
|
||||
export class FloorChange extends RenderItem {
|
||||
class Change extends RenderItem {
|
||||
private tips: string[] = [];
|
||||
/** 当前小贴士 */
|
||||
private usingTip: string = '';
|
||||
/** 透明度 */
|
||||
private alpha: number = 0;
|
||||
private backAlpha: number = 0;
|
||||
private title: string = '';
|
||||
|
||||
constructor(type: RenderItemPosition) {
|
||||
@ -45,10 +46,10 @@ export class FloorChange extends RenderItem {
|
||||
const dt = Date.now() - start;
|
||||
const progress = dt / time;
|
||||
if (progress > 1) {
|
||||
this.alpha = 1;
|
||||
this.backAlpha = 1;
|
||||
this.removeTicker(id);
|
||||
} else {
|
||||
this.alpha = progress;
|
||||
this.backAlpha = progress;
|
||||
}
|
||||
this.update();
|
||||
},
|
||||
@ -71,9 +72,9 @@ export class FloorChange extends RenderItem {
|
||||
const progress = dt / time;
|
||||
if (progress > 1) {
|
||||
this.removeTicker(id);
|
||||
this.alpha = 0;
|
||||
this.backAlpha = 0;
|
||||
} else {
|
||||
this.alpha = 1 - progress;
|
||||
this.backAlpha = 1 - progress;
|
||||
}
|
||||
this.update();
|
||||
},
|
||||
@ -87,9 +88,9 @@ export class FloorChange extends RenderItem {
|
||||
canvas: MotaOffscreenCanvas2D,
|
||||
transform: Transform
|
||||
): void {
|
||||
if (this.alpha === 0) return;
|
||||
if (this.backAlpha === 0) return;
|
||||
const ctx = canvas.ctx;
|
||||
ctx.globalAlpha = this.alpha;
|
||||
ctx.globalAlpha = this.backAlpha;
|
||||
ctx.fillStyle = '#000';
|
||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
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 { wrapInstancedComponent } from '@/core/render';
|
||||
import { RenderItem, RenderItemPosition } from '@/core/render/item';
|
||||
import { Transform } from '@/core/render/transform';
|
||||
import { TimingFn } from 'mutate-animate';
|
||||
@ -18,7 +19,7 @@ function parabola(input: number): [number, number] {
|
||||
return [x, x ** 2 / 20 - 3 * x];
|
||||
}
|
||||
|
||||
export class PopText extends RenderItem {
|
||||
class Pop extends RenderItem {
|
||||
private popList: Set<PopData> = new Set();
|
||||
|
||||
private delegation: number = 0;
|
||||
@ -94,3 +95,5 @@ export class PopText extends RenderItem {
|
||||
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 custom = [
|
||||
'container', 'image', 'sprite', 'shader', 'text', 'comment', 'custom',
|
||||
'layer', 'layer-group', 'animate', 'damage'
|
||||
]
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
vue(),
|
||||
vuejsx(),
|
||||
vue({
|
||||
customElement: custom
|
||||
}),
|
||||
vuejsx({
|
||||
isCustomElement: (tag) => {
|
||||
return custom.includes(tag)
|
||||
}
|
||||
}),
|
||||
legacy({
|
||||
targets: ['defaults', 'not IE 11'],
|
||||
polyfills: true,
|
||||
|
Loading…
Reference in New Issue
Block a user