I've overridden the LibGdx Pan gesture method for a different functionality (a selector). However, I want the pan functionality to use two fingers(pointers) instead. Is this possible?
It's the same question as this post, however his is specific to iPhone and not LibGdx:
How to implement the traditional imageView-inside-a-scrollView pan gesture with two fingers instead of one?
the pan() method will only fire with one finger, not two. I was thinking of keeping track of number of fingers used, by setting a variable in touchDown() using the int pointer variable, however the pan() method will not fire when there are 2 fingers in use.
Any suggestions? Cheers
After 3+ years of research I finally made it.
#Override
public boolean pinch(Vector2 initialPointer1, Vector2 initialPointer2, Vector2 pointer1, Vector2 pointer2) {
// Calculate distances
float initialDistance = initialPointer1.dst(initialPointer2);
float distance = pointer1.dst(pointer2);
// Calculate pinch coordinates
float initialPinchX = (initialPointer1.x + initialPointer2.x) / 2;
float initialPinchY = (initialPointer1.y + initialPointer2.y) / 2;
float pinchX = (pointer1.x + pointer2.x) / 2;
float pinchY = (pointer1.y + pointer2.y) / 2;
// This to avoid first time zooming or panning horrible behavior
if (lastZoomDistance == 0) {
lastZoomDistance = initialDistance;
}
if (lastPinchX == lastPinchY && lastPinchX == 0) {
lastPinchX = initialPinchX;
lastPinchY = initialPinchY;
}
// Zoom
float distanceDifference = distance - lastZoomDistance;
camera.zoom -= distanceDifference / 300;
// Pan
float deltaX = (pinchX - lastPinchX) * camera.zoom;
float deltaY = (pinchY - lastPinchY) * camera.zoom;
camera.translate(-deltaX, deltaY);
// We need to update these for future calculations
lastZoomDistance = distance;
lastPinchX = (pointer1.x + pointer2.x) / 2;
lastPinchY = (pointer1.y + pointer2.y) / 2;
return false;
}
I managed to jig the pinch() method around to calculate a combined two finger panning and zooming gesture, instead of using zoom().
Here's some code which achieves this using the pinch method, by looking at how far the first finger has been dragged from its initial position. The second finger is ignored.
Gdx.input.setInputProcessor(new GestureDetector(new GestureDetector.GestureAdapter() {
#Override
public boolean pinch(Vector2 initialPointer1, Vector2 initialPointer2, Vector2 pointer1, Vector2 pointer2) {
float panAmount = pointer1.x - initialPointer1.x;
// ...
return true;
}
})));
To add onto Luis' answe, sometimes you may want Pan/ Zoom to act as exclusive actions (only one can happen at a time). If that's what you're after, this may be useful
val targetVector1 = Vector2(pointer1).sub(initialPointer1)
val targetVector2 = Vector2(pointer2).sub(initialPointer2)
val isZoom = targetVector1.hasOppositeDirection(targetVector2)
val isPan = !isZoom
Related
I am making a little ant colony simulation in Processing (4).
I have an Ant class, with a sense() , a move()and a render() function.
I also have a Food class with only a position PVector.
My sense class loops through all Foods in a given radius, and it is meant to only 'see' the ones inside a given view angle.
In my render() function I have an arc to visualise this (I do some division and addition so the arc centres in front of the rectangle):
void render() {
// Draw a rectangl rotated in the direction of velocity
float theta = velocity.heading() + radians(90);
if(detectFood) // a Boolean set in sense()
fill(0,173,67); // turns green
else {
stroke(255);
pushMatrix();
translate(position.x, position.y);
fill(200, 100);
rotate(theta); // I copied the rotation code from somewhere :)
rect(0-r/2,0-r,r,r*2); // r is a float used to control size
arc(0, 0, viewRadius * 2, viewRadius * 2, radians(270 - viewAngle/2), radians(270 + viewAngle/2)); // viewRadius is a float set in the constructor
popMatrix();
}
}
This ends up looking like this:
My sense() code uses trigonometry to calculate the angle and the distance (I am using my own calculations because wasn't sure the inbuilt ones did what I thought they did):
void sense() {
if (!detectFood) {
float closest = viewRadius;
Food selected = null;
for (Food fd : foods){
float foodDist = position.dist(fd.position);
if(foodDist <= viewRadius) {
float xs = position.x-fd.position.x;
float ys = position.y-fd.position.y;
float Angle = atan2(abs(ys), abs(xs));
float begin = radians(270 - viewAngle/2);
float end = radians(270 + viewAngle/2);
if(begin < Angle && Angle < end && foodDist < closest){
selected = fd;
closest = foodDist;
println("found food");
}
}
}
if (selected != null){
detectFood = true;
foodFocused = selected;
}
} else {
if(position.dist(foodFocused.position) < r) {
takeFood();
detectFood = false;
}
}
}
The problem is that because I rotate the shape (and the arc with it), my sensing code basically never works. Is there a way to account for rotation in trigonometry or maybe an easier way of doing this? Any help would be apreciated
I've been following along with ThinMatrix's OpenGL tutorial on making a game in Java recently. However as he uses LWJGL2, and I'm using LWJGL3, there's a few differences that require some work arounds. I'm stuck at one point in particular pertaining to creating a 3rd person character on a "player".
I've done enough so that when I click and drag the screen, the camera rotates around the player like it should. However when I let go and move my mouse to make another rotation, instead of continuing from where the position is, it resets it relative to where my second click is.
As LWJGL3 doesn't have a mouse.getDY() or mouse.getDX(), I made one in my DisplayManager class like so:
public float getDY() {
newMouseY = (float) getMouseY();
float dy = newMouseY - oldMouseY;
oldMouseY = newMouseY;
return dy;
}
public float getDX() {
newMouseX = (float) getMouseX();
float dx = newMouseX - oldMouseX;
oldMouseX = newMouseX;
return dx;
}
And I call it in my camera class like so:
private void calculatePitch(DisplayManager window) {
if (window.isMouseDown(GLFW.GLFW_MOUSE_BUTTON_LEFT)) {
float pitchChange = window.getDY() * 0.2f;
pitch -= pitchChange;
}
}
private void calculateAngleAroundPlayer(DisplayManager window) {
if (window.isMouseDown(GLFW.GLFW_MOUSE_BUTTON_LEFT)) {
float angleChange = window.getDX() * 0.3f;
angleAroundPlayer -= angleChange;
}
}
I'm just not sure if this should work and I'm missing something really obvious, or it can't be done this way. I'm pretty new to game dev.
Managed to figure out the issue, all I had to do was call my getDX() and getDY() functions again after the mouse has been pressed in my calculations:
private void calculatePitch(DisplayManager window) {
if (window.isMouseDown(GLFW.GLFW_MOUSE_BUTTON_LEFT)) {
float pitchChange = window.getDY(window) * 0.2f;
pitch += pitchChange;
}
window.getDY(window);
}
private void calculateAngleAroundPlayer(DisplayManager window) {
if (window.isMouseDown(GLFW.GLFW_MOUSE_BUTTON_LEFT)) {
float angleChange = window.getDX(window) * 0.3f;
angleAroundPlayer -= angleChange;
}
window.getDX(window);
}
Rotating Asteroids ( Polygons )
I am trying to rotate asteroids(polygons) so that they look nice. I am doing this through multiple mathematical equations. To start I give the individual asteroid a rotation velocity:
rotVel = ((Math.random()-0.5)*Math.PI/16);
Then I create the polygon shape,
this.shape = new Polygon();
Followed by generating the points,
for (j = 0; j < s; j++) {
theta = 2 * Math.PI / s * j;
r = MIN_ROCK_SIZE + (int) (Math.random() * (MAX_ROCK_SIZE - MIN_ROCK_SIZE));
x = (int) -Math.round(r * Math.sin(theta)) + asteroidData[0];
y = (int) Math.round(r * Math.cos(theta)) + asteroidData[1];
shape.addPoint(x, y);
}
Finally, in a loop a method is being called in which it attempts to move the polygon and its points down as well as rotating them. (I'm just pasting the rotating part as the other one is working)
for (int i = 0; i < shape.npoints; i++) {
// Subtract asteroid's x and y position
double x = shape.xpoints[i] - asteroidData[0];
double y = shape.ypoints[i] - asteroidData[1];
double temp_x = ((x * Math.cos(rotVel)) - (y * Math.sin(rotVel)));
double temp_y = ((x * Math.sin(rotVel)) + (y * Math.cos(rotVel)));
shape.xpoints[i] = (int) Math.round(temp_x + asteroidData[0]);
shape.ypoints[i] = (int) Math.round(temp_y + asteroidData[1]);
}
now, the problem is that when it prints to the screen the asteroids appear to 'warp' or rather the x and y positions on some of the polygon points 'float' off course.
I've noticed that when I make 'rotVel' be a whole number the problem is solved however the asteroid will rotate at mach speeds. So I've concluded that the problem has to be in the rounding but no matter what I do I can't seem to find a way to get it to work as the Polygon object requires an array of ints.
Does anyone know how to fix this?
Currently your asteroids rotate around (0 , 0) as far as i can see. Correct would be to rotate them around the center of the shape, which would be (n , m), where n is the average of all x-coordinates of the shape, and m is the average of all y-coordinates of the shape.
Your problem is definitely caused by rounding to int! The first improvement is to make all shape coordinates to be of type double. This will solve most of your unwanted 'effects'.
But even with double you might experience nasty rounding errors in case you do a lot of very small updates of the coordinates. The solution is simple: Just avoid iterative updates of the asteroid points. Every time, you update the coordinates based on the previous coordinates, the rounding error will get worse.
Instead, add a field for the rotation angle to the shape and increment it instead of the points themselves. Not until drawing the shape, you compute the final positions by applying the rotation to the points. Note that this will never change the points themselves.
You can extend this concept to other transformations (e.g. translation) too. What you get is some kind of local coordinate system for every shape/object. The points of the shape are defined in the local coordinate system. By moving and rotating this system, you can reposition the entire object anywhere in space.
public class Shape {
// rotation and position of the local coordinate system
private double rot, x, y;
// points of the shape in local coordinate system
private double[] xp, yp;
private int npoints;
// points of the shape in world coordinates
private int[][] wxp, wyp;
private boolean valid;
public void setRotation(double r) { this.rot = r; valid = false; }
public void setPosition(double x, double y) { this.x = x; this.y = y; valid = false; }
public void addPoint(double x, double y) {
// TODO: add point to xp, yp
valid = false;
}
public void draw(...) {
if (!valid) {
computeWorldCoordinates(wxp, wyp);
valid = true;
}
// TODO: draw shape at world coordaintes wxp and wyp
}
protected void computeWorldCoordinates(int[] xcoord, int[] ycoord) {
for (int i = 0; i < npoints; i++) {
double temp_x = xp[i] * Math.cos(rot) - yp[i] * Math.sin(rot);
double temp_y = xp[i] * Math.sin(rot) + yp[i] * Math.cos(rot);
xcoord[i] = (int) Math.round(x + temp_x);
ycoord[i] = (int) Math.round(y + temp_y);
}
}
}
I want an object to move away from the position of a touch event.
So far I have the following:
#Override
public boolean touchDown(int screenX, int screenY, int pointer, int button) {
Vector3 touchPosition = new Vector3();
camera.unproject(touchPosition.set(screenX, screenY, 0));
Vector2 directionBody = body.getPosition();
Vector2 directionTouch = new Vector2(touchPosition.x, touchPosition.y);
Vector2 direction = directionBody.sub(directionTouch);
direction.nor();
float speed = 3;
body.setLinearVelocity(direction.scl(speed));
return true;
}
Using this code if I press at the right side of the screen the body moves to the left. If press at the left side of the screen the body will go to the right side. Could someone help me out please?
Your code is a bit fuzzy to me, maybe because you are using classes I don't but generally it's simple:
First you unproject touch coordinates to screen coordinate system, where your body object is, like you did.
Second calculate horizontal and vertical distance between touch place and your body object. Let's say you get dx and dy.
If you want constant speed you just have to check are those dx and dy positive or negative and depending on that you set positive or negative speed, i.e.:
if (dx >0) vx = SPEED_CONSTANT;
else vx = -SPEED_CONSTANT;
Same goes for vertical speed.
If you want your body to accelerate you should use those dx and dy multiplied with some constant. That is, the bigger dx is the higher vertical speed should be. Same goes for vertical speed:
vx = dx * SPEED_CONSTANT;
vy = dy * SPEED_SONSTANT;
If you want your body to decelerate then you should devide some constant value with those dx and dy, to have opposite effect:
vx = SPEED_CONSTANT / dx;
vy = SPEED_CONSTANT / dy;
Something like that. You can set value of that SPEED_CONSTANT by trying some values - tune it up.
I hope this will help.
So I finally did it.
Code snippet:
#Override
public boolean touchDown(int screenX, int screenY, int pointer, int button) {
Vector3 touchPosition3D = new Vector3();
//Change touch coordinates to world coordinates
camera.unproject(touchPosition3D.set(screenX, screenY, 0));
//Add unit factor to the vector
touchPosition3D.x = Utility.convertToMeter(touchPosition3D.x);
touchPosition3D.y = Utility.convertToMeter(touchPosition3D.y);
Vector3 bodyPosition = new Vector3(body.getPosition().x, body.getPosition().y, 0 );
Vector3 finalVector = new Vector3(bodyPosition.x, bodyPosition.y, 0).sub(touchPosition3D);
Vector2 direction = new Vector2(finalVector.x, finalVector.y);
float speed = 3;
body.setLinearVelocity(direction.scl(speed));
return true;
}
Basically I had to unproject the touchDown coordinates and convert them to the units I'm using in my application.
Then I do a simple Vector operation, subtracting the calcualted touch vector drom the vector of my body.
Finally apply some linear velocity.
So in my android game I'm making with andengine, I have it set up so as I touch and drag the player sprite, it continously rotates so that the character is always facing the direction it is traveling.
public class Player extends AnimatedSprite {
private float lastX = Game.centerX;
private float lastY = Game.centerY;
private static int angle = 0;
// ...
#Override
public boolean onAreaTouched(final TouchEvent sceneTouchEvent, final float touchAreaLocalX, final float touchAreaLocalY) {
Body body = OrbCatch.physicsWorld.getPhysicsConnectorManager().findBodyByShape(this);
if (sceneTouchEvent.getAction() == TouchEvent.ACTION_MOVE) {
float currentX = sceneTouchEvent.getX();
float currentY = sceneTouchEvent.getY();
angle = (int) (Math.atan2(currentY - lastY, currentX - lastX) * 180 / Math.PI);
lastX = currentX;
lastY = currentY;
}
body.setTransform(new Vector2(sceneTouchEvent.getX(),sceneTouchEvent.getY() )
.mul(1/PhysicsConstants.PIXEL_TO_METER_RATIO_DEFAULT),angle);
return true;
}
}
The key line is this:
angle = (int) (Math.atan2(currentY - lastY, currentX - lastX) * 180 / Math.PI)
It takes the last known coordinates and the current coordinates, calculates the angle between them, and converts it from radians to degrees. Well, this was all working fine yesterday, but despite changing none of this code today it's behaving strangely. The sprite's orientation changes erratically, with no apparent pattern. If I move it in a straight path, it continuously alternates between 2 or 3 distinctly different angles(usually one of them is the correct one).
edit: solved, see below
The problem was that Body.setTransform angle parameter takes values in radians, not degrees. Andengine is so poorly documented...
math.todegree(math.atan(....))....
You should use toDegree().