User Tools

Site Tools


hpl3:community:scripting:angelscript_tutorial

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
hpl3:community:scripting:angelscript_tutorial [2017/06/18 22:37]
abion47 [Inheritance]
hpl3:community:scripting:angelscript_tutorial [2018/04/15 16:37] (current)
abion47 [Variable Types]
Line 15: Line 15:
 This tutorial will be primarily geared toward people who have never programmed before in any language. If you do have some programming experience, you may just want to do a cursory glance over the tutorial series to just get a handle on what some of the similarities and differences there are between SOMA’s scripting language and other popular programming languages. This tutorial will be primarily geared toward people who have never programmed before in any language. If you do have some programming experience, you may just want to do a cursory glance over the tutorial series to just get a handle on what some of the similarities and differences there are between SOMA’s scripting language and other popular programming languages.
  
-This tutorial is not for people who are looking for a comprehensive guide to modding in SOMA and HPL3. This tutorial focuses on AngelScript specificallyas well as some of the interactions between AngelScript and HPL3. This tutorial will not teach you how to set up a map to make it playable, but once you have created a map, you can use the knowledge in this tutorial to do the scripting and make it come to life.+While this tutorial ​does take some looks into the interaction between AngelScript and HPL3, it is not for people who are looking for a comprehensive guide to modding in SOMA. This tutorial focuses on the AngelScript ​language ​specifically ​so as to hopefully provide a more rounded programming experience. This tutorial will not teach you how to set up a map to make it playable, but once you have created a map, you can use the knowledge in this tutorial to do the scripting ​for that map and make it come to life.
 ==== What is SOMA’s Scripting Language ==== ==== What is SOMA’s Scripting Language ====
  
Line 379: Line 379:
 Let's start with the //​integral//​ types of AngelScript. These types are meant to hold numbers without decimal points. Let's start with the //​integral//​ types of AngelScript. These types are meant to hold numbers without decimal points.
  
-^ Type Name ^ Value Range ^ Default ​^ +^Type Name ^Value Range ^Default ​| 
-| int8 | -128 to 127 | 0 | +|int8 |-128 to 127 |0 | 
-| int16 | -32,768 to 32,767 | 0 | +|int16 |-32,768 to 32,767 |0 | 
-| int | -2,​147,​483,​648 to 2,​147,​483,​647 | 0 | +|int |-2,​147,​483,​648 to 2,​147,​483,​647 |0 | 
-| int64 | -9,​223,​372,​036,​854,​775,​808 to 9,​223,​372,​036,​854,​775,​807 | 0 | +|int64 |-9,​223,​372,​036,​854,​775,​808 to 9,​223,​372,​036,​854,​775,​807 |0 | 
-| uint8 | 0 to 255 | 0 | +|uint8 |0 to 255 |0 | 
-| uint16 | 0 to 65,535 | 0 | +|uint16 |0 to 65,535 |0 | 
-| uint | 0 to 4,​294,​967,​295 | 0 | +|uint |0 to 4,​294,​967,​295 |0 | 
-| uint64 | 0 to 18,​446,​744,​073,​709,​551,​615 | 0 |+|uint64 |0 to 18,​446,​744,​073,​709,​551,​615 |0 |
  
-These types are categorized based on two factors - size, and signedness. ​+These types are categorized based on two factors - size, and signedness.
  
 The //size// of a type is how much memory it takes up in the program. This size is generally measured in bits. An <​html>​int8</​html>,​ for example, uses 8 bits to store its value, whereas an <​html>​int64</​html>​ uses 64 bits. (The <​html>​int</​html>​ and <​html>​uint</​html>​ types use 32 bits, but as 32 bit the most frequently used size, the "​32"​ part of the names have been chopped off for the sake of convenience.) The //size// of a type is how much memory it takes up in the program. This size is generally measured in bits. An <​html>​int8</​html>,​ for example, uses 8 bits to store its value, whereas an <​html>​int64</​html>​ uses 64 bits. (The <​html>​int</​html>​ and <​html>​uint</​html>​ types use 32 bits, but as 32 bit the most frequently used size, the "​32"​ part of the names have been chopped off for the sake of convenience.)
  
