First version, bars of constant size:
package example
import scala.scalajs.js
import scala.scalajs.js.annotation.JSExport
import org.scalajs.dom
import org.scalajs.dom.html
import scala.util.Random
case class Point(x: Int, y: Int) {
def +(p: Point) = Point(x + p.x, y + p.y)
def /(d: Int) = Point(x / d, y / d)
}
@JSExport
object ScalaJSExample {
@JSExport
def main(canvas: html.Canvas): Unit = {
/*setup*/
val renderer = canvas.getContext("2d")
.asInstanceOf[dom.CanvasRenderingContext2D]
canvas.width = canvas.parentElement.clientWidth
canvas.height = 400
renderer.font = "50px sans-serif"
renderer.textAlign = "center"
renderer.textBaseline = "middle"
val obstacleGap = 200
val holeSize = 50
var frame = -50
val obstacles = collection.mutable.Queue.empty[Int]
def runLive(): Unit = {
frame += 2
println(frame)
if(frame > 0 && frame % obstacleGap == 0) {
// obstacles.enqueue(Random.nextInt(canvas.height - 2 * holeSize) + holeSize)
obstacles.enqueue(canvas.height - 2 * holeSize)
}
if(obstacles.length > 7) {
obstacles.dequeue()
frame -= obstacleGap
}
renderer.fillStyle = "darkblue"
for((holeY, i) <- obstacles.zipWithIndex) {
val holeX = i * obstacleGap + canvas.width - frame
renderer.fillRect(holeX, 0, 5, holeY - holeSize)
}
}
def run(): Unit = {
renderer.clearRect(0, 0, canvas.width, canvas.height)
runLive()
}
dom.setInterval(run _, 20)
}
}
Second version: Bars of random size, and
package example
import scala.scalajs.js
import scala.scalajs.js.annotation.JSExport
import org.scalajs.dom
import org.scalajs.dom.html
import scala.util.Random
case class Point(x: Int, y: Int) {
def +(p: Point) = Point(x + p.x, y + p.y)
def /(d: Int) = Point(x / d, y / d)
}
@JSExport
object ScalaJSExample {
@JSExport
def main(canvas: html.Canvas): Unit = {
/*setup*/
val renderer = canvas.getContext("2d")
.asInstanceOf[dom.CanvasRenderingContext2D]
canvas.width = canvas.parentElement.clientWidth
canvas.height = 400
renderer.font = "50px sans-serif"
renderer.textAlign = "center"
renderer.textBaseline = "middle"
val obstacleGap = 200
val holeSize = 50
var frame = -50
val obstacles = collection.mutable.Queue.empty[Int]
def runLive(): Unit = {
frame += 2
println(frame)
if(frame > 0 && frame % obstacleGap == 0) {
obstacles.enqueue(Random.nextInt(canvas.height - 2 * holeSize) + holeSize)
}
if(obstacles.length > 7) {
obstacles.dequeue()
frame -= obstacleGap
}
renderer.fillStyle = "darkblue"
for((holeY, i) <- obstacles.zipWithIndex) {
val holeX = i * obstacleGap + canvas.width - frame
renderer.fillRect(holeX, 0, 5, holeY - holeSize)
}
}
def run(): Unit = {
renderer.clearRect(0, 0, canvas.width, canvas.height)
runLive()
}
dom.setInterval(run _, 20)
}
}
Version 3: Obstacles refactored out
package example
import scala.scalajs.js
import scala.scalajs.js.annotation.JSExport
import org.scalajs.dom
import org.scalajs.dom.{CanvasRenderingContext2D, html}
import scala.util.Random
case class Point(x: Int, y: Int) {
def +(p: Point) = Point(x + p.x, y + p.y)
def /(d: Int) = Point(x / d, y / d)
}
case class Obstacle(gap: Int, holeSize: Int, renderer: CanvasRenderingContext2D, width: Int, height: Int) {
val obstacles = collection.mutable.Queue.empty[Int]
var frame = 0
def render(): Unit = {
frame += 1
if(frame > 0 && frame % gap == 0) {
obstacles.enqueue(Random.nextInt(height - 2 * holeSize) + holeSize)
}
if(obstacles.length > 7) {
obstacles.dequeue()
frame -= gap
}
renderer.fillStyle = "darkblue"
for((holeY, i) <- obstacles.zipWithIndex) {
val holeX = i * gap + width - frame
renderer.fillRect(holeX, 0, 5, holeY - holeSize)
renderer.fillRect(holeX, holeY, 5, height - holeY)
}
}
}
@JSExport
object ScalaJSExample {
@JSExport
def main(canvas: html.Canvas): Unit = {
/*setup*/
val renderer: CanvasRenderingContext2D = canvas.getContext("2d")
.asInstanceOf[dom.CanvasRenderingContext2D]
canvas.width = canvas.parentElement.clientWidth
canvas.height = 400
renderer.font = "50px sans-serif"
renderer.textAlign = "center"
renderer.textBaseline = "middle"
val obstacle = new Obstacle(200, 10, renderer, canvas.parentElement.clientWidth, 400)
def run(): Unit = {
renderer.clearRect(0, 0, canvas.width, canvas.height)
obstacle.render()
}
dom.setInterval(run _, 20)
}
}
Version 4: Adding a player
package example
import scala.scalajs.js
import scala.scalajs.js.annotation.JSExport
import org.scalajs.dom
import org.scalajs.dom.{CanvasRenderingContext2D, html}
import scala.util.Random
case class Point(x: Int, y: Int) {
def +(p: Point) = Point(x + p.x, y + p.y)
def /(d: Int) = Point(x / d, y / d)
}
@JSExport
object ScalaJSExample {
@JSExport
def main(canvas: html.Canvas): Unit = {
/*setup*/
val renderer: CanvasRenderingContext2D = canvas.getContext("2d")
.asInstanceOf[dom.CanvasRenderingContext2D]
canvas.width = canvas.parentElement.clientWidth
canvas.height = 400
renderer.font = "50px sans-serif"
renderer.textAlign = "center"
renderer.textBaseline = "middle"
case class Obstacle(gap: Int, holeSize: Int) {
val obstacles = collection.mutable.Queue.empty[Int]
var frame = 0
def render(): Unit = {
frame += 1
if(frame > 0 && frame % gap == 0) {
obstacles.enqueue(Random.nextInt(canvas.height - 2 * holeSize) + holeSize)
}
if(obstacles.length > 7) {
obstacles.dequeue()
frame -= gap
}
renderer.fillStyle = "darkblue"
for((holeY, i) <- obstacles.zipWithIndex) {
val holeX = i * gap + canvas.width - frame
renderer.fillRect(holeX, 0, 5, holeY - holeSize)
renderer.fillRect(holeX, holeY, 5, canvas.height - holeY)
}
}
}
case class Player(x: Int, y: Int, v: Int, gravity: Int) {
def isDead = false
def render(): Unit = {
renderer.fillStyle = "darkgreen"
renderer.fillRect(x - 5, y - 5, 10, 10)
}
}
val obstacle = new Obstacle(200, 50)
val player = new Player(canvas.width / 2, canvas.height / 2, 0, 1)
def run(): Unit = {
renderer.clearRect(0, 0, canvas.width, canvas.height)
obstacle.render()
player.render()
}
dom.setInterval(run _, 20)
}
}
Version 5: Player responsive to click
package example
import scala.scalajs.js
import scala.scalajs.js.annotation.JSExport
import org.scalajs.dom
import org.scalajs.dom.{CanvasRenderingContext2D, html}
import scala.util.Random
case class Point(x: Int, y: Int) {
def +(p: Point) = Point(x + p.x, y + p.y)
def /(d: Int) = Point(x / d, y / d)
}
@JSExport
object ScalaJSExample {
@JSExport
def main(canvas: html.Canvas): Unit = {
/*setup*/
val renderer: CanvasRenderingContext2D = canvas.getContext("2d")
.asInstanceOf[dom.CanvasRenderingContext2D]
canvas.width = canvas.parentElement.clientWidth
canvas.height = 400
renderer.font = "50px sans-serif"
renderer.textAlign = "center"
renderer.textBaseline = "middle"
case class Obstacle(gap: Int, holeSize: Int) {
val obstacles = collection.mutable.Queue.empty[Int]
var frame = 0
def render(): Unit = {
frame += 1
if(frame > 0 && frame % gap == 0) {
obstacles.enqueue(Random.nextInt(canvas.height - 2 * holeSize) + holeSize)
}
if(obstacles.length > 7) {
obstacles.dequeue()
frame -= gap
}
renderer.fillStyle = "darkblue"
for((holeY, i) <- obstacles.zipWithIndex) {
val holeX = i * gap + canvas.width - frame
renderer.fillRect(holeX, 0, 5, holeY - holeSize)
renderer.fillRect(holeX, holeY, 5, canvas.height - holeY)
}
}
}
class Player(var x: Double, var y: Double, var v: Double, val gravity: Double) {
def isDead = false
def render(): Unit = {
renderer.fillStyle = "darkgreen"
renderer.fillRect(x - 5, y - 5, 10, 10)
}
}
val obstacle = new Obstacle(200, 50)
val player = new Player(canvas.width / 2, canvas.height / 2, 0, 0.1)
def run(): Unit = {
renderer.clearRect(0, 0, canvas.width, canvas.height)
obstacle.render()
player.y += player.v
player.v += player.gravity
player.render()
}
dom.setInterval(run _, 20)
canvas.onclick = (e: dom.MouseEvent) => {
player.v -= 2
}
}
}
Version 6: Collision detection
Separating obstacles and player code is actually not a good idea, it makes the interaction logic very cumbersome…
package example
import scala.scalajs.js
import scala.scalajs.js.annotation.JSExport
import org.scalajs.dom
import org.scalajs.dom.{CanvasRenderingContext2D, html}
import scala.util.Random
case class Point(x: Int, y: Int) {
def +(p: Point) = Point(x + p.x, y + p.y)
def /(d: Int) = Point(x / d, y / d)
}
@JSExport
object ScalaJSExample {
@JSExport
def main(canvas: html.Canvas): Unit = {
/*setup*/
val renderer: CanvasRenderingContext2D = canvas.getContext("2d")
.asInstanceOf[dom.CanvasRenderingContext2D]
canvas.width = canvas.parentElement.clientWidth
canvas.height = 400
renderer.font = "50px sans-serif"
renderer.textAlign = "center"
renderer.textBaseline = "middle"
case class Obstacle(gap: Int, holeSize: Int) {
val obstacles = collection.mutable.Queue.empty[Int]
var frame = 0
def render(): Unit = {
frame += 1
if(frame > 0 && frame % gap == 0) {
obstacles.enqueue(Random.nextInt(canvas.height - 2 * holeSize) + holeSize)
}
if(obstacles.length > 7) {
obstacles.dequeue()
frame -= gap
}
renderer.fillStyle = "darkblue"
for((holeY, i) <- obstacles.zipWithIndex) {
val holeX = i * gap + canvas.width - frame
renderer.fillRect(holeX, 0, 5, holeY - holeSize)
renderer.fillRect(holeX, holeY, 5, canvas.height - holeY)
}
}
}
class Player(var x: Double, var y: Double, var v: Double, val gravity: Double) {
def render(): Unit = {
renderer.fillStyle = "darkgreen"
renderer.fillRect(x - 5, y - 5, 10, 10)
}
}
val obstacle = new Obstacle(200, 50)
val player = new Player(canvas.width / 2, canvas.height / 2, 0, 0.1)
def isPlayerDead(player: Player): Boolean = {
for((holeY, i) <- obstacle.obstacles.zipWithIndex) {
val holeX = i * obstacle.gap + canvas.width - obstacle.frame
if(math.abs(holeX - canvas.width / 2) < 5 && math.abs(holeY - player.y) > obstacle.holeSize) {
return true
}
}
if(player.y < 0 || player.y > canvas.height) {
return true
}
false
}
def run(): Unit = {
renderer.clearRect(0, 0, canvas.width, canvas.height)
obstacle.render()
if(! isPlayerDead(player)) {
player.render()
} else {
renderer.clearRect(0, 0, canvas.width, canvas.height)
renderer.fillStyle = "darkred"
renderer.fillText("Game Over", canvas.width/2, canvas.height/2)
}
player.y += player.v
player.v += player.gravity
}
dom.setInterval(run _, 20)
canvas.onclick = (e: dom.MouseEvent) => {
player.v -= 2
}
}
}
0 comments:
Post a Comment