Ship Collision issues with bullet

Hello,

I am trying to create a simple shooter game. The game has 3 sprites, a ship, bullet and enemy ship. I am able to get the bullets to shoot from the player one ship, but I am having trouble getting a collision to occur between the bullet and the enemy ship. Here is the collision code taken from the gone bananas examples. You will notice I am trying two options one of which I found online. Either way, both of the checks return false.

` Schedule((time) =>
{

            var didCollide = false;

            for (var count = 0; count < bullets.Count - 1; count++)
            {

                var currentBullet = bullets[count];

                // Check is we should terminate a bullet because it is off the screen
                if (bullets[count].BoundingBoxTransformedToParent.MinY > VisibleBoundsWorldspace.MaxY)
                {
                    bullets.Remove(currentBullet);
                }

                if (enemy != null)
                {
                    // CCRect intersect = new CCRect(bullets[count].PositionX, bullets[count].PositionX, bullets[count].ContentSize.Width, bullets[count].ContentSize.Height);

                    float fudgeFactor = 0.9f;
                    var rect = enemy.BoundingBoxTransformedToParent;
                    var smRect = new CCRect(rect.LowerLeft.X, rect.LowerLeft.Y, rect.Size.Width * fudgeFactor, rect.Size.Height * fudgeFactor);

                    didCollide = currentBullet.BoundingBoxTransformedToParent.IntersectsRect(smRect);

                    didCollide = currentBullet.BoundingBoxTransformedToParent.IntersectsRect(enemy.BoundingBoxTransformedToParent);

                    if (didCollide)
                    {
                        RemoveChild(enemy);
                    }

                }
            }

        });`

Best Answers

