libGDX touch in effect after touch - java

I use the following code to match the user touch to a ball object's position, so when the user touches the ball it bounce back up. Code:
int x1 = Gdx.input.getX();
int y1 = Gdx.input.getY();
Vector3 input = new Vector3(x1, y1, 0);
cam.unproject(input);
if(ball.getBoundingCircle().contains(input.x, input.y)) {
ballBounce();
}
But I have a problem with the touch. If the user touches a certain position on screen for a moment and a ball (after a while more appear) about to reach the position the user touched, the ball will recognize itself as been touched and the ballBounce(); method will start and continue with other balls that reach to the same position until the user touch another position on the screen but then that position will be fixed till the new one... Do someone know how I can bypass that problem so if the user stop touching the screen then where he\she touched won't affect the ball objects?

It seems that you are not using any input processor.
Make your class implement InputProcessor and make your touchDown method look like this.
#Override
touchDown(InputEvent event, float x, float y, int pointer, int button) {
Vector3 input = new Vector3(x, y, 0);
cam.unproject(input);
if (ball.getBoundingCircle().contains(input.x, input.y)) {
ballBounce();
}
}
ClickListener (LibGDX API)

Related

LibGDX touchDown touchUp on different Actors

In my LibGDX game, I have a raster of clickable Actors. I want the player to be able to swipe across them and get the Actor where the swipe stops.
I don't want a GestureListener, the only thing I need is a touchUp event on the touchDown generated on another Actor. For example: think about Androids login screen where you swipe a pattern to unlock, but I only need an event for the last dot/Actor.
I have tried using touchDown / touchUp, but the event always fires on the Actor where the touchDown occurred.
A fine solution would be to get events for every Actor hit by the swipe, and then get a global touchUp event. Is this possible in a relatively accessible way - eg. without writing a whole new InputListener?
In touchUp() of your listener, call hit on the parent WidgetGroup of all your clickable Actors to see which one the finger is over when it is released. Note that since you're adding the actors to the group, the group needs to be added to the stage instead of adding the widgets to the stage directly. And remember that an Actor's X and Y are relative to its parent.
final WidgetGroup parent = new WidgetGroup();
InputListener inputListener = new InputListener(){
private final Vector2 tmp = new Vector2();
public boolean touchDown (InputEvent event, float x, float y, int pointer, int button) {
return true;
}
public void touchUp (InputEvent event, float x, float y, int pointer, int button) {
event.getTargetActor().localToParentCoordinates(tmp.set(x, y));
Actor releaseOverActor = parent.hit(tmp.x, tmp.y, true);
if (releaseOverActor != null){
//doSomethingTo(releaseOverActor);
}
}
}
for (Actor clickable : clickableActors){
parent.add(clickable);
clickable.addListener(inputListener);
}
The above is a little bit simplified. You might want to return true one touchDown only if pointer 0 is used, for example.

libgdx - correcting for multiple coordinate origins?

