How to create Minimap in LibGDX? - libgdx

I'm creating Minimap based on different rooms. I've finished all code related on displaying the correct rooms like in world.
Now it is just ArrayList of Sprites with different positions.
It looks like this:
I faced some problems.
First:
I don't know how to place it into frame.
Second:
I want to scroll it and move it on finger touch.
I guess that it should be some Group of Actors.
I looked on Group class and all it's inheritors and it isn't looking like there is Actor that can solve my problem.

If you want use actors you need to have an Image to show the TextureRegion it doesnt work with Sprite. A Dialog to popup and hide and a ScrollPane to enable scrolling of the minimap.
Dialog dialog = new Dialog( "", skin );
Table widget = new Table( skin );
// GameObject is a generic class use your project values
for ( GameObject gameObject : gameObjects ){
// you'll need to check if the position is relative to the parent or it's relative to
// the screen otherwise use the cell.setActorX() cell.setActorY()
Image image = new Image( gameObject.textureRegion );
image.setX( gameObject.x );
image.setY( gameObject.y );
widget.addActor( image );
}
ScrollPane scrollPane = new ScrollPane( widget );
scrollPane.setScrollingDisabled( false, false );
dialog.add( scrollPane ).width( width ).height( height );
To show and hid the dialog set this in update method/click listener.
dialog.show( stage );
dialog.hide();

Related

LibGDX - InputListener bounds on Table

