Facebook Twitter YouTube Frictional Games | Forum | Privacy Policy | Dev Blog | Dev Wiki | Support | Gametee


Thread Rating:
  • 3 Vote(s) - 5 Average
  • 1
  • 2
  • 3
  • 4
  • 5
[Tutorial] Using funcdefs
Apjjm Offline
Is easy to say

Posts: 496
Threads: 18
Joined: Apr 2011
Reputation: 52
#1
[Tutorial] Using funcdefs

So, I haven't noticed many scripts using funcdef, (i'm not sure they are even highlighted in the normal notepad++ hps lang settings). This tutorial explains how to set up and use funcdefs & function pointers. The first two parts of this tutorial cover some very basic examples on use, with the last mixing arrays and function pointers to handle random function calls.

The tutorial is now on the wiki: funcdef. The original tutorial is in spoiler tags below.
Spoiler below!

Introduction to funcdef (Function Definition)

Angelscript Documentation Wrote:A function pointer is a data type that can be dynamically set to point to a global function that has a matching function signature as that defined by the variable declaration.
In other words, you're making a "type" (something like int or string), but instead of this type's variables containing text (string) or numbers (int), they contain functions - or more specifically, a pointer to a function! Let us consider the following example:
funcdef void fdSimpleFunction();
This little snippet defines a "type" which can contain any function which takes no arguments, and returns no parameters. The following is an example of such a function:
void sfHelloWorld() {
//Output hello world
AddDebugMessage("Hello World!",false);
}
All that matters when wanting to store that function in a function pointer defined with "fdSimpleFunction" is simply that it's return type is void, and that it takes no arguments. We have a funcdef, and a function which matches the funcdef signature. Time to actually use this funcdef to make a variable to point to our function.
fdSimpleFunction@ functionVar;
This is similar to just normal variable creation, except the "@" sign - The "@" symbol in angelscript literally means "Handle of" / "Address of", and is just stating that our variable is a pointer. A pointer, as the name suggests points to something - it contains the location of what you are looking for as opposed to what you are actually looking for; consider opening a box, but instead of what you want, the box contains a note telling you to look in a specific different box for what you want. In this case, what we are looking for is a function.

Now we have a variable, which is a function pointer. However, currently, it doesn't point anywhere (it literally "is null"). The aim is to get it to point to the function "sfHelloWorld". Again, this is very similar to what is done with normal variables, except that "@" symbol appears again:
//We know this line declares our functionVar
fdSimpleFunction@ functionVar;
//We now will make this point to sfHelloWorld()!
@functionVar = @sfHelloWorld;
This new line is making "functionVar" point to the address of sfHelloWorld (in other words, we just put a note in our box saying look into the specific other box for sfHelloWorld). You are actually saying "set the handle of function var to the handle of sfHelloWorld" - this means "functionVar" can be called just like the function!
//If all has gone well:
functionVar();
//Does exactly the same as:
sfHelloWorld();

The full code for part 1:
//Make the type "fdSimpleFunction"
funcdef void fdSimpleFunction();

//Output hello world
void sfHelloWorld() {
AddDebugMessage("Hello World!",false);
}

void OnStart() {

//Call the function normally!
AddDebugMessage("Calling the function normally!",false);
sfHelloWorld();

//Call the function using our variable!
AddDebugMessage("Calling the function using the pointer!",false);
fdSimpleFunction@ functionVar;
@functionVar = @sfHelloWorld;
functionVar();
}
So why bother? Why go through all this hassle, to just call a function we could have called anyway?

Solving a problem with funcdef

This following example is a little contrived and could be solved a better way, but obviously the function pointer approach is chosen for the purposes of this tutorial Wink. The idea is that if a function pointer can vary - we can make one function do different things by changing what other functions it calls. Let's say we have two functions, as follows:
void bigFunction()
{
   subFunction();
}

