Mappers' Corner Mappers and Modders

Go Back   Omnip)o(tentS Forums > SERVERS > Mappers' Corner
Reply
 
Thread Tools Display Modes
Old 09-30-2007, 11:46 PM   #1
Optimus_P-Fat
Insider

 
Optimus_P-Fat's Avatar
 
Join Date: Jan 2006
Location: New Jersey
Posts: 3,633
UT2k4 UScript tutorials 103: Functions
Reply With Quote


This is the third basic concepts tutorial in the series. Again, I will be discussing this mainly as a CONCEPT, with some of the examples in UnrealScript as I go about it.

As you may have noticed in the previous tutorials, I have referred to "bits of logic" as this mysterious, far off thing that magically happens that you shouldn't worry about yet. Now we get to them. A function in programming terms is a set of logical steps that you perform often, that you have grouped into a 'reusable' block of code. In general, most functions take in some values, called parameters, and give a result, called a return value or more simply stated, they return a value. While your parameters can be more than one, a function usually only returns a single value, and in some languages, functions do not have to return anything at all. Not returning a value is good for blocks of logic that you wish to execute that don't necessarily calculate anything, but perform a certain task.

Grouping your logic into functions helps make code easy and readable. While I could certainly write all of my logic for a program in a single, monumental set of steps, it would not only be huge, and hard to read, but it would also have the same bits of logic written over and over throughout the giant block of code. Creating functions helps you group you program logic into specific "tasks".

In UnrealScript, you can choose to return a value, or not return anything at all, and you may have as many parameters (even zero) to a function as you need to. Also, in Unrealscript, all functions are written inside objects, and are owned by those objects the same way the object's member variables are. They are called member functions of an object. Therefore, a "shape" class may have a function called "Draw", which contains the logic to draw it on the screen, but an "PlayerScores" object may not have any such function, because its job may be to only record and maintain scores, and not necessarily draw them on the screen.

Consider our "shapes" example from the previous tutorial on objects. I stated in that tutorial that our "shapes" had some logic for drawing to the screen. This would be a function for drawing. Defining a function requires specific things. For Unrealscript, a typical function definition looks like this:

<modifiers> function <return type> <function name>(<parameters>)
{

<code>
}


The <modifiers> are special keywords which affect how the function is run and other things. These are advanced topics, so do not worry about them now. You do not have to specify any modifiers unless you need to (i.e. not having any is perfectly valid)

The keyword function follows the modifiers list, to tell the code that what follows is going to be a function.

The <return type> is the variable TYPE (see tutorial 101 on variable TYPES) of the value you are going to return from this function. If you are writing a function that will not return anything, this field is omitted.

The <function name> is the name you will give to your function. These follow the same naming conventions that variables follow: namely, that they can consist of letters, numbers and the underscore, but cannot begin with a number.

After the <function name> is ALWAYS an open parenthesis, followed by the list of parameters you want to pass into the function. More on why you would want to do so later. The parameters are specified almost exactly like you define variables, except you do not use the keyword var in front of them, and they do not have a semi-colon after them. In 90% of the cases, they are simply a TYPE, and a NAME. Multiple parameters are seperated by commas. A closing Parenthesis always follows the parameters. If you choose not to specify ANY parameters to your function, you must still put in the open and closing parenthesis, albeit with nothing between them.

After your parameter list, you will add your actual logic. This is called the function body. The body is always enclosed in curly braces, so the first thing after your parameter list is always an open curly brace. Then comes your actual function logic, and finally you end it with a matching closing curly brace.
__________________

http://www.p-fat.net/
Optimus_P-Fat is offline  
Old 09-30-2007, 11:47 PM   #2
Optimus_P-Fat
Insider

 
Optimus_P-Fat's Avatar
 
Join Date: Jan 2006
Location: New Jersey
Posts: 3,633
Default
Reply With Quote


What is in a function body?

A function body consists of an optional series of statements declaring local variables, which I will explain in a minute, and then a series of code statements that perform operations on either the parameters, the local variables, or the member variables of the class.

Local variables are just like the variables I explained in Tutorial 101, except they only exist during the execution of the function they belong to, and cannot be accessed outside of it (unless of course, you pass them into another function as parameters). Think of them as temporaries for use in that function, because for all intensive purposes, that is what they are. You declare local variables inside the function by using the keyword local instead of the keyword var. For example:

Code:
function string MyFunction(string MyFirstParameter, int MySecondParameter) 
{
    local string MyFirstLocalVar;
	
    MyFirstLocalVar = MyFirstParameter;

    return MyFirstLocalVar;      
}
The function above is pretty pointless, but it has examples of everything I explained so far. The <return type> of the function is string. It has two parameters, the first is a string parameter named "MyFirstParameter", and the second is an int parameter, named "MySecondParameter" which, in the example isn't actually used. The function declares a local variable of type string called "MyFirstLocalVar", and then proceeds to assign the value of "MyFirstParameter" to it. Then, finally, it uses the return keyword to pass the value of "MyFirstLocalVar" back out of the function as the return value.

Now first, the question you may be asking yourself. "Didn't you say that local variables are temporary and only exist while the function is running? If so, then how can we return MyFirstLocalVar OUT of the function?" The answer to that is that we aren't returning "MyFirstLocalVar" ITSELF from the function, we are returning a copy of it's VALUE from the function. The variable itself DOES go away when the function returns and exits, but before it does so, it for all intensive purposes copies the value FROM "MyFirstLocalVar" to a temporary place that the function who CALLED this function looks if it needs to process the returned value at all. More on CALLING functions in a minute.

There is also one more caveat to writing functions with local variables. That is that ALL of your local variables must be declared before ANY actual logic code is written. That means that functions with local variables will ALWAYS have the complete list of locals before any other code.

Another thing that a beginner may not understand is that the return statement means "stop executing this function RIGHT NOW and return to whoever called me". The return statement only is followed by a value if the function is defined to return a value. For functions that do not return a value (and therefore do not contain a <return type> in their declaration) the return statement stands on its own with nothing but a semi-colon following it. Return can be used to exit a function no matter where in the code you are. For example, it can be used to stop running code that you know will not matter, if a certain situation arises.

This is a silly and pointless function, but it can server the point to illustrate this idea nicely:

Code:
function int MultiplyMyParams(int a, int b)
{
   if( a == 0 || b == 0 )
       return 0;

   return a * b;
}
While this is obviously a waste of cpu cycles to actually CHECK for 0 instead of just doing the multiply, this illustrates the point of how a return statement can end a function early, even though there exists code "after" it in the function body. The function above exits if either parameter passed in has a value of 0, and simply returns 0 as the result. Otherwise, it will continue on and return the result of multiplying a and b together. It also illustrates how the return statement does not have to always have a single value after it - it can contain an expression that evaluates to a single answer as well.
__________________

http://www.p-fat.net/
Optimus_P-Fat is offline  
Old 09-30-2007, 11:47 PM   #3
Optimus_P-Fat
Insider

 
Optimus_P-Fat's Avatar
 
Join Date: Jan 2006
Location: New Jersey
Posts: 3,633
Default
Reply With Quote


Calling Functions

To execute the code in a function, you will have to CALL that function from somewhere that needs it, and be sure to pass in the parameters it expects. Objects can call their own functions, or they can call functions on other objects, if they have a reference to that object (more on that later!). For now, let's focus on calling member functions from inside an object.

To call a member function from inside an object, you simply give the function name, followed by an open parenthesis, and the list of variables you wish to pass as parameters, and finally a close parenthesis and a semi colon. If you wish to use the returned value, should the function return a value, you can "catch" it in a variable using the = assignment operator. For Example, consider the function "MultiplyMyParams" above along with the function below:

Code:
function DoSomeStuff()
{
    local int Val1;
    local int Val2;
    local int Val3;

    Val1 = 5;
    Val2 = 20;
    Val3 = MultiplyMyParams(Val1, Val2);    

    return;
}

This function exists in the same object as "MultiplyMyParams", and therefore to call it we only need to use the function's name, and give it some parameters. Our function above returns NOTHING, so it has no <return type> specified, and has nothing after its return statement. (On a side note, functions with no <return type> do not need a return statement at all if they always execute to the end - the "return" statement in the above function is actually unnecessary, as it will return automatically once it hits the closing brace). This function simply declares three local variables, all of TYPE int. It assigns a value of 5 to the first one named "Val1", 20 to the second one named "Val2", and passes the values of Val1 and Val2 INTO the function MultiplyMyParams. This would make the values of a and b in the MultiplyMyParams function become 5 and 20 respectively. This in turn would make MultiplyMyParams return a value of 100. Our function above "catches" that value of 100 in a third local parameter, called "Val3".

Again, these functions actually serve no real purpose other than to demonstrate how you can go about defining functions that return a value, declare local variables, and call other functions in an object. They are entirely pointless, code-wise.
__________________

http://www.p-fat.net/
Optimus_P-Fat is offline  
Old 09-30-2007, 11:47 PM   #4
Optimus_P-Fat
Insider

 
Optimus_P-Fat's Avatar
 
Join Date: Jan 2006
Location: New Jersey
Posts: 3,633
Default
Reply With Quote


How do functions work with inheritance?

This is the interesting part. If you read tutorial 102 on objects, and made it through the concept of inheritance, you would know that a class that extends from another class "inherits" all of that parent class's member variables. In the case of functions, it also inherits all of those as well! Using the examples from tutorial 102, namely the "shape", "triangle" and "square" classes, I will show you how this works.

First, lets say we declare a function in the parent class "shape", like so:

Code:
function Draw(Canvas C)
{

}
The function itself takes a parameter of type "Canvas", named "C", which is another class type (do not worry about this right now, but object instances can be passed around as variables the same way ints, strings, etc are - I will get to using objects as variables later!). The function does nothing right now, because, well, the shape class itself doesn't know how to draw itself because it doesn't know the specific kind of shape it is. (There is actually a better way to do this, called creating an "abstract" class - it is one of the class modifiers I told you to not worry about for now. I will show you how to do this later!)

Now, both "triangle" and "square" can call this function as if it is their own (even though it does nothing!). From any function of theirs, they can call it as if it was defined in their own code file. Here's the part where inheritence is helpful. Both "square" and "triangle" can implement the SAME function in their source files. Let's say that they both do, and each one implements the correct function body to draw their own specific shape type. This is called "overriding" the function in the child class.

Now, our big overall drawing code, if you remember, has only a big list of objects it thinks are of the "shape" class. However, when it calls the "Draw" function on those instances, the lowest child class in the parent-child tree to override the function, is the version that is called. That means if the object instance is actually an instance of a "square", then the version of "Draw" that was written in the "square" class is the version that is called, and not the version in "shape". THIS lets our outer drawing code tell all of its objects to draw without having to know exactly what TYPE of object they are! Sqaure can implement specific code for drawing a square, and triangle can implement specific code for drawing a triangle, and the outer function simply calls the "Draw" function, no matter what type the object is!

Now, what if "shape" did something important in its "draw" function, that all of the child classes also needed to do, such as draw the object's MyName variable to the screen? Well, the object can "forward" execution back up to the "parent" version of the function, by using the keyword super. As an example:

Code:
class square extends shape;

function Draw(Canvas C)
{
    //do some stuff here to draw a square on the screen
    super.Draw(C);  //call my parent's implementation of Draw()
}
First things first - the words after the double slashes are called comments. These are ignored by the code itself, and are used by the person who is writing the code to explain things that are going on. Everything to the right of a // on a line will be ignored when the code is compiled and run. The importance of adding comments to your code will be discussed later, along with a second way to specify a comment.

The function itself has a comment denoting where normally some code would exist to draw the square itself, and then ends with a statement that calls the "parent", or in UnrealScript terms, the "super" class's implementation of the Draw method. If you inherit multiple levels down, i.e. you have a class A that extends B, which itself extends C, and you want to skip B's call from whatever reason and call right up to C from A, you can put parenthesis and the specific super class's name after the super keyword. For example:

Code:
super(shape).Draw();
That will from any class below "shape" in the inheritance tree, call the specific "shape" class's implementation of Draw, even if it is 2 or three parents upward from your class. Usually however, we never need to do such a thing.

So, the rules regarding inheritance are as follows: Functions that are overridden in child classes will always be called at the lowest child in the tree. If you override function Foo() that exists on the parent class, the parent's code for that function will NEVER be called unless you explicitly call it using the super keyword. (There IS a way to stop child classes from being able to override the function, but that is another advanced keyword and I will discuss it later).

I know this tutorial contained a lot of "jargon" in it. However, these words were all defined in either this tutorial, or in tutorials 101 and 102. If you do not understand something I've explained here, please post your questions on it here.

Furthermore, there IS a way to make a function "hidden" to any "child" classes of the class you define the function in, and there is a way to make it so the child classes cannot override your class's version of a function. This will all be covered when I go over the <modifiers> of the function declarations.
__________________

http://www.p-fat.net/
Optimus_P-Fat is offline  
Reply


Go Back   Omnip)o(tentS Forums > SERVERS > Mappers' Corner

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump

Similar Threads
Thread Thread Starter Forum Replies Last Post
Quick glace into UScript for UE3 Optimus_P-Fat Mappers' Corner 10 12-13-2007 10:51 AM
UScript Tutorials 201: Advanced Variables (modifiers and arrays) Optimus_P-Fat Mappers' Corner 1 10-04-2007 02:25 PM
UScript tutorials 102: Objects (Classes) Optimus_P-Fat Mappers' Corner 1 09-30-2007 03:01 AM
UScript Tutorials 101: Variables Optimus_P-Fat Mappers' Corner 3 09-29-2007 02:44 AM
UScript question Optimus_P-Fat Mappers' Corner 10 03-10-2007 07:23 AM


All times are GMT -5. The time now is 12:32 AM.