Java 3d Game Mouse Camera/Player control - java

In my Game i want to implement Mouse Controls that control Camera and Player movment.
What i want to have is that when the Mouse moves to the bottom left corner of the window for example, the players Rotation goes towards this Vector.
Also i want the Camera to be shifted in the opposite 2d Vector so it appears that the player, when the cursor is in the bottom left moves into the top right corner.
Like This
And the more the cursor is moved away from the screens middle, the more the player should rotate.
The Camera should also be rotated around the player in order to make it appear that the playermodel is rotated towards the vector it rotates itself in.
This is the function that calculates the camera position:
private void calculateCameraPosition(float horizDistance, float verticDistance)
{
float theta = player.getRotY() + angleAroundPlayer;
float offsetX = (float)((horizDistance ) * Math.sin(Math.toRadians(theta)) );
float offsetZ = (float)(horizDistance * Math.cos(Math.toRadians(theta)));
position.y = player.getPosition().y + verticDistance + 8;
position.x = (player.getPosition().x - offsetX);
position.z = player.getPosition().z - offsetZ ;
this.yaw = 180 - (player.getRotY() + angleAroundPlayer);
}
And i get my Inputs from GFLW, a little bit modified as the Mouse positions, which i subtract from the width/2 and divide into smaller numbers.
angleChange_x = (int) (-(inputHandler.Mouse_x - (Width/2)) /20);
angleChange_y = (int) (-(inputHandler.Mouse_y - (Height/2)) /20 );

Related

Java LibGDX relative value addition based on angle and position

I create a 2d game similar to a classic diepio. I created a system for positioning the player's barrel in a specific direction. The updated angle is sent to the server. When the player clicks, the server creates a missile. This only works correctly when the barrel is attached to the center of the player's body. When I want to move the barrel away from the center of the body, there is a problem. I don't know how to update the server-side position where the projectile spawns.
In the image below, the barrel rotates around the center of the player's body. I marked the missile's flight path with the red line.
On this image, the barrels also have a rotation axis in the player's center, but have been shifted to the side. The green line marked the route the missile should take. Unfortunately, I don't know how to do it correctly.
How to update the projectile's spawn point by a given distance (e.g. 10) from the basic distance (if the barrel was not moved) based on the player's angle of rotation and his position?
Projectile spawn method:
float angle = (float) ((player.getRotation() + 90) * Math.PI / 180f);
float forceX = (float) Math.cos(angle);
float forceY = (float) Math.sin(angle);
spawnProjectile(player.getPosition().x + (forceX * 3f), player.getPosition().y + (forceY * 3f));
If I understood your question correctly, you want to find the two points marked in orange in the following image:
Since you know the direction in which the missiles should fly (the red line), the distance from the center position (e.g. 10) and you know that there is a 90° angle between the movement vector of the missile (red line) and the connection line between the two starting positions of the missiles (marked as black line in the image) you can calculate the resulting positions like this:
float angle = (float) ((player.getRotation() + 90) * Math.PI / 180f);
float forceX = (float) Math.cos(angle);
float forceY = (float) Math.sin(angle);
// the center point (start of the red line in the image)
float centerX = player.getPosition().x + (forceX * 3f);
float centerY = player.getPosition().y + (forceY * 3f);
float offsetFromCenterDistanceFactor = 1f; // increase this value to increase the distance between the center of the player and the starting position of the missile
// the vector towards one of the starting positions
float offsetX1 = forceY * offsetFromCenterDistanceFactor;
float offsetY1 = -forceX * offsetFromCenterDistanceFactor;
// the vector towards the other starting position
float offsetX2 = -offsetX1;
float offsetY2 = -offsetY1;
//spawn the upper missile
spawnProjectile(centerX + offsetX1, centerY + offsetY1);
//spawn the lower missile
spawnProjectile(centerX + offsetX2, centerY + offsetY2);
For more detail on the calculation of the orthogonal vectors see this answer.

Java OpenGL - Mouse position from window to world space

