Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
IAttachableRemovable is the base class for FlatRedBall.Math.AttachableList. This interface requires that the implementer has a method called RemoveGuaranteedContain.
RemoveGuaranteedContain is a method with the following signature:
RemoveGuaranteedContain is a method which removes the argument attachable from the list and in the case of FlatRedBall.Math.AttachableList it cleans up the two-way relationship. For AttachableLists calls Remove, which means that the argument method must be contained in the list (a Contains check is not performed internally). This is why the method includes the phrase "GuaranteedContain".
The Relationships list contains all of the relationships being managed by the CollisionManager. In most cases, FlatRedBall games (using Glue) will not modify this list, but it is exposed publicly for:
Plugins to add new types of relationships
Debugging
Manual removal of collision relationships
The Relationships property is a standard List so it can be modified just like any other list. Collisions can be cleared by calling the Clear function:
Note that Glue will automatically clear relationships when a screen is destroyed, so this does not need to be called in most games.
The MakeOneWay method makes the calling AttachbleList a one-way list. In other words, it means that the AttachableList will still contain all of the objects that it contained before calling MakeOneWay; however, the contained elements will no longer know that they belong to this List. This essentially makes the AttachableList behave like a regular List - at least until new objects are added to the List, or until MakeTwoWay is called.
The MakeOneWay method is useful if you have a list of IAttachables (such as Sprites) which you want to remove from their respective managers without having them removed from their List. In the following example, the element at index 0 will get removed:
To prevent the removal from the list, it can be made one way:
The AxisAlignedCube is a geometric shape from the FlatRedBall.Math.Geometry class. As a 3D shape, it can be used for detecting collisions with 3D or 2D shapes. The AxisAlignedCube class shares many similarities with other shape classes (Circle, Line, AxisAlignedRectangle, Polygon, etc). For general information about shapes, see the ShapeManager wiki entry.
AxisAlignedCubes are created through the ShapeManager. The following code creates an AxisAlignedCube through the ShapeManager and resizes it: Add the following using statement using FlatRedBall.Math.Geometry; Create the instance globally AxisAlignedCube mCube; In the Initialize method
Alternatively, you can create an AxisAlignedCube with the Visible property set first by doing this Create the instance globally AxisAlignedCube mCube = ShapeManager.AddAxisAlignedCube();
For detecting collisions with AxisAlignedCubes, use the following code: Add the following using statement
Create the instance globally
In the Initialize method before the base call
In the Update method before the base calls
When two AxisAlignedCubes collide the collision side can be determined rather easily. The following code determines the side that two rectangles collided on: Add the following using statements
Assuming cube1 and cube2 are valid AxisAlignedCubes:
Did this article leave any questions unanswered? Post any question in our forums for a rapid response.
The FindByName method returns the first object in the list with the name matching the argument. The signature for the method is:
The FindByName method is most commonly used on AttachableLists which are populated by data which is loaded from file (or through the content pipeline), such as lists contained in or . For example, a .scnx may be created as the level for a platform game. The goal of the level is to reach a door, which is a in the .scnx. This requires special logic (collision tests, then advancing to the next level on a successful collision). To perform this logic, the code that's loading the .scnx needs to have reference to the door Sprite. To accomplish this, the person assembling the .scnx should appropriately name the door and communicate this to the programmer. We'll assume that the door is named "door". Add the following code to get a handle to this object:
Although AxisAlignedRectangles inherit from the PositionedObject class, which inherits from the IRotatable interface, AxisAlignedRectangles cannot be visibly rotated. This article discusses how rotation values are applied to AxisAlignedRectangles.
As far as the visible representation and collision behavior of an AxisAlignedRectangle, rotation will have no impact on an AxisAlignedRectangle. For more information, see this section.
All positions in FlatRedBall are defined by a combination of X, Y, and Z values. These values, when measuring absolute space, represent the distance from the origin along each axis by the same name as the component (X axis, Y axis, and Z axis). In FlatRedBall, when using an unrotated Camera, positive X points to the right and positive Y points up. The "axis aligned" part of AxisAlignedRectangles simply means that the edges of the rectangle are parallel (line up with) the X and Y axes. This is always true for AxisAlignedRectangles - even if they are rotated. However, that doesn't mean that the underling rotation values are always 0. In other words, if another PositionedObject is attached to an AxisAlignedRectangle, and the (parent) AxisAlignedRectangle is rotated, then the child PositionedObject will react to the rotation.
CollideAgainstMoveSoft performs the following logic:
It checks to see if two AxisAlignedRectangles are overlapping
If so, it adjusts the velocity of the caller and the argument AxisAlignedRectangles according to how much the two shapes overlap each other and according to the separation velocity.
Since this function adjusts the velocity of the caller and the argument, then your code must not explicitly set Velocity on either of the two objects or else your code will overwrite the Velocity values set by this function.
CollideAgainstMoveSoft adjusts the velocities of the two involved objects according to the strength of the separation (the last parameter in the function) as well as how far the two overlap.
A physical example of this would be to have magnets with the same charge (so they repel) on ice. If they are sufficiently far away then the repulsion is essentially 0, but as they get closer the amount increases. The math behind how this works is not physically identical to magnetic attraction and repulsion, but the concepts are similar.
The following code assumes 2 AxisAlignedRectangles are created:
AxisAlignedRectangleInstance1
AxisAlignedRectangleInstance2
It assumes you are using Glue so the code has been written in CustomActivity in a Screen, but it could be used outside of Glue as well.
The KeepThisInsideOf method repositions the calling AxisAlignedRectangle (or its TopParent if it is attached to another PositionedObject) so that it is not outside of the argument AxisAlignedRectangle. This method will do a one-time re-position of the calling instance, so it must be called every frame if the moving AxisAlignedRectangle.
This method can only be used to keep AxisAlignedRectangles inside of other AxisAlignedRectangles - other shapes are not supported.
The Update method performs the every-frame collision checks for all CollisionRelationships contained in the CollisionManager. This includes:
Sorting any partitioned lists
Assigning contained ICollidable LastFrameItemsCollidedAgainst and LastFrameObjectsCollidedAgainst
Resetting contained ICollidable ItemsCollidedAgainst and ObjectsCollidedAgainst
Invoking the BeforeCollision method
Performing collision on all contained CollisionRelationships
Note that most games do not need to call this method manually - it is called automatically by FlatRedBallServices.Update, which is part of all FlatRedBall projects.
The CollisionManager class provides methods for handling collision (the touching of game objects) in FlatRedBall games. The CollisionManager has built-in methods for handling collision between collidable entities (entities which implement the ICollidable interface). Use of the CollisionManager is not necessary, but it can reduce and standardize code for handling collisions. The CollisionManager provides a rich set of functionality for performing collision in code, but usually games do not need to write code against the CollisionManager. Instead, the FlatRedBall Editor provides support for creating CollisionRelationships without any code. For more information, see the FlatRedBall Editor CollisionRelationship page.
The CollisionManager is built around the concept of collision relationships. A collision relationship is created by specifying the two objects (or groups of objects) which are used in the relationship. The following list shows common relationships in games:
Player vs. Enemy Bullet List
Player Bullet List vs. Environment
Race Car List vs. Boost Entity List
Explosion List vs. Destructible Block List
The relationship detects collision and provides automatic and custom handling of the collision. Automatic handling includes separating objects (preventing overlapping) and adjusting the velocity of colliding objects (bouncing the objects). Custom handling is done by assigning a collision event.
The CollisionManager is built to work with collidable entities. To create a collidable entity in Glue:
Open Glue
Right-click on the Entities folder
Click Add Entity
Check one of the objects under the Collisions category, such as Circle
Notice that the ICollidable checkbox is automatically checked
The entity will now implement the ICollidable interface, which means it can be used as an instance or in a list with the CollisionManager.
A relationship may define that two individual entities should perform collision logic against one another. The following assumes that PlayerInstance and EnemyInstance are entities added to the screen in Glue:
Notice that the HandlePlayerVsEnemyCollision is used to perform custom logic whenever a collision occurs.
Entities which are dynamically created or destroyed during the life of a screen (such as bullets fired by a turret) are usually added to lists. The following code can be used to detect collision between a single Player instance and a list of Bullets:
Notice that the CollisionOccurred delegate passes a single bullet even though the type passed to CreateRelationship is a list. This allows the code to handle collision on a case-by-case basis. Similarly, relationships can be created between two lists. The following example shows how to respond to collision between a list of Enemies and a list of Bullets:
Entities can be collied against TileShapeCollections through the CollisionManager. TileShapeCollections are an efficient and convenient way to store a group of rectangles for collision.
See the Move Collision section below for more information.
Collision relationships can specify automatic behavior in addition to assigning the CollisionOccurred delegate. This can be achieved by calling various Set methods. For example, the following results in colliding objects being separated. The SetMoveCollision method takes the relative masses of each object. In this example the colliding player and blocks have equal mass:
The most common ways to call is either with the values of (0,1) and (1,1)
(0,1) - The first object (typically an entity) will have a mass of 0, meaning it will not be able to move the second object.
(1,1) - Both objects have equal masses, and will push each other
Both Move and Bounce collisions can be performed automatically by the relationship by calling either SetMoveCollision or SetBounceCollision. While convenient, these methods require the same mass to be used for all entities in a relationship. In some cases entities may need to have a custom mass per entity. In this example a list of asteroids collides with itself (every asteroid tests for collision against every other asteroid). Each Asteroid has a different Mass value so we can't set one mass for the entire relationship. Instead, the collision is detected and an event is raised, then the bounce collision occurs inside the collision event handler.
Note that the example above shows how to do self-collision, but the same approach of handling the move or bounce collision in an event could be performed between two different separate lists.
The CollisionManager can perform partitioning to improve collision performance. In most cases partitioning can make even large numbers of objects (tens of thousands) have minimal or no impact on your game's frame rate. Partitioning requires two pieces of information:
The axis to partition (X or Y). This should be the axis along which objects are most distributed. For example, a platformer with horizontal levels should partition on the X axis.
The width or height of each object involved in partitioning.
Both objects in a relationship must be partitioned on the same axis before a relationship will be able to use the partitioning to reduce collision calls. Partitioned objects must be sorted for partitioning to function properly. If the order of objects in the list can change, the CollisionManager can automatically sort the lists. If the order does not change once the list has been created, or if the order is explicitly maintained in custom code, then the sortEveryFrame parameter can be false. The following creates a partitioned collision relationship between a list of Enemies and a list of Bullets.
Partitioning must set on both objects in a relationship even if one of the objects is a single instance - in that case the Partition call is needed to get the width or height of the object's collision.
The Partition method only needs to be called once, even if an object is used in multiple relationships.
In general reducing collision count improves the performance of your game. To measure whether your efforts to reduce collision are working (such as by implementing partitioning), the CollisionManager returns the number of deep collisions performed every frame. The term deep collision refers to collision methods which rely on the actual shapes of a collidable object, as opposed to partitioned checks which can eliminate large numbers of objects very quickly. The following code shows how to count and display the number of collisions performed every frame:
Entities may include multiple collision objects used for different responses. For example, a SpaceShip entity may have a small collision shape for its body (for taking damage) and a larger collision shape for a radar ( to detect enemies and display them on a radar HUD). Collision relationships will use the entire entity by default, but can be configured to only use a single shape by calling SetFirstSubCollision or SetSecondSubCollision for the first or second object in the relationship, respectively. The following code shows how to handle SpaceShip body vs. a list of Bullets:
SetFirstSubCollision refers to the first argument in the CreateRelationship call, which is SpaceShipInstance in the example above. SetSecondSubCollision refers to the second argument in the CreateRelationship call, which is BulletList in the example above. Note that if the argument to CreateRelationship is a list, then the SetFirstSubCollision and SetSecondSubCollision will work with a single instance rather than a list. For example, the code above could call SetSecondSubCollision for the BulletList , but the argument would take a single bullet as shown in the following snippet:
When using a sub collision, the partition values may be different to account for the different collision object size. For example, the body of a space ship may be much smaller than the radar. Relationships can override the width or height values used for partitioning. The following code shows how to provide different collision width values (for X axis partitioning) for radar and body collision relationships:
At times collision physics needs to be applied optionally. For example, a Player may be able to transform into a ghost. Normally the player may perform solid collision (Move or Bounce) against enemies, but while in the ghost phase the player can move through enemies. This can be done by telling the collision relationship to optionally apply collision as shown in the following code:
The default behavior for relationships is to perform collisions every-frame. The CollisionRelationship class provides additional control for when collisions are called. The DoCollisions method can be called in custom code to perform collisions as desired. This can be useful if collisions need to happen after certain logic has executed in a frame, or only in certain situations. The following code shows how to create a CollisionRelationship which is only collided when the user holds the Space key:
The AxisAlignedRectangle object supports changing its Color property by assigning a Color value.
The following example requires that LeftRectangle and RightRectangle are valid Rectangles. For this example the rectangles were created in Glue. The following code can be used to modify the colors of the Rectangles:
The LastMoveCollisionReposition is a property that exists for all . If using CollisionRelationships, this property is set on any type of collision relationship that moves an object. This includes:
Move Collision
Bounce Collision
Platformer Solid Collision
Platformer Cloud Collision
If using manual collision calls, this property is set whenever a shape calls CollideAgainstBounce or CollideAgainstMove and the method test results in an actual collision. The LastMoveCollisionReposition property can then be tested to obtain information about collision.
When two AxisAlignedRectangles collide the collision side can be determined rather easily. The following code determines the side that two rectangles collided on: Add the following using statements:
Assuming rectangle1 and rectangle2 are valid AxisAlignedRectangles:
The following code can be used in a platformer to detect if a character is on the ground.
It adjusts the velocity of the calling object (the character in this case) so that it is no longer falling. This prevents gravity accumulation errors.
It adjusts the calling shape's (the character.Collision in this case) LastMoveCollisionReposition.
It returns whether a collision has occurred.
So, as we can see, this first if-statement does *a lot*. Well, the most important thing initially is knowing *if* a collision has occurred. If it does, then we proceed to the body of the if-statement to find out if the player is actually on the ground. The next line of code is:
In this code we assume that character is an Entity that you've created that has an IsOnGround property. IsOnGround is a property that you must create in your Entity - either in custom code or as a new variable in Glue. Of course, you can store this information however you'd like; we've just presented the most common way if using Entities.
The LastMoveCollisionReposition property gives you the reposition of the last shape that a given shape has collided against. This information may not be very useful if you are colliding against a collection of shapes. In this case, you may want to manually keep track of your collision reposition:
An AttachableList is a list which contains instances to . Instances of added to an AttachableList share a two-way relationship with the AttachableList by default. Common AttachableLists include the and classes.
While this article goes into extensive detail about two-way relationships, this paragraph provides a quick explanation of what it means. In short, a two-way relationship means that IAttachables (this includes such as , , and ) keep track of all that they are a part of. The reason that this relationship exists is so that objects can remove themselves from all that they are a part of automatically when removing them through the engine. Any "Remove" method offered by FlatRedBall managers will remove the argument object from all of its lists - at least for all -inheriting objects. A simple code example shows this functionality:
Remove the Sprite from the engine
Remove the Sprite from bullet list
Methods wich return AttachableLists usually fill them using AddOneWay. The assumption is that these lists will have a short lifespan.
Short-lifespan AttachableLists created outside of the engine should have elements added to them using one-way methods. Any AttachableList which has a lifespan of one frame or less should usually be populated using AddOneWay.
Using a foreach statement on an AttachableList will allocate memory. This is a very common gotcha when working with AttachableLists (and PositionedObjectLists). Consider the following to improve the performance of your application:
The RepositionDirections member controls which direction colliding objects will be repositioned. Specifically,RepositionDirections is relevant when calling and . RepositionDirections is also used by CollisionRelationships when using platformer collision. By default standalone AxisAlignedRectangles have all directions active, meaning the rectangle will reposition objects in all directions (up, down, left, right). AxisAlignedRectangles which are part of TileShapeCollections automatically have their RepositionDirections adjusted to prevent snagging.
The default value is All, which means that any objects colliding with the AxisAlignedRectangle can be moved in any of the four directions (Up, Down, Left, and Right) as shown in the following diagram:
RepositionDirections can be changed to any of the four cardinal directions, as shown in the following code:
The code above results in all objects being moved upward:
RepositionDirections can be combined with the | (or) operator, as shown in the following code:
This example shows how RepositionDirections modifies the CollideAgainstMove method. It uses two rectangles:
RedRectangle
BlueRectangle
These are created in Glue so the screen starts as shown in the following image:
RepositionDirections value can be adjusted in code or in Glue. Typically this property is adjusted in code, so this example uses the following code:
Notice that when the first rectangle is moved (with the keyboard), it will only be repositioned to the left.
If two AxisAlignedRectangle instances are colliding against each other, then the RepositionDirection of both rectangles are considered. For example, consider the following situation:
For this example the red rectangle on the left will be called RedRectangle and the blue rectangle on the right will be called BlueRectangle. We'll assume that the BlueRectangle's RepositionDirection value is set to RepositionDirection.All. We'll also use the following collision code between the two:
By default, the red rectangle will be pushed to the left so it no longer overlaps the blue rectangle. Of course, this depends on the following two conditions:
That BlueRectangle has a RepositionDirection including RepositionDirection.Left
That RedRectangle has a RepositionDirection including RepositionDirection.Right
For the default separation to occur, the blue must be able to reposition to its left and the red must be able to reposition to its right. In other words, both rectangles must have opposing RepositionDirection values for a reposition to occur. If either is missing, then other directions will be checked. For example, consider the following code:
This code would result in BlueRectangle only being able to reposition to the right when a collision occurred, resulting in RedRectangle being moved all the way to the right side of BlueRectangle.
Setting a RepositionDirection of Up can help with cloud collisions. Assuming MyPlatform is a valid AxisAlignedRectangle:
RepositionDirections defines which direction objects will move in when either CollideAgainstMove or CollideAgainstBounce are called. If a value of None is set, then CollideAgainstMove and CollideAgainstBounce will not change the position or velocity of either of the colliding objects.
The use of RepositionDirections is critical for proper collision when using TileShapeCollections. For simple situations, a developer does not need to understand the RepositionDirections behavior that is happening when creating TileShapeCollections. This section discusses the behavior of RepositionDirections in TileShapeCollections. To begin, consider a TileShapeCollection which contains a single AxisAlignedRectangle. In this case, the rectangle will have all four RepositionDirections as shown by red arrows.
If a second rectangle is added to the first, then both of them remove their inward reposition directions. This is important to prevent collision snagging - the stopping of a moving object when moving across a smooth surface created by multiple rectangles.
As more rectangles are added, the TileShapeCollection adjusts the RepositionDirections of each to prevent snagging. Usually this happens automatically and no custom code is needed.
The RepositionDirections property on AxisAlignedRectangles in a TileShapeCollection can tell you if a rectangle is a corner or part of a flat surface. As shown above, corners have at least two adjacent reposition directions. Since RepositionDirections is a bitfield, then the logical or can be used to test if an AxisAlignedRectangle is a corner, as shown in the following code.
The CollideAgainstBounce method can be used to have two 3D shapes collide against each other and bounce (modify their or their top parent's velocity appropriately).
This function works the same as the CollideAgainstBounce provided for 2D shapes, so for a more detailed discussion, see .
The following code creates two AxisAlignedCubes, sets the acceleration of one so it falls towards the other, and calls CollideAgainstBounce so that the two collide.
Note that for this example the project is using a 3D camera rather than a 2D camera. For information on how to set up a 3D camera in Glue, see .
Add the following at class scope in your Screen:
Add the following to CustomInitialize:
Add the following to CustomActivity:
GetRandomPositionInThis returns a representing a random position in the calling AxisAlignedRectangle. This value can be used for any logic, such as randomly positioning an object within an AxisAlignedRectangle.
The following example can be used to create 50 inside an AxisAlignedRectangle.
The code assumes that the rectangle is called AxisAlignedRectangleInstance and that you have a CircleList to store all the created Circles:
The AxisAlignedRectangle is a which is used for unrotated bounding box collision. This is preferred over collision if Sprites are not rotated due to speed and memory usage considerations. AxisAlignedRectangles share many similarities with the other shape classes (, , and ). For general information about shapes, see the . For information on using AxisAlignedRectangles in Glue, see . AxisAlignedRectangles do not support being filled-in. They can only render as an outline. To render a solid rectangle, look at using with the
The "AxisAligned" part of AxisAlignedRectangle indicates that the sides of the rectangle are "axis aligned". In other words, the top, bottom, left, and right are all parallel to either the X or Y axes. AxisAlignedRectangles are always axis aligned for performance reasons. Therefore, if you rotate an AxisAlignedRectangle (set its RotationZ), this will have no impact on the collision behavior or its visible representation. If your game requires rotation, you should use the class. Of course, performance will suffer slightly if collision performance is a consideration for your game. For more information on axis alignment and a discussion of axis aligned object rotation and children positions, see .
The AxisAlignedRectangle class inherits from PositionedObject. This means that all properties which are available to (excluding rotation) are available to AxisAlignedRectangles. For more information, see the .
AxisAlignedRectangles are created through the ShapeManager. The following code creates an AxisAlignedRectangle through the ShapeManager and resizes it: Add the following using statement
Create the instance:
Add the following at class scope:
Add the following in Initialize after Initializing FlatRedBall:
Add the following in Update:
The Left, Right, Top, and Bottom properties on AxisAlignedRectangle return the absolute position of the respective side.
The following code shows how to get the four values (Left, Right, Top, and Bottom):
Setting Left, Right, Top, and Bottom results in the X or Y values of the AxisAlignedRectangle changing. These values will not change the Width or Height of the AxisAlignedRectangle. To change dimensions use the Width and Height.
The following code makes an AxisAlignedRectangle named RectangleInstance position its bottom-left corner at the origin (0,0):
This code uses the method. For more information on this method, see . Let's look at the individual pieces of code above to see what is happening. The first line is:
This line of code tests to see if the character's collision (which we assume is a circle) collides against the "LevelCollision" which can be any shape or an entire . This method does the following:
This line of code tells us whether the collision that occurred should be treated as a ground collision. The reason this works is because "move" and "bounce" collision methods move the calling objects so they no longer overlap. For more information on how this works, check . If the player is standing on the ground, then gravity will move the player "into the ground", but the CollideAgainstBounce method will separate the two - repositioning the Circle. If the player is on the ground, then this position is vertical; in other words, it has a Y value of greater than 0. By contrast, if the character is jumping and hits the ceiling with his head, then he will be repositioned downward; the Y value will have a value less than 0. Finally we assign this value to our character's IsOnGround property:
A two way relationship is a relationship between an object that implements the interface and a class that inherits from the AttachableList class. Normally when objects are added to lists, the list stores any number of objects, but the objects themselves are not aware of which lists they belong to. These are known as "one-way" relationships: the knowledge of membership only exists in the list object. A two-way relationship, on the other hand, is one where the list stores all of its contained objects, and all of the contained objects are aware of all of the lists they belong to. When an is added to an AttachableList, the following occurs:
The is added to the collection of items that the AttachableList stores.
The adds the AttachableList to its ListsBelongingTo list.
And similarly, when an is removed from an AttachableList, the following occurs:
The AttachableList is removed from the ListsBelongingTo.
The is removed from the AttachableList.
Two way relationships exist to eliminate scope problems when removing an IAttachable completely. The FlatRedBall Engine often uses AttachableLists internally to categorize Sprites. For example, Sprites which belong to the SpriteManager can be either automatically updated or manually updated. Each is stored in a different array. Similarly, it is common practice to create AttachableLists to organize objects by their behavior. Common examples include placing Polygons which represent triggers in a level in a or to organize player bullets and enemies in separate . A bullet and enemy example clearly shows the kind of problems which can occur in the absence of two-way relationships. When a bullet collides with an enemy, it should be completely removed from the game. Not only does it have to be removed from any internal (such as lists in the ), but also from the programmer-created list of bullets that it belongs to. In the absence of a two-way relationship, at least two calls would be required:
This is inconvenient at best. It also requires code to be written in pairs (or more if an object is part of more lists). While the previous example highlights inconveniences, there is another practical reason for having two-way relationships. Sometimes the code that creates a PositionedObject and the code that destroys a PositionedObject may exist in two different areas of your project. The code that destroys a PositionedObject may not have access to all of the PositionedObjectLists that a given PositionedObject is a part of. The automatic removal enabled by two-way relationships enables removal without having to track down all lists. The two-way relationships between and AttachableLists enables any method to remove objects from all lists both engine and user created through the use of globally-visible static managers such as the and . Simply calling Remove on an object's manager automatically removes it from all lists that it shares two-way relationships with. The following code creates a , adds it to the , adds it to a , then removes it from the . Next the is checked to see if it still has a reference to the . If it does, the application exits. Since it does not, your application will not exit.
can also be added to AttachableLists creating one-way relationships using the AddOneWay method. In a one-way relationship, the AttachableList knows about its contained , but the do not know about their list. This is similar to how normal Arrays and Lists work. Usually one way relationships are useful when the scope of an AttachableList is brief - usually one frame or non-Main method. There are two common situations in which one-way AttachableLists are used:
The reason for two-way relationships is to remove unused from all AttachableLists which reference them. However, if an AttachableList has a short lifespan, then it will likely fall out of scope before any that it references are removed. One reason to use one-way relationships for short-lifespan AttachableLists is to reduce the overhead of creating and managing two-way relationships. The Add method is slightly slower than AddOneWay as two-way relationships require two Add calls to internal List<> objects. Another reason is that additional references reduce the efficiency of the garbage collector. A third reason to avoid two-way relationships for short-lifespan AttachableLists is exemplified by the following code. First, consider what occurs when the following code is executed.
Now, consider the result of using AddOneWay instead of Add:
There are times when an AttachableList should be converted between a two-way and one-way AttachableList. One common scenario is when a one-way AttachableList returned from a method is going to be used for longer than one frame. In this example, all Sprites with the word "collision" in the first are added to the second. Since FindSpritesWithNameContaining returns a one-way , two-way relationships are created for all in the the second .
Similarly, all AttachableLists have a MakeOneWay method which makes the relationship with all contained one-way.
Did this article leave any questions unanswered? Post any question in our for a rapid response.
See .
Solid or moving collision can be performed with AxisAlignedRectangles as well as and . The following code creates three AxisAlignedRectangles - one controlled by the player, one which can be pushed, and one which is is immovable. Add the following using statements:
For information on determining which side an object has collided on, see .
Did this article leave any questions unanswered? Post any question in our for a rapid response.
The IsPointInside method returns whether the argument X,Y are inside the Circle. The X and Y values are absolute values.
The following shows how to check if the Cursor is inside a Circle instance:
The CollideAgainst function returns whether one shape/ShapeCollection is touching another shape/ShapeCollection. CollideAgainst does not modify the positions or velocities of either of the callers, it simply returns true/false.
CollideAgainst is often used in the following situations:
Damage-dealing collision such as a player entity vs. a bullet entity.
Game triggers, such as an area marking the end of a game.
All shape collision for 2D shapes (that is, all shapes except AxisAlignedCube and Sphere) consider only X and Y values when testing for collision. This means that the Z value is ignored. Therefore two shapes which are at different Z values will trigger a collision. For example:
The LastCollisionTangent returns the vector that represents the surface where the collision last occurred. This can be used for custom physics implementations.
The Circle represents a PositionedObject which can be used to draw circles or conduct efficient circular collision. Circles are created and removed through the ShapeManager.
The following example creates two circles and controls one of them with the Keyboard.
Add the following using statement
At Class Scope:
In Initialize:
In Update:
The CollideAgainstBounce is a collision method which performs the following:
Returns true if a collision has occurred.
Repositions the calling Circle and/or the argument object depending on the argument masses.
Changes the calling Shape's Velocity and/or the argument object's Velocity depending on the argument masses.
Note: All collision methods, including CollideAgainstBounce, are methods common to all Shapes. If you came here through a link on a page beside the Circle (such as Polygon), don't worry, the code for all shapes is identical.
The signature for CollideAgainstBounce is as follows:
Arguments:
Circle circle (or other shape) - the shape to collide against. This method supports other shapes as well, and if you find a shape that you'd like to collide against which is not supported, please request this feature in the forums.
float thisMass - the mass of this shape. This does not have to be an absolute mass since it will only be used relative to the otherMass argument.
float otherMass - the mass of the argument shape. Just like thisMass, otherMass does not have to be absolute. It will be compared against thisMass.
float elasticity - The amount of bounce that will result. A value of 1 results in all momentum being preserved through the collision. A value of 0 results in no momentum being preserved. Negative values should not be used. Values greater than 1 introduce extra momentum. Values greater than 1 can be used to simulate "bouncing" against a wound-up spring, or to create false physics.
The following code creates a Plinko board.
Add the following using statements:
Add the following at class scope:
Add the following in Initialize after initializing FlatRedBall:
Add the following in Update:
Since CollideAgainstBounce returns a bool, your code can use the return value to modify your game when a collision occurs:
The Collision tutorial mentions:
"For bouncing to behave properly, we have to make sure that we're not controlling the involved Shapes' position or velocity"
Let's consider why this is the case. Imagine a situation where a shape (or parent of a shape) is moving toward the right. This is being done with the following code:
If this code is called every frame, that means that the XVelocity will be set to 1 every frame, regardless of what it was before. Imagine that "someShape" performs bounce collision against a wall. When collision occurs, the shape's XVelocity will get inverted (that is, set to -1) so that it moves to the left. However, if Velocity is set to 1 every frame, than the -1 would get changed back to 1. In other words, the setting of velocity every frame would cancel out the velocity change from CollideAgainstBounce. Let's look at another example: one where an object is moved with the mouse. In this example, the mouse is simply setting the position of an object:
In this case, someShape will have a velocity of 0 (assuming there is no other code that sets its velocity. Therefore, even though someShape appears to be moving smoothly with the mouse, if another object bounces against it, it may not bounce at all.
The following example shows a problem with using CollideAgainstBounce and Mouse control and how it can be corrected.
Add the following using statements:
Add the following at class scope:
Add the following in Initialize after initializing FlatRedball:
Add the following in Update:
The CollideAgainstBounce method is effective for performing bouncing physics, but it can also be used in situations where you'd like collision to reset velocity, such as in platformers. It's very common to have acceleration in platformers, and the easiest way to do this is to set the player Entity to have a negative YAcceleration. However, if the player collides with the ground using CollideAgainstMove, the YAcceleration will continue to accumulate the YVelocity value. Eventually this value will build up to be so large that the player will fall through the level. Even if this doesn't occur, the player will show weird behavior if it walks off of a ledge. To solve this, you can simply use CollideAgainstBounce instead of CollideAgainstMove. An elasticity of 0 will result in the same behavior as CollideAgainstMove, but the velocity will be modified according to the velocity to solve accumulation errors. You can try this in the demo above by setting the elasticity argument to 0.
A Capsule2D is a PositionedObject which can be used to perform collision tests. Since Capsule2Ds provide the same collision interface as other shapes, it is also considered a "shape". A Capsule2D is a shape that can be any length, but has rounded edges and a straight segment connecting the two points.
Capsules are often used to determine if a collision has occurred between a moving Circle and another object. Since capsules use rounded edges, they can represent the area that a Circle occupies during and between two subsequent frames.
Capsule size is controlled by two variables:
Scale
EndpointRadius
The following diagram shows hwo these values are used in a Capsule2D:
Did this article leave any questions unanswered? Post any question in our forums for a rapid response.
The FloatRectangle struct is a light-weight object which can be used to represent a rectangle using float coordinates. For example, the FloatRectangle can be used to store texture coordinates.
Did this article leave any questions unanswered? Post any question in our for a rapid response.
For more information see the .
Redirect to:
The Visible property on a Circle controls whether the Circle is rendered or not. Invisible Circles will still perform collision properly, so this property does not need to be set if using Circles purely for collision. Rendered circles will render their outline using their property. Circles cannot be filled-in, only their outline will render. Setting Visible to true may result in a Circle being added to the . For more information about how Visible results in membership, see the .
Storing shapes (or Entities which have collision shapes) in a list is very common. You can collide all objects in a list all other objects using doubly-nested for loops:
The ProjectParentVelocityOnLastMoveCollisionTangent method can be used to create realistic collisions between a Circle and any other . The Circle's version of ProjectParentVelocityOnLastMoveCollisionTangent behaves the same as the 's version. For more information, see the .
The ICollidable interface provides a standard collision implementation. Objects which implement ICollidable can collide with all FlatRedBall shapes and other ICollidables. The ICollidable interface requires a ShapeCollection property named Collision. FlatRedBall also offers the following extension methods for ICollidable:
CollideAgainst - Simply returns true/false to indicate whether a collision has occured
CollideAgainstMove - Returns true/false and separates the two objects involved in the collision
CollideAgainstBounce - Returns true/false, separates the two objects involved, and adjusts the velocity of the objects involved to simulate bouncing
Entities in the FRB Editor can be marked as ICollidable. For more information on ICollidable in the FRB Editor, see this page.
ICollidables provide the same three functions for collision as FlatRedBall shapes. The details of the collision behavior depend on the specific shape, but the concpts are similar in all cases. For more information on how shapes respond to collision methods, check the following pages:
Circle
ShapeCollection
Collidables must implement the following four properties:
ItemsCollidedAgainst
LastFrameItemsCollidedAgainst
ObjectsCollidedAgainst
LastFrameObjectsCollidedAgainst
These serve as an alternative to doing custom collision-related logic, and provide some additional functionality such as detecting when an entity exits collision.
The Items
property contains the name of items that the IColliable has collided with, while the Objects
contains a reference to the objects that the ICollidable has collided with. If you are using the FlatRedBall Editor, these properties are automatically generated in your entities, and these are automatically filled through collision relationships. For example, if the Player collides with SolidCollision in the GameScreen, then the items properties would contain the string "SolidCollision". The objects properties contains a reference to the TileShapeCollection.
The following code shows how to check whether the player has exited a collision area with the name PoisonCollision:
Alternatively, the Last properties can be used to identify if a changed was made this frame. For example, the following would detect both entering and exiting poison, and does not require the use of additional properties:
The SetFromAbsoluteEndpoints method can be used to set a line so that its endpoints are located at the argument locations. This method assigns:
RelativePoint1
RelativePoint2
Position
RotationZ
SetFromAbsoluteEndpoints updates a Line's Position property. If the line is attached to another object (such as a line in an Entity) then this method will not properly set the line's values.
Consider the following code:
The code above shows how to create a line using the SetFromAbsoluteEndpoints. To help explain what SetAbsoluteEndpoints does, we can consider how to create the same line without using SetFromAbsoluteEndpoints. To do the same as above, the following assignments would be necessary:
RelativePoint1 = (-50, 0, 0)
RelativePoint2 = (50, 0, 0)
Position = (50, 0, 0)
RotationZ = 0
The following code creates a line which connects the points X=0,Y=0 and X=100,Y=200:
FlatRedBall.Math.Geometry.Line
The LastCollisionPoint is a property which can tell you the last location where collision occurred. This can be used if you would like to play effects or perform custom physics where collision has occurred.
The following example creates two Lines and calls CollideAgainst between them. It then uses the LastCollisionPoint (if it is valid) to position a circle. One of the lines (Line1) can be controlled with the keyboard, resulting in a changing LastColliisionPoint.
This example is structured for Glue, but can be duplicated in a code-only FlatRedBall project:
Add the following to class scope in a Screen:
Add the following to CustomInitialize in a Screen:
Add the following to CustomActivity in a Screen:
A line is defined by two endpoints, so in mathematical terms it is actually a segment. Lines can be used to perform 2D collisions against any other shapes.
The following sample creates a line, a Circle, and an AxisAlignedRectangle. The line is controlled with the keyboard and it changes colors when it collides with the other two shapes. Add the following using statements:
Add the following at class scope:
Add the following in Initialize after Initializing FlatRedBall:
Add the following in Update:
A line can be modified by changing both its PositionedObject properties as well as through the RelativePoint property. The following code connects two rectangles with a line. Add the following in Update:
Add the following in Initialize after Initializing FlatRedBall:
Lines must be one pixel thick. Thicker lines are not supported.
Lines can only draw solid colors - patterns and gradients are not supported.
The IScalable interface provides an interface for 2D objects which can be resized. IScalable objects have two properties which control size:
ScaleX
ScaleY
Note: By default, scale has nothing to do with the Texture that an object is displaying. Two objects showing different Textures of different dimensions will be the same size if they have the same scale values.
Scale defines the distance from the center of an object to its edge. Scale values are used used instead of "width" and "height" because it simplifies collision and object placement. In other words, the following relationships exist:
OR
The following code creates 3 Sprites with different scales.
The ScaleX and ScaleY values on objects such as Sprites is (by default) independent of the Sprite's Texture property. Therefore, if two Sprites both have the same ScaleX and ScaleY values, they will appear as the same size on screen regardless of the size of the Textures that they are displaying.
Understanding the 3D Camera tutorial - Information on Scale and it's relationship to on-screen size
Setting a Sprite to Pixel Size - Shows how to set a Sprite's scale so that it appears the same dimensions as its source Texture.
FlatRedBall.Graphics.Model.PositionedModel.ScaleX - Scale and PositionedModels.
Did this article leave any questions unanswered? Post any question in our forums for a rapid response.
Scale is a common measurement of size in the FlatRedBall Engine. Most sizable objects implement the IScalable interface and have ScaleX and ScaleY variables. This includes the Sprite class. Scale measures the distance from the center of an object to its edge. Therefore, the following relationships exist:
In other words, ScaleX is half of width and ScaleY is half of height.
FlatRedBall can be rendered using a 2D or 3D camera. Games using a 2D camera typically render their sprites according to the size of their source texture (using the TextureScale property). Games using a 3D camera may not rely on a texture's size. In this case, scale values may be manually set. Therefore, two Sprites which reference textures of different size or aspect ratios may have sizes completely unrelated to their texture. Fortunately, sprite's Texture property exposes its dimensions so if you desire to control the size of a Sprite according to its texture, you can set the X and Y scales according to the size of the texture. The following code creates three Sprites - one with a default ScaleX and ScaleY of 1, one with its size relative to the size of its source texture, and one drawn drawn to-the-pixel.
When first creating a level in FlatRedBall, it may seem as if there is nothing to help you indicate how large objects should be. You may be asking "How big should my character be?" or "How do I determine how large objects in my game should be?" What follows are a few suggestions to help you size your objects.
One of the most common approaches is to identify an object as the determining factor for the scale of the other objects. This object (or group of objects) will be used as a reference point when scaling other objects. Two common choices are the player-controlled character and the tile size of the level. The character is a good choice because often times levels are built around the size of the character. Areas must be tall enough for the character to walk through, pits must be the right size so that players can jump over them, and ledges must be low enough so the player can jump up or grab onto them. Using the character as the reference object for scale is generally recommended in games where the player and environment will interact frequently, like in platformers. Another option is to decide on a tile size for the level. The tile size will determine the size of most Sprites used in the level. In this case, objects will generally be scaled to fit with the size of the rest of the level. For example, in a top down role playing game (RPG), the character is usually one or two tiles tall.
Although the SpriteEditor provides a lot of support in assembling levels without having to manually set size and position values, it is very helpful to use scales that are whole numbers - especially for tilemaps. That is, when creating a tilemap, consider having your Sprites have a scale of 1. This makes the math very simple - all objects will be exactly 2 units away from neighbors. This will also help you position objects when you manually have to place them. Furthermore, moving on a tilemap with a unit size simplifies code and can help optimize pathfinding.
The visible screen area can help you decide how large objects should be. In code, the visible area can be determined as follows:
Of course, adding a Sprite and running the code can give you a visual indication of the size. Another good option is to run the SpriteEditor, add a Sprite, and turn on the camera bounds, as explained here.
Did this article leave any questions unanswered? Post any question in our forums for a rapid response.
The Color property controls the color value used when drawing a visible Line.
The following code creates a visible red line:
The following shows a transparent white line. Note that colors are pre-multiplied so all color values must be multiplied by the alpha value.
The following code creates a half-transparent red line:
The transparency can be difficult to see without zooming in. The following image shows the corner of the blue rectangle zoomed in.
The RelativePoint1 and RelativePoint2 values define the shape of a line relative to its absolute position. These values can be set to modify how a line will draw - specifically its angle and length. For simplicity the RelativePoint1 can be set to (0,0), so simply modifying the second point will adjust the line.
RelativePoint1 and RelativePoint2 are used to define the line in combination with the absolute point. The most common, and recommended, approach to create a line is to have RelativePoint1 be (0,0) and have RelativePoint2 define the direction of the line relative to its position. For example, consider the following code and image:
Although not recommended, RelativePoint1 and RelativePoint2 can be set such that the Line's position is not the Line.
Although FlatRedBall allows this kind of Line, and in some cases it may be beneficial for custom logic, it is not recommended for standard collision, and some methods (such as CollideAgainstClosest) will throw exceptions if the Position does not coincide with RelativePoint1. Therefore, unless your game has special requirements, RelativePoint1 should always have a Value of (0,0).
The following code creates a vertical line. The bottom-end of the line will be located at the line's absolute Position. The top-end of the line will be located 100 units up.
The following code creates a horizontal line. The left-end of the line will be located at the line's absolute Position. The right-end of the line will be located 100 units up.
The Line class supports 3D lines (lines with non-zero relative points or absolute positions). The following shows how to create a grid of 3D lines. Note that it requires a 3D camera:
The AbsolutePointPosition method returns the absolute position of the point on the polygon matching the argument index. Passing an index outside of the list of points will result in an exception.
The following code shows how to create a bullet at the 2nd point on a polygon (index 1):
Defines an absolute position in 2D space (using X and Y). The Point class is used in collision. Since precision is important when performing collision operations the Point class uses doubles for its X and Y fields.
Did this article leave any questions unanswered? Post any question in our for a rapid response.
The CollideAgainstBounce is a collision method which performs the following:
Returns true if a collision has occurred.
Repositions the calling Circle and/or the argument object depending on the argument masses.
Changes the calling Shape's Velocity and/or the argument object's Velocity depending on the argument masses.
Note: All collision methods, including CollideAgainstBounce, are methods common to all Shapes. If you came here through a link on a page beside the Circle (such as Polygon), don't worry, the code for all shapes is identical.
The signature for CollideAgainstBounce is as follows:
Arguments:
Circle circle (or other shape) - the shape to collide against. This method supports other shapes as well, and if you find a shape that you'd like to collide against which is not supported, please request this feature in the forums.
float thisMass - the mass of this shape. This does not have to be an absolute mass since it will only be used relative to the otherMass argument.
float otherMass - the mass of the argument shape. Just like thisMass, otherMass does not have to be absolute. It will be compared against thisMass.
float elasticity - The amount of bounce that will result. A value of 1 results in all momentum being preserved through the collision. A value of 0 results in no momentum being preserved. Negative values should not be used. Values greater than 1 introduce extra momentum. Values greater than 1 can be used to simulate "bouncing" against a wound-up spring, or to create false physics. For more information on the elasticity variable, see the Elasticity Examples section below.
Add the following using statements:
Add the following at class scope:
Add the following in Initialize after initializing FlatRedBall:
Add the following in Update:
Since CollideAgainstBounce returns a bool, your code can use the return value to modify your game when a collision occurs:
Let's consider why this is the case. Imagine a situation where a shape (or parent of a shape) is moving toward the right. This is being done with the following code:
If this code is called every frame, that means that the XVelocity will be set to 1 every frame, regardless of what it was before. Imagine that "someShape" performs bounce collision against a wall. When collision occurs, the shape's XVelocity will get inverted (that is, set to -1) so that it moves to the left. However, if Velocity is set to 1 every frame, than the -1 would get changed back to 1. In other words, the setting of velocity every frame would cancel out the velocity change from CollideAgainstBounce. Let's look at another example: one where an object is moved with the mouse. In this example, the mouse is simply setting the position of an object:
In this case, someShape will have a velocity of 0 (assuming there is no other code that sets its velocity. Therefore, even though someShape appears to be moving smoothly with the mouse, if another object bounces against it, it may not bounce at all.
Add the following using statements:
Add the following at class scope:
Add the following in Initialize after initializing FlatRedball:
Add the following in Update:
Due to the popularity of the , it is very common to have shapes attached to other . Because of this common setup, all shapes have special behavior in their collision methods to reposition or change the velocity of their .
The attachment hierarchy is a collection of and their attachments - or child/parent relationships - which define which objects control the position and rotation of other objects. The following shows a typical attachment hierarchy for an .
Methods like CollideAgainstMove and CollideAgainstBounce are commonly used to prevent overlapping and modify velocity in response to collisions. CollideAgainstMove modifies the absolute Position of the calling shape and the shape it is colliding with to prevent overlapping. CollideAgainstBounce prevents overlapping just like CollideAgainstMove, but also modifies the velocity of the two shapes involved.
This may seem to present a problem; shapes have their absolute properties modified through the CollideAgainstMove and CollideAgainstBounce methods; however, as shown in the entity pattern above, shapes are often not at the root of the attachment hierarchy. Therefore it would seem as if these methods will only work when the shapes involved are the roots of their respective attachment hierarchies.
As mentioned above, the CollideAgainstMove and CollideAgainstBounce methods will update the root's absolute properties. However, if two shapes share the same root, the collision methods will not be able to reposition the objects properly. The following image shows the conflict:
Notice that in this situation both children will be modifying the parents' property. However, the parent's absolute values control the absolute values of the attached shapes.
In these situations the regular CollideAgainst methods will work correctly, but the Move and Bounce versions of the method will not be able to properly modify values. Keep this in mind if you are using a shared root for multiple shapes.
The Polygon class is used to define collidable shapes. Polygons can contain any number of points and can be either convex or concave. Polygons can be drawn to the screen by adding them to the ShapeManager or by setting their Visible property to true.
Polygons are typically used for objects which need complex collision. Polygons can collide with other Polygons as well as other FlatRedBall shapes such as Circle and AxisAlignedRectangle.
Polygon collision is significantly slower than Circle and Rectangle collision, so if your game can use simpler shapes those are recommended for performance reasons.
The Polygon class sits is in the FlatRedBall.Math.Geometry namespace, so adding
can simplify your code. Also, Polygons use the FlatRedBall.Math.Geometry.Point struct, so the following code can help solve ambiguities:
The Polygon class provides the CreateRectangle shortcut methods for creating rectangular Polygons:
You can also assign the Points object. For more information, see the .
Polygons can be constructed by assigning the points. Note that points are relative to the center of a polygon, not in absolute coordinates.
Polygons support points in any order, but adding points clockwise is recommended for performance reasons. Internally, FlatRedBall is able to perform faster collision checks between polygons if both have points added clockwise.
The example above adds points clockwise. We can see this by overlaying the polygon's origin and drawing the order in which points have been added as shown in the following image:
Polygons provide an IsClockwise method which checks if the points are in clockwise order, as shown in the following code:
Each Polygon has a BoundingRadius method which is set when the point list reference is updated. This BoundingRadius is used internally in collision methods. When two Polygons collide, their distances are compared to their BoundingRadius. If the two are too far away to possibly have a collision, then no deeper checks are performed.
This helps reduce the cost of performing polygon collision, especially when polygons are often separated by larger distances.
This method repositions a Polygon or its TopParent if it has one, changes the LastMoveCollisionReposition property, and finally updates all attachments. This code performs all of the updates which would happen on a successful CollideAgainstMove call. This method is rarely used. It can be used if collision is handled outside of the polygon class but the LastMoveCollisionReposition property is still used in other parts of code. If collision that repositions a polygon calls this method rather than simply changing its Position values, then code that uses the LastMoveCollisionReposition property (such as ProjectParentVelocityOnLastMoveCollisionTangent) will still work correctly.
Note that FlatRedBall expects shape colors to be pre-multiplied. Therefore a half-transparent red value would have a R,G,B,A value of (128,0,0,128).
The following code creates a board.
The mentions:
The following example shows a problem with using CollideAgainstBounce and control and how it can be corrected.
The CollideAgainstBounce method is effective for performing bouncing physics, but it can also be used in situations where you'd like collision to reset velocity, such as in platformers. It's very common to have acceleration in platformers, and the easiest way to do this is to set the player to have a negative YAcceleration. However, if the player collides with the ground using CollideAgainstMove, the YAcceleration will continue to accumulate the YVelocity value. Eventually this value will build up to be so large that the player will fall through the level. Even if this doesn't occur, the player will show weird behavior if it walks off of a ledge. To solve this, you can simply use CollideAgainstBounce instead of CollideAgainstMove. An elasticity of 0 will result in the same behavior as CollideAgainstMove, but the velocity will be modified according to the velocity to solve accumulation errors. You can try this in the demo above by setting the elasticity argument to 0.
In this hierarchy the entity itself is considered the "root" or . This means that by default absolute changes to position and rotation will affect ONLY the root object. In other words, if the VisibleRepresentation's X is changed in code, the change will not be drawn or persist next frame. To change the VisibleRepresentation's absolute position or rotation, code must either change the root's absolute values, or the relative values of the VisibleRepresentation.
This characteristic is not unique to - it applies to all including all shapes.
Fortunately, this is not the case. Since shapes are often children of other , any modification to position or velocity gets "pushed up" to the root of the attachment hierarchy.
The Polygon class provides collision methods and properties to simplify common game programming tasks. The following code creates two Polygons which turn red when they collide. Note that this uses input code. For information on using FlatRedBall input, see .
The Polygon class overrides the base ForceUpdateDependencies implementation. The Polygon's ForceUpdateDependencies method does the same thing as the base ForceUpdateDependencies implementation (updates the instance's absolute Position and Rotation values according to attachments); however, it *also* updates the Polygon's internal vertices.
In most situations you will not need to call ForceUpdateDependencies. Polygons will automatically update the internal vertices when performing any collision methods (such as CollideAgainst), or right before rendering a Polygon.
Since these two situations are common whenever a Polygon is used, you will usually not need to worry about calling ForceUpdateDependencies. However, if you are creating a Polygon and immediately wanting to use it with any method that may need to use its absolute vertices (such as GetPointsInside, then you will need to manually call ForceUpdateDependencies after instantiation.
The Clone method can be used to create a copy of a given Polygon. A cloned polygon will be identical to the original polygon. Cloned polygons can be used if you are loading Polygons from a file and want to create multiple instances of a given Polygon. For example, you may create a Polygon (which we'll call attack Polygon) in the PolygonEditor. Whenever your character attacks, you may want to clone the attack Polygon and add the newly-created Polygon to a list of polygons in your Screen, which can be used to test collision against enemies.
The IsPointInside method returns whether an absolute X/Y position is inside the calling Polygon. This function takes 3D position objects (such as Vector3) but ignores the Z value. Only X and Y are considered.
The following code creates a Polygon that is in the shape of a rectangle. When the user moves the cursor in the polygon it turns red; otherwise it is yellow.
Add the following using statements:
Define the Polygon at class scope:
Add the following in Initializing after initializing FlatRedBall (or CustomInitialize if using Screens):
Add the following in Update (or CustomActivity if using Screens):
The IsPointInside method requires that the Polygon has constructed its internal vertices. This means that you must either:
Wait for one frame so that the Polygon has a chance to render and have its internal vertices set properly. If your polygon is not rendered or part of the ShapeManager then waiting one frame will not result in the internal vertices being updated.
If neither of the above has happened, then the internal vertices will not be set and this function will always return false.
The CreateEquilateral static method is a method in the Polygon class which can be used to quickly create equilateral (same-length sides) Polygons.
The following code creates two Polygons. One is a 4-sided polygon. The other one is a very high-vertex count Polygon which can be used to draw smooth Circles. Keep in mind that high-vertex polygons like the one created in this example can be expensive when performing collisions.
Add the following using statement:
Add the following to Initialize after initializing FlatRedBall:
CollideAgainstMove can be used to test whether two shapes are touching. If the shapes are overlapping this method also moves one or both so that they no longer overlap. CollideAgainstMove also works between all types of shapes.
CollideAgainstMove is a common method used in games which include solid collision. Sometimes the collision is between two movable objects (such as a player and a box which can be pushed), and sometimes the collision is between a movable and static object (such as a player and a wall).
CollideAgainstMove lets you specify how objects should behave when colliding. The first argument is the object to collide against, the second is the mass of the caller, and the third is the mass of the object colliding against.
For example, to collide a Player entity against a wall (assuming the entity implements ICollidable):
Inversely to have a player push a block, and have the player not slow down at all when pushing the block:
To have both objects impacted equally by the collision, the same mass can be used. For example, if two cars collide (again assuming that the cars implement ICollidable)
Any value can be used for the mass of the two objects - you're not limited to using 0 and 1. We use 0 and 1 above to express intent - that one object should be mass-less, or that the two objects should have the same mass. For example, a heavier object could collide against a lighter object:
The following code loads a .plylstx to create a PositionedObjectList containing Polygons. Another Polygon is created which is controlled by the Keyboard. Move collision is used to keep the moving Polygon and the Polygons loaded from the .plylstx from overlapping. Notice that the mass variables can be modified to allow for different collision behavior.
Files Used: Smiley.plylstx
Add the following using statements:
Add the following at class scope:
Add the following in Initialize after initializing FlatRedBall:
Add the following in Update:
In brief mathematical terms, the CollideAgainstMove repositions the calling shape and the argument shape along the vector that is normal to the surface vector at the point of collision. The amount that each object moves when colliding depends on the two masses passed in to the method.
For example, consider the following situation. There are two Polygons - ball and surface. The ball has a positive XVelocity and negative YVelocity, causing it to fall down and to the right toward the surface Polygon:
Eventually the ball will overlap the surface. Let's assume that the following method is being called every frame:
In this case the ball is given a 0 mass and the surface is given a 1 mass. In other words, surface will never be moved by this method while ball will.
When a collision occurs, the vector of the edge where the collision happened is determined. Then the normal (perpendicular) vector is calculated and the shape(s) is (are) moved along the normal vector the required distance to separate them. For example, the first time ball penetrates surface, the following edge vector and reposition vector are calculated:
Since the ball has 0 mass, it will be moved by the full reposition vector. If the value were different, say .5 and .5, then ball and surface would each move half of the reposition vector. Of course, surface would move in the opposite direction as ball so that the two separate after the call.
Since this CollideAgainstMove resolves this penetration, it is never seen when the engine draws the shapes. But if the velocity of ball is not changed, then it will continue to penetrate surface every frame, then get pushed back out every frame. The result is what appears to be a smooth sliding movement across as follows:
If one of the colliding shapes is an AxisAlignedRectangle then the direction of the "move" (the reposition) is subject to this value. For more information, see the RepositionDirections page.
The CreateRectangle method creates a Polygon that is in the shape of a rectangle. Polygons created through the CreateRectangle method are no different than any other Polygon - the CreateRectangle method is simply a method that simplifies a common task.
Since Polygons and AxisAlignedRectangles have the same methods for performing collision and both inherit from the PositionedObject class, the decision of whether to use the Polygon or AxisAlignedRectangles depends on whether you need rotation support.
If you need to have rotation support, you should use the Polygon class. AxisAlignedRectangles do not consider rotation when drawn or when performing collision. However, because of this, AxisAlignedRectangle collision is faster than Polygon collision. If rotation is not needed, you should consider using the AxisAlignedRectangle class for performance reasons.
The following code creates a Polygon using the CreateRectangle method. The Polygon is then added to the ShapeManager to be visible.
Add the following using statements:
Add the following to Initialize after initializing FlatRedBall:
Notice that the values used in the above line of code are "scale" values. Scale values in FlatRedBall represent the distnace from the center to the edge of an object. For example, an object which has a ScaleX of 2 will have a width of 4. Therefore, the rectangle created in the code above will have a width of 4 units and a height of 6 units.
Although the Polygon object itself is not an IScalable, the CreateRectangle uses scale values. For more information on how scale values are used elsewhere in the FlatRedBall engine, see the IScalable page.
The OptimizeRadius method can be called to adjust the relative points of a Polygon to reduce its radius. The radius of a polygon is used internally to improve the performance of collision methods.
The OptimizeRadius method should be called if a Polygon's points are set or changed. This method will adjust the values of each relative point to optimize the radius of the Polygon. This method will also modify the position of the Polygon to keep the absolute position of each vertex constant. In other words, this method can be called after Polygons are positioned and it should have no impact on the final appearance or behavior of the Polygon - aside from improved performance.
The following code creates two Polygons. The Points are assigned such that the Polygon is not symmetric about its center. The Polygon called optimizedPolygon has its OptimizeRadius method called to make it symmetric. Finally, two circles are created to show the position and radius of both Polygons.
Add the following using statement:
Add the following to Initialize after initializing FlatRedBall:
The ProjectParentVelocityOnLastMoveCollisionTangent method is a very powerful and useful method for creating realistic collision. This method can be used in many types of games to help reduce "tunneling" - that is, objects hopping through other objects because of high speeds.
The ProjectParentVelocityOnLastMoveCollisionTangent property is a very long property name which takes a little bit of understanding. Let's break it down.
To explain what this property means, we'll start from the back and go to the front. First, we'll start with:
This phrase indicates that the property that we are investigating will give us information about the last time a "Move" collision was called. In other words, the last time was called. That's all!
Next, we'll add the Tangent property. In math, a tangent is a line which moves parallel (in the same direction) as a surface. The following image shows a blue line which is a tangent on the yellow circle
The phrase "on last move collision tangent" means that we are going to do something with the line (or Vector in more precise terms) that is tangential to the point where the collision happened the last time was called.
Next we'll look at the phrase Project Velocity. The "..." appears in the title because we're going to explain why the word "Parent" appears after this section. A projection in Linear Algebra is the operation done between two Vectors where one Vector is modified so that it is parallel to the second vector, and its length is set to be its length along the second vector. This is a difficult concept to explain in words, so observe the following picture:
In this picture Vector A is projected onto Vector B, resulting in Vector 3. Notice that the result will always be parallel to the projected-on Vector.
Assuming there is no bouncing, friction, or energy loss from the collision, this projection realistically represents the behavior of a moving object as it collides with another object. In other words, projecting the velocity on the tangent of the last collision will make the object appear to collide with the object and keep moving in a realistic manner after the collision.
ProjectParentVelocityOnLastMoveCollisionTangent provides the following overloads:
The minimumVectorLengthSquared argument is an argument that can control whether velocity modifications should be performed. Shapes which have the ProjectParentVelocityOnLastMoveCollisionTangent method store the vector that they were moved along in the last CollideAgainstMove call. This property is the called LastMoveCollisionReposition. The minimumVectorLengthSquared is a value which is compared against the square of the length of LastMoveCollisionReposition. This can be used to prevent the projection of velocity.
Most of the time this is not needed, and calling the no-argument version of ProjectParentVelocityOnLastMoveCollisionTangent uses a default value of 0 for minimumVectorLengthSquared. This argument can be used in very rare cases where large numbers can result in precision loss.
The following code creates two Polygons. The smaller polygon will move down and to the right, then slide along the larger polygon. Once the smaller polygon reaches the end of the larger polygon, it will continue to move to the right. In other words, it will lose its "downward" component of its velocity vector.
Add the following using statement:
Add the following at class scope:
Add the following to Initialize after initializing FlatRedBall:
Add the following to Update
Things to try: If you remove the call to
you will notice that the moving Polygon will continue to move downward after reaching the end of the larger Polygon. Give it a shot to see the difference.
ProjectParentVelocityOnLastMoveCollisionTangent is useful both for realistic physics, as well as semi-realistic physics as found in many platformers (such as Super Mario Bros.)
The following is a block of code which can serve as an example for how to handle collision in a platformer:
The Points property is an array of values which contain the position of each point on the polygon. These values are in "object space". In other words, they are relative to the containing Polygon's position and rotation values.
The Points property can be assigned - this essentially changes the shape of the Polygon. This is a more-efficient and often more-convenient way to change a Polygon rather than to construct a new one if your game requires dynamic shapes. For more information, see .
The values in the Points array are all relative to the containing Polygon. This means that if the Polygon moves or rotates, the values stored in the Points property will remain the same.
Add the following using statements:
Add the following at class scope:
Add the following in Initialize after initializing FlatRedBall:
Add the following in Update:
The ScaleBy method can be used to adjust a Polygon's points to make it larger or smaller. The ScaleBy method is relative to the current state of the polygon. This means that calling ScaleBy with any number other than 1 multiple times will continually change the polygon.
Calling ScaleBy multiple times will continually change the polygon. For example:
will double the relative value of each point. Calling the method again will double it once more.
Ignoring floating point inaccuracy, the following line of code will result in no changes to a Polygon:
Since ScaleBy modifies the points on a Polygon, the Polygon has no built-in way to preserve the original shape of the polygon. To do so, you will have to keep track of that information yourself:
All FlatRedBall shapes inherit from the class, so they inherit the X, Y, Z, and Position properties.
The Z value of shapes impacts their rendering, but not collision. In other words, shapes will render differently according to their Z value when rendering on a 3D camera, but they will collide as if their Z values are all the same.
The Position of the Polygon is a bit of an abstract concept because it may or may not represent the center of a Polygon. In fact, it may not even lie inside of a Polygon depending on how the Points values are set.
If you use the or functions, then the Position will represent the center of the Polygon. If you have constructed the Polygon yourself by setting the Polygon's , then the Position value relative to points depends on the values you've assigned to .
Shapes and non-Shapes which are on the same Layer (or which are all un-Layered) will not sort with each other. For more information see .
Finally we introduce the word "Parent". The reason Parent appears in this method name is because most shapes are never used alone, but rather are used as children of another object (usually ). Therefore, the shape shouldn't modify its own Velocity, but rather the Velocity of its parent.
This method is only used in more complex situations. You can usually accomplish the exact same behavior by simply calling with an elasticity of 0.
To retrieve the absolute position of each , the relative points must be converted into world (or absolute) coordinates.
The following code creates a Polygon which spins automatically and is repositioned based off of activity. Three are positioned in the world coordinates of each of the Polygon's points in the Update method. Since the are being positioned in world space, the position of the points must be converted to world space so that the appear in the proper position.
ScaleBy will modify the of the calling Polygon is used internally for collisions. There is no need to call after calling ScaleBy.
All FlatRedBall shapes inherit from the PositionedObject class, so they inherit the X, Y, Z, and Position properties. The Z value of shapes impacts their rendering, but not collision. In other words, shapes will render differently according to their Z value when rendering on a 3D camera, but they will collide as if their Z values are all the same.
The Position of the Polygon is a bit of an abstract concept because it may or may not represent the center of a Polygon. In fact, it may not even lie inside of a Polygon depending on how the Points values are set. If you use the CreateEquilateral or CreateRectangle functions, then the Position will represent the center of the Polygon. If you have constructed the Polygon yourself by setting the Polygon's Points, then the Position value relative to points depends on the values you've assigned to Points.
Shapes and non-Shapes which are on the same Layer (or which are all un-Layered) will not sort with each other. For more information see the ShapeManager.ShapeDrawingOrder page.
The Visible property controls whether a particular shape is drawn. Setting Visible to false will make a shape no longer be drawn. Setting Visible to true will make the shape drawn - even if it is not part of the ShapeManager.
All visible shapes are stored in lists in the ShapeManager for drawing. If a visible shape is made invisible (its Visible is set to false) then it is removed from the shape that stores all to-be-drawn shapes of that particular type. This characteristic of the Visible property improves the speed of drawing shapes, but can have a small performance impact on the Visible property.
The AttachTo method is a shortcut method which can be used to attach all contained shapes to the argument object. In other words, the following two blocks of code are identical:
and
The AttachTo method is a shortcut method which attaches all contained objects to the first argument. The ShapeCollection itself does not remember the existence of an attachment. This means:
If a shape is later added to the ShapeCollection, it must also be manually attached to the parent object, at least if the desired behavior is to have the entire ShapeCollection be attached as a whole.
It is possible to have different shapes in the same ShapeCollection attached to different objects.
The ShapeCollection provides a CollideAgainst method which tests if any shape contained in the calling ShapeCollection collides the argument shape. The CollideAgainst method can also be called to test if two ShapeCollections have any shapes that collide between the two.
The CollideAgainst method can be used to test collision against argument shapes. At the time of this writing, some shapes are not completely supported, but this is changing. If you encounter a NotImplementedException, please post on the forums requesting the implementation needed for your project.
The following overloads are available:
The ShapeCollection class can also collide against other ShapeCollections:
The ClosestPointTo returns the closest point on the calling Segment to the argument position. The closest point may be anywhere on the segment including the endpoints.
The following creates a Segment and a Line as a visible representation of the Segment. The update method finds the closest point on the Segment to the Mouse and positions a Sprite there.
Add the following using statements:
Add the following at class scope:
Add the following to Initialize after initializing FlatRedBall:
Add the following to Update:
The VectorFrom returns a vector from the argument position to the calling instance. This method returns the shortest vector which can be traveled along to reach the surface of the calling instance. This method does not return the distance from the calling point to the center of the shape.
The following code creates a concave Polygon and a Line. The Line draws the shortest line from the tip of the cursor to the Polygon. Add the following using statements:
Add the following at class scope:
Add the following in Initialize after initializing FlatRedball:
Add the following to Update:
The ShapeCollection class is a container for a variety of shapes. The ShapeCollection class is often used for collision maps and to define triggers. ShapeCollections are often loaded from .shcx files created by the PolygonEditor. ShapeCollections are a very common class due to the support of .shcx files in Glue.
The ShapeCollection provides a number of methods that can be used to perform actions on all contained Shapes (such as AttachTo). However, you may be working on a game that requires access to individual shapes. The ShapeCollection exposes its shapes. The following members are available:
AxisAlignedRectangles
AxisAlignedCubes
Capsule2Ds
Circles
Lines
Polygons
Spheres
These are all PositionedObjectLists which can be accessed like regular lists. You can add and remove elements from these lists as well as modify the individual members inside the lists. If your need to perform special actions on all elements in a ShapeCollection you can do the following:
Of course, you can also access the shapes using a foreach statement. Keep in mind that foreach statements may have performance penalties compared to regular for loops.
ShapeCollections can be created by loading .shcx files.
The following code loads a .shcx file into memory, adds all contained shapes to the ShapeManager. If you are using Glue you can add ShapeCollections to Screens and Entities just like other files. For an example on how to add a ShapeCollection to Glue, see the Beefball tutorial on creating Screen collisions. File used: ShapeCollection.shcx Add the following using statements:
Add the following to Initialize after initializing FlatRedBall:
Although most games do not need ShapeCollection saving support, FlatRedBall provides easy-to-use classes for saving a ShapeCollection. For more information, see the ShapeCollectionSave page.
All shapes in the ShapeCollection can be accessed through its member lists. The available lists are:
AxisAlignedRectangles
AxisAlignedCubes
Circles
Polygons
Lines
Spheres
The ShapeCollection can be thought of as a container for all of these lists. Therefore, you can use the individual lists to do anything you would do with a normal list such as:
The "thin Polygon problem", also known as collision tunneling, is a collision bug that is very commonly collision problem. As the name suggests, this bug occurs when dealing with thin shapes. Although we use Polygons in this discussion, it applies to AxisAlignedRectangles as well. This bug manifests itself in one of three ways:
Moving objects completely pass through Polygons. This is known as "tunneling".
Moving objects becoming stuck inside polygons.
Moving objects touching and being moved to an unexpected location on the surface of the Polygon.
Tunneling also exists with other shapes: Although this article talks specifically about Polygons, tunneling is a problem that exists with all other shape types and is not limited to Polygons. In fact, the incremental movement that is used in FlatRedBall which leads to tunneling is a general problem in game development, and the term "tunneling" is not a term which is simply used in FlatRedBall. If you are experiencing objects falling-through or passing-through other objects, this article may help you solve your problems - even if you're not using Polygons.
Of course you may be wondering "what defines thin?" Whether a Polygon is thin or not depends on the distance between lines of a Polygon relative to the velocity of objects that will collide against the Polygon. For example, if you have have a rectangle-shaped Polygon that is 1 unit thick, you probably will not experience any collision problems when an object moving at .1 units per second collides against your Polygon. If, on the other hand, the moving object is moving at 60 units per second, you will almost certainly see this bug.
The FlatRedBall collision system is a "historyless" collision system. This means that the collision performed at any given time does not consider the previous positions of objects. This approach has its benefits - it uses less memory, is slightly faster, and is very flexible. Of course, this can result in the bug discussed in this article. Let's look at an example of why a historyless collision system can result in tunneling Frame A shows a yellow ball (could be any shape really) falling towards a light-blue surface. In this example we'll assume that the ball is traveling at a very high speed. There is no collision for frame A, so the ball continues to fall normally. Because the ball traveled so quickly, it has nearly moved completely through the surface in one frame. Since FlatRedBall does look at the ball's position in frame A (it's historyless), the collision that it performs results in what we see in frame C: If you were to look only at frame B without looking at frame A, you may also prefer the end-position resulting in C.
Fortunately there are a number of solutions to the thin polygon problem. Which you employ depends on the type of problem you're seeing and what kind of options you have from the genre of game you're developing.
The first solution to the thin polygon problem is to simply make your objects not move as fast. This will resolve tunneling in almost every situation. However, this may not be an option for you because of the type of game you are developing. If you are making a racing game, then you may not want to slow the cars down just to resolve this issue (and you shouldn't either!). Of course, as we mentioned above, these problems occur when the speed of the colliding object is high relative to the *width* of the polygon. In other words, you can usually solve these problems by simply making your collision areas wider. This is especially effective if the borders of your game mark the absolute boundaries that the player can occupy. In other words, if your game takes place inside a room and the player can't walk outside of the room, then you can widen the walls to virtually any size you'd like to prevent tunneling.
Increasing the frame rate of your game by reducing the Game's TargetElapsedTime is an effective but very expensive approach to solving this problem. More information on this can be found here.
One solution which simulates increasing the frame rate, but isolates the performance impact to the code you are working with is to have your colliding objects (Entities) keep track of their last-frame position, then subdivide the distance they've traveled in one frame into smaller segments, and performing collision tests at each point. At a high level here are steps on how to do the collision tests:
In your Entity object which must perform multiple collisions, mark its position at the end of the Update method.
At the beginning of the frame, compare subtract the last position from the current position to identify the distance traveled.
Determine how much you want to subdivide your frame.
Write a loop as follows:
One of the most common situations where tunneling exists is in platformers that use YAcceleration for gravity. The reason this can cause so many problems is because the speed that the distance that the game character falls impacts how fast the character hits the ground. If your game includes large drops, you may notice that the character sometimes falls through the world when falling long distances. Most professionally-made platformers include a maximum falling speed for game play reasons. Implementing this maximum falling speed (also known as "terminal velocity") can both improve the feel of your game as well as possibly solve tunneling. For a discussion on how to limit falling speed, see this article.
Depending on the shape of your object you may be able to create a swept shape. The Capsule2D class is a useful shape for testing collisions of circles. You can also create your own Polygons or Lines to check if a collision occurred between two frames. If a collision did occur you may need to "rewind time" and subdivide the last frame to get more accurate results.
So far we've talked about tunneling issues, but there are also situations where two polygons may report a collision, but not separate correctly. This occurs in the example that uses the smiley polygon if you move the moving Polygon to the edges of the mouth polygon. To solve this, you can add additional points to the smiley polygon in the PolygonEditor as described here.
The section above describes a situation where polygon collision error occurs even if the speed of the moving Polygon is very low. In general the simpler shapes have more accurate collision than Polygons. Therefore, replacing the moving Polygon with a Circle will improve the accuracy of collision - while improving performance at the same time.
The AxisAlignedRectangles property is a property of type . Therefore, you can use this member the same way you would use a PositionedObjectList<AxisAlignedRectangle>.
The following shows how to loop through all rectangles in a ShapeCollection and perform collision between the rectangles and a Circle named CircleInstance:
The ShapeManager is responsible for managing "Shapes". Shapes are most often used for collision between objects. Objects which belong to the Shape categorization are:
All of these objects share the base class. These are the only objects which the ShapeManager manages. Although there is no "Shape" class, these objects are often referred to as shapes.
The ShapeManager can add any shape through its Add methods. The following code creates a Circle. Add the following using statement:
Add the following using statement:
To properly control visibility, set the value after a shape to the ShapeManager:
The ShapeManager can remove shapes that have been previously added to it through its Remove method. Assuming circle is an instance of a Circle that has been created through the ShapeManager:
Aside from addition and removal of shapes, the ShapeManager is responsible for drawing and managing Sprites. Calling AddPolygon, AddAxisAlignedRectangle, and AddCircle results in the ShapeManager managing and drawing the Sprite added or created by the method. It is possible to create manage-only or draw-only as shown by the following code:
Making shapes visible can be beneficial for debugging collisions. For the final version most games will want shapes to be invisible. Invisible shapes will collide the same as visible ones so in most cases visibility is useful simply during development. Of course, shapes can also be used to draw on-screen line graphics. Clearly, visibility control is important, but why not include shapes in the ShapeManager? The ShapeManager provides some behavior that can be helpful, but in many cases this behavior is not necessary. Shapes should be added to the ShapeManager if any of the following behavior is needed:
Attachment updates when collision methods are not called. See Note
Absolute or relative velocity.
Absolute or relative acceleration.
Absolute or relative rotation velocity;
Instructions
Note about attachments: Shapes which are attached to parents can have their absolute positions updated one of two ways:
By being added to the ShapeManager
By calling one of their collision methods or passing them as arguments to a collision method.
If a shape is to be used only for collision and is not visible, then it is not necessary to add it to the ShapeManager. When the collision method is called then it will automatically have its position updated. If a shape is visible, has a parent, and does not have collision methods being called, then it will need to be added to the ShapeManager to have its absolute position updated. If a shape is to be completely static - like a grid in a tool - then there is no reason to add it to the ShapeManager.
By being added to the ShapeManager
By calling one of their collision methods or passing them as arguments to a collision method.
Since the shape is neither part of the ShapeManager nor having any Collision methods called, then its dependencies are never updated - so the position of the parent has no affect on the child Shape. If you are interested in seeing the shape move with its parent, simply add it to the ShapeManager. If this shape is going to be used for collisions, then there is no need to add it to the ShapeManager - calling collision methods will automatically manage the dependency. This behavior will also be present if the shape is not visible, but you depend on the shape's absolute position. If you're depending on the shape's position outside of collision, be sure to add it to the ShapeManager.
The LastCollision properties in the ShapeCollection can be used to identify which shapes were collided with on the last collision. This is useful because many games need to know specifically which shapes were collided against to perform custom logic. For example, you may be working on a platformer which includes collision shapes representing spikes. If the character touches a spike then he should lose a life. The LastCollision properties enable checking for this information.
The following example shows how to check which shapes the user collided against. It will check collision against a shape collection. If the user touched an AxisAlignedRectangle which has the name that includes the word "Spike" then the game responds appropriately:
A segment is a object defined by two . It is not a and its points are absolute, which differentiates it from the class.
The following code creates two . One line rotates automatically while the other is controlled by input from the . The two call the AsSegment method to create Segments that represent the calling . The segment can then be used to find the point of collision between the two . If the resulting intersection point has valid coordinates then a collision has occurred. A red ball marks the intersection point between the two .
Add the following using statements:
Add the following at class scope:
Add the following in Initialize after initializing FlatRedBall:
Add the following in Update:
The ShapeCollection provides a CollideAgainstMoveWithoutSnag method which tests if any shape contained in the calling ShapeCollection collides the argument shape and moves the argument shape accordingly. This method is different from CollideAgainstMove where CollideAgainstMoveWithoutSnag is an order independent operation where CollideAgainstMove is a order dependent operation. CollideAgainstMoveWithouSnag may be slightly slower than CollideAgainstMove and is recommended only if the order of the collisions matters.
The CollideAgainstMoveWithoutSnag method can be used to test collision against argument shapes. At the time of this writing, some shapes are not completely supported, but this is changing. If you encounter a NotImplementedException, please post on the requesting the implementation needed for your project.
The following overloads are available:
The SortAscending method sorts all contained elements in the ShapeCollection by their position values on the given axis. In other words, if this method is called with an argument of Axis.X, then all contained shapes will be sorted so that their X values are increasing. The sort uses a stable insertion sort making it incredibly fast on nearly-sorted or already-sorted lists.
ShapeCollections are often sorted so that axis-based partitioning can be performed when testing for collisions. If the ShapeCollection will not change after being created, then it only needs to be sorted once (likely when it is first created). Otherwise, it needs to be sorted whenever a change occurs - which may be as frequent as every frame. However, sorting every frame and using partitioning is likely faster than performing a brute-force collision check.
The following code can be used to sort a ShapeCollection along its X axis, then test for collisions using axis-based partitioning:
Note that CalculateMaxRadii must be called in order for the ShapeCollection to know the maximum radii of all shapes (by category).
Adding a Shape to the ShapeManager will automatically make the shape visible. For example, the following code will result in a visible :
ScaleVelocity ( only)
Radius Velocity ( only)
The source of some confusion is what happens when a Shape is attached to a (like a ), but not added to the ShapeManager. If you write code which attaches a shape to a , make the shape visible, then move the parent you will notice that the shape does not move. Why is this the case? Well, again, there are two ways to update a shape's attachments:
Did this article leave any questions unanswered? Post any question in our for a rapid response.
Did this article leave any questions unanswered? Post any question in our for a rapid response.
- Talks about axis-based partitioning which is what the CollideAgainst uses internally when performing partitioned collisions.
The Visible property allows setting the visibility of all contained shapes in a ShapeCollection. It's important to note that the ShapeCollection does not itself store any visibility data, so there is no getter for Visible. The Visible setter loops through all contained shapes and sets their Visible property accordingly.
The following code shows how to make a ShapeCollection invisible. It assumes that there is a ShapeCollection named ShapeCollectionInstance:
The Remove method can be used to remove a shape from the ShapeManager. The Remove method will perform the following:
Remove the shape from being automatically updated by the ShapeManager (velocity, acceleration, attachments, etc)
Remove the shape from being drawn
Remove the shape from any PositionedObjectList it is a part of, including any objects that it is attached to
The following shows how to remove a shape:
One of the most common (incorrect) assumptions about shapes is that a removed shape will not collide with any other shapes. This is incorrect - collision does not require ShapeManager membership. Consider the following code:
When a collision between two shapes is performed, the collision code simply checks the shapes positions, rotations (if appropriate), and size values (such as ScaleX or Radius if appropriate) to see if there is any overlap. In the case of a Circle, the position and radius doesn't change when it is removed from the ShapeManager. The same goes for any other shape - removing a shape from the ShapeManager does not modify the shape and collision methods will still work.
The behavior described above may seem inconvenient. However, in most cases shapes exist in one of two places:
PositionedObjectLists inside a Screen or collision management class
Objects inside an Entity
Games may include a PositionedObjectList or ShapeCollection containing shapes. The behavior of Shapes which are removed from the ShapeManager is the same in either case. Remember, PositionedObjectLists and ShapeCollections both have two-way relationships with objects that they contain, so removing a shape from the ShapeManager will result in the shape also being removed from any PositionedObjectList or ShapeCollection that it is a part of.
If you have an Entity with a shape Object (which may be called "Collision"), removing the shape from the ShapeManager will not remove the shape from the Entity. This means that the Entity's Collision member will still be valid and report collisions. In this case, it is up to you to write logic in your Entity to control whether collision should still be performed. For example, you may do something like this:
If you are storing a reference to a shape in a class (not as a list) and you are performing collision with that shape, you will need to somehow identify that the shape has been removed. For example:
The ShapeCollection provides a CollideAgainstBounceWithoutSnag method which tests if any shape contained in the calling ShapeCollection collides the argument shape and moves the argument shape accordingly while also adjusting velocity. The "WithoutSnag" portion of the method name indicates that the method will attempt to perform the collision such that snagging will not occur. In other words, a row of rectangles should behave the same as one long rectangle.
This method is different from CollideAgainstBounce where CollideAgainstBounceWithoutSnag is an order independent operation where CollideAgainstBounce is a order dependent operation. CollideAgainstBounceWithouSnag may be slightly slower than CollideAgainstBounce and is recommended only if the order of the collisions matters.
The CollideAgainstBounceWithoutSnag method can be used to test collision against argument shapes. At the time of this writing, some shapes are not completely supported, but this is changing. If you encounter a NotImplementedException, please post on the forums requesting the implementation needed for your project.
The following overloads are available:
The 2D shapes in the FlatRedBall.Math.Geometry namespace provide useful methods for detecting and reacting to collisions. The following lists the 2D shapes:
These objects provide methods for performing collision against one another. While these objects can be positioned and in some case rotated in 3D space, the collisions they perform are limited to 2D. When two shapes perform collision methods, they ignore their Z values. Mathematically speaking, shapes are projected onto the XY plane, then the projections perform collision against each other. Therefore, if two shapes may appear to not be touching visibly, their collision methods may still indicate that collisions are occurring if they are overlapping each other.
CollideAgainst
X
CollideAgainstMove
X
X
CollideAgainstBounce
X
X
X
All shapes provide various CollideAgainst methods which can be used to check if two shapes are colliding and to adjust their position and velocity values to resolve the collision (to apply physics).
In most cases these methods are not called explicitly. Rather, these methods are called by CollisionRelationships which are created through the FlatRedBall Editor. However, there are times when these methods may be called directly in custom code. Common examples include:
To do additional checks inside a collision event handler for custom sub-collision. For example, to determine if a bullet has hit an enemy's weak point.
To perform collision in custom code where no collision relationships exist. For example, in a code-only project, or in a test project which is not using the FlatRedBall editor for most of its setup.
When modifying the FlatRedBall Engine, or when performing very high-performance logic.
Collision methods can be performed between collision shapes of the same type, or of mixed type. The most efficient form of collision is Circle vs Circle and AxisAlignedRectangle vs AxisAlignedRectangle. Mixing types, such as Circle vs AxisAlignedRectangle, is a slower form of collision. Polygon collision is slower than Circle and AxisAlignedRectangle regardless of whether it is mixed or Polygon vs Polygon.
Of course, even the slower collision methods tend to be fast enough for most real-world game development scenarios, especially when used in CollisionRelatoinships which are partitioned.
The Detach method is called on a child IAttachable to detach it from its Parent. Calling Detach has two effects:
Sets the calling IAttachable's ParentAsIAttachable (or Parent if using a PositionedObject) to null.
Removes the calling IAttachble from its parent's ChildrenAsIAttachables list (or Children if using a PositionedObject).
Assuming that childObject is attached to something, you can detach its follows:
The TopParent property returns the root of the attachment hierarchy. If the object has no attachment, then its TopParent property will be equal to itself.
CollideAgainstMove is a function which takes any shape type (such as AxisAlignedRectangle or Circle) and calls CollideAgainstMove between the argument and all shapes contained in the ShapeCollection.
This method is an alternative to writing loops for all of the contained objects and manually calling CollideAgainstMove. Using CollideAgainstMove has a number of benefits:
Less code to write - CollideAgainstMove will perform collision against all contained objects in a ShapeCollection. The ShapeCollection object can contain many types of objects, and manually writing for-loops means that one for loop for each type of object is required.
Partitioning (optional) - CollideAgainstMove can perform partitioning which can greatly speed up collision calls. See the later part of this page for information on partitioning.
The following example creates a ShapeCollection, populates it with a very large number of AxisAlignedRectangles, then performs CollideAgainstMove between a free-floating AxisAlignedRectangle and the entire ShapeCollection.
This example assumes that the ShapeCollection and AxisAlignedRectangle (named ShapeCollectionInstance and AxisAlignedRectangleInstance) are both valid instances. The syntax used here assumes a Glue screen. AxisAlignedRectangleInstance is also colored red to help differentiate between it and the static shapes.
Add the following to CustomInitialize:
Add the following to CustomActivity:
The example above shows how to use CollideAgainstMove with the optional partitioning arguments. To recap, to perform partitioned collision, the following are required:
The ShapeCollection must have CalculateAllMaxRadii called prior to the CollideAgainstMove method. If the ShapeCollection adds objects, or if the objects in the ShapeCollection change size, then CalculateAllMaxRadii must be called again.
The ShapeCollection must have SortAscending called prior to the CollideAgainstMove method. If any objects move or are added to the ShapeCollection, then this method must be called again. Notice that the argument for which axis to sort on must match the axis used in CollideAgainstMove. This axis should equal the most distributed axis.
The benefit of this call is that it is considerably faster than simply calling CollideAgainstMove. The downside is that it requires some setup (calling the two functions), and maintenance (calling the two functions again if anything changes). Keep in mind that CalculateAllMaxRadii and SortAscending can be expensive functions for large ShapeCollections, so this method of collision is most effective when the ShapeCollection does not change at all, or if it changes very infrequently.
The number of rectangles in this sample is limited to 5000, but this number could likely be increased to to be much higher. It is very likely that a program with a larger number of rectangles would experience slowdown from rendering the rectangles before it would experience any slowdown on collision due to the efficiency of the partitioned CollideAgainstMove method. If the rectangles were invisible, then the number could go very high without any performance problems.
The ShapeDrawingOrder controls whether all Shapes are drawn under or over other FlatRedBall-drawn objects (for example Sprites, Models, and Texts). Currently shape drawing order cannot be controlled by the shape's Z value; however, as of the June 2009 release of FlatRedBall, most shapes can be placed on layers which provides some control over ordering.
The ShapeDrawingOrder has two options:
UnderEverything
OverEverything (default)
Since the default for the ShapeManager's ShapeDrawingOrder is OverEverything, then all shape drawing will appear on top of other objects.
The following example adds a Sprite and a Line, then sets ShapeDrawingOrder to UnderEverything so that the added Line appears under the Sprite.
Add the following using statements:
Add the following to Initialize after initializing FlatRedBall:
The ShapeMerger can be used to merge shapes together to create one larger shape. This is used by the TileEditor to create level collision.
The following code creates two Polygons - one is the shape of a rectangle, the other is a hexagon. The rectangle is offset, then the two shapes are merged into one. Add the following using statement:
Add the following to Initialize after initializing FlatRedBall:
Did this article leave any questions unanswered? Post any question in our forums for a rapid response.
IAttachables provide properties and methods to control attachments.
An attachment is a relationship between two PositionedObjects in which one is identified as the parent object and one as the child object. The child object's absolute rotation and position values become read-only and its position and rotation are controlled by its parent's absolute position and rotation values as well as its own relative position and rotation values. The following examples highlight some of the uses of attachments:
Relatively positioned objects: There are certain objects which should always be positioned relative to another object, like wings on an airplane. It doesn't matter if the airplane is positioned at x = 0 or 50, nor does it matter if the plane is rotated. The wings should always appear in the same relative position to the airplane. Furthermore, we would like the wings to move with the airplane, so that we don't have to readjust their position every frame.
Orbits: Attached PositionedObjects do not have to be touching for the attachment to be valid. We could easily have one object orbit around another by attaching the orbiting PositionedObject to an invisible, spinning PositionedObject positioned at the center of the orbit.
The relative values in an IAttachable are effective only if an IAttachable has a parent. Otherwise they sit idle and do nothing to the IAttachable.
The following code creates two Sprites. The larger Sprite is the parent Sprite. The smaller Sprite is the child Sprite which is attached to the parent Sprite. Notice that attachments are created through the child and not the parent.
The AttachTo method creates a child-parent relationship between to IAttachables. The child IAttachable creates the relationship by calling AttachTo and passing the parent as the first argument. A parent can have any number of children, but a child can only have one parent. Calling AttachTo a second time breaks the first child-parent relationship and creates a new attachment between the calling instance and the instance passed as the first argument to the AttachTo method. The second argument determines whether relative values change, which also determines whether absolute values remain the same before and after the AttachTo call. For more information see the AttachTo page.
All IAttachables have relative versions of position, velocity, acceleration, rotation, and rotational velocity. Relative properties are named the same as their absolute counterparts prefixed with the word "Relative". If the IAttachable is a PositionedObject as is the case with Sprites, Cameras, Text objects, and Emitters, then the following relationships hold:
"Absolute" Property
"Relative" Property
X
RelativeX
Y
RelativeY
Z
RelativeZ
XVelocity
RelativeXVelocity
YVelocity
RelativeYVelocity
ZVelocity
RelativeZVelocity
XAcceleration
RelativeXAcceleration
YAcceleration
RelativeYAcceleration
ZAcceleration
RelativeZAcceleration
RotationX
RelativeRotationX
RotationY
RelativeRotationY
RotationZ
RelativeRotationZ
RotationXVelocity
RelativeRotationXVelocity
RotationYVelocity
RelativeRotationYVelocity
RotationZVelocity
RelativeRotationZVelocity
As mentioned before, if a PositionedObject has a parent, then its absolute values are read only. For example, consider the X (or Position.X) value. If an object has a parent, then its X position (ignoring rotation) is:
Therefore, if your code sets the X value, it will just get overwritten before rendering by the above code. This is the same for:
position
velocity
acceleration
rotation
rotational velocity.
While absolute position and rotation values are read-only, velocity and acceleration values should not be used at all. Let's examine why velocity is not usable. As mentioned above, attachments result in the absolute position being set every frame according to the Parent's absolute position and the child's relative position. That means that the Position variable is reset every frame. But the Velocity variable also changes the variable every frame. That means that both attachments and Velocity will modify the absolute position of your object. So what happens if you have a non-zero Velocity? The Velocity will add itself (considering time) every frame, but just before drawing, attachments will set the Position property - overriding the changes that occurred earlier in the frame when Velocity was applied. Reading Velocity can also be misleading because velocity is not by default a "reactive" property. That means that if an object is moved by its Parent, the Velocity property will not automatically update itself according to the movement performed due to the attachment. However, making velocity reactive is possible using "Real" values.
By default all PositionedObjects are positioned at their center (the exception to this is the Text object). This can be changed using attachments. First, let's look at the default center and points of rotation. We'll use a Sprite to show how this works (Sprite inherits from PositionedObject which implements IAttachable.
Stop right there! The code shown above for attaching is as brief as possible and simply shows the raw syntax behind attachments. However, you may be using Glue, and if so you shouldn't be writing the code above. Instead, you will likely have the above situation mostly set up - the PositionedObject will be the Entity itself and the Sprite will be a Sprite (or Entire Scene) Object in Glue. Instead of doing this in code, you should simply offset the Sprite in the SpriteEditor. Then when the Entity rotates, the Sprite will rotate about an offset naturally.
Two-way relationships - Explains how two-way relationships work between IAttachables and the lists they belong to.
Did this article leave any questions unanswered? Post any question in our forums for a rapid response.
The ShapeManager is responsible for managing "Shapes". Shapes are most often used for collision between objects. Objects which belong to the Shape categorization are:
All of these objects share the PositionedObject base class. These are the only objects which the ShapeManager manages. Although there is no "Shape" class, these objects are often referred to as shapes.
Any shape added to the FlatRedBall Editor (either as part of a Screen or Entity) will also be added to the ShapeManager by default. For example, the Player entity in the following screenshot contains an AxisAlignedRectangle which is automatically added to the ShapeManager.
The ShapeManager can add any shape through its Add methods. The following code creates a Circle. Add the following using statement:
Add the following using statement:
Adding a Shape to the ShapeManager will automatically make the shape visible. For example, the following code results in a visible Circle:
To properly control visibility, set the value after a shape to the ShapeManager:
The ShapeManager can remove shapes that have been previously added to it through its Remove method. Assuming circle is an instance of a Circle that has been created through the ShapeManager:
Aside from addition and removal of shapes, the ShapeManager is responsible for drawing and managing Sprites. Calling AddPolygon, AddAxisAlignedRectangle, and AddCircle results in the ShapeManager managing and drawing the Sprite added or created by the method. It is possible to create manage-only or draw-only as shown by the following code:
Note that circle2 is never explicitly added to the ShapeManager, but it will still be drawn. This behavior differs from other FlatRedBall objects such as Sprites which must be added explicitly to their respective manager to be drawn.
Shape Management can be a complicated topic because shapes can be managed in a number of different ways. The reason this complexity exists is because games often include a very large number of shapes for collision. For example, a tile-based game may include hundreds or thousands of shapes as part of a TileShapeCollection. Games with a large number of collidable entities (such as twin stick shooters) may similarly have hundreds or thousands of live shapes. Therefore, shape management can be customized for performance and functionality as necessary. The term management can refer to any number of every-frame operations which can be performed on a shape. These can be grouped into a number of categories:
Parent/Child Relationship Management - this results in a shape's position being updated according to its parent - usually an Entity instance.
Visibility/Drawing Management - this management results in the shape being drawn to the screen
Velocity/Physics Management - this management results in a shape's physics values such as Velocity, Acceleration, and Drag being applied every frame
Games which are concerned with performance should only apply every-frame management as necessary for shapes. Unnecessary management can lower the frame rate of your game, in some cases to the point of making the game unplayable. Shape management management can be performed in a number of ways:
Adding a Shape to the ShapeManager - this results in all management being applied, which is handy if you need all types, but this is expensive if shapes do not need full management.
Setting a shape's Visible to true - this adds a shape to the ShapeManager, but only to be drawn. A visible shape which is not explicitly added to the ShapeManager will only be drawn but other forms of management such as physics are not applied. Note that this is true unless the ShapeManager's SuppressAddingOnVisibilityTrue is set to true. See the SuppressAddingOnVisibilityTrue page for more information.
Attaching a shape to a collidable entity - collision functions perform parent/child relationship updates automatically because these types of relationships are very common.
Performing management in generated code - the FlatRedBall Editor provides functionality for performing management in generated code. This approach as the benefit of selectively applying updates only to properties which need management.
Performing management in custom code - custom code can perform custom management just like generated code. This is effective if you would like complete control over management.
Making shapes visible can be beneficial for debugging collisions. For the final version most games will want shapes to be invisible. Invisible shapes will collide the same as visible ones so in most cases visibility is useful simply during development. Of course, shapes can also be used to draw on-screen line graphics.
The source of some confusion is what happens when a Shape is attached to a PositionedObject (like an Entity), but not added to the ShapeManager. If you write code which attaches a shape to an Entity, set the shape's Visible to true, then move the entity, you will notice that the shape does not move. As mentioned above there are two ways to update a shape's parent/child relationship:
By being added to the ShapeManager
By calling one of their collision methods or passing them as arguments to a collision method.
Since the shape is neither part of the ShapeManager nor having any Collision methods called, then its dependencies are never updated - so the position of the parent has no affect on the child Shape. If you are interested in seeing the shape move with its parent, simply add it to the ShapeManager. If this shape is going to be used for collisions, then there is no need to add it to the ShapeManager - calling collision methods will automatically manage the dependency. This behavior will also be present if the shape is not visible, but you depend on the shape's absolute position. If you're depending on the shape's position outside of collision, be sure to add it to the ShapeManager.
Did this article leave any questions unanswered? Post any question in our forums for a rapid response.
Attachments are an effective way to keep one PositionedObject in a given position or rotation relative to another PositionedObject. However, attachments do not immediately modify children positions. This article will discuss when attachments are applied by the engine, exceptions to this rule, and how this behavior can be modified.
The following diagram shows a high-level view of the execution of a FlatRedBall game.
Attachment code is executed in three places:
FlatRedBallServices.Update - RelativeVelocity is applied to modify RelativePosition and RelativeAcceleration is applied to modify RelativeVelocity and RelativePosition. Although Relative values change in this call, they are not used to modify absolute values yet.
Game-Specific Update - Game-specific code creates attachments and modifies relative values. Modifying relative values does not result in absolute values being modified yet.
FlatRedBallServices.Draw - Just prior to drawing, all relative values are applied and absolute values are updated.
The important thing to note is that relative values do not immediately modify absolute values. For performance reasons, absolute values are modified according to relative values only once per frame.
Although the engine automatically updates absolute values according to relative values just before drawing, it may be necessary to perform this update in game-specific code. If so, the ForceUpdateDependencies method can be called. See the ForceUpdateDependencies code.
The MathFunctions class provides helper methods for performing common game math.
The following code returns the screen pixel coordinates of a . Remember, by by default in world coordinates:
Positive X points to the right
Positive Y points up
In pixel coordinates
Positive X points to the right
Positive Y points down (only when dealing with screen pixels)
Add the following using statements
Assuming myObject is a valid
The ParentRotationChangesPosition property controls whether a child IAttachable's absolute Position is modified by its Parent's rotation. By default this value is true, meaning that when an IAttachable's parent rotates, its absolute position will be calculated using its relative position as well as the parent's RelativeRotationMatrix.
To understand when ParentRotationChangesPosition is a useful property, consider the following situation. A top-down tank game uses tanks which can rotate freely. Each tank should have a health bar shown above the tank. The health bar is attached to the tank for convenience; however when the tank rotates, the health bar moves around the tank. To keep the health bar above the tank regardless of the tank's orientation, the health bar's ParentRotationChangesPosition can be set to false. Also, setting the health bar's is also probably needed in this situation.
The RotationMatrix property is a property that represents the full rotation of an IRotatable. The information contained in RotationMatrix combines the information of , , and .
The Matrix object contains numerous Vector3 properties which can be used to extract direction values. For example, the following code can be used to move a car forward in 2D space, assuming that the un-rotated car faces up:
For a more-complete example, check the Rock Blaster tutorial, specifically .
The ParentRotationChangesRotation property tells an object whether its rotation should be relative to its parent rotation. This is true by default but it can be set to false.
This code could be used to tell a health bar to not rotate when whatever it is attached to rotates:
Attachments create a relationship in which the child is both positioned and rotated relative to the parent. In some cases it is useful to suppress some of this behavior.
The ParentRotationChangesRotation property controls whether the rotation of a child is modified by the parents' rotation. In the following example the ship (represented by the red ball) rotates and moves according to input from the keyboard. The is attached to the but does not rotate with it.
Declare the ship at class scope:
In Initialize:
In Update:
Rotation controls the 3D orientation of . Although some such as and objects are 2D, all can be rotated in 3D space. Rotation can be represented using the RotationMatrix property or individual rotation components. These components (RotationX, RotationY, RotationZ) are measured in radians, not degrees: For more information on working with rotations, see the .
But this is not:
The following code creates three rows of Sprites. Each row is rotated on a different axis.
Matrices can be multiplied to apply rotation. The following code rotates an IRotatable around the X axis by PI radians. This is an effective way to rotate about an axis regardless of the orientation of the object.
Warning Continually multiplying matrices in this manner can result in your matrix not being orthonormal. This will throw an exception in FlatRedBall. To prevent this problem, consider using quaternions. For more information, see the following post on MSDN:
If an object is unrotated (the default orientation), then any rotation value will result in the object rotating about a world-world aligned axis. In other rotating a Sprite by using RotationZ will cause it to spin like a record. Rotating a Sprite by using RotationY will cause it to spin like a coin on a table.
However, if a Sprite has non-zero RotationX, RotationY, or RotationZ values, then setting another Rotation value may result in unpredictable rotations. Technically, the resulting orientation IS predictable; however, to understand the result of changing one of these values you must understand how Matrices are multiplied.
As expected the Sprite rotates about its center. To change the point that the Sprite rotates about, we need to use an additional PositionedObject. We'll add a simple PositionedObject, attach the Sprite to it, then make the PositionedObject rotate. Notice that we must add the PositionedObject to the SpriteManager or else the RotationZVelocity member will not apply automatically.
If a Shape is added in code, it must be removed in code as well, typically in a Screen or Entity's CustomDestroy.
See
Notice that although the is attached to the ship , it does not rotate. Try setting the ParentRotationChangesRotation property to true and observe the behavior.
IRotatables represent their rotations as individual components (RotationX, RotationY, and Rotation) as well as a combined RotationMatrix which is of Microsoft.Xna.Framework.Matrix type. For a conceptual understanding of matrices, see . For more information, see our wiki entry to understand more about how rotations work.
If you recall your math classes, you may be familiar with the degree measurement. FlatRedBall uses radians as a measurement for its rotations. For more information on radians vs. degrees and why FlatRedBall uses radians, see the .
Rotation can be directly controlled in two different ways. Just like position and velocity, rotation exposes three individual properties which can be individually modified to rotate an object (RotationX, RotationY, RotationZ). These are generally modified only when one axis is being changed on an object. Although it is possible to perform full 3D rotation by changing all three components individually, the order of rotation may cause unexpected results. When more complex rotation is required, use of the RotationMatrix property is recommended. This can be set to any matrix and the orientation will reflect the change. Also, the rotational components and RotationMatrix will reflect each other. Keep in mind that as the RotationMatrix is a property, it must be set and its individual values should not be changed. In other words, this is fine:
Just as velocity changes position for all managed , rotational velocity changes rotation for all managed . The following code creates a spinning .
Rotation can be used to make an object (such as a gun turret) face toward another object (such as an enemy). For information on how to perform this, see the wiki entry.
If you are going to be working with Matrices, we recommend instead simply performing the Matrix math in your own game logic and then setting the RotationMatrix property. This will give you complete control over how rotation values are combined and applied. For an introduction to Matrices, see our page. To really get a deep understanding of Matrix math, check out the .
The GetPositionAfterTime function will return the position of an object given a set start position, an initial velocity value, and a constant acceleration value (constant during the movement time). Note that this function does not consider an object's Drag value, so if a PositionedObject has a non-zero Drag, the results of this function will not match actual behavior.
The following code will calculate where an object will be given the set values:
Entities (including entities using the Top-Down plugin or the Platformer plugin) can slow down using acceleration. Your game may require calculating the distance that the entity will take to slow down. The following example shows how to calculate the distance it will take to slow down from full speed using the Top-Down plugin.
The RegulateAngle method can be used on a radian value to modify it so that it is always between 0 and 2*PI. This method is internally used on the PositionedObject class to keep all rotation and relative rotation values between 0 and 2*PI.
The method has two overloads: one that returns a modified float, and one that can modify a float passed as a ref:
This method can be used if it is more convenient for you to work with values between 0 and 2*PI. This method will not modify any values that are between 0 and 2*PI. It will modify values which are less than 0 and more than 2*PI.
For the graph below, we will use the following code:
0
0
PI
PI
-PI
1.5PI
2PI
0
3 PI
PI
AngleToVector converts a Radian value and returns a Vector3 pointing in the direction of that direction. Since a value of 0 points to the right, then AngleToVector will return a Vector of (1,0,0). As the angle increases the returned Vector3 will rotate clockwise.
The value returned from AngleToVector will equal a PositionedObject's RotationMatrix.Right given the same RotationZ.
AbsoluteToWindow converts an absolute coordinate to a window coordinate (where 0,0 is the top left of the screen). This can be used to identify the screen coordinates of a FlatRedBall object. This is sometimes necessary to translate between 2D and 3D coordinates, or to obtain screen coordinates for systems which use screen-based coordinates (like Gum).
The following can be used to obtain the screen coordiantes of a FlatRedBall Sprite:
The RoundToInt method can be used to round a float or double value to an integer value. The return value is rounded as an integer which allows for == checks without having to worry about loss of precision.
RoundToInt can be used to convert floats to integers
It will do mathematical rounding as well:
The GetPointInCircle method returns a random point inside a circle with the argument radius, centered at 0,0. This method returns an even distribution, as opposed to a "random radius, random angle" approach which returns a higher concentration of points at the center of the circle.
Add the following using statements:
Use this code to get a random point in a unit circle:
The RoundFloat method can be used to round a float to the nearest non-whole argument value (called multipleOf). The following shows the result of using RoundFloat with multipleOf = 1: RoundFloat(1.2f) -> 1 RoundFloat(3.8f) -> 4 RoundFloat(-8.3) -> -8 If your multipleOf = 2, you would see the following results: RoundFloat(1.2) -> 2 RoundFloat(3.8f) -> 4 RoundFloat(-8.3) -> -8 This method is very useful in tile-based games where your tiles are not centered on whole values.
The following shows how to use the RoundFloat method: Add the following using statement:
Add the following code wherever you need to use RoundFloat:
RoundFloat can be useful for identifying the index of an object based off of its position. For this example, consider a list of Buttons, stacked vertically. The first button appears at Y = 10, and each button is spaced 35 units away from the next. In this situation, you can convert any absolute Y value to the index of the nearest button as follows:
Due to rendering issues in DirectX 9, offsetting the camera by a small amount can correct visual artifacts. The following code shows how to adjust the camera so its position is always offset by .1 pixels (meaning, it X position might be 10.1, 11.1, 12.1, etc):
Returns the shortest distance to rotate from the first argument angle to the second argument angle. All values are in radians. The absolute value of the return value will never be greater than PI (half of a circle). If the value is positive then the shortest distance is achieved by rotating counterclockwise. If the value is negative then the shortest distance is achieved by rotating clockwise.
The AngleToAngle method can be used to turn one object toward a direction. The following code creates a Sprite which will turn toward the point where the cursor is located. Add the following using statements:
Add the following at class scope:
Add the following to Initialize after initializing FlatRedBall:
Add the following to Update:
The AngleToAngle can be used to check if one object is within an angle range. For example, you may be making a stealth game where the player must avoid being seen by enemies. We'll assume that the enemies have a property called ViewAngle which represents the edge-to-edge angle that the enemies can see. We'll also assume that this code has calculated the angle from the enemy to the player. For information on how to calculate this, see this page.
RotatePointAroundPoint is a method that can easily rotate one point around another. This method uses the System.Math.Atan2 method to calculate angles. All functionality provided by this method can also be obtained through System.Math.Atan2.
The following code creates a Sprite which rotates about the origin when the user holds down the space bar. Add the following at class scope:
Add the following to Initialize after initializing FlatRedBall:
Add the following to Update: