Month List

RecentComments

Comment RSS

XNA for Windows Phone 7 and Physics

by Ioannis 8. February 2011 22:23

In this post, we will see how easy it is to create an XNA world for Windows Phone 7 that obeys the laws of physics. To our quest, our weapon will be the Farseerphysics engine. You will be amazed when you realize how interesting a simple circle on the phone’s display becomes when you add some physics to its world! So let our quest begin…

Download the zip file from the project’s site. Unzip its contents and locate the folder containing the sources of the Farseer engine (at the time of this writing the folder is named “Farseer Physics Engine 3.2 XNA”).

Open Visual Studio and create a new project of “Windows Phone Game'” type (important note: if you are a newbie in XNA game development you are strongly encouraged to follow thisseries of blog posts and also download the developer tools from here).

The game we will be making will be as follows: The user sees a sprite on the screen and it gives momentum to the sprite (yellow ball) by applying a gesture to the phones display. The sprite should be bouncing at randomly placed obstacles on the display and it should hit the randomly placed silver balls (ouaou that’s interesting!)

 

SpaceGame

 

In an XNA Game for WP7 your canvas is 800x480 pixels size. Therefore we have created a background image in Photoshop of this size. The sprites that will be used in our game are the following:

 

Sprites

 

And now it is time to get started. When you have created your “Windows Phone Game” project, Visual Studio has created an extra project where you will add your sprites. We add all the aforementioned images (sprites) there following the well known “Add Item” procedure:

image

 

Now go to your solution’s folder and copy the Farseer project you have located before along with its containing folder to your solution folder. Right-click on your solution and select “Add/Existing Project”. Select the .csproj file for WP7 of the Farseer physics engine located in the folder you ‘ve just copied. Finally add a reference at your XNA game project to the engine (if you feel lost download the project at the end of this post and you will see the final state of the folders/projects). If you did everything correctly the solution will compile successfully. And this is all the plumbing you have to do. Now, let us do some physicz!

 

First we create the definitions of our sprites along with some lists of the positions of the obstacles and balls:

 

private Texture2D _background;
private Texture2D _myBall;
private Texture2D _floor;
private Texture2D _otherBall;
private Texture2D _obstacle;

private List<Vector2> _otherBallPositions;
private List<Vector2> _obstaclePositions;

private Random _r;

 

The definition of the random number generator will be used in the creation of the scene. In the LoadContent() method we create out Textures and select their positions on the display (note the random creation of a number of obstacles and balls and their positions):

 

protected override void LoadContent()
{
    spriteBatch = new SpriteBatch(GraphicsDevice);
    
    _background = Content.Load<Texture2D>("Background");
    _floor = Content.Load<Texture2D>("Floor");
    _myBall = Content.Load<Texture2D>("MyBall");
    _obstacle = Content.Load<Texture2D>("Obstacle");
    _otherBall = Content.Load<Texture2D>("OtherBall");

    _obstaclePositions = new List<Vector2>();
    for (int i = 0; i < _r.Next(1, 6); i++)
        _obstaclePositions.Add(new Vector2(_r.Next(100, 700), _r.Next(100, 400)));

    _otherBallPositions = new List<Vector2>();
    for (int i = 0; i < _r.Next(1, 6); i++)
        _otherBallPositions.Add(new Vector2(_r.Next(100, 700), _r.Next(100, 400)));

}

 

Initially we will draw everything on the display in the Draw() method as follows:

 

protected override void Draw(GameTime gameTime)
{
    GraphicsDevice.Clear(Color.CornflowerBlue);
    spriteBatch.Begin();
    spriteBatch.Draw(_background, new Vector2(0, 0), Color.White);
    spriteBatch.Draw(_floor, new Vector2(0, 459), Color.White);
    spriteBatch.Draw(_myBall, new Vector2(40, 432), Color.White);
    for (int i = 0; i < _obstaclePositions.Count; i++)
        spriteBatch.Draw(_obstacle, _obstaclePositions[i], Color.White);
    for (int i = 0; i < _otherBallPositions.Count; i++)
        spriteBatch.Draw(_otherBall, _otherBallPositions[i], Color.White);
    spriteBatch.End();
    base.Draw(gameTime);
}

 

