In Frogatto, we use Frogatto Markup Language (FML) to describe most game data — how levels are laid out, rules for laying out tiles, for defining animations for objects and so forth. For those of you familiar with Wesnoth and WML — or those familiar with XML — FML will seem very familiar. It’s a simple data language, like XML. Here is a small snippet of FML which defines one of Frogatto’s animations, to show you what I mean:
FML, like WML, works very nicely for describing data. However, in Frogatto we really wanted objects to have very flexible behavior — for instance, consider all the complexities of an object as simple as an ant in Frogatto. It has to walk back and forth, fall off cliffs for a red ant, but turn back for a black ant. Then when it is spat out it has to fly through the air, bounce off surfaces, finally ending up on its back and then spring back onto its front.
Describing behavior like this in FML would be far too awkward, at least without hard wiring a lot of core behavior into the engine, and we really wanted Frogatto’s engine to be a very general engine.
So, in addition to FML, Frogatto has an embedded language called Frogatto Formula Language (FFL). FFL is designed to work like a mathematical formula. That is, a formula has inputs, makes calculations, and then results in outputs. A formula itself can’t actually do anything — that is, it can’t modify the environment around it — it can only return a result. Just like a mathematical formula.
Let’s jump into an example of how this works in Frogatto. You know those big metal balls that are thrown by the psychotic bunnies? Here is a (slightly simplified) code snippet which controls what happens when they collide with a wall:
This is taken from the metal_ball.cfg file which defines the metal balls. Now, the first thing I want to note is that the on_collide=”…” part that surrounds this is FML. ONLY the part that is inside the quotation marks is a formula; a formula written in FFL. This defines the event handler that is run whenever the object receives a “collide” event.
What the formula is designed to do is cause the ball to be reflected off the wall, but its velocity dampened by half. One important thing to understand is that the formula itself does not modify the object’s velocity: a formula itself cannot modify its environment. Rather, the formula is evaluated and returns a command object. The game engine executes the command that the formula returns. In technical terms, this makes FFL a pure functional language.
There is an entire API of functions that are exposed to the formula system that will create different commands to return to the engine. The set() function, which creates a command to set the value of an object property, is probably the most useful, however there are a whole host of functions such as spawn(), which spawns a new object (useful, for instance, for an object which shoots projectiles), a teleport() function which allows teleporting the player to a different level, and so forth. The game engine actually comes with a utility that outputs all of the available functions.
Of course, in addition to functions that can create commands, to do useful things we need to be able to inspect the state of the game. For instance, in the above formula we had to read the object’s “velocity_x” property to find its horizontal velocity. When an event handling formula is executed, it gains access to all of the properties of the object receiving the event. Objects are pretty complex entities, and they have properties ranging from their velocity and location, to their hitpoints, current animation, a store of user-defined variables, the level they are located in — from where you can access all the other objects in the level — and so forth. Fortunately we have documented all the available object properties.
Now that we’ve seen the basics of formulas, let’s look at a slightly more complex example. This is how the Frogatto object handles collision with a wall:
if((animation in ['jump', 'fall']) and can_slide(self) and abs(velocity_x) > 200,
[animation('slide'), set(velocity_y, 0)])]"
First things first: this formula returns a list of commands to execute rather than a single command. Lists in FFL are delimited by [ and ]. The first thing this formula does is simply sets Frogatto’s horizontal velocity to 0 when he collides with a wall — if you run into a wall with Frogatto he simply stops rather than bouncing off.
The second part of the formula is to handle Frogatto’s ability to grab onto walls if you jump into them. It makes use of the if() function which is a built-in FFL function. if(cond, x, y) will evaluate to x if cond is true, and y otherwise. You can also use simply if(cond, x) which results to null if cond is false.
We can see that Frogatto will only cling onto a wall under certain conditions — if he’s in a jumping or falling animation, if a custom function defined as “can_slide” is true, and if his horizontal velocity has a magnitude greater than 200. If the criteria to cling onto the wall is met, we will return a command for him to enter his slide animation, and a command to set his vertical velocity to 0.
As we can see, FFL is fairly powerful, but hopefully not too difficult to understand. Using it, we can define objects which have all kinds of sophisticated behavior. Of course, another key element is understanding what events are triggered on an object. We have events that are triggered automatically, periodically, events that are triggered upon collisions of different types, events triggered when the level is loaded, when the player first approaches an object, and so forth. All the events are documented here.
I’ll give one more example of the kinds of things FFL can do:
on_timer="[set(brightness, brightness*4), schedule(5, set(brightness, brightness))]"
This makes it so a ‘timer’ event is received by the object every second. (Frogatto runs at 50FPS). On this timer event, the object is made to be four times as bright as normal for a tenth of a second (five cycles). Note that use of the schedule() function. What schedule does is takes commands and schedules them to be run some number of cycles in the future, rather than right now. So, we are setting brightness to 4x what is it now, and then scheduling it to be set back to its current value 5 cycles in the future.
Note how the entire formula is evaluated, and THEN the commands are evaluated.
FFL is a general formula language, and though its main use in Frogatto is for object events, that’s not its only use. It is also used, for instance, to draw the HUD which shows Frogatto’s status, health, coins, score, and so on. In that context, FFL is still used but it has access to a different set of properties and a different API — an API designed for drawing elements of the screen rather than controlling objects. It’s also used to allow custom scripts that can be used in the Frogatto editor. One can write a script in FFL that applies some effect or another on a level — such as rounding out a cave level, or adding trees to a grassy level. One could even write FFL to try to make an automatic level generator!
I’m hoping this gives a good overall view of FFL, and what it can do. The Frogatto engine is very moddable, and we’re hoping people will try to develop their own concepts — from fixing bugs in Frogatto, to adding new features, such as new objects or better behavior of existing objects. But one could also implement a completely new and different game. We very much welcome people trying to develop their own game concepts with FFL — if you do please come and share them with us on the forum. Good exercises would be to implement a game in the vein of Pong or Space Invaders of Pac Man.