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...
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 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 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:
The AxisAlignedRectangle object supports changing its Color property by assigning a 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 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.
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:
GetRandomPositionInThis returns a Vector3 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 Circles 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 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 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):
The LastCollisionTangent returns the vector that represents the surface where the collision last occurred. This can be used for custom physics implementations.
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 this page.
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 this page.
Add the following at class scope in your Screen:
Add the following to CustomInitialize:
Add the following to CustomActivity:
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 AxisAlignedRectangle is a PositionedObject which is used for unrotated bounding box collision. This is preferred over Polygon collision if Sprites are not rotated due to speed and memory usage considerations. AxisAlignedRectangles share many similarities with the other shape classes (Circle, Line, and Polygon). For general information about shapes, see the ShapeManager wiki entry. For information on using AxisAlignedRectangles in Glue, see this page. AxisAlignedRectangles do not support being filled-in. They can only render as an outline. To render a solid rectangle, look at using Sprites with the Color ColorOperation
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 Polygon 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 this page.
The AxisAlignedRectangle class inherits from PositionedObject. This means that all properties which are available to PositionedObjects (excluding rotation) are available to AxisAlignedRectangles. For more information, see the PositionedObject page.
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:
Solid or moving collision can be performed with AxisAlignedRectangles as well as Polygons and Circles. 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:
Add the following at class scope:
Add the following in Initialize after Initializing FlatRedBall:
Add the following in Update:
For information on determining which side an object has collided on, see this page on LastMoveCollisionReposition.
Did this article leave any questions unanswered? Post any question in our forums for a rapid response.
Redirect to:
For more information see the .
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 .
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 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 forums for a rapid response.
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:
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 forums for a rapid response.
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.
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:
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 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):
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 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.
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:
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.
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.
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 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.
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 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:
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.
Due to the popularity of the Entity pattern, it is very common to have shapes attached to other PositionedObjects. Because of this common setup, all shapes have special behavior in their collision methods to reposition or change the velocity of their TopParent.
The attachment hierarchy is a collection of IAttachables 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 Entity.
In this hierarchy the entity itself is considered the "root" or TopParent. 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 Sprites - it applies to all IAttachables including all shapes.
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.
Fortunately, this is not the case. Since shapes are often children of other IAttachables, any modification to position or velocity gets "pushed up" to the root of the attachment hierarchy.
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 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 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:
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:
The Polygon class overrides the . The Polygon's ForceUpdateDependencies method does the same thing as the (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 , then you will need to manually call ForceUpdateDependencies after instantiation.
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 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:
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 then waiting one frame will not result in the internal vertices being updated.
Call
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.
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 CollideAgainstMove 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 CollideAgainstMove 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.
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 Entities). 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 CollideAgainstBounce with an elasticity of 0.
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 "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 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:
ScaleBy will modify the BoundingRadius of the calling Polygon is used internally for collisions. There is no need to call OptimizeRadius after calling ScaleBy.
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 .
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 .
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 have the same methods for performing collision and both inherit from the class, the decision of whether to use the Polygon or depends on whether you need rotation support.
If you need to have rotation support, you should use the Polygon class. do not consider rotation when drawn or when performing collision. However, because of this, collision is faster than Polygon collision. If rotation is not needed, you should consider using the 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 .
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.
A segment is a object defined by two Points. It is not a PositionedObject and its points are absolute, which differentiates it from the Line class.
The following code creates two Lines. One line rotates automatically while the other is controlled by input from the Keyboard. The two Lines call the AsSegment method to create Segments that represent the calling Lines. The segment can then be used to find the point of collision between the two Lines. If the resulting intersection point has valid coordinates then a collision has occurred. A red ball Sprite marks the intersection point between the two Lines.
Add the following using statements:
Add the following at class scope:
Add the following in Initialize after initializing FlatRedBall:
Add the following in Update:
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.
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 will result 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:
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
ScaleVelocity (AxisAlignedRectangle only)
Radius Velocity (Circle only)
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.
The source of some confusion is what happens when a Shape is attached to a PositionedObject (like a Sprite), but not added to the ShapeManager. If you write code which attaches a shape to a Sprite, make the shape visible, then move the parent Sprite 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:
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.
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 AxisAlignedRectangles property is a property of type PositionedObjectList. 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 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 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 forums requesting the implementation needed for your project.
The following overloads are available:
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:
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 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).
Axis Based Partitioning and Collision - Talks about axis-based partitioning which is what the CollideAgainst uses internally when performing partitioned collisions.
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 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:
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 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 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.
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 as a visible representation of the Segment. The update method finds the closest point on the Segment to the and positions a 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 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:
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:
If a Shape is added in code, it must be removed in code as well, typically in a Screen or Entity's CustomDestroy.
Did this article leave any questions unanswered? Post any question in our for a rapid response.
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 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: