Rotating a QGraphicsItem and finding the new position of it - qt

I am working on a tower defence game and trying to shoot a projectile from a turret. The sprite of the turret I have looks like this
Also, the bullet looks like this
What I want to do is make the turret shot the bullet to a certain point (for example attackDestination = QPointF(1000, 500);
for this, I have a class Bullet, with the slot move:
void Bullet::move()
{
int stepSize = 20; // how fast the bullet moves
double angle = rotation();
double dy = stepSize * qSin(qDegreesToRadians(angle)); // The X that needs to be moved
double dx = stepSize * qCos(qDegreesToRadians(angle)); // The Y that needs to be moved
setPos(x() + dx, y() + dy);
}
which is triggered by a QTimer.
I also have a slot in the Tower class (which stands for the turret)
void Tower::attackTarget()
{
Bullet *bullet = new Bullet();
//getWidthMap() returns the width of the tower
//getHeightMap() returns the height of the tower
bullet->setPos(x() + getWidthMap() /2, y());
QLineF line(QPointF(x() + getWidthMap() /2, y()), attackDestination);
double angle =(-1) * line.angle();
bullet->setRotation(angle);
this->setRotation(90 + angle);
game->scene->addItem(bullet);
}
I have rotated the turret by +90 degrees because its initial position is vertical and it needs to form an angle (that of the line with oX) just like the bullet. The rotation happens clockwise.
The problem is with the position of the bullet relative to the turret when attacking.
Without the line this->setRotation(90 + angle); (first picture), with it (second picture)
As you can see, the bullets are starting from the initial position of the turret (when it was not rotated), because the pos() function keeps the initial X and Y. How can I fix that ?

Related

Yaw pitch rotation issues

am coding some stuff which should intercept target correctly like turret,cannon.
All calculated correctly but while enemy is moving to left or right side of screen (also up and down) my bot cant shoot them correctly, center is fine. Here little example whats happens with wrong yaw issue, same with pitch https://i.stack.imgur.com/GYGNV.png
Mission is save bot rotation as is, and fix missfire issues.
As main concept i taken camera position and enemy center
Vector2 Rotate(Vector3 Calc)
{
Vector2 coord;
float Yaw = (float)(atan2(Calc.y, Calc.x)) * 180.0f / M_PI;
float Pitch = (float)(atan2(Calc.z, sqrtf(Calc.x * Calc.x + Calc.y * Calc.y))) * 180.0f / M_PI;
coord.x = Pitch;
coord.y = Yaw;
return coord;
}
Vector3 Diff = Target - Bot;
Vector2 Angle = Rotate(Diff);

When rotating 2D sprite towards cursor via angularVelocity, it spins at one point

Intro
I've created a spaceship sprite in my Unity project, I wanted it to rotate towards the cursor via angular velocity, because I'd like make my game to be heavily physics based.
Problem
Now my problem with rotating the sprite via by angular velocity is the following:
At -180° / 180° rotation my ship spins around, because while my mouse's angle is already 180°, while my ship's rotation is still -180°, or the other way around.
I tried
I tried to solve it mathematically, wasn't too successful, I could make it spin the right way just much slower/faster, I could fix the 180/-180 point, but made two different ones instead.
Looked for different solutions, but couldn't find a more fitting one.
Code
So I have this code for the rotation:
// Use this for initialization
void Start () {
rb = gameObject.GetComponent<Rigidbody2D>();
}
// Update is called once per frame
void Update () {
//getting mouse position in world units
mousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
//getting the angle of the ship -> cursor vector
angle = Mathf.Atan2(mousePos.y - transform.position.y, mousePos.x - transform.position.x) * Mathf.Rad2Deg;
//getting the angle between the ship -> cursor and the rigidbody.rotation vector
diffAngle = angle - (rb.rotation + 90);
//Increasing angular velocity scaling with the diffAngle
rb.angularVelocity = diffAngle * Time.deltaTime * PlayerShipStats.Instance.speed * 100f;
Thank you for your contribution in advance
Solution for Problem 1
Inserting this code made it work, not for long :
if(diffAngle > 180) {
diffAngle -= 360;
} else if (diffAngle < -180) {
diffAngle += 360;
}
Problem 2 and Solution for Problem 2
The new problem is:
rigidbody.rotation can exceed it's boundaries, it can be rotated for more than 360 degrees.
this code patched this bug:
if(rb.rotation + 90 >= 180) {
rb.rotation = -270;
} else if (rb.rotation + 90 <= -180) {
rb.rotation = 90;
}
The perfect code
void AimAtTarget(Vector2 target, float aimSpeed) {
//getting the angle of the this -> target vector
float targetAngle = Mathf.Atan2(target.y - transform.position.y, target.x - transform.position.x) * Mathf.Rad2Deg;
if (rb.rotation + 90 >= 180) {
rb.rotation = -270;
} else if (rb.rotation + 90 <= -180) {
rb.rotation = 90;
}
//getting the angle between the this -> target and the rigidbody.rotation vector
float diffAngle = targetAngle - (rb.rotation - 90);
if (diffAngle > 180) {
diffAngle -= 360;
} else if (diffAngle < -180) {
diffAngle += 360;
}
//Increasing angular velocity scaling with the diffAngle
rb.angularVelocity = diffAngle * Time.deltaTime * aimSpeed * 100;
}
There are two problems I see here:
Problem 1
angle is always going to be between -180 and 180, while rb.rotation is between 0 and 360. So you are comparing angles using two different notations. The first step is to get both angles returning -180 to 180 or 0 to 360. I chose to do the following which puts both angles between -180 and 180:
//getting the angle of the ship -> cursor vector
float targetAngle = Mathf.Atan2(
mousePos.y - transform.position.y,
mousePos.x - transform.position.x) * Mathf.Rad2Deg;
//get the current angle of the ship
float sourceAngle = Mathf.Atan2(
this.transform.up.y,
this.transform.up.x) * Mathf.Rad2Deg;
Problem 2
If you fix problem 1 and tried your app you would notice that the ship sometimes rotates the wrong way, although it will eventually get to its target. The problem is that diffAngle can sometimes give a result that is greater than +180 degrees (or less than -180). When this happens we actually want the ship to rotate the other direction. That code looks like this:
//getting the angle between the ship -> cursor and the rigidbody.rotation vector
float diffAngle = targetAngle - sourceAngle;
//use the smaller of the two angles to ensure we always turn the correct way
if (Mathf.Abs(diffAngle) > 180f)
{
diffAngle = sourceAngle - targetAngle;
}
I made a simple Unity to verify this works. I was able to rotate my ship in either direction smoothly.
One thing you may have to handle, if you don't already, is appropriately stopping the rotation of the ship when the it is facing the cursor. In my test I noticed that the ship would jitter slightly when it reached its target because it would (very) slightly overshoot the cursor's angle in one direction and then the other. The larger the value of PlayerShipStats.Instance.speed the more pronounced this effect will likely be.

Multi direction gravity 2D Platform Game - trigonometry issue

So, like the title said, I'm making a 2D platform game with multi direction physics, but my lack of trigonometry skills are killing me.
When my character hits something, it returns a Vector2 hitPoint.
If this was a one-way gravity platform game, I would do the following:
// considering the player's origin point in the middle
public void HitGround(Vector2 hitPoint)
{
this.position.y = hitPoint.y + height /2;
}
But since this have multi gravity physics, this simple method can't be done. So I've worked with relative positions, suppose the gravity is (-3, -3), the player would fall southwest.
// considering the player's origin point in the middle
public void HitGround(Vector2 hitPoint)
{
this.position = hitPoint + GetRelativeYPos(height / 2);
}
// returns the relative Y position based on its rotation
public Vector2 GetRelativeYPos(float offset)
{
// rotation is a Quaternion
return this.rotation * new Vector2(0, 1) * offset;
}
So far, so good. It works perfectly!!! But wait... this is only working when the CENTER of the player hits the ground. In fact, the player has 3 raycasts when he is falling. If the center raycast hits the ground, the code WORKS in EVERY direction he falls.
But when the EDGE RAYCASTS hits the ground. The Player tilts to the center, because the code do not allow him to move right. Now, my last code idea was born:
public void HitGround(Vector2 hitPoint, Vector2 raycastOrigin)
{
// Considering that the raycastOrigin is in the same "relative X" as the player's origin
float signedDistance = SignedDistance(hitPoint, raycastOrigin);
this.position = hitPoint + GetRelativeYPos(height / 2) + GetRelativeXPos(signedDistance);
}
// returns the relative Y position based on its rotation
public Vector2 GetRelativeYPos(float offset)
{
return this.rotation * new Vector2(0, 1) * offset;
}
// returns the relative X position based on its rotation
public Vector2 GetRelativeXPos(float offset)
{
return this.rotation * new Vector2(1, 0) * offset;
}
public float SignedDistance(Vector2 p1, Vector2 p2)
{
float distance = Distance(p1, p2);
float angle = Atan2(p2.y, p2.x) + Atan2(p1.y, p1.x);
// Returns the magnitude AND direction of the vector
return distance * Sign(angle);
}
And there you go!
When the code doesn't work, its just because of the sign of the angle, that, for some reason, it's being calculated wrong, When the angle has to be positive, it returns negative, and vice versa, but it only returns the wrong value when the "player's relative north position" is below 0.
For example:
if the player is falling down, when the Y position is higher than 0, it works, when it is lower, it doesn't.
If the player is falling up, when the Y position is lower than 0, it works, when it is higher, it doesn't.
If the player is falling right, when the X position is lower than 0, it works, when it is
higher, it doesn't.
If the player is falling left, when the X position is greater than 0, it works, when it is lower, it doesn't.
Just one more:
If the player is falling southeast, when the Y position is higher than 0 AND the X position is lower than 0, it works, otherwise, it doesn't
The following method is the responsible for calculating the "signed distance" of two points. I mean, the magnitude of the resulting vector and its direction
public float SignedDistance(Vector2 p1, Vector2 p2)
{
float distance = Distance(p1, p2);
// THIS LINE HERE!!!
float angle = Atan2(p2.y, p2.x) + Atan2(p1.y, p1.x);
return distance * Sign(angle);
}
There is something wrong with that line that I CAN'T FIGURE IT OUT. Is there someone kind enough to answer this question?
I hope I was clear. Bye

Calculating if or not a 3D eyepoint is behind a 2D plane or upwards

The setup
Draw XY-coordinate axes on a piece of paper. Write a word on it along X-axis, so that the word's centerpoint is at origo (half on positive side of X/Y, the other half on negative side of X/Y).
Now, if you flip the paper upside down you'll notice that the word is mirrored in relation to both X- and Y-axis. If you look from behind the paper, it's mirrored in relation to Y-axis. If you look at it from behind and upside down, it's mirrored in relation to X-axis.
Ok, I have points in 2D-plane (vertices) that are created in similar way at the origo and I need to apply exactly the same rule for them. To make things interesting:
The 2D plane is actually 3D, each point (vertex) being (x, y, 0). Initially the vertices are positioned to the origo and their normal is Pn(0,0,1). => Correctly seen when looked at from point Pn towards origo.
The vertex-plane has it's own rotation matrix [Rp] and position P(x,y,z) in the 3D-world. The rotation is applied before positioning.
The 3D world is "right handed". The viewer would be looking towards origo from some distance along positive Z-axis but the world is also oriented by rotation matrix [Rw]. [Rw] * (0,0,1) would point directly to the viewer's eye.
From those I need to calculate when the vertex-plane should be mirrored and by which axis. The mirroring itself can be done before applying [Rp] and P by:
Vertices vertices = Get2DPlanePoints();
int MirrorX = 1; // -1 to mirror, 1 NOT to mirror
int MirrorY = 1; // -1 to mirror, 1 NOT to mirror
Matrix WorldRotation = GetWorldRotationMatrix();
MirrorX = GetMirrorXFactor(WorldRotation);
MirrorY = GetMirrorYFactor(WorldRotation);
foreach(Vertex v in vertices)
{
v.X = v.X * MirrorX * MirrorY;
v.Y = V.Y * MirrorY;
}
// Apply rotation...
// Add position...
The question
So I need GetMirrorXFactor() & ..YFactor() -functions that return -1 if the viewer's eyepoint is at greater "X/Y"-angle than +-90 degrees in relation to the vertex-plane's normal after the rotation and world orientation. I have already solved this, but I'm looking for more "elegant" mathematics. I know that rotation matrices somehow contain info about how much is rotated by which axis and I believe that can be utilized here.
My Solution for MirrorX:
// Matrix multiplications. Vectors are vertical matrices here.
Pnr = [Rp] * Pn // Rotated vertices's normal
Pur = [Rp] * (0,1,0) // Rotated vertices's "up-vector"
Wnr = [Rw] * (0,0,1) // Rotated eye-vector with world's orientation
// = vector pointing directly at the viewer's eye
// Use rotated up-vector as a normal some new plane and project viewer's
// eye on it. dot = dot product between vectors.
Wnrx = Wnr - (Wnr dot Pur) * Pur // "X-projected" eye.
// Calculate angle between eye's X-component and plane's rotated normal.
// ||V|| = V's norm.
angle = arccos( (Wnrx dot Pnr) / ( ||Wnrx|| * ||Pnr|| ) )
if (angle > PI / 2)
MirrorX = -1; // DO mirror
else
MirrorX = 1; // DON'T mirror
Solution for mirrorY can be done in similar way using viewer's up and vertex-plane's right -vectors.
Better solution?
if (([Rp]*(1,0,0)) dot ([Rw]*(1,0,0))) < 0
MirrorX = -1; // DO mirror
else
MirrorX = 1; // DON'T mirror
if (([Rp]*(0,1,0)) dot ([Rw]*(0,1,0))) < 0
MirrorY = -1; // DO mirror
else
MirrorY = 1; // DON'T mirror
Explaining in more detail is difficult without diagrams, but if you have trouble with this solution we can work through some cases.

Rotating a D3DXVECTOR3 around a specific point

This is probably a pretty simple thing but my knowledge of direct x is just not up to par with what I'm trying to achieve.
For the moment I am trying to create a vehicle that moves around on terrain. I am attempting to make the vehicle recognize the terrain by creating a square (4 D3DXVECTOR3 points) around the vehicle who's points each detect the height of the terrain and adjust the vehicle accordingly.
The vehicle is a simple object derived from Microsoft sample code. It has a world matrix, coordinates, rotations etc.
What I am trying to achieve is to make these points move along with the vehicle, turning when it does so they can detect the difference in height. This requires me to update the points each time the vehicle moves but I cannot for the life of me figure out how to get them to rotate properly.
So In summary I am looking for a simple way to rotate a vector about an origin (my vehicles coordinates).
These points are situated near the vehicle wheels so if it worked they would stay there regardless of the vehicles y -axis rotation.
Heres What Ive tryed:
D3DXVECTOR3 vec;
D3DXVec3TransformCoord(&vectorToHoldTransformation,&SquareTopLeftPoint,&matRotationY);
SquareTopLeftPoint = vec;
This resulted in the point spinning madly out of control and leaving the map.
xRot = VehicleCoordinateX + cos(RotationY) * (SquareTopleftX - VehicleCoordinateX) - sin(RotationY) * (SquareTopleftZ - VehicleCoordinateZ);
yRot = VehicleCoordinateZ + sin(RotationY) * (SquareTopleftX - VehicleCoodinateX) + cos(RotationY) * (SquareToplefteZ - VehicleCoordinateZ);
BoxPoint refers to the vector I am attempting to rotate.
Vehicle is of course the origin of rotation
RotationY is the amount it has rotated.
This is the code for 1 of 4 vectors in this square but I assume once I get 1 write the rest are just copy-paste.
No matter what I try the point either does not move or spirals out of control under leaving the map all-together.
Here is a snippet of my object class
class Something
{
public:
float x, y, z;
float speed;
float rx, ry, rz;
float sx, sy, sz;
float width;
float length;
float frameTime;
D3DXVECTOR3 initVecDir;
D3DXVECTOR3 currentVecDir;
D3DXMATRIX matAllRotations;
D3DXMATRIX matRotateX;
D3DXMATRIX matRotateY;
D3DXMATRIX matRotateZ;
D3DXMATRIX matTranslate;
D3DXMATRIX matWorld;
D3DXMATRIX matView;
D3DXMATRIX matProjection;
D3DXMATRIX matWorldViewProjection;
//these points represent a box that is used for collision with terrain.
D3DXVECTOR3 frontLeftBoxPoint;
D3DXVECTOR3 frontRightBoxPoint;
D3DXVECTOR3 backLeftBoxPoint;
D3DXVECTOR3 backRightBoxPoint;
}
I was thinking it might be possible to do this using D3DXVec3TransformCoord
D3DXMatrixTranslation(&matTranslate, origin.x,0,origin.z);
D3DXMatrixRotationY(&matRotateY, ry);
D3DXMatrixTranslation(&matTranslate2,width,0,-length);
matAllRotations = matTranslate * matRotateY * matTranslate2;
D3DXVECTOR3 newCoords;
D3DXVECTOR3 oldCoords = D3DXVECTOR3(x,y,z);
D3DXVec3TransformCoord(&newCoords, &oldCoords, &matAllRotations);
Turns out that what I need to do was
Translate by -origin.
rotate
Translate by origin.
What I was doing was
Move to origin
Rotate
Translate by length/width
Thought it was the same.
D3DXMATRIX matTranslate2;
D3DXMatrixTranslation(&matTranslate,-origin.x,0,-origin.z);
D3DXMatrixRotationY(&matRotateY,ry);
D3DXMatrixTranslation(&matTranslate2,origin.x,0,origin.z);
//D3DXMatrixRotationAxis(&matRotateAxis,&origin,ry);
D3DXMATRIX matAll = matTranslate * matRotateY * matTranslate2;
D3DXVECTOR4 newCoords;
D3DXVECTOR4 oldCoords = D3DXVECTOR4(x,y,z,1);
D3DXVec4Transform(&newCoords,&oldCoords,&matAll);
//D3DXVec4TransformCoord(&newCoords, &oldCoords, &matAll);
return newCoords;
Without knowing more about your code I can't say what it does exactly, however one 'easy' way to think about this problem if you know the angle of the heading of your vehicle in world coordinates is to represent your points in a manner such that the center of the vehicle is at the origin, use a simple rotation matrix to rotate it around the vehicle according to the heading, and then add your vehicle's center to the resulting coordinates.
x = vehicle_center_x + cos(heading) * corner_x - sin(heading) * corner_y
y = vehicle_center_y - sin(heading) * corner_x + cos(heading) * corner_y
Keep in mind that corner_x and corner_y are expressed in coordinates relative to the vehicle -- NOT relative to the world.

Resources