Find a free spot? - SOLVED

Apr 13, 2010 at 2:13 PM
Edited Apr 13, 2010 at 2:14 PM

Hello,

 

I'm making a game where the user can place buildings. Now when the user clicks on a building-button with the mouse I place a sensor under the mouse that collides with other buildings. When he user moves his/her mouse over a free spot the building can be placed (a new building not the sensor). But when it is colliding with another building it can not be placed.

I update the position of the sensor when the user moves the mouse.

 

So far I got this:

 

    public class MouseSensor
    {
        public bool CanBuildHere { get; set; }
        public Body body;
        public Geom geom;
        public MouseSensor(int width, int height)
        {

            body = BodyFactory.Instance.CreateRectangleBody(MyFarseer.PhysicsSimulator, width, height, 1);
            body.MomentOfInertia = float.MaxValue; // Prevent rotation
            body.Position = Level.player.mouse.Position;
            geom = GeomFactory.Instance.CreateRectangleGeom(MyFarseer.PhysicsSimulator, body, width, height);
            geom.CollisionCategories = CollisionCategory.Cat6;
            geom.CollidesWith = ~CollisionCategory.All & CollisionCategory.Cat1;
            geom.OnCollision += OnCollision;
            geom.IsSensor = true;
        }

        private bool OnCollision(Geom geometry1, Geom geometry2, ContactList contactlist)
        {
            if (geometry2.CollisionCategories == CollisionCategory.Cat2)
            {
                CanBuildHere = false;
                Level.player.mouse.DrawColor = Color.Red;
                return false;
            }
            Level.player.mouse.DrawColor = Color.White;
            CanBuildHere = true;
            return true;
        }
    }

 

But it isn't working. What am I missing or is my approach wrong? I just made this up since I couldn't find another approach on the internet for this problem.

Apr 13, 2010 at 4:02 PM
Edited Apr 13, 2010 at 4:03 PM

Here is the one thing I noticed.  The line:

geom.OnCollision += OnCollision;

I believe this is wrong, or at least, I've never seen it done like this.  This is an example from a project of mine:

this.m_geom.OnCollision += new FarseerGames.FarseerPhysics.Collisions.CollisionEventHandler(Projectile_Collision);

I'm pretty sure you need some kind of EventHandler.  Also, couldn't you change this?

geom.CollidesWith = ~CollisionCategory.All & CollisionCategory.Cat1;
to:
geom.CollidesWith = CollisionCategory.Cat1;

Though I'm sure that is not a problem.  It just seems redundant.

Ok, and the bigger problem I just noticed, I remember that finding if a geom is NOT colliding with anything isn't as easy as it seems.  You would have to also wire to the OnSeparation event, and keep track of what has been colliding and separating.

You also should set CollisionResponseEnabled to false, so the geometries will pass through each other (They will still fire collision events).  I don't know for sure, but it seems like it could potentially cause problems.

Also, logically, your OnCollision event doesn't make sense.  You set the CollidesWith to only Cat1, but then you check to see if the geometry collides with Cat2.  The only category that will ever come into geometry2 will be Cat1, I believe.

I would think there has to be an easier way to do what you're trying to do...

Apr 13, 2010 at 5:27 PM

Thanks. I made the above changes to the code. Indeed much better.

 

 

geom.OnCollision += OnCollision;

Does work for me but I implemented your approach since it is probably the way to go.

 

That link that you gave is a lot of work for something so trivial... I think i might just place a rectangle on each building and when the user moves the mouse I simply iterate each rectangle with the position of the building hovering under the mouse (the one to be placed). It won't cost much extra performance and it's easy to implement. But still I'm sure that I'm overlooking the easy solution.

Apr 15, 2010 at 5:02 AM
While I don't know for sure that this is your best bet... 

This is what I would do:

public class MouseSensor
    {
        protected bool canBuildHere = true;
        
        public bool CanBuildHere {
           get { return canBuildHere; }
           set 
           {
               canBuildHere = value;
               if (value)
                   Level.player.mouse.DrawColor = Color.White;
               else
                   Level.player.mouse.DrawColor = Color.Red;

           }
        }
        public Body body;
        public Geom geom;
        
        public MouseSensor(int width, int height)
        {
            CanBuildHere = true; // so that it sets the mouse draw color

            body = BodyFactory.Instance.CreateRectangleBody(MyFarseer.PhysicsSimulator, width, height, 1);
            body.MomentOfInertia = float.MaxValue; // Prevent rotation
            body.Position = Level.player.mouse.Position;
            
            geom = GeomFactory.Instance.CreateRectangleGeom(MyFarseer.PhysicsSimulator, body, width, height);
            geom.CollisionCategories = CollisionCategory.Cat2; // assuming this because of your code below
            geom.IsSensor = true;
            
            geom.OnCollision += OnCollision;
            geom.OnSeperation += OnSeperation;
            
        }

        private bool OnCollision(...)
        {
            CanBuildHere = false;
            return false;           
        }
        
        private bool OnSeperation(...)
        {
            CanBuildHere = true;
            return true;
        }
    }
}

If you are only concerned with the one collision cat, then you don't need to check for any others...
but if you are doing other things with other collision cats, you will still need that collision cat check.

Also, you don't need to set CollisionResponseEnabled to false, because setting it as a sensor will do that for you.
and += EventMethod works just fine, so you can leave it as that.

Please note that I didn't test the code (the ellipsis should give that away), so don't just copy paste...
but you get the idea.
Apr 15, 2010 at 11:42 AM

The approach where I keep a List<Rectangle> buildingCols;  worked fine to solve the problem. It's just such a shame that I didn't solve it using the Farseer Engine as that would've been a cleaner solution.

@fidgetwidget:

I like the getter/setter code. But that code won't work. You have to keep a list of every geom that is colliding (i think) which is added in the OnCollision event and the OnSerperation removes that geom from the list. When the list is empty then one can build. Because now when it collides with (for example) 2 other geom's and one of them would raise the OnSeperation event then CanBuildHere is set to true while the other geom could still be colliding. But I'm not sure if adding such a list would complete the code. It seems too simple.



 

Apr 15, 2010 at 3:12 PM

Actually, I think using a list of rectangles is cleaner in the end.

Good luck with your project.