momenimum

Sprite hitTest a point?

Recommended Posts

What object would you like your container to hitTest with? with a point ?

The built in physics module makes collisions very simple, and you don't need to use any physics at all. You just set all the physics parameters to 0. That's what I do in my game - I have my own physics, but I use built-in physics for collisions. You just collide bodies, which can be of circular or rectangular shape.

Share this post


Link to post
Share on other sites

Hi @momenimum Welcome to Panda 2 forum!

So you want to hit test point against sprite that has circle hit area?

You should be able to do that by just calculating the distance between the point and your sprite. If the distance is same or lower than the radius of your circle, then your point is inside the circle.

var point = new game.Vector(100, 100);
var dist = point.distance(sprite.position);

if (dist <= sprite.hitArea.radius) {
  // Point is inside the circle
}

 

Share this post


Link to post
Share on other sites

Hi thanks for the replies.  I am not using physics, I have a sprite and set the hitArea as a circle.  I have a mouse down with an X, Y coordinate.  Just wondering if there was something like sprite.hitTest(x,y) type function already there.  I will just use the distance formula you suggested, thanks!

Share this post


Link to post
Share on other sites

Correct me if I'm wrong, but I think click() is only mouse down then up.  I want an event on mouse down only.  I have a bunch of sprite targets, I want to know if just 1 hits with the mouse, or if the mouse misses.  So instead of putting a listener on every sprite, I want to loop through all of them.  If a hit is detected it would leave the loop, and if nothing hit it would be considered it a miss.  I think if I put a listener on every sprite it would get a bit tricky.

Share this post


Link to post
Share on other sites
1 hour ago, momenimum said:

Correct me if I'm wrong, but I think click() is only mouse down then up.  I want an event on mouse down only.  I have a bunch of sprite targets, I want to know if just 1 hits with the mouse, or if the mouse misses.  So instead of putting a listener on every sprite, I want to loop through all of them.  If a hit is detected it would leave the loop, and if nothing hit it would be considered it a miss.  I think if I put a listener on every sprite it would get a bit tricky.

Do you create those sprites all manually or they are created in a loop an have the same properties ? You could create a class that extends Sprite and have the mousedown event handled there and then create objects of this class and set the parameters with the constructor. No additional loop for checking hit would be needed then.

Share this post


Link to post
Share on other sites

Hi @pstrejczek they are created in a loop with the same properties.  My goal is to know at the moment the mouse is down, if any sprites were hit or it was a miss.  For example there are 10 targets, if the mouse clicks a target, a hit sound is played, if the mouse hit nothing, a miss sound is played.  I'm not sure how to do that without looping through all of them, especially with the miss and only 1 target can be hit at a time.

Share this post


Link to post
Share on other sites

Hi @enpu @momenimum 

Quote

Just wondering if there was something like sprite.hitTest(x,y) type function

I really think something like this would be a brilliant idea for Panda, and would really speed some tasks up. Having a circle against point, circle to circle + similar hitTest functions would be really cool.

Flash had something similar back in the day and made things really straightforward :)

Share this post


Link to post
Share on other sites

This was actually an interesting topic for me, so I decided to give it a shot.

Here is how I did it without update function and looping when checking collisions:

game.module(
    'game.main'
)
.body(function() {

game.addAsset('panda.png')

game.createScene('Main', {
    init: function() {
        
        var bckg = new game.Graphics();
        bckg.fillAlpha = 0;
        bckg.drawRect(0, 0, game.width, game.height);
        bckg.hitarea = new game.Rectangle(0, 0, game.width, game.height);
        bckg.interactive = true;
        
        bckg.mousedown = function() {
            console.log('Not sprite sound');   
        }
        
        bckg.addTo(this.stage);
        
        for(i = 0; i < 5; i++) {
            var sprt = new game.MyClass(i * 100 + 1, i* 100 + 1, 'panda.png');
            sprt.sprite.addTo(this.stage);
        }
    },
    
});

game.createClass('MyClass', {
    init: function(posx, posy, texture) {
        this.sprite = new game.Sprite(texture, {
            x: posx,
            y: posy,
            interactive: true
        });
        
        this.sprite.hitarea = new game.Rectangle(posx, posy, this.sprite.width, this.sprite.height),
        
        this.sprite.mousedown = function() {
            console.log('Sprite sound');
        }
    }
});

});

Maybe not the best solution in the world - but it works!

I personally see no need for additional collision functions - everything I have encountered so far can be easily done with what we have.

Share this post


Link to post
Share on other sites

@pstrejczek Very nice, I understand what you are saying now.  A slight issue is that I wanted the hit or miss code in the same place.  But it does 2 more things I needed, it picks based on the depth sort and it uses the existing hitareas of the sprite.  So it works well for me, thanks!

Share this post


Link to post
Share on other sites
8 minutes ago, momenimum said:

@pstrejczek Very nice, I understand what you are saying now.  A slight issue is that I wanted the hit or miss code in the same place.  But it does 2 more things I needed, it picks based on the depth sort and it uses the existing hitareas of the sprite.  So it works well for me, thanks!

Yes. There is a container in the background which is invisible and it takes the "miss" mousedown events. When it is covered with containers with sprites - those containers pick the event as "hit". The container concept is, in my opinion, one of the best ideas in Panda 2 engine. It makes a lot of things very easy to do. This way you don't have to iterate over all the sprites in update function.

You could also add the sprites to the stage inside MyClass, so you could keep all the sprites code in one place.

