Draw the game board

This commit is contained in:
2020-04-10 19:08:25 +02:00
parent decc2a88d7
commit 39e625cf3d
3 changed files with 104 additions and 35 deletions

2
.gitignore vendored
View File

@@ -5,5 +5,5 @@
### Nim ###
nimcache/
public/mttt.js
.vscode/
# End of https://www.gitignore.io/api/nim

View File

@@ -1,15 +1,29 @@
import dom, jsconsole, sugar, times, strformat
import dom, sugar, times, strformat
import src/game
const
canvasId = "game_canvas"
debug = false ## Debug flag. Set to true to show additional debug info
canvasId = "game_canvas" ## ID of the game canvas element
var frameCount = 0
var mtttGame: Game
var startTime, now, then: Time
var elapsed, fpsInterval: Duration
proc showDebugInfo(): void =
## Update the debug info bar at the top of the window
var sinceStart = now - startTime;
frameCount.inc
var currentFps = (1000 / (sinceStart.inMilliseconds.int / frameCount) * 100) / 100;
var e: Element
e = dom.document.getElementById("fps")
e.innerHTML = fmt"Current FPS: {currentFps:9.2f}"
e = dom.document.getElementById("fps-interval")
e.innerHTML = fmt"FPS Interval: {fpsInterval}"
e = dom.document.getElementById("then")
e.innerHTML = fmt"Seconds since start: {sinceStart.inSeconds}"
proc animate(): void =
# request another frame
discard window.requestAnimationFrame((time: float) => animate())
@@ -22,28 +36,23 @@ proc animate(): void =
# Get ready for next frame by setting then=now, but also adjust for your
# specified fpsInterval not being a multiple of RAF's interval (16.7ms)
then = now - fpsInterval
var sinceStart = now - startTime;
frameCount.inc
var currentFps = (1000 / (sinceStart.inMilliseconds.int / frameCount) * 100) / 100;
var e: Element
e = dom.document.getElementById("fps")
e.innerHTML = fmt"Current FPS: {currentFps:9.2f}"
e = dom.document.getElementById("fps-interval")
e.innerHTML = fmt"FPS Interval: {fpsInterval}"
e = dom.document.getElementById("then")
e.innerHTML = fmt"Seconds since start: {sinceStart.inSeconds}"
if (debug):
showDebugInfo()
mtttGame.nextFrame(elapsed)
proc startAnimating(fps: int): void =
# Make sure we run a 60 FPS
fpsInterval = initDuration(milliseconds = (1000 / fps).toInt)
then = getTime()
startTime = then
animate()
proc onLoad(event: Event) {.exportc.} =
mtttGame = newGame(canvasId, window.innerWidth, window.innerHeight)
if(not debug):
dom.document.getElementById("debug").style.display = "none";
mtttGame = newGame(canvasId, debug)
startAnimating(60)
window.onload = onLoad

View File

