libgdx - correcting for multiple coordinate origins? - java

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.

Related

Java little ball runs away from cursor

I'm trying to make a graphic project where a Ball runs away from my cursor, I already did the other way around where the ball seeks my cursor and when she arrives she loses velocity so it's like she's running fast until she comes around a range of 10 pixels and then she loses velocity until she touches the cursor.
The thing is, I can't find a way to make the ball run away from the cursor in a way that when I enter a diameter(from the ball), she runs slow, if I approach more she starts to run faster to get away but when my cursor leaves the diameter, she runs slow until she stops once again.
I hope I made it clear, I thought about a solution but I don't know if there's a library or some built function in Java that I could use guys:
-have like a percentage from 0 to 100 where the distance between my cursor and the ball fits inside, 0% is velocity=0, 100% is velocity=4 for example, do you have any idea if there is such thing that I could implement?
Thank you in advance!
I've made a Vector class where I change it and access the X and Y coordinates to make the ball move, I used basic trigonometry to make the vector ALWAYS the same length.
code of my ball (Chaser) class:
public class Chaser {
private double x;
private double y;
private double vel = 1;
private double hyp;
private Vector vector = new Vector(0, 0);
private double distance;
public Chaser(int width, int height){
x = width/2;
y = height/2;
}
public void setVel(Point m){
if(m.x != x)
hyp = Math.sqrt(Math.pow(Math.abs(m.x - x), 2) + Math.pow(Math.abs(m.y - y), 2));
else
hyp = Math.abs(m.y - y);
}
public void setDirection(Point m){
if(hyp == 0) return;
vector.change((m.x - x)/hyp, (m.y - y)/hyp);
}
public void draw(Graphics g){
g.setColor(Color.RED);
g.fillOval((int)x - 10, (int)y - 10, 20, 20);
g.setColor(Color.BLACK);
g.drawLine((int)x, (int)y, (int)(vector.getX()*15*vel) + (int)x, (int)(vector.getY()*15*vel) + (int)y);
}
public void move(Point m){
setVel(m);
setDirection(m);
useVector();
}
public void useVector(){
if(vector == null) return;
x -= vector.getX() * vel;
y -= vector.getY() * vel;
}
public void calculateVelocity(Point m){
if(vector == null) return;
// I don't know what to do yet
}
}
If you want to just push the ball around you can do something simple. Let's use vectors to make it easier to understand. Say ball holds the ball's center (x,y) and mouse contains the mouse position (x,y).
You can compute the distance between ball and mouse, that is (mouse - ball).length() to get how far away the mouse is from the ball.
If the distance > ball radius then the mouse is outside.
Otherwise you can do:
tmp = ball - mouse // get the vector from mouse to the ball.
tmp = tmp / tmp.length() * ball_radious // change the vector's length to match the radious of the ball.
ball = mouse + tmp // Move the ball such that the mouse will be on the edge.
As you move the mouse the ball will get pushed by the mouse.
If you want a bit of inertia, so the ball doesn't just stop when you don't push it anymore then you need to keep an additional vector speed and use tmp to get an acceleration.
Something like this:
tmp = ball - mouse // get the vector from mouse to the ball.
force = max(0, ball_radious - tmp.length()) // how strong we push the ball.
acceleration = tmp / tmp.legnth() * f(force) // compute the acceleration vector. f(force) is some function based on force, try k*f or k*f*f and see what looks better for your setup.
speed = speed * kDrag + acceleration // update the speed, kDrag should be between 0 and 1, start with something like 0.8 and try different values.
ball = ball + speed * time_delta // Update the ball's position.
You can play with the constants to get the right feel that you're looking for. time_delta is meant to normalize the speed between frams so you don't need to worry too much if there's some inconsistency between them. You can use a constant as well, but the movement might become jerky at times.
All operations above are vector operations.

libGDX touch in effect after touch

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)

Achieve parallax effect in libGDX game

