Multiple shapes

Topics: Developer Forum
May 12, 2011 at 4:06 AM

Hi. I'm thinking about using farseer on my XNA project, but I need to be sure of one thing first.  My project requires each game object to have two "colliders" (I think in farseer the equivalent is Shape), one that matches the objec visually and it will be used for moving on a terrain, and another "collider" for some interactions with other objects. I haven't found this infomation, is this possible? Also, is there any kind of "doxygen" documentation for the API or there's just the code.

Thanks in advance, great work farseer dev team.

May 12, 2011 at 5:05 PM

Fixtures are what handle the collision detection and all the properties associated with that such as shape.

So yes, you should have no problem with your game objects having multiple where each can be keyed to events to handle whatever you need.  You can make them ignore collisions with each other even though they may overlap.

 

I'm not aware of any detailed documentation other than the website tab above and the code comments.  There could be something out there I'm not aware of though.

May 12, 2011 at 9:07 PM

Thanks for your answer. I've being trying the examples and samples and made my own HelloWorld. But then I tried to move things around to see what happens and the collisions just stopped after a while. I've searched the forum and what I've found is that I should never move a body position explicitly. I modified the Awake property of the moving body every frame to make the collisions work again, but I think see this is not a good solution. In my project, the player controls two characters: one on the ground that follows the terrain and farseer just gives me a good solution for its behavior, and a flying character with no inertia and no delay between the moment the player uses the controls to move or stop it. Is there a way to achieve this and still having the collision detection without having to set Awake to true every time?

I also tried the DebugView but when I move the Camera, they don't stay aligned with the sprites anymore. Any idea why? My code is bellow. Thanks again

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;


using FarseerPhysics;
using FarseerPhysics.Collision;
using FarseerPhysics.Dynamics;
using FarseerPhysics.Factories;

using FarseerPhysics.DebugViews;


namespace TesteFSE
{
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        // XNA
        private GraphicsDeviceManager graphics;
        private SpriteBatch spriteBatch;
        private GamePadState oldGPS;

        // Sprites
        private Texture2D npc1;
        private Texture2D npc2;
        private Texture2D ground;
        private const float scale = 0.6F;

        // FSE simulator
        private World world;
        private Body npc1Body;
        private Body npc2Body;
        private Body groundBody;
        Vector2 npc1Position;

        // Debug
        private DebugViewXNA debugView;
        private bool showDebug;

        // Camera
        private Matrix view;
        private Matrix projection;
        private Vector2 cameraPos;
        private Vector2 screenCenter;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            graphics.PreferredBackBufferWidth = 2000;
            graphics.PreferredBackBufferHeight = 1000;

            Content.RootDirectory = "Content";