void subFunction()
{
}
At the end of bigFunction we want to call a function called "output1". However, subFunction should have a random chance of making bigFunction call "output2" instead:
void output1() {
AddDebugMessage("Yo!",false);
}
void output2() {
AddDebugMessage("Dawg!",false);
}

We can do that using a function pointer. This is our code so far:
//Create a function definition (which is actually the same as fdSimpleFunction)
//Which will matches the signature of both our output functions
funcdef void fdOutput();

//Our output functions
void output1() {
AddDebugMessage("Yo!",false);
}
void output2() {
AddDebugMessage("Dawg!",false);
}

//The actual stuff that does the descision making
void bigFunction()
{
   subFunction();
}

void subFunction()
{

}
We next add our variable, in this case we shall imaginatively call it "outputChoice". We shall initially make it point to output1 inside bigFunction, and call the function "outputChoice" points to at the end.
fdOutput @outputChoice;
void bigFunction() {
@outputChoice = @output1;
subFunction();
outputChoice();
}
Meanwhile, our subfunction will have a 1 in 4 chance of replacing which function it points to with output2 instead!
void subFunction()
{
   if(RandInt(0,3)==1)
    {
      //A 1 in 4 chance of this code being reached
      @outputChoice = @output2;
    }
}

Giving us the following final code:
//Create a function definition (which is actually the same as fdSimpleFunction)
//Which will matches the signature of both our output functions
funcdef void fdOutput();

//Our output functions
void output1() {
AddDebugMessage("Yo!",false);
}
void output2() {
AddDebugMessage("Dawg!",false);
}

//The actual stuff that does the descision making
fdOutput @outputChoice;
void bigFunction() {
  //Initially point to output 1
  @outputChoice = @output1;
  //1 in 4 of output 2...
  subFunction();
  //Call whichever output has been chosen
  outputChoice();
}

void subFunction()
{
   if(RandInt(0,3)==1)
    {
      //A 1 in 4 chance of this code being reached
      @outputChoice = @output2;
    }
}

void OnStart() {
//Call bigFunction 20 times - 1/4 chance of output2?
for(int i=0; i<20; i++) bigFunction();
}

That's all well and good; but that isn't useful except in certain not-so-common circumstances when it comes to amnesia scripting. So how can it help out in more "normal" scripts?

Arrays, funcdef and you

We shall extend the above problem to have an arbitrary number of functions, and still manage calling one at random. Though each function will have the same probability of being called this time to make things easier. Obviously we could do a massive set of ifs, or a huge switch-case set (what if there are 100 choices? who would want to write that out that switch-case?). We shall re-use some of our code from the first section, however, this time there are two new signature matching functions:
//This creates a signature called "SimpleFunction"
//Which matches functions which take no arguments, and return nothing.
funcdef void fdSimpleFunction();

//Such as these example functions
void sfHelloWorld() {
//Output hello world
AddDebugMessage("Hello World!",false);
}
void sfDisplayTimesCalled() {
    //This function will output how many times it has been called
    AddLocalVarInt("DTC_TimesCalled",1);
    AddDebugMessage("DisplayTimesCalled, called: " +
                    GetLocalVarInt("DTC_TimesCalled") + " times", false);
}
void sfPlayScarySound() {
    //Play the sound of an angry brute!
    PlayGuiSound("enemy\\brute\\notice.snt",1.0f);
}
The problem is going to be solved by making an array, each index is going to contain one of the above functions. We just then pick a random index - It scales to any size too!

This is how to create an array of the function pointers:
fdSimpleFunction@[] simpleFunctions = { @sfHelloWorld, @sfDisplayTimesCalled, @sfPlayScarySound };
If you are not familiar with angelscript arrays, check out the documentation, or the wiki. Now all is left to do is create a function that picks an index at random, and calls the function at that index:
void callRandomSimpleFunction() {
    //Pick a random index from the array
    uint index = RandInt(0,simpleFunctions.length()-1);
    //Select that function
    fdSimpleFunction @functionToCall = simpleFunctions[index];
    //Call that function
    functionToCall();
        //Note this can be simplified down to one line:
    //simpleFunctions[RandInt(0,simpleFunctions.length()-1)]();
}

