I like making games but sometimes I spend a lot of time doing stuff that is just annoying. What if a language or environment were designed to make those things easy?


Snorlax Evaluation

(define-by-reference, or: lazy evaluation taken to extremes)

Okay, so I've got a Bomb entity in my game. It's already got the behavior for flying toward its destination. What if I want to make a SmartBomb that tracks a target even if they move? I might do it something like this:

SmartBomb.on_update = function (timestep) {
	// other update behaviors go in this function as well
	this.destination = target.position
}

I have to explicitly tell my engine to execute this every frame. I think in terms of this kind of thing already, because I'm a math and physics person. Why can't I define relationships like this more simply? What if, in fact, this was the default way we referred to values? It's worth noting that what I've thought of here is essentially just functional reactive programming. You can go read wikipedia if you want.

SmartBomb.destination = target.position

Any time something fetches the value of SmartBomb.destination it'll fetch the value of target.position. Writing it this way, instead of explicitly specifying which variables need to be kept up-to-date with other variables (and when they need to be updated!), means it's easier to give variables interesting dependencies on other values - like this shield which should float a certain distance between the player and the enemy:

shield.position = player.position + shield.distance * unit_vector(enemy.position - player.position)

If and when you do need to store something's value at a specific time, you can do that if you want - for example, this code when a bomb explodes:

explosion_location = current_value(bomb.position)
// or maybe the syntax is like this:
explosion_location = bomb.position.current_value

So far, everything's time-independent. For example, the shield object's position depends only on the positions of other objects.

Why not include time-dependence? A bullet has a position and a velocity, and these are related in a way that doesn't change at any point during gameplay. Why not codify that relationship in a similar way?

bullet.position = bullet.position + time_step * bullet.velocity
// or maybe
bullet.position = bullet.position + bullet.position.time_since_last_calculation * bullet.velocity

Okay, if this seems like impossible magic, I'm sorry. Let's walk through it:

  1. The global loop asks for bullet.position in order to know where it should be drawn.
  2. bullet.position looks at its cached value and sees that it's out of date.
  3. bullet.position subtracts timestamps to find the time step between now and whenever it last got calculated, and remembers this as time_step
  4. The time-dependence expression is now evaluated, using the last calculated value for bullet.position, and fetching bullet.velocity as normal.

Hell, why not remember the entire history of a variable's values?Performance. When I fetch the last calculated value for bullet.position, why not go ahead and make that a graph-crawling reference-calculation just like everything else?Reactive time-dependence is something to think about for an interesting mechanic, if nothing else - keeping track of dependencies on history means you could change a value at any point throughout the game's history and see how it changes things... Just like Achron! And the flaws that make it seem infeasible here wouldn't be a problem in, say, a turn-based time game.

Performance is a legitimate worry at that point. If you include the full-on time-dependence, then depending on implementation you might end up following a reference back through every frame the game's been through. This approach would obviously fall apart because every value in your world is going to be at least O(t) to calculate, which sucks - there should be no time-dependent complexity - but there are solutions:

Besides which, we have fast computers, and without running into hard algorithmic complexity problems (collision detection, I'm lookin' at you!) it's not that easy to overload a modern gaming machine. And hey, maybe having declarative time-dependent values is a pipe dream - I don't care. The core idea is still awesome.

Here's an example (with more attention paid to data types) of how I'd come up with a vector that always points to a set offset from the position of bullet.

bullet.position = Wrapper(v(1,2))
offset = v(3,4)
endpoint = bullet.position + offset
print endpoint
--> WrapperSum(bullet.position, offset)
print endpoint.current_value
--> v(4,6)
bullet.position.current_value = v(-10,0)
print endpoint.current_value
--> v(-7,4)

Once I've started thinking and talking about this, I start wondering how much we need imperative game loops at all, for anything other than those troublesome time dependencies and feedback loops. I wonder...

print bullet.contacts
--> Wrapper(List<Contacts>)
contact_acceleration = sum(contact.force_to_resolve for contact in bullet.contacts)
bullet.acceleration = contact_acceleration

Assuming that force due to contacts is the only thing present in the world, then the bullet now feels enough acceleration to resolve all the conflicts. I mean, assuming contacts are simple to work with, and they always are. Right? Right?

I'm thinking about this a lot because this kind of referencing is very natural for things like defining interlinks between components in my game engine - I've got conflicting interests:

