Clicking & selecting objects/entities


(Victor) #1

I’m a new FlashPunk developer and I have a very noob issue. By the way please apologize my english grammar (you can write your complaints to the Google Translator team if you have one :D)

I have 2 entities acting as a buttons, these two objects sometimes may be overlapped and when I want to click one of them (in this case the entity that is on top) if the entity that is behind of it is in certain position, it also receives the click event and as result I have two clicks “events” in one Mouse Click…

The code that I’m using is the following:

if (collidePoint(x, y, Input.mouseX, Input.mouseY))
{
	if (Input.mouseReleased)
	{
		clicked();
	}else if (Input.mouseDown)
	{
			mouseDown();
		}else {
			mouseOver();
	}
}

I’ve solved this issue by adding one static boolean var and the result is (In my Entities)

if (collidePoint(x, y, Input.mouseX, Input.mouseY))
{
	if (Input.mouseReleased)
	{
		if(Button.!isClicked){ Button.isClicked = true; clicked(); }
	}else if (Input.mouseDown)
	{
			mouseDown();
		}else {
			mouseOver();
	}
}

(In my WORLD update)

override public function update():void
{
	super.update();
	if (Button.isClicked) { Button.isClicked = false; }
		
}

The thing is that I’m developing a RTS game and I may have two or more units overlapped in a certain moment and if the player try to click one unit to select will select the whole units in one click and I’m not sure if my solution is the best for this situation.


(rostok) #2

You could just move your click&select code into mouse cursor or world class. Iterate through all the entities and break the loop on the first click.


(Jacob Albano) #3

Or you could use World.collidePoint(). :slight_smile:

if (Input.mouseReleased)
{
    var e:Entity = world.collidePoint("unit", world.mouseX, world.mouseY);
    if (e)
    {
        // e is now the top-most entity at the position you clicked
    }

(JP Mortiboys) #4

Actually, it’s just the first object FP finds that is of that type, at that position - the layer or implicit z-index doesn’t come into it. As I recall for the UI library PunkUI you had to add a function to the World class to pick the topmost object - frontCollidePoint I think it was called?


(Jacob Albano) #5

Interesting, I didn’t know that. So in that case a more accurate solution would be:

var units:Array = [];
var e:Entity;
var topLayer:int = Int.MAX_VALUE;
world.collidePointInto("unit", world.mouseX, world.mouseY, units);

for each (item:Entity in units)
{
    if (item.layer < topLayer)
    {
        e = item;
        topLayer = item.layer;
    }
}

if (e)
{
    // e is now the top-most entity at the position you clicked
}

(azrafe7) #6

@jacobalbano: your solution works fine…

I was wondering whether there is a way to replicate frontCollidePoint()'s functionality in an optimized way (specifically: avoiding to hack the World class and the use of the type parameter while keeping away from calls to getAll()) only using the members exposed by the World class, or if it’s necessary to make some more private variables public/namespaced (as proposed by @AbelToy et. al. in this issue).


(Zachary Lewis) #7
// Create storage.
var units:Array = [];

// Gather units.
world.collidePointInto("unit", world.mouseX, world.mouseY, units);

// Check for collisions.
if (units.length > 0)
{
  // Sort the array numerically by layer.
  units.sortOn("layer", Array.NUMERIC);

  // Perform operations on the topmost Entity.
  units[0].clicked();
}

(Jacob Albano) #8

Oh now that’s just beautiful.


(azrafe7) #9

Nice!

Though you’ll have to resort to using an Array, as the Vector class doesn’t have a sortOn() function ( - or implement a simple compare function and use Vector.sort() instead).


(JP Mortiboys) #10

Well, you could always do FP.sortBy(units, "layer") - that will work fine with a vector.

Of course this doesn’t address the fact that different entities can have the same layer but a different rendering order, but as long as you ensure that the entities have different layers you’ll be fine.


(Jacob Albano) #11

True, and at this time unfortunately that’s not something we can fix without modifying the World class itself.


(Zachary Lewis) #12

Good eye. I’ve updated the sample above.


(azrafe7) #13

Using all the ideas you’ve hinted at so far I put this together.

It can surely be optimized (especially by making the render lists in the World class “public” and thus following the same path the guys of punk.ui have taken).

I’ve roughly tested it, but it should also address the “same layer but different render order” case (like frontCollidePoint()).

public function topmostCollidePoint(type:String, pX:Number, pY:Number):Entity 
{
	// Create storage
	var collidingEntities:Vector.<Entity> = new Vector.<Entity>;

	// Gather units
	FP.world.collidePointInto(type, pX, pY, collidingEntities);

	// Check for collisions
	if (collidingEntities.length > 0)
	{
		// Sort the vector numerically by layer
		FP.sortBy(collidingEntities, "layer");

		// Get entities on colliding layer
		var layerEntities:Vector.<Entity> = new Vector.<Entity>;
		FP.world.getLayer(collidingEntities[0].layer, layerEntities);
		
		// Get topmost entity
		var i:int = layerEntities.length - 1;
		while (i > 0 && collidingEntities.indexOf(layerEntities[i]) < 0) i--;

		return layerEntities[i];
	}
	
	return null;
}

[EDIT] AS3 Syntax Hihlighting (enclose in

```as3

and

``` ).


(Jacob Albano) #14

```as3 ``` should work, according to the formatting file, but I’ve found that ```actionscript ``` is more accurate.