Jump to content

Collision Detection Question


Ninjadoodle
 Share

Recommended Posts

Hi @enpu

I'm having a bit of an issue with collision detection, as I'm in need of a circle vs rotated square collision.

I know there are some libraries out there, that handle this sort of thing (for games that don't require physics).

I'm not really sure tho, if there is an easy way to do something like this in panda without using physics or having to resort to coding your own classes.

I know it's a different story, but in Construct this is handled by setting up collision shapes for each object. The hit-test is then done on these collision shapes - even without physics.

Is something like this possible in Panda to make collision a little easier to implement?

Link to comment
Share on other sites

I'm actually also interested in that.
I couldn't find anything built in into the engine except hitTest for sprite or container.

The collision shapes are one thing, but also more data would be great (f.ex. direction of collision - was the object hitted from above, below, left or right). 
I can see I can do everything that with P2 plugin, but when I'm not interested in advanced physics or want to do my own simple one, P2 is a little overkill.

 

Link to comment
Share on other sites

Hit detection in the built-in physics are only for non-rotated rectangles and circles. I want to keep the built-in physics engine as simple as possible and adding hit detection for rotated rectangles would mean that it should then also include collision solving for those, which would make it a lot more complicated.

As @pstrejczek said, this can be done with P2, but if you are only looking for the hit detection and nothing more, P2 might be a bit overkill.

I found this JavaScript function for circle vs rotated rectangle hit detection:

https://gist.github.com/snorpey/8134c248296649433de2

Should be pretty easy to implement that to your needs.

Let me know if you need help with that one.

Link to comment
Share on other sites

Hi @enpu

Thank you for the link! I managed to get the function working, but it still doesn't seem to be very precise.

Not too sure if I'm using all the values correctly, but the ships still seem to be getting hit quite a few pixels off (visibly).

Any chance of getting a simpler way of doing this implemented in Panda (if you want to avoid P2)?

Thanks heaps!

Link to comment
Share on other sites

@Ninjadoodle & @pstrejczek

Finally got it working, here is live example:

https://www.panda2.io/examples#misc-hittest

(use mouse to move the circle, click to change rectangle position)

As you can see the hit test function is actually pretty simple :D

Note that this version only works on sprites with anchor point at center.

Link to comment
Share on other sites

@enpu

For some reason I'm still not getting precise collision with moving bullets. I'm using the function as in the example, but for some reason, my bullets seem to be hitting / destroying the enemy 'visibly' off by quite a few pixels (visually it looks like 20-40 pixels).

I'm not really sure what I'm doing wrong tho. Any ideas on what could be a cause of something like this?

PS. My bullets are moving pretty slow but my circular enemies are constantly rotating.

Link to comment
Share on other sites

Hi @enpu

Here is my laser class and my enemy class - I've tried different sizes for the circle and rectangle, like in your updated function, but things still seem very off for me.

I'm doing the hitTest in the bottom of the Laser class :)

game.createClass('Spinvader', {
    
    rot: 2,
    
    init: function(x, y) {
        
        this.sprite = new game.Sprite('spinvader.png');
        
        this.sprite.position.set(x, y);
        this.sprite.anchorCenter();
        this.sprite.alpha = 0;
        this.sprite.addTo(game.scene.mg);
        
        var angle = this.sprite.position.angle(game.scene.playerShip.sprite.position);
        var distance = this.sprite.position.distance(game.scene.playerShip.sprite.position) - 256;
                
        var xPos = this.sprite.position.x + distance * Math.cos(angle);
        var yPos = this.sprite.position.y + distance * Math.sin(angle);
        
        game.Tween.add(this.sprite, {
           alpha: 1
        }, 500, {
            easing: 'Linear.None'
        }).start();
        
        this.tween1 = game.Tween.add(this.sprite, {
           x: xPos,
           y: yPos
        }, 2000, {
            easing: 'Quadratic.In'
        }).onComplete(function() {
            game.scene.stageSetup.shake(game.scene.gm, 10, 250);
            this.rot *= -1;
        }.bind(this)).start();
        
        this.tween2 = game.Tween.add(this.sprite, {
           x: x,
           y: y
        }, 2500, {
            easing: 'Quadratic.Out'
        }).onComplete(function() {
            this.tween1.start();
        }.bind(this)).stop();
        
        this.tween1.chain(this.tween2);
    },
    
    update: function() {
        
        this.sprite.rotation += this.rot * game.delta;
    },
    
    remove: function() {
        game.Tween.stopTweensForObject(this.sprite);
        game.scene.spinvaders.erase(this);
        this.sprite.remove();
        game.scene.shot ++;
        game.scene.progressCheck();
    }
});



game.createClass('Laser', {
    
    init: function() {
        
        this.sprite = new game.Sprite('laser.png');
        
        this.sprite.position.set(game.scene.playerShip.sprite.x, game.scene.playerShip.sprite.y);
        this.sprite.anchorCenter();
        this.sprite.rotation = game.scene.playerShip.sprite.rotation;
        this.sprite.addTo(game.scene.bg);
        
        game.Timer.add(2000, this.remove.bind(this));
    },
    
    remove: function() {
        this.sprite.remove();
        game.scene.removeObject(this);
    },
    
    update: function() {
        this.sprite.position.x += game.scene.laserSpeed * Math.cos(this.sprite.rotation - (0.5 * Math.PI)) * game.delta;
        this.sprite.position.y += game.scene.laserSpeed * Math.sin(this.sprite.rotation - (0.5 * Math.PI)) * game.delta;
        
        // Reverse for loop
        for (var i = game.scene.spinvaders.length - 1; i >= 0; i--) {
            
            var enemy = game.scene.spinvaders[i];
            
            if (game.scene.hitTest(this.sprite, enemy.sprite)) {
                enemy.remove();
                this.remove();
            }
        }
    }
});

 

Link to comment
Share on other sites

The hit test function uses local positions for the hit detection. So if your rect and circle has different origin (position 0,0 is not same place) then that is definitely the issue. 

To fix that, you should get world positions for both and then replace all the rect.x, rect.y, circle.x, circle.y values

Link to comment
Share on other sites

Well do you understand the difference between local and world position?

I can see that you are adding your laser.png to game.scene.bg container and spinvader.png to game.scene.mg container?

Is the world positions of those containers same? If not then that's your problem.

Link to comment
Share on other sites

Hi @enpu - So, I've played with the images and did what you suggested, and part of the problem is that I have transparent pixels around the image.

I've been trying to use the setting in your new function to set the size of the rect and circle to make it different than the size of the image.

Cutting out the transparent pixels makes it work ... however, I need the images with the transparent pixels and adjusting the size using the function properties doesn't seem to work right - the hitTest is very off.

Thanks for your help!

Link to comment
Share on other sites

Oh well then that's the issue, the hitTest function assumed that you are using the whole image. It used the rectWidth and rectHeight values to determine the center position of the rectangle, but since you were using values that are lower than the actual image, the center position would be off.

hitTest: function(rect, circle, rectWidth, rectHeight, circleRadius) {
    rectWidth = rectWidth || rect.width;
    rectHeight = rectHeight || rect.height;
    circleRadius = circleRadius || circle.width / 2;

    var rectCenterX = rect.x + Math.cos(rect.rotation) * (rect.width / 2 - rect.anchor.x) - Math.sin(rect.rotation) * (rect.height / 2 - rect.anchor.y);
    var rectCenterY = rect.y + Math.sin(rect.rotation) * (rect.width / 2 - rect.anchor.x) + Math.cos(rect.rotation) * (rect.height / 2 - rect.anchor.y);
    
    var circleCenterX = circle.x - circle.anchor.x + circleRadius;
    var circleCenterY = circle.y - circle.anchor.y + circleRadius;
    
    var circleX = Math.cos(-rect.rotation) * (circleCenterX - rectCenterX) - Math.sin(-rect.rotation) * (circleCenterY - rectCenterY) + rectCenterX;
    var circleY = Math.sin(-rect.rotation) * (circleCenterX - rectCenterX) + Math.cos(-rect.rotation) * (circleCenterY - rectCenterY) + rectCenterY;
    var close = new game.Vector(circleX, circleY);
    
    if (circleX < rectCenterX - rectWidth / 2) {
        close.x = rectCenterX - rectWidth / 2;
    } else if (circleX > rectCenterX + rectWidth / 2) {
        close.x = rectCenterX + rectWidth / 2;
    }

    if (circleY < rectCenterY - rectHeight / 2) {
        close.y = rectCenterY - rectHeight / 2;
    } else if (circleY > rectCenterY + rectHeight / 2) {
        close.y = rectCenterY + rectHeight / 2;
    }
    
    if (close.distance(circleX, circleY) < circleRadius) return true;
},

Try this one

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...