add terrain collision

This commit is contained in:
2024-12-23 18:30:59 +09:00
parent ae6f4c2847
commit 37a77e902c
5 changed files with 51 additions and 24 deletions
+26 -2
View File
@@ -10,6 +10,9 @@ renderer.setAnimationLoop(render)
camera.position.set(5, -12, 10) camera.position.set(5, -12, 10)
camera.rotation.set((60 / 180) * Math.PI, 0, 0) camera.rotation.set((60 / 180) * Math.PI, 0, 0)
const entityMaterial = new THREE.MeshToonMaterial({ color: 0xffffff })
const terrainMaterial = new THREE.MeshToonMaterial({ color: 0xffd700 })
const minimapCamera = new THREE.OrthographicCamera(-5, 5, 5, -5) const minimapCamera = new THREE.OrthographicCamera(-5, 5, 5, -5)
const minimapRenderer = new THREE.WebGLRenderer() const minimapRenderer = new THREE.WebGLRenderer()
@@ -18,6 +21,7 @@ minimapRenderer.setAnimationLoop(minimapRender)
minimapCamera.position.set(5, 5, 10) minimapCamera.position.set(5, 5, 10)
const entities = {} const entities = {}
const terrains = {}
const geometry = new THREE.PlaneGeometry(0, 0) const geometry = new THREE.PlaneGeometry(0, 0)
const material = new THREE.MeshToonMaterial({ color: 0x115011 }) const material = new THREE.MeshToonMaterial({ color: 0x115011 })
@@ -75,13 +79,13 @@ function connectWebSocket() {
ground.position.set(state.width / 200, state.height / 200, 0) ground.position.set(state.width / 200, state.height / 200, 0)
} }
for (const e of state.entities) { for (const e of state.entities ?? []) {
let entity let entity
if (e.id in entities) { if (e.id in entities) {
entity = entities[e.id] entity = entities[e.id]
} }
else { else {
entity = new THREE.Mesh(new THREE.SphereGeometry(e.radius / 100), new THREE.MeshToonMaterial({ color: 0xffffff })) entity = new THREE.Mesh(new THREE.SphereGeometry(e.radius / 100), entityMaterial)
entity.userData.type = 'entity' entity.userData.type = 'entity'
entity.userData.id = e.id entity.userData.id = e.id
scene.add(entity) scene.add(entity)
@@ -91,6 +95,26 @@ function connectWebSocket() {
entity.position.set(e.position.x / 100, e.position.y / 100, e.radius / 100) entity.position.set(e.position.x / 100, e.position.y / 100, e.radius / 100)
} }
for (const t of state.terrains ?? []) {
let terrain
if (t.id in terrains) {
terrain = terrains[t.id]
}
else {
const vertices = t.relativeVertices
const shape = new THREE.Shape()
shape.moveTo(vertices.at(0).x / 100, vertices.at(0).y / 100)
vertices.slice(1).forEach((v) => shape.lineTo(v.x / 100, v.y / 100))
terrain = new THREE.Mesh(new THREE.ExtrudeGeometry(shape, { bevelEnabled: false, depth: 0.5 }), terrainMaterial)
terrain.userData.type = 'terrain'
terrain.userData.id = t.id
scene.add(terrain)
terrains[t.id] = terrain
}
terrain.position.set(t.position.x / 100, t.position.y / 100, 0)
}
document.getElementById('state').innerHTML = JSON.stringify(state, null, 2) document.getElementById('state').innerHTML = JSON.stringify(state, null, 2)
} }
} }
+11 -4
View File
@@ -28,15 +28,22 @@ export default class Entity {
set y(value) { this.position.y = value } set y(value) { this.position.y = value }
get collidables() { get collidables() {
return this.game?.entities.filter((e) => e.id != this.id).map((e) => e.collider) const entityColliders = (this.game?.entities ?? []).filter((e) => e.id != this.id).map((e) => e.collider)
const terrainColliders = (this.game?.terrains ?? []).map((t) => t.colliders).flat()
return entityColliders.concat(terrainColliders)
} }
get collider() { get collider() {
return new SAT.Circle(new SAT.Vector(this.x, this.y), this.radius) return new SAT.Circle(new SAT.Vector(this.x, this.y), this.radius)
} }
isColliding(collider) { get colliders() {
return SATX.colliding(this.collider, collider) return [this.collider]
}
isColliding(...colliders) {
return SATX.collideObjects(this.collider, colliders)
} }
moveAction(x, y) { moveAction(x, y) {
@@ -71,7 +78,7 @@ export default class Entity {
const position = distance <= speed ? fixedDest : stepTaken const position = distance <= speed ? fixedDest : stepTaken
const collider = Entity.collider(position.x, position.y, this.radius) const collider = Entity.collider(position.x, position.y, this.radius)
const isColliding = this.collidables.some((c) => SATX.colliding(collider, c)) const isColliding = SATX.collideObjects(collider, this.collidables)
if (!isColliding) { if (!isColliding) {
this.position.copy(position) this.position.copy(position)
+7 -11
View File
@@ -42,25 +42,21 @@ app.listen(port, () => {
const entity1 = new Entity() const entity1 = new Entity()
entity1.id = '1' entity1.id = '1'
entity1.teleport(100, 100) entity1.teleport(350, 500)
entity1.radius = 35 entity1.radius = 50
game.spawn_entity(entity1) game.spawn_entity(entity1)
const entity2 = new Entity() const entity2 = new Entity()
entity2.id = '2' entity2.id = '2'
entity2.teleport(200, 100) entity2.teleport(800, 100)
entity2.radius = 35 entity2.radius = 35
game.spawn_entity(entity2) game.spawn_entity(entity2)
const vertices = [ const vertices = [
{ x: 0, y: 0 }, { x: 400, y: 400 },
{ x: 20, y: 0 }, { x: 600, y: 400 },
{ x: 20, y: 20 }, { x: 600, y: 600 },
{ x: 10, y: 20 }, { x: 400, y: 600 },
{ x: 10, y: 5 },
{ x: 5, y: 5 },
{ x: 5, y: 20 },
{ x: 0, y: 20 },
] ]
const terrain1 = new Terrain(vertices) const terrain1 = new Terrain(vertices)
+5 -1
View File
@@ -1,7 +1,7 @@
import SAT from 'sat' import SAT from 'sat'
export default class SATX { export default class SATX {
static colliding(collider1, collider2) { static collideObject(collider1, collider2) {
if (collider1 instanceof SAT.Circle && collider2 instanceof SAT.Circle) { if (collider1 instanceof SAT.Circle && collider2 instanceof SAT.Circle) {
return SAT.testCircleCircle(collider1, collider2) return SAT.testCircleCircle(collider1, collider2)
} }
@@ -20,4 +20,8 @@ export default class SATX {
return false return false
} }
static collideObjects(collider1, colliders) {
return colliders.some((c) => this.collideObject(collider1, c))
}
} }
+2 -6
View File
@@ -23,10 +23,6 @@ export default class Terrain {
state() { state() {
return { return {
...this, ...this,
position: {
x: this.x,
y: this.y,
},
} }
} }
@@ -41,8 +37,8 @@ export default class Terrain {
const indicesToPolygon = (indices) => { const indicesToPolygon = (indices) => {
const satPoints = [ const satPoints = [
new SAT.Vector(...points.shape[indices[0]].toArray()), new SAT.Vector(...points.shape[indices[0]].toArray()),
new SAT.Vector(...points.shape[indices[1]].toArray()), new SAT.Vector(...points.shape[indices[1]].clone().sub(points.shape[indices[0]]).toArray()),
new SAT.Vector(...points.shape[indices[2]].toArray()), new SAT.Vector(...points.shape[indices[2]].clone().sub(points.shape[indices[0]]).toArray()),
] ]
return new SAT.Polygon(satPoints[0], [new SAT.Vector(), satPoints[1], satPoints[2]]) return new SAT.Polygon(satPoints[0], [new SAT.Vector(), satPoints[1], satPoints[2]])