I'm building a small Physics engine that fires a projectile on a set of launch parameters by the user (angle, height, time interval and initial velocity), then displays some information such as the total distance or angle at every time interval that it's in the air:
bool heightCheck = false;
double theta;
double initialVelocity, velocity;
double yNew = 0.0, xNew, xOld = 0.0, yOld = 0.0;
const double time = 0.1;
const double gravitiyHalf = 9.8 / 2;
double velocityX = 0.0, velocityY = 0.0;
double angle = 0.0;
double totalT = 0;
double maxHeight = 0.0;
double thetaDegrees = 0;
#define PI 3.14159265l // constant for PI
cout << "Insert a lanuch Angle (theta): ";
cin >> thetaDegrees;
cout << "Insert a launch height: ";
cin >> yOld;
cout << "Insert an initial velocity: ";
cin >> initialVelocity;
cout << "Time (DeltaT) in seconds: ";
cin >> totalT;
for (double deltaTime = 0.0; deltaTime < totalT; deltaTime += 0.1) {
const double squared = deltaTime * deltaTime; // squared constant for deltaTime squared
theta = thetaDegrees * PI / 180; // converts theta to a degrees value
velocityX = initialVelocity * cos(theta); // calculates Vx
velocityY = initialVelocity * sin(theta); // calculates Vy
// apply initialV to velocity
velocity = initialVelocity + 9.8 * time;
xNew = xOld + velocity * time; // works out displacement for X
yNew = yOld + velocity * deltaTime - gravitiyHalf / 0.5 * (squared); // calculates Y
velocityY = velocity - 9.8 * deltaTime; // includes gravity to Y
angle = atan2(yNew, xNew) * 180 / PI; // convert angle to degrees
cout << "\nHeight: " << yNew << endl;
cout << "Distance in Meters: " << xNew << "m" << endl;
cout << "Angle: " << angle << endl;
cout << "Time: " << deltaTime << "s " << endl;
if (heightCheck == false) {
maxHeight = yOld;
// keep maxheight equal to previous height
}
if (yNew < yOld && heightCheck == false) {
heightCheck = true;
// if projectile is going down, trigger maxheight
}
cout << "Maximum height: " << maxHeight << endl;
if ((yNew < 0) || (deltaTime == totalT)) {
getchar(); // stops if delta t = total T or projectile landed
}
yOld = yNew; // refresh x & y
xOld = xNew;
}
If I enter the following values at the start of my program:
theteDegrees = 45
yOld = 0
initialVelocity = 20
totalT = 10
My program displays the expected results that show my projectile going up, then down. However, if I enter the same values expect -40 for thetaDegrees, my projectile should head straight down, instead it just goes up and then down again.
Where have I gone wrong in my code?
As Igor said, during distance calculation over X and Y you've not taken velocityX and velocityY into consideration.
xNew = xOld + velocity * time; // works out displacement for X
yNew = yOld + velocity * deltaTime - gravitiyHalf / 0.5 * (squared); // calculates Y
There is some redundant calculations involved. A more simplistic version could be like this.
theta = thetaDegrees * PI / 180; // converts theta to a degrees value
velocityX = initialVelocity * cos(theta); // calculates Vx
velocityY = initialVelocity * sin(theta); // calculates Vy
//cout<<velocityX<<endl<<velocityY<<endl;
for (double deltaTime = 0.0; deltaTime < totalT; deltaTime += 0.1) {
const double squared = deltaTime * deltaTime; // squared constant for deltaTime squared
xNew = xOld + velocityX * time; // works out displacement for X
yNew = yOld + velocityY * deltaTime - gravitiyHalf / 0.5 * (squared); // calculates Y
velocityY = velocityY - 9.8 * deltaTime; // includes gravity to Y
angle = atan2(yNew, xNew) * 180 / PI; // convert angle to degrees
cout << "\nHeight: " << yNew << endl;
cout << "Distance in Meters: " << xNew << "m" << endl;
cout << "Angle: " << angle << endl;
cout << "Time: " << deltaTime << "s " << endl;
if (heightCheck == false) {
maxHeight = yOld;
// keep maxheight equal to previous height
}
if (yNew < yOld && heightCheck == false) {
heightCheck = true;
// if projectile is going down, trigger maxheight
}
cout << "Maximum height: " << maxHeight << endl;
if ((yNew < 0) || (deltaTime == totalT)) {
getchar(); // stops if delta t = total T or projectile landed
}
yOld = yNew; // refresh x & y
xOld = xNew;
}
You either have to change
xNew = xOld + velocityX * time; // works out displacement for X
to
xNew = xOld + velocityX * deltaTime; // works out displacement for X
or remove
xOld = xNew;
at the end, as leaving both unchanged would give you a quadratically speeding x coordinate, not a linear motion of constant velocity.
Related
I'm trying to create a vector2D class for my game but I think I'm getting the math wrong.
When I create a new vector2d object it automatically sets its x and y to 1, 1 in the constructor.
Vector2D vec;
std::cout << " x: " << vec.GetX() << " y: " << vec.GetY() << " angle rad: " << vec.GetAngleRad() << " magnitude: " << vec.GetMagnitude() << std::endl;
system("pause");
return 0;
and it outputs:
x: 1
y: 1
angle in rad: 0.785398
magnitude: 1.41421
(which is exactly what i expect)
but the problem is when I parse anything to the setAngle funciton, I get some wired results.
For example:
Vector2D vec;
vec.SetAngleRad(3);
std::cout << " x: " << vec.GetX() << " y: " << vec.GetY() << " angle rad: " << vec.GetAngleRad() << " magnitude: " << vec.GetMagnitude() << std::endl;
system("pause");
return 0;
I would expect it to output angle in rad: 3
but instead I get
angle in rad: 0.141593.
This is the vector2D class (I've tried to comment my code so you can see my what I was thinking when I wrote it):
#include "Vector2D.h"
Vector2D::Vector2D():
_x(1.0f),
_y(1.0f)
{
}
Vector2D::~Vector2D()
{
}
void Vector2D::SetX(float x)
{
_x = x;
}
float Vector2D::GetX()
{
return _x;
}
void Vector2D::SetY(float y)
{
_y = y;
}
float Vector2D::GetY()
{
return _y;
}
void Vector2D::SetAngleRad(float angle)
{
float hypotenuse = GetMagnitude();
SetX( cos(angle) * hypotenuse); // cos of angle = x / hypotenuse
// so x = cos of angle * hypotenuse
SetY( sin(angle) * hypotenuse); //sin of angle = y / hypotenuse
// so y = sin of angle * hypotenuse
}
float Vector2D::GetAngleRad()
{
float hypotenuse = GetMagnitude();
return asin( _y / hypotenuse ); // if sin of angle A = y / hypotenuse
// then asin of y / hypotenuse = angle
}
void Vector2D::SetMagnitude(float magnitude)
{
float angle = GetAngleRad();
float hypotenuse = GetMagnitude();
SetX( (cos(angle) * hypotenuse) * magnitude ); // cos of angle = x / hypotenuse
// so cos of angle * hypotenuse = x
// multiplied by the new magnitude
SetY( (sin(angle) * hypotenuse) * magnitude); //sin of angle = y / hypotenuse
// so sin of angle * hypotenuse = y
// multipied by the new magnitude
}
float Vector2D::GetMagnitude()
{
return sqrt( (_x * _x) + (_y * _y) ); // a^2 + b^2 = c^2
//so c = sqrt( a^2 + b^2 )
}
So I'd really appreciate it if someone could explain to me what I'm doing wrong here :)
To get angle in full circle range, you have to use both y and x components with atan2 function
return atan2( _y, _x );
Note result range -Pi..Pi and correct negative one by +2*Pi if you need range 0..2*Pi
Another issue: :SetMagnitude method really multiplies current magnitude by magnitude multiplier, while name assumes that method should set it (so vector length 2 after applying SetMagnitude(2) will have magnitude 4)).
So it would better to remove *hypotenuse multiplication (or change method name)
I have read this and I tried to implement it in C++, but the output is quite different. I have no idea what is wrong.
The code I used:
double cordinate_print()
{
int x, y;
int number_of_chunks = 5;
double angle=0;
double x_p[5] ; // number of chunks
double y_p[5]; // number of chunks
//double x0, y0 = radious;
double rad = 150;
for (int i = 0; i < number_of_chunks; i++)
{
angle = i * (360 / number_of_chunks);
float degree = (angle * 180 / M_PI);
x_p[i] = 0 + rad * cos(degree);
y_p[i] = 0 + rad * sin(degree);
//printf("x-> %d y-> %d \n", x_p[i], y_p[i]);
cout << "x -> " << x_p[i] << "y -> " << y_p[i] << "\n";
}
//printing x and y values
printf("\n \n");
return 0;
}
Output
x -> 150 y -> 0
x -> -139.034 y -> -56.2983
x -> 107.74 y -> 104.365
x -> -60.559 y -> -137.232
x -> 4.77208 y -> 149.924
The correct output
(150,0)
(46,142)
(-121,88)
(-121,-88)
(46,-142)
Issue with the conversion of degree into radian
float degree = (angle * 180 / M_PI);
The correct conversion formula is
float radian = (angle * M_PI / 180);
Also as mentioned in the comment use the good name to avoid any confusion.
Since your default angles are in degrees, you need to convert them to radians first before using sin() and cos(), then multiplying it to the radius.
double cordinate_print()
{
int number_of_chunks = 5;
double degrees = 0; // <-- correction
double x_p[5]; // number of chunks
double y_p[5]; // number of chunks
double radius = 150; // <-- correction
for (int i = 0; i < number_of_chunks; i++)
{
degrees = i * (360 / number_of_chunks); // <-- correction
float radian = (degrees * (M_PI / 180)); // <-- correction
x_p[i] = radius * cos(radian); // <-- correction
y_p[i] = radius * sin(radian); // <-- correction
cout << "x -> " << x_p[i] << "y -> " << y_p[i] << "\n";
}
//printing x and y values
printf("\n \n");
return 0;
}
I've been working on a project using OpenGL and Bullet physics, but can't seem to figure out how to move the rigidbody to the camera. I've tried setting the motion state repeatedly and translating it upon key presses and both of these methods do not work. This is my camera movement code.(I have commented out both methods.)
void CameraUpdateLoop()
{
xpos = 0;
ypos = 0;
deltatime = glfwGetTime() - prevtime;
glfwGetCursorPos(window,&xpos,&ypos);
// cout << xpos << " " << ypos << endl;
glfwSetCursorPos(window,800/2,600/2);
hangle += mousesens * deltatime * (400 - xpos);
vangle += mousesens * deltatime * (300 - ypos);
direction = glm::vec3(cos(vangle)* sin(hangle),sin(vangle),cos(vangle)* cos(hangle));
right = glm::vec3(sin(hangle - 3.14f/2.0f),0,cos(hangle - 3.14f/2.0f));
up = glm::vec3(glm::cross(right,direction));
if(glfwGetKey(window,GLFW_KEY_W) == GLFW_PRESS)
{
position += direction * deltatime * speed;
// rigidbody->translate(btVector3(direction.x,direction.y,direction.z) * deltatime * speed);
}
if(glfwGetKey(window,GLFW_KEY_S) == GLFW_PRESS)
{
position -= direction * deltatime * speed;
// rigidbody->translate(-(btVector3(direction.x,direction.y,direction.z) * deltatime * speed));
}
if(glfwGetKey(window,GLFW_KEY_D) == GLFW_PRESS)
{
position += right * deltatime * speed;
//rigidbody->translate(btVector3(right.x,right.y,right.z) * deltatime * speed);
}
if(glfwGetKey(window,GLFW_KEY_A) == GLFW_PRESS)
{
position -= right * deltatime * speed;
//rigidbody->translate(-(btVector3(right.x,right.y,right.z) * deltatime * speed));
}
btTransform output;
//fallMotionState = new btDefaultMotionState(btTransform(btQuaternion(0, 0, 0, 1), btVector3(position.x,position.y, position.z)));
//rigidbody->setMotionState(fallMotionState);
rigidbody->getMotionState()->getWorldTransform(output);
cout << output.getOrigin().getZ() << endl;
if(vangle < -90.0f)
{
vangle = -90.0f;
}
if(vangle > 90.0f)
{
vangle = 90.0f;
}
// cout << vangle << endl;
// cout << hangle << endl;
ProjectionMatrix = glm::perspective(initfov,4.0f/3.0f,0.1f,100.0f);
ViewMatrix= (glm::lookAt(position,position+direction,up));
prevtime = glfwGetTime();
}
The issue is that no manifolds are produced with each tick even though getting the origin with motion states usually produces correct output.
static void test (btDynamicsWorld *world, btScalar timeStep)
{
int numManifolds = world->getDispatcher()->getNumManifolds();
if(numManifolds >= 1)
{
cout << "collision" << endl;
glClearColor(0.0,0.7,0.8,1.0);
}
else if(numManifolds < 1)
{
glClearColor(0.0,0.3,0.8,1.0);
}
I am trying to generate a set of points that I will connect to make polygon. The data has to be generated in a systematic way.
I am trying to generate the point set by randomly deriving radial coordinate r and evenly incrementing angular coordinate theta such that all the points are linked orderly without crossing with each other. I followed the correct formulas and I increment the angle but the data comes out negative because of sin and cos. I wanted to know if I'm doing this correctly.
struct Point2D {
int x;
int y;
};
Point2D poly[10];
int N = 80;
int x = (rand() % N + 1) * 5;
int y = (rand() % N + 1) * 5;
int r = sqrt(x*x + y*y);
int theta = int(atan ((float)y/(float)x) * 180 / PI);
cout << "x " << x << " y " << y << " r " << r << " theta " << theta << endl;
for (int i = 0; i < 10; i++) {
Point2D p;
p.x = x;
p.y = y;
poly[i] = p;
theta += 20;
x = r * sin(theta);
y = r * cos(theta);
cout << "x " << x << " y " << y << endl;
}
sin and cos return points on a unit circle centered around (0, 0), as paddy pointed out. To have no negative values in the points on your own polygon, you'll need to shift the origin of that circle. You're already changing its size, with r * sin(theta); you can accomplish a minimum translation with:
x = r * cos(theta) + r;
y = r * cos(theta) + r;
When I make this change to your program, I don't get negative values anymore.
Having said that, I suspect that you're not incrementing theta the way you intend. If you're trying to divide the circle into 10 equal angles, then theta should be a float or double and incremented like this:
theta += (2 * M_PI / 10);
theta is in radians, so 2 * M_PI is once around the unit circle.
While trying to create my own physics engine (don't try persuading me not to), I decided to create a class for each pixel, called Particle, this system has an x and a y, and a x and y velocity, as shown below. Unfortunately, the code for calculateGravitationalVelocity doesn't abide by the laws of physics in certain situations. For example, if the x of the particle and the x of the other particle is the same, the particle will fall towards the object realistically, but when the particle gets too close, it pings off towards the positive x. I am only going to include the class source code, but I can include the source code of the other file, though it is partly written in SFML
Particle.cpp:
#include <iostream>
#include <string>
#include <math.h>
class Particle
{
private:
//Coords:
double x, y;
//Velocities:
double xVelocity = 0;
double yVelocity = 0;
//Material:
std::string material = "Generic";
//Mass:
double mass = 0;
public:
//Coords:
void setCoords(double, double);
float getCoords(char);
//Velocities:
void giveVelocity(char, float);
void setVelocity(char, float);
float getVelocity(char);
//Gravitational Velocity:
void calculateGravitationalVelocity(Particle);
//Material:
void setMaterial(std::string);
std::string getMaterial();
//Mass:
void setMass(double);
double getMass();
//Update:
void update();
};
//Coords:
void Particle::setCoords(double newX, double newY)
{
x = newX;
y = newY;
}
float Particle::getCoords(char axis)
{
if (axis == 'x')
{
//return floor(x);
return x;
}
else if (axis == 'y')
{
//return floor(y);
return y;
}
}
//Velocities:
void Particle::giveVelocity(char axis, float addedVelocity)
{
if (axis == 'x') {xVelocity = xVelocity + addedVelocity;}
else if (axis == 'y') {yVelocity = yVelocity + addedVelocity;}
}
void Particle::setVelocity(char axis, float newVelocity)
{
if (axis == 'x') {xVelocity = newVelocity;}
else if (axis == 'y') {yVelocity = newVelocity;}
}
float Particle::getVelocity(char axis)
{
if (axis == 'x') {return xVelocity;}//floor(xVelocity);}
else if (axis == 'y') {return xVelocity;}//floor(yVelocity);}
}
//Gravitational Velocity (Where the problems probably are):
void Particle::calculateGravitationalVelocity(Particle distantParticle)
{
//Physics constants:
const double pi = 3.14159265359; //Pi
const double G = 0.00000000006673; //Gravitational Constant (or Big G)
//Big Triangle Trigonometry:
//Get coords of moving particle:
double x1 = x;
double y1 = y;
//Get coords of particle with gravity:
double x2 = distantParticle.getCoords('x');
double y2 = distantParticle.getCoords('y');
if (x1 != x2)
{
//Work out the angle:
double A = atan((y2 - y1) / (x2 - x1)) * 180 / pi;
//Remove the minus sign:
A = fabs(A);
//Small Triangle Trigonometry:
//Work out the hypotenuse of the big triangle:
double hyp = sqrt(pow(x2 - x1, 2) + pow(y2 - y1, 2));
//Work out the gravitational field (hyp of small triangle):
long double gravitationalField = G * (distantParticle.getMass() / pow(hyp, 2));
//For testing purposes:
//std::cout << "X: " << (cos(A) * gravitationalField) / 1000 << std::endl;
//std::cout << "Y: " << (sin(A) * gravitationalField) / 1000 << std::endl;
//Work out the X velocity:
xVelocity = xVelocity + (cos(A) * gravitationalField) / 1000;
//Work out the Y velocity:
yVelocity = yVelocity + (sin(A) * gravitationalField) / 1000;
}
else
{
//Work out the hypotenuse of the big triangle:
double hyp = sqrt(pow(x2 - x1, 2) + pow(y2 - y1, 2));
//Work out the gravitational field (hyp of small triangle):
long double gravitationalField = G * (distantParticle.getMass() / pow(hyp, 2));
yVelocity = yVelocity + gravitationalField / 1000;
}
}
//Material:
void Particle::setMaterial(std::string newMaterialType)
{
material = newMaterialType;
}
std::string Particle::getMaterial()
{
return material;
}
//Mass:
void Particle::setMass(double newMass)
{
mass = newMass;
}
double Particle::getMass()
{
return mass;
}
//Update:
void Particle::update()
{
x = x + xVelocity;
y = y + yVelocity;
}
I am sorry for the very open question, and it probably goes against the rules somewhere, but I couldn't find it. The code for working out mostly uses a two triangles to make a x and y velocity. Here is an image of what I was hoping the code would do as a triangle (sorry it doesn't look great, but I like using a whiteboard):
You don't need to perform any trigonometric calculation.
...
//Get coords of particle with gravity:
double x2 = distantParticle.getCoords('x');
double y2 = distantParticle.getCoords('y');
// Get difference vector
double rx = x1 - x2;
double ry = y1 - y2;
// square of distance
double r2 = rx * rx + ry * ry;
// distance
double r = sqrt (r2);
if (r != 0) {
// normalize difference vector
double ux = rx / r;
double uy = ry / r;
// acceleration of gravity
double a = - G * distantParticle.getMass() / r2;
xVelocity += a * ux / 1000;
yVelocity += a * uy / 1000;
}
}