I'm trying to write a simple c++ program that outputs an objects current height once it hits a specific point. The object I'm trying to accomplish is that you have an object that starts at a varied position a moves off under a random velocity with gravity attached. If the ball collides with a wall or another object, it should move backward, with no energy loss, but still continue to fall due to gravity. Once the ball has reached a specific height, output that value.
Now, all I'm trying to do right now is check to see if my ball has gone beyond my width bounds. But for the life of me I can't see why my last if statement at the bottom wont call.
Am I missing / doing something really stupid?
int _tmain(int argc, _TCHAR* argv[])
{
float velocity;
float height, targetHeight;
float gravity;
float time;
float angle;
float width;
float move;
float distance;
gravity = 9.80f;
time = 0;
distance = 0;
cout << "Set Height\n";
cin >> height;
cout << "Set target height\n";
cin >> targetHeight;
cout << "Set Angle ( 0 - 90 ): \n";
cin >> angle;
angle *= 3.14 * 180; // convert to radians
cout << "Set velocity (0 - 100): \n";
cin >> velocity;
cout << "Set Play field Width: \n";
cin >> width;
while( height >= target )
{
time++;
distance += velocity * cos(angle) * time;
height += (velocity * sin(angle) * time) - (gravity * pow(time, 2) ) / 2;
}
if( distance == width)
{
cout << "You've hit the wall\n";
}
return 0;
}
Your final if statement if( distance == width ) does not test if the distance has gone beyond the width. You probably want if( distance >= width ). There doesn't appear to be any testing of distance traveled within your movement loop so distance could easily be greater than the width and thus cause your if to not be true.
Move backwards at the same speed: velocity = -velocity;. Of course, if it's moving backwards, it may hit the other wall, so you probably want to check the distance == 0; as well. (Since it's floating point, I would also suggest you use >= and <= instead of exact comparisons, or you may find that the ball was one micrometer PAST the wall and then continues until it hits the sun, or you run out of math, or whatever else happens if you keep going forever).
I would further suggest that you would need the width and breadth of the "room" the ball is bouncing around in. So, in total you need X, Y, and Z coordinates of the ball.
Note: your time ever increases, which implies a direct formula for height and distance (one saying distance = f(time);), but you're accumulating.
So probably you want to assign instead of increment your distance and height variables:
distance = velocity * cos(angle) * time;
height = (velocity * sin(angle) * time) - (gravity * pow(time, 2) ) / 2;
Next to that, you probably want to check whether the travelled distance exceeds the distance to the wall (equality with floats is very improbable, plus inaccurate).
Some stylistic advice: put those equations in functions of their own.
Related
I'm trying to make a pong game. I have code which detects when the ball reaches the edge of the screen and will change direction, however as soon as it does not meet the if statement it continues in the previous direction it was travelling. This leaves the ball to get stuck on the edge and continue travelling on the x-axis. I cannot think of a way to make the direction change permanent. How would I go about doing this?
//grab the position of the ball
float x_pos = ball->xPos();
float y_pos = ball->yPos();
//move the bal in x and y direction
x_pos += 250 * (game_time.delta.count() / 1000.f);
y_pos += 400 * (game_time.delta.count() / 1000.f);
std::cout << "The Y co-ord is " << y_pos << std::endl;
float angle = y_pos / x_pos;
std::cout << "The angle it hits is " << angle << std::endl;
//change direction when ball hits edge
if (y_pos >= (game_height - 32) || y_pos <= 0)
{
y_pos += -400 * (game_time.delta.count() / 1000.f);
}
// update the position of the ball
ball->xPos(x_pos);
ball->yPos(y_pos);
Knowing just the position of the ball is not enough. You don't know if the ball is(should be) going towards the wall or away from it. So you need to store the position and the velocity vector.
Just use a variable for the velocity:
// before the loop
x_velocity = 250;
y_velocity = 400;
// then inside the loop
if ( bounce ) y_velocity = -y_velocity;
x_pos += x_velocity * (game_time.delta.count() / 1000.f);
y_pos += y_velocity * (game_time.delta.count() / 1000.f);
In addition, consider what this answer states. To determine if the ball bounces you need to also check the velocity not only the position. What if you already bounced in the last iteration, but the ball is still close to the wall on the next iteration. Only bounce it when it is close to the wall and its current direction is away from the screen.
The issue:
When using the math.h trigonometry functions with a simple SDL 2.0.4 application (top-down movement/rotation attempt), I discovered that there were some slight errors in the trigonometry calculations, resulting in the 'player' not moving exactly in the direction being faced, which bugged me a lot. I searched up why this could be, and the main reason seems to be that of floating point arithmetic.
I resorted to using a fixed-point maths library called libfixmath - and the problem was solved, but only to some extent. The cosine of 90 degrees returned in radians was 0.00775146, rather than 0; however, a cosine of 270 degrees did return 0 radians! I must admit this problem has got me stuck and I need a bit of help (my mathematics skills aren't great, which doesn't help).
The variables used in fixed-point arithmetic:
double direction = 0.0;
double sinRadians = 0.0;
double cosRadians = 1.0; // presuming player starts facing direction of 0 degrees!
And then the part of 'int main(int argc, char* argv[])' involving these variables:
if (keyDownW == true)
{
Player.setXPos(Player.getXPos() + (10 * sinRadians));
Player.setYPos(Player.getYPos() - (10 * cosRadians)); // +- = -
cout << "Cosine and Sine (in radians): " << cosRadians << ", " << sinRadians << endl;
cout << direction << " degrees \n" << endl;
}
else if (keyDownS == true)
{
Player.setXPos(Player.getXPos() - (6 * sinRadians));
Player.setYPos(Player.getYPos() + (6 * cosRadians)); // -- = +
}
if (keyDownA == true)
{
if (direction <= 0)
{
direction = 345;
}
else
{
direction -= 15;
}
direction = direction * (3.14159 / 180); // convert to radians
cosRadians = fix16_to_dbl(fix16_cos(fix16_from_dbl(direction)));
sinRadians = fix16_to_dbl(fix16_sin(fix16_from_dbl(direction)));
direction = direction * (180 / 3.14159); // convert back to degrees
}
else if (keyDownD == true)
{
if (direction >= 345)
{
direction = 0;
}
else
{
direction += 15;
}
direction = direction * (3.14159 / 180); // convert to radians
cosRadians = fix16_to_dbl(fix16_cos(fix16_from_dbl(direction)));
sinRadians = fix16_to_dbl(fix16_sin(fix16_from_dbl(direction)));
direction = direction * (180 / 3.14159); // convert back to degrees
}
When cosRadians and sinRadians are assigned, what's happening is that direction (which has been converted from degrees to radians) is converted to a fixed-point number, which is then used to calculate the cosine and sine individually, then converted from a fixed-point number back to a double for assignment, all with the use of the libfixmath library.
Here's the program currently (compiled as an .exe with the necessary .dll files; I statically linked the libfixmath library) so you can see the issue for yourself: https://mega.nz/#!iJggRbxY!ySbl-2X_oiJKFACyp_kLg9yuLcEsFM07lTRqLtKCsy4
Any ideas as to why this is happening?
One problem is that you are converting back and forth from degrees to radians and back every frame, and you will lose precision each time. Instead, do the conversion into a temporary variable, like this:
double direction_radians = direction * (3.14159 / 180); // convert to radians
cosRadians = fix16_to_dbl(fix16_cos(fix16_from_dbl(direction_radians)));
sinRadians = fix16_to_dbl(fix16_sin(fix16_from_dbl(direction_radians)));
This way the errors don't accumulate.
Also, is there any reason you can't just use the normal sin and cos functions from math.h instead of fixed point versions? If this is only being done for the player once per frame it shouldn't be a speed critical calculation.
One more thing: is the player x and y position stored as an integer or a double? If it's an integer then you won't be able to move in the precise direction you want because you have to move integral amounts each frame. To solve this you can maintain 2 double variables for the x, y position, and add your movement vector on to them each frame. Then set the x and y by converting to int. This will stop you losing the fractional part of your position each time.
e.g.
playerX += (10 * sinRadians);
playerY -= (10 * cosRadians);
Player.setXPos((int)playerX);
Player.setYPos((int)playerY);
I am using the Xinput API, but I am having trouble with the following bit of code. My assumption is that the definition of R/LX and R/LY, should dynamically change as its called again and again, but the value for the position of the thumb stick is arbitrarily set to -13108, so the normalized magnitude of X and Y is -.707, and the normalized magnitude is ~.428. I keep trying to move the control stick but the values won't change. Any ideas? Am I misunderstanding the Xinput API? Does the struct controller state make sense? The code is below is just for the left stick, but the right stick is very similar.
#define XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE 7849
#define XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE 8689
#define XINPUT_GAMEPAD_TRIGGER_THRESHOLD 30
struct CONTROLLER_STATE
{
XINPUT_STATE state;
bool bConnected;
};
CONTROLLER_STATE g_Controllers[4];
while(1)
{
//...
XINPUT_STATE state = g_Controllers[1].state;
float LX = state.Gamepad.sThumbLX;
float LY = state.Gamepad.sThumbLY;
//determine how far the controller is pushed
float magnitude = sqrt(LX*LX + LY*LY);
//determine the direction the controller is pushed
float normalizedLX = LX / magnitude;
float normalizedLY = LY / magnitude;
cout << " Y " << LY << endl;
float normalizedMagnitude = 0;
//check if the controller is outside a circular dead zone
if (magnitude > XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE)
{
//clip the magnitude at its expected maximum value
if (magnitude > 32767) magnitude = 32767;
//adjust magnitude relative to the end of the dead zone
magnitude -= XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE;
//optionally normalize the magnitude with respect to its expected range
//giving a magnitude value of 0.0 to 1.0
normalizedMagnitude = magnitude / (32767 - XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE);
cout << "normalizedMagnitude " << normalizedMagnitude;
}
else //if the controller is in the deadzone zero out the magnitude
{
magnitude = 0.0;
normalizedMagnitude = 0.0;
}
}
You have normalised a state, and it is rather empty. I would assume that you are atleast calling XInputGetState() in your bool function bConnected, however this would probably be called once and hence values would remain the same. Therefore, either in your main, or in your associated function displayed above, you should call the getstate function once, first line in the while loop, so as it runs, the state is updated continously.
I've been working on this for about an hour now and I can't figure out what I'm doing wrong. This is the problem statement for the problem:
Draw a series of circles along one diagonal of a window. The circles
should be different colors and each circle should touch (but not
overlap) the one above and below it. Allow the program user to
determine how many circles are to be drawn.
These are some hints that have been given to me:
You will find the geometry involved in putting geometric elements on
the diagonals easier if you make your window square. Rather than using
getmaxheight() and getmaxwidth(), consider using getmaxheight() for
both dimensions.
Don't forget the Pythagorean theorem when working out distances in
your code such as the length of the diagonal. Keep in mind, though,
that the units on the screen are pixels, so fractions in the
computations are not too useful. This is definitely a place for
integer arithmetic.
Use the number of elements you are going to draw (squares, circles,
etc) to divide up the total length into steps for your loops to work
with.
Use for loops to draw figures when you know how many to draw, and what
size they are to be. Determine the count and size before the loop.
So far this is the code that I have created. Inputting 4 circles only draws 3 on screen, with the third one partially off screen. The circles also do not touch, which makes no sense to me because moving the center of the next circle down and over by the length of the diameter should have to the two circles touching. This is the code I have:
#include <graphics.h>
#include <cmath>
#include <iostream>
using namespace std;
int main()
{
int foreColor;
int diagLength;
int radius,diameter;
int centerX = 0, centerY = 0;
int numCircles; // number of circles.
int width, height; // screen width and height
cout << "Enter number of circles: ";
cin >> numCircles;
width = getmaxheight();
height = getmaxheight();
initwindow(width, height, "Circles");
diagLength = sqrt((width * width) + (height * height));
diameter = diagLength / numCircles;
radius = diameter / 2;
centerX = radius;
centerY = radius;
for (int i = 1; i <= numCircles; i++)
{
foreColor = i % 16; // 0 <= foreColor <= 15
setcolor(foreColor);
setfillstyle(i % 12, foreColor); // Set fill style
fillellipse(centerX, centerY, radius, radius);
centerX = centerX + diameter;
centerY = centerY + diameter;
}
getch(); // pause for user
closegraph();
}
Here's a diagram of what I think you want:
The basic problem comes down to determining
What the diameter D of each circle is
Where the center of each circle is.
The diameter is easy. First calculate the length L of the diagonal using Pythagoras' theorem, then divide by the desired number of circles N. Of course, if you need the radius just divide again by 2.
L = Sqrt(Width * Width + Height * Height);
D = L / N;
The trick to working out the position of the circle centers is to realise that the X are evenly spaced along the X axis, and same with the Y coordinates - so you can work out the distances I've labelled Dx and Dy really easily using the same division:
Dx = Width / N;
Dy = Height / N;
From there the center of each circle is easily calculated:
for (i = 0; i < N; i++)
{
centerX = (Dx / 2) + i * Dx;
centerY = (Dy / 2) + i * Dy;
/* Draw the circle at (centerX, centerY) with diameter D */
}
That's all there is to it!
By the way, if you were wondering why your code was drawing circles further apart than they should be, the reason is because you were adding D to centerX and centerY rather than Dx and Dy.
I've been working on detecting collision between to object in my game. Right now everything tavels vertically, but would like to keep the option for other movement open. It's classic 2d vertical space shooter.
Right now I loop through every object, checking for collisions:
for(std::list<Object*>::iterator iter = mObjectList.begin(); iter != mObjectList.end();) {
Object *m = (*iter);
for(std::list<Object*>::iterator innerIter = ++iter; innerIter != mObjectList.end(); innerIter++ ) {
Object *s = (*innerIter);
if(m->getType() == s->getType()) {
break;
}
if(m->checkCollision(s)) {
m->onCollision(s);
s->onCollision(m);
}
}
}
Here is how I check for a collision:
bool checkCollision(Object *other) {
float radius = mDiameter / 2.f;
float theirRadius = other->getDiameter() / 2.f;
Vector<float> ourMidPoint = getAbsoluteMidPoint();
Vector<float> theirMidPoint = other->getAbsoluteMidPoint();
// If the other object is in between our path on the y axis
if(std::min(getAbsoluteMidPoint().y - radius, getPreviousAbsoluteMidPoint().y - radius) <= theirMidPoint.y &&
theirMidPoint.y <= std::max(getAbsoluteMidPoint().y + radius, getPreviousAbsoluteMidPoint().y + radius)) {
// Get the distance between the midpoints on the x axis
float xd = abs(ourMidPoint.x - theirMidPoint.x);
// If the distance between the two midpoints
// is greater than both of their radii together
// then they are too far away to collide
if(xd > radius+theirRadius) {
return false;
} else {
return true;
}
}
return false;
}
The problem is it will randomly detect collisions correctly, but other times does not detect it at all. It's not the if statement breaking away from the object loop because the objects do have different types. The closer the object is to the top of the screen, the better chance it has of collision getting detected correctly. Closer to the bottom of the screen, the less chance it has of getting detected correctly or even at all. However, these situations don't always occur. The diameter for the objects are massive (10 and 20) to see if that was the problem, but it doesn't help much at all.
EDIT - Updated Code
bool checkCollision(Object *other) {
float radius = mDiameter / 2.f;
float theirRadius = other->getDiameter() / 2.f;
Vector<float> ourMidPoint = getAbsoluteMidPoint();
Vector<float> theirMidPoint = other->getAbsoluteMidPoint();
// Find the distance between the two points from the center of the object
float a = theirMidPoint.x - ourMidPoint.x;
float b = theirMidPoint.y - ourMidPoint.y;
// Find the hypotenues
double c = (a*a)+(b*b);
double radii = pow(radius+theirRadius, 2.f);
// If the distance between the points is less than or equal to the radius
// then the circles intersect
if(c <= radii*radii) {
return true;
} else {
return false;
}
}
Two circular objects collide when the distance between their centers is small enough. You can use the following code to check this:
double distanceSquared =
pow(ourMidPoint.x - theirMidPoint.x, 2.0) +
pow(ourMidPoint.x - theirMidPoint.x, 2.0);
bool haveCollided = (distanceSquared <= pow(radius + theirRadius, 2.0));
In order to check whether there was a collision between two points in time, you can check for collision at the start of the time interval and at the end of it; however, if the objects move very fast, the collision detection can fail (i guess you have encountered this problem for falling objects that have the fastest speed at the bottom of the screen).
The following might make the collision detection more reliable (though still not perfect). Suppose the objects move with constant speed; then, their position is a linear function of time:
our_x(t) = our_x0 + our_vx * t;
our_y(t) = our_y0 + our_vy * t;
their_x(t) = their_x0 + their_vx * t;
their_y(t) = their_y0 + their_vy * t;
Now you can define the (squared) distance between them as a quadratic function of time. Find at which time it assumes its minimum value (i.e. its derivative is 0); if this time belongs to current time interval, calculate the minimum value and check it for collision.
This must be enough to detect collisions almost perfectly; if your application works heavily with free-falling objects, you might want to refine the movement functions to be quadratic:
our_x(t) = our_x0 + our_v0x * t;
our_y(t) = our_y0 + our_v0y * t + g/2 * t^2;
This logic is wrong:
if(std::min(getAbsoluteMidPoint().y - radius, getPreviousAbsoluteMidPoint().y - radius) <= theirMidPoint.y &&
theirMidPoint.y <= std::max(getAbsoluteMidPoint().y + radius, getPreviousAbsoluteMidPoint().y + radius))
{
// then a collision is possible, check x
}
(The logic inside the braces is wrong too, but that should produce false positives, not false negatives.) Checking whether a collision has occurred during a time interval can be tricky; I'd suggest checking for a collision at the present time, and getting that to work first. When you check for a collision (now) you can't check x and y independently, you must look at the distance between the object centers.
EDIT:
The edited code is still not quite right.
// Find the hypotenues
double c = (a*a)+(b*b); // actual hypotenuse squared
double radii = pow(radius+theirRadius, 2.f); // critical hypotenuse squared
if(c <= radii*radii) { // now you compare a distance^2 to a distance^4
return true; // collision
}
It should be either this:
double c2 = (a*a)+(b*b); // actual hypotenuse squared
double r2 = pow(radius+theirRadius, 2.f); // critical hypotenuse squared
if(c2 <= r2) {
return true; // collision
}
or this:
double c2 = (a*a)+(b*b); // actual hypotenuse squared
double c = pow(c2, 0.5); // actual hypotenuse
double r = radius + theirRadius; // critical hypotenuse
if(c <= r) {
return true; // collision
}
Your inner loop needs to start at mObjectList.begin() instead of iter.
The inner loop needs to iterate over the entire list otherwise you miss collision candidates the further you progress in the outer loop.