How to pick up a sprite? - libgdx

A simple question: how could I pick up a sprite in libgdx? By this, I mean that when I click / touch the screen, it checks which (if any) sprite is clicked.

if(Gdx.input.justTouched())
{
cam.unproject(touchPoint.set(Gdx.input.getX(),Gdx.input.getY(), 0));
if(Assets.playButton.getBoundingRectangle().contains(touchPoint.x,touchPoint.y ))
{
// do what u want to do when image is touched
}
Well Assets.playButton is actually a sprite type object
getBoundingRectangle() gives u the rectangle enclosed by the sprite
P.S:- touchPoint is a Vector3 type object

Related

AS3 - How to use pixel/point detection with mouse event instead of object detection

This seems like it should be so easy that I'm embarrassed to ask, but I just can't get it.
I have a large round MovieClip (being used as a button). This MovieClip contains a PNG with a transparent background inserted into the MovieClip.
Due to its size there are large empty registration areas on the 4 corners (the bounding box).
How can I have the mouse register as being over only the circle pixels and not the blank space (of Alpha channel pixels) in the square boundary box?
Simple sample code:
public function simpleSample () : void
{
mc1.buttonMode = true;
mc1.addEventListener(MouseEvent.CLICK, doStuff);
}
public function doStuff (event:MouseEvent) : void
{
mc2.gotoAndStop(2);
}
Here are 3 different ways to accomplish this.
EDIT Since you've later explained that your button is an image, this first option won't work for you
If the shape flag on hitTestPoint works with your button (eg it's a shape), you can use hitTestPoint inside your mouse click handler to figure out if the click is actually over the object:
public function doStuff(event:MouseEvent){
//only continue if hit test point is true,
//the x and y values are global (not relative to the mc your testing as one might suppose)
//the third parameter should be true, so it takes into account the shape of object and not just it's bounds
if(mc1.hitTestPoint(stage.mouseX, stage.mouseY, true)){
mc2.gotoAndStop(2);
}
}
If the above doesn't work because you have bimtap data in your button, then an easy way to accomplish this is to just add a shape mask to the button.
So, either inside your button using FlasPro, mask everything with a circle shape, or, do it via code by doing the following when you first show the button:
var s:Shape = new Shape();
s.graphics.beginFill(0);
s.graphics.drawCircle(mc1.x + (mc1.width * .5), mc1.y + (mc1.height * .5), mc1.width / 2);
addChild(s);
mc1.mask = s;
If using an image as the button, or you want to set a threshold of how transparent to consider a click, then you can check the transparency of the pixel under the mouse:
function doStuff(event:MouseEvent){
//only continue if pixel under the mosue is NOT transparent
//first, you need a bitmap to work with
//if you know for sure the position of your bitmap, you can do something like this:
var bm:Bitmap = mc1.getChildAt(0) as Bitmap;
//annoyingly though, FlashPro makes timeline bitmaps shapes,
//so the above won't work UNLESS you take your bitmap in the FlashPro Library
//and export it for actionscript, giving it a class name, then it will be an actual bitmap on the timeline.
//As an alternative, you could (very CPU expensively) draw the whole button as a bitmap
var bmd:BitmapData = new BitmapData(mc1.width,mc1.height,true,0x00000000);
bmd.draw(mc1);
var bm:Bitmap = new Bitmap(bmd);
//we get the 32bit pixel under the mouse point
var pixel:uint = bm.bitmapData.getPixel32(bm.x + event.localX,bm.y + event.localY);
//then we grab just the Alpha part of that pixel ( >> 24 & 0xFF ).
//if the value is 0, it's totally transparent, if it's 255, it's totally opaque.
//for this example, let's say anything greater than 0 is considered good to be a click
if((pixel >> 24 & 0xFF) > 0){
mc2.gotoAndStop(2);
}
}

AS3 - How can I change contents of all the same Movieclips runtime?

So, basically, what I want to do is replace the contents of a sword MovieClip that's inside a Player MovieClip already, and is animated, so it has multiple instances of the sword MovieClip across the Player MovieClip.
Can I somehow edit the contents of the sword MovieClip in actionscript so as all the sword MovieClips update and are changed?
What I want to achieve is just changing weapons of a character animation that doesn't require me to await every frame and removeChild() the previous weapon and addChild() the new one of every instance of the weapon.
I'm not sure I'm getting it but Maybe you need a weapons event class
package WeaponEvents{
import flash.events.Event;
[Event(name="sword1", type="event.sword1")]
[Event(name="sword2", type="event.sword2")]
public class SwordEvent extends Event
{
public static const SWORD_1 : String = "sword1";
public static const SWORD_2 : String = "sword2";
public var arg:*;
public function SwordEvent(type:String, bubbles:Boolean = false, cancelable:Boolean = false, ... a:*) {
super(type, bubbles, cancelable);
arg = a;
}
// Override clone
override public function clone():Event{
return new LoadEvent(type, bubbles, cancelable, arg);
}
}
}
Just add the event to when your user chagnes weapons.
I'm in a similar boat as you. Except I have 30-50 bodyparts to cover, so I really can't go the brute force way.
If it's just sword that you want replace though, I assume in the animation it is on its own layer? Then a "cheap way" I found is:
Copy paste all frames in that layer (and only that layer) into another movieclip i.e. Sword1Swing.
Remove that layer (and only that layer) from your MC animation.
In yet another movie layer, put Sword1Swing on frame 1, Sword2Swing on frame 2, etc.
MC.gotoAndStop("SwordSwing"); SwordMC.gotoAndStop("Sword1Swing");
As long as you keep the .x and .y of the SwordMC synchronized with MC, the animation should always line up.
Hand often goes over the top of the sword, in which case... You can copy the hand too. Or the whole arm. Or you can custom shape your sword symbol's basic graphics in the library to match exactly with the empty spaces between fingers (copy and paste hand graphics in place to remove unwanted portions, then delete the hand graphics, then you'll have the perfect shape).
It's the ghetto way I know, I am very much in need of an actual efficient swapping. But this may help you to get through the project fast.

Input detection for a Group with overlapping transparent Images

I use a Group to store some Images and draw them to a SpriteBatch. Now I want to detect which Image has been clicked. For this reason I add an InputListener to the Group to get an event on touch down. The incoming InputEvent got an method (getTarget) that returns an reference to the clicked Actor.
If I click on a transparent area of an Actor I want to ignore the incoming event. And if there is an Actor behind it I want to use this instead. I thought about something like this:
myGroup.addListener(new InputListener() {
#Override
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
Actor targetActor = event.getTarget();
// is the touched pixel transparent: return false
// else: use targetActor to handle the event and return true
};
});
Is this the right way to do it? I thought returning false for the method touchDown would continue propagation for the event and let me also receive touchDown events for other Actors at the same position. But this seems to be a misunderstanding...
UPDATE
P.T.s answer solves the problem of getting the right Event. Now I have got the problem to detect if the hit pixel is transparent. It seems to me that I need the Image as a Pixmap to get access. But I do not know how to convert an Image to a Pixmap. I also wonder if this is a good solution in terms of performance and memory usage..
I think you want to override the Actor.hit() method. See the scene2d Hit Detection wiki.
To do this, subclass Image, and put your hit specialization in there. Something like:
public Actor hit(float x, float y, boolean touchable) {
Actor result = super.hit(x, y, touchable);
if (result != null) { // x,y is within bounding box of this Actor
// Test if actor is really hit (do nothing) or it was missed (set result = null)
}
return result;
}
I believe you cannot accomplish this within the touchDown listener because the Stage will have skipped the Actors "behind" this one (only "parent" actors will get the touchDown event if you return false here).