If we also wanted to add a function that called each script, one per second, we could do that too:
void callEachFunction(string &in asTimerName) {
//Get the index
uint index = GetLocalVarInt("simpleFunctionIndex");
//Don't go any further if we have called all the functions
if(index >= simpleFunctions.length()) return;
//Access, and call like before:
fdSimpleFunction @functionToCall = simpleFunctions[index];
functionToCall();
//Increment the index
AddLocalVarInt("simpleFunctionIndex",1);
//Call this function again in 1 second
AddTimer(asTimerName,1.0f,"callEachFunction");
}

Below is a sample hps implementing all of the above - don't forget to visit the OnStart routine and uncomment one of the two lines - or you won't see anything!
//This creates a signature called "SimpleFunction"
//Which matches functions which take no arguments, and return nothing.
funcdef void fdSimpleFunction();

//Such as these example functions
void sfHelloWorld() {
//Output hello world
AddDebugMessage("Hello World!",false);
}
void sfDisplayTimesCalled() {
    //This function will output how many times it has been called
    AddLocalVarInt("DTC_TimesCalled",1);
    AddDebugMessage("DisplayTimesCalled, called: " +
                    GetLocalVarInt("DTC_TimesCalled") + " times", false);
}
void sfPlayScarySound() {
    //Play the sound of an angry brute!
    PlayGuiSound("enemy\\brute\\notice.snt",1.0f);
}

//We now can create an array of these simple functions for further use.
//Note that fdSimpleFunction@ is like a type now - like string, or int!
fdSimpleFunction@[] simpleFunctions = { @sfHelloWorld, @sfDisplayTimesCalled, @sfPlayScarySound };

//Some example uses of this:

//Calling a random function
void callRandomSimpleFunction() {
    //Pick a random index from the array
    uint index = RandInt(0,simpleFunctions.length()-1);
    //Select that function
    fdSimpleFunction @functionToCall = simpleFunctions[index];
    //Call that function
    functionToCall();
    //Note this can be simplified down to one line:
    //simpleFunctions[RandInt(0,simpleFunctions.length()-1)]();
}

//Using a timer to call one function per second in sequence.
void callEachFunction(string &in asTimerName) {
//Get the index
uint index = GetLocalVarInt("simpleFunctionIndex");
//Don't go any further if we have called all the functions
if(index >= simpleFunctions.length()) return;
//Access, and call like before:
fdSimpleFunction @functionToCall = simpleFunctions[index];
functionToCall();
//Increment the index
AddLocalVarInt("simpleFunctionIndex",1);
//Call this function again in 1 second
AddTimer(asTimerName,1.0f,"callEachFunction");
}

//Using a timer to repeatedly call a random function
void callRandTimer(string &in asTimerName) {
callRandomSimpleFunction();
AddTimer(asTimerName,1.0f,"callRandTimer");
}

void OnStart() {
//Uncomment one of the following to test it out!
//callEachFunction("testTimer1");
//callRandTimer("testTimer2");
}

You should now understand a little about funcdefs. Have a play around, you can use this to make sequences, callbacks, and all sorts of fun stuff. Also, don't forget to check out the documentation on the matter, a little example showing how to check if a function pointer doesn't actually point to anything.

This is my first tutorial. So please leave feedback so I can improve it. Thanks for reading, i hope i was of assistance.


Edit: Grammar, accidentally putting "fundef" in a number of places, and refining parts Smile
(This post was last modified: 08-14-2011, 12:46 AM by Apjjm.)
08-12-2011, 09:57 PM
Find
DRedshot Offline
Senior Member

Posts: 374
Threads: 23
Joined: Jun 2011
Reputation: 11
#2
RE: [Tutorial] Using funcdefs

