fix game loop timer
This commit is contained in:
+29
-3
@@ -249,6 +249,35 @@ function connectWebSocket() {
|
|||||||
terrain.position.set(t.position.x / 100, t.position.y / 100, 0)
|
terrain.position.set(t.position.x / 100, t.position.y / 100, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (playerId != null) {
|
||||||
|
const player = state.entities.find((e) => e.id == playerId)
|
||||||
|
if (player != null) {
|
||||||
|
for (let abilityIndex = 0; abilityIndex < 4; abilityIndex++) {
|
||||||
|
if (player.abilities[abilityIndex] != null) {
|
||||||
|
const ability = player.abilities[abilityIndex]
|
||||||
|
const lastCast = player.cooldowns[ability.id] ?? -99999
|
||||||
|
const cooldownDuration = (ability.cooldown * state.tickRate) ?? 0
|
||||||
|
const remainingCooldown = (lastCast + cooldownDuration) - state.currentTick
|
||||||
|
let cssPercentage = '100%'
|
||||||
|
let text = ''
|
||||||
|
if (remainingCooldown > 0) {
|
||||||
|
const cooldownPercentage = 1 - (remainingCooldown / cooldownDuration)
|
||||||
|
cssPercentage = `${Math.round(100 * cooldownPercentage)}%`
|
||||||
|
if (remainingCooldown / state.tickRate <= 5) {
|
||||||
|
text = `${(Math.round(10 * remainingCooldown / state.tickRate) / 10).toFixed(1)}`
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
text = `${Math.round(remainingCooldown / state.tickRate)}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById(`ability-${abilityIndex}-cooldown`).style.clipPath = `polygon(0 ${cssPercentage}, 100% ${cssPercentage}, 100% 100%, 0 100%)`
|
||||||
|
document.getElementById(`ability-${abilityIndex}-cooldown-text`).innerHTML = text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
document.getElementById('state').innerHTML = JSON.stringify(state, null, 2)
|
document.getElementById('state').innerHTML = JSON.stringify(state, null, 2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -291,9 +320,6 @@ window.addEventListener('load', () => {
|
|||||||
if (event.code == 'KeyE') {
|
if (event.code == 'KeyE') {
|
||||||
websocket.send(JSON.stringify({ action: 'cast', slot: 3, id: playerId, x, y }))
|
websocket.send(JSON.stringify({ action: 'cast', slot: 3, id: playerId, x, y }))
|
||||||
}
|
}
|
||||||
if (event.code == 'KeyR') {
|
|
||||||
websocket.send(JSON.stringify({ action: 'cast', slot: 4, id: playerId, x, y }))
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event.code == 'KeyD') {
|
if (event.code == 'KeyD') {
|
||||||
websocket.send(JSON.stringify({ action: 'teleport', id: playerId, x, y }))
|
websocket.send(JSON.stringify({ action: 'teleport', id: playerId, x, y }))
|
||||||
|
|||||||
+69
-1
@@ -56,6 +56,53 @@
|
|||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
border-right: none;
|
border-right: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.hud {
|
||||||
|
position: fixed;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 15px;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
display: flex;
|
||||||
|
inset: auto 0 0 0;
|
||||||
|
width: fit-content;
|
||||||
|
margin: auto;
|
||||||
|
border: 5px solid gray;
|
||||||
|
background-color: black;
|
||||||
|
border-top-left-radius: 10px;
|
||||||
|
border-top-right-radius: 10px;
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ability {
|
||||||
|
position: relative;
|
||||||
|
flex: 1 0 0;
|
||||||
|
border: 1px solid white;
|
||||||
|
width: 75px;
|
||||||
|
height: 75px;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cooldown {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 73px;
|
||||||
|
height: 73px;
|
||||||
|
background-color: grey;
|
||||||
|
opacity: 0.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cooldown-text {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 73px;
|
||||||
|
height: 73px;
|
||||||
|
line-height: 73px;
|
||||||
|
text-align: center;
|
||||||
|
color: white;
|
||||||
|
font-family: monospace;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@@ -63,7 +110,28 @@
|
|||||||
<p>Connection: <span id="connection"></span></p>
|
<p>Connection: <span id="connection"></span></p>
|
||||||
<pre id="state"></pre>
|
<pre id="state"></pre>
|
||||||
</div>
|
</div>
|
||||||
<!-- TODO: HUD -->
|
<div class="hud">
|
||||||
|
<div id="ability-0" class="ability">
|
||||||
|
A
|
||||||
|
<div id="ability-0-cooldown" class="cooldown"></div>
|
||||||
|
<div id="ability-0-cooldown-text" class="cooldown-text"></div>
|
||||||
|
</div>
|
||||||
|
<div id="ability-1" class="ability">
|
||||||
|
Q
|
||||||
|
<div id="ability-1-cooldown" class="cooldown"></div>
|
||||||
|
<div id="ability-1-cooldown-text" class="cooldown-text"></div>
|
||||||
|
</div>
|
||||||
|
<div id="ability-2" class="ability">
|
||||||
|
W
|
||||||
|
<div id="ability-2-cooldown" class="cooldown"></div>
|
||||||
|
<div id="ability-2-cooldown-text" class="cooldown-text"></div>
|
||||||
|
</div>
|
||||||
|
<div id="ability-3" class="ability">
|
||||||
|
E
|
||||||
|
<div id="ability-3-cooldown" class="cooldown"></div>
|
||||||
|
<div id="ability-3-cooldown-text" class="cooldown-text"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<!-- TODO: cast indicator -->
|
<!-- TODO: cast indicator -->
|
||||||
<script type="module" src="client.js"></script>
|
<script type="module" src="client.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
+6
-2
@@ -4,12 +4,16 @@ export default class Ability {
|
|||||||
id = crypto.randomUUID()
|
id = crypto.randomUUID()
|
||||||
name = 'Ability'
|
name = 'Ability'
|
||||||
cooldown = 0
|
cooldown = 0
|
||||||
effect = () => {}
|
|
||||||
castTime = 0
|
castTime = 0
|
||||||
|
|
||||||
|
#effect = () => {}
|
||||||
|
|
||||||
|
get effect() { return this.#effect }
|
||||||
|
set effect(value) { this.#effect = value }
|
||||||
|
|
||||||
constructor(options) {
|
constructor(options) {
|
||||||
if (options.name != null) { this.name = options.name }
|
if (options.name != null) { this.name = options.name }
|
||||||
if (options.effect != null) { this.effect = options.effect }
|
if (options.effect != null) { this.#effect = options.effect }
|
||||||
if (options.cooldown != null) { this.cooldown = options.cooldown }
|
if (options.cooldown != null) { this.cooldown = options.cooldown }
|
||||||
if (options.castTime != null) { this.castTime = options.castTime }
|
if (options.castTime != null) { this.castTime = options.castTime }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import SAT from 'sat'
|
|||||||
import SATX from './satx.js'
|
import SATX from './satx.js'
|
||||||
import Pathfind from './pathfind.js'
|
import Pathfind from './pathfind.js'
|
||||||
import Ability from './ability.js'
|
import Ability from './ability.js'
|
||||||
import Effect from './effect.js'
|
|
||||||
|
|
||||||
export default class Entity {
|
export default class Entity {
|
||||||
id = crypto.randomUUID()
|
id = crypto.randomUUID()
|
||||||
@@ -16,7 +15,6 @@ export default class Entity {
|
|||||||
Ability.straightShot(800, 7, 3000),
|
Ability.straightShot(800, 7, 3000),
|
||||||
() => {},
|
() => {},
|
||||||
() => {},
|
() => {},
|
||||||
() => {},
|
|
||||||
]
|
]
|
||||||
casting = null
|
casting = null
|
||||||
// TODO: teams
|
// TODO: teams
|
||||||
|
|||||||
+19
-4
@@ -8,12 +8,14 @@ export default class Game {
|
|||||||
currentTick = 0
|
currentTick = 0
|
||||||
width = 2000
|
width = 2000
|
||||||
height = 2000
|
height = 2000
|
||||||
|
running = false
|
||||||
|
nextTick = 0
|
||||||
|
|
||||||
#entities = []
|
#entities = []
|
||||||
#eventEmitter = new EventEmitter()
|
#eventEmitter = new EventEmitter()
|
||||||
#projectiles = []
|
#projectiles = []
|
||||||
#terrains = []
|
#terrains = []
|
||||||
#tickBudget = Math.floor(1000 / this.tickRate)
|
#tickBudget = 1000 / this.tickRate
|
||||||
|
|
||||||
get entities() { return this.#entities }
|
get entities() { return this.#entities }
|
||||||
get eventEmitter() { return this.#eventEmitter }
|
get eventEmitter() { return this.#eventEmitter }
|
||||||
@@ -80,10 +82,23 @@ export default class Game {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gameLoop() {
|
||||||
|
const tickBudget = this.#tickBudget
|
||||||
|
|
||||||
|
if (this.nextTick != null) {
|
||||||
|
const nextTick = this.nextTick
|
||||||
|
this.nextTick = null
|
||||||
|
|
||||||
|
let start = performance.now()
|
||||||
|
while (start < nextTick) { start = performance.now() }
|
||||||
|
|
||||||
|
this.update()
|
||||||
|
this.nextTick = start + tickBudget
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
start() {
|
start() {
|
||||||
const start = performance.now()
|
setInterval(() => this.gameLoop(), 1)
|
||||||
this.update()
|
|
||||||
setTimeout(() => this.start(), Math.max(0, this.#tickBudget - (performance.now() - start)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
update() {
|
update() {
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ 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'
|
import Terrain from './terrain.js'
|
||||||
import SATX from './satx.js'
|
|
||||||
import { Vector2 } from 'three'
|
import { Vector2 } from 'three'
|
||||||
|
|
||||||
const app = new WebSocketExpress()
|
const app = new WebSocketExpress()
|
||||||
|
|||||||
Reference in New Issue
Block a user