I have a collision Rectangle (the libgdx one, not awt) set to a ball's coordinates. What I want to do is check if I've tapped on it. Two problems:
a.) I don't know where the origin of the rectangle's coordinates is.
b.) I can't find a way of correct way of correcting for the tap location.
What can I do?
Edit per request:
public void create() {
camera = new OrthographicCamera(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
...
camera.unproject(tmpPos);
}
public void render() {
if (Gdx.input.isTouched()) {
float x = Gdx.input.getX();
float y = Gdx.input.getY();
tmpPos.set(x, y, 0);
camera.unproject(tmpPos);
// now the world coordinates are tmpPos.x and tmpPos.y
if (target.contains(tmpPos.x, tmpPos.y)) {
System.out.println("Touched. ");
score++;
}
System.out.println("Score: " + score + "..." + "X: " + (int) tmpPos.x + "...Y: " + (int) tmpPos.y);
...
}
I assume that you have a orthographic camera. (you really can't make a game without it)
You don't need to know its origin to check if is touched, but if you want to, the origin is
float originX = rectangle.x + rectangle.width / 2f;
float originY = rectangle.y + rectangle.height / 2f;
Anyways, you will need to unproject your touch coordinates, this means that you need to translate from the screen position to the camera position (world position).
For this you need a Vector3, is prefered to declare it somewhere outside of the method you are using, because initializing a object in every frame is not recommended.
Vector3 tmpPos=new Vector3();
And now check if your rectangle is touched, anywhere you want.
You have 2 ways to do this.
Do this in the render / update method, checking the position of the finger / cursor.
public void render(float delta) {
if (Gdx.input.isTouched() { // use .isJustTouched to check if the screen is touched down in this frame
// so you will only check if the rectangle is touched just when you touch your screen.
float x = Gdx.input.getX();
float y = Gdx.input.getY();
tmpPos.set(x, y, 0);
camera.unproject(tmpPos);
// now the world coordinates are tmpPos.x and tmpPos.y
if (rectangle.contains(tmpPos.x, tmpPos.y)) {
// your rectangle is touched
System.out.println("YAAAY I'm TOUCHED");
}
}
}
Options 2, use a input listener, so you only check when a touch event is triggered.
In the show / create method of your screen / game add your input listener
public void show() {
// .....
Gdx.input.setInputProcessor(new InputProcessor() {
// and in the method that you want check if the rectangle is touched
#override
public void touchDown(int screenX, int screenY, int pointer, int button) {
tmpPos.set(screenX, screenY, 0);
camera.unproject(tmpPos);
// now the world coordinates are tmpPos.x and tmpPos.y
if (rectangle.contains(tmpPos.x, tmpPos.y)) {
// your rectangle is touched
System.out.println("YAAAY I'm TOUCHED");
}
}
// and the rest of the methods
// ....
//
});
}
I would use the second way.
let me know if it worked for you.
My eventual solution was this:
Set the vector to the touch coordinates, however, with this correction: (Gdx.input.getY() * -1) + cy where cy is the height of the screen. I then unproject that vector, and draw it. Instead of the inverse y location I got without the correction, the circle follows directly under my finger and communicates with other objects in the world perfectly.

Drag And Drop LibGDX

I want to make a game where you can build stuff by dragging and dropping objects into place. I think LibGDX only supports DragNDrop on Actors, but I need physics on bricks in order to make them fall down if the construction is not stable.
So far, my approach to drag and drop is:
for(Brick b : map.getList()){
final Image im = new Image(b.ar);
stage.addActor(im);
im.setPosition(b.posX, b.posY);
im.setOrigin(b.posX, b.posY);
im.addListener((new DragListener() {
public void touchDragged (InputEvent event, float x, float y, int pointer) {
im.setOrigin(x, y);
im.setPosition(x, y);
//System.out.println("touchdragged ---> X=" + x + " , Y=" + y);
}
}));
}
where the map.getLists contains all bricks to be painted. b.ar is the texture to be painted.
With this aproach [this] is what happens. I don't know what may be causing it.
#Override
public void render(float delta) {
spritebatch.begin();
map.getWorld().step(1/60f, 6, 2);
renderer.render(map.getWorld(), camera.combined);
if(Gdx.input.justTouched()){
Vector3 touchPoint = new Vector3(Gdx.input.getX(), Gdx.input.getY(),0);
camera.unproject(touchPoint.set(Gdx.input.getX(), Gdx.input.getY(), 0));
System.out.println(touchPoint);
}
stage.draw();
spritebatch.end();
}
Of course i'd like to make the body fell (with the box 2d engine from libgdx) if you drop the object and it has nothing under it.
Thanks in advance
You're setting the origin in your listener callback to a screen coordinate. That is not going to work.
The origin is used to define the "center" of your object, so when you reposition it, Libgdx knows which part of the actor to put where. Generally the origin is either the bottom left corner of the object (I think this is the default) or its the center of the object.
I guess you may want to reset the origin so if someone taps on the left edge of a brick and then you reposition the object you'll reposition that point on the brick (and not reposition the bottom left corner of the brick). To do that you'll need to convert the screen coordinates into coordinates in the actor's space.
That's all somewhat icky though. I think you'd be better off just doing relative repositioning. Instead of trying to position the brick absolutely with setPosition just reposition it relatively:
im.setPosition(im.getX() + dx, im.getY() + dy);
Then it doesn't matter where the "origin" is.
You'll have to compute dx and dy in your listener based on the previous touch point.
It appears that the drag listener gives coordinates relative to the origin of the actor that is raising the event. That is a bit strange when you are moving that actor in response to the drag events, because the origin keeps changing. Essentially, I found that if I just move the actor by the x and y values of the event, it will follow the mouse or finger.
One improvement is to record the position that the drag started at and use it as an offset, so the pointer stays the same distance from the actor's origin.
Another option might be to add the listener to the stage instead of the button. I expect the coordinates would then be relative to the stage's origin, which is not changing. I haven't tried that technique.
Here's the code I used to drag a button horizontally:
DragListener dragListener = new DragListener() {
private float startDragX;
#Override
public void dragStart(
InputEvent event,
float x,
float y,
int pointer) {
startDragX = x;
}
#Override
public void drag(InputEvent event, float x, float y, int pointer) {
insertButton.translate(x - startDragX, 0);
}
};
dragListener.setTapSquareSize(2);
insertButton.addListener(dragListener);
If you want to drag something in two dimensions, just copy the x code for the y position.

Scene2d - Rotated actor not translated as expected

I asked this at the libgdx forums but didn't get a response so I was hoping y'all could help me out:
I have Actors that represent game pieces. What I'm trying to do is make it so the player can click-and-drag the tile to move it around the screen and rotate it multiple times before submitting the placeTile command. From what I understand of DragAndDrop it doesn't seem to be designed with my use case in mind so I figured I'd instead attach a dragListener listener to each game piece (code below). It works well for dragging, except I can't figure out how to set the 'minimum distance before drag starts' to 0... but that's not my main question (though any insights would be appreciated )
Anyway, the big problem comes in when I rotate the actor, and then try to drag it: At 30 degrees rotation, drag acts almost like normal: at 60 degrees, very small movements of the mouse send the actor moving in a tight circle very quickly. Another 30 degrees, the tile actor exits the screen in 1-2 frames, moving in a wide arc. If the actor is rotated clockwise, it's movements are clockwise; same pattern for counter-clockwise.
It looks like the translation of the actor is taking rotation into account; I guess my question is, is it possible to rotate an Actor/Group without the rotation affecting future translations? Alternatively, is there a better way to drag an Actor around the screen based on touch/mouse input? I included some code below: I imagine I'm screwing up something basic, but I can't figure out what:
// during initial stage creation
tileActor.setOrigin(tileActor.getWidth() / 2, tileActor.getHeight() / 2);
tileActor.addListener(new DragListener() {
public void dragStart(InputEvent event, float x, float y,
int pointer) {
chosenTileActor = event.getTarget();
}
public void drag(InputEvent event, float x, float y, int pointer) {
Actor target = event.getTarget();
target.translate(x, y);
}
});
And for the listener that deals with rotation via scrolling mouse wheel:
multiplexer.addProcessor(new InputAdapter() {
#Override
public boolean scrolled(int amt) {
if (chosenTileActor == null)
return false;
else
chosenTileActor.rotate(amt * 30);
return true;
}
});
Any pointers? Am I even going the right direction by using DragListener?
Thanks for reading!
Instead of translating, just set the actor's position directly to the stage coordinates of your drag event:
tileActor.addListener(new DragListener() {
private float offsetX, offsetY;
#Override
public void dragStart(InputEvent event, float x, float y, int pointer) {
Actor target = event.getTarget();
this.offsetX = event.getStageX() - target.getX();
this.offsetY = event.getStageY() - target.getY();
}
#Override
public void drag(InputEvent event, float x, float y, int pointer) {
event.getTarget().setPosition(event.getStageX() - offsetX, event.getStageY() - offsetY);
}
});
I'm computing the offsets in dragStart so that the actor doesn't immediately jump to wherever I clicked when I started dragging (making the drags relative to my mouse). Tested this and it works with any rotation.

Libgdx + rotate sprite point to the touch position

I'm having problem trying to point my "arrow_sprite" to the touch position.
The result I want is that the arrow(the sprite that I want to rotate)will point to the touch position.
How can I acheive this?
You need to calculate the vector between the touch position and the sprite position. To do so you have to unproject the touch position received through your InputProcessor.
public class MyInputProcessor implements InputProcessor {
...
#Override
public boolean touchDown (int x, int y, int pointer, int button) {
Vector3 touchDown = new Vector3(x, y, 0);
camera.unproject(touchDown);
Vector3 spritePosition = new Vector3(yourSpriteX, yourSpriteY, 0);
spritePosition.sub(touchDown);
return false;
}
...
}
Now we have a vector pointing from your sprite to the touch position. From there you just need to calculate the rotation angle. One way would be to use Vector2.angle(), as such creating a Vector2 from the spritePosition Vector3.
If you want to draw in 2D, then you have to set your SpriteBatch to Camera.combined,
something like this : yourBatch.setProjectionMatrix(yourCamera.combined);
Then you must unproject the camera like "batch" said.
After, you must choose if you want to use a vector or 2d coordinate. Basically identycal ending for angle calculation :
First define a float degrees then make it like so in the touchDown method :
degrees = (float) ((Math.atan2 (touchPoint.x - arrowPosition.x, -(touchPoint.y - arrowPosition.y))*180.0d/Math.PI)+90.0f);
assuming you use a Vector3 or 2 for both input and arrow.
then when rendering the Arrow sprite :
Batch.draw(Arrow, x, y, originX, originY, width, height, scaleX, scaleY, degrees);
Hope this will be helpfull.
Just like Psilopat said, we define the degree by :
degrees = (float) ((Math.atan2 (touchPoint.x - arrowPosition.x, -(touchPoint.y - arrowPosition.y))*180.0d/Math.PI)+90.0f);
but if the rotation seems wrong, change the "+90.0f" at the end of line to something else. My arrow is pointing up, so I change it to "-180.0f"

Categories

Resources