            world = new World(new Vector2(0, 1F));
        }

        protected override void Initialize()
        {
            base.Initialize();
        }

        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            // State in the first frame
            oldGPS = GamePad.GetState(PlayerIndex.One);

            #region Camera
            view = Matrix.Identity;
            cameraPos = Vector2.Zero;
            screenCenter = new Vector2(graphics.GraphicsDevice.Viewport.Width / 2F, graphics.GraphicsDevice.Viewport.Height / 2F);
            projection = Matrix.CreateOrthographicOffCenter(0f, ConvertUnits.ToSimUnits(graphics.GraphicsDevice.Viewport.Width),
                                                            ConvertUnits.ToSimUnits(graphics.GraphicsDevice.Viewport.Height), 0f, 0f,
                                                            1f);
            #endregion

            #region NPC1
            // Sprite
            npc1 = Content.Load<Texture2D>("Images/Speero");

            // Position
            npc1Position = screenCenter + new Vector2(-500F, 100F);
            npc1Position = ConvertUnits.ToSimUnits(npc1Position);

            // Shape + Fixture
            npc1Body = BodyFactory.CreateRectangle(world, ConvertUnits.ToSimUnits(255F * scale), ConvertUnits.ToSimUnits(144F * scale),
                                                    1F, npc1Position);

            // Physical Properties
            npc1Body.IgnoreGravity = true;
            npc1Body.Restitution = 0.3F;
            npc1Body.Friction = 0.5F;
            npc1Body.BodyType = BodyType.Dynamic;

            #endregion

            #region NPC2
            // Sprite
            npc2 = Content.Load<Texture2D>("Images/Geera");

            // Position
            Vector2 npc2Position = screenCenter + new Vector2(0, -400F);
            npc2Position = ConvertUnits.ToSimUnits(npc2Position);

            // Shape + Fixture
            npc2Body = BodyFactory.CreateCircle(world, ConvertUnits.ToSimUnits(128F * scale), 1F, npc2Position);

            // Physical Properties
            npc2Body.Restitution = 0.3F;
            npc2Body.Friction = 0.5F;
            npc2Body.BodyType = BodyType.Dynamic;
            #endregion

            #region Ground
            // Sprite
            ground = Content.Load<Texture2D>("Images/BG");

            // Position
            Vector2 groundPosition = screenCenter + new Vector2(0, 200F);
            groundPosition = ConvertUnits.ToSimUnits(groundPosition);

            // Shape + Fixture
            groundBody = BodyFactory.CreateRectangle(world, ConvertUnits.ToSimUnits(256F * scale), ConvertUnits.ToSimUnits(64F * scale),
                                                     1F, groundPosition);
            groundBody.IsStatic = true;
            groundBody.Friction = 0.5F;
            groundBody.Restitution = 0.3F;
            #endregion

            #region DebugView
            debugView = new DebugViewXNA(world);
            debugView.LoadContent(graphics.GraphicsDevice, Content);
            debugView.AppendFlags(DebugViewFlags.Shape);
            debugView.AppendFlags(DebugViewFlags.CenterOfMass);
            #endregion
        }

        protected override void UnloadContent()
        {
        }

        protected override void Update(GameTime gameTime)
        {
            GamePadState gps = GamePad.GetState(PlayerIndex.One);

            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();

            if (gps.IsButtonDown(Buttons.A) && oldGPS.IsButtonUp(Buttons.A)) 
                showDebug = !showDebug;

            if (gps.Buttons.Y == ButtonState.Pressed && oldGPS.Buttons.Y == ButtonState.Released)
                npc2Body.ApplyLinearImpulse(new Vector2(0, -5));


            if (gps.Buttons.X == ButtonState.Pressed && oldGPS.Buttons.X == ButtonState.Released)
                npc1Body.ApplyLinearImpulse(new Vector2(6, 0));

            npc1Position.X += ConvertUnits.ToSimUnits(gps.ThumbSticks.Left.X * 5);
            npc1Position.Y -= ConvertUnits.ToSimUnits(gps.ThumbSticks.Left.Y * 5);
            npc1Body.Position = npc1Position;

            // Update Camera
            cameraPos.X -= gps.ThumbSticks.Right.X;
            cameraPos.Y += gps.ThumbSticks.Right.Y;

            view = Matrix.CreateTranslation(new Vector3(cameraPos - screenCenter, 0F)) * Matrix.CreateTranslation(new Vector3(screenCenter, 0F));

            // The update method of FSE.
            // 0.001 sets the simulation to work in milliseconds
            world.Step((float)(gameTime.ElapsedGameTime.TotalMilliseconds * 0.001));

            oldGPS = gps;

            base.Update(gameTime);
        }

        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.Black);

            // Position
            Vector2 npc1Pos = ConvertUnits.ToDisplayUnits(npc1Body.Position);
            Vector2 npc2Pos = ConvertUnits.ToDisplayUnits(npc2Body.Position);
            Vector2 groundPos = ConvertUnits.ToDisplayUnits(groundBody.Position);

            // Center 
            Vector2 npc1Origin = new Vector2(npc1.Width / 2F, npc1.Height / 2F);
            Vector2 npc2Origin = new Vector2(npc2.Width / 2F, npc2.Height / 2F);
            Vector2 groundCenter = new Vector2(ground.Width / 2F, ground.Height / 2F);

            spriteBatch.Begin(SpriteSortMode.Deferred, null, null, null, null, null, view);
                spriteBatch.Draw(ground, groundPos, null, Color.White, 0F, groundCenter, scale, SpriteEffects.None, 0);
                spriteBatch.Draw(npc2, npc2Pos, null, Color.White, npc2Body.Rotation, npc2Origin, scale, SpriteEffects.None, 0F);
                spriteBatch.Draw(npc1, npc1Pos, null, Color.White, npc1Body.Rotation, npc1Origin, scale, SpriteEffects.None, 0F);
            spriteBatch.End();

           
            if (showDebug)
            {
                debugView.RenderDebugData(ref projection, ref view);
            }
            
            base.Draw(gameTime);
        }

    }
}

 

 

