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
Related
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();
I have the following function in a scene that extends citrus.core.starling.StarlingState - it loads the PlayerRun animation and displays it on the screen. For the most part, this code works: I see the sprite on the screen (it's running in place).
protected final override function DrawScene ():void
{
Player = new CitrusSprite ( "Player" );
Player.velocity = [60, 0]; // Doesn't seem to have an effect
Player.x = 40;
Player.y = 40;
var oClip:MovieClip = new MovieClip ( Resources.getTextures (
"PlayerRun" ), 24 );
Player.view = oClip;
add ( Player );
}
I'm not sure how I'm supposed to use the velocity property - there's no documentation for it and no matter what numbers I use in the code above, it doesn't change the display: the animation plays but the sprite is stationary (it doesn't move horizontally as I would expect it).
Am I using the velocity property incorrectly? Does Citrus support sprite velocity or is this something I'd have to implement myself?
As it turns out, CitrusSprite has a property, updateCallEnabled that's false by default and disables calls to update(). Once I set this property to true, the code started working as expected.
I haven't used Citrus yet, but looking at the source code it should work the way you've gone about it assuming that the update method is called on your player:
You can review the way the velocity property works at these locations:
The getter and setter for velocity.
The update loop for CitrusSprite.
MathVector, the type used for velocity internally.
I suspect you need to add the player to something that will queue it for updating.
So I have this titledborderbox in flex 4 with custom skin, I am working on drag and drop application and I have run into some problems.
When I initiate drag and drop on that component I am able to drag it's children but not the titledborderbox itself, for example: I can grab the label, or button which is inside this box and I can start dragging it but that is not a desired functionality. I should be able to click anywhere on the titledborderbox and drag the whole component with it's children to the new position. I have tried the mouseChildren = false but that disables the events on the children which is not good as some of the children are buttons with which I have to be able to interact anyway. Is there any workaround this problem, how could that be solved?
My titledborderbox containers are in HGroup, I would like to be able to drop the titledborderbox at specific position, for example: if I have 4 titledborderbox containers and I start dragging the last one then I would like to be able to place it at the position 1 in the HGroup container but drag manager always places the dropped item as the last item in the group, is there any solution to that?
I have to use this layout and I can't change to other layout, I can't find any decent example of what I am trying to achieve, is that even possible? Any help appreciated, thank you. Here is my code for dragging:
private function handleStartDrag( evt:MouseEvent ):void
{
// grab the item renderer and relevant data
var dragItem:IUIComponent = evt.target as IUIComponent;
var dragSource:DragSource = new DragSource();
dragSource.addData( dragItem, "item" );
DragManager.doDrag( dragItem, dragSource, evt );
this.buttonMode = true;
}
protected function handleDragEnter( evt:DragEvent ):void
{
if( evt.dragSource.hasFormat( "item" ) )
DragManager.acceptDragDrop( evt.target as IUIComponent );
}
protected function handleDragDrop( evt:DragEvent ):void
{
var dragItem:Object = evt.dragSource.dataForFormat( "item" );
var dragItemOwner:Group = ( dragItem.owner as Group );
dragItemOwner.removeElement( dragItem as IVisualElement );
var targetOwner:Group = ( evt.target as Group );
targetOwner.addElement( dragItem as IVisualElement );
}
Try changing
DragManager.acceptDragDrop( evt.target as IUIComponent );
to
DragManager.acceptDragDrop( evt.currentTarget as IUIComponent );
event.target is the reference to the object with "caused" the event (maybe an image or textfield within you box). event.currentTarget is the reference to the object which binds the event listener (your box).
Another option could be to set the mouseChildren-property of your box to false so that they do not fire MouseEvents. But this is only useful if those children should NEVER fire a MouseEvent.
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;
}
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();
}