AS3 Determine if MovieClip fills another MovieClip completely

I am trying to determine in AS3 Flash if a draggable movieclip on the stage completely fills another movieclip also on the stage. I looked into another StackOverflow article with this code:
var inter = mcOverlay.getRect(this).intersection(mcLoadedImage.getRect(this));
if ((inter.width * inter.height) == 0) {
return false;
} else {
return true;
}
This code uses the intersect method, it works, but I also want to check that the movieclip is completely covered by the draggable movieclip on the stage.
Any suggestions? Thanks!
I think you could use compare each movieclips rect, ie compare the left, right, top and bottom values.
Instead of using intersection, use Rectangle.contains.
var contains : Boolean = mcContainer.getRect(this).contains(mcContained.getRect(this));
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/geom/Rectangle.html#containsRect()

action script 3 - is it possible to trigger click event only when mouse is clicked on the image part?

I have a problem and I have potential solution. But I wanted to confirm if there is an easy and simple way to solve my problem.
App type:
Isometric Game
Problem statement:
I am loading images in my flash app and have mouse events attached to them.
The images I load are prop images like vehicles, trees, buildings etc., and all of them are transparent.
Example: Red ball asset (please ignore the yellow background which I applied to describe the problem)
If I click on the actual image area (colored in red), then every thing works perfect
I don't want to trigger mouseevent when I click on empty image part (or transparent area, which I have shown in yellow color)
There is one way I know by creating masks in flash. I don't want to do it unless that is the final option left because I load image assets instead of flash assets and I don't want to create a new mask asset for all the assets
There is another method I was going to adopt by using getPixel method of Bitmap. Which is discussed here.
But there is another problem with this method.
I might be able to ignore the click event when I click on the empty part of the asset but if there is some other asset is behind the image in the same location, then I need to process the click event for the occluded image.
Well, thinking of solution to this problem takes me to the getObjectsUnderPoint where I can scan the occluded assets
Well, what you proposed as a solution is 100% valid. Just move the logic of determining what game object is clicked outside of that object.
Listen for MOUSE_DOWN/MOUSE_UP events at container which contains your game objects.
Catch an event
Check if the game object which is the target of this event is transparent at this point using BitmapData.getPixel32
If it is use getObjectsUnderPoint to find out all other game objects at this point
Find in a loop the first object which is not transparent at this point
Now you got the actual object which is hit.
One interesting solution is to use Sprite objects with the individual non-transparent pixels burnt onto them.
Suppose this is your Loader "complete" handler:
private function loaderCompleteHandler(event:Event):void
{
// Loader is not our child, we use a Sprite instead (below).
var loader:Loader = Loader(event.target);
var sprite:Sprite = new Sprite();
addChild(sprite);
var w:Number = loader.content.width;
var h:Number = loader.content.height;
// Use transparent bitmap.
var bitmapData:BitmapData = new BitmapData(w, h, true, 0);
bitmapData.draw(loader.content);
// Now burn the image onto the Sprite object, ignoring
// the transparent pixels.
for (var xPos:int = 0; xPos < w; xPos++) {
for (var yPos:int = 0; yPos < h; yPos++) {
var pixel32:uint = bitmapData.getPixel32(xPos, yPos);
var alpha:int = pixel32 >>> 24;
if (alpha != 0) {
sprite.graphics.beginFill(pixel32 & 0xFFFFFF, alpha / 0xFF);
sprite.graphics.drawRect(xPos, yPos, 1, 1);
sprite.graphics.endFill();
}
}
}
}
Essentially you want "empty" pixels that aren't clickable, and fully transparent pixels aren't quite the same thing. With this solution you get empty pixels.
Only problem is that this might be slow. Give it a shot.