Why is my pygame ball not accelerating when using classes? - python

so I am new to pygame/python/coding and first time using classes. I already made a working pygame program of a ball bouncing around and accelerating with gravity. However, now that I use classes, the ball only seems to be moving at a constant speed. Please tell me what I am doing wrong and how
I should change my code. Thanks in advance!
import pygame, sys
pygame.init()
red = (255,0,0)
black = (0,0,0)
white = (255,255,255)
blue = (0,0,255)
pygame.mouse.set_visible(0)
clock = pygame.time.Clock()
displaySize = (800,600)
screen = pygame.display.set_mode(displaySize)
g = 30
dt = 0.05
class ball:
def __init__(self, x, y, vx, vy, r,ax,ay, color):
dt = 0.05
Cd = 0.01
m = 5
self.ay = ay
self.ax = ax
self.x = x
self.y = y
self.r = r
self.color = color
self.vx = vx
self.vy = vx
def update(self, x, y):
x, y = physics()
pygame.draw.circle(screen, self.color, (int(round(x)),int(round(y))), self.r)
pygame.display.update()
def physics(self):
self.Dy = Cd*self.vy*abs(self.vy)
self.Dx = Cd*self.vx*abs(self.vx)
self.Fy = self.m*g - self.Dy
self.Fx = -self.Dx
self.ay = self.Fy/m
self.ax = self.Fx/m
self.vy += self.ay*dt
self.vx += self.ax*dt
self.x +=self.vx*dt
self.y +=self.vy*dt
return self.x, self.y
one = ball(100,100,0,0,0,0,30,red)
while 1:
clock.tick(30)
screen.fill(blue)
one.update()
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()

self.vx and self.vy should be within the constructor method instead of the physics method, otherwise their values will be set back to 50 and 0 each time the physics method is called within the game loop, resulting in a constant speed.

Related

How do I make my object not leave a trail while moving in Pygame?

This is the image Okay so I am not able to figure out how to delete the previous render of the object every frame from screen so as to not leave a trail behind, in other words I want the object to have the appearance of Movement rather than leaving a bunch of red squares behind it. Image has been inserted for understanding what I mean by "leaving a trail" The following is my code:-
import pygame
import random
WIDTH = 1000
ROWS = 25
WIN = pygame.display.set_mode((WIDTH,WIDTH))
pygame.display.set_caption("Particle Physics")
clock = pygame.time.Clock()
RED = (255, 0, 0)
GRAVITY = 2
wid = 5
class Square:
def __init__(self,x,y,width,color,vel_x,vel_y):
self.x = x
self.y = y
self.width = width
self.color = color
self.velx = vel_x
self.vely = vel_y
def draw(self):
pygame.draw.rect(WIN, self.color, (self.x, self.y, self.width, self.width))
def move(self):
self.x += self.velx
self.y += self.vely
self.vely += GRAVITY[enter image description here][1]
def particles():
pass
def main():
run = True
a,b = 500,300
c = random.randint(-20,20)
d = random.randint(-50,0)
square = Square(a,b,wid, RED, c, d)
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
square.move()
square.draw()
pygame.display.update()
pygame.time.delay(100)
#clock.tick(60)
pygame.quit()
main()
In the main function add the set background color at the start so as to delete the previous frames.
def main()
...
while run
....
screen.fill((255,255,255))
...

Pygame how to set up collision in my game, I dont understand it