This will generate a nice scene but everything will be static. Here is where we apply out physics engine to bring everything to life. Define a World object and create it in the Initialize method:

 

private World _world;

protected override void Initialize()
{
    _r = new Random();
    _world = new World(new Vector2(0,9.81F));
    base.Initialize();
}

 

The parameter is the gravity which we set it as a vector perpendicular to our x-axis (as is in real life). Now for every sprite in your world  you need to define its Body (mass, physical properties) and its Shape (its geometry). In the Farseer physics world you have to define the two and then connect them (to represent a single object) with a Fixture, actually saying that a body with mass X will be represented as a rectangle (for example) with Y,Z dimensions. Since this may be too much code to write, you may use a factory to do the job for you. For example for the floor (a rectangular body at the bottom of our game scene) we define:

In the LoadContent() method we create the Fixture for the floor which automatically defines a body of mass with density 1 and a rectangular shape of size the same as the floor’s texture:

 

private Fixture _floorFixture;
protected override void LoadContent()
{
    (…)
    _floor = Content.Load<Texture2D>("Floor");
    (…)
    _floorFixture=FixtureFactory.CreateRectangle(_world,_floor.Width,_floor.Height,1);
    _floorFixture.Body.BodyType = BodyType.Static; 
    _floorFixture.Body.Position=new Vector2(0, 459);

    (…)
}

 

Note that there is no connection with the texture, at least in code. The only thing we use the texture for is when we draw it on the position of the body. In the update method, we tell our world to process the position of the bodies (the Step method gets a parameter defining how much time has passed since the last step for the world) and then in the draw we use the result to draw the floor. The time that has passed is the actual-real time:

 

protected override void Update(GameTime gameTime)
{
    (…)
    _world.Step(gameTime.ElapsedGameTime.TotalSeconds);
    base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
    (…)
    spriteBatch.Draw(_floor, _floorFixture.Body.Position, Color.White);
    (…)
    base.Draw(gameTime);
}

 

Here is the first tricky part. If you use the aforementioned placement, the position of the floor texture will not correspond to the position of the floor body. This is because FarSeer considers the local shape’s origin point (0,0) to be at the center of the rectangle while XNA considers the same point (0,0) to be at the top left cornet of the rectangle. This is illustrated in the following figure:

image

Due to this, the “FarSeer world” and the “Visible world” have different opinions on where the body is located since the position of a body/texture defines the position of its local origin:

image

To compensate for this you need to do two things. First, place the floor to location (400,469), which will place the body in the correct position in the FarSeer world (left) and then change the local origin when drawing the texture from (0,0) to (400,10) (right) which will also position correctly the texture in the “Visible world”:

image

 

This in code is translated as follows:

 

    (…)
    // The following is in the LoadContent() method!
    _floorFixture=FixtureFactory.CreateRectangle(_world,_floor.Width,_floor.Height,100);
    _floorFixture.Body.BodyType = BodyType.Static;
    _floorFixture.Body.Position=new Vector2(400, 469); //THIS IS DIFFERENT NOW
    (…)
    // The following is in the Draw() method! 
    spriteBatch.Draw(_floor, _floorFixture.Body.Position, null, Color.White, _floorFixture.Body.Rotation, 
                      new Vector2(_floor.Width / 2,_floor.Height/2), 1, SpriteEffects.None, 1);
        

 

When you compile and run the program now nothing seems to have changed but believe me the physics world is there. To prove this just change the definition of Body.BodyType from BodyType.Static to BodyType.Dynamic and the floor will just fall from its position obeying the Newtonian rule of gravity! Let’s now follow the same approach for the other sprites in our world (the principle is exactly the same so below is the complete listing):

 