The solution I've been working with is wrap together sets of related data (as components). The player object has a PositionComponent and a CollisionComponent, and the CollisionComponent stores a reference to the player's PositionComponent so that it can access it at any time. Actually holding those references constant means that there would be problems if the player.position_component suddenly changed to a different object - but that should never happen. I'm satisfied with this for the reasons I implemented it in the first place, but it doesn't get me the dynamic interlinking I'd like from something more like functional reactive programming.

So I don't know. I would love to have the capacity for functional reactive programming in my game engines, at the least! It's so useful for making bulletproof dynamic interactive systems.


Time Integration

I find myself writing cooldowns and countdowns all the time. Bullets can only fire so fast, bombs explode after five seconds, enemies spawn every 10-15 seconds. Every time I do it, I write the same boilerplate in the initialization:

goomba.cooldown_max = 5 // 5 seconds to cool down
goomba.cooldown_timer = goomba.cooldown_max

and the same boilerplate in the update function:

if goomba.cooldown_timer > 0:
	goomba.cooldown_timer -= time_step
if goomba.cooldown_timer < 0:
	// insert end behavior here

and somewhere, I activate this behavior (in response to a keypress, or proximit, or contact):

goomba.cooldown_timer = goomba.cooldown_max

I do this kind of thing so often that I feel like it should be one of the basic functionalities offered when I'm trying to describe game entity behavior.

// initialization
goomba.shot_timer = new CooldownTimer(length=5s, on_end=function() {...})
// update functionality should be taken care of automatically, but if not:
goomba.shot_timer.step(time_step)
// activating the timer:
goomba.shot_timer.start()

Now, wasn't that easy?

Of course, that's a pretty simple case! There are obvious extensions, though:

goomba.blink_timer = new EndlessTimer()
goomba.blink_timer.interval = 5s
goomba.blink_timer.at_progress(0%, goomba.change_color) // change color at the start of each interval
goomba.blink_timer.at_progress(50%, goomba.take_step) // take a step once every interval halfway through

And now making timed behavior for our goomba is easy! We can even speed up the goomba easily: goomba.blink_timer.interval = 3s

Ideally, you don't need to even specify when to step your timers - that should be taken care of automatically. Of course, that means we'd want to register these timers instead of simply declaring them as a member - but maybe that's a matter of a grander architectural problem.

Although! If we do let the developer take care of stepping the timers, then we can also use it for things that aren't time-dependent:

player.limit_bar = new Meter(length=100) // general verison of the CooldownTimer
player.on('hit by enemy', player.limit_bar.step)
player.limit_bar.at_progress(100%, player.limit_break)

And now we've got a way for limit breaks to engage. Assuming there's a pre-existing event system and everything is architected carefully, it's now incredibly easy to fill meters in response to events.

Easy Peasy State Machinesy

Why do I always have to actually use boolean flags (manually setting and unsetting them) to track the states of my entities?

player.shielding = new Toggle()
keyboard.on('w pressed', player.shielding.next)

I'm just throwing this syntax together off the top of my head. The syntax isn't important - what's important is that I should be able to describe this and have it happen.

strobelight.color = new Sequence(['red', 'green', 'blue'])
strobelight.update = function(dt) {
	strobelight.color.next()
	strobelight.draw()
}

Seriously, don't worry about this syntax - it's ugly and I just threw it together. The point is the kinds of things I should be able to do. This is all totally feasible.

goomba.ai = new StateMachine(['sleep', 'suspicious', 'alert'])
goomba.ai['sleep'].transitions_to['suspicious'].on(goomba.near(player))
// the state 'sleep' should transition to 'suspicious' on the event goomba.near(player)
goomba.ai['sleep'].update = goomba.draw_snores

goomba.ai['suspicious'].transitions_to['alert'].on(goomba.sees(player))
goomba.ai['suspicious'].update = goomba.search_for_player

// shit we forgot to make a state for just patrolling
goomba.ai.add_state('patrol')
goomba.ai['patrol'].transitions_to['sleep'].after(5 seconds)
goomba.ai['patrol'].update = goomba.wander

// oh hey, she should occasionally wake up to patrol!
goomba.ai['sleep'].transitions_to['patrol'].after(5-10 seconds)

So y'know. State machines are powerful. In order to really use them, you need a robust event system, or else they're a huge pain - it's still possible, you just... need to code it to check the conditions on each update loop. That could get expensive!

For the example of checking goomba.near(player) - which has an optional argument for the distance - that function checks if there's already a collision detection object attached to goomba for setting off proximity events. If there is, it returns the event handle, which can be used for binding behaviors to the firing of that event. If a proximity collider of the right size doesn't exist, then it creates one, and returns the event handle.