feat: tiles
This commit is contained in:
parent
aef8a292d0
commit
a31bc01911
|
@ -52,7 +52,7 @@ module.exports = {
|
||||||
|
|
||||||
// Node
|
// Node
|
||||||
{
|
{
|
||||||
files: ['.eslintrc.cjs', 'server.js', 'vite.config.js', 'websocket.js'],
|
files: ['.eslintrc.cjs', 'server.js', 'vite.config.js', 'websocket.js', 'public/assets/tileset.js'],
|
||||||
env: {
|
env: {
|
||||||
node: true,
|
node: true,
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,10 +1,14 @@
|
||||||
import {Sprite} from '@pixi/react';
|
import {Container, Sprite} from '@pixi/react';
|
||||||
import {useState} from 'react';
|
import {useState} from 'react';
|
||||||
|
|
||||||
import {RESOLUTION} from '@/constants.js'
|
import {RESOLUTION} from '@/constants.js'
|
||||||
import Ecs from '@/engine/ecs.js';
|
import Ecs from '@/engine/ecs.js';
|
||||||
import usePacket from '@/hooks/use-packet';
|
import usePacket from '@/hooks/use-packet';
|
||||||
|
|
||||||
|
import TileLayer from './tile-layer';
|
||||||
|
|
||||||
|
const tiles = Array(100 * 100).fill(0).map(() => 1 + Math.floor(Math.random() * 4));
|
||||||
|
|
||||||
export default function EcsComponent() {
|
export default function EcsComponent() {
|
||||||
const [ecs] = useState(new Ecs());
|
const [ecs] = useState(new Ecs());
|
||||||
const [entities, setEntities] = useState({});
|
const [entities, setEntities] = useState({});
|
||||||
|
@ -22,12 +26,14 @@ export default function EcsComponent() {
|
||||||
else {
|
else {
|
||||||
updatedEntities[id] = ecs.get(id);
|
updatedEntities[id] = ecs.get(id);
|
||||||
if (updatedEntities[id].MainEntity) {
|
if (updatedEntities[id].MainEntity) {
|
||||||
|
if (!mainEntity) {
|
||||||
setMainEntity(ecs.get(id));
|
setMainEntity(ecs.get(id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
setEntities(updatedEntities);
|
setEntities(updatedEntities);
|
||||||
}, [entities]);
|
}, [entities, mainEntity]);
|
||||||
if (!mainEntity) {
|
if (!mainEntity) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -46,8 +52,16 @@ export default function EcsComponent() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<>
|
<Container>
|
||||||
|
<TileLayer
|
||||||
|
size={{x: 100, y: 100}}
|
||||||
|
tiles={tiles}
|
||||||
|
tileset="/assets/tileset.json"
|
||||||
|
tileSize={{x: 16, y: 16}}
|
||||||
|
x={-cx}
|
||||||
|
y={-cy}
|
||||||
|
/>
|
||||||
{sprites}
|
{sprites}
|
||||||
</>
|
</Container>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
49
app/react-components/tile-layer.jsx
Normal file
49
app/react-components/tile-layer.jsx
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
import {useEffect, useState} from 'react';
|
||||||
|
import {PixiComponent} from '@pixi/react';
|
||||||
|
import '@pixi/spritesheet'; // NECESSARY!
|
||||||
|
import {CompositeTilemap} from '@pixi/tilemap';
|
||||||
|
|
||||||
|
import {Assets} from '@pixi/assets';
|
||||||
|
|
||||||
|
const TileLayerInternal = PixiComponent('TileLayer', {
|
||||||
|
create: () => new CompositeTilemap(),
|
||||||
|
applyProps: (tilemap, {tiles: oldTiles}, props) => {
|
||||||
|
const {asset, tiles, tileSize, size, x, y} = props;
|
||||||
|
const {textures} = asset;
|
||||||
|
tilemap.position.x = x;
|
||||||
|
tilemap.position.y = y;
|
||||||
|
if (tiles === oldTiles) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tilemap.clear();
|
||||||
|
let i = 0;
|
||||||
|
for (let y = 0; y < size.y; ++y) {
|
||||||
|
for (let x = 0; x < size.x; ++x) {
|
||||||
|
tilemap.tile(textures[tiles[i++]], tileSize.x * x, tileSize.y * y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
export default function TileLayer(props) {
|
||||||
|
const {tileset} = props;
|
||||||
|
const [asset, setAsset] = useState();
|
||||||
|
useEffect(() => {
|
||||||
|
const asset = Assets.get(tileset);
|
||||||
|
if (asset) {
|
||||||
|
setAsset(asset);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Assets.load(tileset).then(setAsset);
|
||||||
|
}
|
||||||
|
}, [setAsset, tileset]);
|
||||||
|
if (!asset) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<TileLayerInternal
|
||||||
|
{...props}
|
||||||
|
asset={asset}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
41
package-lock.json
generated
41
package-lock.json
generated
|
@ -8,6 +8,8 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@msgpack/msgpack": "^3.0.0-beta2",
|
"@msgpack/msgpack": "^3.0.0-beta2",
|
||||||
"@pixi/react": "^7.1.2",
|
"@pixi/react": "^7.1.2",
|
||||||
|
"@pixi/spritesheet": "^7.4.2",
|
||||||
|
"@pixi/tilemap": "^4.1.0",
|
||||||
"@remix-run/express": "^2.9.2",
|
"@remix-run/express": "^2.9.2",
|
||||||
"@remix-run/node": "^2.9.2",
|
"@remix-run/node": "^2.9.2",
|
||||||
"@remix-run/react": "^2.9.2",
|
"@remix-run/react": "^2.9.2",
|
||||||
|
@ -38,6 +40,7 @@
|
||||||
"eslint-plugin-jsx-a11y": "^6.7.1",
|
"eslint-plugin-jsx-a11y": "^6.7.1",
|
||||||
"eslint-plugin-react": "^7.33.2",
|
"eslint-plugin-react": "^7.33.2",
|
||||||
"eslint-plugin-react-hooks": "^4.6.0",
|
"eslint-plugin-react-hooks": "^4.6.0",
|
||||||
|
"image-size": "^1.1.1",
|
||||||
"storybook": "^8.1.6",
|
"storybook": "^8.1.6",
|
||||||
"vite": "^5.1.0",
|
"vite": "^5.1.0",
|
||||||
"vitest": "^1.6.0"
|
"vitest": "^1.6.0"
|
||||||
|
@ -3386,6 +3389,15 @@
|
||||||
"@pixi/sprite": "7.4.2"
|
"@pixi/sprite": "7.4.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@pixi/spritesheet": {
|
||||||
|
"version": "7.4.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@pixi/spritesheet/-/spritesheet-7.4.2.tgz",
|
||||||
|
"integrity": "sha512-YIvHdpXW+AYp8vD0NkjJmrdnVHTZKidCnx6k8ATSuuvCT6O5Tuh2N/Ul2oDj4/QaePy0lVhyhAbZpJW00Jr7mQ==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@pixi/assets": "7.4.2",
|
||||||
|
"@pixi/core": "7.4.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@pixi/text": {
|
"node_modules/@pixi/text": {
|
||||||
"version": "7.4.2",
|
"version": "7.4.2",
|
||||||
"resolved": "https://registry.npmjs.org/@pixi/text/-/text-7.4.2.tgz",
|
"resolved": "https://registry.npmjs.org/@pixi/text/-/text-7.4.2.tgz",
|
||||||
|
@ -3420,6 +3432,11 @@
|
||||||
"@pixi/utils": "7.4.2"
|
"@pixi/utils": "7.4.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@pixi/tilemap": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@pixi/tilemap/-/tilemap-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-jyc5JqpvPsdsowhkX0VURSMuUd1KdZmG0+Kh11EFGhkrraEKMqTOtU32TPwJb6G0jIqT2o7ynVrG3h5WRoasIg=="
|
||||||
|
},
|
||||||
"node_modules/@pixi/utils": {
|
"node_modules/@pixi/utils": {
|
||||||
"version": "7.4.2",
|
"version": "7.4.2",
|
||||||
"resolved": "https://registry.npmjs.org/@pixi/utils/-/utils-7.4.2.tgz",
|
"resolved": "https://registry.npmjs.org/@pixi/utils/-/utils-7.4.2.tgz",
|
||||||
|
@ -11125,6 +11142,21 @@
|
||||||
"node": ">= 4"
|
"node": ">= 4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/image-size": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/image-size/-/image-size-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-541xKlUw6jr/6gGuk92F+mYM5zaFAc5ahphvkqvNe2bQ6gVBkd6bfrmVJ2t4KDAfikAYZyIqTnktX3i6/aQDrQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"queue": "6.0.2"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"image-size": "bin/image-size.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.x"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/import-fresh": {
|
"node_modules/import-fresh": {
|
||||||
"version": "3.3.0",
|
"version": "3.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
|
||||||
|
@ -14936,6 +14968,15 @@
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/queue": {
|
||||||
|
"version": "6.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz",
|
||||||
|
"integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"inherits": "~2.0.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/queue-microtask": {
|
"node_modules/queue-microtask": {
|
||||||
"version": "1.2.3",
|
"version": "1.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
|
||||||
|
|
|
@ -14,6 +14,8 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@msgpack/msgpack": "^3.0.0-beta2",
|
"@msgpack/msgpack": "^3.0.0-beta2",
|
||||||
"@pixi/react": "^7.1.2",
|
"@pixi/react": "^7.1.2",
|
||||||
|
"@pixi/spritesheet": "^7.4.2",
|
||||||
|
"@pixi/tilemap": "^4.1.0",
|
||||||
"@remix-run/express": "^2.9.2",
|
"@remix-run/express": "^2.9.2",
|
||||||
"@remix-run/node": "^2.9.2",
|
"@remix-run/node": "^2.9.2",
|
||||||
"@remix-run/react": "^2.9.2",
|
"@remix-run/react": "^2.9.2",
|
||||||
|
@ -44,6 +46,7 @@
|
||||||
"eslint-plugin-jsx-a11y": "^6.7.1",
|
"eslint-plugin-jsx-a11y": "^6.7.1",
|
||||||
"eslint-plugin-react": "^7.33.2",
|
"eslint-plugin-react": "^7.33.2",
|
||||||
"eslint-plugin-react-hooks": "^4.6.0",
|
"eslint-plugin-react-hooks": "^4.6.0",
|
||||||
|
"image-size": "^1.1.1",
|
||||||
"storybook": "^8.1.6",
|
"storybook": "^8.1.6",
|
||||||
"vite": "^5.1.0",
|
"vite": "^5.1.0",
|
||||||
"vitest": "^1.6.0"
|
"vitest": "^1.6.0"
|
||||||
|
|
36
public/assets/tileset.js
Normal file
36
public/assets/tileset.js
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
import {writeFileSync} from 'node:fs';
|
||||||
|
import {basename, dirname, extname, join} from 'node:path';
|
||||||
|
|
||||||
|
import imageSize from 'image-size';
|
||||||
|
|
||||||
|
const tileset = process.argv[2];
|
||||||
|
const w = parseInt(process.argv[3]);
|
||||||
|
const h = parseInt(process.argv[4]);
|
||||||
|
|
||||||
|
const {width, height} = imageSize(tileset);
|
||||||
|
|
||||||
|
const json = {
|
||||||
|
frames: {},
|
||||||
|
meta: {
|
||||||
|
format: 'RGBA8888',
|
||||||
|
image: tileset,
|
||||||
|
scale: 1,
|
||||||
|
size: {w: width, h: height},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let i = 0;
|
||||||
|
for (let y = 0; y < height; y += h) {
|
||||||
|
for (let x = 0; x < width; x += w) {
|
||||||
|
json.frames[i++] = {
|
||||||
|
frame: {x, y, w, h},
|
||||||
|
spriteSourceSize: {x: 0, y: 0, w, h},
|
||||||
|
sourceSize: {w, h},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writeFileSync(
|
||||||
|
`${join(dirname(tileset), basename(tileset, extname(tileset)))}.json`,
|
||||||
|
JSON.stringify(json),
|
||||||
|
);
|
1
public/assets/tileset.json
Normal file
1
public/assets/tileset.json
Normal file
File diff suppressed because one or more lines are too long
BIN
public/assets/tileset.png
Normal file
BIN
public/assets/tileset.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 195 KiB |
|
@ -35,6 +35,7 @@ export default defineConfig({
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
server: {
|
server: {
|
||||||
|
host: true,
|
||||||
https: {
|
https: {
|
||||||
key: readFileSync(`${cacheDirectory}/localhost-key.pem`),
|
key: readFileSync(`${cacheDirectory}/localhost-key.pem`),
|
||||||
cert: readFileSync(`${cacheDirectory}/localhost.pem`),
|
cert: readFileSync(`${cacheDirectory}/localhost.pem`),
|
||||||
|
|
Loading…
Reference in New Issue
Block a user