improve position fixing
This commit is contained in:
Generated
+7
@@ -9,6 +9,7 @@
|
||||
"version": "1.0.0",
|
||||
"license": "UNLICENSED",
|
||||
"dependencies": {
|
||||
"quickhull": "^1.0.3",
|
||||
"sat": "^0.9.0",
|
||||
"three": "^0.171.0",
|
||||
"websocket-express": "^3.1.2"
|
||||
@@ -745,6 +746,12 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/quickhull": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/quickhull/-/quickhull-1.0.3.tgz",
|
||||
"integrity": "sha512-AQbLaXdzGDJdO9Mu3qY/NY5JWlDqIutCLW8vJbsQTq+/bydIZeltnMVRKCElp81Y5/uRm4Yw/RsMdcltFYsS6w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/range-parser": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
"license": "UNLICENSED",
|
||||
"description": "",
|
||||
"dependencies": {
|
||||
"quickhull": "^1.0.3",
|
||||
"sat": "^0.9.0",
|
||||
"three": "^0.171.0",
|
||||
"websocket-express": "^3.1.2"
|
||||
|
||||
+46
-1
@@ -43,6 +43,7 @@ global.renderer = renderer
|
||||
global.camera = camera
|
||||
global.scene = scene
|
||||
|
||||
|
||||
const keysDown = {}
|
||||
|
||||
function render() {
|
||||
@@ -54,8 +55,52 @@ function minimapRender() {
|
||||
minimapRenderer.render(scene, minimapCamera)
|
||||
}
|
||||
|
||||
const cameraSpeed = 0.1
|
||||
var cameraLocked = true
|
||||
function followCamera() {
|
||||
// entity = entities.filter((e) => e.id = '1').at(0)
|
||||
const entity = entities['1']
|
||||
if (entity == null) { return }
|
||||
|
||||
const offsetX = 0
|
||||
const offsetY = -15
|
||||
|
||||
const distanceX = Math.abs((entity.position.x + offsetX) - camera.position.x)
|
||||
const distanceY = Math.abs((entity.position.y + offsetY) - camera.position.y)
|
||||
|
||||
if (distanceX > 0.01) {
|
||||
if (entity.position.x + offsetX > camera.position.x) {
|
||||
camera.position.x += cameraSpeed * distanceX
|
||||
}
|
||||
if (entity.position.x + offsetX < camera.position.x) {
|
||||
camera.position.x -= cameraSpeed * distanceX
|
||||
}
|
||||
}
|
||||
else if (distanceX != 0) {
|
||||
camera.position.x = entity.position.x + offsetX
|
||||
}
|
||||
|
||||
if (distanceY > 0.01) {
|
||||
if (entity.position.y + offsetY > camera.position.y) {
|
||||
camera.position.y += cameraSpeed * distanceY
|
||||
}
|
||||
if (entity.position.y + offsetY < camera.position.y) {
|
||||
camera.position.y -= cameraSpeed * distanceY
|
||||
}
|
||||
}
|
||||
else if (distanceY != 0) {
|
||||
camera.position.y = entity.position.y + offsetY
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
const cameraSpeed = 0.03
|
||||
function cameraMovement() {
|
||||
if (cameraLocked) {
|
||||
followCamera()
|
||||
return
|
||||
}
|
||||
|
||||
if (keysDown.ArrowLeft) { camera.position.x -= cameraSpeed }
|
||||
else if (keysDown.ArrowRight) { camera.position.x += cameraSpeed }
|
||||
|
||||
|
||||
+11
-15
@@ -64,23 +64,20 @@ export default class Entity {
|
||||
return entityColliders.concat(terrainColliders)
|
||||
}
|
||||
|
||||
// DEPRECATED: hulls were a failed concept for position fixing
|
||||
collidableHulls() {
|
||||
const entityColliders = (this.game?.entities ?? []).filter((e) => e.id != this.id).map((e) => e.collider)
|
||||
const terrainColliders = (this.game?.terrains ?? []).map((t) => t.hull)
|
||||
|
||||
return entityColliders.concat(terrainColliders)
|
||||
}
|
||||
|
||||
isColliding(...colliders) {
|
||||
return SATX.collideObjects(this.collider, colliders)
|
||||
}
|
||||
|
||||
// TODO: calculate convex hulls of collidables because tris handle collisions sequentially
|
||||
moveAction(x, y) {
|
||||
const temp = SATX.fixCollisions(new Vector2(x, y), this.collidables(), this.radius)
|
||||
// const entity = new Entity()
|
||||
// entity.teleport(temp.x, temp.y)
|
||||
// entity.radius = this.radius
|
||||
// this.game.spawn_entity(entity)
|
||||
this.#dest = SATX.clamp(
|
||||
temp,
|
||||
this.game?.width,
|
||||
this.game?.height,
|
||||
this.radius,
|
||||
)
|
||||
this.#dest = SATX.fixCollisions(new Vector2(x, y), this.collidables(), this.radius, this.game?.width, this.game?.height)
|
||||
}
|
||||
|
||||
state() {
|
||||
@@ -97,7 +94,6 @@ export default class Entity {
|
||||
this.position.set(x, y)
|
||||
}
|
||||
|
||||
// TODO: pathfinding stops if wall is clicked (did you forget to fix the destination?)
|
||||
takeStep(distanceTraveled = 0) {
|
||||
const speed = (this.speed / (this.game?.tickBudget ?? 1000)) - distanceTraveled
|
||||
const collidables = this.collidables()
|
||||
@@ -105,14 +101,14 @@ export default class Entity {
|
||||
const fixedDest = SATX.clamp(SATX.fixCollisions(this.#dest, collidables, this.radius), this.game?.width, this.game?.height, this.radius)
|
||||
|
||||
if (this.#path.length < 1 || !this.#path.at(-1).equals(fixedDest)) {
|
||||
console.time('pathfinding')
|
||||
// console.time('pathfinding')
|
||||
const start = SATX.vectorToFloat32Array(this.position)
|
||||
const goal = SATX.vectorToFloat32Array(fixedDest)
|
||||
const nonUniqueWaypoints = this.waypoints().map((w) => SATX.vectorToFloat32Array(w)).concat([start, goal])
|
||||
const waypoints = Pathfind.uniqueWaypoints(nonUniqueWaypoints)
|
||||
const graph = Pathfind.buildGraph(waypoints, collidables, this.radius)
|
||||
this.#path = Pathfind.shortestPath(graph, start, goal).map((waypoint) => new Vector2(waypoint[0], waypoint[1]))
|
||||
console.timeEnd('pathfinding')
|
||||
// console.timeEnd('pathfinding')
|
||||
}
|
||||
|
||||
if (this.#path.length > 0) {
|
||||
|
||||
@@ -34,6 +34,10 @@ export default class Game {
|
||||
this.#terrains.push(terrain)
|
||||
}
|
||||
|
||||
remove_terrain(terrain) {
|
||||
this.#terrains = this.#terrains.filter((t) => t.id != terrain.id)
|
||||
}
|
||||
|
||||
state() {
|
||||
return {
|
||||
...this,
|
||||
|
||||
+10
-7
@@ -25,6 +25,10 @@ app.ws('/ws', async (req, res) => {
|
||||
websocket.on('message', (rawData) => {
|
||||
const message = JSON.parse(rawData)
|
||||
const entity = message.id != null ? game.entities.find((e) => e.id == message.id) : null
|
||||
if (entity == null) {
|
||||
console.log({ error: { reason: 'Invalid ID', message } })
|
||||
return
|
||||
}
|
||||
console.log(message)
|
||||
|
||||
if (message.action == 'teleport') {
|
||||
@@ -46,11 +50,11 @@ app.listen(port, () => {
|
||||
entity1.radius = 50
|
||||
game.spawn_entity(entity1)
|
||||
|
||||
const entity2 = new Entity()
|
||||
entity2.id = '2'
|
||||
entity2.teleport(110, 110)
|
||||
entity2.radius = 50
|
||||
game.spawn_entity(entity2)
|
||||
// const entity2 = new Entity()
|
||||
// entity2.id = '2'
|
||||
// entity2.teleport(110, 110)
|
||||
// entity2.radius = 50
|
||||
// game.spawn_entity(entity2)
|
||||
|
||||
// const triangle = new Terrain([
|
||||
// { x: 400, y: 200 },
|
||||
@@ -115,8 +119,7 @@ app.listen(port, () => {
|
||||
pole.id = 'pole'
|
||||
game.add_terrain(pole)
|
||||
|
||||
// entity1.moveAction(1000, 500)
|
||||
|
||||
// entity1.moveAction(750, 950)
|
||||
// setTimeout(() => entity1.moveAction(100, 400), 10)
|
||||
|
||||
game.start()
|
||||
|
||||
+25
-11
@@ -69,18 +69,32 @@ export default class SATX {
|
||||
])
|
||||
}
|
||||
|
||||
static fixCollisions(entityPosition, colliders, radius = 0) {
|
||||
const position = entityPosition.clone()
|
||||
let collider = Entity.collider(position.x, position.y, radius)
|
||||
colliders.forEach((c) => {
|
||||
let result = new SAT.Response()
|
||||
if (this.collideObject(collider, c, result)) {
|
||||
position.sub(new Vector2(result.overlapV.x, result.overlapV.y))
|
||||
collider = Entity.collider(position.x, position.y, radius)
|
||||
}
|
||||
})
|
||||
static fixCollisions(entityPosition, colliders, radius = 0, maxX = Infinity, maxY = Infinity) {
|
||||
if (!this.collideObjects(Entity.collider(entityPosition.x, entityPosition.y, radius), colliders)) {
|
||||
return entityPosition
|
||||
}
|
||||
// console.time('fixCollisions')
|
||||
|
||||
return position
|
||||
let direction = new Vector2(0, 5)
|
||||
let multiplier = 1
|
||||
const rotationSlices = 16
|
||||
|
||||
for (let limit = 1; limit <= 10000; limit++) {
|
||||
const rads = (limit % rotationSlices) * 2 * Math.PI / rotationSlices
|
||||
const offset = direction.clone().rotateAround(new Vector2(), rads).multiplyScalar(multiplier)
|
||||
const position = SATX.clamp(entityPosition.clone().add(offset), maxX, maxY, radius)
|
||||
if (!this.collideObjects(Entity.collider(position.x, position.y, radius), colliders)) {
|
||||
// console.timeEnd('fixCollisions')
|
||||
return position
|
||||
}
|
||||
|
||||
if (limit % rotationSlices == 0) {
|
||||
multiplier++
|
||||
}
|
||||
}
|
||||
|
||||
// console.timeEnd('fixCollisions')
|
||||
console.error('ERROR: can\'t fix collision')
|
||||
}
|
||||
|
||||
static line(fromX, fromY, toX, toY) {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import QuickHull from 'quickhull' // DEPRECATED: hulls were a failed concept for position fixing
|
||||
import SAT from 'sat'
|
||||
import { Shape, ShapeUtils, Vector2 } from 'three'
|
||||
|
||||
@@ -8,6 +9,7 @@ export default class Terrain {
|
||||
relativeVertices = []
|
||||
|
||||
#colliders = []
|
||||
#hull = null
|
||||
#vertices = []
|
||||
#unadjustedWaypoints = []
|
||||
|
||||
@@ -24,6 +26,7 @@ export default class Terrain {
|
||||
}
|
||||
|
||||
get colliders() { return this.#colliders }
|
||||
get hull() { return this.#hull } // DEPRECATED: hulls were a failed concept for position fixing
|
||||
get unadjustedWaypoints() { return this.#unadjustedWaypoints }
|
||||
get vertices() { return this.#vertices }
|
||||
|
||||
@@ -73,6 +76,16 @@ export default class Terrain {
|
||||
}
|
||||
|
||||
this.#colliders = ShapeUtils.triangulateShape(points.shape, points.holes).map(indicesToPolygon)
|
||||
this.#calculateHull()
|
||||
}
|
||||
|
||||
// DEPRECATED: hulls were a failed concept for position fixing
|
||||
#calculateHull() {
|
||||
const vertices = QuickHull(this.#vertices.map((v) => ({ x: v.x, y: v.y }))).map((v) => new Vector2(v.x, v.y))
|
||||
const first = vertices.at(0)
|
||||
const satPoints = [new SAT.Vector(...first.toArray()), ...vertices.slice(1).map((v) => new SAT.Vector(...v.clone(first).sub(first).toArray()))]
|
||||
|
||||
this.#hull = new SAT.Polygon(satPoints[0], [new SAT.Vector(), ...satPoints.slice(1)])
|
||||
}
|
||||
|
||||
#calculatePosition() {
|
||||
|
||||
Reference in New Issue
Block a user