I'm working in a game with some friends in which we have a large horizontal world and a OrthographicCamera that shows only 1/3 of it. This camera it's moved when the horizontal position of the player change so the camera only move to the left and to the right.
Some of the objects showed in the game are near the player point-of-view but others are far away (for example, islands). With this in consideration, we cannot set fixed positions for elements and move only the camera. We need to achieve a parallax effect taking in consideration the distance of the elements.
Here is a simple image to explain it better:
The viewport to the left shows 3 objects of the game. The green one is near the player, the red ellipse is far and the yellow one is in the middle. In the viewport to the right the camera has been moved to the right so all the objects disappear to the left. The thing is that the relative movement of the green rectangle is greater than the movement of the yellow. In the same way, movement of yellow object is greater than red object movement.
I created all my assets scaled taking in consideration how far they are but now, how can I simulate this perspective using libGDX? Is there any class to do it? If I have to set elements position in each iteration, how could I calculate the right position?
Note that the example below is not tested as I am just recalling how I did it. The idea is simple - create layers with an extra layer for each with initial positions and velocity and move them. If a layer goes off the edge, put another one (that is why we create an extra layer) at the opposite edge.
Say you have a parallax object that takes initial positions, size, and velocity-
public class Parallax extends DynamicGameObject {
public float width, height; // Use setter/getter if you prefer
public Parallax(float x, float y, float width, float height, float velocityX, float velocityY) {
super(x, y, width, height);
velocity.set(velocityX, velocityY);
this.width = width;
this.height = height;
}
public void update(float deltaTime) {
position.add(velocity.x * deltaTime, velocity.y * deltaTime);
}
public void setPosition(float x, float y) {
position.set(x, y);
}
}
DynamicGameObject is taken from SuperJumper demo-
public class DynamicGameObject extends GameObject {
public final Vector2 velocity;
public final Vector2 accel;
public DynamicGameObject(float x, float y, float width, float height) {
super(x, y, width, height);
velocity = new Vector2();
accel = new Vector2();
}
}
GameObject as well-
public class GameObject {
public final Vector2 position;
public final Rectangle bounds;
public GameObject(float x, float y, float width, float height) {
this.position = new Vector2(x,y);
this.bounds = new Rectangle(x - width/2f, y - height/2f, width, height);
}
}
Say we have two layers - one in front and the other goes at back. We have one texture for each. Each texture fills the entire screen. We create two instances for each layer so that when one texture starts going off the screen, the other shows up at the edge to fill the gap. If you have smaller textures, you need to determine first how many textures you need to fill the screen and then create layers with one extra to fill the gap in between.
We can create an array of parallax layers during world creation-
Array<Parallax> parallaxList = new Array<Parallax>(4);
We can create the layers like this-
// Back
/* First parallax for back layer is at 0 x-axis. If you want to move the texture from right to left, the value of BACK_VELOCITY_X should be negative. You can experiment with velocity value for desire pace of movement. We do not want our layer to move on y-axis. Hence, it is set to 0. */
parallaxList.add(new Parallax(0, BACK_TEXTURE_HEIGHT, BACK_TEXTURE_WIDTH, BACK_TEXTURE_HEIGHT, BACK_VELOCITY_X, 0));
/* This one is also for back layer but it is positioned at the right edge of the layer above*/
parallaxList.add(new Parallax(BACK_TEXTURE_WIDTH, BACK_TEXTURE_HEIGHT, BACK_TEXTURE_WIDTH, BACK_TEXTURE_HEIGHT, SOME_VELOCITY_X, 0));
// Front
parallaxList.add(new Parallax(0, 0, FRONT_TEXTURE_WIDTH, FRONT_TEXTURE_HEIGHT, FRONT_VELOCITY_X, 0));
parallaxList.add(new Parallax(FRONT_TEXTURE_WIDTH, 0, FRONT_TEXTURE_WIDTH, FRONT_TEXTURE_HEIGHT, FRONT_VELOCITY_X, 0));
We update the layers on an update call in each frame-
// In our example, TOTAL_LAYERS is 4
for (int i = 0; i < TOTAL_LAYERS; i++) {
int tmpInt;
Parallax parallax = parallaxList.get(i);
parallax.update(deltaTime);
// If one layer is off the edge, put it at the right of the next one
// In this example, layers are moving from right to left
if (parallax.position.x <= -parallax.width) {
// We know that parallaxList's indexes 0 and 1 hold the back layers
// and indexes 2 and 3 have the front layers. You can add additional
// parameters in Parallax class to indicate a group so that you do not
// have to determine the group in dirty way like this
if(i == 0){
tmpInt = 1;
} else if(i == 1) {
tmpInt = 0;
} else if(i == 2) {
tmpInt = 3;
} else {
tmpInt = 2;
}
parallax.setPosition(parallaxList.get(tmpInt).position.x + parallax.width, parallax.position.y);
}
}
You can use an OrthographicCamera and a SpriteBatch to draw the parallax layers. You can actually use the game camera you have but I think using a separate camera is much cleaner. Anyways, parallax textures are usually big enough to be batched in a separate call so using the game camera most probably will not save you a draw call.

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.

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