GitHub Decentralized Repo
GitHub Centralized Repo GitHub Flat Repo

Let’s add a simple object type to SpaceObject. That should let us do drawing in a more generic fashion. Update: Found a better way.

So far, there’s just SpaceObject:

data class SpaceObject(
    var x: Double,
    var y: Double,
    var dx: Double,
    var dy: Double,
    var angle: Double = 0.0
)

They all move the same way, adding dx, dy to x and y. The two kinds we have have separate drawing functions:

fun drawAsteroid(spaceObject: SpaceObject, drawer: Drawer) {
    draw(spaceObject, drawer, asteroidPoints)
}

fun drawShip(spaceObject: SpaceObject, drawer: Drawer) {
    draw(spaceObject, drawer, shipPoints)
}

fun draw(spaceObject: SpaceObject, drawer: Drawer, points: List<Vector2>) {
    drawer.isolated {
        drawer.translate(spaceObject.x, spaceObject.y)
        drawer.scale(4.00, 4.0)
        drawer.rotate(spaceObject.angle)
        drawer.stroke = ColorRGBa.WHITE
        drawer.lineStrip(points)
    }
}

These functions just differ by what list of points they provide to the draw function. We could look up that list if we had a type field. For now, let’s make an Enum.

enum class SpaceObjectType {
    ASTEROID, SHIP
}

Let’s make a little map, mapping type to the list of points:

val points = mapOf(
    SpaceObjectType.ASTEROID to asteroidPoints,
    SpaceObjectType.SHIP to shipPoints
)

Let’s add a type to SpaceObject, and provide it literally for now:

data class SpaceObject(
    var x: Double,
    var y: Double,
    var dx: Double,
    var dy: Double,
    var angle: Double = 0.0,
    val type: SpaceObjectType
)

IDEA wants to update usages. We’ll do that …

No, we can’t put type last like that, after defaulting angle. Let’s put it first.

data class SpaceObject(
    val type: SpaceObjectType,
    var x: Double,
    var y: Double,
    var dx: Double,
    var dy: Double,
    var angle: Double = 0.0,
)

Now fix up the calls, a half-dozen like this:

val ship = SpaceObject(
            SpaceObjectType.SHIP,
            512.0,
            512.0,
            100.0,
            -90.0,
            45.0,
        )

Everything should work at this point. Tests are green. Drawing is good. Commit: added SpaceObjectType parameter to SpaceObject.

Let’s implement draw to look up the points. It will change from this:

fun draw(spaceObject: SpaceObject, drawer: Drawer, points: List<Vector2>) {
    drawer.isolated {
        drawer.translate(spaceObject.x, spaceObject.y)
        drawer.scale(4.00, 4.0)
        drawer.rotate(spaceObject.angle)
        drawer.stroke = ColorRGBa.WHITE
        drawer.lineStrip(points)
    }
}

I have a bit of an argument with Kotlin and wind up with this:

fun draw(
    spaceObject: SpaceObject,
    drawer: Drawer,
) {
    val points = points[spaceObject.type] ?: return
    drawer.isolated {
        drawer.translate(spaceObject.x, spaceObject.y)
        drawer.scale(4.00, 4.0)
        drawer.rotate(spaceObject.angle)
        drawer.stroke = ColorRGBa.WHITE
        drawer.lineStrip(points)
    }
}

Perfectly OK with me. Now I’ll remove the functions drawShip and drawAsteroid and just call draw.

        extend {
            drawer.fill = ColorRGBa.WHITE
            drawer.stroke = ColorRGBa.RED
            deltaTime = seconds - lastTime
            lastTime = seconds
            if (controls_accelerate) {
                ship.dy += 120.0*deltaTime
            }
            move(ship, width + 0.0, height + 0.0, deltaTime)
            move(asteroid, width + 0.0, height + 0.0, deltaTime)

            draw(ship, drawer)
            draw(asteroid, drawer)

            drawer.fontMap = font
            drawer.fill = ColorRGBa.WHITE
            drawer.text("Asteroids™", width / 2.0, height / 2.0)
        }

Everything is green and it draws just fine. Commit:draw function can draw either ship or asteroid based on type.

both ship and asteroid on screen

So that’s nice. Here’s our complete SpaceObject (ship and asteroid) code:

private val shipPoints = listOf(
    Vector2(-3.0, -2.0), Vector2(-3.0, 2.0), Vector2(-5.0, 4.0),
    Vector2(7.0, 0.0), Vector2(-5.0, -4.0), Vector2(-3.0, -2.0)
)

private val shipFlare = listOf(
    Vector2(-3.0,-2.0), Vector2(-7.0,0.0), Vector2(-3.0, 2.0)
)

private val asteroidPoints =
    listOf(
        Vector2(4.0, 2.0), Vector2(3.0, 0.0), Vector2(4.0, -2.0),
        Vector2(1.0, -4.0), Vector2(-2.0, -4.0), Vector2(-4.0, -2.0),
        Vector2(-4.0, 2.0), Vector2(-2.0, 4.0), Vector2(0.0, 2.0),
        Vector2(2.0, 4.0), Vector2(4.0, 2.0),
    )

enum class SpaceObjectType {
    ASTEROID, SHIP
}

val points: Map<SpaceObjectType, List<Vector2>> = mapOf(
    SpaceObjectType.ASTEROID to asteroidPoints,
    SpaceObjectType.SHIP to shipPoints
)

data class SpaceObject(
    val type: SpaceObjectType,
    var x: Double,
    var y: Double,
    var dx: Double,
    var dy: Double,
    var angle: Double = 0.0,
)

fun draw(
    spaceObject: SpaceObject,
    drawer: Drawer,
) {
    val points = points[spaceObject.type] ?: return
    drawer.isolated {
        drawer.translate(spaceObject.x, spaceObject.y)
        drawer.scale(4.00, 4.0)
        drawer.rotate(spaceObject.angle)
        drawer.stroke = ColorRGBa.WHITE
        drawer.lineStrip(points)
    }
}

fun move(spaceObject: SpaceObject, width: Double, height: Double, deltaTime: Double) {
    with (spaceObject) {
        x += dx*deltaTime
        if ( x > width ) x -= width
        if ( x < 0 ) x += width
        y += dy*deltaTime
        if ( y > height ) y -= width
        if ( y < 0 ) y += width
    }
}

That’s all there is and we can draw and move asteroids and ships. So far this is pretty small, and pretty darn easy. Very interesting.

I hope you’ll follow along, both of you.

Update

I didn’t like that I had to check the points to see if they came back so changed things like this:

enum class SpaceObjectType(val points: List<Vector2>) {
    ASTEROID(asteroidPoints),
    SHIP(shipPoints)
}

fun draw(
    spaceObject: SpaceObject,
    drawer: Drawer,
) {
    drawer.isolated {
        drawer.translate(spaceObject.x, spaceObject.y)
        drawer.scale(4.00, 4.0)
        drawer.rotate(spaceObject.angle)
        drawer.stroke = ColorRGBa.WHITE
        drawer.lineStrip(spaceObject.type.points)
    }
}

Adding the additional parameter to the enum means that we can just reference the list back from the enum instance itself. So that’s a good thing. Thanks to GeePaw Hill for the idea.

Back to whatever you were doing. See you next time!