Kotlin 245 - Object Type
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.
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!