protected override void LoadContent()
{
    spriteBatch = new SpriteBatch(GraphicsDevice);
    
    _background = Content.Load<Texture2D>("Background");
    _floor = Content.Load<Texture2D>("Floor");
    _myBall = Content.Load<Texture2D>("MyBall");
    _obstacle = Content.Load<Texture2D>("Obstacle");
    _otherBall = Content.Load<Texture2D>("OtherBall");

    _floorFixture=FixtureFactory.CreateRectangle(_world,_floor.Width,_floor.Height,1);
    _floorFixture.Body.BodyType = BodyType.Static;
    _floorFixture.Restitution = 0.7f;
    _floorFixture.Body.Position=new Vector2(400, 469);

    _otherBallFixtures = new List<Fixture>();
    for (int i = 0; i < _r.Next(1, 16); i++)
    {
        Fixture Temp = FixtureFactory.CreateCircle(_world, _otherBall.Height / 2, 1);
        Temp.Body.Position = new Vector2(_r.Next(100, 700), _r.Next(100, 400));
        Temp.Body.IsStatic = false;
        Temp.Restitution = 0.7f;
        _otherBallFixtures.Add(Temp);
    } 

    _obstacleFixtures = new List<Fixture>();
    for (int i = 0; i < _r.Next(1, 6); i++)
    {
        Fixture Temp = FixtureFactory.CreateRectangle(_world,_obstacle.Width,_obstacle.Height,100);
        Temp.Body.BodyType = BodyType.Static;
        Temp.Body.Position=new Vector2(_r.Next(100, 700), _r.Next(100, 400));
        Temp.Restitution = 0.7f;
        _obstacleFixtures.Add(Temp);
    } 
}

 

And the Draw() is as follows:

 

protected override void Draw(GameTime gameTime)
{
    GraphicsDevice.Clear(Color.CornflowerBlue);
    spriteBatch.Begin();
        spriteBatch.Draw(_background, Vector2.Zero, Color.White);
        spriteBatch.Draw(_floor, _floorFixture.Body.Position, null, Color.White, 
                         _floorFixture.Body.Rotation, new Vector2(_floor.Width / 2,_floor.Height/2), 
                          1, SpriteEffects.None, 1);
        spriteBatch.Draw(_myBall, new Vector2(40, 432), Color.White);
        for (int i = 0; i < _obstacleFixtures.Count; i++)
            spriteBatch.Draw(_obstacle, _obstacleFixtures[i].Body.Position, null, Color.White, 
                             _obstacleFixtures[i].Body.Rotation, 
                             new Vector2(_obstacle.Width / 2, _obstacle.Height / 2), 1, 
                             SpriteEffects.None, 1);
        for (int i = 0; i < _otherBallFixtures.Count; i++)
            spriteBatch.Draw(_otherBall, _otherBallFixtures[i].Body.Position, null, Color.White, 
                             _otherBallFixtures[i].Body.Rotation, 
                             new Vector2(_otherBall.Width / 2, _otherBall.Height / 2), 1, 
                             SpriteEffects.None, 1);
    spriteBatch.End();
    base.Draw(gameTime);
}

 

Note that we have also defined the Restitution coefficient for our bodies which defines how much of the initial velocity is lost when the ball will bounce on the obstacle (1 for example will have the ball bouncing endlessly like being made from a very elastic rubber, while a 0 will make the ball behave like it is made from iron).

Now when you run the program all the balls will fall to the floor and will also bounce. But the first thing you will notice is that they are too slow. You can go experimenting by raising the gravity value or increasing the time in the Step() method but those approaches are all trial and error and you will not get to far by this. The actual problem is the fact that you are playing with very large numbers in the FarSeer engine. While gravity and mass are defined in Kg and m/sec2 without any issues, you are defining shapes like the floor that are 800 meters long and 20 meters tall since the pixel to meter ratio is 1:1. This causes the engine to overflow. To get more realistic results you need to define the pixels/meter ratio to something different. In our case, let’s say that 1 pixel is 50 meters. This means that our view will be 16m wide and 9,6m tall (800x480 pixels) which is fine. Define a new private field as follows:

 

private float _pixelsPerMeter = 50;

Now when you define a shape you need you divide by this. For example:

 

_floorFixture = FixtureFactory.CreateRectangle(_world, _floor.Width / _pixelsPerMeter, 
                                                       _floor.Height / _pixelsPerMeter, 1f);
_floorFixture.Body.BodyType = BodyType.Static;
_floorFixture.Restitution = 0.7f;
_floorFixture.Body.Position = new Vector2(400 / _pixelsPerMeter, 469 / _pixelsPerMeter);

 

That is you are translating everything from pixels to meters for the FarSeer world. When you draw you need to do the opposite as follows:

 

spriteBatch.Draw(_floor, _floorFixture.Body.Position * _pixelsPerMeter, null, Color.White, 
                         _floorFixture.Body.Rotation * _pixelsPerMeter, 
                          new Vector2(_floor.Width / 2, _floor.Height / 2) , 1, SpriteEffects.None, 1);