May 13, 2011 at 4:18 PM

I can't answer all your questions, but you are correct...don't set the position directly.

 

I see you doing that

npc1Position.X += ConvertUnits.ToSimUnits(gps.ThumbSticks.Left.X * 5);
npc1Position.Y -= ConvertUnits.ToSimUnits(gps.ThumbSticks.Left.Y * 5);
npc1Body.Position = npc1Position;

You shouldn't have to modify it like that, the impulse you are applying will update its position in the direction over the next frames. You also shouldn't have to modify the 'awake' value like it sounds you are. 

 

As for your sprites not lining up with the physics objects, it looks like you are taking care of the typical conversions so that's good.  I can only see one thing that you may want to try.  In your spritebatch begin you aren't making use of the projection matrix. Try multiplying that with your view...and it should help

 

If you don't understand, check out how the debugView renderdebugdata makes use of the matrices.

May 14, 2011 at 8:27 PM

Thanks again for your answer Jerkald. About your suggestion for lining up the colliders with the sprites, I  didn't understand what you said so I went check DebugView.  RenderDebugData calls a Draw in another class first and this draw has a BasicEffect which sets its View and Projection Matrix. After that, renderdebugdata draws everything needed and then returns. The point is, I suppose the multiplication is done in this class, PrimitiveBatch, RenderDebugData is calling. Also, in the Samples, no individual example is multiplying these two matrices. So, I think this multiplication is being done.  Even so,  I tried to setup a BasicEffect in my class the same way PrimitiveBatch does, but it didn't work either.

basicEffect.Projection = projection;
basicEffect.View = view;
basicEffect.CurrentTechnique.Passes[0].Apply();

spriteBatch.Begin(SpriteSortMode.Deferred, null, null, null, null, basicEffect, view);

I'll keep trying to solve this problem, but if you or anyonelse finds out what's wrong, please share. As for the other problems, I will make more tests, but I think Farseer has everything I'll need for my project.

Thanks

May 16, 2011 at 5:31 PM

When you want to render something to your world you have to specify where it will be located.  When you do that incorrectly things won't appear in the right spot or perhaps not even show up. My recommendation is something you need to do, whether it will fix the problem, I don't know...but I suspect it will.

When I recommended using the renderdebugdata as an example, I hadn't actually looked at it and was going off of memory.  So it may have confused you a bit because it didn't approach the rendering how I had recalled.  This though can be a learning experience none the less.  The first thing to keep in mind is yes, you should multiply your projection * view.  This means that you are going to be rendering at the point where your camera is looking and it will have the correct projection (it will be the correct dimension, angle, etc).  You need to do that every draw frame in case your camera's view changes for example. 

spriteBatch.Begin(SpriteSortMode.Deferred, null, null, null, null, null, projectionView);

The basic effect stuff is just another way to render.  In truth, the spritebatch uses an effect to render behind the scenes, so either way you are doing the same thing in the end.  If you come to the point where you want to do cool effects you will have to start using effects, so it may or may not be a good time to plug it in.  If you're still learning then you may want to wait.  If you did use an effect, you wouldn't pass in the view as a parameter in your spritecatch.begin call, since you've passed it to your effect.  (I think that's the case at least...it's been awhile since I've had to touch that stuff)