GitHub Decentralized Repo
GitHub Centralized Repo GitHub Flat Repo

I often predict in these lines what I’ll do, and only rarely is that what actually happens. today I don’t even know what I’ll try next.

We chatted briefly last night in the Friday Geek’s Night Out Tuesday Evening Zoom Ensemble, about what might happen as I go forward with this “Procedural Asteroids” idea, where I create no classes. We spoke of how I might organize the code, with the favored idea being to keep all the functions in one big file (because it would be so horrible). I think it was Bryan who thought it would be interesting, when it’s all done, to refactor the resulting program to make it better. He seems nice but he has evil ideas.

We also discussed how many times one person can write Asteroids before they have to be incarcerated for their own protection. It’s not clear how that number correlates with the number of cats you can own before being deemed a Crazy Cat Person.

Let’s talk about style.

Style

I think we could call the style I’ll use “Everything Global”. For example, in the centralized version, we have an object Controls and the main program creates one and stuffs flags into it:

    program {
        val font = loadFont("data/fonts/default.otf", U.FONT_SIZE)
        val controls = Controls()
        val game = Game().also { it.createInitialContents(controls) }
        keyboard.keyDown.listen {
            when (it.name) {
                "d" -> {controls.left = true}
                "f" -> {controls.right = true}
                "j" -> {controls.accelerate = true}
                "k" -> {controls.fire = true}
                "space" -> {controls.hyperspace = true}
                "q" -> { game.insertQuarter(controls)}
            }
        }
        keyboard.keyUp.listen {
            when (it.name) {
                "d" -> {controls.left = false}
                "f" -> {controls.right = false}
                "j" -> {controls.accelerate = false}
                "k" -> {controls.fire = false}
                "space" -> {
                    controls.hyperspace = false
                }
            }
        }

        extend {
            drawer.fontMap = font
            game.cycle(seconds, drawer)
        }
    }
}

We also see there that the main program has an instance of Game and tells it to cycle. We’ll have none of that fancy stuff up in our program, no sir. We’ll have global variables that the main sets values into and that the program looks at. I’m sure that’s the manly way to code this thing.

Let’s do the controls right now. I’ll put the globals right in the main, I guess.

I should mention that I don’t plan to reinvent every wheel. For example, I’m going to copy-paste that listen code and then edit it to match our design, which shall soon be trademarked, copyrighted, and/or patented as “Excellent Asteroids Design™© pat.pend.”.

var controls_left: Boolean = false
var controls_right: Boolean = false
var controls_accelerate: Boolean = false
var controls_fire: Boolean = false
var controls_hyperspace: Boolean = false

        keyboard.keyDown.listen {
            when (it.name) {
                "d" -> {controls_left = true}
                "f" -> {controls_right = true}
                "j" -> {controls_accelerate = true}
                "k" -> {controls_fire = true}
                "space" -> {controls_hyperspace = true}
//                "q" -> { insertQuarter()}
            }
        }
        keyboard.keyUp.listen {
            when (it.name) {
                "d" -> {controls_left = false}
                "f" -> {controls_right = false}
                "j" -> {controls_accelerate = false}
                "k" -> {controls_fire = false}
                "space" -> {
                    controls_hyperspace = false
                }
            }
        }

I just replaced controls. with controls_, making a command decision on the kind of names we might have, and commented out the call to insertQuarter for now. I suppose this will compile and run. After a test run, I commit: controls now set useful globals like controls_left.

It would be nice to test this. Let’s do this: when I press the accelerate key, “j”, let’s adjust the velocity of the asteroid by some small amount. The acceleration I used in the other program is 120.0, so we’ll try that. It should get scaled by deltaTime of course.

I’ll just put the control code right in the main for now.

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

        drawer.circle(asteroid.x, asteroid.y, 32.0)

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

That works nicely. As I hold down the “j”, the asteroid shifts from going up diagonally to moving more and more horizontally, and if I hold the key long enough, it starts moving downward more and more steeply, faster and faster. Clearly we need to limit the asteroid’s velocity to some maximum, but this was just an experiment. Asteroids don’t really accelerate at all.

We’ll commit: acceleration key tied experimentally to asteroid y velocity.

Reflecting

Of course I’m just experimenting, starting to work out how to do this program, but I have already done some horrible things. The global variables control_XXX are defined in main. I’m sure we won’t put all our globals in main, unless we do. In order to allow the acceleration test to work, I had to make the dy in Asteroid var, so I made the dx var also. This isn’t what I’d call 21st century good practice, but we’re still just beginning to get a sense of what our objects want to be.

Moving On

Let’s draw an asteroid instead of that circle. I’ll borrow the points from the other program. In that one, an asteroid looks like this:

    listOf(
        Point(4.0, 2.0), Point(3.0, 0.0), Point(4.0, -2.0),
        Point(1.0, -4.0), Point(-2.0, -4.0), Point(-4.0, -2.0),
        Point(-4.0, 2.0), Point(-2.0, 4.0), Point(0.0, 2.0),
        Point(2.0, 4.0), Point(4.0, 2.0),
    ),

We don’t have Point in our program and we aren’t smart enough to create one. But we do know Kotlin, and it has a Pair. So we’ll use that.

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

I guess we’re smart enough to use private when we can. We’ll see how that goes. Oops! I find that OPENRNDR’s lineStrip expects Vector2 where I have Pair. I guess I have to admit to knowing that type.

I try this function:

fun drawAsteroid(asteroid: Asteroid, drawer: Drawer) {
    drawer.isolated {
        drawer.translate(asteroid.x, asteroid.y)
        drawer.scale(4.00, 4.0)
        drawer.stroke = ColorRGBa.WHITE
        drawer.lineStrip(asteroidPoints)
    }
}

And in the main:

    drawAsteroid(asteroid, drawer)

Nothing for it but to run it. It’s nearly good:

screen showing small asteroid with thick lines

The asteroid is smaller than I might have hoped, and its lines are thicker than we want, but for a first drawing, it’s not bad at all. We’ll clearly have to be dealing with setting the overall scale and strokeWidth and all that but we have an asteroid on the screen, and it’s even moving at a nearly reasonable speed.

This is good. I think we’ll stop for today.

Summary

We can draw an asteroid, albeit a bit out of scale, and we have a rudimentary ability to connect the keyboard to entities in our code. I think we’re a bit beyond “just an experiment”, though we’re far from having a good understanding of how to organize things. I do feel fairly good about having a separate Asteroid file that has the asteroid points and the two functions moveAsteroid and drawAsteroid. Maybe we’ll continue that sort of thing. We have even used a bit of private, making the asteroid points private to the Asteroid file. Pretty advanced stuff.

I think near future work should perhaps improve the scaling a bit, and that we should turn our attention to drawing the ship next, so that we can work on flying it about.

I feel better about this than I did yesterday. It might turn out to be interesting.

I hope my reader, whoever you are, will follow along. And toot me up at ronjeffries on mastodon. Or Twitter while it lives and while I’m still there.

See you next time!