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.
Related
I am using a custom game engine created by one of my lecturers.
I have 4 inputs that the user can have for a pong game. These inputs work as intended however as soon as the sprite (paddle for pong) touches either the top or the bottom they get stuck there and are unable to move. Adding an else statement saying w_keypress = false; doesn't work.
if (w_keypress)
{
if (player_one->yPos() >= 0 && player_one->yPos() + player_one->height() <= game_height)
{
yspeed_player_one = -500;
s_keypress = false;
y_pos_player_one += yspeed_player_one * (game_time.delta.count() / 1000.f);
player_one->yPos(y_pos_player_one);
std::cout << "keypress w" << std::endl;
}
}
EDIT: The problem can easily be fixed by setting the y value to a y value that doesn't make the sprite interfere with the top or the bottom of the screen.
e.g.
if (player_one-yPos() > game_height)
{
player_one->yPos(game_height - (player_one->height() / 2)
}
else if (player_one->yPos() < 0)
{
player_one->yPos(0 + (player_one->height() / 2)
}
This code detects if the player has gone off the top or the bottom of the screen and then moves the player half of its height down or up depending on which y value you are.
Let's take a closer look at
if (player_one->yPos() >= 0 && player_one->yPos() + player_one->height() <= game_height)
{
yspeed_player_one = -500;
s_keypress = false;
y_pos_player_one += yspeed_player_one * (game_time.delta.count() / 1000.f);
player_one->yPos(y_pos_player_one);
}
bounds check position. If in bounds,
Update position by adding current velocity
Else
Do nothing
The problem is step 1.1 is too naive. If your sprite is zipping along toward a wall at sufficient speed, it can enter or completely pass through the wall as soon as you update its position. The next test of the bounds will trap the sprite because it is out of bounds.
Eg: Sprite is at 1000. Its height is 50 and its velocity per tick is 50. The wall is at 1080.
Step 1 tests 1000 + 50 <= 1080. This is true, so step 1.1 updates the position: 1000 + 50 = 1050. The sprite now occupies 1050 to 1099 and is inside the wall.
On the next button press, Step 1 tests 1050 + 50 <= 1080. This is false, so 2.1 is executed and the sprite does not move.
The test for collision with the wall is effectively performed after the sprite's gone out of bounds and by then it is too late.
You want a function that does something like
TYPE clamp_move(TYPE max_move,
TYPE distance_to_wall)
{
if (max_move < distance_to_wall)
{
max_move = distance_to_wall;
}
return max_move;
}
to prevent over shooting the bounds. Note this is pretty much std::min, so use std::min.
You wind up with
deltapos = std::min(yspeed_player_one * (game_time.delta.count() / 1000.f),
player_one->yPos());
y_pos_player_one -= deltapos;
or
deltapos = std::min(yspeed_player_one * (game_time.delta.count() / 1000.f),
game_height - (player_one->yPos() + player_one->height()));
y_pos_player_one += deltapos;
depending on which way the sprite is moving. Or by catching the overshoot and clamping before the next test.
y_pos_player_one += yspeed_player_one * (game_time.delta.count() / 1000.f);
if (y_pos_player_one <0)
{
y_pos_player_one = 0;
}
else if (y_pos_player_one > game_height - player_one->height())
{
y_pos_player_one = game_height - player_one->height();
}
whichever is easier on your brain.
I have to move a ball in an angle in an open SFML and keep it within the window size (Like the DVD thing), but my current function makes it to the bottom and doesn't "bounce". It slides across the bottom and stops once it reaches the other corner. The initial position is (1,1)
void Bubble::updatePosition() {
if( isTopBottom() ){
do{
_x += .1;
_y += -.2;
}while( !isTopBottom() );
}
else if( isLeftRight() ){
do{
_x += -.1;
_y += .2;
}while( !isLeftRight() );
}
else{
_x += .1;
_y += .2;
}
_bubble.setPosition(_x, _y);
}
the isLeftRight, isTopBottom are bools that check if they have reached the edges
Simple Solution
Use velocities and manipulate those on collision; then, use the velocity to update the position.
Check each edge separately and decide on one relevant velocity component based on that.
e.g. (following your values)
// Positions:
float x = 1.f;
float y = 1.f;
// Velocities:
float vx = 0.1f;
float vy = 0.2f;
// ... then, inside loop:
// Check collisions (and adjust velocity):
if (x < 0.f)
vx = 0.1f;
else if (x > 640.f)
vx = -0.1f;
if (y < 0.f)
vy = 0.2f;
else if (y > 640.f)
vy = -0.2f;
// update position (still inside loop):
x += vx;
y += vy;
Cleaner Solution
This is the same as the simple solution above but, since you tagged SFML, you can use SFML vectors to keep the two components together. Also modified variable names to be more clear. Pulled out the size of the window and the velocity amounts from being hard-coded into the logic as well:
const sf::Vector2f windowSize(640.f, 640.f);
const sf::Vector2f velocityAmount(0.1f, 0.2f);
sf::Vector2f position(1.f, 1.f);
sf::Vector2f velocity = velocityAmount;
// ... then, inside loop:
// Check collisions (and adjust velocity):
if (position.x < 0.f)
velocity.x = velocityAmount.x;
else if (position.x > windowSize.x)
velocity.x = -velocityAmount.x;
if (position.y < 0.f)
velocity.y = velocityAmount.y;
else if (position.y > windowSize.y)
velocity.y = -velocityAmount.y;
// update position (still inside loop):
position += velocity;
You should notice that the velocity is the values that are added to the position on each iteration of the loop and that velocity does not change when it is not considered colliding with an edge.
Initial Problem
The initial problem you had is it always moves in the same direction (towards the bottom-right) if it is not hitting an edge. This means that it'll never be allowed to rise above the bottom edge (or away from right edge).
I've asked this question over at GameDev but got not response so far and this question is a bit time sensitive unfortunately.
I'm pretty sure this is just me doing something stupid or not understanding something that I should but I cannot figure out what is wrong here.
I'm having a problem bouncing a projectile off a sprite, we've been asked to move the projectile using the equations of motions which makes things a little more difficult but as far as I can see what I have should work.
What I'm trying to do is change the angle of the collided projectile depending on which direction it is coming from.
Here is a video that is hopefully not too laggy for you to see what is happening:
Link
When the projectile collides with the left or right hand side of the sprite everything works as expected, it just switches X direction.
When it hit's the top or bottom of the sprite however it doesn't change, it just sort of rolls along the top and the shoots off.
Here is the movement code:
float nX = get_x() + cos(nGetAngle() * 3.14 / 180) * getU() * getT();
float nY = get_y() - sin(nGetAngle() * 3.14 / 180) * getU() * getT() + 0.5 * 9.8 * getT() * getT();
set_world_position(nX, nY);
Where U is initial velocity, T is time and nGetAngle() is the angle in degrees (which is set to radians whenever the angle is set).
Here is my collision for the top of the player:
//if the projectile is colliding in any way with the player sprite
if (projectiles[currProj]->get_y() < player->get_y()) // top of player
{
float vx = cos(projectiles[currProj]->nGetAngle());
float vy = sin(projectiles[currProj]->nGetAngle());
float newAngle = atan2(-vy, vx) * 180 / 3.14;
projectiles[currProj]->nSetAngle(newAngle);
projectiles[currProj]->set_world_position_y(player->get_y() - projectiles[currProj]->get_height() - 1);
}
and here is my collision for the left of the player:
else if (projectiles[currProj]->get_x() < player->get_x()) // left of player
{
projectiles[currProj]->set_world_position_x(player->get_x() - projectiles[currProj]->get_width());
float vx = cos(projectiles[currProj]->nGetAngle());
float vy = sin(projectiles[currProj]->nGetAngle());
float newAngle = atan2(vy, -vx) * 180 / 3.14;
projectiles[currProj]->nSetAngle(newAngle);
}
The left side collision works, the top does not and I have no idea why.
If necessary I can post the entire project somewhere.
Full collision code for player:
void Game::playerCollision()
{
if (projectiles[currProj]->bb_collision(player))
{
if (projectiles[currProj]->get_y() < player->get_y()) // top of player
{
float vx = cos(projectiles[currProj]->nGetAngle());
float vy = sin(projectiles[currProj]->nGetAngle());
float newAngle = atan2(-vy, vx) * 180 / 3.14;
projectiles[currProj]->nSetAngle(newAngle);
projectiles[currProj]->set_world_position_y(player->get_y() - projectiles[currProj]->get_height() - 1);
}
else if (projectiles[currProj]->get_y() + projectiles[currProj]->get_height() > player->get_y() + player->get_height() + 1) // bottom of player
{
projectiles[currProj]->set_world_position_y(player->get_y() + player->get_height());
float vx = cos(projectiles[currProj]->nGetAngle());
float vy = sin(projectiles[currProj]->nGetAngle());
float newAngle = atan2(-vy, vx) * 180 / 3.14;
projectiles[currProj]->nSetAngle(newAngle);
}
else if (projectiles[currProj]->get_x() < player->get_x()) // left of player
{
projectiles[currProj]->set_world_position_x(player->get_x() - projectiles[currProj]->get_width());
float vx = cos(projectiles[currProj]->nGetAngle());
float vy = sin(projectiles[currProj]->nGetAngle());
float newAngle = atan2(vy, -vx) * 180 / 3.14;
projectiles[currProj]->nSetAngle(newAngle);
}
else if (projectiles[currProj]->get_x() > player->get_x()) // right of player
{
projectiles[currProj]->set_world_position_x(player->get_x() + player->get_width());
float vx = cos(projectiles[currProj]->nGetAngle());
float vy = sin(projectiles[currProj]->nGetAngle());
float newAngle = atan2(vy, -vx) * 180 / 3.14;
projectiles[currProj]->nSetAngle(newAngle);
}
}
}
I think your collision detection is not sufficient. without knowing your representation in detail
you do not check where the projectile (pr) came from. a collision top left within the player (pl) might have entered through the top or from the left
you do not bounce the pr immediately, you just alter the direction. depending on the entry depth it might not be able to exit with the next iteration. this happens especially on the top where the pr accelerates downwards but slows down upwards.
so you must
detect the entry surface (determines angle)
and most important rebounce immediately
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.
Here is what I'm trying to do. I'm trying to make a bullet out of the center of the screen. I have an x and y rotation angle. The problem is the Y (which is modified by rotation on the x) is really not working as intended. Here is what I have.
float yrotrad, xrotrad;
yrotrad = (Camera.roty / 180.0f * 3.141592654f);
xrotrad = (Camera.rotx / 180.0f * 3.141592654f);
Vertex3f Pos;
// get camera position
pls.x = Camera.x;
pls.y = Camera.y;
pls.z = Camera.z;
for(float i = 0; i < 60; i++)
{
//add the rotation vector
pls.x += float(sin(yrotrad)) ;
pls.z -= float(cos(yrotrad)) ;
pls.y += float(sin(twopi - xrotrad));
//translate camera coords to cube coords
Pos.x = ceil(pls.x / 3);
Pos.y = ceil((pls.y) / 3);
Pos.z = ceil(pls.z / 3);
if(!CubeIsEmpty(Pos.x,Pos.y,Pos.z)) //remove first cube that made contact
{
delete GetCube(Pos.x,Pos.y,Pos.z);
SetCube(0,Pos.x,Pos.y,Pos.z);
return;
}
}
This is almost identical to how I move the player, I add the directional vector to the camera then find which cube the player is on. If I remove the pls.y += float(sin(twopi - xrotrad)); then I clearly see that on the X and Z, everything is pointing as it should. When I add pls.y += float(sin(twopi - xrotrad)); then it almost works, but not quite, what I observed from rendering out spheres of the trajector is that the furthur up or down I look, the more offset it becomes rather than stay alligned to the camera's center. What am I doing wrong?
Thanks
What basically happens is very difficult to explain, I'd expect the bullet at time 0 to always be at the center of the screen, but it behaves oddly. If i'm looking straight at the horizon to +- 20 degrees upward its fine but then it starts not following any more.
I set up my matrix like this:
void CCubeGame::SetCameraMatrix()
{
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glRotatef(Camera.rotx,1,0,0);
glRotatef(Camera.roty,0,1,0);
glRotatef(Camera.rotz,0,0,1);
glTranslatef(-Camera.x , -Camera.y,-Camera.z );
}
and change the angle like this:
void CCubeGame::MouseMove(int x, int y)
{
if(!isTrapped)
return;
int diffx = x-lastMouse.x;
int diffy = y-lastMouse.y;
lastMouse.x = x;
lastMouse.y = y;
Camera.rotx += (float) diffy * 0.2;
Camera.roty += (float) diffx * 0.2;
if(Camera.rotx > 90)
{
Camera.rotx = 90;
}
if(Camera.rotx < -90)
{
Camera.rotx = -90;
}
if(isTrapped)
if (fabs(ScreenDimensions.x/2 - x) > 1 || fabs(ScreenDimensions.y/2 - y) > 1) {
resetPointer();
}
}
You need to scale X and Z by cos(xradrot). (In other words, multiply by cos(xradrot)).
Imagine you're pointing straight down the Z axis but looking straight up. You don't want the bullet to shoot down the Z axis at all, this is why you need to scale it. (It's basically the same thing that you're doing between X and Z, but now doing it on the XZ vector and Y.)
pls.x += float(sin(yrotrad)*cos(xrotrad)) ;
pls.z -= float(cos(yrotrad)*cos(xrotrad)) ;
pls.y += float(sin(twopi - xrotrad));