M src/App.vue => src/App.vue +3 -2
@@ 19,7 19,9 @@ export default {
<style>
html,body,#app {
- display: block;
+ display: flex;
+ flex-flow: column nowrap;
+ justify-content: center;
width: 100vw;
height: 100vh;
background: black;
@@ 27,5 29,4 @@ html,body,#app {
padding: 0;
overflow: hidden;
}
-
</style>
M src/Background.vue => src/Background.vue +9 -10
@@ 4,6 4,7 @@
<script>
import solarQuartet from './solar-quartet'
+import { BLOCK_SIZE, STAGE_WIDTH, STAGE_HEIGHT } from './level/def'
export default {
name: 'background',
@@ 21,18 22,16 @@ export default {
time () { this.refresh() }
},
mounted () {
- const canvasSize = 512
- const godraysSize = 128
const canvas = this.$refs.canvas
const godraysCanvas = document.createElement('canvas')
- canvas.width = canvasSize
- canvas.height = canvasSize
- godraysCanvas.width = godraysSize
- godraysCanvas.height = godraysSize
+ canvas.width = STAGE_WIDTH * BLOCK_SIZE
+ canvas.height = STAGE_HEIGHT * BLOCK_SIZE
+ godraysCanvas.width = ~~(canvas.width / 8.0)
+ godraysCanvas.height = ~~(canvas.height / 8.0)
this.redraw = solarQuartet.bind(
null,
- canvas, canvas.getContext('2d'), canvasSize, canvasSize,
- godraysCanvas, godraysCanvas.getContext('2d'), godraysSize, godraysSize,
+ canvas, canvas.getContext('2d'), ~~(canvas.width / 2.0), ~~(canvas.height / 2.0),
+ godraysCanvas, godraysCanvas.getContext('2d'), godraysCanvas.width, godraysCanvas.height,
)
this.refresh()
},
@@ 65,8 64,8 @@ export default {
<style>
#background {
display: block;
- width: 100%;
- height: 100%;
+ width: var(--field-width);
+ height: var(--field-height);
object-fit: contain;
background: black;
}
M src/Field.vue => src/Field.vue +45 -49
@@ 1,13 1,14 @@
<template>
<div id="field" :class="daytimeClass">
<input v-keep-focussed type="text"
- @keydown.up="jump = jump || !blocked.down ? jump : 20"
- @keydown.down="moveTo = 'down'"
- @keydown.right="moveTo = 'right'"
- @keydown.left="moveTo = 'left'"
- @keyup.down="moveTo = null"
- @keyup.right="moveTo = null"
- @keyup.left="moveTo = null"
+ @keydown.up="inputY = -1"
+ @keydown.down="inputY = 1"
+ @keydown.right="inputX = -1"
+ @keydown.left="inputX = 1"
+ @keyup.up="inputY = inputY === -1 ? 0 : 1"
+ @keyup.down="inputY = inputY === 1 ? 0 : 1"
+ @keyup.right="inputX = inputX === -1 ? 0 : 1"
+ @keyup.left="inputX = inputX === 1 ? 0: -1"
@keypress.p="togglePause"
@keydown.space="digging = true"
@keyup.space="digging = false"
@@ 18,7 19,7 @@
<div v-for="(block, x) in row" class="block" :class="[block.type]" />
</template>
</div>
- <div id="player" :class="[playerDirection]" />
+ <div id="player" :class="[player.direction]" />
<div id="level-indicator">
x:{{ floorX }}, y:{{ floorY }}
<template v-if="moving !== false">({{clock}})</template>
@@ 29,36 30,39 @@
<script>
// import throttle from 'lodash/throttle'
-import Level from './level'
import MountainBackground from './Background'
-
-const BLOCK_SIZE = 32
-const RECIPROCAL = 1 / BLOCK_SIZE
-const PLAYER_X = ~~(BLOCK_SIZE / 2) + 1
-const PLAYER_Y = BLOCK_SIZE - 15
-const PLAYER_MAX_VELOCITY = 32
-const level = new Level(BLOCK_SIZE + 2, BLOCK_SIZE + 2)
+import Level from './level'
+import { Moveable } from './physics'
+import {
+ BLOCK_SIZE,
+ RECIPROCAL,
+ STAGE_WIDTH,
+ STAGE_HEIGHT,
+ PLAYER_X,
+ PLAYER_Y
+} from './level/def'
+
+const level = new Level(STAGE_WIDTH + 2, STAGE_HEIGHT + 2)
+const player = new Moveable(PLAYER_X, PLAYER_Y)
export default {
name: 'field',
components: { MountainBackground },
data () {
return {
+ player,
x: 0,
- y: 0,
- playerDirection: 'left',
- playerVelocityX: 0,
- playerVelocityY: 8,
- moveTo: null,
- jump: 0,
- digging: false,
- gravity: 8.0 / 20,
+ y: 12,
+ inputX: 0,
+ inputY: 0,
+ time: 250,
moving: false,
- time: 250
+ lastTick: 0
}
},
mounted () {
- this.move()
+ this.lastTick = performance.now()
+ this.move(this.lastTick)
},
computed: {
rows () { return level.grid(this.floorX, this.floorY) },
@@ 110,28 114,21 @@ export default {
}
},
methods: {
- move () {
- // set time of day in ticks
- this.time = (this.time + 0.1) % 1000
+ move (thisTick) {
+ this.moving = requestAnimationFrame(this.move)
- const x = this.x
- const y = this.y
+ // keep roughly 20 fps
+ if (thisTick - this.lastTick < 50) return
- if (this.moveTo !== null) this.playerDirection = this.moveTo
-
- if (this.moveTo === 'right') {
- this.playerVelocityX = 8
- } else if (this.moveTo === 'left') {
- this.playerVelocityX = -8
- } else {
- this.playerVelocityX = 0
- }
+ // set time of day in ticks
+ this.time = (this.time + 0.1) % 1000
- // this.player_velocity_y += this.gravity
- let dx = this.playerVelocityX * RECIPROCAL
- let dy = (this.playerVelocityY - this.jump) * RECIPROCAL
+ const player = this.player
+ const x = player.x
+ const y = player.y
- if (this.jump > 0) this.jump -= 2
+ let dx = player.vx * player.dir * RECIPROCAL
+ let dy = player.vy * RECIPROCAL
// don't walk / fall into blocks
if (dx > 0 && this.blocked.right) dx = 0
@@ 140,14 137,14 @@ export default {
if (dy < 0 && this.blocked.up) dy = 0
// don't walk, work!
- if (!this.jump && this.digging) {
+ if (!this.inputY && this.digging) {
dx = 0
this.dig()
}
this.x += dx
this.y += dy
- this.moving = setTimeout(() => this.move(), 64) // roughly every 4 frames
+ this.lastTick = thisTick
},
dig () {
console.log('dig', this.playerDirection, this.surroundings[this.playerDirection])
@@ 163,10 160,9 @@ export default {
},
togglePause () {
if (this.moving === false) { // is paused
- this.moving = true // avoid (unlikely) race condition
this.move()
} else {
- clearTimeout(this.moving)
+ cancelAnimationFrame(this.moving)
this.moving = false
}
}
@@ 179,7 175,7 @@ export default {
:root {
--block-size: 32px;
--field-width: 1024px;
- --field-height: 1024px;
+ --field-height: 576px;
--spare-blocks: 2;
}
M src/level/def.js => src/level/def.js +12 -0
@@ 1,3 1,15 @@
+export const BLOCK_SIZE = 32 // each block is 32̨̣̌̇x32 pixel in size and equals 1m
+export const RECIPROCAL = 1 / BLOCK_SIZE
+
+export const STAGE_WIDTH = 32 // 32*32 = 1024 pixel wide stage
+export const STAGE_HEIGHT = ~~(STAGE_WIDTH * 0.5625) // 16:9 😎
+
+// the player position is fixed to the middle of the x axis
+export const PLAYER_X = ~~(STAGE_WIDTH / 2) + 1
+export const PLAYER_Y = ~~(STAGE_HEIGHT * 0.5) // fall from the center
+
+export const GRAVITY = 10 // blocks per second
+
export const type = {
air: {type: 'air', hp: Infinity, walkable: true},
grass: {type: 'grass', hp: 1, walkable: false},
A src/physics.js => src/physics.js +22 -0
@@ 0,0 1,22 @@
+import { GRAVITY } from './level/def'
+
+/** physics gets input like
+ instance of Moveable,
+ position: [x, y],
+ surroundings: [top, right, bottom, left] where each is a block type
+ and updates the Moveable instance values accordingly
+*/
+
+export class Moveable {
+ constructor (x, y, direction = 1) {
+ this.x = x
+ this.y = y
+ this.dir = direction
+ this.vx = 0
+ this.vy = 0
+ }
+
+ get direction () {
+ return this.dir > 0 ? 'left' : 'right'
+ }
+}