Day 3: User Functions and Variables
We have a working robot now. In fact, if you did some tinkering over the weekend, you should have an army of robots ready to fight. These last two lessons will be about improving and tuning your robots to make them stronger, faster, better.
User Variables
Each robot is equipped with 20 value registers (variables) which can be used to store numerical values. For those of you who have done 3DGameStudio programming, these variables are similar to the skills that each entity has. The variables can be accessed by the name value1, value2, value3, on up to value20. And they can be used in normal equations like so:
value1 = value2 + value3;You can use three consecutive user variables for vectors:
// value1 = robot.x, value2 = robot.y, value3 = robot.zNote: In the above case we are only interested in the value returned in value4 but, since vec_to_angle takes two vectors, value5 and value6 are overwritten in the process.
These value variables are also useful for storing values during consecutive frames. For instance, lets say we want our rocket robot to approach our target, fire 3 shots, run away for 10 seconds, and then repeat the cycle:
function AI_ROBOT()Wow, that is the most complicated script we have covered so far! And I left out the longer bits of code like aiming and moving. But don’t worry, I will cover these bits of code next.
Local Functions
It would be trivial to use what we already learned on day one and two to fill in those empty pieces of code (moving and aiming) but this would make this one function extremely long and hard to debug. To solve this we are going to use the second topic for today, local functions.
Note: Those of you familiar with 3DGS or other programming functions will immediately recongnize local functions as just normal functions. The only restriction is that you only get eight local functions and they must be named either FUNC_ONE, FUNC_TWO, FUNC_THREE, on up to FUNC_EIGHT.
Local functions work in a similar manner as or main AI function (AI_ROBOT), except we can call this function ourselves.
For example, let’s say we want a function for moving towards our target. This is something we do a lot, so it might be nice to have a separate function to handle it:
function FUNC_ONE()
This looks a lot like the AI_ROBOT movement code from day 1. The only real difference is that it is its own function and can be called at any time from the main AI function:
function AI_ROBOT()
{
// call movement code
FUNC_ONE();
}
We can add more functions as well for firing and hiding. We can mix and match functions, and even have functions call other functions as well.
NOTE: If you call a local function from inside another function make sure that you don’t create a loop:
function FUNC_ONE()
{
FUNC_TWO(); // call function two
}
function FUNC_TWO()
{
FUNC_ONE(); // call function one
}
function AI_ROBOT()
{
FUNC_ONE(); // call function one
}
The code above will cause an endless loop, making your robot crash.
Putting it all Together (Local Functions and User Variables)
Something that is very useful in most programming endeavors is code reuse. In our original example, we have a section for running towards our target and another for running away. Each of these could be done as a separate local function, but there are only two lines of code that we would have to change:
if(angle_to_target > 5) // target to the left…with
if(ang(angle_to_target+180) > 5) // target to the left…
-and-
if(angle_to_target < -5) // target to the right…
with
if(ang(angle_to_target+180) < -5) // target to the right…
It would be a waste of time to create two 26 lines functions when only two lines would need to be changed. What’s worse, if we wanted to turn to a 90o angle from our target we would have to write a whole new function (and another if we wanted to do –90o). What we need is a way to use an arbitrate value inside a local function. That way we could set the value to whatever heading we want and the robot will just turn in that direction.
User variables are ‘local’ to the robot, but all functions inside that robot can use and modify them. We can use this feature to pass values to and from local robot functions:
// movement functionNow, all we need to do is set value7 to whatever value we want to turn to before we call this function:
// approach targetNot only does this save us from writing several functions that do roughly the same thing, it helps us when we tune and debug our robots. For instance, if you plan on using the Cannon robot you might want to increase the turning forces otherwise you will have a hard time tracking your targets. If we did three separate functions to handle movement we would have to make three times as many changes. With function reuse we only need to make one.
Since we can modify values inside local functions as well as read them, we can use them to pass values back to the main function as well:
// aim at target
This function aims the robot at the target. If the robot is already facing the target, we check to see if we have a clear shot and if our main gun is loaded. If so, we set value6 to 1, otherwise we set value6 to 0. We can then use this value elsewhere in our code to see if we should "take the shot":
// aim robot
Conclusion
In the last 4 days I have exposed all the mysteries of Robot AI programming. Really. That’s it! There is only one thing missing, determination. Programming a robot requires a bit of creativity, logic, and lots patients.
But don’t panic! Remember that this is only a game. Your robots will break down in the arena, they will bounce into walls, they might even shoot themselves. This is normal. Finding the cause of these idiosyncrasies is just part of the challenge. In the next lesson I will show you some tricks to make debugging your robots easier.
Below I’ve included the entire "Fire and Fade" script. Before you get too excited, this robot as is does very poorly against all its opponents. I present it here as a working example only. I’m sure somebody can adopt this poor robot and tune it into a killing machine. ;)
// Name: Doug Poston