add client side terrain without collision
This commit is contained in:
@@ -128,3 +128,5 @@ dist
|
|||||||
.yarn/build-state.yml
|
.yarn/build-state.yml
|
||||||
.yarn/install-state.gz
|
.yarn/install-state.gz
|
||||||
.pnp.*
|
.pnp.*
|
||||||
|
|
||||||
|
public/temp
|
||||||
|
|||||||
+4
-4
@@ -1,4 +1,4 @@
|
|||||||
import * as THREE from 'three'
|
import { Vector2 } from 'three'
|
||||||
import SAT from 'sat'
|
import SAT from 'sat'
|
||||||
import SATX from './satx.js'
|
import SATX from './satx.js'
|
||||||
|
|
||||||
@@ -7,7 +7,7 @@ export default class Entity {
|
|||||||
speed = 400
|
speed = 400
|
||||||
radius = 0
|
radius = 0
|
||||||
|
|
||||||
#position = new THREE.Vector2(0, 0)
|
#position = new Vector2()
|
||||||
#dest = null
|
#dest = null
|
||||||
#game = null
|
#game = null
|
||||||
|
|
||||||
@@ -40,7 +40,7 @@ export default class Entity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
moveAction(x, y) {
|
moveAction(x, y) {
|
||||||
this.#dest = new THREE.Vector3(x, y, 0)
|
this.#dest = new Vector2(x, y)
|
||||||
}
|
}
|
||||||
|
|
||||||
state() {
|
state() {
|
||||||
@@ -60,7 +60,7 @@ export default class Entity {
|
|||||||
takeStep() {
|
takeStep() {
|
||||||
const speed = this.speed / (this.game?.tickBudget ?? 1000)
|
const speed = this.speed / (this.game?.tickBudget ?? 1000)
|
||||||
if (this.#dest != null) {
|
if (this.#dest != null) {
|
||||||
const fixedDest = new THREE.Vector2(
|
const fixedDest = new Vector2(
|
||||||
Math.min(Math.max(this.radius, this.#dest.x), (this.game?.width ?? Infinity) - this.radius),
|
Math.min(Math.max(this.radius, this.#dest.x), (this.game?.width ?? Infinity) - this.radius),
|
||||||
Math.min(Math.max(this.radius, this.#dest.y), (this.game?.height ?? Infinity) - this.radius),
|
Math.min(Math.max(this.radius, this.#dest.y), (this.game?.height ?? Infinity) - this.radius),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -8,10 +8,12 @@ export default class Game {
|
|||||||
|
|
||||||
#entities = []
|
#entities = []
|
||||||
#eventEmitter = new EventEmitter()
|
#eventEmitter = new EventEmitter()
|
||||||
|
#terrains = []
|
||||||
#tickBudget = Math.floor(1000 / this.tickRate)
|
#tickBudget = Math.floor(1000 / this.tickRate)
|
||||||
|
|
||||||
get entities() { return this.#entities }
|
get entities() { return this.#entities }
|
||||||
get eventEmitter() { return this.#eventEmitter }
|
get eventEmitter() { return this.#eventEmitter }
|
||||||
|
get terrains() { return this.#terrains }
|
||||||
get tickBudget() { return this.#tickBudget }
|
get tickBudget() { return this.#tickBudget }
|
||||||
|
|
||||||
spawn_entity(entity) {
|
spawn_entity(entity) {
|
||||||
@@ -19,10 +21,15 @@ export default class Game {
|
|||||||
entity.game = this
|
entity.game = this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
add_terrain(terrain) {
|
||||||
|
this.#terrains.push(terrain)
|
||||||
|
}
|
||||||
|
|
||||||
state() {
|
state() {
|
||||||
return {
|
return {
|
||||||
...this,
|
...this,
|
||||||
entities: this.#entities.map((e) => e.state()),
|
entities: this.#entities.map((e) => e.state()),
|
||||||
|
terrains: this.#terrains.map((t) => t.state()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+21
-5
@@ -2,6 +2,7 @@ import express from 'express'
|
|||||||
import { WebSocketExpress } from 'websocket-express'
|
import { WebSocketExpress } from 'websocket-express'
|
||||||
import Game from './game.js'
|
import Game from './game.js'
|
||||||
import Entity from './entity.js'
|
import Entity from './entity.js'
|
||||||
|
import Terrain from './terrain.js'
|
||||||
|
|
||||||
const app = new WebSocketExpress()
|
const app = new WebSocketExpress()
|
||||||
const port = 1280
|
const port = 1280
|
||||||
@@ -39,11 +40,11 @@ app.ws('/ws', async (req, res) => {
|
|||||||
app.listen(port, () => {
|
app.listen(port, () => {
|
||||||
console.log(`Server started! Visit http://localhost:${port}`)
|
console.log(`Server started! Visit http://localhost:${port}`)
|
||||||
|
|
||||||
const entity = new Entity()
|
const entity1 = new Entity()
|
||||||
entity.id = '1'
|
entity1.id = '1'
|
||||||
entity.teleport(100, 100)
|
entity1.teleport(100, 100)
|
||||||
entity.radius = 35
|
entity1.radius = 35
|
||||||
game.spawn_entity(entity)
|
game.spawn_entity(entity1)
|
||||||
|
|
||||||
const entity2 = new Entity()
|
const entity2 = new Entity()
|
||||||
entity2.id = '2'
|
entity2.id = '2'
|
||||||
@@ -51,5 +52,20 @@ app.listen(port, () => {
|
|||||||
entity2.radius = 35
|
entity2.radius = 35
|
||||||
game.spawn_entity(entity2)
|
game.spawn_entity(entity2)
|
||||||
|
|
||||||
|
const vertices = [
|
||||||
|
{ x: 0, y: 0 },
|
||||||
|
{ x: 20, y: 0 },
|
||||||
|
{ x: 20, y: 20 },
|
||||||
|
{ x: 10, y: 20 },
|
||||||
|
{ x: 10, y: 5 },
|
||||||
|
{ x: 5, y: 5 },
|
||||||
|
{ x: 5, y: 20 },
|
||||||
|
{ x: 0, y: 20 },
|
||||||
|
]
|
||||||
|
|
||||||
|
const terrain1 = new Terrain(vertices)
|
||||||
|
terrain1.id = 'a'
|
||||||
|
game.add_terrain(terrain1)
|
||||||
|
|
||||||
game.start()
|
game.start()
|
||||||
})
|
})
|
||||||
|
|||||||
+12
@@ -6,6 +6,18 @@ export default class SATX {
|
|||||||
return SAT.testCircleCircle(collider1, collider2)
|
return SAT.testCircleCircle(collider1, collider2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (collider1 instanceof SAT.Circle && collider2 instanceof SAT.Polygon) {
|
||||||
|
return SAT.testCirclePolygon(collider1, collider2)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (collider1 instanceof SAT.Polygon && collider2 instanceof SAT.Circle) {
|
||||||
|
return SAT.testPolygonCircle(collider1, collider2)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (collider1 instanceof SAT.Polygon && collider2 instanceof SAT.Polygon) {
|
||||||
|
return SAT.testPolygonPolygon(collider1, collider2)
|
||||||
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,61 @@
|
|||||||
|
import SAT from 'sat'
|
||||||
|
import { Shape, ShapeUtils, Vector2 } from 'three'
|
||||||
|
|
||||||
|
export default class Terrain {
|
||||||
|
id = crypto.randomUUID()
|
||||||
|
|
||||||
|
position = new Vector2()
|
||||||
|
relativeVertices = []
|
||||||
|
|
||||||
|
#colliders = []
|
||||||
|
#vertices = []
|
||||||
|
|
||||||
|
constructor(vertices) {
|
||||||
|
this.#vertices = vertices.map((v) => new Vector2(v.x, v.y))
|
||||||
|
this.#calculateColliders()
|
||||||
|
this.#calculatePosition()
|
||||||
|
this.#calculateRelativeVertices()
|
||||||
|
}
|
||||||
|
|
||||||
|
get colliders() { return this.#colliders }
|
||||||
|
get vertices() { return this.#vertices }
|
||||||
|
|
||||||
|
state() {
|
||||||
|
return {
|
||||||
|
...this,
|
||||||
|
position: {
|
||||||
|
x: this.x,
|
||||||
|
y: this.y,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#calculateColliders() {
|
||||||
|
const complexShape = new Shape()
|
||||||
|
|
||||||
|
complexShape.moveTo(this.#vertices.at(0).x, this.#vertices.at(0).y)
|
||||||
|
this.#vertices.slice(1).forEach((v) => complexShape.lineTo(v.x, v.y))
|
||||||
|
|
||||||
|
const points = complexShape.extractPoints(16)
|
||||||
|
|
||||||
|
const indicesToPolygon = (indices) => {
|
||||||
|
const satPoints = [
|
||||||
|
new SAT.Vector(...points.shape[indices[0]].toArray()),
|
||||||
|
new SAT.Vector(...points.shape[indices[1]].toArray()),
|
||||||
|
new SAT.Vector(...points.shape[indices[2]].toArray()),
|
||||||
|
]
|
||||||
|
|
||||||
|
return new SAT.Polygon(satPoints[0], [new SAT.Vector(), satPoints[1], satPoints[2]])
|
||||||
|
}
|
||||||
|
|
||||||
|
this.#colliders = ShapeUtils.triangulateShape(points.shape, points.holes).map(indicesToPolygon)
|
||||||
|
}
|
||||||
|
|
||||||
|
#calculatePosition() {
|
||||||
|
this.position = this.#vertices.reduce(((sum, v) => sum.add(v)), new Vector2()).divideScalar(this.#vertices.length)
|
||||||
|
}
|
||||||
|
|
||||||
|
#calculateRelativeVertices() {
|
||||||
|
this.relativeVertices = this.#vertices.map((v) => v.clone().sub(this.position))
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user