Now when you compile and run everything will make more sense. It takes approx. 1 sec for the ball to fall from the top to the bottom (4km) at 9.81 g. Well let’s see:

image

Well as you can see you have a perfectly realistic world that obeys the laws of physics without experimenting. If you change your ratio to 25 your world is bigger, your balls are higher and it will take them twice the time to reach the bottom. The only thing left to do is apply some force to the yellow ball by the use of gestures.

First we define the yellow ball as a body too, like we did for the other sprites and then we add in the Update() method the following:

 

while (TouchPanel.IsGestureAvailable)
{
    GestureSample gesture = TouchPanel.ReadGesture();
    if (gesture.GestureType == GestureType.Flick)
        _myBallFixture.Body.ApplyForce(Vector2.Divide(gesture.Delta, 2.0f));
}

 

This will constantly check for gestures and apply them as a force to the ball. The last thing to be done is to enable this type of gestures at the initialization method as follows:

 

TouchPanel.EnabledGestures = GestureType.Flick;

 

And this concludes our introduction to the physics engine. The project can be downloaded  

.

 

Shout it

XNA 2D Basic Collision Detection with Rotation (Scene2Sprite)

by Ioannis 16. February 2010 07:54

In the previous post we have worked with collision detection between roated sprites. In this post we will deal with the situtation where we want to detect whether a specific sprite that can be rotated is in a specific area in the scene (eg a car is on the road in the scene).

Suppose that you have the following scenario:

 

A car is driven by the user around the scene. When the car hits the boundaries of the road, the car should lose all of its speed. The user uses the UP/DOWN keys to acelerate/break and LEFT/RIGHT for steering.

You want to detect the situation where the car is going out of the road bounds. A way to do this is as follows:

Use a second texture of the road, one that you will use for collision detection which has a specific plain color for the road:

Then get the bounding rectangle of the rotated texture that is parallel to the window's axis:

Within this rectangle check whether there is one point that is on the car's texture but the equivalent point on the road texture is not gray.

This programmatically is achieved as follows:

To get a rotated texture alone from scene, you use an additional back-buffer where you draw the rotated car's texture and then get a snapshot of it. The AdditionalRenderTargetForCollision is initialized once in the main game loop (download the project to see how):

public static RenderTarget2D AdditionalRenderTargetForCollision { get; set; }

private static Color[] _getSingleSpriteAloneFromScene(SpriteBatch spriteBatch,Texture2D textureToDraw,
                             Vector2 Position,Vector2 Origin,float Theta,List<Vector2> RectanglePoints)
{
    spriteBatch.GraphicsDevice.SetRenderTarget(0, AdditionalRenderTargetForCollision);
    spriteBatch.GraphicsDevice.Clear(Color.Black);
    spriteBatch.Begin();
    spriteBatch.Draw(textureToDraw, Position, null, Color.White, 
                                     Theta, Origin, 1.0f, SpriteEffects.None, 0);
    spriteBatch.End();
    spriteBatch.GraphicsDevice.SetRenderTarget(0, null);
    Texture2D SceneTexture = AdditionalRenderTargetForCollision.GetTexture();
    Rectangle BoundingRectangle = _getBoundingRectangleOfRotatedRectangle(RectanglePoints);
    int PixelSize = BoundingRectangle.Width * BoundingRectangle.Height;
    Color[] TextureData = new Color[PixelSize];
    SceneTexture.GetData(0, BoundingRectangle, TextureData, 0, PixelSize);

    return TextureData;
}

The code that checks for bounds is as follows:

