mirror of
				https://github.com/unanmed/HumanBreak.git
				synced 2025-11-04 07:02:58 +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