I've set an InputListener on a Table with mouseMoved overridden. I'd like for the input to be received when the mouse is moved anywhere on the screen, but right now it only receives input when the mouse moves over the any of the widgets inside the table (see red box in screenshot).
font = new BitmapFont( );
style = new Label.LabelStyle( font, Color.WHITE );
xLabel = new Label( "x: ", style );
yLabel = new Label( "y: ", style );
xNum = new Label( "0", style );
yNum = new Label( "0", style );
this.setFillParent( true );
System.out.println( this.getWidth() + ", " + this.getHeight() );
this.addListener(new InputListener() {
#Override
public boolean mouseMoved(InputEvent event, float x, float y) {
xNum.setText( String.valueOf( x ) );
yNum.setText( String.valueOf( y ) );
return true;
}
});
this.left().top();
this.add(xLabel).padLeft( 5 );
this.add( xNum );
this.row();
this.add( yLabel ).padLeft( 5 );
this.add( yNum );
this.debug();
Most libgdx Actor's handle inputs by default,Tabledoes not.
The reason:
In the constructor ofTablesetTouchable(Touchable.childrenOnly);is called, which disables touchable for this widget, but enables it for its children. This is why input is only received over theTable`s widgets.
setTouchable( Touchable.enabled ); solves this problem
Thats the regular function if you bind an InputListener to an widget. Sure it's bound to the Widget and nothing more. And so it's bound to the size of the widget.
If you want to listen to all events you need to write an InputProcessor and handle the events yourself.
Use the InputProcessor Interface and add the processor to the gdx.
Gdx.input.setInputProcessor(yourProcessor);
The Stage is already an input processor and fires all events to the actors that are at that position. You can use a Stage for a good eventhandling for example to get to know if some tile or some character is clicked. But if you like more complex things you create your own InputProcessor. But if you like to you can add an Listener to the Stage and get all move events. This meight be a solution too. stage.addListener(listener);
Also take a look at Scene2D at the listener and event section
for more clarification.
Wiki InputProcessor
Else resize your Table to fit the screen size and you get all movements of the mouse. But i wouldnt recommend that. Everything you need to know for that you can find at the git wiki from the Table layout

Changing levels of nested movieclips in Flash AS 3.0

I am working on a zooming square navigation project (visually like Windows Metro, but squares take over the screen when clicked on). I'm having trouble getting the squares to the top layer (setChildIndex). Here is the code for when a square is clicked on:
function zoom(event:MouseEvent):void {
// saves location and size of icons
returnto = event.currentTarget;
returntoX = event.currentTarget.x;
returntoY = event.currentTarget.y;
returntoWidth = event.currentTarget.width;
returntoHeight = event.currentTarget.height;
// turn off button behaviours
event.currentTarget.buttonMode = false;
event.currentTarget.alpha = 1;
event.currentTarget.removeEventListener(MouseEvent.MOUSE_OVER, rollover);
event.currentTarget.removeEventListener(MouseEvent.CLICK, zoom);
event.currentTarget.removeEventListener(MouseEvent.ROLL_OUT, rollout);
// put clicked box on top
setChildIndex(returnto, numChildren-1);
// fade out icon decor
TweenLite.to(returnto.decor, .25, {alpha:0});
// make square take over screen, call finished when done
TweenLite.to(returnto, 1, {x:stage.x,y:stage.y, width:stage.stageWidth, height:stage.stageHeight, ease:Expo.easeInOut, onComplete:finished});
}
the squares are all inside a main movieclip (named navigation) on the stage (so the first square is navigation.sq_1)
I am not a Flash developer, so I'm really struggling with AS3. Hopefully this is enough info for someone to help. Thanks!
S
Just try to add your returnto clip with addChild() method:
// put clicked box on top
addChild(returnto);
Display List's default behavior when addChild() is to put the child on top of all childs. Other children's indexes are recounted.

Can I manipulate when DisplayObjects will render in AS3?

Without using BitmapData.draw(); is it possible to stop the display list from rendering, and perform a render when I see fit (whether that is of the entire display list or individual DisplayObjects).
If not, what is the best method to achieve this (inclusive of the option where I use BitmapData.draw() to render DisplayObjects that haven't been added to the stage)?
You can force a render after a mouse/keyboard event using e.updateAfterEvent(), but you cannot prevent the display list from redrawing to screen. Your best bet would be :
Add all your objects into one holder Sprite, which is off the stage display list.
Add a Bitmap and associated BitmapData to the stage.
Everytime you want to 'force' a render simply draw() the holder onto the BitmapData and it will be rasterized by the exact same renderer that typically operates. Just that you get more control over the rasterizing settings.
Try the following, or see this for more info:
function snapClip( clip:DisplayObject ):BitmapData
{
var bounds:Rectangle = clip.getBounds( clip );
var bitmap:BitmapData = new BitmapData( int( bounds.width + 0.5 ), int( bounds.height + 0.5 ), true, 0 );
bitmap.draw( clip, new Matrix(1,0,0,1,-bounds.x,-bounds.y) );
return bitmap;
}

In AIR/AS3 How to capture image of whats visible on the stage and save that image as anything like jpg/pdf

In AIR/AS3 Flex Mobile for Android project.
How can I capture an "image" of whats visible on the view
then just save that image as anything like jpg/pdf to the SD card?
Basically a screenshot on Android using AS3.
private function getBitmapData( target:DisplayObject ) : BitmapData
{
//target.width and target.height can also be replaced with a fixed number.
var bd : BitmapData = new BitmapData( target.width, target.height );
bd.draw( target );
return bd;
}
Create the byte array using JPEGEncoder and save this to Your sd card. In some case if the width and height of the target is not working, You can use the getbounds method to get the bounds of the object and from the bounds take the width and height.

Load External SWF, assign MOUSE_DOWN event, and prevent empty space / sub stage from being clicked

I've been developing in Flash now for over 15 years, and recently started developing games in Flash Actionscript 3. I am having some difficulties, and need some help. I've spent days trying to find a solution, with no luck.
I have a main SWF, which loads sub SWF animations. One object can have 5 different animations / SWFs associated with it. So lets say I have a chicken, chicken01.swf, chicken02.swf, ...
I assign a MOUSE_DOWN event to the first loaded SWF, then based on the tool used while clicking on the object, it will load the other animations. My problem is that each SWF has empty space around it which becomes clickable. I only need the object clickable, and not the empty space, because some of the objects can overlap each other, which makes it hard to click on the object behind another object.
The Sub SWFs / animations are on a single timeline, and I played with Bitmap Tracing to remove the empty space around the imported PNG objects. This works if I reduce the Stage size to behind the Object, but then screws up the size of the loaded SWF due to the Stage size being smaller than the object. So when I assign a width and height to the object, with a smaller stage, the object is huge. If I constraint the stage size to the size of the object, even as a Traced bitmap image, the stage is still clickable. I tried to assign the MOUSE_DOWN event to the object on the sub SWF, from the Main SWF, but this gives errors.
My goal is to load a sub SWF, assign the MOUSE_DOWN event, and only have the object clickable, and not the Stage, or empty space around the object.
Is this possible? I also played around with creating an invisible button, but this makes it difficult to assign to 300 + objects of different shapes and sizes.
Below is some of the code I'm using.
var loadimage = foreground_list[i].imagelocation + foreground_list[i].image;
var loader:SWFLoader = new SWFLoader(loadimage,{container:tn_mc,x:current_tn_x,y:current_tn_y,name:current_name,alpha:1,width:current_tn_w,height:current_tn_h,rotation:0});
loader.load();
tn_mc.buttonMode = true;
tn_mc.addEventListener( MouseEvent.MOUSE_DOWN, tn_down );
tn_mc.addEventListener( MouseEvent.MOUSE_UP, tn_up );
addChild( tn_mc );
function tn_down(e:MouseEvent):void
{
switch (MovieClip(this.root).PointerTool)
{
case "move" :
stage.addEventListener(MouseEvent.MOUSE_UP, stage_up );
e.target.startDrag();
break;
case "play" :
var loader4:SWFLoader = new SWFLoader(foreground_list.imagelocation + foreground_list.playimage,{container:tn_mc,name:e.target.name,x:foreground_list.setx,y:foreground_list.sety,width:foreground_list.setw,height:foreground_list.seth,rotation:0});
tn_mc.removeChildAt(0);
tn_mc.addEventListener( MouseEvent.MOUSE_DOWN, tn_down );
tn_mc.addEventListener( MouseEvent.MOUSE_UP, tn_up );
loader4.load();
loader4.addEventListener(Event.COMPLETE, completeactionHandler);
break;
default :
//Some other animation
break;
}
}
Create a movieclip - a vector shape inside each swf, that is the same shape as your clickable area. Set the alpha to 0% on the vector's fill color. Give it and instance name of something like activeArea, and assign your event listener to that instead of the outer shell moveiclip.
Another approach that might work is to use hitTestObject() on a MOUSE_DOWN event, which would allow you to choose to ignore the transparency.
EDIT
Hard to tell exactly what you are trying to do without seeing it. I didn't actually compile this so I'm not sure if this will work just the way it is, but in theory it should be close. Its a slightly different approach than you are using. I used Loader() instead of SWFLoader, and cleaned up the idea a little bit. As a side note, you should avoid the use of root in as3.
var _swfLoader:Loader;
var loadimage = foreground_list[i].imagelocation + foreground_list[i].image;
var loader:SWFLoader = new SWFLoader(loadimage,{container:tn_mc,x:current_tn_x,y:current_tn_y,name:current_name,alpha:1,width:current_tn_w,height:current_tn_h,rotation:0});
loader.load();
tn_mc.buttonMode = true;
tn_mc.addEventListener( MouseEvent.MOUSE_DOWN, tn_down );
addChild( tn_mc );
function tn_down(e:MouseEvent):void
{
tn_mc.addEventListener( MouseEvent.MOUSE_UP, tn_up );
switch (MovieClip(this.root).PointerTool)
{
case "move" :
stage.addEventListener(MouseEvent.MOUSE_UP, stage_up );
e.target.startDrag();
break;
case "play" :
_swfLoader = new Loader();
var req:URLRequest = new URLRequest(foreground_list.imagelocation + foreground_list.playimage);
_swfLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, animationLoaded);
_swfLoader.load(req);
break;
default :
//Some other animation
break;
}
}
function tn_up(e:MouseEvent):void
{
tn_mc.removeEventListener( MouseEvent.MOUSE_UP, tn_up );
}
function animationLoaded(evt:Event):void
{
_swfLoader.contentLoaderInfo.removeEventListener(Event.COMPLETE, animationLoaded);
tn_mc.removeChildAt(0);
var loadedSwf = evt.target.content;
loadedSwf.x = foreground_list.setx;
loadedSwf.y = foreground_list.sety;
loadedSwf.width = foreground_list.setw;
loadedSwf.height = foreground_list.seth;
loadedSwf.rotation = 0;
loadedSwf.addEventListener(MouseEvent.MOUSE_DOWN, onAnimationStart);
// might wanna add theses to an array to keep track of them and run clean up later on
// now add to some display list
}
function onAnimationStart(evt:MouseEvent):void
{
loadedSwf.addEventListener(MouseEvent.MOUSE_UP, onAnimationStop);
// play your animation or whatever else
evt.target.play();
}
function onAnimationStop(evt:MouseEvent):void
{
loadedSwf.removeEventListener(MouseEvent.MOUSE_UP, onAnimationStop);
// stop your animation or whatever else
evt.target.stop();
}