Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
The following method can be used to determine the required initial magnitude required for a fired object to pass through a point at RelativeX, RelativeY.
The term "collision" in game development refers to both detecting whether objects are touching as well as changing the state of these objects when this touching occurs.
Almost all games requires some type of collision. For example, Pong used ball vs. paddle collision and Super Mario Bros. performs collision between Mario and the ground to test whether he should fall or not. Even games which might not seem to use collision likely do. For example, mouse-driven adventure games like King's Quest VI used collision to detect whether the cursor is overlapping certain areas when clicking.
The only games free from collision are pure text-based games, but it's likely that if you're working on one of those you probably won't need to use FlatRedBall.
Before we get into how FlatRedBall handles collision, let's cover some basic terminology.
We've already discussed what a collision is, so let's give some examples. The following image shows three situations and labels whether a collision is occurring:
Notice that collision doesn't just occur if the edges of two objects are touching. When FlatRedBall performs collision, it considers objects to be filled in. Therefore, if one object is inside of another, a collision is still occurring.
A move collision is a collision where one or both of the involved shapes are repositioned during the collision to prevent overlap. The term "move" was selected because this type of collision can result in the shapes moving to new positions. The amount that each shape is moved depends on their masses relative to each other. It is very common to make one object static by giving it a non-zero mass and giving the other object a mass of zero. The following image shows the result of a move collision assuming that the rectangle is static and the circle is not.
To use FlatRedBall terms, move collisions will adjust the position of the involved shape(s) if a collision does occur.
A "bounce" collision is a collision which can be used to simulate physics. As the name implies, bounce collisions cause objects to bounce against each other. Just like move collisions, bounce collisions use the masses of two objects to calculate how the forces from the bounce should be applied. Just like with move collisions, it is common to have one object in a bounce collision have a non-zero mass while the other has a mass of zero. This causes one of the shapes (the one with the non-zero mass) to not be affected by the collision.
You're probably pretty familiar with the word "shape". In FlatRedBall, shape defines a shape like a square, triangle, or circle. To be a little more precise, a shape is an object which is a PositionedObject, has collision methods, and is managed by the ShapeManager class.
You may also be expecting there to be a Shape interface or class that all shapes implement. Actually, there isn't such an interface or class. The reason for this is performance. Initial performance measurement tests in FlatRedBall showed that the majority of time spent in collision loops is the actual method calls. In other words, we compared the amount of time required to call collision methods to the amount of time calling "empty" methods and over half of the time was spent in the method call.
Of course this ratio can fluctuate depending on the complexity of the collision method and the frequency of collision occurrence. However, making a method virtual makes the method call itself take longer than otherwise, and we decided to give up the convenience of a base class for better performance. FlatRedBall provides a number of shape classes, and this list may continue to grow as more sophisticated collision needs are satisfied. If you'd like to find out which shape classes are available, check out the ShapeManager page.
Most games use Shapes to detect whether collision is occurring; however, not all games keep collision Shapes visible. Therefore, it is likely that your game will use Shapes for collision and another type of object for drawing (such as Sprites). In this situation, the Shapes that you use will approximate the collidable area of your visible object. In some cases simple Shapes such as Circles and AxisAlignedRectangles will be sufficient. In cases where more complex collision shapes are needed, the Polygon object is a good alternative. In more complex situations multiple Shapes can be used.
As you'll see in the next sections where we begin writing some sample code, Shapes have a Visible property which can be used to control whether the FlatRedBall Engine draws them. In the following examples the Shapes that are created through the ShapeManager will automatically be set to be Visible. When using Shapes only for collision and attaching them to other objects, it is not necessary to create them through or add them to the ShapeManager. Keep this in mind as you read other documentation about Shapes.
Collision can be detected between shapes simply by calling CollideAgainst. The following code example creates two Circles. One Circle is positioned at the point of the mouse. If a collision occurs, the moving Circle becomes orange. Otherwise, it is blue. Add the following using statements:
Add the following at class scope:
Add the following to Initialize after initializing FlatRedBall:
Add the following to Update:
WARNING: The code above may not compile because the Color struct is not qualified (the code doesn't know which namespace it is in). Which "using" statement you add to your code depends on which version of FlatRedBall you are using. For more information, see the Color page.
The CollideAgainst method is a method that only returns whether collision has occurred - it does not modify the calling Shape or the argument Shape. The following sections describe methods which do.
The CollideAgainstMove method returns a true or false depending on whether the calling Shape and the argument Shape are touching. This method also modifies the Position of one or both Shapes.
Keep in mind that since this method modifies the Position, we should not directly modify the Positions in custom code every frame. Doing so would result in the CollideAgainstMove and our own code both attempting to control the Position of the involved Shapes. While this won't cause a crash, it may result in unexpected or undesirable behavior. In most cases where CollideAgainstMove is used, the Shapes involved (or their Parent Entities) are controlled either by Velocity or Acceleration.
We'll use Velocity for the following example: Use the same using statements, class-level declarations, and Initialize code as above, then replace the custom Update logic with the following:
The first "1" argument is the mass of the first object. The second "1" is the mass of the second object. Try changing the first argument to 0. This will make secondCircle immovable. Then try changing the first argument back to 1, then the second to 0. This will make the moving circle unaffected by secondCircle.
The third method that we'll look at is CollideAgainstBounce. The CollideAgainstBouncegives us all of the same functionality as CollideAgainstMove( true/false return value as well as repositioning), but it also modifies the involved Shapes' Velocity values. For bouncing to behave properly, we have to make sure that we're not controlling the involved Shapes' position or velocity. Therefore, this leaves us with controlling acceleration. To use CollideAgainstBounce, simply replace your custom Update code with the following:
Now the two Circles will collide against each other and bounce. Of course, you will probably notice that this setup results in secondCircle being moved off of the screen very quickly. You may want to change the firstCircle's mass to 0 to keep the secondCircle on screen so you can test multiple collisions.
This article covers a lot of information about collisions; however, this topic has a lot of depth. To find out more, start at the ShapeManager page and follow the links to the information you're interested in.
Although FlatRedBall XNA provides a Circle class for collisions, you may be interested in writing your own custom circle collision code for customizable behavior. What follows is a conceptual discussion of circle collision.
The most basic form of collision returns true or false to indicate if two circles are touching. To keep things lightweight, consider a class Circle with three properties:
X
Y
Radius
These properties are all we need to perform collisions between two circles. By definition circles are perfectly uniform. No point on the edge of a circle is any further from the center than any other point, and all points on the edge are the distance of the radius away from the center. This makes collision very simple. Conceptually, to perform a circle collision we compare the distance from the centers of the two circles and see if it's greater than the sum of the two radii (plural of radius). Given two circles, the following code determines whether they are touching:
That works well, but efficiency-minded programmers might realize the usage of the Sqrt function when calculating the distanceBetweenCircles. We could do the following to speed things up slightly:
We simply square the sum of the circles' radii to avoid calling the more expensive square root method System.Math.Sqrt.
The image above shows a circle move collision before and after the repositioning occurs. In this example, the top circle stays static while the bottom circle moves to solve the overlapping. The blue dotted line is the line between the center of the two circles and the black line with arrows on the end outlines the distance which must be moved to solve the overlapping. To keep the circles from overlapping we need to find out the properties of the black line. More specifically we need to know the length and direction of that line. The most obvious relationship is that the black line lies directly on top of the line connecting the center of the two circles. In other words, the direction or angle of that line is the same as the direction or angle of the line connecting the center of the two circles. To calculate an angle, we'll use the System.Math.Atan2 method:
Now we have our angle stored. Next, we need to calculate the distance that the circles need to move over. Remember from the previous example that if the distance between two circles is less than the sum of the radii, there is a collision. If the distance between two circles equals the sum of their radii, then the two circles will be barely touching, as in the image on the right. The following relationship is true as well:
If the distance between the circles is equal to the sum of the radii, notice that distanceToMove is 0. Therefore, to calculate the distance by which to move:
At this point we have the angle and the distance by which to move the circles. To keep things simple, I'll just move circle2:
Circle bouncing is a simple way to introduce basic physics into your game. While circle bouncing can be calculated with only trigonometry, linear algebra provides us with a cleaner and faster solution. Circle vs. circle bouncing collision is actually very similar to circle vs. flat surface collision. How is this so? Consider the following image:
Although the example I've shown so far highlights one circle which is moving bouncing off of another which is (and remains) stationary, this is not always the case. In fact, either circle could be moving. Fortunately we can generalize the code so that it will work regardless of who is moving and who is stationary:
Now performing the dot product of the relative velocity vector and the tangent vector gives us the length of the velocity component parallel to the tangent (the length of the orange).
And we simply multiply the normalized tangent vector by the length to get the vector component parallel to the tangent (orange line).
Simply subtracting the velocity component parallel to the tangent (orange) from the relative velocity gives us the velocity component perpendicular to the tangent (red).
Now we have all of the information necessary to perform the collision. At this point we can make some choices regarding how we want the collision to affect the circles. To make both circles bounce off of each other like pool balls after a collision, the velocity component perpendicular to the tangent (red line) should be applied to both. To make one circle move, it should be applied to it twice.
To make just one circle move:
Many video cards expect textures to be "power of two". That means that images used in FlatRedBall should have their width and heights matching one of the following values:
The Rotation property on entities makes it easy to create a moving entity from another rotating entity like a turret. The Rotation property provides four values which can be used to set the velocity of a newly-created entity:
Up
Down
Left
Right
Your code should use one of the four mentioned above depending on which direction the turret faces when it is not rotated at all. For example, if an unrotated turret faces to the right, then the Right property might be used to create an entity, as shown in the following code:
Rotation can be used to rotate an object to "face" another object. For example a turret may rotate to face an enemy. If the cursor is to be used as a target, the following code can be used to rotate a Sprite so it faces the cursor. The assumption on this code is that there is some identifiable "front" to the Sprite such as the nose of a plane or the tip of a turret. When the Sprite is unrotated (has a RotationZ of 0) the front of the Sprite will face a particular direction. This direction will be the "offset" variable.
When presented with a current angle and a target angle, rotating clockwise or counter-clockwise is not always evident. The following method helps with this calculation:
First, keep in mind that a Sprite's RotationMatrix property has a relationship with the RotationX, RotationY, and RotationZ set of values - updating one automatically updates the other. Therefore, even if you only use the individual rotation values, you can still read from the RotationMatrix for finding the real-world positions of a vertex. There are 4 steps for finding the position of a vertex. 1. Determine the vertex that you want to edit. For this example, I will do the bottom right. The position of the vertex of a default, untouched Sprite would be (1, -1, 0). At ScaleX and ScaleY = 1, these are the coordinates of the bottom right point.
2. Next, scale the vector according to the Sprite's Scale values:
3. The RotationMatrix is what is actually used when the Sprite for its rotation. We can take the point and transform it by the matrix:
4. Finally, the vector's coordinates are relative to the center of the Sprite, so move them by the Sprite's position.
For this discussion we will assume that rectangles are axis aligned (not rotated). Also, rectangles can be defined either by their center point (as is the case with FlatRedBall AxisAlignedRectangle) or by their top-left corner (as is the case the MonoGame Rectangle struct).
For this discussion we will use the MonoGame Rectangle struct, although the concepts are similar for FlatRedBall.
Specifically we will be using the following properties:
Top
Bottom
Left
Right
These properties clear the ambiguity that comes from using X and Y for a Rectangle.
Conceptually two rectangles are colliding if they overlap, otherwise they are considered not overlapping. Two rectangles may touch one another but this is not considered a collision, although in practical terms if rectangles are positioned using float
values, then whether they overlap or not may be subject to floating point inaccuracy. For this discussion we'll ignore floating point inaccuracy.
Visually it can be easy to identify if two rectangles are colliding. Logically we need to perform checks to see if they overlap.
To understand the logic, let's focus specifically on the collided case from the image above.
In this case, the right-side of the blue rectangle is further to the right than the left side of the pink rectangle.
In fact, it is impossible for two rectangles to collide without this being the case. Any situation where the two rectangles collide must have the blue rectangle be further to the right than the left side of the pink rectangle.
This concept is important for two reasons. First, comparing "opposite" edges is ultimately how we can detect if collisions occurred. Second, we will use these "opposite" edge distances later to determine how to separate rectangles.
Of course, we cannot detect a collision only by using one edge. The right-side of the blue rectangle could be further to the right (have a greater X value) than the left-side of the pink, but there may not be a collision, as shown in the following image:
Therefore, for collision to be checked, we must actually check all four sides. Conceptually the checks are:
Blue right must to the right of pink left
Blue left must to the left of pink right
Blue top must be above pink bottom
Blue bottom must be below pink top
If all four of these conditions are true, then a collision has occurred. In code, this would look like this:
As mentioned in the comments notice that positive Y points down when using XNA (MonoGame/FNA) rectangles. If using FlatRedBall shapes, the top/bottom checks would be inverted to account for it using positive Y as up.
Detecting whether a collision has occurred is the first step in resolving collisions. For many games, the next step is to separate the rectangles so that they no longer overlap.
For this section we will assume that the logic for the code is as follows:
Move an object (apply velocity to change its position)
Detect whether collision has occurred
If collision has occurred, separate the objects to prevent overlapping
Visually, this may look like this:
Of course, all of the three steps happen in one frame, so from the user's point of view they only see the rectangle move from position A to position B, as shown in the following image:
We talked about how to detect whether collision has occurred (step 2), so now we need to determine how to reposition the rectangle (step 3).
If we take a look at the collision, it probably makes sense to us that the rectangle is repositioned by moving "up" - after all, the blue rectangle came from above, so it should end up resting on the pink rectangle. But the question is - why should the rectnagle move straight up? In other words, why do we choose to move the rectangle into position A in the diagram below? Note that the rectangle sizes have been made smaller to help with visualization:
There are a few ways to answer this question. The first question is - when separating two shapes, the shapes should be moved by the shortest distance possible to resolve the separation. In other words, moving to A requires moving the blue rectangle the shortest possible distance - moving to B or C requires moving a longer distance.
Another way to look at it is - when we move the blue rectangle, we should move the rectangle perpendicular to the surface with which it collided. In other words, the direction that we move the rectangle should create a right angle with the surface, as shown in the following diagram:
Knowing that the rectangle must move perpendicular to the surface means that the rectangle can cannot be moved diagonally, as shown in the diagram above with movements A, B, and C, at least when colliding with a surface that is perfectly vertical or horizontal, as will be the case with an axis aligned (unrotated) rectangle.
Since the rectangle can move only in one of four directions, then that means that any collision can only be resolved by moving the blue rectangle to one of four spots. For example, if the blue rectangle falls inside of the pink rectangle, then there are four possible ways to resolve the overlap as shown in the following diagram:
Of course we can tell just by looking at the diagram that the correct way to resolve the overlap would be to move the rectangle to A - because that's the shortest distance.
Of course, visually identifying the shortest distance is easy, but we need to write code that can do it.
We can modify our code from above to both detect and also provide the separation vector - a vector which can be used to move the blue rectangle so that it no longer overlaps the pink rectangle:
The method of repositioning explained above is used to reposition an object so that it is pushed back when overlapping. Most of the time this approach is sufficient, but sometimes this approach can result in issues which are referred to as collision tunneling.
Collision tunneling occurs when a moving object moves so fast that it overlaps another object in a way that causes it to be pushed out the other side.
For example, consider the following diagram:
The diagram above shows a typical movement and reposition. This results in the blue rectangle being repositioned to the left, so that it rests along the left side of the pink polygon. however, if the blue polygon were moving even faster, it might cross over the halfway point of the pink polygon resulting in collision tunneling, as shown in the following diagram:
From the player's point of view this all happens in one frame, so the collision seems to "pop" out of the other side.
This problem can be difficult to solve in a general way, especially when dealing with more complex shapes. The following options exist as solutions:
Limit the movement of your objects to make collision tunneling less likely
Increase the thickness of collision areas, especially boundaries surrounding your level
Increase the "physics framerate" so that collision is performed more frequently
Perform ray-casting or collision between elongated shapes
Add additional checks such as whether a shape that was repositioned started and ended on the same side (X or Y)
The next most common behavior performed when conducting collisions is known as "move" collisions in FlatRedBall. These collisions move objects when a collision occurs so that they no longer overlap. The first step is to understand the concept of how this works with circle collision.
If the moving circle bounces off of the circle at the bottom at the very top point, it will bounce as if it hit a flat surface. The same is true for any other point. Don't believe me? Look at the image and turn your head sideways. In more mathematical terms, the circle on top bounces off of the circle on the bottom as if it collided with a flat surface whose slope is defined by the tangent of the circle at the point of impact. Once we find the tangent at the point of collision, all we have to do is reflect the velocity on that tangent then assign the velocity to the bouncing circle. Now our problem is split into two smaller problems. Note that the previous two sections on determining collision and performing move collision will likely be used prior to performing the bouncing code. That is, you'll want to make sure your circles actually have collided, and you'll also want to separate them so that they don't get "stuck" together. Since we'll be using linear algebra to solve this problem, we'll define our tangent as a vector rather than a angle. There are a few ways to identify the point of impact for the collision, but for this example we'll simply assume the point of impact lies where a line drawn between the two circles touches the either of the circles. Since we've already performed our move, our scenario may look like this: Fortunately, the actual point of impact doesn't matter to us. Only the vector that is parallel to the tangent at that point. This is simple to calculate. The tangent is perpendicular to the line drawn between the center of two points. I will use XNA's Vector2 syntax for all Vector2's.
Once we have the vector parallel to the tangent of the circle at the point of impact we can reflect the velocity on this vector. To do this, we need to break the velocity vector into two vectors - the vector perpendicular to the tangent and the vector parallel to the tangent as shown in the following diagram. Finally, we take the component of the velocity vector that is perpendicular to the tangent, then subtract it twice from the original vector to get the new bounce vector as shown by the green line in the following diagram. If you're not familiar with linear algebra you may be wondering how to calculate the component of the velocity vector that is perpendicular to the tangent (the red line). Fortunately, this is relatively easy. We simply need to "project" the velocity vector on the tangent and that will give us the velocity vector component parallel to the tangent vector (orange line). For our projection to work correctly, the tangent vector must be normalized (have a length of 1).
Exponential Notation | Expanded Notation | Value |
---|
Rotation is the property or collection of properties which control the orientation of an object. In other words, it controls which way the object is "facing". Rotation can be used to make a planet rotate on its axis, to rotate a space ship according to user input, or to spin particles to give them more natural-looking behavior. Objects which implement the interface can be rotated. Rotation is performed using radians.
The FlatRedBall Engine uses instead of to measure orientation.
For more information, see the .
This page discusses the details of how rectangle collision works in games. If you are using FlatRedBall, then rectangle collision is handled for you automatically through the object. If you would like more information about how rectangle collision works in FlatRedBall, or if you would like to implement your own rectangle collision, this page provides a deep dive on rectangle collision.