Buff Queen AKA "Ez egy fa?"
Because the first placeholder player model resembled a queen that's been to the gym a bit too much. Also, before she got her head and hands, she looked like a tree, legit.
This commit is contained in:
@@ -0,0 +1,4 @@
|
|||||||
|
.git
|
||||||
|
*Dockerfile*
|
||||||
|
*docker-compose*
|
||||||
|
node_modules
|
||||||
+130
@@ -0,0 +1,130 @@
|
|||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
.pnpm-debug.log*
|
||||||
|
|
||||||
|
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||||
|
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||||
|
|
||||||
|
# Runtime data
|
||||||
|
pids
|
||||||
|
*.pid
|
||||||
|
*.seed
|
||||||
|
*.pid.lock
|
||||||
|
|
||||||
|
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||||
|
lib-cov
|
||||||
|
|
||||||
|
# Coverage directory used by tools like istanbul
|
||||||
|
coverage
|
||||||
|
*.lcov
|
||||||
|
|
||||||
|
# nyc test coverage
|
||||||
|
.nyc_output
|
||||||
|
|
||||||
|
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||||
|
.grunt
|
||||||
|
|
||||||
|
# Bower dependency directory (https://bower.io/)
|
||||||
|
bower_components
|
||||||
|
|
||||||
|
# node-waf configuration
|
||||||
|
.lock-wscript
|
||||||
|
|
||||||
|
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||||
|
build/Release
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
|
node_modules/
|
||||||
|
jspm_packages/
|
||||||
|
|
||||||
|
# Snowpack dependency directory (https://snowpack.dev/)
|
||||||
|
web_modules/
|
||||||
|
|
||||||
|
# TypeScript cache
|
||||||
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
# Optional npm cache directory
|
||||||
|
.npm
|
||||||
|
|
||||||
|
# Optional eslint cache
|
||||||
|
.eslintcache
|
||||||
|
|
||||||
|
# Optional stylelint cache
|
||||||
|
.stylelintcache
|
||||||
|
|
||||||
|
# Microbundle cache
|
||||||
|
.rpt2_cache/
|
||||||
|
.rts2_cache_cjs/
|
||||||
|
.rts2_cache_es/
|
||||||
|
.rts2_cache_umd/
|
||||||
|
|
||||||
|
# Optional REPL history
|
||||||
|
.node_repl_history
|
||||||
|
|
||||||
|
# Output of 'npm pack'
|
||||||
|
*.tgz
|
||||||
|
|
||||||
|
# Yarn Integrity file
|
||||||
|
.yarn-integrity
|
||||||
|
|
||||||
|
# dotenv environment variable files
|
||||||
|
.env
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
.env.local
|
||||||
|
|
||||||
|
# parcel-bundler cache (https://parceljs.org/)
|
||||||
|
.cache
|
||||||
|
.parcel-cache
|
||||||
|
|
||||||
|
# Next.js build output
|
||||||
|
.next
|
||||||
|
out
|
||||||
|
|
||||||
|
# Nuxt.js build / generate output
|
||||||
|
.nuxt
|
||||||
|
dist
|
||||||
|
|
||||||
|
# Gatsby files
|
||||||
|
.cache/
|
||||||
|
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||||
|
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||||
|
# public
|
||||||
|
|
||||||
|
# vuepress build output
|
||||||
|
.vuepress/dist
|
||||||
|
|
||||||
|
# vuepress v2.x temp and cache directory
|
||||||
|
.temp
|
||||||
|
.cache
|
||||||
|
|
||||||
|
# Docusaurus cache and generated files
|
||||||
|
.docusaurus
|
||||||
|
|
||||||
|
# Serverless directories
|
||||||
|
.serverless/
|
||||||
|
|
||||||
|
# FuseBox cache
|
||||||
|
.fusebox/
|
||||||
|
|
||||||
|
# DynamoDB Local files
|
||||||
|
.dynamodb/
|
||||||
|
|
||||||
|
# TernJS port file
|
||||||
|
.tern-port
|
||||||
|
|
||||||
|
# Stores VSCode versions used for testing VSCode extensions
|
||||||
|
.vscode-test
|
||||||
|
|
||||||
|
# yarn v2
|
||||||
|
.yarn/cache
|
||||||
|
.yarn/unplugged
|
||||||
|
.yarn/build-state.yml
|
||||||
|
.yarn/install-state.gz
|
||||||
|
.pnp.*
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
FROM node:current-alpine
|
||||||
|
WORKDIR /app
|
||||||
|
COPY package.json package-lock.json ./
|
||||||
|
RUN npm install
|
||||||
|
COPY . .
|
||||||
|
CMD ["node", "js/index.js"]
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 95 B |
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
|
After Width: | Height: | Size: 95 B |
Generated
+1090
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"name": "instructions-clear",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"main": "src/index.js",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"author": "Thayol",
|
||||||
|
"license": "UNLICENSED",
|
||||||
|
"description": "",
|
||||||
|
"dependencies": {
|
||||||
|
"detect-collisions": "^9.24.0",
|
||||||
|
"three": "^0.171.0",
|
||||||
|
"victor": "^1.1.0",
|
||||||
|
"websocket-express": "^3.1.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="author" content="Thayol">
|
||||||
|
<script type="importmap">
|
||||||
|
{
|
||||||
|
"imports": {
|
||||||
|
"three": "/three/build/three.module.js",
|
||||||
|
"three/addons/": "/three/examples/jsm/"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
font-family: sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
html, body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.debug-panel {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0 0 auto auto;
|
||||||
|
border-bottom-left-radius: 10px;
|
||||||
|
padding: 10px 10px 20px 20px;
|
||||||
|
background-color: white;
|
||||||
|
border: 5px solid gray;
|
||||||
|
border-top: none;
|
||||||
|
border-right: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="debug-panel">
|
||||||
|
<p>Connection: <span id="connection"></span></p>
|
||||||
|
<pre id="state"></pre>
|
||||||
|
</div>
|
||||||
|
<script type="module" src="main.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
import * as THREE from 'three'
|
||||||
|
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'
|
||||||
|
|
||||||
|
const global = (0,eval)("this")
|
||||||
|
const scene = new THREE.Scene()
|
||||||
|
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)
|
||||||
|
const renderer = new THREE.WebGLRenderer()
|
||||||
|
const entities = {}
|
||||||
|
renderer.setSize(window.innerWidth, window.innerHeight)
|
||||||
|
renderer.setAnimationLoop(animate)
|
||||||
|
document.body.appendChild(renderer.domElement)
|
||||||
|
|
||||||
|
const geometry = new THREE.BoxGeometry(1000, 1000)
|
||||||
|
const material = new THREE.MeshBasicMaterial({ color: 0x115011 })
|
||||||
|
const ground = new THREE.Mesh(geometry, material)
|
||||||
|
scene.add(ground)
|
||||||
|
|
||||||
|
// const loader = new GLTFLoader();
|
||||||
|
// loader.load('player.gltf', (gltf) => scene.add(gltf.scene), undefined, (err) => console.log(err))
|
||||||
|
|
||||||
|
global.camera = camera
|
||||||
|
global.scene = scene
|
||||||
|
camera.position.set(0, -250, 500)
|
||||||
|
camera.lookAt(0, 0, 0)
|
||||||
|
|
||||||
|
function animate() {
|
||||||
|
renderer.render(scene, camera)
|
||||||
|
}
|
||||||
|
|
||||||
|
var websocket = null
|
||||||
|
var timerId = null
|
||||||
|
|
||||||
|
function connectWebSocket() {
|
||||||
|
websocket = new WebSocket('ws://127.0.0.1:1280/ws')
|
||||||
|
global.websocket = websocket
|
||||||
|
websocket.onerror = () => websocket.close()
|
||||||
|
websocket.onopen = () => {
|
||||||
|
document.getElementById('connection').innerHTML = 'open'
|
||||||
|
clearInterval(timerId)
|
||||||
|
}
|
||||||
|
websocket.onclose = () => {
|
||||||
|
websocket = null
|
||||||
|
document.getElementById('connection').innerHTML = 'closed'
|
||||||
|
timerId = setInterval(() => {
|
||||||
|
if (websocket == null) {
|
||||||
|
connectWebSocket()
|
||||||
|
}
|
||||||
|
}, 2000)
|
||||||
|
}
|
||||||
|
|
||||||
|
websocket.onmessage = (event) => {
|
||||||
|
let state = JSON.parse(event.data)
|
||||||
|
for (const e of state.entities) {
|
||||||
|
let entity
|
||||||
|
if (e.id in entities) {
|
||||||
|
entity = entities[e.id]
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
entity = new THREE.Mesh(new THREE.SphereGeometry(e.radius), new THREE.MeshBasicMaterial({ color: 0xffffff, wireframe: true }))
|
||||||
|
scene.add(entity)
|
||||||
|
entities[e.id] = entity
|
||||||
|
}
|
||||||
|
|
||||||
|
entity.position.set(e.pos.x, e.pos.y, e.radius)
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById('state').innerHTML = JSON.stringify(state, null, 2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('load', () => {
|
||||||
|
connectWebSocket()
|
||||||
|
})
|
||||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,50 @@
|
|||||||
|
import { Circle } from 'detect-collisions'
|
||||||
|
import Victor from 'victor'
|
||||||
|
|
||||||
|
export default class Entity {
|
||||||
|
id = crypto.randomUUID()
|
||||||
|
pos = new Victor(0, 0)
|
||||||
|
radius = 0
|
||||||
|
#collider = null
|
||||||
|
#dest = null
|
||||||
|
#game = null
|
||||||
|
|
||||||
|
constructor(...options) {
|
||||||
|
Object.entries(options).forEach((value, key) => this[key] = value)
|
||||||
|
this.#collider = new Circle(this.pos, this.radius)
|
||||||
|
}
|
||||||
|
|
||||||
|
get collider() { return this.#collider }
|
||||||
|
get game() { return this.#game }
|
||||||
|
get system() { return this.game?.system }
|
||||||
|
set game(value) { this.#game = value }
|
||||||
|
|
||||||
|
moveAction(x, y) {
|
||||||
|
this.#dest = new Victor(x, y)
|
||||||
|
}
|
||||||
|
|
||||||
|
async takeStep() {
|
||||||
|
if (this.#dest != null) {
|
||||||
|
this.pos.add(this.#dest.clone().subtract(this.pos).normalize())
|
||||||
|
if (this.pos.clone().subtract(this.#dest).length() <= 1) {
|
||||||
|
this.pos = this.#dest
|
||||||
|
this.#dest = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateCollider() {
|
||||||
|
if (this.pos.x != this.collider.pos.x || this.pos.y != this.collider.pos.y || this.radius != this.collider.unscaledRadius) {
|
||||||
|
this.system?.remove(this.#collider)
|
||||||
|
this.#collider = new Circle(this.pos, this.radius)
|
||||||
|
this.system?.insert(this.#collider)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async update() {
|
||||||
|
await Promise.allSettled([
|
||||||
|
this.updateCollider(),
|
||||||
|
this.takeStep(),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
+32
@@ -0,0 +1,32 @@
|
|||||||
|
import { System } from 'detect-collisions'
|
||||||
|
import { EventEmitter } from 'node:events'
|
||||||
|
|
||||||
|
export default class Game {
|
||||||
|
entities = []
|
||||||
|
tickRate = 30
|
||||||
|
currentTick = 0
|
||||||
|
|
||||||
|
#eventEmitter = new EventEmitter()
|
||||||
|
#system = new System()
|
||||||
|
#tickBudget = Math.floor(1000 / this.tickRate)
|
||||||
|
|
||||||
|
get eventEmitter() { return this.#eventEmitter }
|
||||||
|
get system() { return this.#system }
|
||||||
|
|
||||||
|
spawn_entity(entity) {
|
||||||
|
this.entities.push(entity)
|
||||||
|
this.#system.insert(entity.collider)
|
||||||
|
}
|
||||||
|
|
||||||
|
async start() {
|
||||||
|
const start = performance.now()
|
||||||
|
await this.update()
|
||||||
|
setTimeout(() => this.start(), Math.max(0, this.#tickBudget - (performance.now() - start)))
|
||||||
|
}
|
||||||
|
|
||||||
|
async update() {
|
||||||
|
Promise.allSettled(this.entities.map((e) => e.update()))
|
||||||
|
this.currentTick++
|
||||||
|
this.eventEmitter.emit('tick')
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
import express from 'express'
|
||||||
|
import { WebSocketExpress } from 'websocket-express'
|
||||||
|
import Game from './game.js'
|
||||||
|
import Entity from './entity.js'
|
||||||
|
import Victor from 'victor'
|
||||||
|
|
||||||
|
const app = new WebSocketExpress()
|
||||||
|
const port = 1280
|
||||||
|
const game = new Game()
|
||||||
|
|
||||||
|
app.use('/', express.static('public'))
|
||||||
|
app.use('/three/', express.static('node_modules/three'))
|
||||||
|
app.use(express.urlencoded({ extended: true }))
|
||||||
|
|
||||||
|
app.ws('/ws', async (req, res) => {
|
||||||
|
const websocket = await res.accept()
|
||||||
|
|
||||||
|
const subscription = () => websocket.send(JSON.stringify(game))
|
||||||
|
game.eventEmitter.on('tick', subscription)
|
||||||
|
|
||||||
|
websocket.on('close', () => {
|
||||||
|
game.eventEmitter.removeListener('tick', subscription)
|
||||||
|
})
|
||||||
|
|
||||||
|
websocket.on('message', (rawData) => {
|
||||||
|
const message = JSON.parse(rawData)
|
||||||
|
console.log(message)
|
||||||
|
|
||||||
|
if (message.action == 'teleport') {
|
||||||
|
const entity = game.entities.find((e) => e.id == message.id)
|
||||||
|
entity.pos = new Victor(message.x, message.y)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message.action == 'move') {
|
||||||
|
const entity = game.entities.find((e) => e.id == message.id)
|
||||||
|
entity.moveAction(message.x, message.y)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
app.listen(port, () => {
|
||||||
|
console.log(`Server started! Visit http://localhost:${port}`)
|
||||||
|
|
||||||
|
const entity = new Entity()
|
||||||
|
entity.id = '1'
|
||||||
|
entity.pos = new Victor(0, 0)
|
||||||
|
entity.radius = 35
|
||||||
|
game.spawn_entity(entity)
|
||||||
|
|
||||||
|
const entity2 = new Entity()
|
||||||
|
entity2.id = '2'
|
||||||
|
entity2.pos = new Victor(200, 100)
|
||||||
|
entity2.radius = 35
|
||||||
|
game.spawn_entity(entity2)
|
||||||
|
|
||||||
|
game.start()
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user