From 462dfe7b9a7bcb93db3344fe99c5bff667d8941c Mon Sep 17 00:00:00 2001 From: Thayol Date: Sat, 11 Jan 2025 19:38:40 +0900 Subject: [PATCH] add tweening --- package-lock.json | 7 +++++++ package.json | 1 + public/client.js | 41 ++++++++++++++++++++++++++--------------- public/index.html | 11 +++++++---- src/entity.js | 10 +++++++++- src/index.js | 11 ++++++----- 6 files changed, 56 insertions(+), 25 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8180253..85d9b35 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,12 +9,19 @@ "version": "1.0.0", "license": "UNLICENSED", "dependencies": { + "@tweenjs/tween.js": "^25.0.0", "quickhull": "^1.0.3", "sat": "^0.9.0", "three": "^0.171.0", "websocket-express": "^3.1.2" } }, + "node_modules/@tweenjs/tween.js": { + "version": "25.0.0", + "resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-25.0.0.tgz", + "integrity": "sha512-XKLA6syeBUaPzx4j3qwMqzzq+V4uo72BnlbOjmuljLrRqdsd3qnzvZZoxvMHZ23ndsRS4aufU6JOZYpCbU6T1A==", + "license": "MIT" + }, "node_modules/@types/body-parser": { "version": "1.19.5", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", diff --git a/package.json b/package.json index fdb664b..289fdb5 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "license": "UNLICENSED", "description": "", "dependencies": { + "@tweenjs/tween.js": "^25.0.0", "quickhull": "^1.0.3", "sat": "^0.9.0", "three": "^0.171.0", diff --git a/public/client.js b/public/client.js index c75d2a5..2fee4a5 100644 --- a/public/client.js +++ b/public/client.js @@ -1,6 +1,7 @@ import * as THREE from 'three' +import { Tween } from '@tweenjs/tween.js' -const global = (0,eval)("this") +const global = (0,eval)('this') const scene = new THREE.Scene() const raycaster = new THREE.Raycaster() const camera = new THREE.PerspectiveCamera(30, window.innerWidth / window.innerHeight, 0.1, 1000) @@ -13,16 +14,15 @@ 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(-10, 10, 10, -10) -const minimapCamera = new THREE.OrthographicCamera(-6, 6, 6, -6) +const minimapCamera = new THREE.OrthographicCamera(-10, 10, 10, -10) const minimapRenderer = new THREE.WebGLRenderer() -minimapRenderer.setSize(600, 600) +minimapRenderer.setSize(300, 300) minimapRenderer.setAnimationLoop(minimapRender) -// minimapCamera.position.set(10, 10, 10) -minimapCamera.position.set(6, 6, 6) +minimapCamera.position.set(10, 10, 10) const entities = {} +const positionTweens = {} const terrains = {} const geometry = new THREE.PlaneGeometry(0, 0) @@ -43,11 +43,12 @@ global.renderer = renderer global.camera = camera global.scene = scene - +const tweenDuration = 60 const keysDown = {} function render() { cameraMovement() + Object.values(positionTweens).forEach((tween) => tween.update()) renderer.render(scene, camera) } @@ -57,8 +58,7 @@ function minimapRender() { var cameraLocked = true function followCamera() { - // entity = entities.filter((e) => e.id = '1').at(0) - const entity = entities['1'] + const entity = entities[playerId] if (entity == null) { return } const offsetX = 0 @@ -90,8 +90,6 @@ function followCamera() { else if (distanceY != 0) { camera.position.y = entity.position.y + offsetY } - - } const cameraSpeed = 0.03 @@ -115,9 +113,10 @@ function cameraMovement() { var websocket = null global.websocket = null var timerId = null +var playerId = null function connectWebSocket() { - websocket = new WebSocket('ws://127.0.0.1:1280/ws') + websocket = new WebSocket(`ws://${window.location.hostname}:1280/ws`) global.websocket = websocket websocket.onerror = () => websocket.close() websocket.onopen = () => { @@ -156,7 +155,8 @@ function connectWebSocket() { entities[e.id] = entity } - 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) + positionTweens[entity.id] = new Tween(entity.position).to({ x: e.position.x / 100, y: e.position.y / 100, z: e.radius / 100 }, tweenDuration).start() } for (const t of state.terrains ?? []) { @@ -184,6 +184,12 @@ function connectWebSocket() { } window.addEventListener('load', () => { + const params = Object.fromEntries(new URLSearchParams(window.location.search).entries()) + playerId = params.id + if (playerId == null) { + playerId = prompt('Player ID:') + } + connectWebSocket() const canvas = renderer.domElement @@ -196,13 +202,13 @@ window.addEventListener('load', () => { if (event.button == 0) { const x = Math.round(intersect.x * 100) const y = Math.round(intersect.y * 100) - websocket.send(JSON.stringify({ action: 'move', id: '2', x, y })) + websocket.send(JSON.stringify({ action: 'teleport', id: playerId, x, y })) } if (event.button == 2) { const x = Math.round(intersect.x * 100) const y = Math.round(intersect.y * 100) - websocket.send(JSON.stringify({ action: 'move', id: '1', x, y })) + websocket.send(JSON.stringify({ action: 'move', id: playerId, x, y })) } } }) @@ -233,6 +239,11 @@ window.addEventListener('load', () => { document.addEventListener('contextmenu', (event) => event.preventDefault()) window.addEventListener('keydown', (event) => keysDown[event.code] = true) window.addEventListener('keyup', (event) => keysDown[event.code] = false) + window.addEventListener('keydown', (event) => { + if (event.code == 'Space') { + cameraLocked = !cameraLocked + } + }) document.body.appendChild(canvas) diff --git a/public/index.html b/public/index.html index b076159..3333640 100644 --- a/public/index.html +++ b/public/index.html @@ -6,7 +6,8 @@ { "imports": { "three": "/three/build/three.module.js", - "three/addons/": "/three/examples/jsm/" + "three/addons/": "/three/examples/jsm/", + "@tweenjs/tween.js": "/@tweenjs/tween.js/dist/tween.esm.js" } } @@ -26,17 +27,19 @@ } .debug-panel { - display: none; + /* display: none; */ + font-size: 0.8rem; position: fixed; opacity: 0.2; - inset: 0 0 auto auto; + overflow-y: scroll; + inset: 0 0 290px auto; border-bottom-left-radius: 10px; padding: 10px 10px 20px 20px; background-color: white; border: 5px solid gray; border-top: none; border-right: none; - width: 400px; + width: 300px; transition-duration: 0.2s; transition-property: opacity; } diff --git a/src/entity.js b/src/entity.js index 244101e..5a7eca5 100644 --- a/src/entity.js +++ b/src/entity.js @@ -72,6 +72,10 @@ export default class Entity { return entityColliders.concat(terrainColliders) } + fixPosition() { + this.#position = SATX.fixCollisions(this.#position, this.collidables(), this.radius, this.game?.width, this.game?.height) + } + isColliding(...colliders) { return SATX.collideObjects(this.collider, colliders) } @@ -91,9 +95,12 @@ export default class Entity { } teleport(x, y) { - this.position.set(x, y) + const position = SATX.fixCollisions(new Vector2(x, y), this.collidables(), this.radius, this.game?.width, this.game?.height) + this.position.set(position.x, position.y) } + // TODO: unset destination on teleports, etc. + // TODO: recalculate path on obstructions (currently next waypoint is used) takeStep(distanceTraveled = 0) { const speed = (this.speed / (this.game?.tickBudget ?? 1000)) - distanceTraveled const collidables = this.collidables() @@ -140,6 +147,7 @@ export default class Entity { update() { this.takeStep() + this.fixPosition() } waypoints() { diff --git a/src/index.js b/src/index.js index ac72423..6461863 100644 --- a/src/index.js +++ b/src/index.js @@ -10,6 +10,7 @@ const game = new Game() app.use('/', express.static('public')) app.use('/three/', express.static('node_modules/three')) +app.use('/@tweenjs/', express.static('node_modules/@tweenjs')) app.use(express.urlencoded({ extended: true })) app.ws('/ws', async (req, res) => { @@ -50,11 +51,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 },