public static bool SpriteIsOnValidArea(Texture2D TextureScene, Texture2D TextureSprite, Vector2 PosSprite, Vector2 OrigSprite, List<Vector2> RectSprite,float ThetaSprite,SpriteBatch spriteBatch,Color OKColor)
 {
     Color[] TextureDataSprite = _getSingleSpriteAloneFromScene(spriteBatch, TextureSprite, PosSprite, OrigSprite, ThetaSprite, RectSprite);
     Color[] TextureDataScene =  new Color[TextureScene.Width*TextureScene.Height];
     TextureScene.GetData(TextureDataScene);


     Rectangle RectangleSpriteBounding = _getBoundingRectangleOfRotatedRectangle(RectSprite);
     for (int i = RectangleSpriteBounding.Top; i < RectangleSpriteBounding.Bottom; i++)
         for (int j = RectangleSpriteBounding.Left; j < RectangleSpriteBounding.Right; j++)
             if (TextureDataScene[i * TextureScene.Width + j] != OKColor && TextureDataSprite[(i-RectangleSpriteBounding.Top) *
                 RectangleSpriteBounding.Width + (j-RectangleSpriteBounding.Left)] != new Color(0,0,0,0))
                 return false;
    
     return true;
 }

 

The code initially gets the car's rotated texture and the scene's texture. It then uses a method to calculate the bounding rectangle of the rotated sprite's texture. Then, within this rectangle it tests for the sprite's pixels one by one. If it locates even one texture's point that is not on the selected color (OKColor) it returns false (the car is out of bounds).

The project that performs this kind of collision detection can be downloaded here.

 (You may also want to check out a newer post on integrating a Physics Engine with XNA here)

kick it on DotNetKicks.com

Shout it

Tags:

XNA

XNA 2D Basic Collision Detection with Rotation (Sprite2Sprite)

by Ioannis 10. February 2010 07:26

