framerate independent movement
This commit is contained in:
parent
e146052f33
commit
7711c112e2
3 changed files with 71 additions and 29 deletions
76
src/App.vue
76
src/App.vue
|
@ -1,6 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { BLOCK_SIZE, STAGE_WIDTH, STAGE_HEIGHT } from './level/def'
|
||||
import { BLOCK_SIZE, STAGE_WIDTH, STAGE_HEIGHT, type Block } from './level/def'
|
||||
import createLevel from './level'
|
||||
|
||||
import useTime from './util/useTime'
|
||||
|
@ -9,7 +9,7 @@ import usePlayer from './util/usePlayer'
|
|||
|
||||
const { updateTime, timeOfDay, clock } = useTime()
|
||||
const { player, direction, dx, dy } = usePlayer()
|
||||
const { inputX, inputY, digging, paused } = useInput(player)
|
||||
const { inputX, inputY, running, digging, paused } = useInput()
|
||||
const level = createLevel(STAGE_WIDTH + 2, STAGE_HEIGHT + 2)
|
||||
|
||||
let animationFrame = 0
|
||||
|
@ -23,24 +23,56 @@ const tx = computed(() => (x.value - floorX.value) * -BLOCK_SIZE)
|
|||
const ty = computed(() => (y.value - floorY.value) * -BLOCK_SIZE)
|
||||
const rows = computed(() => level.grid(floorX.value, floorY.value))
|
||||
|
||||
// TODO: mock
|
||||
const blocked = {
|
||||
left: false,
|
||||
right: false,
|
||||
up: false,
|
||||
down: false,
|
||||
type Surroundings = {
|
||||
at: Block,
|
||||
left: Block,
|
||||
right: Block,
|
||||
up: Block,
|
||||
down: Block,
|
||||
}
|
||||
const surroundings = computed<Surroundings>(() => {
|
||||
const px = player.x
|
||||
const py = player.y
|
||||
const row = rows.value
|
||||
|
||||
return {
|
||||
at: row[py][px],
|
||||
left: row[py][px - 1],
|
||||
right: row[py][px + 1],
|
||||
up: row[py - 1][px],
|
||||
down: row[py + 1][px],
|
||||
}
|
||||
})
|
||||
const blocked = computed(() => {
|
||||
const { left, right, up, down } = surroundings.value
|
||||
return {
|
||||
left: !left.walkable,
|
||||
right: !right.walkable,
|
||||
up: !up.walkable,
|
||||
down: !down.walkable,
|
||||
}
|
||||
})
|
||||
|
||||
function dig() {
|
||||
console.warn('digging not yet implemented')
|
||||
}
|
||||
|
||||
function move(thisTick) {
|
||||
let lastTimeUpdate = 0
|
||||
|
||||
const move = (thisTick: number): void => {
|
||||
animationFrame = requestAnimationFrame(move)
|
||||
|
||||
// do nothing when paused, otherwise keep roughly 20 fps
|
||||
if (paused.value || thisTick - lastTick < 50) return
|
||||
updateTime()
|
||||
// do nothing when paused
|
||||
if (paused.value) return
|
||||
|
||||
const tickDelta = thisTick - lastTick
|
||||
lastTimeUpdate += tickDelta
|
||||
// update in-game time every 60ms by 0.1
|
||||
// then a day needs 10000 updates, and it takes about 10 minutes
|
||||
if (lastTimeUpdate > 60) {
|
||||
updateTime()
|
||||
lastTimeUpdate = 0
|
||||
}
|
||||
|
||||
player.vx = inputX.value
|
||||
player.vy = inputY.value
|
||||
|
@ -50,19 +82,24 @@ function move(thisTick) {
|
|||
let dx_ = dx.value
|
||||
let dy_ = dy.value
|
||||
|
||||
if (dx > 0 && blocked.right) dx_ = 0
|
||||
else if (dx < 0 && blocked.left) dx_ = 0
|
||||
if (running.value) dx_ *= 2
|
||||
|
||||
if (dy > 0 && blocked.down) dy_ = 0
|
||||
else if (dy < 0 && blocked.up) dy_ = 0
|
||||
if (dx_ > 0 && blocked.value.right) dx_ = 0
|
||||
else if (dx_ < 0 && blocked.value.left) dx_ = 0
|
||||
|
||||
if (dy_ > 0 && blocked.value.down) dy_ = 0
|
||||
else if (dy_ < 0 && blocked.value.up) dy_ = 0
|
||||
|
||||
if (!inputY.value && digging.value) {
|
||||
dx_ = 0
|
||||
dig()
|
||||
}
|
||||
|
||||
x.value += dx_ * 32
|
||||
y.value += dy_ * 32
|
||||
const optimal = 16 // 16ms per tick => 60 FPS
|
||||
const movementMultiplier = (tickDelta / optimal) * 2
|
||||
|
||||
x.value += dx_ * movementMultiplier
|
||||
y.value += dy_ * movementMultiplier
|
||||
lastTick = thisTick
|
||||
}
|
||||
|
||||
|
@ -86,8 +123,7 @@ onMounted(() => {
|
|||
x:{{ floorX }}, y:{{ floorY }}
|
||||
<template v-if="paused">(PAUSED)</template>
|
||||
<template v-else>({{ clock }})</template>
|
||||
<div>{{ inputX }}, {{ inputY }}, {{ player.lastDir }}</div>
|
||||
<div>{{ dx }}, {{ dy }}, {{ direction }}</div>
|
||||
<div>{{ player.vx }}, {{ player.vy }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -2,12 +2,16 @@ import { ref } from 'vue'
|
|||
|
||||
export default function useInput() {
|
||||
let inputX = ref(0)
|
||||
let inputY = ref(0)
|
||||
let inputY = ref(1)
|
||||
let running = ref(false)
|
||||
let digging = ref(false)
|
||||
let paused = ref(false)
|
||||
|
||||
function handleKeyDown(event: KeyboardEvent) {
|
||||
switch (event.key) {
|
||||
case 'Shift':
|
||||
running.value = true
|
||||
break
|
||||
case 'ArrowUp':
|
||||
inputY.value = -1
|
||||
break
|
||||
|
@ -31,12 +35,13 @@ export default function useInput() {
|
|||
|
||||
function handleKeyUp(event: KeyboardEvent) {
|
||||
switch (event.key) {
|
||||
case 'Shift':
|
||||
running.value = false
|
||||
break
|
||||
// Arrow Keys
|
||||
case 'ArrowUp':
|
||||
inputY.value = inputY.value === -1 ? 0 : 1
|
||||
break
|
||||
case 'ArrowDown':
|
||||
inputY.value = inputY.value === 1 ? 0 : 1
|
||||
inputY.value = 1
|
||||
break
|
||||
case 'ArrowRight':
|
||||
inputX.value = inputX.value === 1 ? 0 : -1
|
||||
|
@ -59,6 +64,7 @@ export default function useInput() {
|
|||
return {
|
||||
inputX,
|
||||
inputY,
|
||||
running,
|
||||
digging,
|
||||
paused,
|
||||
}
|
||||
|
|
|
@ -2,19 +2,19 @@ import { computed, reactive } from 'vue'
|
|||
import { RECIPROCAL } from '../level/def'
|
||||
|
||||
export interface Moveable {
|
||||
x: number, // position on x-axis (always 0 for the player)
|
||||
y: number, // position on y-axis (always 0 for the player)
|
||||
x: number, // position on x-axis (fixed for the player)
|
||||
y: number, // position on y-axis (fixed for the player)
|
||||
lastDir: number, // store last face direction
|
||||
vx: number, // velocity on the x-axis
|
||||
vy: number, // velocity on the y-axis
|
||||
}
|
||||
|
||||
const player = reactive({
|
||||
x: 0,
|
||||
y: 0,
|
||||
x: 16,
|
||||
y: 10,
|
||||
lastDir: 0,
|
||||
vx: 0,
|
||||
vy: 0,
|
||||
vy: 1, // always falling, because of gravity
|
||||
})
|
||||
|
||||
export default function usePlayer() {
|
||||
|
|
Loading…
Add table
Reference in a new issue