I know this question has been asked multiple times but I just cant wrap my head around the concept of collisions, now I know I am asking a lot but I will appreciate it greatly!, All I ask is someone to add collisions to my game and explain each step so I can try to understand whats going on,I tried youtube videos and they aren't explained very well or I just get overwhelmed which is what I feel right now, So I just want the player to stop when hitting the tree, something as simple as that! And if possible I want it to get the collision from a list for example I would have trees and other images in the list.
import pygame
import sys
import math
from pygame.locals import *
import tiles_list
pygame.init()
display_w = 800
display_h = 600
window = pygame.display.set_mode((display_w, display_h), HWSURFACE | DOUBLEBUF | RESIZABLE)
pygame.display.set_icon(pygame.image.load("game_icon.png"))
pygame.display.set_caption("Work in progress")
clock = pygame.time.Clock()
background = pygame.image.load("background.png")
class Player(object):
"""The controllable player in game"""
def __init__(self, x, y, width, height, speed):
self.x = x
self.y = y
self.width = width
self.height = height
self.image = pygame.image.load("sprite_sheet.png")
self.speed = speed
self.timer = 0
self.frames = 1
self.direction = 2
def animation(self):
x_coord = 50 * self.frames
y_coord = 50 * self.direction
self.character = self.image.subsurface(x_coord, y_coord, self.width, self.height)
self.timer += 0
if self.timer >= 10:
self.timer = 0
self.frames += 1
if self.frames >= 9:
self.frames = 1
def draw(self):
window.blit(self.character, (self.x, self.y))
self.rect = pygame.Rect(self.x, self.y, self.width, self.height)
def movement(self):
self.keys = pygame.key.get_pressed()
if self.keys[pygame.K_LEFT] or self.keys[pygame.K_a]:
self.x -= self.speed
self.direction = 1
self.timer += 2
if self.keys[pygame.K_RIGHT] or self.keys[pygame.K_d]:
self.x += self.speed
self.direction = 3
self.timer += 2
if self.keys[pygame.K_UP] or self.keys[pygame.K_w]:
self.y -= self.speed
self.direction = 0
self.timer += 2
if self.keys[pygame.K_DOWN] or self.keys[pygame.K_s]:
self.y += self.speed
self.direction = 2
self.timer += 2
if self.x >= 780:
self.x = 780
self.frames = 0
if self.y >= 555:
self.y = 555
self.frames = 0
if self.x <= 0:
self.x = 0
self.frames = 0
if self.y <= 0:
self.y = 0
self.frames = 0
player = Player(400, 300, 50, 50, 4.5)
class Tree:
def __init__(self, x, y, width, height):
self.x = x
self.y = y
self.width = width
self.height = height
self.image = (
pygame.transform.scale(tiles_list.tiles.subsurface(pygame.Rect(99, 147, self.width, self.height)),
(62, 82)))
def draw(self):
window.blit(self.image, (self.x, self.y))
tree = Tree(348, 300, 42, 62)
running = True
while running:
window.blit(background, (0, 0))
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
if event.type == VIDEORESIZE:
screen = pygame.display.set_mode((event.w, event.h), RESIZABLE)
elif event.type == pygame.KEYUP:
if player.keys:
player.frames = 0
dist_x = math.hypot(tree.x - player.x)
dist_y = math.hypot(tree.y - player.y) # Does nothing except calculate the distence
print("distx", dist_x, "disty", dist_y)
tree.draw()
player.movement()
player.animation()
player.draw()
clock.tick(60)
pygame.display.flip()
Fist of all I suggest that you start to try to implement the collision test, before you ask a question. If you get stuck, you can ask a question about the code that gets you stuck.
Anyway I'll give you some hints.
In common the collision detection is based on pygame.Rect. For instance create a rectangle from pygaem.Surface objects player.image and tree.image by get_rect(). Evaluate if the 2 objects are colliding by colliderect():
player_rect = player.image.get_rect(topleft = (player.x, player.y))
tree_rect = tree.image.get_rect(topleft = (tree.x, tree.y))
if player_rect.colliderect(tree_rect):
print("hit")
If you "just want the player to stop when hitting the tree", the store the position of the player and move the player. After that evaluate if the player hits the tree. If the player is hitting, then restore the position:
while running:
# [...]
# store position of player
player_pos = (player.x, player.y)
# move player
player.movement()
player_rect = player.image.get_rect(topleft = (player.x, player.y))
tree_rect = tree.image.get_rect(topleft = (tree.x, tree.y))
# test collision of player and tree
if player_rect.colliderect(tree_rect):
# restore player position
(player.x, player.y) = player_pos
See also How to detect collisions between two rectangular objects or images in pygame
I am willing to explain you the process of collisions.
In your game you have two objects - the tree and the player. So, you can say that if the x and y coordinates of the player are the same , then there occurs a collisions. You can use this concept and create variables for detecting collisions.
Thanks