Share this post


Link to post
Share on other sites

@pstrejczek is absolutely correct here. In your case just using mousedown events should be enough.

Though there is some things wrong in your code example.

bckg.hitarea = new game.Rectangle(0, 0, game.width, game.height);

Correct property name is hitArea, not hitarea. First two parameters of Rectangle constructor are width and height, so you are creating rectangle with size of 0x0 pixels.

Here is full example with one scene and two classes, where i'm creating 20 circles that move to the right. You remove them by clicking on them, and if you miss the background will flash red (by changing the background color of the scene):

game.module(
    'game.main'
)
.body(function() {

game.createScene('Main', {
    init: function() {
        var bg = new game.Background();
        bg.addTo(this.stage);
        
        for (var i = 0; i < 20; i++) {
            var target = new game.TargetCircle(game.width.random(), game.height.random());
            target.circle.addTo(this.stage);
        }
    }
});

game.createClass('Background', 'Container', {
    init: function() {
        this.interactive = true;
        this.hitArea = new game.Rectangle(game.width, game.height);
    },
    
    mousedown: function() {
        game.scene.backgroundColor = '#ff0000';
        game.Timer.add(100, function() {
            game.scene.backgroundColor = '#000000';
        });
    }
});

game.createClass('TargetCircle', {
    init: function(x, y) {
        this.radius = Math.random(20, 100);
        
        this.circle = new game.Graphics();
        this.circle.drawCircle(0, 0, this.radius);
        this.circle.position.set(x, y);
        this.circle.hitArea = new game.Circle(this.radius);
        this.circle.interactive = true;
        this.circle.mousedown = this.mousedown.bind(this);
    },
    
    mousedown: function() {
        this.circle.remove();
    },
    
    update: function() {
        this.circle.x += this.radius * 2 * game.delta;
        if (this.circle.x > game.width + this.radius) this.circle.x = -this.radius;
    }
});

});

As you can see, to detect if i miss a circle or not, i'm using empty container with custom hitArea, in size of game.width x game.height.

example.gif.d1cc849740af6409931448746d60fd3e.gif

Share this post


Link to post
Share on other sites

@enpu You are totally right - I have made those two mistakes - this code was actually a quick shot. It's interesting why this correctly worked with those mistakes ... If it wouldn't work, I would probably check this with the API.  Anyway .. I work with the editor and engine almost every day in my free time, and still make that small mistakes ... Thanks for the example - I'm still learning proper use of the engine :)

Share this post


Link to post
Share on other sites

@pstrejczek

The reason why your code worked was because this line

bckg.hitarea = new game.Rectangle(0, 0, game.width, game.height);

You are actually doing nothing. And if there is no custom hitArea defined, it will automatically use the container's dimensions for the hit area, when interactive is set to true.

And this part

var bckg = new game.Graphics();
bckg.fillAlpha = 0;
bckg.drawRect(0, 0, game.width, game.height);
bckg.hitarea = new game.Rectangle(0, 0, game.width, game.height);
bckg.interactive = true;

Would work exactly same as empty Container with custom hitArea:

var bckg = new game.Container();
bckg.hitArea = new game.Rectangle(game.width, game.height);
bckg.interactive = true;

 

Share this post


Link to post
Share on other sites
On 7/21/2018 at 3:23 AM, Ninjadoodle said:

Having a circle against point, circle to circle + similar hitTest functions would be really cool.

I can't just create "circle to point" or "circle to circle" functions, because there are many things that can be circle. Sprite texture, hit area, graphics shape, shape of physics body etc.

I have modified hitTest function in Container, so that you can also use Vector as a target parameter. Also there is now second parameter, which if you set to true, will calculate the hit as if the container was a circle (and also the target if it's a container too).

So there are now 4 different ways to use hitTest function:

sprite1.hitTest(sprite2)

hit1.gif.056302bbd1be2e5a57ab1328f8788e7f.gif

sprite1.hitTest(sprite2, true); // Hit test as circles

hit2.gif.5a99d44fbb4786a3dae51bc94b5de535.gif

sprite1.hitTest(game.input.mouse); // Hit test against Vector

hit3.gif.89cf6e297025e9e970471e71b70d3993.gif

sprite1.hitTest(game.input.mouse, true); // Hit test against Vector as a circle

hit4.gif.5d557a924b447c7cb55a2b09187c5b59.gif

Share this post


Link to post
Share on other sites

Awesome work @enpu ! Could you please put this in the examples section? This is quite important and I'm sure many people in the future will ask about this.

I see there is a polygon hitArea in the roadmap for the next engine version (2.10). Does it mean detecting a collision of (for example) circle with polygon will be possible? This would be awesome!

Share this post


Link to post
Share on other sites

@pstrejczek Yeah i will update the examples in the website as soon as those new changes are merged into the master branch, which means releasing next version of engine (2.10.0).

@momenimum Now that you asked, i think i made things a bit too complicated. hitTest function should really use the container's hitArea for the hit testing (not bounds like it currently uses).

That would mean that you could do this:

sprite1.hitTest(sprite2);

Which would mean that it does hit test for sprite1's hitArea against sprite2's hitArea, which both could be either Rectangle or Circle (or even Polygon in future).

Then if the parameter is Vector instead of Container, it would make the hit test against that.

I think that would be a lot cleaner and easier to understand solution.

Share this post


Link to post
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...

  • Recently Browsing   0 members

    No registered users viewing this page.