@@ -1,4 +1,4 @@
import dom, math, strformat, times
import dom, strformat, times, jsconsole
import gamelight/[graphics, vec]
@@ -19,43 +19,47 @@ type
MainMenu, Game
const
gameBgColor = "#e6e6e6"
gameBgColor = "#eaeaea"
metaBoardColor = "#484d4d"
miniBoardColor = "#6a6c6c"
font = "Helvetica, monospace"
padding = 10 # Padding around the game area in pixels
var
debugMode = true
renderWidth, renderHeight: int ## Canvas render area in pixels
proc toTimeString(milliseconds: float): string =
let seconds = ((milliseconds / 1000) mod 60).toInt()
let minutes = ((milliseconds / (1000 * 60)) mod 60).toInt()
let hours = ((milliseconds / (1000 * 60 * 60)) mod 24).toInt()
result = fmt"{hours}:{minutes}:{seconds}"
# TODO this should be dynamic
sidebarWidth = 150 ## Size of the left sidebar
proc switchScene(game: Game, scene: Scene) =
case scene:
of Scene.MainMenu:
discard
of Scene.Game:
var elements: seq[Element] = @[]
let timeTextPos = (padding.toFloat, padding.toFloat).toPoint
elements.add game.renderer.createTextElement("Time", timeTextPos, "#000000", 26, font)
discard game.renderer.createTextElement("Game Time", timeTextPos, "#000000", 26, font)
let timePos = (padding.toFloat, padding.toFloat + 35.0).toPoint
game.timeElement = game.renderer.createTextElement("0", timePos, "#000000", 14, font)
game.timeElement = game.renderer.createTextElement("0", timePos, "#000000", 20, font)
proc newGame*(canvasId: string, width, height: int): Game =
proc newGame*(canvasId: string, debug: bool = false): Game =
debugMode = debug
var
player1 = Player(name: "Player 1")
player2 = Player(name: "Player 2")
renderWidth = width
renderHeight = height
# Update game area size
renderHeight = window.innerHeight
renderWidth = window.innerWidth
console.debug(fmt"Game area: {renderWidth}x{renderHeight}")
if debugMode:
renderHeight = renderHeight - 19 ## The size of the debug bar
console.debug(fmt"Game area after debug check: {renderWidth}x{renderHeight}")
result = Game(
renderer: newRenderer2D(canvasId, width, height),
renderer: newRenderer2D(canvasId, renderWidth, renderHeight),
scene: Scene.Game,
state: newGameState(player1, player2),
paused: false,
@@ -64,7 +68,7 @@ proc newGame*(canvasId: string, width, height: int): Game =
switchScene(result, Scene.Game)
proc update(game: Game, time: Duration) =
# Update the game logic
## Update the game logic
# Return early if paused.
if game.paused or game.scene != Scene.Game: return
@@ -73,14 +77,69 @@ proc update(game: Game, time: Duration) =
proc drawMainMenu(game: Game) =
discard
proc drawPos(renderer: Renderer2D, x, y: float): void =
renderer.fillText(fmt"(x:{x.toInt}, y:{y.toInt})", (x, y).toPoint)
proc drawPos(renderer: Renderer2D, point: Point): void =
renderer.drawPos(point.x.toFloat, point.y.toFloat)
proc drawBoard(game: Game, zero: Point, size: int, color = "#000000", pad = 10): void =
## Draw a Tic Tac Toe board with the given sizes
if (debugMode):
game.renderer.drawPos(zero) # Top left corner
game.renderer.drawPos((zero.x + size, zero.y).toPoint) # Top right corner
game.renderer.drawPos((zero.x, zero.y + size).toPoint) # Bottom left corner
game.renderer.drawPos((zero.x + size, zero.y + size).toPoint) # Bottom right corner
for i in 1 .. 2:
let offX = ((size / 3) * i.toFloat) + zero.x.toFloat
let offY = ((size / 3) * i.toFloat) + zero.y.toFloat
game.renderer.beginPath()
# horizontal line
game.renderer.moveTo((zero.x+pad).toFloat, offY)
if (debugMode): game.renderer.drawPos(zero.x.toFloat, offY)
game.renderer.lineTo(zero.x + size - pad, offY)
if (debugMode): game.renderer.drawPos((zero.x + size).toFloat, offY)
# vertical line
game.renderer.moveTo(offX, (zero.y+pad).toFloat)
if (debugMode): game.renderer.drawPos(offX, zero.y.toFloat)
game.renderer.lineTo(offX, zero.y + size - pad)
if (debugMode): game.renderer.drawPos(offX, (zero.y + size).toFloat)
game.renderer.closePath()
game.renderer.strokePath(color, 3)
proc drawGame(game: Game) =
# Draw changing UI Elements
## Redraw the UI elements
game.timeElement.innerHTML = fmt"{game.totalTime.inMinutes}m {game.totalTime.inSeconds mod 60}s"
let
gAreaX = 2 * padding + sidebarWidth
gAreay = padding
gAreaH = renderHeight - 2 * padding
gAreaW = renderWidth - sidebarWidth - 3 * padding
gSize = min(gAreaH, gAreaW)
bSize = (gSize / 3).toInt
# Draw a box araound the game board
game.renderer.strokeRect(gAreaX, gAreaY, gSize, gSize, "#0000004d")
# Draw the meta board
game.drawBoard((gAreaX, gAreaY).toPoint, gSize, metaBoardColor, 5)
# Draw the small boards
for x in 0 .. 2:
for y in 0 .. 2:
game.drawBoard((gAreaX + x*bSize, gAreaY + y*bSize).toPoint, bSize, miniBoardColor, 15)
proc draw(game: Game) =
# Draw the current screen on the canvas
## Draw the current screen on the canvas
# Fill background color.
game.renderer.fillRect(0.0, 0.0, renderWidth, renderHeight, gameBgColor)
case game.scene
of Scene.MainMenu:
drawMainMenu(game)
@@ -88,6 +147,7 @@ proc draw(game: Game) =
drawGame(game)
proc nextFrame*(game: Game, frameTime: Duration) =
# Determine id an update is necessary
## Prepare the next frame that should be displayed
# Determine if an update is necessary
game.update(frameTime)
game.draw()