Why does my second called ball in pygame flicker?

I made a class Ball with two instances: ballOne and ballTwo. When I call ballTwo.update() and then ballOne.update(), the last called ball sometimes disappears on some frames, like it sometimes flickers on and off. Can someone please help?
import pygame, sys
pygame.init()
red = (255,0,0)
black = (0,0,0)
white = (255,255,255)
blue = (0,0,255)
green = (0,255,0)
pygame.mouse.set_visible(0)
clock = pygame.time.Clock()
displaySize = (800,600)
screen = pygame.display.set_mode(displaySize)
g = 50
dt = 0.05
Cd = 0.01
m = 5
class ball:
def __init__(self, x, y, vx, vy, r,ax,ay, color):
self.Fx = 0
self.Fy = 0
self.Dx = 0
self.Dy = 0
self.ay = ay
self.ax = ax
self.x = x
self.y = y
self.r = r
self.color = color
self.vx = vx
self.vy = vy
def update(self):
self.x, self.y = self.physics()
pygame.draw.circle(screen, self.color, (int(round(self.x)),int(round(self.y))), self.r)
pygame.display.update()
def physics(self):
self.x +=self.vx*dt
self.y +=self.vy*dt
self.vy += self.ay*dt
self.vx += self.ax*dt
self.ay = self.Fy/m
self.ax = self.Fx/m
self.Fy = m*g - self.Dy
self.Fx = -self.Dx
self.Dy = Cd*self.vy*abs(self.vy)
self.Dx = Cd*self.vx*abs(self.vx)
if self.x <= self.r:
self.vx *= -0.7
if self.x >= displaySize[0]- self.r:
self.vx *= -0.7
if self.y <= self.r:
self.vy *= -0.7
if self.y >= displaySize[1] - self.r:
self.vy *= -0.7
return self.x, self.y
ballOne = ball(100,100,50,-100,30,0,0,red)
ballTwo = ball(500,500,-75,0,45,0,0,green)
while 1:
clock.tick(60)
screen.fill(blue)
ballTwo.update()
ballOne.update()
You're calling pygame.display.update() for each object, which is causing the flickering. Remove those calls and use pygame.display.flip() in the game loop.
I also recommend separating the updating and drawing of objects. Later, you may want to draw the objects in a different order than you want to update the objects.
A typical game loop does the following:
Process events
Calculate new position of objects
Draw next frame
Display frame
In Python, the game loop might look like this:
objects = [ballOne, ballTwo]
while True:
# 1. handle events
# 2. update objects
for object in objects:
object.update()
# 3. draw frame
screen.fill(blue)
for object in objects:
object.draw()
# 4. display frame
pygame.display.flip()
clock.tick(60)

Adding a speed vector to a position vector only works with integers

In using this script for simple vector math, I can add the speed vector when the values are integers such as 1 and 2, but when the speed vector is initialized with a float .5 there is no movement. As far as I can tell python doesn't require declaring float or int, but I have a feeling that the resulting number is being truncated
import pygame
from pygame.locals import *
pygame.init()
screen = pygame.display.set_mode((800, 600))
background = pygame.Surface(screen.get_size())
rectangle = pygame.Rect(65, 45, 50, 50)
class Vector2D:
def __init__(self, x, y):
self.x = x
self.y = y
def __iadd__(self, vector):
self.x += vector.x
self.y += vector.y
return self
def __isub__(self, vector):
self.x -= vector.x
self.y -= vector.y
return self
def copy(self, vector):
self.x = vector.x
self.y = vector.y
speed = Vector2D(.5, .5)
going = True
while going:
#Handle Input Events
for event in pygame.event.get():
if event.type == QUIT:
going = False
elif event.type == KEYDOWN and event.key == K_ESCAPE:
going = False
rectangle.left += speed.x
rectangle.top += speed.y
#Draw Everything
screen.blit(background, (0, 0))
pygame.draw.rect(screen, (255, 255, 255), rectangle, 1)
pygame.display.flip()
pygame.quit()
Since there is nothing like half a pixel, the Rect class truncates the decimal of the floats you are adding.
So keeping track of the position within the Vector2D class is a better Idea:
class Vector2D:
def __init__(self, x, y, vx, vy):
self.x = x
self.y = y
self.vx = vx
self.vy = vy
def update(self):
self.x += self.vx
self.y += self.vy
def copyto(self, rect):
rect.x = int(round(self.x,0))
rect.y = int(round(self.y,0))
speed = Vector2D(100, 100, .5, .5)
And have this:
speed.update()
speed.copyto(rectangle)
In place of:
rectangle.left += speed.x
rectangle.top += speed.y

