I am trying to have a character hold a gun, but I want the gun to move with the mouse. For example, if the mouse is up, the gun points up. If the mouse is to the left, the gun points to the left. I used the player position and the mouse position to construct a right triangle, then used inverse sine to find the angle of elevation. However, this only works for 90 degrees of movement. Any ideas of how else I could approach this so that I get a full 360 degrees of rotation?
Code for calculating the angle:
private double calcAngle()
{
double mouseX,mouseY,subX,subY,playerToMouse,mouseToSub,angle;
mouseX = Mouse.getX();
mouseY = Mouse.getY();
subX = mouseX;
subY = y;
playerToMouse = Math.sqrt(Math.pow(x-mouseX,2)+Math.pow(y-mouseY,2));
mouseToSub = Math.sqrt(Math.pow(mouseX-subX,2)+Math.pow(mouseY-subY,2));
angle = Math.toDegrees(Math.asin(mouseToSub/playerToMouse));
return angle;
}
Current rotation (Pink represents player; Green represents gun; Yellow represents mouse):
You can use Math.atan2(mouseY-gunY, mouseX-gunX) which will return an angle between pi and -pi radians, or 180 and -180 degrees after you convert it to degrees. The problem with using asin is that 1/1 is equal to -1/-1 which makes it impossible for the function to tell them apart, and you want different results in each case.
Related
I searched and implemented things from this forum, it doesn't come out right.
What I'm trying to achieve is to calculate a spawnPoint for player bullets relative to his position and rotation.
The spawnPoint should be and his X + his width (the player is set to point to the right by default) and y + height/2 (to spawn from his center on the Y axis).
This is what I got from this forum:
this.bulletSpawn.x = (float)(this.position.x + this.width/2 + this.width * Math.cos(rotation));
this.bulletSpawn.y = (float)(this.position.y + this.height/2 + this.height/2 * Math.sin(rotation));
The rotation is in Radians. The this is the Player class.
Images showing what I expect to happen:
Original Position
Expected Behaviour
The red dot is the spawnPoint I'm trying to calculate knowing the player position and rotation.
The player Sprite is what rotates, and it rotates related to his center x and y, which is done with a lib, i do not have these variables. The entire arrow would be the player , the arrow direction is where the player is pointing at, and the red dot would be the bulletSpawn point (or the expected one)
Using the code I posted, the bullets seem to be spawning from somewhere else. Even at the beggining they have an offset and when I rotate the player the spawnPoint seems to be relative to a different origin than what I'm expecting.
This is the bullet position code:
position.x = holder.bulletSpawn.x - (float)(this.width/2 * holder.rotation);
position.y = holder.bulletSpawn.y - (float)(this.height/2 * holder.rotation);
This is inside the Bullet class. The position variable is a Vector2 of bullet, and holder is the player instance. This code is merely to give an offset for the bullet to spawn at the center of its own size
I added some fixes related to the comments, but the bullets still have a tiny offset that looks wrong at certain angles.
Basically the distance i want to get is the width of the player, and his center y which is height/2.
Let's initial position is X0, Y0, rotation is about center point CX, CY, and rotation angle is Theta. So new position after rotation is:
NX = CX + (X0-CX) * Cos(Theta) - (Y0-CY) * Sin(Theta)
NY = CY + (X0-CX) * Sin(Theta) + (Y0-CY) * Cos(Theta)
This equations describe affine transformation of rotation of arbitrary point about center point, and affine matrix is combination of translation, rotation, and back translation matrices.
About center CX, CY - you wrote
it rotates related to his x and y origin at his bottom left
About initial point coordinate - for bullet it seems to be
X + Width, Y + Height/2
Swift extension:
extension CGSize {
static func offsetFrom(angle:CGFloat, distance:CGFloat) -> CGSize {
let rad = angle * CGFloat.pi / 180
return CGSize(width: sin(rad) * distance, height: cos(rad) * distance)
}
}
I am trying to create some sort of "lighthouse" effect by creating a cone and rotating it around the X/Y. The result "runs off" on the Z axis and is also wrong on the Y/X axis if there is a combination of both a Yaw and Pitch degrees.
(The Z axis goes from right to left, X towards the screen, Y upwards).
The yellow cone is the effect. it starts as the see-through cone.
This is hoe the x,y,z locations of the cone are set:
float c = this.height/2;
this.locationX = player.getPosition().x - c*((float)Math.sin(Math.toRadians(tiltOnY)));
this.locationY = player.getPosition().y + c*((float)Math.sin(Math.toRadians(tiltOnX)));
this.locationZ = player.getPosition().z-c;
where height is the height of the cone, and tilt starts at 0 and is added or reduced (as a result of input) with module of 360 degrees.
This is the code in render:
inst = new ModelInstance(this._game_.viewConeModel);
inst.transform.setToTranslation(cone.getX(),cone.getY(),cone.getZ())
.rotate(Vector3.Y,cone.getTiltOnY()).rotate(Vector3.X,cone.getTiltOnX()).rotate(Vector3.X,90);
this._game_.modelBatch.render(inst,this._game_.environment);
The desired result is that the point of the cone remains in the same location, but the cone itself rotates by the tilts on X and Y axis.
Circle c1 = new Circle(20);
c1.relocate(200,200); //Set X and Y
What I want to do is make the circle move in circles around an invisible center of rotation. How can that be achieved?
Thanks.
Edit: I have extremely poor trigonometry skills.
You can use equations for a point on a circle using polar coordinates:
circle_x = rot_center_x + radius * cos(angle)
circle_y = rot_center_y + radius * sin(angle)
Using this you'll get a center point for your new circle. Then you just need to increase (counter clock wise) or decrease (clockwise) your angle, blank screen and draw the circle again.
The angle for trigonometric function is in radians, you have 2*pi radians in a full circle. So if you want angle zero degrees, put in 0. For 90 degrees, put in pi/2.0.
For any other angle use this conversion formula:
angle_rad = pi/180.0 * angle_degrees
If you want to time your rotation you have to choose the angular speed of rotation omega.
omega = 2*pi*f
where f is frequency of rotation, for example f=1Hz means your object will rotate in full circle after one seecond. Omega is in radians per second, so if you have omega 10 radians, then your object will rotate 10 radians during one second, or 100 radians during 10 seconds.
Now you have to determine how much angle you need to add each frame of animation:
ang_inc = omega / fps_avg;
ang += ang_inc;
where fps_avg is measured average of frames per second.
So I'm having trouble making my placer face a planet. I have the angle between the player and the planet, I also have the angle that the player is currently at, now what I want to do with these is make my player face the planet but with an incremental change. (I do this because I want my placer to be able to orbit the planet)
The problem is with the math, I increment the player rotation to match the angle between the player and the planet however because angles work in 0 to 360 my player won't orbit because player rotation might be 2 however angle to planet is 280 so the game will turn the player around, sorry for the bad explanation.
Does anyone know how to make my player successfully orbit my planet?
Here is my code:
double rotation = Math.toDegrees(Math.atan2(currentPlanet.pos[1]-currentPlayer.pos[1], currentPlanet.pos[0]-currentPlayer.pos[0]));
if(rotation < 0)
{
rotation += 360;
}
if(currentPlayer.rotation < rotation)
{
currentPlayer.rotation += 0.15*delta;
}
if(currentPlayer.rotation > rotation)
{
currentPlayer.rotation -= 0.15*delta;
}
The problem is 350° is also -10°. You want the smaller absolute value.
The solution is very simple. Use modulo operation to translate your angles into correct range.
/* returns angle x represented in range -180.0 ... 180.0 */
double clampAngle(double x) {
return (x%360.0+360.0+180.0)%360.0-180.0;
}
Pass your angle difference to this function. Sign of the result will tell you in which direction you should turn:
double rotation = Math.toDegrees(Math.atan2(currentPlanet.pos[1]-currentPlayer.pos[1], currentPlanet.pos[0]-currentPlayer.pos[0]));
double diff = ((rotation-currentPlayer.rotation)%360.0+360.0+180.0)%360.0-180.0;
if(diff>0)
turn right
else
turn left
You might want to not turn at all if abs(diff) is very small.
I'm not sure if it will make your player orbit your planet. You will need to set correct angular and linear speed.
What you want is to make your player rotate to face either plus or minus 90 degrees of the angle you've computed between the planet and the player. Orbit occurs when all movement is tangent (90 degrees) to the planet.
So, compute the angle, compare the player angle to both the +90 and the -90, and rotate your player toward the closer of the two.
I have a 3D arrow drawn with OpenGL that points to the coordinates (0, 0, 0) and I want it to point to a specific GPS location depending on my GPS position and Orientation.
I've tried calculating the azimuth (with my phone's orientation) and adjusting it to be the real north (not the magnetic north).
SensorManager.getOrientation(remappedRotationMatrix, orientation);
// convert radians to degrees
float azimuth = orientation[0];
azimuth = azimuth * 360 / (2 * (float) Math.PI);
GeomagneticField geoField = new GeomagneticField(
Double.valueOf(loc.getLatitude()).floatValue(),
Double.valueOf(loc.getLongitude()).floatValue(),
Double.valueOf(loc.getAltitude()).floatValue(),
System.currentTimeMillis());
// converts magnetic north into true north
azimuth -= geoField.getDeclination();
Then getting the bearing from my Location to the Location I want to point.
target.setLatitude(42.806484);
target.setLongitude(-1.632482);
float bearing = loc.bearingTo(target); // (it's already in degrees)
if (bearing < 0) {
bearing = bearing + 360;
}
float degrees = bearing - azimuth;
if (degrees < 0) {
degrees = degrees + 360;
}
and calculating the degrees I have to rotate the arrow
gl.glRotatef(degrees, 0.0f, 1.0f, 0.0f);
arrow.draw(gl);
Is there someway to do it? Could another possibility be to convert the GPS position to the OpenGL coordinates and use GLU.gluLookAt to point to it?
Thanks.
This seem to be purely a math problem.
Your question is pretty vague, I don't think I can help you without understanding more precisely how your scene is set up and what you want.
Do you know how to use 3D rotation matrices? If not, you probably should learn how they work.
It shouldn't be complicated to calculate the bearing and then rotate the arrow by the degrees you get. I have done the same in 2D although not in OpenGL. I based my code on the Radar sample (http://apps-for-android.googlecode.com/svn/trunk/Radar/). Here is how I draw the 2D arrow:
double bearingToTarget = mBearing - mOrientation;
// Draw an arrow in direction of target
canvas.rotate((float) bearingToTarget, center, center);
final int tipX = center;
final int tipY = center-radius;
canvas.drawLine(center, center, tipX, tipY, mArrowPaint);
final int tipLen = 30;
final int tipWidth = 20;
Path path = new Path();
path.moveTo(tipX, tipY);
path.lineTo(tipX + tipWidth/2, tipY + tipLen);
path.lineTo(tipX - tipWidth/2, tipY + tipLen);
path.lineTo(tipX, tipY);
path.close();
canvas.drawPath(path, mArrowPaint);
canvas.restore();
mBearing is calculated using the method GeoUtils.bearing from the Radar sample which takes care of the complicated math. mOrientation is just the orientation from the sensor listener. So the idea is to compute the difference between the bearing of the GPS location you want to point to (mBearing) and the current orientation of the phone (mOrientation). This gives us the angle bearingToTarget. We then rotate the view about its center by that angle before drawing the arrow along the y axis. This is the same as drawing the arrow rotated by bearingToTarget degrees.
You should be able to apply the same logic in OpenGL by rotating the view about the center of the screen by bearingToTarget degrees before you draw the arrow. Exactly what point you rotate about depends on how your view is set up. To make it simple, make the starting point of your arrow at the origin. Then you can simply rotate about the origin using glRotatef. Otherwise you would first need to translate to the center of the rotation, rotate and then translate back again (this is the common OpenGL technique for rotation about a point).