I try to transform the window mouse coordinates (0/0 is the upper left corner) into world space coordinates. I just tried to solve it by this description. Here is my code:
public void showMousePosition(float mx, float my){
Matrix4f projectionMatrix = camera.getProjectionMatrix();
Matrix4f viewMatrix = camera.getViewMatrix();
Matrix4f projMulView = projectionMatrix.mul(viewMatrix);
projMulView.invert();
float px = ((2*mx)/650)-1;
float py = ((2*my)/650)-1;
Vector4f vec4 = new Vector4f(px, py*(-1), 0.0f, 1.0f);
vec4.mul(projMulView);
vec4.w = 1.0f / vec4.w;
vec4.x *= vec4.w;
vec4.y *= vec4.w;
vec4.z *= vec4.w;
System.out.println(vec4.x + ", " + vec4.y);
}
But thats not 100% correct. I have an Object on 0/-11 on world space and when I move my mouse to this point, my function say 0/9,8. And when I go to the left side of my window the x value is 5,6 but it should be something like 28.
Someone know what is wrong on my code?
First of all, your code says that your windows size is always width=650, height=650.
Then you are getting the position when z=0. But this z is in screen space and therefore it changes as you change the camera position and orientation. Normally, you get this information from the depth buffer, using glReadPixel. You should do it in this case.
However, there is another way to do this also. In the code I will share, I am looking for the intersection between a ray (generated from the mouse position) and the plane (0,0,0) with normal (0,1,0), I hope this helps.
/*Given the inverse PV (projection*view) matrix, the position of the mouse on screen and the size of the screen, transforms the screen coordinates to world coordinates*/
glm::vec3 Picking::OnWorld(glm::mat4 const& m_inv, glm::vec2 const & spos,size_t width, size_t height) {
float x = spos.x;
float y = spos.y;
y = height - y;
//InputOrigin, start of the ray for intersection with plane
glm::vec4 inputO = glm::vec4(x / width*2.0f - 1.0f, y / height*2.0f - 1.0f, -1.0f, 1.0f); //transforms screen position to the unit cube range
glm::vec4 resO = m_inv*inputO; //transforms to world space
if (resO.w == 0.0f)
return glm::vec3(-1); //return an invalid value to show a problem during a calculation, normally this means that the m_inv matrix was incorrect
resO /= resO.w; //homogeneous division
glm::vec4 inputE = inputO; //inputEnd, the end of the ray
inputE.z = 1.0;
//End of ray to world space
glm::vec4 resE = m_inv*inputE;
//checks that the coordinates are correct
if (resE.w == 0.0f)
return glm::vec3(-1); //return an invalid value to show a problem during a calculation, normally this means that the m_inv matrix was incorrect
resE /= resE.w;
//ray for intersection
glm::vec3 ray = glm::vec3(resE - resO); //vector between z=-1 and z=1
glm::vec3 normalRay = glm::normalize(ray);
glm::vec3 normalPlane = glm::vec3(0, 1, 0); //detects collision with plane 0, normal 1
float denominator = glm::dot(normalRay, normalPlane);
if (denominator == 0)
return glm::vec3(-1); //return an invalid value to show a problem during a calculation, normally this means that the m_inv matrix was incorrect
float numerator = glm::dot(glm::vec3(resO), normalPlane);
//intersection between ray and plane
glm::vec3 result = glm::vec3(resO) - normalRay*(numerator / denominator);
return result;
}
The math for the intersection can be read from this link:
https://www.cs.princeton.edu/courses/archive/fall00/cs426/lectures/raycast/sld017.htm

simple Circle on circle Collision libgdx

