diff options
| author | James Barnett <noreply@jamesbarnett.xyz> | 2020-12-28 15:49:23 +0000 |
|---|---|---|
| committer | James Barnett <noreply@jamesbarnett.xyz> | 2020-12-28 15:49:23 +0000 |
| commit | f28500d963d6546feda522d7748a0462a568ba28 (patch) | |
| tree | 0240d3528277930b28790c336ed10d77ea8928e1 /src | |
| parent | 01727649bb1fbe3e0c721a850bd21973d7e760f2 (diff) | |
| download | kotlin-raycaster-f28500d963d6546feda522d7748a0462a568ba28.tar.xz kotlin-raycaster-f28500d963d6546feda522d7748a0462a568ba28.zip | |
Refactor components
Diffstat (limited to 'src')
| -rw-r--r-- | src/main/kotlin/Map.kt | 17 | ||||
| -rw-r--r-- | src/main/kotlin/Minimap.kt | 17 | ||||
| -rw-r--r-- | src/main/kotlin/Raycaster.kt | 42 | ||||
| -rw-r--r-- | src/main/kotlin/Renderer.kt | 8 | ||||
| -rw-r--r-- | src/main/kotlin/main.kt | 68 | ||||
| -rw-r--r-- | src/main/kotlin/raycaster.kt | 117 |
6 files changed, 138 insertions, 131 deletions
diff --git a/src/main/kotlin/Map.kt b/src/main/kotlin/Map.kt new file mode 100644 index 0000000..d458a67 --- /dev/null +++ b/src/main/kotlin/Map.kt @@ -0,0 +1,17 @@ +class Map { + val data = listOf( + listOf(1,1,1,1,1,1,1,1,1,1), + listOf(1,0,0,0,0,0,0,0,0,1), + listOf(1,0,0,0,0,0,0,0,0,1), + listOf(1,0,0,1,1,0,1,0,0,1), + listOf(1,0,0,1,0,0,1,0,0,1), + listOf(1,0,0,1,0,0,1,0,0,1), + listOf(1,0,0,1,0,1,1,0,0,1), + listOf(1,0,0,0,0,0,0,0,0,1), + listOf(1,0,0,0,0,0,0,0,0,1), + listOf(1,1,1,1,1,1,1,1,1,1) + ) + + val width = data[0].size + val height = data.size +}
\ No newline at end of file diff --git a/src/main/kotlin/Minimap.kt b/src/main/kotlin/Minimap.kt index dc7cc91..eb762ab 100644 --- a/src/main/kotlin/Minimap.kt +++ b/src/main/kotlin/Minimap.kt @@ -2,16 +2,13 @@ import kotlinx.browser.document import org.w3c.dom.CanvasRenderingContext2D import org.w3c.dom.HTMLCanvasElement -class Minimap(private val map: List<List<Int>>) { - +class Minimap(private val map: Map) { private val scale = 30 - private val mapWidth = map[0].size - private val mapHeight = map.size private val canvas = (document.createElement("canvas") as HTMLCanvasElement) .apply { - width = mapWidth * scale - height = mapHeight * scale + width = map.width * scale + height = map.height * scale id = "minimap" style.width = "${width}px" style.height = "${height}px" @@ -23,9 +20,9 @@ class Minimap(private val map: List<List<Int>>) { } private fun drawMap() { - for (y in 0 until mapHeight) { - for (x in 0 until mapWidth) { - val wall = map[y][x] + for (y in 0 until map.height) { + for (x in 0 until map.height) { + val wall = map.data[y][x] if (wall > 0) { context.fillStyle = "#000000" context.fillRect((x * scale).toDouble(), (y * scale).toDouble(), scale.toDouble(), scale.toDouble()) @@ -35,7 +32,7 @@ class Minimap(private val map: List<List<Int>>) { } fun update(camera: Camera) { - context.clearRect(0.0, 0.0, (mapWidth * scale).toDouble(), (mapHeight * scale).toDouble()) + context.clearRect(0.0, 0.0, (map.width * scale).toDouble(), (map.height * scale).toDouble()) drawMap() context.fillStyle = "#FF0000" context.fillRect(camera.xPos * scale, camera.yPos * scale, scale.toDouble(), scale.toDouble()) diff --git a/src/main/kotlin/Raycaster.kt b/src/main/kotlin/Raycaster.kt new file mode 100644 index 0000000..d3fa694 --- /dev/null +++ b/src/main/kotlin/Raycaster.kt @@ -0,0 +1,42 @@ +import kotlin.math.pow + +class Raycaster(private val stepPrecision: Int = 32) { + + fun raycast(raycastContext: RaycastContext) { + val (renderer, camera, map, _) = raycastContext + + val viewportWidth = renderer.viewportWidth + val viewportHeight = renderer.viewportHeight + val viewportHeightHalf = viewportHeight / 2 + + var raySweepAngle = camera.rotation - camera.halfFov + + for (rayIndex in 0 until viewportWidth) { + + var rayX = camera.xPos + var rayY = camera.yPos + + do { + rayX += kotlin.math.cos(toRadians(raySweepAngle)) / stepPrecision + rayY += kotlin.math.sin(toRadians(raySweepAngle)) / stepPrecision + // TODO bounds checking + val wallHit = map.data[kotlin.math.floor(rayY).toInt()][kotlin.math.floor(rayX).toInt()] > 0 + } while (!wallHit) + + val distanceToWall = kotlin.math.sqrt((camera.xPos - rayX).pow(2) + (camera.yPos - rayY).pow(2)) + val wallHeight = kotlin.math.floor(viewportHeightHalf / distanceToWall).toInt() + + // Sky + renderer.drawLine(rayIndex, 0, rayIndex, viewportHeightHalf - wallHeight, "cyan") + // Wall + renderer.drawLine(rayIndex, viewportHeightHalf - wallHeight, rayIndex, viewportHeightHalf + wallHeight, "red") + // Floor + renderer.drawLine(rayIndex, viewportHeightHalf + wallHeight, rayIndex, viewportHeight, "green") + + raySweepAngle += (camera.fov / viewportWidth.toDouble()) + } + } + + + +}
\ No newline at end of file diff --git a/src/main/kotlin/Renderer.kt b/src/main/kotlin/Renderer.kt index b266e61..0bc8988 100644 --- a/src/main/kotlin/Renderer.kt +++ b/src/main/kotlin/Renderer.kt @@ -2,12 +2,12 @@ import kotlinx.browser.document import org.w3c.dom.CanvasRenderingContext2D import org.w3c.dom.HTMLCanvasElement -class Renderer(val w: Int, val h: Int) { +class Renderer(val viewportWidth: Int, val viewportHeight: Int) { private val canvas = (document.createElement("canvas") as HTMLCanvasElement) .apply { - width = w - height = h + width = viewportWidth + height = viewportHeight } private val context = canvas.getContext("2d") as CanvasRenderingContext2D @@ -25,7 +25,7 @@ class Renderer(val w: Int, val h: Int) { } fun clear() { - context.clearRect(0.0, 0.0, w.toDouble(), h.toDouble()) + context.clearRect(0.0, 0.0, viewportWidth.toDouble(), viewportHeight.toDouble()) } }
\ No newline at end of file diff --git a/src/main/kotlin/main.kt b/src/main/kotlin/main.kt new file mode 100644 index 0000000..c07a661 --- /dev/null +++ b/src/main/kotlin/main.kt @@ -0,0 +1,68 @@ +import kotlinx.browser.document + +private const val MOVE_SPEED = 0.5 +private const val ROTATE_SPEED = 5 + +data class RaycastContext( + val renderer: Renderer, + val camera: Camera, + val map: Map, + val minimap: Minimap +) + +fun main() { + val renderer = Renderer(640, 480) + val camera = Camera( + fov = 60, + xPos = 2.0, + yPos = 2.0, + rotation = 90.0 + ) + val map = Map() + val minimap = Minimap(map) + + val context = RaycastContext(renderer, camera, map, minimap) + + val raycaster = Raycaster() + + document.onkeydown = { + when (it.code) { + "KeyW" -> { + console.log("key w") + val cameraCos = kotlin.math.cos(toRadians(camera.rotation)) * MOVE_SPEED + val cameraSin = kotlin.math.sin(toRadians(camera.rotation)) * MOVE_SPEED + camera.xPos += cameraCos + camera.yPos += cameraSin + } + "KeyS" -> { + console.log("key s") + val cameraCos = kotlin.math.cos(toRadians(camera.rotation)) * MOVE_SPEED + val cameraSin = kotlin.math.sin(toRadians(camera.rotation)) * MOVE_SPEED + camera.xPos -= cameraCos + camera.yPos -= cameraSin + } + "KeyA" -> { + console.log("key a") + camera.rotation -= ROTATE_SPEED + } + "KeyD" -> { + console.log("key d") + camera.rotation += ROTATE_SPEED + } + } + paint(raycaster, context) + console.log("camera x:${camera.xPos} y:${camera.yPos} r: ${camera.rotation}") + } + + paint(raycaster, context) +} + +fun paint(raycaster: Raycaster, raycastContext: RaycastContext) { + raycastContext.renderer.clear() + raycaster.raycast(raycastContext) + raycastContext.minimap.update(raycastContext.camera) +} + +fun toRadians(degrees: Double): Double { + return degrees * kotlin.math.PI / 180 +} diff --git a/src/main/kotlin/raycaster.kt b/src/main/kotlin/raycaster.kt deleted file mode 100644 index 5b717bc..0000000 --- a/src/main/kotlin/raycaster.kt +++ /dev/null @@ -1,117 +0,0 @@ -import kotlinx.browser.document -import kotlin.math.pow - -private const val SCREEN_WIDTH = 640 -private const val SCREEN_HEIGHT = 480 -private const val MOVE_SPEED = 0.5 -private const val ROTATE_SPEED = 5 - -private val map = listOf( - listOf(1,1,1,1,1,1,1,1,1,1), - listOf(1,0,0,0,0,0,0,0,0,1), - listOf(1,0,0,0,0,0,0,0,0,1), - listOf(1,0,0,1,1,0,1,0,0,1), - listOf(1,0,0,1,0,0,1,0,0,1), - listOf(1,0,0,1,0,0,1,0,0,1), - listOf(1,0,0,1,0,1,1,0,0,1), - listOf(1,0,0,0,0,0,0,0,0,1), - listOf(1,0,0,0,0,0,0,0,0,1), - listOf(1,1,1,1,1,1,1,1,1,1) -) - -data class RaycastContext( - val renderer: Renderer, - val camera: Camera, - val minimap: Minimap -) - -fun main() { - val renderer = Renderer(SCREEN_WIDTH, SCREEN_HEIGHT) - val camera = Camera( - fov = 60, - xPos = 2.0, - yPos = 2.0, - rotation = 90.0 - ) - val minimap = Minimap(map) - - val context = RaycastContext(renderer, camera, minimap) - - document.onkeydown = { - when (it.code) { - "KeyW" -> { - console.log("key w") - val cameraCos = kotlin.math.cos(toRadians(camera.rotation)) * MOVE_SPEED - val cameraSin = kotlin.math.sin(toRadians(camera.rotation)) * MOVE_SPEED - camera.xPos += cameraCos - camera.yPos += cameraSin - } - "KeyS" -> { - console.log("key s") - val cameraCos = kotlin.math.cos(toRadians(camera.rotation)) * MOVE_SPEED - val cameraSin = kotlin.math.sin(toRadians(camera.rotation)) * MOVE_SPEED - camera.xPos -= cameraCos - camera.yPos -= cameraSin - } - "KeyA" -> { - console.log("key a") - camera.rotation -= ROTATE_SPEED - } - "KeyD" -> { - console.log("key d") - camera.rotation += ROTATE_SPEED - } - } - paint(context) - console.log("camera x:${camera.xPos} y:${camera.yPos} r: ${camera.rotation}") - } - - paint(context) -} - -fun paint(raycastContext: RaycastContext) { - raycastContext.renderer.clear() - raycast(raycastContext) - raycastContext.minimap.update(raycastContext.camera) -} - -fun raycast(raycastContext: RaycastContext) { - val incrementAngle : Double = raycastContext.camera.fov / SCREEN_WIDTH.toDouble() - val rayCastPrecision = 32 - - var rayAngle = raycastContext.camera.rotation - raycastContext.camera.halfFov - - for (rayIndex in 0 until SCREEN_WIDTH) { - - var rayX = raycastContext.camera.xPos - var rayY = raycastContext.camera.yPos - - val rayCos = kotlin.math.cos(toRadians(rayAngle)) / rayCastPrecision - val raySin = kotlin.math.sin(toRadians(rayAngle)) / rayCastPrecision - - var isWall = false - while(!isWall) { - rayX += rayCos - rayY += raySin - - // TODO bounds checking - val foo = map[kotlin.math.floor(rayY).toInt()][kotlin.math.floor(rayX).toInt()] - if (foo == 1) { - isWall = true - } - } - - val distance = kotlin.math.sqrt((raycastContext.camera.xPos - rayX).pow(2) + (raycastContext.camera.yPos - rayY).pow(2)) - val wallHeight = kotlin.math.floor((SCREEN_HEIGHT/2) / distance).toInt() - - raycastContext.renderer.drawLine(rayIndex, 0, rayIndex, (SCREEN_HEIGHT/2)-wallHeight, "cyan") - raycastContext.renderer.drawLine(rayIndex, (SCREEN_HEIGHT/2)-wallHeight, rayIndex, (SCREEN_HEIGHT/2)+wallHeight, "red") - raycastContext.renderer.drawLine(rayIndex, (SCREEN_HEIGHT/2)+wallHeight, rayIndex, SCREEN_HEIGHT, "green") - - rayAngle += incrementAngle - } -} - -fun toRadians(degrees: Double): Double { - return degrees * kotlin.math.PI / 180 -} |