| Both sides previous revision Previous revision Next revision | Previous revision | ||
|
hpl2:tutorials:script:funcdef [2011/08/14 10:17] thegreatcthulhu God damn editor, allways something... |
hpl2:tutorials:script:funcdef [2011/08/15 01:09] (current) apjjm [Solving a basic problem with function pointers] |
||
|---|---|---|---|
| Line 8: | Line 8: | ||
| - | The [[http://www.angelcode.com/angelscript/sdk/docs/manual/doc_datatypes_funcptr.html|Angelscript documentation]] states that //"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// or //numbers//, it contains functions - or more specifically, pointers to functions . \\ To understand better what a function pointer is: Consider a box, and this box contains a note telling you to look in another box somewhere else - this is your function pointer, it just contains information on where to look. The other box, the one //referenced, // contains the actual functional information. We can use these "extra boxes" to create variables that can be called just like functions. | + | The [[http://www.angelcode.com/angelscript/sdk/docs/manual/doc_datatypes_funcptr.html|Angelscript documentation]] states that //"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// or //numbers//, it contains functions - or more specifically, pointers to functions . \\ To understand better what a function pointer is: Consider a box, and this box contains a note telling you to look in another box somewhere else - this is your function pointer, it just contains information on where to look. The other box, the one //referenced, // contains the actual functional information. We can use these "extra boxes" to create variables that can be called just like functions. |
| Line 17: | Line 17: | ||
| funcdef void fdSimpleFunction(); // Used for pointers to functions which take no arguments and return no vales | funcdef void fdSimpleFunction(); // Used for pointers to functions which take no arguments and return no vales | ||
| funcdef int fdReturningFunction(); // Used for pointers to functions which return an int and take no arguments. | funcdef int fdReturningFunction(); // Used for pointers to functions which return an int and take no arguments. | ||
| - | funcdef int fdComplexFunction(int,int); // Used for pointers to functions which of return type int and take two ints as arguments | + | funcdef int fdComplexFunction(int, int); // Used for pointers to functions which of return type int and take two ints as arguments |
| </code> | </code> | ||
| Line 25: | Line 25: | ||
| <code cpp>void sfHelloWorld() { | <code cpp>void sfHelloWorld() { | ||
| - | AddDebugMessage("Hello World!",false); // Outputs: Hello World! | + | AddDebugMessage("Hello World!", false); // Outputs: Hello World! |
| - | } | + | } |
| </code> | </code> | ||
| Line 34: | Line 34: | ||
| <code cpp> | <code cpp> | ||
| - | fdSimpleFunction@ functionVar; | + | fdSimpleFunction@ functionVar; |
| </code> | </code> | ||
| Line 41: | Line 41: | ||
| <code cpp>// We know this line declares our functionVar | <code cpp>// We know this line declares our functionVar | ||
| fdSimpleFunction@ functionVar; | fdSimpleFunction@ functionVar; | ||
| - | | + | |
| // We now will make this point to sfHelloWorld()! | // We now will make this point to sfHelloWorld()! | ||
| - | @functionVar = @sfHelloWorld; | + | @functionVar = @sfHelloWorld; |
| </code> | </code> | ||
| - | 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 is why //functionVar // can be called just like the function, even though it is actually a variable: | + | 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 is why //functionVar // can be called just like the function, even though it is actually a variable: |
| <code cpp>// If all has gone well: | <code cpp>// If all has gone well: | ||
| functionVar(); | functionVar(); | ||
| // Does exactly the same as: | // Does exactly the same as: | ||
| - | sfHelloWorld(); | + | sfHelloWorld(); |
| </code> | </code> | ||
| Line 61: | Line 61: | ||
| <code cpp>// Make the type "fdSimpleFunction" | <code cpp>// Make the type "fdSimpleFunction" | ||
| funcdef void fdSimpleFunction(); | funcdef void fdSimpleFunction(); | ||
| - | | + | |
| // Output hello world | // Output hello world | ||
| void sfHelloWorld() { | void sfHelloWorld() { | ||
| - | AddDebugMessage("Hello World!",false); | + | AddDebugMessage("Hello World!", false); |
| } | } | ||
| - | | + | |
| void OnStart() { | void OnStart() { | ||
| // Call the function normally! | // Call the function normally! | ||
| - | AddDebugMessage("Calling the function normally!",false); | + | AddDebugMessage("Calling the function normally!", false); |
| sfHelloWorld(); | sfHelloWorld(); | ||
| - | | + | |
| // Call the function using our variable! | // Call the function using our variable! | ||
| - | AddDebugMessage("Calling the function using the pointer!",false); | + | AddDebugMessage("Calling the function using the pointer!", false); |
| fdSimpleFunction@ functionVar; | fdSimpleFunction@ functionVar; | ||
| @functionVar = @sfHelloWorld; | @functionVar = @sfHelloWorld; | ||
| functionVar(); | functionVar(); | ||
| - | } | + | } |
| </code> | </code> | ||
| So far, all we have managed to achieve is something we could have done already! Why go through all this hassle, to just call a function we could have called anyway? The next section shows how the variable aspect of the function can be exploited to solve a basic problem. | So far, all we have managed to achieve is something we could have done already! Why go through all this hassle, to just call a function we could have called anyway? The next section shows how the variable aspect of the function can be exploited to solve a basic problem. | ||
| + | |||
| ==== Solving a basic problem with function pointers ==== | ==== Solving a basic problem with function pointers ==== | ||
| Line 93: | Line 94: | ||
| } | } | ||
| void subFunction() { | void subFunction() { | ||
| - | // do something... | + | //Implementing later... |
| - | } | + | } |
| </code> | </code> | ||
| Line 100: | Line 101: | ||
| The specification is as follows: At the end of //bigFunction// we want to call a function called //output1//. However, //subFunction// should have a random (1/4) chance of making //bigFunction// call //output2// instead. Both the output functions are defined below: | The specification is as follows: At the end of //bigFunction// we want to call a function called //output1//. However, //subFunction// should have a random (1/4) chance of making //bigFunction// call //output2// instead. Both the output functions are defined below: | ||
| <code cpp>void output1() { | <code cpp>void output1() { | ||
| - | AddDebugMessage("Yo!",false); | + | AddDebugMessage("Yo!", false); |
| } | } | ||
| void output2() { | void output2() { | ||
| - | AddDebugMessage("Dawg!",false); | + | AddDebugMessage("Dawg!", false); |
| - | } | + | } |
| </code> | </code> | ||
| Line 113: | Line 114: | ||
| <code cpp>// Create a function definition (which is actually the same as fdSimpleFunction) | <code cpp>// Create a function definition (which is actually the same as fdSimpleFunction) | ||
| // Which will matches the signature of both our output functions | // Which will matches the signature of both our output functions | ||
| - | funcdef void fdOutput(); | + | funcdef void fdOutput(); |
| </code> | </code> | ||
| - | With the second step creating the variable which shall initially point to //output1 // at the start of //bigFunction. // Next we call//subFunction // as before, which will change the function pointer. Finally, we will call the function pointed to by the pointer: | + | With the second step creating the variable which shall initially point to //output1 // at the start of //bigFunction. // Next we call //subFunction // as before, which will change the function pointer. Finally, we will call the function pointed to by the pointer: |
| <code cpp> | <code cpp> | ||
| fdOutput @outputChoice; | fdOutput @outputChoice; | ||
| Line 124: | Line 125: | ||
| subFunction(); | subFunction(); | ||
| outputChoice(); | outputChoice(); | ||
| - | } | + | } |
| </code> | </code> | ||
| - | The subFunction implementation is as follows: | + | The //subFunction// implementation is as follows: |
| - | + | ||
| <code cpp>void subFunction() { | <code cpp>void subFunction() { | ||
| if(RandInt(0, 3) == 1) | if(RandInt(0, 3) == 1) | ||
| Line 136: | Line 135: | ||
| @outputChoice = @output2; // A 1 in 4 chance of this code being reached | @outputChoice = @output2; // A 1 in 4 chance of this code being reached | ||
| } | } | ||
| - | } | + | } |
| </code> | </code> | ||
| Line 147: | Line 146: | ||
| funcdef void fdOutput(); | funcdef void fdOutput(); | ||
| - | // Our output functions /////////// | + | // Our output functions |
| void output1() { | void output1() { | ||
| - | AddDebugMessage("Yo!",false); | + | AddDebugMessage("Yo!", false); |
| } | } | ||
| void output2() { | void output2() { | ||
| - | AddDebugMessage("Dawg!",false); | + | AddDebugMessage("Dawg!", false); |
| } | } | ||
| - | /////////////////////////////////// | ||
| // The actual stuff that does the descision making | // The actual stuff that does the descision making | ||
| fdOutput @outputChoice; | fdOutput @outputChoice; | ||
| void bigFunction() { | void bigFunction() { | ||
| - | // Initially point to output 1 | + | @outputChoice = @output1; // Initially point to output 1 |
| - | @outputChoice = @output1; // 1 in 4 of output 2... | + | subFunction(); // Call with 1/4 chance of changing outputChoice |
| - | subFunction(); | + | |
| - | + | ||
| outputChoice(); // Call whichever output has been chosen | outputChoice(); // Call whichever output has been chosen | ||
| } | } | ||
| void subFunction() { | void subFunction() { | ||
| - | if(RandInt(0,3)==1) | + | if(RandInt(0, 3) == 1) |
| { | { | ||
| - | @outputChoice = @output2; // A 1 in 4 chance of this code being reached | + | @outputChoice = @output2; // A 1 in 4 chance of changing the function pointer |
| } | } | ||
| } | } | ||
| - | void OnStart() { | + | void OnStart() { |
| - | for(int i=0; i<20; i++) bigFunction(); // Call bigFunction 20 times - 1/4 chance of output2? | + | // Call bigFunction 20 times - notice roughly a 1/4 chance of output2? |
| - | } | + | for(int i=0; i<20; i++) bigFunction(); |
| + | } | ||
| </code> | </code> | ||
| Line 188: | Line 185: | ||
| <code cpp>// This creates a signature called "SimpleFunction" | <code cpp>// This creates a signature called "SimpleFunction" | ||
| - | // Which matches functions which take no arguments, and return nothing. | + | // Which matches functions which take no arguments, and return nothing... |
| - | funcdef void fdSimpleFunction(); | + | funcdef void fdSimpleFunction(); |
| - | // Such as these example functions | + | // ...such as the following sample functions: |
| + | // This function will output "hello world" | ||
| void sfHelloWorld() { | void sfHelloWorld() { | ||
| - | AddDebugMessage("Hello World!",false); // Output hello world | + | AddDebugMessage("Hello World!", false); |
| } | } | ||
| - | // This function will output how many times it has been called void sfDisplayTimesCalled() { | + | // This function will output how many times it has been called |
| - | AddLocalVarInt("DTC_TimesCalled",1); | + | void sfDisplayTimesCalled() { |
| + | AddLocalVarInt("DTC_TimesCalled", 1); | ||
| AddDebugMessage("DisplayTimesCalled, called: " + GetLocalVarInt("DTC_TimesCalled") + " times", false); | AddDebugMessage("DisplayTimesCalled, called: " + GetLocalVarInt("DTC_TimesCalled") + " times", false); | ||
| } | } | ||
| + | //This function will play the sound of an angry brute! | ||
| void sfPlayScarySound() { | void sfPlayScarySound() { | ||
| - | PlayGuiSound("enemy\\brute\\notice.snt",1.0f); // Play the sound of an angry brute! | + | PlayGuiSound("enemy\\brute\\notice.snt", 1.0f); |
| } | } | ||
| </code> | </code> | ||
| Line 219: | Line 219: | ||
| <code cpp>void callRandomSimpleFunction() { | <code cpp>void callRandomSimpleFunction() { | ||
| - | uint index = RandInt(0,simpleFunctions.length()-1); // Pick a random index from the array | + | uint index = RandInt(0, simpleFunctions.length()-1); // Pick a random index from the array |
| fdSimpleFunction @functionToCall = simpleFunctions[index]; // Select that function | fdSimpleFunction @functionToCall = simpleFunctions[index]; // Select that function | ||
| functionToCall(); // Call that function | functionToCall(); // Call that function | ||
| // Note this can be simplified down to one line: | // Note this can be simplified down to one line: | ||
| - | // simpleFunctions[RandInt(0,simpleFunctions.length()-1)](); | + | // simpleFunctions[RandInt(0, simpleFunctions.length()-1)](); |
| } | } | ||
| </code> | </code> | ||
| Line 240: | Line 240: | ||
| functionToCall(); | functionToCall(); | ||
| - | AddLocalVarInt("simpleFunctionIndex",1); // Increment the index | + | AddLocalVarInt("simpleFunctionIndex", 1); // Increment the index, so the next function is called shortly... |
| - | AddTimer(asTimerName,1.0f,"callEachFunction"); // Call this function again in 1 second | + | AddTimer(asTimerName, 1.0f, "callEachFunction"); // Call this function again in 1 second, so the next function is called |
| } | } | ||
| </code> | </code> | ||
| - | One potential extension for use with sequences is making each function return a float, which can then be used as the delay time before calling the next function. Below is a sample script file for this section, don't forget to visit the OnStart routine and uncomment one of the two lines - or you won't see anything! | + | One potential extension for use with sequences is making each function return a float, which can then be used as the delay time before calling the next function. The sample script file for this section is below, don't forget to visit the OnStart routine and uncomment one of the two lines - or you won't see anything, and additional function is added to loop the //callRandomSimpleFunction// with a timer so it's effects can be seen easier: |
| - | + | ||
| <code cpp>// This creates a signature called "SimpleFunction" | <code cpp>// This creates a signature called "SimpleFunction" | ||
| // Which matches functions which take no arguments, and return nothing. | // Which matches functions which take no arguments, and return nothing. | ||
| - | funcdef void fdSimpleFunction(); // Such as these example functions | + | funcdef void fdSimpleFunction(); |
| + | // Such as these sample functions: | ||
| void sfHelloWorld() { | void sfHelloWorld() { | ||
| - | AddDebugMessage("Hello World!",false); // Output hello world | + | AddDebugMessage("Hello World!", false); // Output hello world |
| } | } | ||
| - | // This function will output how many times it has been called | ||
| void sfDisplayTimesCalled() { | void sfDisplayTimesCalled() { | ||
| - | AddLocalVarInt("DTC_TimesCalled",1); | + | AddLocalVarInt("DTC_TimesCalled", 1); //Display how many times function was called: |
| AddDebugMessage("DisplayTimesCalled, called: " + GetLocalVarInt("DTC_TimesCalled") + " times", false); | AddDebugMessage("DisplayTimesCalled, called: " + GetLocalVarInt("DTC_TimesCalled") + " times", false); | ||
| } | } | ||
| void sfPlayScarySound() { | void sfPlayScarySound() { | ||
| - | PlayGuiSound("enemy\\brute\\notice.snt",1.0f); // Play the sound of an angry brute! | + | PlayGuiSound("enemy\\brute\\notice.snt", 1.0f); // Play the sound of an angry brute! |
| } | } | ||
| // We now can create an array of these simple functions for further use. | // 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 }; | fdSimpleFunction@[] simpleFunctions = { @sfHelloWorld, @sfDisplayTimesCalled, @sfPlayScarySound }; | ||
| Line 271: | Line 268: | ||
| // Calling a random function | // Calling a random function | ||
| void callRandomSimpleFunction() { | void callRandomSimpleFunction() { | ||
| - | uint index = RandInt(0,simpleFunctions.length()-1); // Pick a random index from the array | + | uint index = RandInt(0, simpleFunctions.length()-1); // Pick a random index from the array |
| fdSimpleFunction @functionToCall = simpleFunctions[index]; // Select that function | fdSimpleFunction @functionToCall = simpleFunctions[index]; // Select that function | ||
| functionToCall(); // Call that function | functionToCall(); // Call that function | ||
| // Note this can be simplified down to one line: | // Note this can be simplified down to one line: | ||
| - | // simpleFunctions[RandInt(0,simpleFunctions.length()-1)](); | + | // simpleFunctions[RandInt(0, simpleFunctions.length()-1)](); |
| } | } | ||
| // Using a timer to call one function per second in sequence. | // Using a timer to call one function per second in sequence. | ||
| Line 287: | Line 284: | ||
| functionToCall(); | functionToCall(); | ||
| - | AddLocalVarInt("simpleFunctionIndex",1); // Increment the index | + | AddLocalVarInt("simpleFunctionIndex", 1); // Increment the index |
| - | AddTimer(asTimerName,1.0f,"callEachFunction"); // Call this function again in 1 second | + | AddTimer(asTimerName, 1.0f, "callEachFunction"); // Call this function again in 1 second |
| } | } | ||
| // Using a timer to repeatedly call a random function | // Using a timer to repeatedly call a random function | ||
| void callRandTimer(string &in asTimerName) { | void callRandTimer(string &in asTimerName) { | ||
| callRandomSimpleFunction(); | callRandomSimpleFunction(); | ||
| - | AddTimer(asTimerName,1.0f,"callRandTimer"); | + | AddTimer(asTimerName, 1.0f, "callRandTimer"); |
| } | } | ||
| void OnStart() { | void OnStart() { | ||
| Line 301: | Line 298: | ||
| } | } | ||
| </code> | </code> | ||
| + | |||
| ==== Closing comments ==== | ==== Closing comments ==== | ||
| - | A quick note on an earlier point - when the function pointer is defined initially, it can point nowhere. This can be tested for with the following code: | + | A quick note on an earlier point - when the function pointer is defined initially, it points nowhere (as it does if an assignment fails due to mismatching signatures). This can be tested for with the following code: |
| - | <code cpp>if( functionPointer is null ) | + | <code cpp>if( functionPointer is null ) |
| </code> | </code> | ||
| - | Finally, that state of a function pointer **is not saved when the map is - ** there is no way to save the state of a function pointer - so keep them the same (which is fine in the case of an array - like the last example), or assume they haven't changed within the**scope** of one function (as in the second example). | + | Finally, that state of a function pointer **is not saved when the map is - ** there is no way to save the state of a function pointer - so keep them the same (which is fine in the case of an array - like the last example), or assume they haven't changed within the **scope** of the first function (E.g. the second example). |