made two circles one of radius 8(image 16x16)
and one of radius 20( image 40x40)
i am calling the circle over overlap method and the collsion is just off. It is colliding with a circle that is around the 0,0 point of where ever my image of the ball is. the bullet can go within the ball on the bottom and right sides.
public class MyGame extends ApplicationAdapter {
SpriteBatch batch;
Texture ballImage, bulletImage;
OrthographicCamera cam;
Circle ball;
Array <Circle> bullets;
long lastShot;
#Override
public void create ()
{
System.out.println("game created");
ballImage = new Texture(Gdx.files.internal("ball.png"));
bulletImage = new Texture(Gdx.files.internal("bullet.png"));
cam = new OrthographicCamera();
cam.setToOrtho(true,320,480);//true starts top right false starts top left
batch = new SpriteBatch();
ball = new Circle();
ball.radius=20;
ball.x=320/2-ball.radius; // half screen size - half image
ball.y=480/2-ball.radius;
bullets = new Array<Circle>();
spawnBullet();
/*
batch.draw(bulletImage,bullet.x,bullet.y);
bullet.x++;
bullet.y++; */
}
public void spawnBullet()
{
Circle bullet = new Circle();
bullet.radius=8;
bullet.x=0;
bullet.y=0;
bullets.add(bullet);
lastShot = TimeUtils.nanoTime();
}
#Override
public void render ()
{
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
cam.update();
batch.setProjectionMatrix(cam.combined);
batch.begin();
batch.draw(ballImage,ball.x,ball.y);
for(Circle bullet: bullets)
{
batch.draw(bulletImage, bullet.x, bullet.y);
}
batch.end();
if(Gdx.input.isTouched())
{
Vector3 pos = new Vector3();
pos.set(Gdx.input.getX(), Gdx.input.getY(),0);
cam.unproject(pos);
ball.y = pos.y - ball.radius;
ball.x = pos.x - ball.radius ;
}
//if(TimeUtils.nanoTime()-lastShot >1000000000) one second
//spawnBullet();
Iterator<Circle> i = bullets.iterator();
while(i.hasNext())
{
Circle bullet = i.next();
bullet.x++;
bullet.y++;
if(bullet.overlaps(ball))
{
System.out.println("overlap");
i.remove();
}
}
}
}
If your bullet and the ball are 2 circles, like you said you don't need an overlap method.
It is simple: 2 circles collide, if their distance is smaller then the sum of their radiuses.
To calculate the distance you need to make a squareroot. This is a pretty expensive calculation, so it would be better to use squared distance and squared sum of radiuses:
float xD = ball.x - bullet.x; // delta x
float yD = ball.y - bullet.y; // delta y
float sqDist = xD * xD + yD * yD; // square distance
boolean collision = sqDist <= (ball.radius+bullet.radius) * (ball.radius+bullet.radius);
Thats it.
Also in your cam.setToOrtho you wrote a cooment:
//true starts top right false starts top left
Thats wrong, it is top left or bottom left. By default it is bottom left, because this is the way a coordinate system works normaly. The top left is, because the monitor addresses pixels starting from top left = pixel 1.
EDIT: this should be the problem: The coordinates you give the batch.draw method are the left lower corner of the Texture by default, if you are using the "y = Down"-System it should be the top left corner (you have to try i am not sure).
The Circles position instead is its center.
To solve the problem you need to adjust the position like this (for "y = Up"-System):
batch.draw(bulletImage, bullet.x - bullet.radius, bullet.y - bullet.radius);
It is possible, that the same formula works also for the "y = Down"-System but i am not sure

Java 2D "tank" game: Shooting 'missile' from rotated sprite doesn't work