(If you are in a hurry and do not want to know how it's done but just do it, dowload the project and extract the CollisionDetection2D.cs file to include in your project that contains the collision detection methods)

In my previous post ,we have seen several collision detection techniques that can be used in 2D games in XNA. In that post, we have left out the changes that affect our code when textures can also be rotated. In this post, I intend to fill this gap by picking up from exactly where we stopped and adding rotation to the equation.

Therefore, if you haven't read the previous post , I recommend that you go and give it a look. In this post we will cover what happens when we want to check for collision between two sprites that can be rotated.? In the next post we will work with the case where the collision needs to be checked with elements from the scene (such as a car in a road), and the sprite (eg the car) can be rotated.

Prior to considering the two aforementioned problems lets see how we can rotate a texture in XNA. Rotating is done by using a different overload of the _spriteBatch.Draw method. We want our sprite (DrawableGameComponent) to be decorated with two more properties:

private Vector2 _textureReferencePoint;
public Vector2 TextureReferencePoint
{
    get { return _textureReferencePoint; }
    set { _textureReferencePoint = value; }
}

private float _rotationAngle;
public float RotationAngle
{
    get { return _rotationAngle; }
    set { _rotationAngle = value; }
}

(...)

The first is the reference point of the texture and the second is the angle of the rotation. The reference  is the point that is used as reference when we want to put a texture to a specific position. It is expressed in texture coordinates which means that point (0,0) is the upper-left corner of the texture. In the basic draw method where we do not use the texture's reference point, XNA defaults to point (0,0). So when we say that our texture should be drawn at _position (a Vector2 object) the texture's reference point is moved to _position and the texture's pixels are drawn based on that point:

If we change the texture's reference point, then, the new reference point will be moved to _position, resulting in a different placement of the texture at _postion. So if reference is set to (texture.width/2,texture.height/2) then:

The texture's reference point plays also a central role in rotation since it is the center around which the rotation will be performed. The following figure shows two 90 degrees rotations around different reference points:

 The rotation angle is measured in rads. You can use degrees and convert them to rads using the following formula:

 

((2 * (float)Math.PI) / 360) * AngleInDegrees.

 

Having those two properties available, we can now use a different overload of the SpriteBatch.Draw method to rotate our texture as we draw it on screen (the rotation will be performed around the TextureReferencePoint and the texture's TextureReferencePoint point will be placed at _position):

 _spriteBatch.Draw(_texture, _position,null,Color.White,RotationAngle,TextureReferencePoint,1.0f,SpriteEffects.None,0f);

 

The 1.0f stands for scaling (play with it to make your texture smaller/bigger).

And now for the tricky part that concerns collisions:

 

Sprite/Sprite collision detection (extends the previous post)

The first thing to be done is to adjust our bouding shapes accordingly since now the texture's origin has changed and _position points at the central point of the texture and not its upper left corner. Then, we need to take into consideration that the texture is rotated so along with it we must rotate the bounding shape.

We use the following method that rotates a point (PointToRotate) around an arbitary origin (OriginOfRotation point):

private Vector2 _rotatePoint(Vector2 PointToRotate,Vector2 OriginOfRotation,float ThetaInRads)
{
    Vector2 RotationVector = PointToRotate - OriginOfRotation;
    Vector2 RotatedVector = new Vector2()
    {
        X = (float)(RotationVector.X * Math.Cos(ThetaInRads) - RotationVector.Y * Math.Sin(ThetaInRads)),
        Y = (float)(RotationVector.X * Math.Sin(ThetaInRads) + RotationVector.Y * Math.Cos(ThetaInRads))
    };

    return OriginOfRotation + RotatedVector;
}

Apparently in the case where we use bounding circles, we do not have to do anything apart from taking into consideration that the origin of the texture is not at (0,0) but at (texture.width/2, texture.height/2) and therefore the center of the bounding circle is just the _position vector as follows:

public Vector2 CircleCenter
{
    get { return new Vector2(_position.X,_position.Y); }
}
public int CircleRadius
{
    get { return Math.Max(_texture.Width / 2, _texture.Height / 2); }
}

But with rectangles and triangles there is the need of rotating the bounding shape along with the texture. We use the aforementioned rotation method as follows. For the triangle:

public Vector2 TrianglePoint1
{
    get { return _rotatePoint(_position - TextureReferencePoint, _position, RotationAngle); }
}
public Vector2 TriangleOffset1 { get; set; }
public Vector2 TrianglePoint2
{
    get { return _rotatePoint(_position - TextureReferencePoint + TriangleOffset1, 
                                                _position, RotationAngle); }
}
public Vector2 TriangleOffset2 { get; set; }
public Vector2 TrianglePoint3
{
    get { return _rotatePoint(_position - TextureReferencePoint + TriangleOffset2,
                                                _position, RotationAngle); }
}

And for the rectangle we keep the same properties upper left point, width and height and we also use the rotation angle and the rotation origin.

For the circle and the triangle the collision detection methods demonstrated at the previous post work perfectly. But the method used for the bounding rectangles fails since it does not support rotation. To solve this we divde the rectangle in two triangles and then we use the same method we use  for the triangles. Therefore we do not pass the upper left corners and the widths and heights but the individual points of the rectangle making a method that can detect collisions for any 4point shape. You can see this in action (see the methods _createTrianglesFromRectangle() and BoundingRectangle() from the project).

But how do we deal with Per-Pixel collision detection? First, we do a collision detection for the rotated bounding rectangles by using the previously mentioned "bounding rectangle" approach. If this detects a collision the we need to check a per-pixel collision. But to do this we need to know the overlapping area of two rotated bounding rectangles which is kinda difficult. Actually, even if we could find the overlapping area of the two rectangles it would be difficult to perform a per-pixel collision detection on this area which could be of any shape (not parallel to the axis).

So instead of that we use a bounding rectangle that encloses the rotated texture as shown in the figure (check the _getBoundingRectangleOfRotatedRectangle() method of the project):

All we want to do is get the pixels within this bounding rectagles and perform a per-pixel collision detection on them. Apparently those are not the pixels of the texture only. The plot thickens...

A solution is a follows:

  • Draw the texture on screen.
  • Get a snapshot of the screen at the bounding rectangles of the rotated textures.
  • Perform a PerPixel collision detection on those two snapshots.

But how can we take a snapshot that contains only the two textures and not the background? Well, we create an imaginary drawing surface and we draw on that surface the textures one by one. Upon drawing each one on the imaginary surface we take the desired snapshots. Then we have what we need to perform perpixel collision detection the "traditional" way (Check the methods _getSingleSpriteAloneFromScene() and PerPixelWR() from the project).

  (You may also want to check out a newer post on integrating a Physics Engine with XNA here)

Shout it

kick it on DotNetKicks.com

Tags:

XNA

Powered by BlogEngine.NET 2.0.0.36

ITPRO DevConnections 2011

Creative Commons License

Programming Blogs - BlogCatalog Blog Directory Add to Technorati Favorites

MVP Award


Ioannis Panagopoulos





This blog is using BlogEngine.Net and is hosted in the hoster below. I have not experienced any problems installing BlogEngine.Net in the host and I am satisfied with the host's response times. Therefore I recommend it.


DiscountASP Add