Skip to content

Instantly share code, notes, and snippets.

@MarkCLewis
Created August 26, 2017 13:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save MarkCLewis/3492149d1edd0a8b75647e72938b9e94 to your computer and use it in GitHub Desktop.
Save MarkCLewis/3492149d1edd0a8b75647e72938b9e94 to your computer and use it in GitHub Desktop.
Renderers that students can use for their graphical games in CSCI 1321.
import scalafx.scene.canvas.GraphicsContext
import scalafx.scene.image.Image
/**
* This is a 2D renderer that with draw your game elements to a Canvas. You should change the
* images to fit the style of your game. Also, alter the entities to match what you have in
* your game.
*/
class Renderer2D(gc: GraphicsContext, blockSize: Double) {
private var lastCenterX = 0.0
private var lastCenterY = 0.0
// Put variables for images here
private val floorImage = new Image("file:images/floor.png")
private val wallImage = new Image("file:images/wall.png")
private val playerImage = new Image("file:images/player.png")
private val enemyImage = new Image("file:images/enemy.png")
private val generatorImage = new Image("file:images/generator.png")
private val bulletImage = new Image("file:images/bullet.png")
/**
* These two methods are used to figure out where to draw things. They are used by the render.
*/
def blocksToPixelsX(bx: Double): Double = gc.canvas.getWidth / 2 + (bx - lastCenterX) * blockSize
def blocksToPixelsY(by: Double): Double = gc.canvas.getHeight / 2 + (by - lastCenterY) * blockSize
/**
* These two methods are used to go from coordinates to blocks. You need them if you have mouse interactions.
*/
def pixelsToBlocksX(px: Double): Double = (px - gc.canvas.getWidth / 2) / blockSize + lastCenterX
def pixelsToBlocksY(py: Double): Double = (py - gc.canvas.getHeight / 2) / blockSize + lastCenterY
/**
* This method is called to render things to the screen.
*/
def render(level: Level, cx: Double, cy: Double): Unit = {
lastCenterX = cx
lastCenterY = cy
val drawWidth = (gc.canvas.getWidth / blockSize).toInt + 1
val drawHeight = (gc.canvas.getWidth / blockSize).toInt + 1
// Draw walls and floors
for {
x <- cx.toInt - drawWidth / 2 - 1 to cx.toInt + drawWidth / 2 + 1
y <- cy.toInt - drawHeight / 2 - 1 to cy.toInt + drawHeight / 2 + 1
} {
val img = if (level.maze(x, y)) {
wallImage
} else {
floorImage
}
gc.drawImage(img, blocksToPixelsX(x), blocksToPixelsY(y), blockSize, blockSize)
}
// Draw entities
for (e <- level.entities) {
val img = e match {
case p: Player => playerImage
case e: Enemy => enemyImage
case b: Bullet => bulletImage
case g: Generator => generatorImage
}
if(level.maze.wrap) {
for(rx <- -1 to 1; ry <- -1 to 1)
gc.drawImage(img, blocksToPixelsX(e.x-e.width/2+rx*level.maze.width), blocksToPixelsY(e.y-e.height/2+ry*level.maze.height), e.width*blockSize, e.height*blockSize)
} else {
gc.drawImage(img, blocksToPixelsX(e.x-e.width/2), blocksToPixelsY(e.y-e.height/2), e.width*blockSize, e.height*blockSize)
}
}
}
}
import scalafx.Includes._
import scalafx.scene.Scene
import scala.collection.mutable
import scalafx.scene.shape.Shape
import scalafx.scene.shape.Box
import scalafx.scene.paint.PhongMaterial
import scalafx.scene.image.Image
import scalafx.scene.paint.Color
import scalafx.scene.PerspectiveCamera
import scalafx.scene.AmbientLight
import scalafx.scene.PointLight
import scalafx.geometry.Point3D
import scalafx.scene.shape.Sphere
import scalafx.scene.transform.Rotate
import scalafx.scene.transform.Translate
import scalafx.scene.shape.Shape3D
/**
* This is a renderer that uses the 3D capabilities of JavaFX to let you render your game in 3D.
* 3D games can't wrap, so make sure you have that option turned off in your maze.
*/
class Renderer3D(scene: Scene, maze: Maze, isoDistAngle: Option[(Double, Double)]) {
private val sceneEntities = mutable.Map[String, Shape3D]()
// Change images and materials to match the desired theme of your game
private val wallImage = new Image("file:images/banish.gif")
private val floorImage = new Image("file:images/pebble_round.jpg")
private val ceilingImage = new Image("file:images/yellow_wood_planks.jpg")
private val enemyImage = new Image("file:images/enemy.png")
private val enemyMat = new PhongMaterial()
enemyMat.diffuseMap = enemyImage
private val generatorMat = new PhongMaterial(Color.Green)
private val bulletMat = new PhongMaterial(Color.Red)
private val playerMat = new PhongMaterial(Color.Aqua)
private val cam = new PerspectiveCamera(true)
placeCamera()
placeMazeGeometry()
private def placeCamera(): Unit = {
// cam.fieldOfView = 110 // Use this line to get a wider field of view
scene.camera = cam
scene.content += cam
}
/**
* Adds the lighting. You can play around with this. For example, there is a PointLight type
* that you could try having follow your player around.
*/
private def addLighting(): Unit = {
val ambient = new AmbientLight(Color.White)
scene.content += ambient
}
/**
* Places the maze geometry as well as the floor and possibly ceiling.
*/
private def placeMazeGeometry(): Unit = {
while (wallImage.backgroundLoading) {}
val mat = new PhongMaterial()
mat.setDiffuseMap(wallImage)
for (x <- 0 to maze.width; y <- 0 to maze.height) {
if (maze(x, y)) {
val box = new Box(1, 1, 1)
box.translateX = x
box.translateY = y
box.material = mat
scene.content += box
}
}
val floor = new Box(maze.width, maze.height, 1)
val floorMat = new PhongMaterial()
floorMat.diffuseMap = floorImage
floor.translateX = maze.width / 2
floor.translateY = maze.height / 2
floor.translateZ = 1.0
floor.material = floorMat
scene.content += floor
if(isoDistAngle.isEmpty) {
val ceiling = new Box(maze.width, maze.height, 1)
val ceilingMat = new PhongMaterial()
ceilingMat.diffuseMap = ceilingImage
ceiling.translateX = maze.width / 2
ceiling.translateY = maze.height / 2
ceiling.translateZ = -1.0
ceiling.material = ceilingMat
scene.content += ceiling
}
}
/**
* Call this with the entities from your game each update. All entities have to have a unique name for this
* to work. It entities have the same name you will get very odd behavior. You can modify the types and add
* types, but you probably shouldn't modify the code that moves the camera.
*/
def updateEntities(entities: Seq[Entity]): Unit = {
// Add/update the entities
for (e <- entities) {
e match {
case p: Player3D =>
if(!sceneEntities.contains(e.name)) {
val player = new Sphere(e.width/2)
player.material = playerMat
scene.content += player
sceneEntities(e.name) = player
}
sceneEntities(e.name).translateX = e.x-0.5
sceneEntities(e.name).translateY = e.y-0.5
isoDistAngle.map { case (dist, theta) =>
cam.transforms = List(new Translate(p.x-0.5, p.y-0.5, 0), new Rotate(theta, new Point3D(1, 0, 0)), new Translate(0, 0, -dist))
}.getOrElse {
cam.transforms = List(new Translate(p.x-0.5, p.y-0.5), new Rotate(p.facing, new Point3D(0, 0, 1)), new Rotate(90, new Point3D(1, 0, 0)))
}
case _: Enemy =>
if(!sceneEntities.contains(e.name)) {
val enemy = new Sphere(e.width/2)
enemy.material = enemyMat
scene.content += enemy
sceneEntities(e.name) = enemy
}
sceneEntities(e.name).translateX = e.x-0.5
sceneEntities(e.name).translateY = e.y-0.5
case _: Generator =>
if(!sceneEntities.contains(e.name)) {
val generator = new Box(e.width, e.height, 0.5)
generator.material = generatorMat
scene.content += generator
sceneEntities(e.name) = generator
}
sceneEntities(e.name).translateX = e.x-0.5
sceneEntities(e.name).translateY = e.y-0.5
case _: Bullet =>
if(!sceneEntities.contains(e.name)) {
val bullet = new Sphere(e.width/2)
bullet.material = bulletMat
scene.content += bullet
sceneEntities(e.name) = bullet
}
sceneEntities(e.name).translateX = e.x-0.5
sceneEntities(e.name).translateY = e.y-0.5
case _ =>
}
// Remove anything that is no longer part of entities
val victims = sceneEntities.keySet diff entities.map(_.name).toSet
for(v <- victims) {
scene.content -= sceneEntities(v)
sceneEntities -= v
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment