Created
August 26, 2017 13:03
-
-
Save MarkCLewis/3492149d1edd0a8b75647e72938b9e94 to your computer and use it in GitHub Desktop.
Renderers that students can use for their graphical games in CSCI 1321.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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