Andengine: destinguish between OnAreaTouched and OnSceneTouched - java

Hi Everyone,
in the game I develop with AndEngine, there are a lot of sprites running around. Now every of this sprites has a TouchArea registered to the scene, because I display some informations about the sprite when it is touched. The Scene itself has a OnSceneTouchListener that I use for moving the camera around and to zoom.
My problem is, that every time the user moves the camera (by touching the display somewhere and moving his finger around) the OnAreaTouched() method of any sprite, that is accidentally under the finger, gets called, when the movement is finished (finger gets lifted). I already limited the triggering events to event.getAction()==UP (before it was a real mess of called touchAreas) but this is not enough. If the user is zooming or moving the camera, the sprites touchAreas should not be activated.
Is there any way I can distinguish between an OnAreaTouched-event and an OnSceneTouched-event? Which one is called first and can I suppress the other?
This is my OnSceneTouched() method (simplified):
public boolean onSceneTouchEvent(Scene scene, final TouchEvent event) {
boolean isZooming = event.getMotionEvent().getPointerCount() >= 2;
if (event.getAction() == MotionEvent.ACTION_DOWN) {
// REMEMBER FIRST TOUCHPOINT, TO KNOW IN WHICH DIRECTION TO MOVE
this.touchPoint = new Point(event.getMotionEvent().getX(), event.getMotionEvent().getY());
} else if (event.getAction() == MotionEvent.ACTION_MOVE) {
if (isZooming) {
// DO SOME ZOOM STUFF
} else {
// DO SOME MOVEMENT STUFF
}
return true;
}
OK, actually this is not very interesting - but as you can see I always return true to signal that the touch event was handled. Still the OnAreaTouched() gets called
This is a typical OnAreaTouched() Method of a sprite:
public boolean onAreaTouched(final TouchEvent touchEvent, float touchAreaLocalX, float touchAreaLocalY) {
if (touchEvent.getAction() == TouchEvent.ACTION_UP) {
// DISPLAY INFORMATION ABOUT THE SPRITE
return true;
}
return false;
}
You see, there is nothing special to it. So I hope someone can help me out here to find a solution how to suppress the OnAreaTouch-event when the OnSceneTouch-event should be used. Maybe I can somehow catch the event.getAction()==UP in the OnSceneTouched()-Method??
I hope I could explain the problem good enough for you to understand (sorry, it's not that easy for me : ). Any help is much appreciated, and thank you for you time!
regards
Christoph
edit:
After experimenting with MahdeTo's suggestion to tag the event somehow I found out the following:
the TouchEvent that triggers the OnSceneTouchEvent() Method is not the same as the one triggering the OnAreaTouched() Method.
OnAreaTouched() gets called 20 ms later than OnSceneTouchEvent()
the event calling OnAreaTouched() starts actually when the user puts his finger down on the display (than he moves it around and the OnSceneTouchEvent() gets called multiple times), when he then lifts his finger the first event stops and gets handled. (I tried it out by measuring the time)
So I came up with the solution to measure how long a touch event lasted. If the event is longer than 200 ms, I guess the user wanted not to simply click but move or zoom (because these actions usually take longer). So now the OnAreaTouched() method gets only called when someone really meant to click and not accidentally swiped over the area.
but it's still not a good solution and I would really appreciate if anyone knows more about controlling such events.
thank you

I have recently written a fairly complicated application using touch controls - pressing buttons on the screen, pinch zooming, rotating and moving objects with two fingers and more. What I ended up doing was iteratively improving the application and adding rules controlling how to respond to different combinations of touch events. Whenever something was wrong, I recorded the sequence of TouchEvents and added rules so that the misbehaving action was handled.
You can also create boolean switches that prevent the onAreaTouched to execute, just add a condition that, for example if you want to touch an object, checks whether doNotTouchObject is true or false.
Another thing I found useful were touch mode switchers. I wrote several methods that completely change the behavior when user touches the screen. I believe you can switch them on the go, so it should be possible to call scene.setOnAreaTouchListener(touchListener) while you are touching the screen.
Since un/registering touch areas is blazing fast, simply unregistering certain touch areas while you perform some action is a possibility. After the action is completed, reregister them again.
I used a combination of all of the above and it works rather well, but it is essential to keep the code clean, otherwise debugging or implementing new features will be a pain.

maybe you can use the TouchDetector.
use the clickDetector instead of process TouchUp event, then only the detected click event is processed by the sprites;
use the scroll detector for the Scene Scrolling too;
enable the SceneTouchListenerBinding and TouchAreaBinding will also help you to by pass all the unintended in-progress events.

Related

JavaFX Button spams MouseExit and MouseEntered

I'm trying to make a button disable when the mouse enters the node and enable it again when the mouse leaves it. The button responds to MouseEvent.ANY and the handle method gets the event type and handles it accordingly.
The println shows a constant stream of this even though I don't move the cursor:
MOUSE_EXITED
MOUSE_EXITED_TARGET
MOUSE_ENTERED
MOUSE_ENTERED_TARGET
MOUSE_EXITED
MOUSE_EXITED_TARGET
MOUSE_ENTERED
MOUSE_ENTERED_TARGET
MOUSE_EXITED
MOUSE_EXITED_TARG
This makes the button flicker on and off.
public void handle(MouseEvent me) {
System.out.println(me.getEventType());
if(me.getEventType() == MouseEvent.MOUSE_ENTERED_TARGET || me.getEventType() == MouseEvent.MOUSE_MOVED) {
nejButton.setDisable(true);
}
else if((me.getEventType() == MouseEvent.MOUSE_EXITED) || (me.getEventType() == MouseEvent.MOUSE_EXITED_TARGET) )nejButton.setDisable(false);
}
Looks like disabling a Node means the mouse is no longer considered to be within its bounds. It may be a bit of a stretch, but I believe this is documented by the Node#disabled property:
Indicates whether or not this Node is disabled. A Node will become disabled if disable is set to true on either itself or one of its ancestors in the scene graph.
A disabled Node should render itself differently to indicate its disabled state to the user. Such disabled rendering is dependent on the implementation of the Node. The shape classes contained in javafx.scene.shape do not implement such rendering by default, therefore applications using shapes for handling input must implement appropriate disabled rendering themselves. The user-interface controls defined in javafx.scene.control will implement disabled-sensitive rendering, however.
A disabled Node does not receive mouse or key events. [emphasis added]
What happens, I believe, is that when you disable the Node the mouse "exits" because said Node is no longer eligible to receive mouse events. This happens regardless of if you move your mouse or not. Then your code reacts to the mouse "exiting" and you enable the Node. However, since the now enabled Node is again eligible to receive mouse events, and the fact that you kept your mouse in the same place, the mouse "enters" the Node again. Your code then reacts to the mouse "entering" the Node and disables the it again—causing a never-ending loop.
I suspect there's no way to work around this behavior1.
That being said, I find the desired goal strange. What are you trying to tell the user by having the Button disabled only when the mouse hovers over it? It seems that would convey, "Come click this button... just kidding!". That doesn't seem like a pleasant user experience.
1. Since a disabled node does not receive mouse events one could argue this behavior is a bug. However, stopping this loop from occurring won't help your case; moving the mouse out of your button won't fire a mouse-exited event, regardless of if there's a "disabled while hovering" mouse-exited event fired or not. I also am inclined to believe this behavior is not a bug.

Detect swipe gesture without lifting the finger

I am developing a game for android with libgdx and I have a little problem, I want to make it trigger a function when the user swipes, this I achieve. The problem is that it runs the function when you finish swiping (when you lift your finger). How can I make the function run while the swipe is doing?
This is the current code for the gesture listener:
private static class DirectionGestureListener extends GestureAdapter{
DirectionListener directionListener;
public DirectionGestureListener(DirectionListener directionListener){
this.directionListener = directionListener;
}
#Override
public boolean fling(float velocityX, float velocityY, int button) {
if(Math.abs(velocityX)>Math.abs(velocityY)){
if(velocityX>0){
directionListener.onRight();
}else{
directionListener.onLeft();
}
}else{
if(velocityY>0){
directionListener.onDown();
}else{
directionListener.onUp();
}
}
return super.fling(velocityX, velocityY, button);
}
}
And the game scene:
Gdx.input.setInputProcessor(new SimpleDirectionGestureDetector(new SimpleDirectionGestureDetector.DirectionListener() {
#Override
public void onUp() {
/*something*/
}
#Override
public void onRight() {
/*something*/
}
#Override
public void onLeft() {
/*something*/
}
#Override
public void onDown() {
/*something*/
}
}));
I looked into GestureDetector.java of libgdx source code itself. fling() will be executed whenever event of touchUp() happened. Thus aligned with what you experienced.
I think an option to make this work is to implement such gesture detection yourself by extending InputAdapter class or implementing InputProcessor interface class, then work on touchDown(), and touchDragged() to have an intended effect as you aimed for.
The idea is to keep track of touch id as it firstly touched on the screen inside touchDown() (its 3rd parameter is int button which is what you're looking at), then use that id to check and further operate inside touchDragged(). If id matches, then for simple approach, you can check whether user touched and moved for pre-defined distance by comparing it against original touching position.
Let's say we want a swipe gesture only when user firstly touches the screen, moves it for (at least) distance we pre-defined set, and within pre-defined duration. So if user firstly touches the screen, moves finger around but still not more than pre-defined distance, then finally moves far enough within duration we've set, this still won't be treated as swipe as our conditions set that it must be from first intention (first touch) to do such gesture. This means we calculate distance against original touching point, not moved point. Of course, you can customize the conditions to suit your need too.
From above, conditions to regard it as swipe gesture can include following (you can adapt these yourself)
distance it moved compared to original touching point
duration it took to move (might be 150ms, etc), longer duration user can be more relaxing not to act fast to make it registered as swipe gesture
only 1 swipe at a time can be taken into effect i.e. if user uses 2 fingers (2 touches) to swipe at the same time, only first one (or last one) can be used as swipe effect, etc.
Treating it separately for x, y direction is per your need. If so, you have to add handling code inside touchDragged() to check for both direction and send you corresponding event i.e. swipe-x, or swipe-y per se. Then you hook it up by calling one of your method to let your game knows.

LibGDX Scene2D acting odd to multi-touch

So I've recently been abusing Google for an answer, however just can't find one, and my head's starting to ache. I've recently released my second game on the Google Play Store, and only now have I been getting feedback that the people aren't too crazy about the button pressing mechanics (same mechanics as the first game). My game can be found here if you want to give it a go, unfortunately I don't have an HTML version hosted elsewhere.
Making the game, I used LibGDX, and for the UI, I've used Scene2D that comes with LibGDX. My game requires that you press the green "bug" button to get a point (the "bug" buttons cycle around at random), you are forced to play against a timer, so you would obviously want to go faster for a higher score (think Piano Tiles).
Now, to the problem at hand, one of the game modes in my new game is called "Zen", two buttons, one gives you 1 point, the other 2 points. The problem I'm experiencing is that if you hold both "bug" (bottom right and bottom left) buttons down, and release one of the buttons, the released one triggers a score increase, and the other button resets as if it was never pressed on in the first place (even if your finger is still on it). Here's the code for my Scene2D buttons (working off memory, don't have my code near me at the moment):
// All is initialized correctly, no crashes, here's the part about the listener.
leftBugButton.addListener(new ChangeListener()
{
#Override
public void changed(ChangeEvent event, Actor actor)
{
// Run code here when button has been released.
}
}
// And the right hand button's code. Initialization is perfect, no problems.
rightBugButton.addListener(new ChangeListener()
{
#Override
public void changed(ChangeEvent event, Actor actor)
{
// Run code here when button has been released.
}
}
Getting to my question, what I want to go for is that if a button press has been completed (pressing and then releasing), I don't want the other buttons to reset to an unpressed state.
Any tips, or perhaps a different Listener I should use, or even dumping Scene2D for these buttons (using sprites with InputListener for example)? Any help would be greatly appreciated.
Thanks in advance,
TV
Don't use ChangeListener for that, use a ClickListener (button.addClickListener(new ClickListener...)) and handle your process on touchUp event, ChangeListener gonna process if ANYTHING on the button change and it's not a good approach to handle a button release event.
Found my problem, I kept rebuilding the Stack after every button press, resulting in the odd behavior, opted instead for a .setStyle() on my buttons which is working a lot better.

Android some action when button is pressed

I want to rotate my object when I PRESS_DOWN button and stop rotating when I PRESS_UP.
I tried to do it with OnTouchListener but seems there is no way to kmow that button is still pressed. For example I touch button 1st time, and there is 1st touch event raised with ACTION_DOWN, but then if I keep my finger not moving nothing will happen (no more events). Or if I will move it very fast there will be different speed of raising on touch event ACTION_MOVE. Different speed that depends on finger's speed moves is unacceptable for my task. So I decided to start timer (that do rotating with fixed speed) when ACTION_DOWN raised and to cancel TimerTask when ACTION_UP is raised, it works good, just like I need. But I think it's not the best solution or even the worst. Please give me an advice of other possible solutions.
Your solution of using a timer is good, and is what you are supposed to you.
If you are running an animation, you cannot rely on UI events, you have to have a separate thread or timer for periodic updates.
Using timer is a good way. To my knowledge, I think using a simple if-else may solve your problem more efficiently. For example,
boolean state = true;
if(state)
{
// do the code for ACTION_DOWN
}
else
{
// do the code for ACTION_UP
state = true;
}
Give a loop to this if statement, as many times you want. But I don't know, whether this solution meets your requirements, or not.

How to detect a "win" in a Minesweeper game?

I am working on a Minesweeper which I have pretty much complete.
The only thing missing is the detection of winning. What would be the best way to implement this? I know its going to be part of the actionlistener that detects clicks, at some point the last click should detect a winner.
Could anyone give me some ideas thanks!
The player has won if
numUnopenedCells == numBombs
(where a cell is unopened if it is in its initial state, or flagged as a mine).
If numUnopenedCells > numBombs then the player has unopened cells which are not bombs (i.e. some work left to do)
If numUnopenedCells < numBombs then the player has necessarily "opened" a bomb cell and already lost.
I know its going to be part of the actionlistener that detects clicks, at some point the last click should detect a winner.
Yes, this snippet would be executed directly or indirectly by the action listener. I'd suggest you have a model of the game state, and in the openCell(int x, int y) method you check the above, and take the appropriate action.
If the opened fields are #(all fields) - #(bomb fields).

Categories

Resources