EDIT: Problem solved. I forgot putting Math.toRadians() anywhere I do Math.cos or Math.sin. :)
I'm working on a simple Java 2D game involving two tanks that shoot missiles and move around the screen.
For this question, let's assume there's only one tank.
Controls:
Adjust angle of movement counter-clockwise: LEFT arrow key
Adjust angle of movement clockwise: RIGHT arrow key
Move forward: UP arrow key
Shoot missile: ENTER
Pressing LEFT or RIGHT also rotates the sprite.
I tried to code the game so that missiles will shoot from the "tank barrerl" (point marked in green). However, the code I wrote doesn't achieve this.
Instead, when pressing ENTER the missile appears in a point on the sprite or around it, that seems to be random.
(Orange point is the tank's origin: tank.getX(),tank.getY() ).
It wouldn't be a problem to code this, if it weren't for the fact the the tank rotates. The location of the green point changes everytime the user rotates the tank.
What is wrong with my code? This code should make the missile 'shoot' from wherever the "barrel" currently is. As I said, doesn't work.
This is what runs when the user presses ENTER.
enterAction = new AbstractAction(){
public void actionPerformed(ActionEvent e){
double missileXposition, missileYposition;
double tankMiddleX,tankMiddleY;
double angle, radius;
tankMiddleX = tank1.getX() + (tank1.getWidth()/2);
tankMiddleY = tank1.getY() + (tank1.getHeight()/2);
angle = tank1.getAngle();
radius = tank1.getWidth()/2;
missileXposition = tankMiddleX + ( Math.cos(angle) * radius );
missileYposition = tankMiddleY + ( Math.sin(angle) * radius );
missiles1.add( new Missile( missileXposition, missileYposition , "red" , tank1.getAngle() , tank1) );
}
};
The Missile class:
public class Missile extends Entity {
public Missile(double x, double y, String type, double angle, Tank tank){
this.x = x;
this.y = y;
this.dx = dx;
this.dy = dy;
this.type = type;
this.angle = angle;
if(type.equals("red")) image = new ImageIcon(this.getClass().getResource("sprites/redrocket1.png")).getImage();
if(type.equals("blue")) image = new ImageIcon(this.getClass().getResource("sprites/bluerocket1.png")).getImage();
width = image.getWidth(null);
height = image.getHeight(null);
if(type.equals("blue")) dx = (-1) * ( 6 * Math.cos(Math.toRadians(tank.getAngle())) );
if(type.equals("red")) dx = 6 * Math.cos(Math.toRadians(tank.getAngle()));
if(type.equals("blue")) dy = (-1) * ( 6 * Math.sin(Math.toRadians(tank.getAngle()) ) );
if(type.equals("red")) dy = 6 * Math.sin(Math.toRadians(tank.getAngle()));
}
}
Thanks a lot
Determine the distance between the sprite's origin (orange) and the missile start point (green) (d)and the angle between the upper rim of the sprite and the line orange-green a.
Now you set the start point of the missiles to
tank1.getX()+Math.cos(d*Math.toRadians(a+tank1.getAngle()));
tank1.getY()+Math.sin(d*Math.toRadians(a+tank1.getAngle()));
I don't know whether it's 100% correct, but I think it's the right direction.

Trigonometry of a 3D "free" camera

I'm sorry if this question was asked before, I did search, and I did not find an answer.
My problem is, that I'd like to make movement on all 3 axes with the X and Y rotation of the camera being relevant.
This is what I did:
private static void fly(int addX, int addY){ //parameters are the direction change relative to the current rotation
float angleX = rotation.x + addX; //angle is basically the direction, into which we will be moving(when moving forward this is always the same as our actual rotation, therefore addX and addY would be 0, 0)
float angleY = rotation.y + addY;
float speed = (moveSpeed * 0.0002f) * delta;
float hypotenuse = speed; //the length that is SUPPOSED TO BE moved overall on all 3 axes
/* Y-Z side*/
//Hypotenuse, Adjacent and Opposite side lengths of a triangle on the Y-Z side
//The point where the Hypotenuse and the Adjacent meet is where the player currently is.
//OppYZ is the opposite of this triangle, which is the ammount that should be moved on the Y axis.
//the Adjacent is not used, don't get confused by it. I just put it there, so it looks nicer.
float HypYZ = speed;
float AdjYZ = (float) (HypYZ * Math.cos(Math.toRadians(angleX))); //adjacent is on the Z axis
float OppYZ = (float) (HypYZ * Math.sin(Math.toRadians(angleX))); //opposite is on the Y axis
/* X-Z side*/
//Side lengths of a triangle on the Y-Z side
//The point where the Hypotenuse and the Adjacent meet is where the player currently is.
float HypXZ = speed;
float AdjXZ = (float) (HypXZ * Math.cos(Math.toRadians(angleY))); //on X
float OppXZ = (float) (HypXZ * Math.sin(Math.toRadians(angleY))); //on Z
position.x += AdjXZ;
position.y += OppYZ;
position.z += OppXZ;
}
I only implement this method when moving forwards(parameters: 0, 90) or backwards(params: 180, 270), since movement can't happen on the Y axis while going sideways, since you don't rotate on the Z axis. ( the method for going sideways(strafing) works just fine, so I won't add that.)
the problem is that when I look 90 degrees up or -90 down and then move forward I should be moving only on the Y axis(vertically) but for some reason I also move forwards(which means on the Z axis, as the X axis is the strafing).
I do realize that movement speed this way is not constant. If you have a solution for that, I'd gladly accept it as well.
I think your error lies in the fact that you don't fully project your distance (your quantity of movement hypothenuse) on your horizontal plane and vertical one.
In other words, whatever the chosen direction, what you are doing right now is moving your point of hypothenuse in the horizontal plane X-Z, even though you already move it of a portion of hypothenuse in the vertical direction Y.
What you probably want to do is moving your point of a hypothenuse quantity as a total.
So you have to evaluate how much of the movement takes place in the horizontal plane and how much in the vertical axis. Your direction gives you the answer.
Now, it is not clear to me right now what your 2 angles represent. I highly recommend you to use Tait–Bryan angles in this situation (using only yawn and pitch, since you don't seem to need the rolling - what you call the Z-rotation), to simplify the calculations.
In this configuration, the yawn angle would be apparently similar to your definition of your angleY, while the pitch angle would be the angle between the horizontal plane and your hypothenuse vector (and not the angle of the projection in the plane Y-Z).
A schema to clarify:
With :
s your quantity of movement from your initial position P_0 to P_1 (hypothenuse)
a_y the yawn angle and a_p the pitch one
D_x, D_y, D_z the displacements for each axis (to be added to position, ie AdjXZ, OppYZ and OppXZ)
So if you look at this representation, you can see that your triangle in X-Z doesn't have s as hypotenuse but its projection s_xz. The evaluation of this distance is quite straightforward: if you place yourself in the triangle P_0 P_1 P_1xz, you can see that s_xz = s * cos(a_p). Which gives you:
float HypXZ = speed * Math.cos(Math.toRadians(angleP))); // s_xz
float AdjXZ = (float) (HypXZ * Math.cos(Math.toRadians(angleY))); // D_x
float OppXZ = (float) (HypXZ * Math.sin(Math.toRadians(angleY))); // D_z
As for D_y ie OppYZ, place yourself in the triangle P_0 P_1 P_1xz again, and you'll obtain:
float OppYZ = (float) (speed * Math.sin(Math.toRadians(angleP))); // D_y
Now, if by angleX you actually meant the angle of elevation as I suppose you did, then angleP = angleX and HypXZ = AdjYZ in your code.
With this correction, if angleX = 90 or angleX = -90, then
HypXZ = speed * cos(angleX) = speed * cos(90deg) = speed * 0;
... and thus AdjXZ = 0 and OppXZ = 0. No movement in the horizontal plane.
Note:
To check if your calculations are correct, you can verify if you actually move your point of the wanted quantity of movement (hypothenuse ie speed ie s). Using Pythagorean theorem:
s² = s_xz² + D_z² // Applied in the triangle P_0 P_1 P_1xz
= D_x² + D_y² + D_z² // Applied in the triangle P_0 P_1x P_1xz
With the definitions of the displacements given above:
D_x² + D_y² + D_z²
= (s * cos(a_p) * cos(a_y))² + (s * cos(a_p) * sin(a_y))² + (s * sin(a_p))²
= s² * (cos(a_p)² * cos(a_y)² + cos(a_p)² * sin(a_y)² + sin(a_p)²)
= s² * (cos(a_p)² * (cos(a_y)² + sin(a_y)²) + sin(a_p)²)
= s² * (cos(a_p)² * 1 + sin(a_p)²)
= s² * (cos(a_p)² + sin(a_p)²)
= s² * 1 // Correct
Hope it helped... Bye!

Categories

Resources