perf: huge entity gainz
This commit is contained in:
parent
b53d7e3d35
commit
a4949bd7a0
|
@ -145,7 +145,7 @@ export default class Component {
|
||||||
toJSON() {
|
toJSON() {
|
||||||
return Component.constructor.filterDefaults(this);
|
return Component.constructor.filterDefaults(this);
|
||||||
}
|
}
|
||||||
update(values) {
|
async update(values) {
|
||||||
for (const key in values) {
|
for (const key in values) {
|
||||||
if (concrete.properties[key]) {
|
if (concrete.properties[key]) {
|
||||||
this[`$$${key}`] = values[key];
|
this[`$$${key}`] = values[key];
|
||||||
|
@ -214,10 +214,12 @@ export default class Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateMany(entities) {
|
async updateMany(entities) {
|
||||||
|
const promises = [];
|
||||||
for (let i = 0; i < entities.length; i++) {
|
for (let i = 0; i < entities.length; i++) {
|
||||||
const [entityId, values] = entities[i];
|
const [entityId, values] = entities[i];
|
||||||
this.get(entityId).update(values);
|
promises.push(this.get(entityId).update(values));
|
||||||
}
|
}
|
||||||
|
return Promise.all(promises);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,12 +14,14 @@ export default class Sprite extends Component {
|
||||||
}
|
}
|
||||||
set anchorX(anchorX) {
|
set anchorX(anchorX) {
|
||||||
this.$$anchor = {x: anchorX, y: this.anchorY};
|
this.$$anchor = {x: anchorX, y: this.anchorY};
|
||||||
|
super.anchorX = anchorX;
|
||||||
}
|
}
|
||||||
get anchorY() {
|
get anchorY() {
|
||||||
return this.$$anchor.y;
|
return this.$$anchor.y;
|
||||||
}
|
}
|
||||||
set anchorY(anchorY) {
|
set anchorY(anchorY) {
|
||||||
this.$$anchor = {x: this.anchorX, y: anchorY};
|
this.$$anchor = {x: this.anchorX, y: anchorY};
|
||||||
|
super.anchorY = anchorY;
|
||||||
}
|
}
|
||||||
get animation() {
|
get animation() {
|
||||||
return super.animation;
|
return super.animation;
|
||||||
|
@ -77,12 +79,14 @@ export default class Sprite extends Component {
|
||||||
}
|
}
|
||||||
set scaleX(scaleX) {
|
set scaleX(scaleX) {
|
||||||
this.$$scale = {x: scaleX, y: this.scaleY};
|
this.$$scale = {x: scaleX, y: this.scaleY};
|
||||||
|
super.scaleX = scaleX;
|
||||||
}
|
}
|
||||||
get scaleY() {
|
get scaleY() {
|
||||||
return this.$$scale.y;
|
return this.$$scale.y;
|
||||||
}
|
}
|
||||||
set scaleY(scaleY) {
|
set scaleY(scaleY) {
|
||||||
this.$$scale = {x: this.scaleX, y: scaleY};
|
this.$$scale = {x: this.scaleX, y: scaleY};
|
||||||
|
super.scaleY = scaleY;
|
||||||
}
|
}
|
||||||
get size() {
|
get size() {
|
||||||
if (!this.$$sourceJson.frames) {
|
if (!this.$$sourceJson.frames) {
|
||||||
|
@ -98,6 +102,32 @@ export default class Sprite extends Component {
|
||||||
const {elapsed, ...rest} = super.toNet(recipient, data);
|
const {elapsed, ...rest} = super.toNet(recipient, data);
|
||||||
return rest;
|
return rest;
|
||||||
}
|
}
|
||||||
|
update(values) {
|
||||||
|
for (const key in values) {
|
||||||
|
switch (key) {
|
||||||
|
case 'anchorX': {
|
||||||
|
this.$$anchor.x = values[key];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'anchorY': {
|
||||||
|
this.$$anchor.y = values[key];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'scaleX': {
|
||||||
|
this.$$scale.x = values[key];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'scaleY': {
|
||||||
|
this.$$scale.y = values[key];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return super.update(values);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
async load(instance) {
|
async load(instance) {
|
||||||
|
@ -123,6 +153,6 @@ export default class Sprite extends Component {
|
||||||
scaleY: {defaultValue: 1, type: 'float32'},
|
scaleY: {defaultValue: 1, type: 'float32'},
|
||||||
source: {type: 'string'},
|
source: {type: 'string'},
|
||||||
speed: {type: 'float32'},
|
speed: {type: 'float32'},
|
||||||
tint: {type: 'uint32'},
|
tint: {defaultValue: 0xffffff, type: 'uint32'},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
18
app/react/components/pixi/aabb.js
Normal file
18
app/react/components/pixi/aabb.js
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import {Graphics} from '@pixi/graphics';
|
||||||
|
|
||||||
|
export default class Aabb extends Graphics {
|
||||||
|
constructor({color, width = 0.5}) {
|
||||||
|
super();
|
||||||
|
this.$$color = color;
|
||||||
|
this.$$width = width;
|
||||||
|
}
|
||||||
|
redraw({x0, y0, x1, y1}) {
|
||||||
|
this.clear();
|
||||||
|
this.lineStyle(this.$$width, this.$$color);
|
||||||
|
this.moveTo(x0, y0);
|
||||||
|
this.lineTo(x1, y0);
|
||||||
|
this.lineTo(x1, y1);
|
||||||
|
this.lineTo(x0, y1);
|
||||||
|
this.lineTo(x0, y0);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,19 +0,0 @@
|
||||||
import {Graphics} from '@pixi/react';
|
|
||||||
import {memo, useCallback} from 'react';
|
|
||||||
|
|
||||||
function Aabb({color, width = 0.5, x0, y0, x1, y1}) {
|
|
||||||
const draw = useCallback((g) => {
|
|
||||||
g.clear();
|
|
||||||
g.lineStyle(width, color);
|
|
||||||
g.moveTo(x0, y0);
|
|
||||||
g.lineTo(x1, y0);
|
|
||||||
g.lineTo(x1, y1);
|
|
||||||
g.lineTo(x0, y1);
|
|
||||||
g.lineTo(x0, y0);
|
|
||||||
}, [color, width, x0, x1, y0, y1]);
|
|
||||||
return (
|
|
||||||
<Graphics draw={draw} />
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default memo(Aabb);
|
|
21
app/react/components/pixi/crosshair.js
Normal file
21
app/react/components/pixi/crosshair.js
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import {Graphics} from '@pixi/graphics';
|
||||||
|
|
||||||
|
export default function crosshair() {
|
||||||
|
const g = new Graphics();
|
||||||
|
g.clear();
|
||||||
|
g.lineStyle(1, 0x000000);
|
||||||
|
g.moveTo(-5, 0);
|
||||||
|
g.lineTo(5, 0);
|
||||||
|
g.moveTo(0, -5);
|
||||||
|
g.lineTo(0, 5);
|
||||||
|
g.lineStyle(0.5, 0xffffff);
|
||||||
|
g.moveTo(-5, 0);
|
||||||
|
g.lineTo(5, 0);
|
||||||
|
g.moveTo(0, -5);
|
||||||
|
g.lineTo(0, 5);
|
||||||
|
g.lineStyle(1, 0x000000);
|
||||||
|
g.drawCircle(0, 0, 3);
|
||||||
|
g.lineStyle(0.5, 0xffffff);
|
||||||
|
g.drawCircle(0, 0, 3);
|
||||||
|
return g;
|
||||||
|
}
|
|
@ -1,27 +0,0 @@
|
||||||
import {Graphics} from '@pixi/react';
|
|
||||||
import {memo, useCallback} from 'react';
|
|
||||||
|
|
||||||
function Crosshair() {
|
|
||||||
const draw = useCallback((g) => {
|
|
||||||
g.clear();
|
|
||||||
g.lineStyle(1, 0x000000);
|
|
||||||
g.moveTo(-5, 0);
|
|
||||||
g.lineTo(5, 0);
|
|
||||||
g.moveTo(0, -5);
|
|
||||||
g.lineTo(0, 5);
|
|
||||||
g.lineStyle(0.5, 0xffffff);
|
|
||||||
g.moveTo(-5, 0);
|
|
||||||
g.lineTo(5, 0);
|
|
||||||
g.moveTo(0, -5);
|
|
||||||
g.lineTo(0, 5);
|
|
||||||
g.lineStyle(1, 0x000000);
|
|
||||||
g.drawCircle(0, 0, 3);
|
|
||||||
g.lineStyle(0.5, 0xffffff);
|
|
||||||
g.drawCircle(0, 0, 3);
|
|
||||||
}, []);
|
|
||||||
return (
|
|
||||||
<Graphics draw={draw} />
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default memo(Crosshair);
|
|
|
@ -4,65 +4,85 @@ import {Container} from '@pixi/react';
|
||||||
import {useCallback, useEffect, useRef, useState} from 'react';
|
import {useCallback, useEffect, useRef, useState} from 'react';
|
||||||
|
|
||||||
import {usePacket} from '@/react/context/client.js';
|
import {usePacket} from '@/react/context/client.js';
|
||||||
|
import {useDebug} from '@/react/context/debug.js';
|
||||||
import {useEcs, useEcsTick} from '@/react/context/ecs.js';
|
import {useEcs, useEcsTick} from '@/react/context/ecs.js';
|
||||||
import {useMainEntity} from '@/react/context/main-entity.js';
|
import {useMainEntity} from '@/react/context/main-entity.js';
|
||||||
import {useRadians} from '@/react/context/radians.js';
|
import {useRadians} from '@/react/context/radians.js';
|
||||||
|
|
||||||
import Entity from './entity.jsx';
|
import Entity from './entity.js';
|
||||||
|
|
||||||
export default function Entities({monopolizers, particleWorker}) {
|
export default function Entities({monopolizers, particleWorker}) {
|
||||||
|
const [debug] = useDebug();
|
||||||
const [ecs] = useEcs();
|
const [ecs] = useEcs();
|
||||||
|
const containerRef = useRef();
|
||||||
const entities = useRef({});
|
const entities = useRef({});
|
||||||
|
const pool = useRef([]);
|
||||||
const [mainEntity] = useMainEntity();
|
const [mainEntity] = useMainEntity();
|
||||||
const radians = useRadians();
|
const radians = useRadians();
|
||||||
const [willInteractWith, setWillInteractWith] = useState(0);
|
const willInteractWith = useRef(0);
|
||||||
const [interactionFilters] = useState([
|
const [interactionFilters] = useState([
|
||||||
new AdjustmentFilter(),
|
new AdjustmentFilter(),
|
||||||
new GlowFilter({color: 0x0}),
|
new GlowFilter({color: 0x0}),
|
||||||
]);
|
]);
|
||||||
const [, forceRender] = useState();
|
const pulse = (Math.cos(radians / 4) + 1) * 0.5;
|
||||||
|
interactionFilters[0].brightness = (pulse * 0.75) + 1;
|
||||||
|
interactionFilters[1].outerStrength = pulse * 0.5;
|
||||||
|
const updateEntities = useCallback((payload) => {
|
||||||
|
if (0 === Object.keys(entities.current).length) {
|
||||||
|
for (let i = 0; i < 1000; ++i) {
|
||||||
|
pool.current.push(new Entity());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const id in payload) {
|
||||||
|
if (!payload[id]) {
|
||||||
|
entities.current[id].removeFromContainer();
|
||||||
|
pool.current.push(entities.current[id]);
|
||||||
|
delete entities.current[id];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const entity = ecs.get(id);
|
||||||
|
if (!entity.Position) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!entities.current[id]) {
|
||||||
|
entities.current[id] = pool.current.length > 0 ? pool.current.pop() : new Entity();
|
||||||
|
entities.current[id].reset(entity, debug);
|
||||||
|
}
|
||||||
|
if (mainEntity === id) {
|
||||||
|
entities.current[id].setMainEntity();
|
||||||
|
}
|
||||||
|
entities.current[id].update(payload[id]);
|
||||||
|
entities.current[id].addToContainer(containerRef.current);
|
||||||
|
}
|
||||||
|
}, [debug, ecs, mainEntity])
|
||||||
|
useEffect(() => {
|
||||||
|
for (const key in entities.current) {
|
||||||
|
entities.current[key].setDebug(debug);
|
||||||
|
}
|
||||||
|
}, [debug]);
|
||||||
|
usePacket('EcsChange', async () => {
|
||||||
|
for (const id in entities.current) {
|
||||||
|
entities.current[id].removeFromContainer();
|
||||||
|
}
|
||||||
|
entities.current = {};
|
||||||
|
});
|
||||||
|
const onEcsTickEntities = useCallback((payload) => {
|
||||||
|
updateEntities(payload);
|
||||||
|
}, [updateEntities]);
|
||||||
|
useEcsTick(onEcsTickEntities);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!ecs || !particleWorker) {
|
if (!ecs || !particleWorker) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
async function onMessage(diff) {
|
async function onMessage(diff) {
|
||||||
await ecs.apply(diff.data);
|
await ecs.apply(diff.data);
|
||||||
for (const id in diff.data) {
|
updateEntities(diff.data);
|
||||||
if (!diff.data[id]) {
|
|
||||||
delete entities.current[id]
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
entities.current[id] = ecs.get(id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
forceRender(Math.random());
|
|
||||||
}
|
}
|
||||||
particleWorker.addEventListener('message', onMessage);
|
particleWorker.addEventListener('message', onMessage);
|
||||||
return () => {
|
return () => {
|
||||||
particleWorker.removeEventListener('message', onMessage);
|
particleWorker.removeEventListener('message', onMessage);
|
||||||
};
|
};
|
||||||
}, [ecs, particleWorker]);
|
}, [ecs, particleWorker, updateEntities]);
|
||||||
const pulse = (Math.cos(radians / 4) + 1) * 0.5;
|
|
||||||
interactionFilters[0].brightness = (pulse * 0.75) + 1;
|
|
||||||
interactionFilters[1].outerStrength = pulse * 0.5;
|
|
||||||
usePacket('EcsChange', async () => {
|
|
||||||
entities.current = {};
|
|
||||||
});
|
|
||||||
const onEcsTickEntities = useCallback((payload, ecs) => {
|
|
||||||
for (const id in payload) {
|
|
||||||
if ('1' === id) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const update = payload[id];
|
|
||||||
if (false === update) {
|
|
||||||
delete entities.current[id];
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
entities.current[id] = ecs.get(id);
|
|
||||||
}
|
|
||||||
forceRender(Math.random());
|
|
||||||
}, []);
|
|
||||||
useEcsTick(onEcsTickEntities);
|
|
||||||
const onEcsTickParticles = useCallback((payload) => {
|
const onEcsTickParticles = useCallback((payload) => {
|
||||||
for (const id in payload) {
|
for (const id in payload) {
|
||||||
const update = payload[id];
|
const update = payload[id];
|
||||||
|
@ -77,26 +97,25 @@ export default function Entities({monopolizers, particleWorker}) {
|
||||||
const onEcsTickInteractions = useCallback((payload, ecs) => {
|
const onEcsTickInteractions = useCallback((payload, ecs) => {
|
||||||
const main = ecs.get(mainEntity);
|
const main = ecs.get(mainEntity);
|
||||||
if (main) {
|
if (main) {
|
||||||
setWillInteractWith(main.Interacts.willInteractWith);
|
if (willInteractWith.current !== main.Interacts.willInteractWith) {
|
||||||
|
if (entities.current[willInteractWith.current]) {
|
||||||
|
entities.current[willInteractWith.current].diffuse.filters = [];
|
||||||
|
}
|
||||||
|
willInteractWith.current = main.Interacts.willInteractWith;
|
||||||
|
}
|
||||||
|
const interacting = entities.current[main.Interacts.willInteractWith];
|
||||||
|
if (interacting) {
|
||||||
|
interacting.diffuse.filters = 0 === monopolizers.length
|
||||||
|
? interactionFilters
|
||||||
|
: [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, [mainEntity]);
|
}, [interactionFilters, mainEntity, monopolizers]);
|
||||||
useEcsTick(onEcsTickInteractions);
|
useEcsTick(onEcsTickInteractions);
|
||||||
const renderables = [];
|
|
||||||
for (const id in entities.current) {
|
|
||||||
const isHighlightedInteraction = 0 === monopolizers.length && id == willInteractWith;
|
|
||||||
renderables.push(
|
|
||||||
<Entity
|
|
||||||
filters={isHighlightedInteraction ? interactionFilters : null}
|
|
||||||
entity={entities.current[id]}
|
|
||||||
key={id}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return (
|
return (
|
||||||
<Container
|
<Container
|
||||||
|
ref={containerRef}
|
||||||
sortableChildren
|
sortableChildren
|
||||||
>
|
/>
|
||||||
{renderables}
|
|
||||||
</Container>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
191
app/react/components/pixi/entity.js
Normal file
191
app/react/components/pixi/entity.js
Normal file
|
@ -0,0 +1,191 @@
|
||||||
|
import {Assets} from '@pixi/assets';
|
||||||
|
import {Container as PixiContainer} from '@pixi/display';
|
||||||
|
import {Sprite} from '@pixi/sprite';
|
||||||
|
|
||||||
|
import Aabb from './aabb.js';
|
||||||
|
import createCrosshair from './crosshair.js';
|
||||||
|
import {deferredLighting, PointLight} from './lights.js';
|
||||||
|
|
||||||
|
export default class Entity {
|
||||||
|
static assets = {};
|
||||||
|
attached = false;
|
||||||
|
colliderAabb = new Aabb({color: 0xffffff});
|
||||||
|
container = new PixiContainer();
|
||||||
|
crosshair = createCrosshair();
|
||||||
|
debug = new PixiContainer();
|
||||||
|
diffuse = new Sprite();
|
||||||
|
isMainEntity = false;
|
||||||
|
normals = new Sprite();
|
||||||
|
point;
|
||||||
|
sprite = new PixiContainer();
|
||||||
|
visibleAabb = new Aabb({color: 0xff00ff});
|
||||||
|
constructor() {
|
||||||
|
this.diffuse.parentGroup = deferredLighting.diffuseGroup;
|
||||||
|
this.normals.parentGroup = deferredLighting.normalGroup;
|
||||||
|
this.container.addChild(this.diffuse);
|
||||||
|
this.container.addChild(this.normals);
|
||||||
|
this.container.addChild(this.crosshair);
|
||||||
|
this.debug.addChild(this.colliderAabb);
|
||||||
|
this.debug.addChild(this.visibleAabb);
|
||||||
|
}
|
||||||
|
addToContainer(container) {
|
||||||
|
if (this.attached || !container) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
container.addChild(this.container);
|
||||||
|
container.addChild(this.debug);
|
||||||
|
this.attached = true;
|
||||||
|
}
|
||||||
|
static async loadAsset(source = '') {
|
||||||
|
if (!this.assets['']) {
|
||||||
|
const {Texture} = await import('@pixi/core');
|
||||||
|
this.assets[''] = {data: {meta: {}}, textures: {'': Texture.WHITE}};
|
||||||
|
}
|
||||||
|
if (!this.assets[source]) {
|
||||||
|
this.assets[source] = Assets.load(source);
|
||||||
|
}
|
||||||
|
return this.assets[source];
|
||||||
|
}
|
||||||
|
removeFromContainer() {
|
||||||
|
this.container.parent.removeChild(this.container);
|
||||||
|
this.debug.parent.removeChild(this.debug);
|
||||||
|
this.attached = false;
|
||||||
|
}
|
||||||
|
reset(entity, debug) {
|
||||||
|
this.entity = entity;
|
||||||
|
if (this.light) {
|
||||||
|
this.container.removeChild(this.light);
|
||||||
|
this.light = undefined;
|
||||||
|
}
|
||||||
|
this.setDebug(debug);
|
||||||
|
this.isMainEntity = false;
|
||||||
|
if (this.interactionAabb) {
|
||||||
|
this.debug.removeChild(this.interactionAabb);
|
||||||
|
}
|
||||||
|
this.interactionAabb = undefined;
|
||||||
|
}
|
||||||
|
setDebug(isDebugging) {
|
||||||
|
if (isDebugging) {
|
||||||
|
this.crosshair.alpha = 1;
|
||||||
|
this.debug.alpha = 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.crosshair.alpha = 0;
|
||||||
|
this.debug.alpha = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setMainEntity() {
|
||||||
|
if (this.isMainEntity) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.isMainEntity = true;
|
||||||
|
this.interactionAabb = new Aabb({color: 0x00ff00});
|
||||||
|
this.debug.addChild(this.interactionAabb);
|
||||||
|
}
|
||||||
|
static textureFromAsset(asset, animation, frame) {
|
||||||
|
if (!asset) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
let texture;
|
||||||
|
if (asset.data.animations) {
|
||||||
|
if (!animation) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
texture = asset.animations[animation][frame];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
texture = asset.textures[''];
|
||||||
|
}
|
||||||
|
return texture;
|
||||||
|
}
|
||||||
|
update({Direction, Light, Position, Sprite, VisibleAabb}) {
|
||||||
|
if (Light) {
|
||||||
|
if (!this.light) {
|
||||||
|
this.light = new PointLight(
|
||||||
|
0xffffff - 0x2244cc,
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
this.light.brightness = Light.brightness;
|
||||||
|
}
|
||||||
|
if (Position) {
|
||||||
|
const {x, y} = this.entity.Position;
|
||||||
|
this.container.x = x;
|
||||||
|
this.container.y = y;
|
||||||
|
this.container.zIndex = y;
|
||||||
|
}
|
||||||
|
if (Sprite) {
|
||||||
|
const {diffuse, normals} = this;
|
||||||
|
if (!this.attached || 'alpha' in Sprite) {
|
||||||
|
diffuse.alpha = normals.alpha = this.entity.Sprite.alpha;
|
||||||
|
}
|
||||||
|
if (!this.attached || 'anchorX' in Sprite) {
|
||||||
|
diffuse.anchor.x = normals.anchor.x = this.entity.Sprite.anchorX;
|
||||||
|
}
|
||||||
|
if (!this.attached || 'anchorY' in Sprite) {
|
||||||
|
diffuse.anchor.y = normals.anchor.y = this.entity.Sprite.anchorY;
|
||||||
|
}
|
||||||
|
if (!this.attached || 'scaleX' in Sprite) {
|
||||||
|
diffuse.scale.x = normals.scale.x = this.entity.Sprite.scaleX;
|
||||||
|
}
|
||||||
|
if (!this.attached || 'scaleY' in Sprite) {
|
||||||
|
diffuse.scale.y = normals.scale.y = this.entity.Sprite.scaleY;
|
||||||
|
}
|
||||||
|
if (!this.attached || 'tint' in Sprite) {
|
||||||
|
diffuse.tint = normals.tint = this.entity.Sprite.tint;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
!this.attached
|
||||||
|
|| ('source' in Sprite)
|
||||||
|
|| ('animation' in Sprite)
|
||||||
|
|| ('frame' in Sprite)
|
||||||
|
) {
|
||||||
|
this.constructor.loadAsset(this.entity.Sprite.source).then(async (asset) => {
|
||||||
|
const texture = await this.constructor.textureFromAsset(
|
||||||
|
asset,
|
||||||
|
this.entity.Sprite.animation,
|
||||||
|
this.entity.Sprite.frame,
|
||||||
|
);
|
||||||
|
diffuse.texture = texture;
|
||||||
|
if (asset.data.meta.normals) {
|
||||||
|
const {pathname} = new URL(
|
||||||
|
Sprite.source
|
||||||
|
.split('/')
|
||||||
|
.slice(0, -1)
|
||||||
|
.concat(asset.data.meta.normals)
|
||||||
|
.join('/'),
|
||||||
|
'http://example.org',
|
||||||
|
);
|
||||||
|
this.constructor.loadAsset(pathname).then(async (asset) => {
|
||||||
|
const texture = await this.constructor.textureFromAsset(
|
||||||
|
asset,
|
||||||
|
this.entity.Sprite.animation,
|
||||||
|
this.entity.Sprite.frame,
|
||||||
|
);
|
||||||
|
normals.texture = texture;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Direction) {
|
||||||
|
const {diffuse, normals} = this;
|
||||||
|
if ('direction' in Direction) {
|
||||||
|
if (this.entity.Sprite.rotates) {
|
||||||
|
const rotation = Direction.direction + this.entity.Sprite.rotation;
|
||||||
|
diffuse.rotation = rotation;
|
||||||
|
normals.rotation = rotation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.entity.Collider) {
|
||||||
|
this.colliderAabb.redraw(this.entity.Collider.aabb);
|
||||||
|
}
|
||||||
|
if (VisibleAabb) {
|
||||||
|
this.visibleAabb.redraw(this.entity.VisibleAabb);
|
||||||
|
}
|
||||||
|
if (this.isMainEntity) {
|
||||||
|
this.interactionAabb.redraw(this.entity.Interacts.aabb());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,84 +0,0 @@
|
||||||
import {Container} from '@pixi/react';
|
|
||||||
import {memo} from 'react';
|
|
||||||
|
|
||||||
import {useDebug} from '@/react/context/debug.js';
|
|
||||||
import {useMainEntity} from '@/react/context/main-entity.js';
|
|
||||||
|
|
||||||
import Aabb from './aabb.jsx';
|
|
||||||
import Crosshair from './crosshair.jsx';
|
|
||||||
import Light from './light.jsx';
|
|
||||||
import SpriteComponent from './sprite.jsx';
|
|
||||||
|
|
||||||
function Entity({entity, ...rest}) {
|
|
||||||
const [debug] = useDebug();
|
|
||||||
const [mainEntity] = useMainEntity();
|
|
||||||
if (!entity) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const {Direction, id, Sprite} = entity;
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Container
|
|
||||||
x={entity.Position.x}
|
|
||||||
y={entity.Position.y}
|
|
||||||
zIndex={entity.Position?.y || 0}
|
|
||||||
>
|
|
||||||
{entity.Sprite && (
|
|
||||||
<SpriteComponent
|
|
||||||
alpha={Sprite.alpha}
|
|
||||||
anchor={Sprite.anchor}
|
|
||||||
animation={Sprite.animation}
|
|
||||||
direction={Direction?.direction}
|
|
||||||
frame={Sprite.frame}
|
|
||||||
id={id}
|
|
||||||
scale={Sprite.scale}
|
|
||||||
rotates={Sprite.rotates}
|
|
||||||
rotation={Sprite.rotation}
|
|
||||||
source={Sprite.source}
|
|
||||||
tint={Sprite.tint}
|
|
||||||
{...rest}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{entity.Light && (
|
|
||||||
<Light
|
|
||||||
brightness={entity.Light.brightness}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{debug && entity.Position && (
|
|
||||||
<Crosshair />
|
|
||||||
)}
|
|
||||||
</Container>
|
|
||||||
{debug && (
|
|
||||||
<Aabb
|
|
||||||
color={0xff00ff}
|
|
||||||
{...entity.VisibleAabb}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{debug && entity.Collider && (
|
|
||||||
<>
|
|
||||||
<Aabb
|
|
||||||
color={0xffffff}
|
|
||||||
width={0.5}
|
|
||||||
{...entity.Collider.aabb}
|
|
||||||
/>
|
|
||||||
{entity.Collider.aabbs.map((aabb, i) => (
|
|
||||||
<Aabb
|
|
||||||
color={0xffff00}
|
|
||||||
width={0.25}
|
|
||||||
key={i}
|
|
||||||
{...aabb}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{debug && mainEntity == entity.id && (
|
|
||||||
<Aabb
|
|
||||||
color={0x00ff00}
|
|
||||||
{...entity.Interacts.aabb()}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default memo(Entity);
|
|
|
@ -1,34 +0,0 @@
|
||||||
import {PixiComponent} from '@pixi/react';
|
|
||||||
|
|
||||||
import {PointLight} from './lights.js';
|
|
||||||
|
|
||||||
const LightInternal = PixiComponent('Light', {
|
|
||||||
create({brightness}) {
|
|
||||||
const light = new PointLight(
|
|
||||||
0xffffff - 0x2244cc,
|
|
||||||
brightness,
|
|
||||||
);
|
|
||||||
// light.shader.program.fragmentSrc = light.shader.program.fragmentSrc.replace(
|
|
||||||
// 'float D = length(lightVector)',
|
|
||||||
// 'float D = length(lightVector) / 1.0',
|
|
||||||
// );
|
|
||||||
// light.shader.program.fragmentSrc = light.shader.program.fragmentSrc.replace(
|
|
||||||
// 'intensity = diffuse * attenuation',
|
|
||||||
// 'intensity = diffuse * (attenuation * 2.0)',
|
|
||||||
// );
|
|
||||||
// light.falloff = [0.5, 5, 50];
|
|
||||||
// light.falloff = light.falloff.map((n, i) => n / (2 + i));
|
|
||||||
// light.parentGroup = deferredLighting.lightGroup;
|
|
||||||
// delete light.parentGroup;
|
|
||||||
return light;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export default function Light({brightness}) {
|
|
||||||
return (
|
|
||||||
<LightInternal
|
|
||||||
brightness={brightness}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,102 +0,0 @@
|
||||||
import {Sprite as PixiSprite} from '@pixi/react';
|
|
||||||
import {memo, useEffect, useState} from 'react';
|
|
||||||
|
|
||||||
import {useAsset} from '@/react/context/assets.js';
|
|
||||||
|
|
||||||
import {deferredLighting} from './lights.js';
|
|
||||||
|
|
||||||
function textureFromAsset(asset, animation, frame) {
|
|
||||||
if (!asset) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
let texture;
|
|
||||||
if (asset.data.animations) {
|
|
||||||
if (!animation) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
texture = asset.animations[animation][frame];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
texture = asset.textures[''];
|
|
||||||
}
|
|
||||||
return texture;
|
|
||||||
}
|
|
||||||
|
|
||||||
function Sprite(props) {
|
|
||||||
const {
|
|
||||||
alpha,
|
|
||||||
anchor,
|
|
||||||
animation,
|
|
||||||
direction,
|
|
||||||
frame,
|
|
||||||
scale,
|
|
||||||
rotates,
|
|
||||||
rotation,
|
|
||||||
source,
|
|
||||||
tint,
|
|
||||||
...rest
|
|
||||||
} = props;
|
|
||||||
const [mounted, setMounted] = useState();
|
|
||||||
const [normals, setNormals] = useState();
|
|
||||||
const [normalsMounted, setNormalsMounted] = useState();
|
|
||||||
const asset = useAsset(source);
|
|
||||||
const normalsAsset = useAsset(normals);
|
|
||||||
useEffect(() => {
|
|
||||||
if (!asset) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const {normals} = asset.data.meta;
|
|
||||||
if (normals) {
|
|
||||||
const {pathname} = new URL(
|
|
||||||
source.split('/').slice(0, -1).concat(normals).join('/'),
|
|
||||||
'http://example.org',
|
|
||||||
);
|
|
||||||
setNormals(pathname);
|
|
||||||
}
|
|
||||||
}, [asset, source]);
|
|
||||||
const texture = textureFromAsset(
|
|
||||||
asset,
|
|
||||||
animation,
|
|
||||||
frame,
|
|
||||||
);
|
|
||||||
const normalsTexture = textureFromAsset(
|
|
||||||
normalsAsset,
|
|
||||||
animation,
|
|
||||||
frame,
|
|
||||||
);
|
|
||||||
if (mounted) {
|
|
||||||
mounted.parentGroup = deferredLighting.diffuseGroup;
|
|
||||||
}
|
|
||||||
if (normalsMounted) {
|
|
||||||
normalsMounted.parentGroup = deferredLighting.normalGroup;
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{texture && (
|
|
||||||
<PixiSprite
|
|
||||||
alpha={alpha}
|
|
||||||
anchor={anchor}
|
|
||||||
ref={setMounted}
|
|
||||||
{...(rotates ? {rotation: direction + rotation} : {})}
|
|
||||||
scale={scale}
|
|
||||||
texture={texture}
|
|
||||||
{...(0 !== tint ? {tint} : {})}
|
|
||||||
{...rest}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{normalsTexture && (
|
|
||||||
<PixiSprite
|
|
||||||
alpha={alpha}
|
|
||||||
anchor={anchor}
|
|
||||||
ref={setNormalsMounted}
|
|
||||||
{...(rotates ? {rotation: direction + rotation} : {})}
|
|
||||||
scale={scale}
|
|
||||||
texture={normalsTexture}
|
|
||||||
{...rest}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default memo(Sprite);
|
|
|
@ -10,13 +10,11 @@ const TargetingGhostInternal = PixiComponent('TargetingGhost', {
|
||||||
create: () => {
|
create: () => {
|
||||||
// Solid target square.
|
// Solid target square.
|
||||||
const target = new Graphics();
|
const target = new Graphics();
|
||||||
target.alpha = 0.7;
|
|
||||||
target.lineStyle(1, 0xffffff);
|
target.lineStyle(1, 0xffffff);
|
||||||
target.drawRect(0.5, 0.5, tileSize.x, tileSize.y);
|
target.drawRect(0.5, 0.5, tileSize.x, tileSize.y);
|
||||||
target.pivot = {x: tileSize.x / 2, y: tileSize.y / 2};
|
target.pivot = {x: tileSize.x / 2, y: tileSize.y / 2};
|
||||||
// Inner spinny part.
|
// Inner spinny part.
|
||||||
const targetInner = new Graphics();
|
const targetInner = new Graphics();
|
||||||
targetInner.alpha = 0.3;
|
|
||||||
targetInner.lineStyle(3, 0x333333);
|
targetInner.lineStyle(3, 0x333333);
|
||||||
targetInner.beginFill(0xdddddd);
|
targetInner.beginFill(0xdddddd);
|
||||||
targetInner.pivot = {x: tileSize.x / 2, y: tileSize.y / 2};
|
targetInner.pivot = {x: tileSize.x / 2, y: tileSize.y / 2};
|
||||||
|
@ -25,6 +23,7 @@ const TargetingGhostInternal = PixiComponent('TargetingGhost', {
|
||||||
targetInner.position.y = 0.5;
|
targetInner.position.y = 0.5;
|
||||||
// ...
|
// ...
|
||||||
const container = new Container();
|
const container = new Container();
|
||||||
|
container.alpha = 0.4;
|
||||||
container.addChild(target, targetInner);
|
container.addChild(target, targetInner);
|
||||||
return container;
|
return container;
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in New Issue
Block a user