Answers

  • RamiTabbaraRamiTabbara AUMember, Xamarin Team Xamurai

    Hi Gregg,

    It's a bit difficult to say what the issue is without knowing how you've setup your scene, however as a first test I would check to make sure that your bullets and enemies sprites all have the same CCNode parent (probably a CCLayer parent). Otherwise, you're bounding boxes are expressed relative to two different coordinate systems and so you're comparing apples with oranges.

    It might also be a good idea to store currentBullet.BoundingBoxTransformedToParent as a variable and step through your code to see if it's giving sensible bounds.

  • GreggColemanGreggColeman USMember

    Hey Rami. Thank you for responding. I will double check, but I'm pretty sure that they are the both part of the "IntroLayer" which comes out of the box with the VS templates. If it helps any, other than a new enemy class, It is (orshould be) exactly like the Xamarin Entities Example

    developer.xamarin.com/guides/cross-platform/game_development/cocossharp/entities/.

    Here is what the Scene (IntroLayer) looks like. Thanks again for your help and le t me know if you would like more code or just have me upload the solution.

    `using System;
    using System.Collections.Generic;
    using CocosSharp;
    using Microsoft.Xna.Framework;
    using ShooterGame.Entities;
    using ShooterGame.Factories;
    using ShooterGame.WP81;

    namespace ShooterGame.Shared
    {
    public class IntroLayer : CCLayerColor
    {

        // Define a label variable
        CCLabel label;
    
        Ship ship;
        EnemySprite enemy;
    
        List<Bullet> bullets;
    
        public IntroLayer()
            : base(CCColor4B.Black)
        {
    
            // create and initialize a Label
            //label = new CCLabel("Hello CocosSharp", "fonts/MarkerFelt", 22, CCLabelFormat.SpriteFont);
            // add the label as a child to this Layer
            // AddChild(label);
    
            AddPlayer1Ship();
            AddEnemyShip();
    
            bullets = new List<Bullet>();
            BulletFactory.Self.BulletCreated += Self_BulletCreated;
    
            Schedule((time) =>
            {
    
                var didCollide = false;
    
                for (var count = 0; count < bullets.Count - 1; count++)
                {
    
                    var currentBullet = bullets[count];
    
                    // Check is we should terminate a bullet because it is off the screen
                    if (bullets[count].BoundingBoxTransformedToParent.MinY > VisibleBoundsWorldspace.MaxY)
                    {
                        bullets.Remove(currentBullet);
                    }
    
                    if (enemy != null)
                    {
                        // CCRect intersect = new CCRect(bullets[count].PositionX, bullets[count].PositionX, bullets[count].ContentSize.Width, bullets[count].ContentSize.Height);
    
                        float fudgeFactor = 0.9f;
                        var rect = enemy.BoundingBoxTransformedToParent;
                        var smRect = new CCRect(rect.LowerLeft.X, rect.LowerLeft.Y, rect.Size.Width * fudgeFactor, rect.Size.Height * fudgeFactor);
    
                        didCollide = currentBullet.BoundingBoxTransformedToParent.IntersectsRect(smRect);
    
                        didCollide = currentBullet.BoundingBoxTransformedToParent.IntersectsRect(enemy.BoundingBoxTransformedToParent);
    
                        if (didCollide)
                        {
                            RemoveChild(enemy);
                        }
    
                    }
                }
    
                //   bullets = masterBullets;
    
            });
    
        }
    
        private void Self_BulletCreated(Bullet obj)
        {
            AddChild(obj);
            bullets.Add(obj);
        }
    
        protected override void AddedToScene()
        {
            base.AddedToScene();
    
            // Use the bounds to layout the positioning of our drawable assets
            var bounds = VisibleBoundsWorldspace;
    
            // position the label on the center of the screen
            //label.Position = bounds.Center;
    
        }
    
        void AddPlayer1Ship()
        {
            ship = new Ship();
            ship.PositionX = 240;
            ship.PositionY = 50;
    
            AddChild(ship);
        }
    
        void AddEnemyShip()
        {
            enemy = EnemySprite.CreateEnemy();
            enemy.PositionX = 240;
            enemy.PositionY = 280;
    
            AddChild(enemy);
        }
    
    }
    

    }

    `

  • GreggColemanGreggColeman USMember

    Rami Tabbara,

    Any new ideas or need any more information from me? I'm at aloss on this.

  • GreggColemanGreggColeman USMember
    edited November 2015

    Thanks Flippy. I will have to give it a try. I worked around this for now by doing a check on the actual (x,y) coordinates because even during the debug the numbers weren't making sense.

    So this actually works:

     didCollide = (currentBullet.BoundingBoxTransformedToParent.MinY) >=(enemy.BoundingBoxTransformedToParent.MaxY - 20);
    
                            if (didCollide)
                            {
                                enemy.RemoveFromParent();
                            }
    

    I think it has to do with the fact that I am trying to use the "Entities" approach and both the enemy and the bullet are separate classes inheriting from CCSprite (I tried CCNode as well with the same result). I verified that they are both using the same parent class of the IntroLayer.cs as well.

    If it makes any sense, when I did use the IntersectRect method for collision BUT I created the enemy and bullet CCSprites inside the IntroLayer, it worked fine. I am guessing it has something to do with making the sprites into entities and separate classes while inheriting from CCSprite that is causing it to somehow be in separate world spaces or something of that nature.

    Make any sense to you?

    Thanks again.

  • FlippyFlippy USMember ✭✭

    Hi Gregg, I'm away from my computer at the moment, but I think what you're missing is understanding how the coordinate system works for child nodes. When you add a child to another node, the coordintaes are relative to the parent node with the center of the sprite being the point where the sprite is drawn. In other words, if I create a 50x50 sprite with a position of (0, 0), you will only see the top right half of the sprite. It is for this reason why I suggested drawing boxes around the actual rectangles you are comparing so you can visually see the rectangles being evaluated for collision. When I get back to my computer, I can create you a sample project if you don't have it figured out by then.

  • Hey Flippy. Something I'm doing must be terribly wrong. I tried what you said to do and I'm not even getting a box around my bullets. Here is what I have:

    void DrawSpriteBoundingBoxes(Bullet currentBullet)
            {
              RemoveChild(bulletBox);
                //RemoveChild(enemyBox);
    
                bulletBox.DrawRect(currentBullet.BoundingBox, CCColor4B.Transparent, 2f, CCColor4B.Red);
                //enemyBox.DrawRect(enemy.BoundingBox, CCColor4B.Transparent, 2f, CCColor4B.Red);
    
                AddChild(bulletBox);
                //AddChild(enemyBox);
            }
    

    Then this is the Schedule loop inside the Introlayer constructor calling the Draw method.

                Schedule((time) =>
                {
    
                    var didCollide = false;
    
    
    
                    for (var count = 0; count < bullets.Count - 1; count++)
                    {
    
                        var currentBullet = bullets[count];
                        DrawSpriteBoundingBoxes(currentBullet);
                     }
    ......
    

    If you have a quick sample project that shows using collision and this box test using 'Entities' (not everything in the game layer file), that would really help at this point I think.

    Also, assuming it uploaded properly as it was 33MB file, attached is t he current state of my project.

    Thanks again for your help.

  • kjpou1kjpou1 LUMember, Xamarin Team Xamurai

    Also @GreggColeman

    Not wanting to take away from the learning experience but you also have a memory leak with your bullets once your enemy ship is destroyed.

    Keeping with your Entity based architecture you can have your bullet notify your BulletFactory when it should be removed. Following the same line of thought with your CreateBullet delegate.

        public class Bullet : CCNode
        {
    
            public event Action<Bullet> OffScreen;
    
            **<Left blank> ...... **
    
            private void ApplyVelocity(float time)
            {
                PositionX += VelocityX * time;
                PositionY += VelocityY * time;
    
                ApplyOffScreen();
    
            }
    
            void ApplyOffScreen()
            {
                if (PositionY > VisibleBoundsWorldspace.MaxY)
                {
                    RemoveFromParent();
                    OnOffScreen();
                }
            }
    
            void OnOffScreen()
            {
                if (OffScreen != null)
                {
                    OffScreen(this);
                }
            }
    
        **<Left blank> ...... **
    

    Add a BulletRemoved delegate in your factory:

    using ShooterGame.Entities;
    using System;
    using System.Collections.Generic;
    using System.Text;
    
    namespace ShooterGame.Factories
    {
        public class BulletFactory
        {
            static Lazy<BulletFactory> self = new Lazy<BulletFactory>(() => new BulletFactory());
    
            public static BulletFactory Self { get { return self.Value; } }
    
            public event Action<Bullet> BulletCreated;
            public event Action<Bullet> BulletRemoved;
    
            private BulletFactory()
            {
    
            }
    
            public Bullet CreateNewBullet()
            {
                Bullet bullet = new Bullet();
                bullet.OffScreen += Bullet_OffScreen;
                if (BulletCreated != null)
                {
                    BulletCreated(bullet);
                }
    
                return bullet;
            }
    
            private void Bullet_OffScreen(Bullet obj)
            {
                if (BulletRemoved != null)
                {
                    BulletRemoved(obj);
                }
            }
        }
    }
    

    In your IntroLayer add a delegate to remove your bullet from the bullets list

            bullets = new List<Bullet>();
            BulletFactory.Self.BulletCreated += Self_BulletCreated;
            BulletFactory.Self.BulletRemoved += Self_BulletRemoved;
    

    In your handler remove the bullet from your list

        private void Self_BulletRemoved(Bullet obj)
        {
            bullets.Remove(obj);
        }
    

    This is just one idea of cleaning the bullet objects up instead of verifying it in the IntroLayer.

  • @kjpou1 Thank you very much for the response and not taking too much away from my learning :). I'm doing this more for learning something new than anything else, so I appreciate it! I'm usually a backend developer, so game development is new to me :).

    2 questions. First is, why did your box creation work and mine did not? I had that DrawSpriteBoundingBoxes in the IntroLayer and applied it to the same sprite. Just not inside the bullet class. Why does it matter? I couldn't even get the box to show up.

    Second question is, I might be missing something simple, but where is the actual memory leak? It looks like at the end of the day you are calling bullets.Remove() anyway? Yours is much better putting it in the Bullets class, just not seeing the memory leak.

    Ok one more. When I tried to just RemoveChild(Enemy) in the introLayer, It would still show it as existing when it went through the loop after a collision and did the collision test again. This is why I nulled it out. Is this the proper way to handle this? I would have thought that the RemoveChild takes it completely off the screen, not just hides it.

    Thanks again for your help.

  • Geeze. Thanks Flippy. That was a pretty silly mistake :(. I have been getting frustrated, so I just copied it in. Thank you for seeing that and telling me.

  • GreggColemanGreggColeman USMember
    edited November 2015

    Hey @Flippy or @kjpou1 .... I was able to get the rectangle to show for the enemy. It turned out the enemy image was not working right because as soon as I changed the image it worked. For some reason though, the IntersectsRect is still not working. In the attached screen shot, the Y-axis is 286 and the space ship is static right not at 280. I would think this would cause a collision, but it is never falling into the if statement.

      if (currentBullet.BoundingBoxTransformedToParent.IntersectsRect(enemy.VoundingBoxTransformedToParent))
                            {
                                Explode(enemy.Position);
                                enemy.RemoveFromParent();
                            }
    

    I tried the ToWorld, just BoundingBox. I've alst tried the "Intersection" and it always comes back with all zeros.

    Any ideas?

  • Forgot to attach image

  • FlippyFlippy USMember ✭✭

    Hey Gregg,

    I'm assuming you took Rami's advice about checking the parent node? To my knowledge, the IntersectsRect is doing a simple calculation on two rectangles to see if they touch or overlap. If this is not working, it is because the rectangles being evaluated are not on the same plane sort of speak, so their coordinate systems are different, even though they might "appear" to overlap on the actual screen itself. I would double check the rectangles. If it helps any, create two static CCRect variables and assign them some coordinates and play around IntersectsRect method. This should always work. Once you get this down, you can start assigning the "BoundingBoxes to your variables to see if they make sense as Rami suggested. I think you're almost there Gregg. You can also zip up your current project and I can take a look at it tomorrow as it's getting late in my time zone as of now.

    @RamiTabbara said:
    Hi Gregg,

    It's a bit difficult to say what the issue is without knowing how you've setup your scene, however as a first test I would check to make sure that your bullets and enemies sprites all have the same CCNode parent (probably a CCLayer parent). Otherwise, you're bounding boxes are expressed relative to two different coordinate systems and so you're comparing apples with oranges.

    It might also be a good idea to store currentBullet.BoundingBoxTransformedToParent as a variable and step through your code to see if it's giving sensible bounds.

  • @Flippy. Yes I checked the parent node property on both and the "Layer" property and both say IntroLayer. The thing that is throwing me off too is that it's not just the visual that is showing them as crossing, but the Y-axis on both are to my knowledge crossing. As I mentioned above, the bullet was 286 and the enemy is a static 280 to make things simple. I think you are right that something like the wrong plane or "layer" is at play here. Almost like they are on 2 different Photoshop layers that although they are the same coordinates because both "layers" are the same size, not being on the same "layer" makes them never cross. Not sure if that is a proper analogy, but that's what it feels like. I'm just at a loss what else to look at. I attached my project if you don't mind looking at it.

    Thanks again for all you help and nicely sticking through this with me while I learn this. I really appreciate it.

  • @kjpou1 and @Flippy. Very much appreciated. That did the trick. It would be really helpful if there were actual comments for the various methods and properties within the CocosSharp framework. It would help find things like this. I wouldn't have fund that otherwise. Maybe they will in a future revision or soething.

    Thanks again guys!

Sign In or Register to comment.