• Making a ball bounce
    11 replies, posted
Okay, I'm using python and pygame, and I'm really struggling to get a ball bouncing. Here is my code so far: [CODE]import pygame, sys, random, math width = 640 height = 400 screen = pygame.display.set_mode((width,height)) clock = pygame.time.Clock() class Ball: def __init__(self, x, y): self.x = x self.y = y self.dx = 0 self.dy = 0 self.size = 10 self.color = (random.randint(0,255),random.randint(0,255),random.randint(0,255)) self.gravity = 1 def think(self): self.x += self.dx self.y += self.dy if self.y + self.size < height: self.y += self.gravity self.gravity += 1 if self.y + self.size >= height: self.gravity = -(self.gravity) def draw(self): pygame.draw.circle(screen, self.color, (self.x, self.y), self.size) running = True balls = [] while running: for event in pygame.event.get(): if event.type == pygame.QUIT: running = False pygame.quit() sys.exit() if event.type == pygame.MOUSEBUTTONDOWN: x,y = event.pos balls.append(Ball(x,y)) screen.fill((255,255,255)) for i in balls: i.think() i.draw() pygame.display.flip() clock.tick(40) [/CODE] The code generates a ball which will fall to the ground, but when it reaches the bottom, it just stops... Any help would be very much welcomed, thank you.
[quote][code] if self.y + self.size < height: self.y += self.gravity self.gravity += 1 if self.y + self.size >= height: self.gravity = -(self.gravity)[/code][/quote] If I'm reading this correctly, when the ball hasn't hit the floor move the ball down grav amount and increase gravity once the ball has hit the floor, reverse gravity but you're not moving the ball when it hits the floor, so on the next think it's still in the floor and gravity is reversed again, and again....
Reverse velocity, not gravity. Otherwise the ball will accelerate upward and fly away into the sky, rather than move upward but eventually fall back down again.
[QUOTE=Wyzard;33336276]Reverse velocity, not gravity. Otherwise the ball will accelerate upward and fly away into the sky, rather than move upward but eventually fall back down again.[/QUOTE] He named his variables wrong. The 'gravity' variable is actually velocity. Gravitational acceleration in his program is a constant value of 1. The real problem is that he doesn't adjust the ball's y-coordinate upon collision, so it just gets 'stuck' in the ground. @OP: after reversing velocity like you do, you have to set self.y to height-self.size-1.
Yeah, I managed to get it to bounce, in a sort of glitchy way, I may upload a GIF of how it looks later. The ball bounces higher with each bounce until it eventually bounces off-screen. There's another problem also, if I keep spawning balls they start to fall throught the botton of the screen, is this a problem with my programming or Python ?
[QUOTE=joyenusi;33345808]Yeah, I managed to get it to bounce, in a sort of glitchy way, I may upload a GIF of how it looks later. The ball bounces higher with each bounce until it eventually bounces off-screen. There's another problem also, if I keep spawning balls they start to fall throught the botton of the screen, is this a problem with my programming or Python ?[/QUOTE] With the programming - I had some fun with your code and that doesn't happens to me.
[QUOTE=Ehmmett;33354036]Taking a guess here. guessing it bounces higher and higher because you're constantly adding gravity. also guessing it falls through because it's moving too fast.[/QUOTE] That's how gravity works, it's a constant force. I can't see any reason why collision detection should fail either, but he hasn't posted the latest code.
Yeah, I managed to fix it, can't find a good tool to screen cap. into GIF, but here's the latest code. I also managed to make it vaguely realistic. [CODE] import pygame, sys, random, math width = 640 height = 400 screen = pygame.display.set_mode((width,height)) clock = pygame.time.Clock() class Ball: def __init__(self, x, y, energy): self.x = x self.y = y self.dx = energy self.dy = 0 self.size = random.randint(5,20) self.color = (random.randint(0,255),random.randint(0,255),random.randint(0,255)) self.pos = [x,y] self.gravity = 1 def think(self): self.x += self.dx self.y += self.dy if self.x + self.size >= width: self.dx = -(self.dx) self.dx += 1 if self.x <= 0: self.dx = -(self.dx) self.dx -=1 if self.y <= 0: self.gravity = -(self.gravity) if self.y + self.size < height: self.y += self.gravity if self.gravity >= 0: self.gravity += 1 if self.gravity < 0: self.gravity += 1 if self.y + self.size >= height: self.y -= (self.y-height) +(self.size+1) self.gravity -= 10 self.gravity = -(self.gravity) if self.dx>0: self.dx-=1 if self.dx<0: self.dx+=1 def draw(self): pygame.draw.circle(screen, self.color, (self.x, self.y), self.size) running = True balls = [] energy = 0 mousedown = False while running: for event in pygame.event.get(): if event.type == pygame.QUIT: running = False pygame.quit() sys.exit() if event.type == pygame.MOUSEBUTTONDOWN: mousedown = True x,y = event.pos if event.type == pygame.MOUSEBUTTONUP: balls.append(Ball(x,y, energy)) mousedown = False if mousedown == True: energy += 1 if mousedown == False: energy = 0 ## Attempt at gravity ## if len(balls) == 2: ## direction = [balls[0].x - balls[1].x, balls[0].y - balls [1].y] ## balls[0].x += direction[0] * 0.5 ## balls[0].y += direction[1] * 0.5 screen.fill((255,255,255)) pygame.draw.rect(screen, (255,0,0), (10,height - energy *10-5, 20, energy*10)) for i in balls: i.think() i.draw() ## Removing the balls when they are off-screen, commented out as sometimes if the self.dx or dy values will force the ball off-screen before changing direction ## for i in balls: ## if i.x > width or i.x < 0 or i.y < 0 or i.y > height +10: ## balls.remove(i) print(len(balls)) pygame.display.flip() clock.tick(40) [/CODE] The gravity wasn't working properly because of the way I was handling the negation of the gravity: [CODE]self.gravity = -(self.gravity)[/CODE] I'm not sure, I fixed it though! :D I am also trying to learn C# but it seems a bit more complicated, anyone recommend a fun way to learn C#, C++ and XNA ? I attempted also to have gravitation between the balls but that's another story. Have a play around with the code and tell me what you think.
You've went backwards, the way you have it written right now doesn't make sense. dx and dy seem to be velocity, however you're still adding 'gravity' directly to the y-coordinate. You shouldn't do this. Gravity is a [i]constant acceleration[/i], and acceleration is the [i]second[/i] derivative of position. Replace "self.y += self.gravity" with "self.[b]d[/b]y += self.gravity" and replace "self.gravity = -(self.gravity)" with "self.dy = -self.dy" everywhere it occurs. Don't modify gravity anywhere.
My Messy C++ Code from when I did it. [code]#include <SFML/Graphics.hpp> #include <math.h> #include <iostream> #include <string> #include <sstream> int main() { int game_w = 800; int game_h = 600; sf::RenderWindow window(sf::VideoMode(game_w, game_h, 32), "Game-Physics"); window.SetFramerateLimit(60); float gravity = 1.0; float restitution = 0.6; float friction = 0.9; int ball_w = 32; int ball_h = 32; float ball_x = (game_w /2) - (ball_w /2); float ball_y = (game_h /2) - (ball_h /2); float grass_x = 0; float grass_y = 568; float sun_x = game_w - 128; float sun_y = 0; float vel_x = 0; float vel_y = 0; float pos_x = ball_x; float pos_y = ball_y; float old_x = ball_x; float old_y = ball_y; int radius = ball_w / 2; bool dragging = false; bool ball_draggable = false; int up = 0; int down = 0; int right = 0; int left = -0;//Why was this -0 typo? bool drawFps = false; float fpsupdate= 0; int fpstotal = 0; const sf::Input& Input = window.GetInput(); unsigned int MouseX = Input.GetMouseX(); unsigned int MouseY = Input.GetMouseY(); //Load the sprite image from a file sf::Image imageSun; if (!imageSun.LoadFromFile("images/sun_128_128.png")) return EXIT_FAILURE; sf::Image imageGrass; if(!imageGrass.LoadFromFile("images/grass_800_32.png")) return EXIT_FAILURE; sf::Image imageBall; if(!imageBall.LoadFromFile("images/ball_32_32.png")) return EXIT_FAILURE; // spriteSun sf::Sprite spriteSun(imageSun); spriteSun.SetColor(sf::Color(255, 255, 255, 255)); spriteSun.SetPosition(sun_x, sun_y); spriteSun.SetScale(1.f, 1.f); // spriteSun // spriteGrass sf::Sprite spriteGrass(imageGrass); spriteGrass.SetColor(sf::Color(255,255,255,255)); spriteGrass.SetPosition(grass_x, grass_y); spriteGrass.SetScale(1.f, 1.f); // spriteGrass // spriteBall sf::Sprite spriteBall(imageBall); spriteBall.SetColor(sf::Color(255,255,255,255)); spriteBall.SetPosition(ball_x, ball_y); spriteBall.SetScale(1.f,1.f); // spriteBall //Create String to Draw for fps sf::Text Debug_Fps("Fps:", sf::Font::GetDefaultFont(), 30); while (window.IsOpened()) { sf::Event Event; while (window.PollEvent(Event)) { if (Event.Type == sf::Event::Closed) window.Close(); if ((Event.Type == sf::Event::KeyPressed) && (Event.Key.Code == sf::Key::F1)) { sf::Image Screenshot; Screenshot.CopyScreen(window); Screenshot.SaveToFile("screenshot.png"); } if ((Event.Type == sf::Event::KeyPressed) && (Event.Key.Code == sf::Key::F2)) { drawFps = !drawFps; } } float ElapsedTime = window.GetFrameTime(); unsigned int MouseXl = MouseX; unsigned int MouseYl = MouseY; MouseX = Input.GetMouseX(); MouseY = Input.GetMouseY(); if (!dragging) { vel_y = vel_y + gravity;// * ElapsedTime); pos_x = pos_x + vel_x; pos_y = pos_y + vel_y; if (pos_y + radius > grass_y) { pos_y = grass_y - radius; vel_y = vel_y * -restitution;// * ElapsedTime); vel_x = vel_x * friction;// * ElapsedTime); } if (pos_x + radius > game_w) { pos_x = game_w - radius; vel_x = vel_x * -restitution;// * ElapsedTime); } if (pos_x - radius < 0) { pos_x = radius; vel_x = vel_x * -restitution;// * ElapsedTime); } } if (sqrt(pow((MouseX - ball_x),2) + pow((MouseY - ball_y),2)) <= radius) { ball_draggable = true; } else { if (!dragging) { ball_draggable = false; } } if (Input.IsMouseButtonDown(sf::Mouse::Left) and ball_draggable) { dragging = true; if (MouseX < MouseXl) { left = MouseXl - MouseX; ball_x = ball_x - left; } else { if (MouseX > MouseXl) { right = MouseX - MouseXl; ball_x = ball_x + right; } } if (MouseY < MouseYl) { up = MouseYl - MouseY; ball_y = ball_y - up; } else { if (MouseY > MouseYl) { down = MouseY - MouseYl; ball_y = ball_y + down; } } } else { dragging = false; } if (!dragging) { ball_x = pos_x; ball_y = pos_y; } else { old_x = pos_x; old_y = pos_y; pos_x = ball_x; pos_y = ball_y; vel_x = (pos_x - old_x) / 2; vel_y = (pos_y - old_y) / 2; } spriteBall.SetPosition(ball_x - ball_w/2, ball_y - ball_h/2); window.Clear(sf::Color(001, 191, 254)); window.Draw(spriteSun); window.Draw(spriteGrass); window.Draw(spriteBall); //Calculate FPS fpsupdate = fpsupdate + ElapsedTime; fpstotal++; int FPS; if (fpsupdate>=1) { FPS = fpstotal; fpstotal = 0; fpsupdate = 0; std::ostringstream fpsString; fpsString << "Fps:" << FPS; Debug_Fps.SetString(fpsString.str()); } if (drawFps) { window.Draw(Debug_Fps); } window.Display(); } return EXIT_SUCCESS; }[/code]
go outside --> bounce ball --> film it with camera --> import video on computer --> watch video and enjoy
Sorry, you need to Log In to post a reply to this thread.