Thanks a lot for taking the time to write this tutorial. It's really useful for people like me, who has barely touched any programming languages, but has learned almost everything there is to learn about angelscript, and wants to know more! I think i understand most of it, but it will take a few more readthroughs and a couple of google searches to fully understand arrays. keep doing things like this!

08-12-2011, 11:59 PM
Find
Endlvl Offline
Junior Member

Posts: 28
Threads: 4
Joined: Aug 2011
Reputation: 0
#3
RE: [Tutorial] Using funcdefs

Of course I use custom functions but I dnt ever start with functdef. My question is y u can't append data types to the standard functions such as adding an Int to the timer function and also since I get fairly confused with engine exe c++ on the blog will hPl3 have stuff like object.xpos or thing.active (if I'm not comPletdly lost thOse are method right?)

And thx for tut even with obj handels wow
(This post was last modified: 08-13-2011, 04:40 AM by Endlvl.)
08-13-2011, 04:39 AM
Find
Your Computer Offline
SCAN ME!

Posts: 3,456
Threads: 32
Joined: Jul 2011
Reputation: 235
#4
RE: [Tutorial] Using funcdefs

(08-13-2011, 04:39 AM)Endlvl Wrote: Of course I use custom functions but I dnt ever start with functdef. My question is y u can't append data types to the standard functions such as adding an Int to the timer function and also since I get fairly confused with engine exe c++ on the blog will hPl3 have stuff like object.xpos or thing.active (if I'm not comPletdly lost thOse are method right?)

AngelScript supports function overloading (i.e. declaring a function with the same name but with differing parameters and (or) return types), so it is possible, for example, to have the AddTimer function return an int.

Methods are functions within a class. As you should know, functions require starting and ending parentheses. Without the parentheses the parser will assume that you are trying to access a variable within an object.

Tutorials: From Noob to Pro
(This post was last modified: 08-13-2011, 05:22 AM by Your Computer.)
08-13-2011, 05:21 AM
Website Find
plutomaniac Offline
Super Moderator

Posts: 6,368
Threads: 45
Joined: May 2011
Reputation: 183
#5
RE: [Tutorial] Using funcdefs

Amazing, plane amazing. Very nice work. Please add it at the WIKI too, so that the credit will go to you:

http://www.wiki.frictionalgames.com
(This post was last modified: 08-13-2011, 08:50 AM by plutomaniac.)
08-13-2011, 08:49 AM
Find
Apjjm Offline
Is easy to say

Posts: 496
Threads: 18
Joined: Apr 2011
Reputation: 52
#6
RE: [Tutorial] Using funcdefs

(08-13-2011, 08:49 AM)plutomaniac Wrote: Amazing, plane amazing. Very nice work. Please add it at the WIKI too, so that the credit will go to you:

http://www.wiki.frictionalgames.com

Took a while, but Done. Smile
(This post was last modified: 08-14-2011, 01:16 AM by Apjjm.)
08-14-2011, 12:43 AM
Find
palistov Offline
Posting Freak

Posts: 1,208
Threads: 67
Joined: Mar 2011
Reputation: 57
#7
RE: [Tutorial] Using funcdefs

Took me quite a few read-throughs but I'm starting to understand funcdefs and where they might be useful choices in coding.

I wrote out a 250-line script which will let the player use a crowbar on any door they see, and the crux of the script is a large switch-case function executed based on a step (which is determined by whether the door is open or whether its weak enough to break with the crowbar, etc). The whole thing is a mess to read since all the outcomes are mashed into a single function. I'm thinking I might do a design overhaul and use funcdefs now...that might even be easier seeing as I can expand it to include other tools like a hammer and chipper or whatever other tool there might be. I'm not sure if it would shorten the script but I think it would certainly make it easier to dissect and read for other people...

I hope you do more of these in the future, it's always a pleasure to learn more about programming Smile

10-31-2011, 01:54 AM
Find




Users browsing this thread: 1 Guest(s)