Pygame particle effects

So I have this mini particle effect that produces circles that move up. I want to make it look like smoke. I'm having a lot of problems though.
First off I want to make it recursive so that when the particle reaches the top it
returns to its original spot and begins again, what I have works a little but not exactly.
The other thing is I don't feel like it really looks like smoke can anyone suggest changes to make it look better?
Also I want to add this into my game, how would I make this callable for my game so I could just call particle and give it a location and it appears there. Can anyone help with any of this?
My Code
import pygame,random
from pygame.locals import *
xmax = 1000 #width of window
ymax = 600 #height of window
class Particle():
def __init__(self, x, y, dx, dy, col):
self.x = x
self.y = y
self.col = col
self.ry = y
self.rx = x
self.dx = dx
self.dy = dy
def move(self):
if self.y >= 10:
if self.dy < 0:
self.dy = -self.dy
self.ry -= self.dy
self.y = int(self.ry + 0.5)
self.dy -= .1
if self.y < 1:
self.y += 500
def main():
pygame.init()
screen = pygame.display.set_mode((xmax,ymax))
white = (255, 255, 255)
black = (0,0,0)
grey = (128,128,128)
particles = []
for part in range(25):
if part % 2 > 0: col = black
else: col = grey
particles.append( Particle(random.randint(500, 530), random.randint(0, 500), 0, 0, col))
exitflag = False
while not exitflag:
for event in pygame.event.get():
if event.type == QUIT:
exitflag = True
elif event.type == KEYDOWN:
if event.key == K_ESCAPE:
exitflag = True
screen.fill(white)
for p in particles:
p.move()
pygame.draw.circle(screen, p.col, (p.x, p.y), 8)
pygame.display.flip()
pygame.quit()
if __name__ == "__main__":
main()
I have made some major edits to your code. For starters, I cleaned up your class a great deal. Let's start with the arguments and the __init__ function.
First of all, instead of going down 500 to reset, particles go to the location that was set as their starting point. It's location that it starts out in is now chosen randomly in the __init__ function rather than in the game. I got rid of some of your unnecessary arguments as well.
In the move function of your class, I simplified quite a bit. In order for the particle to detect if it should reset, it simply sees if it's above 0. The going up is just a simple decrease of the y by 1. A change I added in is that the x changes randomly and goes to the right and left. This will make the smoke look a lot better / more realistic.
I didn't make many changes to the rest of your code. I changed your calling of the Particle class to fit the new arguments. I made a ton more particles, once again for visual effect. I also massively decreased the size of the circles drawn for (can you guess it?) visual effect. I added in a clock as well to keep the particles from going at supersonic speed.
Here is the final code. I hope you like it.
import pygame,random
from pygame.locals import *
xmax = 1000 #width of window
ymax = 600 #height of window
class Particle():
def __init__(self, startx, starty, col):
self.x = startx
self.y = random.randint(0, starty)
self.col = col
self.sx = startx
self.sy = starty
def move(self):
if self.y < 0:
self.x=self.sx
self.y=self.sy
else:
self.y-=1
self.x+=random.randint(-2, 2)
def main():
pygame.init()
screen = pygame.display.set_mode((xmax,ymax))
white = (255, 255, 255)
black = (0,0,0)
grey = (128,128,128)
clock=pygame.time.Clock()
particles = []
for part in range(300):
if part % 2 > 0: col = black
else: col = grey
particles.append( Particle(515, 500, col) )
exitflag = False
while not exitflag:
for event in pygame.event.get():
if event.type == QUIT:
exitflag = True
elif event.type == KEYDOWN:
if event.key == K_ESCAPE:
exitflag = True
screen.fill(white)
for p in particles:
p.move()
pygame.draw.circle(screen, p.col, (p.x, p.y), 2)
pygame.display.flip()
clock.tick(50)
pygame.quit()
if __name__ == "__main__":
main()
update
In order to add particles into your code, just do what you did in the code above. It works fine. If you wanted to do something to show the smoke starting, just put a pause time into your arguments and inhibit the movement of the smoke until that amount of time has passed. New class with that added in:
class Particle():
def __init__(self, startx, starty, col, pause):
self.x = startx
self.y = starty
self.col = col
self.sx = startx
self.sy = starty
self.pause = pause
def move(self):
if self.pause==0:
if self.y < 0:
self.x=self.sx
self.y=self.sy
else:
self.y-=1
self.x+=random.randint(-2, 2)
else:
self.pause-=1
The code you will need to create new particles:
for part in range(1, A):
if part % 2 > 0: col = black
else: col = grey
particles.append( Particle(515, B, col, round(B*part/A)) )
A and B are variables (I reccomend around 300 for A, B will be the Y value)
The new code will make particles spawn at the start location, and rise continously with no breaks. Hope you enjoy.
I have made a lot of changes to your code, especially in the Particle class.
Although, there are rather puzzling things in this code, it will be more flexible than your current code.
Here,
I have quite literately rewritten you Particle class.
Other than changing the __init__, to take many arguments (7 to be exact),
I have used trigonometry and the math module to move the particle, making it easier to manage (if you are good at math!). And I have also added bounce and draw methods to the Particle, and made the code more readable.
Just like #PygameNerd, I have added a clock, for restricting the max fps.
I haven't changed any event handling, but have used the bounce and draw funcs in the for p in particles: loop.
import pygame, random, math
def radians(degrees):
return degrees*math.pi/180
class Particle:
def __init__(self, (x, y), radius, speed, angle, colour, surface):
self.x = x
self.y = y
self.speed = speed
self.angle = angle
self.radius = 3
self.surface = surface
self.colour = colour
self.rect = pygame.draw.circle(surface,(255,255,0),
(int(round(x,0)),
int(round(y,0))),
self.radius)
def move(self):
""" Update speed and position based on speed, angle """
# for constant change in position values.
self.x += math.sin(self.angle) * self.speed
self.y -= math.cos(self.angle) * self.speed
# pygame.rect likes int arguments for x and y
self.rect.x = int(round(self.x))
self.rect.y = int(round(self.y))
def draw(self):
""" Draw the particle on screen"""
pygame.draw.circle(self.surface,self.colour,self.rect.center,self.radius)
def bounce(self):
""" Tests whether a particle has hit the boundary of the environment """
if self.x > self.surface.get_width() - self.radius: # right
self.x = 2*(self.surface.get_width() - self.radius) - self.x
self.angle = - self.angle
elif self.x < self.radius: # left
self.x = 2*self.radius - self.x
self.angle = - self.angle
if self.y > self.surface.get_height() - self.radius: # bottom
self.y = 2*(self.surface.get_height() - self.radius) - self.y
self.angle = math.pi - self.angle
elif self.y < self.radius: # top
self.y = 2*self.radius - self.y
self.angle = math.pi - self.angle
def main():
xmax = 640 #width of window
ymax = 480 #height of window
white = (255, 255, 255)
black = (0,0,0)
grey = (128,128,128)
pygame.init()
screen = pygame.display.set_mode((xmax,ymax))
clock = pygame.time.Clock()
particles = []
for i in range(1000):
if i % 2:
colour = black
else:
colour = grey
# for readability
x = random.randint(0, xmax)
y = random.randint(0, ymax)
speed = random.randint(0,20)*0.1
angle = random.randint(0,360)
radius = 3
particles.append( Particle((x, y), radius, speed, angle, colour, screen) )
done = False
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
break
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
done = True
break
if done:
break
screen.fill(white)
for p in particles:
p.move()
p.bounce()
p.draw()
clock.tick(40)
pygame.display.flip()
pygame.quit()
if __name__ == "__main__":
main()

Categories