-<​note>​In computer memory, one bit is one switch in the computer'​s memory that can either be on or off. This is considered to have a value of 1 (on) or 0 (off). ​ Stringing these bits together, you can use different combinations of bits to refer to larger and larger values. This is known as the binary system. You can learn more about the binary system [[https://​www.mathsisfun.com/​binary-number-system.html|here]].</​note>​+<​note>​In computer memory, one bit is one switch in the computer'​s memory that can either be on or off. This is considered to have a value of 1 (on) or 0 (off). Stringing these bits together, you can use different combinations of bits to refer to larger and larger values. This is known as the binary system. You can learn more about the binary system [[https://​www.mathsisfun.com/​binary-number-system.html|here]].</​note>​
  
 The //​signedness//​ of a type is whether or not the values in the type can have a sign, or, in other words, whether or not the type supports negative numbers. If you compare <​html>​int8</​html>​ and <​html>​uint8</​html>,​ you'll see that the range of <​html>​int8</​html>​ is -128 to 127, whereas the range of "​uint8"​ is 0 to 255. The number of possible values in both cases is still the same (256 possible values), but the "​signed"​ type has been shifted over so that 0 is in the middle of the range instead of the beginning. The //​signedness//​ of a type is whether or not the values in the type can have a sign, or, in other words, whether or not the type supports negative numbers. If you compare <​html>​int8</​html>​ and <​html>​uint8</​html>,​ you'll see that the range of <​html>​int8</​html>​ is -128 to 127, whereas the range of "​uint8"​ is 0 to 255. The number of possible values in both cases is still the same (256 possible values), but the "​signed"​ type has been shifted over so that 0 is in the middle of the range instead of the beginning.
Line 399: Line 399:
 Next, we have what are known as the //​floating-point//​ types. These types are for representing numbers with decimal points. Next, we have what are known as the //​floating-point//​ types. These types are for representing numbers with decimal points.
  
-^ Type Name ^ Value Range ^ Smallest Positive Value ^ Maximum Number of Digits ^ Default ​^ +^Type Name ^Value Range ^Smallest Positive Value ^Maximum Number of Digits ^Default ​| 
-| float | +/- 3.402823466e+38 | 1.175494351e-38 | 6 | 0.0 | +|float |+/- 3.402823466e+38 |1.175494351e-38 |6 |0.0 | 
-| double | +/- 1.79769313486231e+308 | 2.22507385850720e-308 | 15 | 0.0 |+|double |+/- 1.79769313486231e+308 |2.22507385850720e-308 |15 |0.0 |
  
 You can see from the table that the floating-point types aren't quite as straight-forward as the integral types. Instead of a nice clean minimum and maximum value, we have a bunch of decimal numbers in scientific notation. This has to do with how floating-point types are stored in memory. That topic is fairly advanced, so I won't go over it here, but if you're curious, the process is described [[https://​www3.ntu.edu.sg/​home/​ehchua/​programming/​java/​datarepresentation.html#​zz-4.|here]]. You can see from the table that the floating-point types aren't quite as straight-forward as the integral types. Instead of a nice clean minimum and maximum value, we have a bunch of decimal numbers in scientific notation. This has to do with how floating-point types are stored in memory. That topic is fairly advanced, so I won't go over it here, but if you're curious, the process is described [[https://​www3.ntu.edu.sg/​home/​ehchua/​programming/​java/​datarepresentation.html#​zz-4.|here]].
Line 411: Line 411:
 There is one more built-in type for AngelScript,​ and it has nothing to do with numbers: There is one more built-in type for AngelScript,​ and it has nothing to do with numbers:
  
-^ Type Name ^ Possible Values ​^ Default ​^ +^Type Name ^Possible Values ^Default ​| 
-| bool | true, false | false |+|bool |true, false |false |
  
 The <​html>​bool</​html>​ type name is short for //​boolean//​. Boolean values are restricted to one of two values: //true// or //false//. Booleans have to do with whether something is or is not. For example, some boolean variables you might store could be: The <​html>​bool</​html>​ type name is short for //​boolean//​. Boolean values are restricted to one of two values: //true// or //false//. Booleans have to do with whether something is or is not. For example, some boolean variables you might store could be:
- +<code =c++>
-<​code=c++>​+
 bool IsLightOn = true; bool IsLightOn = true;
 bool HasPlayerEnteredArea = false; bool HasPlayerEnteredArea = false;
Line 424: Line 423:
 === Special Types === === Special Types ===
  
-Beyond the built-in types of AngelScript,​ there are a number of other types that are specific to HPL3. If you were to go to another game that uses AngelScript (such as Overgrowth),​ chances are that game's modding system won't have these types available (though ​chances are they will have equivalents).+Beyond the built-in types of AngelScript,​ there are a number of other types that are specific to HPL3. If you were to go to another game that uses AngelScript (such as Wolfire'​s ​Overgrowth),​ chances are that that game's modding system won't have these types available (though ​it's possible ​they will have equivalents).
  
-^ Type Name ^ Possible Values ^ Default ​^ +^Type Name ^Possible Values ^Default ​| 
-| [[hpl3/community/scripting/Classes/tString]] | "Hello SOMA" | ""​ | +|[[:hpl3:community:scripting:​classes:​tstring|tString]] |"​Hello SOMA" |<font inherit/inherit;;#​FF0000;;​inherit><​Empty><​/font> ​ |
-| [[hpl3/community/scripting/​Classes/​tID]] | //(See below)// | N/A | +
-| [[hpl3/​community/​scripting/​Classes/​cVector2f]] | (2.0, 3.5) | //All members are 0.0// | +
-| [[hpl3/​community/​scripting/​Classes/​cVector3f]] | (2.0, 3.5, -1.2) | //All members are 0.0// | +
-| [[hpl3/​community/​scripting/​Classes/​cVector4f]] | (2.0, 3.5, -1.2, 6.6) | //All members are 0.0// | +
-| [[hpl3/​community/​scripting/​Classes/​cMatrixf]] | [ 1.0,  2.0,  3.0,  4.0],[ 5.0,  6.0,  7.0,  8.0],[-1.0, -2.0, -3.0, -4.0],​[-5..0,​ -6.0, -7.0, -8.0] | //All members are 0.0// | +
-| [[hpl3/​community/​scripting/​Classes/​cColor]] | cColor_Red | //All members are 0.0// |+
  
-First, we have HPL3'​s ​string typethe <​html>​tString</​html>​. A string ​in programming means a sequence of characters, ​such as letters ​or numbers. ​Together, they may form word or phrase that you can use for your various purposes. (You may remember in the introduction lesson where we used string ​to print words onto the screen.)+In HPL3'​s ​AngelScripta `tString` is a specific flavor of a string, which is a sequence of characters, letters, and numbers. ​It doesn'​t have fixed length, so it can be anything from `"a"​` ​to `"​Greetings,​ sirs, my what a lovely fine evening we have here. Might you fancy a cup of tea?"`.
  
-Next is the <​html>​tID</​html>​ type. This type is used by the HPL3 engine ​to refer to different objects or entities in the game. Each entity is guaranteed a unique ID that points to them(That being said, there are various helper methods with which to refer to entities by their names rather than IDs, so it's unlikely ​that you will have to deal with this type very often.)+The most common way to use a `tString` ​is to store some kind of word or label, such as when you want to display a message on the screen ​that the player will readThere are some handy functions ​that you can use with a `tString`:
  
-There are a number of vector types as well, and each one has to do with how many dimensions it represents<​html>​cVector2f</​html>,​ for example, only deals with two dimensions, similar to if you were working on a 2D graph with X and Y coordinatesSimilarly, the <​html>​cVector3f</​html> ​deals in three dimensions, so it adds a Z coordinate on top of the X and Y of the 2D vector. And as you might expect, the <​html>​cVector4f</​html>​ deals in four dimensions, so it adds a corresponding W coordinate. +^Function Name ^Description ^Example Usage | 
- +|length |Gets the number of characters represented by the string<​html>​int stringLength = tStringValue.length();</​html> ​| 
-Similar to the vector types, there is a matrix type as well, known as <​html>​cMatrixf</​html>​. In theory, a matrix value can have any number of arbitrary rows and columns, but it is optimized ​to be 2x2, 3x3, or 4x4. There are a number of helper methods that work with interactions between matrices and vectors as well, so the linear algebra aficionados out there can do some crazy stuff with them. +|resize |Resizes ​the string ​to be the specified length. (Removes characters ​or adds "null" ​characters ​as necessary.) | <html>tString resizedString ​originalString.resize(5);</html|
- +
-And lastly, there is a color type, <​html>​cColor</​html>​. This type stores its information in four color channels: red, green, blue, and alpha (opacity). Color variables can come in handy when dealing with stuff that uses color, such as working with terminals ​or GUI applications. +
- +
-<​note>​The ​"t" ​in the above types stands for "​type",​ where the "​c"​ stands for "​class"​. The "​t"​ types can generally be used as is, but the "​c"​ types might require you to access the value'​s //members//I go into class members more in Lesson 7, but for now, you can know that you access them using //​dot-notation//​. This means you use the variable name, followed by a period and the name of the member you want to get. These members can be like the following:​ +
- +
-<code=c++> +
-    cVector4f vector; +
-    float x vector.x; +
-    float y = vector.y; +
-    float z = vector.z; +
-    float w = vector.w; +
-     +
-    cColor color; +
-    float red = color.r; +
-    float green = color.g; +
-    float blue = color.b; +
-    float alpha = color.a; +
-</code> +
-  +
-To see the full list of a type's members, go to that type's reference page on this wiki under "​Community -> Scripting -> Classes"​ or by clicking the links in the type table.</​note>+
  
 === Arrays === === Arrays ===
  
-All the types we've been talking about so far have been single-value types - an <​html>​int</​html>​ will store 5 or 3but not both at the same timeThere are occasions where you want to store more than one value at a time, thoughFor example, if you had 10 lights that you wanted to store the brightness levels for, you might do it like this:+In addition to variables that store a single value, ​there is also a type of variable that holds ''​many''​ valuesThese variables ​are called '''​arrays'''​.
  
-<​code=c++>​ +Some languages allow you to create arrays ​that can store anythingbut in AngelScriptarrays can only hold a single ​type. This type is chosen when the array is first declared:
-    float LightLevel0;​ +
-    float LightLevel1;​ +
-    float LightLevel2;​ +
-    float LightLevel3;​ +
-    float LightLevel4;​ +
-    float LightLevel5;​ +
-    float LightLevel6;​ +
-    float LightLevel7;​ +
-    float LightLevel8;​ +
-    float LightLevel9;​ +
-</​code>​ +
- +
-But that looks tediousdoesn'​t it? You would have to look at each variable individuallyand they aren't stored in any way that packages them together for a single ​purposeWhat's more, if you changed the number of lights you had, you would have to then add or delete variables, making sure that you also add or delete where the variables are used. +
- +
-This is where [[hpl3/​community/​scripting/​classes/​array|arrays]] come in. Where other types store a single value, arrays store a collection of values that you can reference by index. For example, take a look at how the above code might be simplified with the use of an array:+
  
 <​code=c++>​ <​code=c++>​
-    ​array<floatlightLevels;+array<intintArray; 
 +array<​bool>​ boolArray;​ 
 +array<​tString>​ stringArray;
 </​code>​ </​code>​
  
-That's it. The 10 lines of code (or 20, or 100, depending on how many lights there are) just got turned into a single line of code. Let's go ahead and look at that code. +In the above example, the type name inside ​the angled ​brackets is the type of value that array can storeFor example, ​the `intArray` can only store integers, and the `stringArray` can only hold string ​values.
- +
-Firstyou have the type name <​html>​array</​html>​. No surprise there, that's how we've been declaring variables ​the whole time up until now. But notice how alongside the "​array"​ there'​s another type name between angle brackets ​<​html><​float></​html>​. This is part of the declaration ​of array typesIt tells the program what type of values are stored in the array. In this case, we have an array that stores <​html>​float</​html> ​values. ​You can have arrays of other types too: +
- +
-<​code=c++>​ +
-    array<​int>​ intValues;​ +
-    array<​bool>​ booleanValues;​ +
-    array<​tString>​ stringValues;​ +
-    array<​cVector3f>​ vectorValues;​ +
-</​code>​+
  
 That's all well and good, but how do you get at the values in the array? That is done using something called //indexer notation//. Let me show you how it works: That's all well and good, but how do you get at the values in the array? That is done using something called //indexer notation//. Let me show you how it works:
Line 514: Line 465:
 In addition to adding a value at a specific index, there are also different ways you can more dynamically access an array. These ways use the array'​s functions, which we will get into in Appendix 2. For now, these are the more useful functions of an array and how they are used: In addition to adding a value at a specific index, there are also different ways you can more dynamically access an array. These ways use the array'​s functions, which we will get into in Appendix 2. For now, these are the more useful functions of an array and how they are used:
  
-^ Function Name ^ Description ^ Example Usage ^ +^Function Name ^Description ^Example Usage | 
-| length | Gets the number of values stored in the array. | <​code=c++>​int length = lightLevels.length();</​code> ​+|length |Gets the number of values stored in the array. |  
-| push_back | Adds a value to the end of the array. (If indices 0-4 are used, the value is stored at index 5.) | <​code=c++>​lightLevels.push_back(1.0);</​code> ​+|push_back |Adds a value to the end of the array. (If indices 0-4 contain values, the new value is stored at index 5.) |  
-| resize | Resizes the array to the specified length, adding or removing values as necessary. ​<​code=c++>​lightLevels.resize(10);</​code> ​|+|resize |Resizes the array to  |
  
 ---- ----
 +
 +\\
  
 ===== Lesson Three: Operators and Expressions ===== ===== Lesson Three: Operators and Expressions =====
Line 2109: Line 2062:
 ==== Constants ==== ==== Constants ====
  
-Sometimes when you are writing a program, your code will contain what is known as "magic numbers"​. These are numbers that you have chosen to fulfill a certain purpose, but that purpose is not illustrated in the number'​s usage, so anyone who doesn'​t know what you are doing just has to trust that the number will work. Take, for example, the following code:+Sometimes when you are writing a program, your code will contain what is known as "magic numbers"​. These are numbers that you have chosen to [[https://​en.wikipedia.org/​wiki/​Fast_inverse_square_root#​Overview_of_the_code|fulfill a certain purpose]], but that purpose is not illustrated in the number'​s usage, so anyone who doesn'​t know what you are doing just has to trust that the number will work. Take, for example, the following code:
  
 <​code=c++>​ <​code=c++>​
Line 2421: Line 2374:
  
 <​note>​The <​html>​TryX</​html>​ technique is a popular one when dealing with situations that need to be specially handled to prevent potential errors from happening. In a situation where a string needs to be converted (aka //parsed//) to a numeric value, for example, the string might not be a valid number (it could contain letters or something). Trying to convert such a string would result in one of two things - success or an error. If the <​html>​TryParse</​html>​ approach was implemented instead, however, then the function would return a boolean value showing whether or not a parsing could be performed and, if so, the result of the parsing is handed back to the caller through an <​html>&​out</​html>​ parameter.</​note>​ <​note>​The <​html>​TryX</​html>​ technique is a popular one when dealing with situations that need to be specially handled to prevent potential errors from happening. In a situation where a string needs to be converted (aka //parsed//) to a numeric value, for example, the string might not be a valid number (it could contain letters or something). Trying to convert such a string would result in one of two things - success or an error. If the <​html>​TryParse</​html>​ approach was implemented instead, however, then the function would return a boolean value showing whether or not a parsing could be performed and, if so, the result of the parsing is handed back to the caller through an <​html>&​out</​html>​ parameter.</​note>​
 +
 +----
 +
 +==== Object Handles ====
  
 ---- ----
Line 2531: Line 2488:
     DoSomethingHeroic(w);​ // Since Warrior inherits HeroClass, then w is treated like a HeroClass object as well as a Warrior object     DoSomethingHeroic(w);​ // Since Warrior inherits HeroClass, then w is treated like a HeroClass object as well as a Warrior object
 </​code></​note>​ </​code></​note>​
 +
 +Keep in mind that the limitation of inheritance is that a class may only specify one base class to inherit. Trying to inherit from multiple classes will result in an error:
 +
 +<​code=c++>​
 +    class BaseClassA {}
 +    ​
 +    class BaseClassB {}
 +    ​
 +    // The following class will result in an error, since a class cannot inherit from more than one base class
 +    class DerivedClass : BaseClassA, BaseClassB {}
 +</​code>​
  
 === Overriding Base Classes === === Overriding Base Classes ===
Line 2571: Line 2539:
     }     }
 </​code>​ </​code>​
 +
 +<​note>​When a class is inherited by another class, that is inheritance. When an inheriting class changes properties or behaviors of that base class, however, it takes inheritance a step further into a concept called **polymorphism**.</​note>​
  
 This might seem like we are falling into the trap of code repetition again, but there is actually a reason for the repetition in this case. Remember that I said that every derived class is treated as though it was of the base class type? Well, that applies to overridden functions as well, except the program takes it one step further. When you have a derived class passed into a function that treats it like a base class and that function calls something that has been overridden, it will automatically call the overriding version: This might seem like we are falling into the trap of code repetition again, but there is actually a reason for the repetition in this case. Remember that I said that every derived class is treated as though it was of the base class type? Well, that applies to overridden functions as well, except the program takes it one step further. When you have a derived class passed into a function that treats it like a base class and that function calls something that has been overridden, it will automatically call the overriding version:
Line 2670: Line 2640:
 ---- ----
 ==== Interfaces ==== ==== Interfaces ====
 +
 +Overriding a class'​s functions is one way to implement polymorphism,​ but another way is to use blueprints that, rather than providing a collection of fields and functions, merely dictates what functions a class //should// have and leaves it up to the class to implement them. This is done using a construct called an //​interface//​.
 +
 +The best way to explain an interface is to show how they are declared. This is done by using the <​html>​interface</​html>​ keyword:
 +
 +<​code=c++>​
 +    interface iMyInterface
 +    {
 +        int mlSomeField;​
 +        ​
 +        void DoSomething();​
 +        bool GetSomeBool();​
 +        void DoSomethingWithParam(int param);
 +    }
 +</​code>​
 +
 +As you can see, interfaces are declared a bit differently than classes. Fields are declared the same, but functions are only defined by giving them their identifiers,​ parameters, and return types. Instead of providing function bodies, they are closed instead with the semicolon. This is because an interface does not provide functions to use, but rather offers a series of functions that classes which use it are required to provide the bodies for:
 +
 +<​code=c++>​
 +    class cMyClass : iMyInterface
 +    {
 +        void DoSomething()
 +        {
 +            // Do something
 +        }
 +        ​
 +        bool GetSomeBool()
 +        {
 +            return true;
 +        }
 +        ​
 +        void DoSomethingWithParams(int param)
 +        {
 +            // Do something with param
 +        }
 +    }
 +</​code>​
 +
 +<​note>​When a class declares that it is using a base class, that is known as //​inheriting//​ the class. When a class declares that it is using an interface, that is known as //​implementing//​ the interface.</​note>​
 +
 +Just like with inheritance,​ the interface can be used as a type for variables:
 +
 +<​code=c++>​
 +    iMyInterface obj = new cMyClass();
 +    bool bValue = obj.GetSomeBool();​
 +</​code>​
 +
 +<​note>​While the practice of giving bodies to functions defined in interfaces is discouraged,​ AngelScript doesn'​t explicitly disallow it. If you wanted, you could define an interface with a complete function in it:
 +
 +<​code=c++>​
 +    interface iMyInterface
 +    {
 +        void DoSomething();​ // This function is required to be given a body by an implementing class
 +        void DoSomethingSpecific()
 +        {
 +            // This function body is provided by the interface itself rather than by any implementing classes
 +        }
 +    }
 +</​code></​note>​
 +
 +The power of interfaces comes from the fact that while a class may only inherit from one base class at the most, it may implement as many interfaces as it wants. You can do this by separating the interface names in the class declaration with a comma. Similarly, if a class is inheriting from a base class //and// wants to implement an interface, the class name is given first, followed by the names of the interfaces:
 +
 +<​code=c++>​
 +    class cComplexClass : cBaseClass, iInterfaceA,​ iInterfaceB {}
 +</​code>​
 +
 +This comes in handy when you have a class with a large number of traits. If for example, you had a custom array class, you might want to implement a number of interfaces that define what it can do:
 +
 +<​code=c++>​
 +    class cCustomArray : iIndexable, iEnumerable,​ iSortable, iCopyable
 +    {
 +        // The class contents
 +    }
 +</​code>​
 +
 +In this code, the interface <​html>​iIndexable</​html>​ guarantees that the class can be accessed by an index, similarly to a normal array. The interface <​html>​iEnumerable</​html>​ guarantees that the class may also be enumerated (i.e. traversed with a loop). The interface <​html>​iSortable</​html>​ guarantees that the class provides utility methods for sorting the underlying data into a given order. And the interface <​html>​iCopyable</​html>​ guarantees that the class contains functions that, when called, will return an object that contains an exact copy of the data within the calling object.
 +
 +Not only is this capability useful in ensuring that certain functions exist, it also helps group classes that may be similar in certain aspects but not in others. For example, if in addition to our custom array class, we also had custom classes for a [[https://​en.wikipedia.org/​wiki/​Binary_tree|binary tree]], a [[https://​en.wikipedia.org/​wiki/​Linked_list|linked list]], and a [[https://​en.wikipedia.org/​wiki/​Associative_array|dictionary]],​ the interfaces they implement could be used to treat all the classes under a given interface, such as <​html>​iIndexable</​html>:​
 +
 +<​code=c++>​
 +    iIndexable array = new cCustomArray();​
 +    iIndexable tree = new cBinaryTree();​
 +    iIndexable dict = new cDictionary();​
 +    ​
 +    SomeType arrayValue = array.ValueAtIndex(0);​
 +    SomeType treeValue = tree.ValueAtIndex(2);​
 +    SomeType dictValue = dict.ValueAtIndex(5);​
 +</​code>​
 +
 +As you can see, all three of the objects are based on a different data structure, but because they all implement the <​html>​iIndexable</​html>​ interface, they are all guaranteed to have an indexer function of some sort, shown here as the <​html>​ValueAtIndex</​html>​ function.
 +
 +<​note>​In many ways, the similarities between inheritance vis a base class and the implementation of an interface can lead to some fuzzy lines in program design. It's fairly common when designing a program for the designer to ask whether they should use an interface or a base class to group similarly intended classes together. The ruling philosophy over the difference between inheritance and interfaces is that a base class describes what a class //is//, whereas an interface describes what a class //does//.
 +
 +For example, with the RPG class script examples from the [[hpl3/​community/​scripting/​angelscript_tutorial#​Inheritance|Inheritance]] section, the <​html>​HeroClass</​html>​ base class denoted that <​html>​Warrior</​html>,​ <​html>​Archer</​html>,​ and <​html>​Mage</​html>​ were all hero classes, while they could implement an interface such as <​html>​iAttacker</​html>​ that would describe that they can attack things. Use this distinction when designing your own class structure for your programs.</​note>​
  
 ---- ----
  
 +==== Mixin Classes ====
 +
 +Sometimes, rather than exploiting the benefits of inheritance or interfaces, you merely want to segment some code into pieces in order to maximize code reuse and efficiency. In these cases, AngelScript provides a somewhat unique feature called a //mixin class//. Where inheritance and interfaces are intended to extend a class'​s capabilities or allow for flexibility in cases of variable types, a mixin class is merely intended to provide a class with a pre-defined set of fields and functions.
 +
 +A mixin class is defined with the <​html>​mixin</​html>​ keyword:
 +
 +<​code=c++>​
 +    mixin class MyMixinClass
 +    {
 +        void DoSomething()
 +        {
 +            // Do something
 +        }
 +    }
 +</​code>​
 +
 +This mixin class can now be used in the definition of other classes, which is done in the same way as declaring a base class or interface:
 +
 +<​code=c++>​
 +    class cMyClass : MyMixinClass {}
 +</​code>​
 +
 +Now, as far as other places in your code are concerned, the <​html>​cMyClass</​html>​ has the functions in it that were originally declared in <​html>​MyMixinClass</​html>:​
 +
 +<​code=c++>​
 +    cMyClass obj;
 +    obj.DoSomething();​
 +</​code>​
 +
 +Keep in mind that this is not the same thing as inheritance. With inheritance,​ the derived class //is// the base class (i.e. a "​Circle"​ //is// a "​Shape"​). However, <​html>​cMyClass</​html>​ is not a <​html>​MyMixinClass</​html>​. It is merely taking on the fields and functions of <​html>​MyMixinClass</​html>​ as if they were its own. To illustrate this, see what happens if you try to instantiate a <​html>​MyMixinClass</​html>​ variable with an instance of <​html>​cMyClass</​html>:​
 +
 +<​code=c++>​
 +    MyMixinClass obj = new cMyClass(); // Error
 +</​code>​
 +
 +This code would result in an error because <​html>​cMyClass</​html>​ and <​html>​MyMixinClass</​html>​ are not compatible types. In fact, <​html>​MyMixinClass</​html>​ is not a type at all. It cannot be instantiated,​ nor can variables be declared with it as their type. The only reason it exists is to provide <​html>​cMyClass</​html>​ with its contents. ​
 +
 +This concept is known in programming as //syntactic sugar//, which describes a programming feature that only exists to make life easier for the programmer in their code. Once the code is compiled, the syntactic sugar is specially processed in a way that, if you were to inspect the machine code after the script is compiled, no trace of the sugar would remain.
 +
 +<​note>​I included this section for the sake of completeness,​ but in my opinion, mixin classes are not good to use. When you define code in one place and it gets used in another, that would be better handled with either inheritance or interfaces. If a class is meant to have a set of functions that relate to a behavior, that is what interfaces are for. 
 +
 +The only time I would recommend using a mixin class is for when you are dealing with a particularly massive class and want to break it into pieces for organizational purposes. C# hosts a feature for this express purpose called "​partial classes",​ and I recommend anyone interested in mixin classes to [[https://​www.codeproject.com/​Articles/​709273/​Partial-Classes-in-Csharp-With-Real-Example|read about C#'s partial classes]] to get a feeling for what I think is the best and only legitimate use case for mixin classes.</​note>​
 +
 +----
 ==== Delegates ==== ==== Delegates ====
 +
 +Up until now, you've been using variables to store data of types. These types could be primitive types, such as <​html>​int</​html>​ or <​html>​bool</​html>,​ or they could be object types like <​html>​cVector3f</​html>​ or <​html>​cMatrixf</​html>​. They could be arrays or even names of classes or interfaces that you came up with yourself. What if I told you, though, that you could also use a variable to store a //​function?//​
 +
 +A //​delegate//​ is, in a nutshell, just that - a variable that stores a pointer to a function. The variable can be used to call that function just as though as you were calling it by name directly.
 +
 +=== Funcdef ===
 +
 +Before you can learn how to use delegates, you need to understand what a Funcdef is. Funcdef is short for "​function definition",​ and the basic explanation is that it defines a function "​type"​. Appropriately,​ it is declared using the <​html>​funcdef</​html>​ keyword:
 +
 +<​code=c++>​
 +    funcdef bool FunctionType(int,​ int);
 +</​code>​
 +
 +From this code, you can see that after the <​html>​funcdef</​html>​ keyword, the rest looks like a typical function declaration. One major exception is that the parameters don't have parameter names. That's because the Funcdef only concerns itself with the types of the parameters, and doesn'​t care about their names.
 +
 +After this, <​html>​FunctionType</​html>​ can be used to describe the "​type"​ of any function that shares an identical function signature with it:
 +
 +<​code=c++>​
 +    // This function takes two int parameters and returns a bool, so it matches FunctionType
 +    bool DoSomething(int a, int b)
 +    {
 +        return true;
 +    }
 +</​code>​
 +
 +<​note>​As a general rule, Funcdefs should be declared at the global scope, although there'​s no rule against declaring them in the class scope.</​note>​
 +
 +=== Using Delegates ===
 +
 +Now that we have a Funcdef, we can use it to declare and instantiate a delegate. When declaring a delegate, you use the Funcdef just as you would use a type for a regular variable:
 +
 +<​code=c++>​
 +    funcdef void MyDelegateType(int);​
 +    ​
 +    // ...
 +    // Elsewhere in your code
 +    ​
 +    MyDelegateType @delegate;
 +</​code>​
 +
 +A delegate is a pointer to a function, so it is necessary to declare it as a pointer object (see [[hpl3/​community/​scripting/​angelscript_tutorial#​object_handles|Object Handles]]). From here, you can use the function name of a function to "​assign"​ the function to the delegate:
 +
 +<​code=c++>​
 +    void DoSomething(int a) {}
 +    ​
 +    // ... 
 +    // Elsewhere in your code
 +    ​
 +    delegate = @DoSomething;​
 +</​code>​
 +
 +Similarly to the declaration,​ you must use the handle operator to get the pointer to the function and assign it to the delegate.
 +
 +Now that the delegate has been assigned a function pointer, you can use it as though you were calling the function directly:
 +
 +<​code=c++>​
 +    delegate(5);​ // Calls the DoSomething function that was assigned before
 +</​code>​
 +
 +When creating a delegate for a class function, a slightly different approach is required. Because the function is bound to the instance object of the class, you need to create the delegate using the Funcdef constructor,​ passing a reference to the object'​s function as a parameter:
 +
 +<​code=c++>​
 +    class A 
 +    {
 +        void DoSomething(int a) {}
 +    }
 +
 +    // ...
 +    // Elsewhere in your code
 +    ​
 +    A a;
 +    delegate = MyDelegateType(a.DoSomething);​
 +</​code>​
 +
 +Now that you know how to declare, assign, and use delegates, let me explain why they are useful. Imagine you have a situation where you have a number of functions, and you want to conditionally call one of them based on a value. You could use <​html>​if</​html>​s,​ of course:
 +
 +<​code=c++>​
 +    if (value == 1)
 +    {
 +        Function1():​
 +    }
 +    else if (value == 2)
 +    {
 +        Function2():​
 +    }
 +    // ...
 +    else if (value == 99)
 +    {
 +        Function99():​
 +    }
 +</​code>​
 +
 +That immediately throws red flags, so let's do the sane thing and replace it with a <​html>​switch</​html>:​
 +
 +<​code=c++>​
 +    switch (value)
 +    {
 +        case 1:
 +            Function1();​
 +            break;
 +        case 2:
 +            Function2();​
 +            break;
 +        // ...
 +        case 99:
 +            Function99();​
 +            break;
 +    }
 +</​code>​
 +
 +This code is much more efficient, but we run into another problem. I clipped the code in the example, but imagine if I had actually typed out all 99 cases of that <​html>​if</​html>​ or <​html>​switch</​html>​ code. It would be unsustainably long, to the point that if you had to find any particular case it would be nontrivially cumbersome to do. Not to mention if you later needed to add one, twenty, or a thousand more.
 +
 +In this case, you can instead use a delegate to store the function you want to call:
 +
 +<​code=c++>​
 +    funcdef void StateFunction();​
 +    StateFunction @state;
 +    ​
 +    // ...
 +    // Elsewhere in your code
 +    ​
 +    state = @Function99;​
 +    state();
 +</​code>​
 +
 +<​note>​This approach is commonly used when designing game engines. Because each level of each map of each mode of the game's operation needs to be handled differently,​ using a <​html>​switch</​html>​ block could require thousands of different cases, so it isn't really an option. Instead, the game stores a delegate that points to a function that handles that particular state (such as the main menu, or the pause screen, or level 2-A). 
 +
 +When something happens in the game that changes its state (for example, when the player presses the pause button), the game performs the corresponding assignment to the state delegate. Then when the next time the game refreshes itself, it will automatically call the new function, making a seamless transition into the new state.</​note>​
  
 ---- ----
Line 2689: Line 2925:
  
 ---- ----
-==== Built-In HPL3 Classes ==== 
  
----- 
hpl3/community/scripting/angelscript_tutorial.1497825424.txt.gz · Last modified: 2017/06/18 22:37 by abion47