I use libGDX for a 3D game. In this game an 3D arrow should indicate a certain direction in 3D space. As the player is moving, the direction of the 3D arrow is updated for every frame.
I want to place the arrow in the direction, the camera is looking 3 units before the camera, so that the arrow is always visible. Therefore I added the camera direction to the current camera position and used this as translation part for the arrow. The direction is calculated by substracting the arrow position from the target.
All this works fine, the arrow is displayed at the right position and it moves. The problem is, it sometimes points to the target, and sometimes not.
Here my code:
Vector3 targetPosition = new Vector3(1, 1, 10);
Vector3 cameraPosition = gameController.getCamera().position.cpy();
Vector3 cameraDirection = gameController.getCamera().direction.cpy();
Vector3 up = gameController.getCamera().up.cpy();
Vector3 arrowPosition = cameraDirection.scl(3).add(cameraPosition);
Vector3 arrowDirection = targetPosition.cpy();
arrowDirection = arrowDirection.sub(arrowPosition).nor();
Matrix4 transformationMatrix = new Matrix4();
// calculate orthogonal up vector
up.crs(arrowDirection).crs(arrowDirection);
arrowModel.transform = transformationMatrix.setToLookAt(arrowDirection,
up).trn(arrowPosition);
To avoid that the vectors are calculated wrong, some debug lines were added:
modelBuilder.begin();
MeshPartBuilder partBuilder = modelBuilder.part("lines", GL20.GL_LINES,
Usage.Position,
new Material(ColorAttribute.createDiffuse(Color.GREEN)));
Vector3 linePos = arrowPosition.cpy();
partBuilder.line(linePos.cpy().add(arrowDirection), linePos);
partBuilder.line(linePos.cpy().add(up), linePos);
debugModel = new ModelInstance(modelBuilder.end());
As a result, I see a correct green line for the up vector (current up vector of the camera), and a green line pointing correctly to my target, the green cube. The arrow does something other:
http://www.directupload.net/file/d/3638/2ka7kvvj_jpg.htm
In the picture the arrow is quite near the right direction (green line), there are other situations, where it points in the completely wrong direction.
I used quite a lot combinations of .translate, .trn, .setToLookAt, and .rotate but all of them showed similar, wrong results.
Do you have an idea what went wrong here?
Related
i try to rotate a 3D instance when I move the mouse. i use Orthographic Camera, and i have a tiled isomap in background.
[EDIT] I use this code :
public boolean mouseMoved (int x, int y) {
Vector3 direction = new Vector3();
Vector3 worldCoordinate = cam.unproject(new Vector3(x, y, 0));
direction = (worldCoordinate).sub(position).nor();
direction.set(-direction.x, -direction.y, -direction.z);
Matrix4 instanceRotation = instance.transform.cpy().mul(instance.transform);
instanceRotation.setToLookAt(direction, new Vector3(0,-1,0));
instanceRotation.rotate(0, 0, 1, 180);
quat = new Quaternion();
instanceRotation.getRotation(quat);
instance.transform.set(position, quat);
return false;
}
Now, the instance follows the cursor, but in all axes. I just want to turn on the Y axis. I can change quat.x = 0 and quat.z = 0 to lock the rotation. After that, the rotation only works on the Y axis. But, the model should turn over if my cursor is at the top of the screen, but it remains facing, no matter where my cursor is.
I don't know how to convert the coordinates to tell my rotation that it needs to turn over. And to modify the quat in this way is not very elegant
Edit : Some image for illustrate what i want
Here, with cursor in the bottom of the screen, the model turn to look in good direction :
Here, the model dont turn over, like the cursor is in bottom of the screen :
I'm rotating my object(ship) to face a target with the code below.
playerBody.setTransform(playerBody.getPosition(), MathUtils.lerpAngle(playerBody.getAngle(), getDesiredAngle(),lerpProgress));
I want to to move the ship in the direction the ship is looking. I thought that retrieving the Up vector of the ship would suffice. I tried the below code but that didn't work.
Vector2 direction = playerBody.getWorldPoint(new Vector2(0,1));
playerBody.setLinearVelocity(direction.nor());
I believe that getWorldPoint() does not give you what you are looking for. It returns the vector (direction + distance) of a local point, in regard to the box2D world's origin. In this picture, this would be the blue vector: getWorldPoint(). What you want is the red vector.
The most basic way of doing would be something like this :
float angle = playerBody.getAngle();
Vector2 direction = new Vector2(MathUtils.cos(angle),MathUtils.sin(angle))
I must add that in my case, I had to add 90°, since I needed an angle of 0° to correspond to a vertical body : angle = playerBody.getAngle() + MathUtils.PI/2f
I'm having some difficulty implementing very basic collision within libGDX. The update code for the "player" is as so:
private void updatePosition(float _dt)
{
Vector2 _oldPos = _pos;
_pos.add(_mov.scl(_dt*50));
_bounds.setPosition(_pos.x-8, _pos.y-12);
if(_game.getMap().checkCollision(_bounds))
{
System.out.println("Collision Detected");
_pos = _oldPos;
_bounds.setPosition(_pos.x-8, _pos.y-12);
}
}
So, initially _oldPos is stored as the values of _pos position vector before applying any movement.
Movement is then performed by adding the movement vector _mov (multiplied by the delta-time * 50) to the position vector, the player's bounding rectange _bounds is then updated to this new position.
After this, the player's new bounding Rectangle is checked for intersections against every tile in the game's "map", if an intersection is detected, the player cannot move in that direction, so their position is set to the previous position _oldPos and the bounding rectangle's position is also set to the previous position.
Unfortunately, this doesn't work, the player is able to travel straight through tiles, as seen in this image:
So what is happening here? Am I correct in thinking this code should resolve the detected collision?
What is strange, is that replacing
_pos = _oldPos;
with (making the same move just made in reverse)
_pos.sub(_mov.scl(_dt*50));
Yields very different results, where the player still can travel through solid blocks, but encounters resistance.
This is very confusing, as the following statement should be true:
_oldPos == _pos.sub(_mov.scl(_dt*50));
A better solution for collision detection would be to have a Vector2 velocity, and every frame add velocity to position. You can have a method that tests if Up arrow key is pressed, add 1 (or whatever speed you would like) to velocity. And if down is pressed, subtract 1 (or whatever speed). Then you can have 4 collision rectangles on player, 1 on top of player, bottom, left, and right. You can say
if(top collides with bounds){
if(velocity.y > 0){
set velocity.y = 0.
}
}
And do the same for down, left and right (eg... for bottom, make sure to test if(velocity.y < 0) instead of if(velocity.y > 0).
EDIT: You code is not working because you set oldPos = pos without instantiating a new Vector2. Which means when you add onto pos, it also changes oldPos. So say oldPos = new Vector2(pos);
try to test future position before move. If collision, don't move.
I am trying to create a 3d world that behaves similarly to minecraft where a player can look in 360 degrees and if he trys to click on a spot (X,Y,Z coordinate in the 3D world), the model being drawn there gets deleted. I am very new to programming a 3D world in LibGdx so any help is useful. I do camera rotation with this:
float deltaX = -Gdx.input.getDeltaX() * player.degreesPerPixel;
float deltaY = -Gdx.input.getDeltaY() * player.degreesPerPixel;
if(deltaX>0)
player.camera.rotate(Vector3.Z, (float)1.5);
else if(deltaX<0)
player.camera.rotate(Vector3.Z, (float)-1.5);
player.tmp.set(player.camera.direction).crs(player.camera.up).nor();
player.camera.direction.rotate(player.tmp, deltaY);
player.setDir(player.camera.direction.x, player.camera.direction.y);
Thank you
In a 2D environment, you would usually just use Camera.unproject(...), which takes some screenspace point and transforms that back into gameworld coordinates.
In 3D it's not that easy, because of the additional dimension adding some depths to your world. That's why a click on the 2D plane (your screen) could hit basically infinitely many points in the 3D world. In libgdx, this ray of possibly clicked points is called the pick-ray.
If you want the intersection point on a certain plane, the code could look like his:
public void hitSomething(Vector2 screenCoords) {
// If you are only using a camera
Ray pickRay = camera.getPickRay(screenCoords.x, screenCoords.y);
// If your camera is managed by a viewport
Ray pickRay = viewport.getPickRay(screenCoords.x, screenCoords.y);
// we want to check a collision only on a certain plane, in this case the X/Z plane
Plane plane = new Plane(new Vector3(0, 1, 0), Vector3.Zero);
Vector3 intersection = new Vector3();
if (Intersector.intersectRayPlane(pickRay, plane, intersection)) {
// The ray has hit the plane, intersection is the point it hit
} else {
// Not hit
}
}
In your case, when you have a minecraft-like world, your code could look like the following:
public void hitSomething(Vector2 screenCoords) {
Ray pickRay = ...;
// A bounding box for each of your minecraft blocks
BoundingBox boundingBox = new BoundingBox();
Vector3 intersection = tmp;
if (Intersector.intersectRayBounds(pickRay, boundingBox, intersection)) {
// The ray has hit the box, intersection is the point it hit
} else {
// Not hit
}
}
Please not that in a minecraft world there are literally thousands of such blocks. Going this simple approach would not be very fast. You will probably have to end up with a hierarchical solution, that first checks big chunks (bounding boxes that include many blocks) for a possible hit, and then start checking individual blocks.
i am writing a game using Libgdx and i need to detect when the user touches a sprite.
I tried to do so with this following code:
this code set the rect's position
for(int i = 0;i<circlesArray.length;i++)
{
rect[i] = new Rectangle(circles.getPosition(i).x,circles.getPosition(i).y,height/8,height/8);
}
and this code set the click as rectangle and checks if it overlaps the sprite
if(Gdx.input.isTouched())
{
click = new Rectangle(Gdx.input.getX(),Gdx.input.getY(),Gdx.input.getX(),Gdx.input.getY());
if(Intersector.overlaps(click,rect[1]));
{
System.out.println("clicked");
x[1] = 0;
}
}
From some reason that i cant understand the program detects a collision even when it is not happened, when i tap anywhere on the screen it says that i pressed the sprite.
What should i do to fix it?
This is your issue:
click = new Rectangle(Gdx.input.getX(), Gdx.input.getY(), Gdx.input.getX(), Gdx.input.getY());
Should be:
click = new Rectangle(Gdx.input.getX(), Gdx.input.getY(), 1, 1);
The last two parameters of a Rectangle are width and height. Here we give the rectangle a width and a height of 1 pixel but you can set it to whatever you like. You were setting those parameters as the inputs x and y which are different every time giving you varying results.
Edit:
To translate the input coordinates to your game world's coordinates based on the camera you have to do this:
Vector3 clickPos = new Vector3(Gdx.input.getX(), Gdx.input.getY(), 0);
camera.unproject(clickPos);
click = new Rectangle(clickPos.x, clickPos.y, 1, 1);
When you use camera.unproject(Vector) it translates your inputs coordinates to work based off of where your camera is positioned in the game world. So now we're using a vector clickPos.
The reason we use a Vector3 is because this is what is required by the unproject function. We supply this vector with the inputs x and y but give it a 0 for the third parameter z since this is a 2d world.