Sprite Rules |
Sprite rules are used to define the general behavior of a sprite, and are often independent of which map the sprite is on (they apply generally throughout the game). (Behaviors specific to a particular map or a particular area are usually better represented as a "Plan" object.) The rules accomplish such tasks as moving the sprite, making it react to solidity on the map, making it switch states depending on which direction it's going, and changing it's velocity based on input.
Sprite Rules are only one aspect of the Sprite Definition. For information about other aspects of the sprite definition, see:
Sprite Rules are very similar to plan rules defined in the Edit Plans window. They do differ somewhat, however, because the rules within a sprite definition operate within the context of a sprite instance. A sprite instance of a particular type could be running on any map, so the sprite definition can't (or shouldn't) include map-specific rules. Plan Rules, by contrast, operate within the context of a layer, which may be running many different sprites. Therefore the types of rules and the specific parameters accepted by certain rules will differ based on the fact that the rules are targeted for a different context. For example, the ModulateColor function available in the plan editor is nice to have as an option there because there may be a specific event that occurs on the map that should change the color of the sprite (maybe it just went underwater), but then it requires a parameter to determine which sprite's color to change. The sprite definition rule editor, on the other hand, doesn't have a ModulateColor function because it has ModulateRed, ModulateGreen, ModulateBlue and ModulateAlpha properties that can be set directly with the "=" function, and there's no need to specify a specific sprite because these rules are operating on a sprite. Those properties may be useful in a more general context of setting the sprite color based on when it collides with another type of sprite, for example. Similarly there are many functions available in a Sprite Definition such as ReactToSolid, LandDownOnPlatform and SnapToGround that make more sense within the context of a sprite's general behavior than a specific layer, and thus have no counterpart in the Plan Editor.
A toolbar appears above the hierarchical list of rules to manage the creating, deleting and moving of rules in the list. The same commands appear in the "Sprite Definition" menu.
This displays a list of rules associated with the current sprite definition in a hierarchical view. When a rule represents a condition or a loop that affects a number of nested rules, the nested rules are indented under that rule and can be collapsed into it. Note that a condition (a rule of type "If") can also include an "Else" rule, which defines a series of rules to apply when the "If" condition was false. Since the else also depends on the same condition described in the "If" rule, it (and the statements applying to it) is included as a nested rule along with the rest of the rules nested in the condition. That may be difficult for programmers used to writing indented code to get used to because often times code is written with the "Else" being un-indented to the same level as the "If". But when displaying the rules in a hierarchy it truly is part of the same condition, and therefore is nested within the condition. (The code generated by the rules when the project is compiled *is* indented as a programmer would expect, however.)
The name of a rule is used to uniquely identify the rule within the sprite definition. Therefore the name must be unique among all rules within the same sprite definition. A rule name is not constrained by the same limitations as most names because it does ot represent the name of an object that gets converted into source code. The name of a rule simply becomes a comment in the generated code when the project is compiled. The name of a rule can contain any characters that can be typed into a single line. Some good guildelines or considerations for naming rules will help to quickly understand rules when looking at them in the rule list:
Of course you are free to devise your own guidelines as well, but these guidelines are used in the included samples and following them will make your rules consistent with any imported samples.
This will exclude the rule from being included in the generated project. It will be treated as if it does not exist when compiling and running the project. A good example is an imported player sprite. The imported player sprite template may have rules that make the sprite react to ladders based on a tile category named "Ladders". However, in order to support projects that may not have ladders or have not yet defined a tile category for ladders, the sprite sprite definition (in its exported, stand-alone form) would probably suspend the rules relating to ladders, so that the project designer who imports the sprite definition can choose whether and when to enable ladders.
Note: It is strongly recommended when suspending a rule that contains nested rules, all the nested rules also be suspended. Unexpected behavior may occur if a rule is suspended without suspending all of the rules nested within it. See the "Toggle Suspend for This and Child Rules" command above.
There are 8 fundamental classes of rules that can be defined using the first dropdown list. If the descriptions of these types seem overwhelming or confusing, skip them for now and just try out some examples (look at some existing sample rules in other projects). These should be relatively self explanatory, but you can refer to these descriptions for detailed information about how they behave and why they exist. Before proceeding, let's establish some terminology. A "Rule Type" is one of these 8 types selected in the first dropdown list. And the term "Rule Function" (described in more detail below) will be used to refer to a specific function selected in the second dropdown list.
Do | This simplest of rule types indicates that some action will be performed rather than defining a condition or loop. Note that rule functions that are normally associated with "If" type rules can also be used with "Do", and any action that they perform will be executed, but the result will be ignored instead of used to define a condition. |
If | This defines the beginning of a condition. Rules that come after an "If" rule will be nested within it until one of the rules uses the "End If" checkbox to end the condition. The set of rule functions available with an "If" type rule is limited. Only functions that return a true or false value can be used with "If". (In the source code, the function must return "bool".) When the rule returns success/true, the conditions nested in the "If" conditions will also apply, otherwise they are skipped. (There are exceptions, see "Else" below.) |
And | This rule type can only be used immediately after an "If", "ElseIf", "While", "And" or "Or" type rule, to extend the condition. The rules contained inside the condition will only be applied if all the rules combined with "And" return success/true. The same list of rule functions will be available for this rule type as are available for the "If" rule type. |
Or | This rule type can only be used immediately after an "If", "ElseIf", "While", "And" or "Or" type rule, to extend the condition. The rules contained inside the condition will be applied if any one of the rules combined with "Or" return success/true. The same list of rule functions will be available for this rule type as are available for the "If" rule type. |
ElseIf | This rule type can only be used within a block of rules started by "If" or another "ElseIf". It begins a new condition block nested yet another level below that of the current rule. The condition is only checked when the initial condition failed (returned false), and the statements nested under this rule are only applied if the initial condition failed and the condition specified in this rule succeeds (returns true). Because the statements are nested another level, you must remember to include at least two End If rules, one to finish the nested condition and one to finish the initial condition. If there are rules between the end of the inner condition and the end of the outer condition, they will be executed when the outer condition's expression is false regardless of the result of the inner condition. |
Else | This rule type can only be used within a block of rules started by an "If". It causes the rules within the block after this one to be applied only if the condition specified with "If" failed. This is actually more like an "Else Do" than a plain "Else" because it also includes the ability to perform a function within the same rule. |
End | Normally when you create a rule, in order to make it the last rule in a nested block of statements (the last rule that depends on an "If" condition, for example) you can check the "End If/End While" box. However, there is no way to indicate that a statement should be the last statement of two nested conditions (for example, the end of an "If" block as well as another "If" block containing that). That's the purpose of the "End" rule type. It's the only rule type that doesn't perfom any action in itself (there's no rule function associated with it). It just ends a block of nested statements that eas started by an "If", "ElseIf" or "While". |
While | This begins a block of statements that will execute repeatedly ("loop") as long as a rule function returns success/true. This can be used in conjunction with "And" and "Or" rule types to form complex conditions that must be met to continue the loop. The same list of rule functions will be available for this rule type as are available for the "If" rule type. It cannot be used in conjunction with ElseIf or Else rules. In other words, you can nest a "While" inside and "If" or an "ElseIf" block, and you can nest an "Else" or "ElseIf" rule inside an "If" block contained within a "While", but you cannot directly include an "Else" or "ElseIf" rule within a "While" block. Be careful with this rule type because it could easily result in endless loops that will make the game freeze if used incorrectly. It can also make the game run slowly. |
This checkbox can be used with the "If", "ElseIf", "While", "And" or "Or" rule type to invert the result of the rule. For example, you can check the "Not" box on a rule that reads "If - IsInputPressed" and then the rules contained in this block will only apply when the specified key is not pressed.
The second dropdown list on this form represents the rule function associated with the rule. Here you can select one of the many functions available to define sprite behavior, including functions defined yourself by editing files in the "Source Code" folder of the project. For more details about defining your own rule functions, see the coding reference . A number of pre-defined functions exist to perform simple comparisons, calculations and copying of values:
- | Subtract the "left operand" in the second parameter from the "right operand" in the first parameter and put the result in the variable provided in "Output to". |
!= | Compare the values of the first two parameters and return true/success if they are different, or false/failure if they are equal. |
+ | Compute the sum of the first two parameters and output the result to the variable provided in "Output to" |
< | Compare the values of the first two parameters and return true/success if "left operand" is less than "right operand", or false/failure otherwise. |
<= | Compare the values of the first two parameters and return true/success if "left operand" is less than or equal to "right operand", or false/failure otherwise. |
= | Copy the value provided in the first parameter to the variable provided in "Output to" |
== | Compare the values of the first two parameters and return true/success if they are equal, or false/failure if they are different. |
> | Compare the values of the first two parameters and return true/success if "left operand" is greater than "right operand", or false/failure otherwise. |
>= | Compare the values of the first two parameters and return true/success if "left operand" is greater than or equal to "right operand", or false/failure otherwise. |
When defining an "If", "While", "And", "Or", "ElseIf", or "While" type rule, the list of functions displays only rules that return a true or false value (sometimes thought of as success or failure). These rule functions are referred to as "boolean" functions. When defining a "Do" type rule, all rule functions are available for use, and if a boolean function is used, the function will perform its usual work (if any) but the result will simply be ignored. This might be useful because some functions actually do something that affects the game and then return a value indicating some true or false value. The LandDownOnPlatform function is a good example because it makes the sprite ride on a platform if it just hit a platform from above, and it returns true if the sprite just landed on a platform. But you don't have to check the return value in order for the sprite to land on the platform; only if you care to know whether the sprite landed on a platform or not.
After selecting a function, some or all of the parameter fields below will open up for input. Each function has its own set of parameters, and depending on how many parameters it uses and what types of parameters they are, the boxes below will become available and present different options in the dropdown lists. The box immediately below the rule function box will also display a brief description of what the function is and how to use it. Furthermore, if the function can output a number, the "Output to" box will open up and allow you to select a counter or plan parameter in which to store the result.
If there is an error compiling the project or processing the function name (it's possible to type an arbitrary name into the function field in case you want to call an undocumented function) then the description field will contain a generic error message and all the parameters will be available for input, but without names. This is to allow a function to be specified even if the environment doesn't know that it's valid.
After a function is selected, a brief description of the function is displayed in the box below. Don't forget that this box can scroll and there may be more information than is initially displayed. Usually a description will also include information about the meaning of the parameters and output value if any.
Many rule functions can accept parameters that specify details about how they should operate or what they should affect. In most cases a helpful list of suggested values is provided in the dropdown list for each parameter based on the type of the parameter, but any value can be manually typed because not all possible values can be predicted. For example if you want to call a function to display a message, you obviously won't be able to select the message from the dropdown list, you'll have to type it in. That brings up another important topic. When providing text for a parameter (also known as a "string") you must enclose the text in quotes. This (indirectly) allows you to include special formatting characters in the text. These are inserted with "escape codes". Here are the most important escape codes with which you should be familiar when entering a string into a parameter value:
Code | Result | Example parameter | Example result |
---|---|---|---|
\r\n | Inserts a line break | "Display me on\r\nTwo Lines" | Display me on Two Lines |
\" | Embeds a double-quote in the text | "He said his name was \"Little John\"" | He said his name was "Little John" |
Many other parameter types are available when defining rules for sprite definitions. Below is a list, but it is by no means comprehensive because the list is customizable. It can be expanded by adding your own enumerations in the game project's source code folder:
Type Name / Summary | Description/Source | Example |
---|---|---|
Keypress | Preset list of static values, each value representing a key on the keyboard | Key.A |
Direction | Preset list of "Up", "Right", "Down" and "Left" | SpriteBase.Direction.Down |
Relative Position | Preset list of pre-defined corner or centered positions within a rectangle (especially the sprite's solidity rectangle). | RelativePosition.BottomCenter |
Sprite | Since sprite definitions don't know about specific instances on maps, this list will provide a "this" option referring to the current sprite instance being processed. This usually applied to parameters of custom rules or other rules that can apply either a plan or a sprite definition. | this |
SpriteAnimationType | Preset list of "ByFrame", "ByHorizontalVelocity", "ByVerticalVelocity" and "ByVectorVelocity" | SpriteBase.SpriteAnimationType.ByHorizontalVelocity |
Sprite Collection | Displays a list of sprite categories defined in the project. (A sprite category object includes all sprite instances within a particular category for the layer on which the current sprite resides.) | ParentLayer.m_SpriteCategories.Enemies |
boolean | True of false | true |
Solidity | Displays a list of all the solidity definitions in the project. | Solidity.StandardSolid |
Counter | Displays a list of counters in the project. | Counter.Lives |
Color | Displays a preset list of colors. | System.Drawing.KnownColor.Crimson |
Sprite InputBits | Displays a preset list of inputs that can affect a sprite. | SpriteBase.InputBits.Button1 |
Map | Displays a list of maps defined in the project. (They are referred to by type instead of specific map instances because it's conceivable that you could have multiple copies of the same map loaded.) | typeof(Level_1_Map) |
Sprite Definition | Displays a list of Sprite Definitions. This differs from the other sprite parameter types because they require a specific sprite instance on the layer whereas this lists Sprite Definitions which are independent of the maps. | typeof(Sprites.Explosion) |
Sprite State | Displays a list of all states in a particular sprite (if no previously specified parameters have specified a sprite, this won't display a list). | (int)Sprites.Player.State.Left |
Integer | Displays a list of all counters, all parameters defined on the Parameters tab, and miscellaneous other shared integer variables. Some rules may want to alter a parameter value that is passed in (receive the parameter "by reference" so it can be changed). In these cases the values in the list are preceded by "ref" indicating that the variable that is provided may have a different value afterwards (failing to pass a a variable by reference where a reference is required will result in an error.) Some properties of a sprite (besides those listed on the Parameters tab) are numeric, but they are not integersl; these can be provided as a normal input parameter, but will not be available for use with "ref" parameter. Furthermore, counters cannot be provided to a parameter requiring a reference to an integer either, but another version of the rule function may be available that accepts a counter, or the value can be stored in a parameter and subsequently be copied into the counter with an "=" rule. | Counter.Lives.CurrentValue |
Tileset | Displays a list of Tilesets defined within the project. | Tileset.FireText |
Some functions output integer values. For those that do, this field is enabled to allow you to specify where to put the result. If you want to store it in a value specific to this sprite, select one of the provided sprite parameter values. Many other values, including all the counters, are also provided. You can add any number of parameters to a sprite (on the sprite parameters tab) if you need to store many values. Keep in mind that each instance of a sprite has its own copy of each value. But each copy only requires 4 bytes so there's little risk of being a memory hog until you have hundreds of sprites each wich hundreds of parameters.
Whereas parameters are all required, the "Output to" box can be left blank if you don't care about storing or using the result of a rule function.
If you want to mark this rule as the last rule in a block that was started with an "If", "ElseIf", or "While" type rule, check this box. Note that if you need to end two (or more) blocks at once, you will need to also add an "End" type rule after this. The "End" type rule will allow you to add another rule that has no function or parameters, and just checks the "End If / End While" box.
Take care to match every "If", "ElseIf" and "While" with one and only one matching rule that has the "End If / End While" box checked. The code generator will automatically close the remainder of your blocks if you have some unfinished blocks at the end of your plan, but other failures to match the beginnings of blocks with an end could result in unwanted behavior. One occasion when this can be particularly tricky is when deleting a rule that begins or ends a block. Be careful to find the corresponding rule at the other end of the block and delete or adjust it appropriately too.