I have pretty much everything done for this little game except I can't seem to get the enemy to just aimlessly float around. They spawn at the top of the window but it's rather bland having them stand in line Civil War style. I'm pretty sure it's something to do with class Enemy, but not sure. Any tips on how to get the player and aliens moving around would be appreciated!
import sys, logging, os, random, math, open_color, arcade
#check to make sure we are running the right version of Python
version = (3,7)
assert sys.version_info >= version, "This script requires at least Python {0}.{1}".format(version[0],version[1])
#turn on logging, in case we have to leave ourselves debugging messages
logging.basicConfig(format='[%(filename)s:%(lineno)d] %(message)s', level=logging.DEBUG)
logger = logging.getLogger(__name__)
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
MARGIN = 30
SCREEN_TITLE = "Intergalactic slam"
NUM_ENEMIES = 5
STARTING_LOCATION = (400,100)
BULLET_DAMAGE = 10
ENEMY_HP = 10
HIT_SCORE = 10
KILL_SCORE = 100
PLAYER_HP = 100
class Bullet(arcade.Sprite):
def __init__(self, position, velocity, damage):
'''
initializes the bullet
Parameters: position: (x,y) tuple
velocity: (dx, dy) tuple
damage: int (or float)
'''
super().__init__("assets/Player/PNG/Sprites/Missiles/spaceMissiles_012.png", 0.5)
(self.center_x, self.center_y) = position
(self.dx, self.dy) = velocity
self.damage = damage
def update(self):
'''
Moves the bullet
'''
self.center_x += self.dx
self.center_y += self.dy
class Enemy_Bullet(arcade.Sprite):
def __init__(self, position, velocity, damage):
super().__init__("PNG/laserGreen1.png", 0.5)
(self.center_x, self.center_y) = position
(self.dx, self.dy) = velocity
self.damage = damage
def update(self):
self.center_x += self.dx
self.center_y += self.dy
class Player(arcade.Sprite):
def __init__(self):
super().__init__("assets/Player/PNG/Sprites/Ships/spaceShips_005.png", 0.5)
(self.center_x, self.center_y) = STARTING_LOCATION
self.hp = PLAYER_HP
class Enemy(arcade.Sprite):
def __init__(self, position):
'''
initializes an alien enemy
Parameter: position: (x,y) tuple
'''
super().__init__("PNG/shipGreen_manned.png", 0.5)
self.hp = ENEMY_HP
(self.center_x, self.center_y) = position
class Window(arcade.Window):
def __init__(self, width, height, title):
super().__init__(width, height, title)
file_path = os.path.dirname(os.path.abspath(__file__))
os.chdir(file_path)
self.set_mouse_visible(True)
arcade.set_background_color(open_color.black)
self.bullet_list = arcade.SpriteList()
self.enemy_list = arcade.SpriteList()
self.enemy_bullet_list = arcade.SpriteList()
self.player = Player()
self.score = 0
self.win = False
self.lose = False
def setup(self):
'''
Set up enemies
'''
for i in range(NUM_ENEMIES):
x = 120 * (i+1) + 40
y = 500
enemy = Enemy((x,y))
self.enemy_list.append(enemy)
def update(self, delta_time):
self.bullet_list.update()
self.enemy_bullet_list.update()
if (not (self.win or self.lose)):
for e in self.enemy_list:
for b in self.bullet_list:
if (abs(b.center_x - e.center_x) <= e.width / 2 and abs(b.center_y - e.center_y) <= e.height / 2):
self.score += HIT_SCORE
e.hp -= b.damage
b.kill()
if (e.hp <= 0):
e.kill()
self.score += KILL_SCORE
if (len(self.enemy_list) == 0):
self.win = True
if (random.randint(1, 75) == 1):
self.enemy_bullet_list.append(Enemy_Bullet((e.center_x, e.center_y - 15), (0, -10), BULLET_DAMAGE))
for b in self.enemy_bullet_list:
if (abs(b.center_x - self.player.center_x) <= self.player.width / 2 and abs(b.center_y - self.player.center_y) <= self.player.height / 2):
self.player.hp -= b.damage
b.kill()
if (self.player.hp <= 0):
self.lose = True
def on_draw(self):
arcade.start_render()
arcade.draw_text(str(self.score), 20, SCREEN_HEIGHT - 40, open_color.white, 16)
arcade.draw_text("HP: {}".format(self.player.hp), 20, 40, open_color.white, 16)
if (self.player.hp > 0):
self.player.draw()
self.bullet_list.draw()
self.enemy_bullet_list.draw()
self.enemy_list.draw()
if (self.lose):
self.draw_game_loss()
elif (self.win):
self.draw_game_won()
def draw_game_loss(self):
arcade.draw_text(str("LOSER!"), SCREEN_WIDTH / 2 - 90, SCREEN_HEIGHT / 2 - 10, open_color.white, 30)
def draw_game_won(self):
arcade.draw_text(str("WINNER!"), SCREEN_WIDTH / 2 - 90, SCREEN_HEIGHT / 2 - 10, open_color.white, 30)
def on_mouse_motion(self, x, y, dx, dy):
'''
The player moves left and right with the mouse
'''
self.player.center_x = x
def on_mouse_press(self, x, y, button, modifiers):
if button == arcade.MOUSE_BUTTON_LEFT:
x = self.player.center_x
y = self.player.center_y + 15
bullet = Bullet((x,y),(0,10),BULLET_DAMAGE)
self.bullet_list.append(bullet)
def main():
window = Window(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
window.setup()
arcade.run()
if __name__ == "__main__":
main()
You'd want to change the self.center_x and self.center_y of each Enemy on every update, like you're already doing for each Bullet, but make the dx and dy values random in some way. For example:
class Enemy(arcade.Sprite):
def __init__(self, position):
...
(self.center_x, self.center_y) = position
(self.dx, self.dy) = (0, 0)
def update(self):
self.dx += random.random() - 0.5
self.dy += random.random() - 0.5
self.center_x += self.dx
self.center_y += self.dy
Now, this may look more like "twitching wildly" than "floating": many times a second, the thing potentially changes course completely. That's technically random movement, but it's not something a spaceship would do.
If it's too twitchy, make it so that dx and dy change more slowly, for example by dividing the random.random() - 0.5 by a fixed number. If it's too floaty, make it so that every update changes it more.
If you want the enemy to prefer moving down, or towards the player, get out the trigonometry and adjust dx and dy to match.
Related
I'm making a racing game with almost realistic cars in pygame. I made the cars succesfully and I want to make the car move a bit slower when it's not touching the drive way. For that, I want to use pygame mask collisions. Instead of using bounding boxes, they use the pixels and count them based on how many pixels of something are touching something. This all works correctly, but when I use the cam.follow_object(player) function, the game behaves very weird and there rise unexplainable bugs.
I included each individual picture and file, but I also included it on github because I know it's annoying to download all images one by one.
Assets:
app.py:
import pygame
from pygame.locals import *
from random import choice, randint
import math, car, ui, pySave, camera, level
pygame.init()
# Basic Variables
screen_width = 1000
screen_height = 1000
fps = 80
screen = pygame.display.set_mode((screen_width, screen_height))
clock = pygame.time.Clock()
cam = camera.Camera(1, screen)
#cam.zoom_game(1.55)
stage = "home"
# Loading Images
images = {
"start" : pygame.transform.scale(pygame.image.load("road_texture.png"), (200*cam.zoom, 75*cam.zoom)).convert_alpha(),
"bcg" : pygame.transform.scale(pygame.image.load("map2.png"), (1000*cam.zoom, 1000*cam.zoom)).convert_alpha(),
"car" : pygame.image.load("car.png").convert_alpha()
}
# Functionality Functions
def play_game():
global stage
stage = "game"
# Instansiating stuff
pos_save = pySave.Save_Manager("saved_info", "pos")
start_button = ui.Button((500,500), images["start"], play_game, text="startgame")
test_car = car.CarSprite(images["car"], 400, 400,[2, 2.3, 2.7], 0.013, rotations=360, camera=cam)
speedometer = ui.TextWithBackground((100,50), (100,950), images["start"])
level = level.Level(images["bcg"], test_car,cam,pos = (0,100))
# Groups and Lists
car_group = pygame.sprite.Group()
# Adding to Groups
car_group.add(test_car)
# Game Functions
def render():
if stage == "home":
screen.fill((255,255,255))
start_button.update("Start Game")
start_button.draw(screen)
elif stage == "game":
screen.fill((0,76,18))
level.update(screen)#screen.blit(images["bcg"], (0-cam.scroll[0],0-cam.scroll[1]))
car_group.update()
car_group.draw(screen)
#cam.follow(test_car, )
#speedometer.draw(screen)
speedometer.update(screen,round(test_car.speed*60,2) , " km/h")
#cam.zoom_game(2)
def collisions():
pass
run = True
while run:
clock.tick_busy_loop(80)
render()
collisions()
for event in pygame.event.get():
if event.type == QUIT:
run = False
pos_save.save("x", test_car.rect.x)
pos_save.save("y", test_car.rect.y)
pos_save.apply()
print(f"Quit with {round(clock.get_fps(), 2)} FPS")
quit()
pygame.display.update()
car.py:
import pygame
from pygame.locals import *
from random import choice, randint
import math, camera
class CarSprite( pygame.sprite.Sprite ):
def __init__( self, car_image, x, y, max_speed, accel,rot_speed=[1.8, 2.2, 3] ,rotations=360, camera="" ):
pygame.sprite.Sprite.__init__(self)
self.rotated_images = {}
self.min_angle = ( 360 / rotations )
for i in range( rotations ):
rotated_image = pygame.transform.rotozoom( pygame.transform.scale(car_image, (12*camera.zoom,25*camera.zoom)), 360-90-( i*self.min_angle ), 1 )
self.rotated_images[i*self.min_angle] = rotated_image
self.min_angle = math.radians( self.min_angle )
self.image = self.rotated_images[0]
self.rect = self.image.get_rect()
self.rect.center = ( x, y )
self.reversing = False
self.heading = 0
self.speed = 0
self.velocity = pygame.math.Vector2( 0, 0 )
self.position = pygame.math.Vector2( x, y )
self.speed_hardening = 1
self.acc = False
self.steer_strenght_acc = rot_speed[0]
self.steer_strength_normal= rot_speed[1]
self.steer_strength_drift= rot_speed[2]
self.steer_strength = rot_speed[1]
self.drift_point = 0.00
self.accel = accel
self.max_speed = self.accel * 150
self.cam = camera
def turn( self, ori=1 ):
if self.speed > 0.1 or self.speed < 0. :
self.heading += math.radians( self.steer_strenght * ori )
image_index = int((self.heading + self.min_angle / 2) / self.min_angle) % len(self.rotated_images)
image = self.rotated_images[image_index]
if self.image is not image:
x,y = self.rect.center
self.image = image
self.rect = self.image.get_rect()
self.rect.center = (x,y)
def accelerate( self):
self.speed += self.accel
def brake( self ):
if self.speed > 0:
self.speed -= self.accel * 3
if abs(self.speed) < 0.1:
self.speed = 0
self.velocity.from_polar((self.speed, math.degrees(self.heading)))
def move(self):
keys = pygame.key.get_pressed()
if keys[K_w]:
self.accelerate()
if keys[K_s]:
self.brake()
if keys[K_a]:
self.turn(-1)
if keys[K_d]:
self.turn()
if keys[pygame.K_s] or keys [pygame.K_w]:
self.acc = True
else:
self.acc = False
def update( self ):
self.move()
self.speed_hardening = self.speed / 100
self.speed = round(self.speed, 3)
if self.acc:
self.steer_strenght = self.steer_strenght_acc
else:
self.steer_strenght = self.steer_strength_normal
if self.speed > self.max_speed and not pygame.key.get_pressed()[K_SPACE] and not self.drift_point > 0:
self.speed += self.accel / 4 - self.speed_hardening / 2
if self.speed > self.max_speed * 1.8:
self.speed = self.max_speed * 1.8
if self.speed < -self.max_speed / 4:
self.speed = -self.max_speed / 4
if not pygame.key.get_pressed()[K_SPACE]:
self.velocity.from_polar((self.speed, math.degrees(self.heading)))
self.speed += self.drift_point
self.drift_point -= 0.0001
if self.drift_point < 0:
self.drift_point = 0
self.speed -= self.drift_point
else:
self.steer_strenght = self.steer_strength_drift
self.drift_point += 0.0001
if self.drift_point > self.accel / 1.5:
self.drift_point = self.accel / 1.5
if not self.acc and not self.speed < 0.04:
self.speed -= (self.accel / 2) + self.speed_hardening
if self.speed < 0.05:
self.speed = 0
self.position += self.velocity
self.rect.center = self.position
level.py
import pygame
from pygame.locals import *
from random import choice, randint
import math, car, ui, pySave, camera
screen_width = 1000
screen_height = 1000
class Level:
def __init__(self,image, car, camera, pos=(0,0)):
# Convert the images to a more suitable format for faster blitting
self.image = image.convert()
self.road = image
self.cam = camera
self.x,self.y = pos
self.bcg_mask = pygame.mask.from_surface(self.road)
self.car = car
self.get_car_mask()
def update(self, screen):
# Calculate the overlap between the car mask and the background mask
overlap = self.bcg_mask.overlap_mask(
self.car_mask,
(self.car.rect.x, self.car.rect.y)
)
self.x = 0 - self.cam.scroll[0]
self.y = 0 - self.cam.scroll[1]
# Fill the screen with the background color
screen.blit(self.road.convert_alpha(), (self.x, self.y))
screen.blit(overlap.to_surface(unsetcolor=(0,0,0,0), setcolor=(255,255,255,255)), (self.x, self.y))
# Print the overlap count to the console
print(overlap.count())
def get_car_mask(self):
# Convert the car image to a more suitable format for faster blitting
carimg = self.car.image.convert()
carimg.set_colorkey((0,0,0))
self.carimg = carimg
self.car_mask = pygame.mask.from_surface(self.carimg)
camera.py:
import pygame
from pygame.locals import *
class Camera:
def __init__(self, speed, screen):
self.scroll = [5,5]
self.speed = speed
self.screen = screen
self.zoom = 1
def move_on_command(self):
keys = pygame.key.get_pressed()
if keys[K_UP]:
self.scroll[1] -= self.speed
if keys[K_DOWN]:
self.scroll[1] += self.speed
if keys[K_RIGHT]:
self.scroll[0] += self.speed
if keys[K_LEFT]:
self.scroll[0] -= self.speed
def follow(self, obj, speed=12):
#self.scroll[0], self.scroll[1] = obj.rect.x, obj.rect.y
if (obj.rect.x - self.scroll[0]) != self.screen.get_width()/2:
self.scroll[0] += ((obj.rect.x - (self.scroll[0] + self.screen.get_width()/2)))
if obj.rect.y - self.scroll[1] != self.screen.get_height()/2:
self.scroll[1] += ((obj.rect.y - (self.scroll[1] + self.screen.get_height()/2)))
def zoom_game(self, zoom):
self.zoom = zoom
ui.py :
import pygame
from pygame.locals import *
from random import choice, randint
class Button:
def __init__(self, pos, image, action, click_times=1, dissapear=True , text="", textcolor = (255,255,255), fontsize=30):
self.image = image
self.rect = self.image.get_rect(center=pos)
self.clicked = False
self.clicked_times = click_times
self.dissapear = dissapear
self.dont_draw = False
self.function = action
self.text= text
if not self.text == "":
self.font = pygame.font.SysFont("Arial", fontsize, False, False)
self.color = textcolor
def update(self, var=""):
pressed = pygame.mouse.get_pressed()[0]
if not self.text == "":
self.text = self.font.render(f"{var}", 1, self.color)
self.text_rect = self.text.get_rect(center = self.rect.center)
if pressed and not self.clicked and not self.clicked_times <= 0 and self.rect.collidepoint(pygame.mouse.get_pos()):
self.function()
self.clicked = True
self.clicked_times -= 1
if not pressed:
self.clicked = False
if self.clicked_times <= 0:
if self.dissapear:
self.dont_draw = True
def draw(self, screen):
if not self.dont_draw:
screen.blit(self.image, (self.rect.x,self.rect.y))
screen.blit(self.text, (self.text_rect.x, self.text_rect.y))
class TextWithBackground:
def __init__(self, image_size, pos, image, fontsize=30, colour=(255, 255, 255)):
self.image = pygame.transform.scale(image, image_size)
self.rect = self.image.get_rect(center=pos)
self.font = pygame.font.SysFont("Arial", fontsize, False, False)
self.color = colour
self.text = self.font.render("", 1, self.color)
self.other_text = None
self.other_rect = None
def update(self, screen, variable, othertext=""):
self.text = self.font.render(f"{variable}", 1, self.color)
self.text_rect = self.text.get_rect(center=self.rect.center)
if othertext:
self.other_text = self.font.render(f"{othertext}", 1, self.color)
self.other_rect = self.other_text.get_rect(topleft=(self.rect.center[0] - 5, self.rect.center[1] - 5))
screen.blit(self.image, (self.rect.x, self.rect.y))
screen.blit(self.text, (self.rect.x, self.rect.y))
if self.other_text:
screen.blit(self.other_text, (self.other_rect.x, self.other_rect.y))
Here you can find my images:
car.png
map2.png
road_texture.png
See PyGame collision with masks. You need to calculate the offset between the car and the map when you get the overlap area of the masks:
class Level:
# [...]
def update(self, screen):
self.x = 0 - self.cam.scroll[0]
self.y = 0 - self.cam.scroll[1]
# Calculate the overlap between the car mask and the background mask
offset = (self.car.rect.x - self.x, self.car.rect.y - self.y)
overlap = self.bcg_mask.overlap_mask(self.car_mask, offset)
# Fill the screen with the background color
screen.blit(self.road.convert_alpha(), (self.x, self.y))
screen.blit(overlap.to_surface(unsetcolor=(0,0,0,0), setcolor=(255,255,255,255)), (self.x, self.y))
# Print the overlap count to the console
print(overlap.count())
So I was trying to make something that resembles fireworks. I made a particle class, which will make up the fireworks.
class Particle:
def __init__(self, pos, angle):
self.pos = pos
self.angle = angle
self.color = choice([(217, 103, 51), (238, 95, 30)])
self.radius = uniform(2, 7)
self.pull = 0
self.start = time.time()
def adjust(self):
self.radius -= 0.03
def draw(self):
if self.radius > 0:
pygame.draw.circle(D, self.color, (int(self.pos[0])
, int(self.pos[1])), int(self.radius))
def move(self):
now = time.time()
self.pos[0] += cos(radians(self.angle)) * 2
self.pos[1] += (sin(radians(self.angle)) + self.pull) * 2
if now - self.start > 0.1:
self.pull += 0.25
self.start = now
Then I made a Fireworks class which shoots particles from 0 to 360 degrees.
class FireWorks:
def __init__(self):
self.particles = []
for i in range(360):
self.particles.append(Particle([600, 300], i))
def explode(self):
for i in range(len(self.particles)):
self.particles[i].draw()
self.particles[i].move()
self.particles[i].adjust()
for p in self.particles:
if p.radius < 0:
self.particles.remove(p)
Now I want to draw a line from the position they spawn (600, 300), along the path the particles take. But the thing is particles don't move in a straight line. To make it look a bit more natural, I did this to the y-value: self.pos[1] += (sin(radians(self.angle)) + self.pull) * 2. Value of self.pull is incremented by 0.25 every 0.1 seconds. One thing I tried is to store the position value every time y increases and draw lines between those positions so that a curve forms, but nothing is drawn at all and it causes a lag. Here is the code showing just the bits involved in generating the points and drawing lines between the points.
class Particle:
def __init__(self, pos, angle):
self.points = [] #added list to init to hold the points between which lines need to be drawn
def move(self):
# In move method, every time a value is added to y, we record position at that point
if now - self.start > 0.1:
self.points.append(self.pos)
self.pull += 0.25
def draw(self):
# Iterate through the points in the list and draw a line between them
for i in range(len(self.points)):
for j in range(1, len(self.points)):
pygame.draw.line(D, self.color, (int(self.points[i][0]), int(self.points[i][1]))
, (int(self.points[j][0]), int(self.points[j][1])), int(self.radius))
Here is the complete code for reference.
import pygame
from math import radians, sin, cos
from random import choice, uniform, randint
import time
pygame.init()
WIN = pygame.display
D = WIN.set_mode((1200, 600))
class Particle:
def __init__(self, pos, angle):
self.pos = pos
self.angle = angle
self.color = choice([(217, 103, 51), (238, 95, 30)])
self.radius = uniform(2, 7)
self.pull = 0
self.start = time.time()
self.points = []
def adjust(self):
self.radius -= 0.03
def draw(self):
if self.radius > 0:
pygame.draw.circle(D, self.color, (int(self.pos[0])
, int(self.pos[1])), int(self.radius))
for i in range(len(self.points)):
for j in range(1, len(self.points)):
pygame.draw.line(D, self.color, (int(self.points[i][0]), int(self.points[i][1]))
, (int(self.points[j][0]), int(self.points[j][1])), int(self.radius))
def move(self):
now = time.time()
self.pos[0] += cos(radians(self.angle)) * 2
self.pos[1] += (sin(radians(self.angle)) + self.pull) * 2
if now - self.start > 0.1:
self.points.append(self.pos)
self.pull += 0.25
self.start = now
class FireWorks:
def __init__(self):
self.particles = []
for i in range(360):
self.particles.append(Particle([600, 300], i))
def explode(self):
for i in range(len(self.particles)):
self.particles[i].draw()
self.particles[i].move()
self.particles[i].adjust()
for p in self.particles:
if p.radius < 0:
self.particles.remove(p)
f = FireWorks()
D.fill((0, 0, 0))
while True:
pygame.event.get()
D.fill((0, 0, 0))
f.explode()
WIN.flip()
You have to append a copy of the position tuple to the list of positions, rather than an reference to the positions:
self.points.append(self.pos)
self.points.append(self.pos[:])
Note, self.pos refers to a tuple with 2 components. Hence self.points.append(self.pos) appends a new reference to this position to the list, but it doesn't generate a new position.
To improve the performance, I recommend to add integral positions to the list and to draw the line along the path of the particles by pygame.draw.lines():
class Particle:
# [...]
def draw(self):
if self.radius > 0:
pygame.draw.circle(D, self.color, (int(self.pos[0])
, int(self.pos[1])), int(self.radius))
if len(self.points) > 1:
pygame.draw.lines(D, self.color, False, self.points)
def move(self):
now = time.time()
self.pos[0] += cos(radians(self.angle)) * 2
self.pos[1] += (sin(radians(self.angle)) + self.pull) * 2
if now - self.start > 0.1:
x, y = round(self.pos[0]), round(self.pos[1])
self.points.append((x, y))
self.pull += 0.25
self.start = now
i am trying to make a game where you can shoot bullets to kill emojis. However, i can't manage to figure out how to stop spamming the space key to shoot bullets. If you keep on spamming, the game would be too easy. I am not exactly sure if I should use the sleep command or something else.Please help! thanks!
Here is my code:
# import everything from turtle
from turtle import *
import random
import math
#create a link to the object (creates the environment)
screen = Screen()
speed1 = 1.3
ht()
amountOfEmojis = 11
#set a boundary for screen, if touches end, goes to the other side
screenMinX = -screen.window_width()/2
screenMinY = -screen.window_height()/2
screenMaxX = screen.window_width()/2
screenMaxY = screen.window_height()/2
#establish important data for screen environment
screen.setworldcoordinates(screenMinX,screenMinY,screenMaxX,screenMaxY)
screen.bgcolor("black")
#turtle setup
penup()
ht()
speed(0)
goto(0, screenMaxY - 50)
color('white')
write("Welcome to Emoji Run!", align="center", font=("Courier New",26))
goto(0, screenMaxY - 70)
write("Use the arrow keys to move and space to fire. The point of the game is to kill the emojis", align="center")
goto(0, 0)
color("red")
emojis = ["Poop_Emoji_7b204f05-eec6-4496-91b1-351acc03d2c7_grande.png", "1200px-Noto_Emoji_KitKat_263a.svg.png",
"annoyningface.png", "Emoji_Icon_-_Sunglasses_cool_emoji_large.png"]
class Bullet(Turtle):
#constructor, object for a class, pass in information
def __init__(self,screen,x,y,heading):
#create a bullet
Turtle.__init__(self)#clones bullet
self.speed(0)
self.penup()
self.goto(x,y)
self.seth(heading)#pointing to itself
self.screen = screen
self.color('yellow')
self.max_distance = 500
self.distance = 0
self.delta = 20
self.shape("bullet")
#logic to move bullet
def move(self):
self.distance = self.distance + self.delta#how fast it's going to move
self.forward(self.delta)
if self.done():
self.reset()
def getRadius(self):
return 4#collision detection helper function
def blowUp(self):
self.goto(-300,0)#function that makes something go off the screen
def done(self):
return self.distance >= self.max_distance # append to list
class Asteroid(Turtle):
def __init__(self,screen,dx,dy,x,y,size,emoji):#spawn asteroid randomly
Turtle.__init__(self)#clone itself
self.speed(0)
self.penup()
self.goto(x,y)
self.color('lightgrey')
self.size = size
self.screen = screen
self.dx = dx
self.dy = dy
r = random.randint(0, len(emoji) - 1)
screen.addshape(emojis[r])
self.shape(emojis[r])
#self.shape("rock" + str(size)) #sets size and shape for asteroid
def getSize(self):#part of collision detection
return self.size
#getter and setter functions
def getDX(self):
return self.dx
def getDY(self):
return self.dy
def setDX(self,dx):
self.dx = dx
def setDY(self,dy):
self.dy = dy
def move(self):
x = self.xcor()
y = self.ycor()
#if on edge of screen. go to opposite side
x = (self.dx + x - screenMinX) % (screenMaxX - screenMinX) + screenMinX
y = (self.dy + y - screenMinY) % (screenMaxY - screenMinY) + screenMinY
self.goto(x,y)
def blowUp(self):
self.goto(-300,0)#function that makes something go off the screen
def getRadius(self):
return self.size * 10 - 5
class SpaceShip(Turtle):
def __init__(self,screen,dx,dy,x,y):
Turtle.__init__(self)
self.speed(0)
self.penup()
self.color("white")
self.goto(x,y)
self.dx = dx
self.dy = dy
self.screen = screen
self.bullets = []
self.shape("turtle")
def move(self):
x = self.xcor()
y = self.ycor()
x = (self.dx + x - screenMinX) % (screenMaxX - screenMinX) + screenMinX
y = (self.dy + y - screenMinY) % (screenMaxY - screenMinY) + screenMinY
self.goto(x,y)
#logic for collision
def powPow(self, asteroids):
dasBullets = []
for bullet in self.bullets:
bullet.move()
hit = False
for asteroid in asteroids:
if intersect(asteroid, bullet):#counts every asteroid to see if it hits
asteroids.remove(asteroid)
asteroid.blowUp()
bullet.blowUp()
hit = True
if (not bullet.done() and not hit):
dasBullets.append(bullet)
self.bullets = dasBullets
def fireBullet(self):
self.bullets.append(Bullet(self.screen, self.xcor(), self.ycor(), self.heading()))
def fireEngine(self):#how turtle moves
angle = self.heading()
x = math.cos(math.radians(angle))
y = math.sin(math.radians(angle))
self.dx = self.dx + x#how it rotates
self.dy = self.dy + y
self.dx = self.dx / speed1
self.dy = self.dy / speed1
#extra function
def turnTowards(self,x,y):
if x < self.xcor():
self.left(7)
if x > self.xcor():
self.right(7)
def getRadius(self):
return 10
def getDX(self):
return self.dx
def getDY(self):
return self.dy
#collision detection
def intersect(object1,object2):
dist = math.sqrt((object1.xcor() - object2.xcor())**2 + (object1.ycor() - object2.ycor())**2)
radius1 = object1.getRadius()
radius2 = object2.getRadius()
# The following if statement could be written as
# return dist <= radius1+radius2
if dist <= radius1+radius2:
return True
else:
return False
#adds object to screen
screen.register_shape("rock3",((-20, -16),(-21, 0), (-20,18),(0,27),(17,15),(25,0),(16,-15),(0,-21)))
screen.register_shape("rock2",((-15, -10),(-16, 0), (-13,12),(0,19),(12,10),(20,0),(12,-10),(0,-13)))
screen.register_shape("rock1",((-10,-5),(-12,0),(-8,8),(0,13),(8,6),(14,0),(12,0),(8,-6),(0,-7)))
screen.register_shape("ship",((-10,-10),(0,-5),(10,-10),(0,10)))
screen.register_shape("bullet",((-2,-4),(-2,4),(2,4),(2,-4)))
#ship spawn exactly the middle everytime
ship = SpaceShip(screen,0,0,(screenMaxX-screenMinX)/2+screenMinX,(screenMaxY-screenMinY)/2 + screenMinY)
#randomize where they spawn
asteroids = []
for k in range(amountOfEmojis):
dx = random.random() * 6 - 3
dy = random.random() * 6 - 3
x = random.randrange(10) * (screenMaxX - screenMinX) + screenMinX
y = random.random() * (screenMaxY - screenMinY) + screenMinY
asteroid = Asteroid(screen,dx,dy,x,y,random.randint(1,3), emojis)
asteroids.append(asteroid)
def play():
# Tell all the elements of the game to move
ship.move()
gameover = False
for asteroid in asteroids:
r = random.randint(0, 1)
if r == 1:
asteroid.right(50)
else:
asteroid.left(20)
asteroid.move()
if intersect(ship,asteroid):
write("You Got Killed :(",font=("Verdana",25),align="center")
gameover = True
ship.powPow(asteroids)
screen.update()
if not asteroids:
color('green')
write("You Killed the Emojis!!",font=("Arial",30),align="center")
ht()
if not gameover:
screen.ontimer(play, 30)
bullets = []
#controls
def turnLeft():
ship.left(7)
def turnRight():
ship.right(7)
def go():
ship.fireEngine()
def fire():
ship.fireBullet()
ht()
screen.tracer(0);
screen.onkey(turnLeft, 'left')
screen.onkey(turnRight, 'right')
screen.onkey(go, 'up')
screen.onkey(fire, 'space')
screen.listen()
play()
Thanks!
You could try something like this:
import time
FIRE_DELAY = 0.1 # number of seconds between bullets
LAST_FIRE = 0
def fire():
t = time.time()
if t - LAST_FIRE > FIRE_DELAY:
LAST_FIRE = t
ship.fireBullet()
I'm working on a 2D python game project for my CS class, and I've hit a bump, I don't know what the problem is:
The project is a large part of my grade, and up until now I've had an A+
This project is incredibly frustrating
NEW
ok so i've got everything working so far, except for some reason My protaganist() is stuck at the top left corner of the game screen !
Also, i need ideas on how to create a jump action
If anyone could help I would be incredibly grateful!
I am importing a game engine my teacher made available from his book website, but i it is too long for me to add but i will try to add some of it at the bottom
Here is all my code:
import gameEngine
import pygame
import math
pygame.init()
screen = pygame.display.set_mode((640, 480))
pygame.mixer.init()
sndAtk = pygame.mixer.Sound("OOT_AdultLink_Attack1.wav")
#goal is to create a game
#must have menu to start game
#menu should have a start and quit button.. start runs gaming operations and quit exits program
#sprites for character and enemies and bullets maybe, use one large image and simply move visibiliy
#this saves memory as 1 image is loaded instead of many
"""
class game(gameEngine.scene):
def __init__(self, scene):
self.background()
self.sprites["spawn.gif", "badguys.gif"]
"""
"""
protaganist is our hero sprite
should run left and right, jump left and right
and attack left and right...
I might add in the bow and jump attack
"""
class scrollinggrass(gameEngine.SuperSprite):
def __init__(self, scene):
gameEngine.SuperSprite.__init__(self, scene)
self.setImage("gamebackground.jpg")
self.rect.centerx = 20
self.rect.centery = 500
self.rect = self.image.get_rect()
self.dx = 10
self.dy = 0
self.checkKeys()
def checkKeys(self):
keys = pygame.key.get_pressed()
if keys[pygame.K_RIGHT]:
print("working")
self.forward(3)
run.play()
if keys[pygame.K_LEFT]:
self.forward(-3)
class hearts(gameEngine.SuperSprite):
def __init__(self, scene):
gameEngine.SuperSprite.__init__(self, scene)
self.setImage("heart.png")
self.setTransparentColor = self.imageMaster.get_at((1,1))
self.imageMaster.set_colorkey(self.setTransparentColor)
self.setPosition((550 , 30))
class badguy(gameEngine.SuperSprite):
def __init__(self, scene):
gameEngine.SuperSprite.__init__(self, scene)
self.setImage("badguy1.png")
self.rect = self.imageMaster.get_rect()
self.health = 2
self.DEAD = 1
self.state = 0
class protaganist(gameEngine.SuperSprite):
def __init__(self, scene):
gameEngine.SuperSprite.__init__(self, scene)
self.imageList = []
self.rect = self.imageMaster.get_rect()
self.STANDING = 0
self.RUNNING = 1
self.ATTACKING = 2
self.JUMPING = 3
self.DEAD = 10
self.imageFrame = 0
self.state = self.STANDING
self.hearts = 1
self.heartPts = self.hearts * 3
self.stats()
self.loadImages()
# self.image = self.imageList[0]
self.checkKeys()
def stats(self):
#sets it up so each heart is essentially 3 hit points
if self.heartPts >= 3:
self.hearts = 1
elif self.heartPts >= 6:
self.hearts = 2
elif self.heartPts == 9:
self.hearts = 3
elif self.heartPts > 9:
self.heartPts = 9
# changes state to dead if hp == 0
if self.heartPts == 0:
self.state = DEAD
def loadImages(self):
self.setPosition((320 , 380))
self.setImage("heroSTANDING.gif")
self.setTransparentColor = self.imageMaster.get_at((1,1))
self.imageMaster.set_colorkey(self.setTransparentColor)
def checkKeys(self):
keys = pygame.key.get_pressed()
if keys[pygame.K_RIGHT]:
self.state = runRight
self.frame += 1
if self.frame >= len(self.imageList):
self.frame = 1
self.image = self.imageList[self.frame]
# self.image = self.image.get_rect()
# self.rect.center = (320, 240)
if keys[pygame.K_LEFT]:
self.state = 1
while keys[pygame.K_g]:
self.state = Attacking
sndAtk.play()
if self.state == self.DEAD:
self.image = self.deadImgList[0]
self.frame += 1
self.image = self.deadImgList[self.frame]
#self.image = self.image.get_rect()
#self.rect.center = (320, 240)
class game(gameEngine.Scene):
def __init__ (self):
gameEngine.Scene.__init__(self)
pygame.display.set_caption("Link's Mediocre Adventure")
background = pygame.Surface(screen.get_size())
background.fill((0, 0, 0))
screen.blit(background, (0, 0))
pro = protaganist(self)
baddy = badguy(self)
baddy1 = badguy(self)
heart = hearts(self)
grass = scrollinggrass(self)
goodlySprites = self.makeSpriteGroup((grass, pro, heart))
baddySprites = self.makeSpriteGroup((baddy, baddy1))
# self.addSpriteGroup(goodlySprites)
self.addGroup((baddySprites))
clock = pygame.time.Clock()
keepGoing = True
while keepGoing:
clock.tick(30)
for event in pygame.event.get():
if event.type == pygame.QUIT:
keepGoing = False
if pro.state == pro.ATTACKING:
if pro.collidesGroup(baddySprites):
baddy.health -= 1
baddy1.health -= 1
if baddy.health == 0:
baddy.reset()
elif baddy1.health == 0:
baddy.reset()
elif pro.state != pro.ATTACKING:
if pro.collideGroup(baddySprites):
pro.heartPts -= 1
goodlySprites.update()
baddySprites.update()
goodlySprites.draw(screen)
baddySprites.draw(screen)
pygame.display.flip()
def main():
game.start()
if __name__ == "__main__":
game()
game engine
class SuperSprite(pygame.sprite.Sprite):
""" An enhanced Sprite class
expects a gameEngine.Scene class as its one parameter
Use methods to change image, direction, speed
Will automatically travel in direction and speed indicated
Automatically rotates to point in indicated direction
Five kinds of boundary collision
"""
def __init__(self, scene):
pygame.sprite.Sprite.__init__(self)
self.scene = scene
self.screen = scene.screen
#create constants
self.WRAP = 0
self.BOUNCE = 1
self.STOP = 2
self.HIDE = 3
self.CONTINUE = 4
#create a default text image as a placeholder
#This will usually be changed by a setImage call
self.font = pygame.font.Font("freesansbold.ttf", 30)
self.imageMaster = self.font.render(">sprite>", True, (0, 0,0), (0xFF, 0xFF, 0xFF))
self.image = self.imageMaster
self.rect = self.image.get_rect()
#create properties
#most will be changed through method calls
self.x = 200
self.y = 200
self.dx = 0
self.dy = 0
self.dir = 0
self.rotation = 0
self.speed = 0
self.maxSpeed = 10
self.minSpeed = -3
self.boundAction = self.WRAP
self.pressed = False
self.oldCenter = (100, 100)
self.states = {}
self.currentState = "default"
def update(self):
self.oldCenter = self.rect.center
self.checkEvents()
self.__rotate()
self.__calcVector()
self.__calcPosition()
self.checkBounds()
self.rect.center = (self.x, self.y)
def checkEvents(self):
""" overwrite this method to add your own event code """
pass
def __rotate(self):
""" PRIVATE METHOD
change visual orientation based on
rotation property.
automatically called in update.
change rotation property directly or with
rotateBy(), setAngle() methods
"""
oldCenter = self.rect.center
self.oldCenter = oldCenter
self.image = pygame.transform.rotate(self.imageMaster, self.rotation)
self.rect = self.image.get_rect()
self.rect.center = oldCenter
def __calcVector(self):
""" calculates dx and dy based on speed, dir
automatically called in update
"""
theta = self.dir / 180.0 * math.pi
self.dx = math.cos(theta) * self.speed
self.dy = math.sin(theta) * self.speed
self.dy *= -1
def __calcPosition(self):
""" calculates the sprites position adding
dx and dy to x and y.
automatically called in update
"""
self.x += self.dx
self.y += self.dy
def checkBounds(self):
""" checks boundary and acts based on
self.BoundAction.
WRAP: wrap around screen (default)
BOUNCE: bounce off screen
STOP: stop at edge of screen
HIDE: move off stage and wait
CONTINUE: keep going at present course and speed
automatically called by update
"""
scrWidth = self.screen.get_width()
scrHeight = self.screen.get_height()
#create variables to simplify checking
offRight = offLeft = offTop = offBottom = offScreen = False
if self.x > scrWidth:
offRight = True
if self.x < 0:
offLeft = True
if self.y > scrHeight:
offBottom = True
if self.y < 0:
offTop = True
if offRight or offLeft or offTop or offBottom:
offScreen = True
if self.boundAction == self.WRAP:
if offRight:
self.x = 0
if offLeft:
self.x = scrWidth
if offBottom:
self.y = 0
if offTop:
self.y = scrHeight
elif self.boundAction == self.BOUNCE:
if offLeft or offRight:
self.dx *= -1
if offTop or offBottom:
self.dy *= -1
self.updateVector()
self.rotation = self.dir
elif self.boundAction == self.STOP:
if offScreen:
self.speed = 0
elif self.boundAction == self.HIDE:
if offScreen:
self.speed = 0
self.setPosition((-1000, -1000))
elif self.boundAction == self.CONTINUE:
pass
else:
# assume it's continue - keep going forever
pass
def setSpeed(self, speed):
""" immediately sets the objects speed to the
given value.
"""
self.speed = speed
def speedUp(self, amount):
""" changes speed by the given amount
Use a negative value to slow down
"""
self.speed += amount
if self.speed < self.minSpeed:
self.speed = self.minSpeed
if self.speed > self.maxSpeed:
self.speed = self.maxSpeed
def setAngle(self, dir):
""" sets both the direction of motion
and visual rotation to the given angle
If you want to set one or the other,
set them directly. Angle measured in degrees
"""
self.dir = dir
self.rotation = dir
def turnBy (self, amt):
""" turn by given number of degrees. Changes
both motion and visual rotation. Positive is
counter-clockwise, negative is clockwise
"""
self.dir += amt
if self.dir > 360:
self.dir = amt
if self.dir < 0:
self.dir = 360 - amt
self.rotation = self.dir
def rotateBy(self, amt):
""" change visual orientation by given
number of degrees. Does not change direction
of travel.
"""
self.rotation += amt
if self.rotation > 360:
self.rotation = amt
if self.rotation < 0:
self.rotation = 360 - amt
def setImage (self, image):
""" loads the given file name as the master image
default setting should be facing east. Image
will be rotated automatically """
self.imageMaster = pygame.image.load(image)
self.imageMaster = self.imageMaster.convert()
def setDX(self, dx):
""" changes dx value and updates vector """
self.dx = dx
self.updateVector()
def addDX(self, amt):
""" adds amt to dx, updates vector """
self.dx += amt
self.updateVector()
def setDY(self, dy):
""" changes dy value and updates vector """
self.dy = dy
self.updateVector()
def addDY(self, amt):
""" adds amt to dy and updates vector """
self.dy += amt
self.updateVector()
def setComponents(self, components):
""" expects (dx, dy) for components
change speed and angle according to dx, dy values """
(self.dx, self.dy) = components
self.updateVector()
def setBoundAction (self, action):
""" sets action for boundary. Values are
self.WRAP (wrap around edge - default)
self.BOUNCE (bounce off screen changing direction)
self.STOP (stop at edge of screen)
self.HIDE (move off-stage and stop)
self.CONTINUE (move on forever)
Any other value allows the sprite to move on forever
"""
self.boundAction = action
def setPosition (self, position):
""" place the sprite directly at the given position
expects an (x, y) tuple
"""
(self.x, self.y) = position
def moveBy (self, vector):
""" move the sprite by the (dx, dy) values in vector
automatically calls checkBounds. Doesn't change
speed or angle settings.
"""
(dx, dy) = vector
self.x += dx
self.y += dy
self.checkBounds()
def forward(self, amt):
""" move amt pixels in the current direction
of travel
"""
#calculate dx dy based on current direction
radians = self.dir * math.pi / 180
dx = amt * math.cos(radians)
dy = amt * math.sin(radians) * -1
self.x += dx
self.y += dy
def addForce(self, amt, angle):
""" apply amt of thrust in angle.
change speed and dir accordingly
add a force straight down to simulate gravity
in rotation direction to simulate spacecraft thrust
in dir direction to accelerate forward
at an angle for retro-rockets, etc.
"""
#calculate dx dy based on angle
radians = angle * math.pi / 180
dx = amt * math.cos(radians)
dy = amt * math.sin(radians) * -1
self.dx += dx
self.dy += dy
self.updateVector()
def updateVector(self):
#calculate new speed and angle based on dx, dy
#call this any time you change dx or dy
self.speed = math.sqrt((self.dx * self.dx) + (self.dy * self.dy))
dy = self.dy * -1
dx = self.dx
radians = math.atan2(dy, dx)
self.dir = radians / math.pi * 180
def setSpeedLimits(self, max, min):
""" determines maximum and minimum
speeds you will allow through
speedUp() method. You can still
directly set any speed you want
with setSpeed() Default values:
max: 10
min: -3
"""
self.maxSpeed = max
self.minSpeed = min
def dataTrace(self):
""" utility method for debugging
print major properties
extend to add your own properties
"""
print "x: %d, y: %d, speed: %.2f, dir: %.f, dx: %.2f, dy: %.2f" % \
(self.x, self.y, self.speed, self.dir, self.dx, self.dy)
def mouseDown(self):
""" boolean function. Returns True if the mouse is
clicked over the sprite, False otherwise
"""
self.pressed = False
if pygame.mouse.get_pressed() == (1, 0, 0):
if self.rect.collidepoint(pygame.mouse.get_pos()):
self.pressed = True
return self.pressed
def clicked(self):
""" Boolean function. Returns True only if mouse
is pressed and released over sprite
"""
released = False
if self.pressed:
if pygame.mouse.get_pressed() == (0, 0, 0):
if self.rect.collidepoint(pygame.mouse.get_pos()):
released = True
return released
def collidesWith(self, target):
""" boolean function. Returns True if the sprite
is currently colliding with the target sprite,
False otherwise
"""
collision = False
if self.rect.colliderect(target.rect):
collision = True
return collision
def collidesGroup(self, target):
""" wrapper for pygame.sprite.collideany
simplifies checking sprite - group collisions
returns result of collision check (sprite from group
that was hit or None)
"""
collision = pygame.sprite.spritecollideany(self, target)
return collision
def distanceTo(self, point):
""" returns distance to any point in pixels
can be used in circular collision detection
"""
(pointx, pointy) = point
dx = self.x - pointx
dy = self.y - pointy
dist = math.sqrt((dx * dx) + (dy * dy))
return dist
def dirTo(self, point):
""" returns direction (in degrees) to
a point """
(pointx, pointy) = point
dx = self.x - pointx
dy = self.y - pointy
dy *= -1
radians = math.atan2(dy, dx)
dir = radians * 180 / math.pi
dir += 180
return dir
def drawTrace(self, color=(0x00, 0x00, 0x00)):
""" traces a line between previous position
and current position of object
"""
pygame.draw.line(self.scene.background, color, self.oldCenter,
self.rect.center, 3)
self.screen.blit(self.scene.background, (0, 0))
def addState(self, stateName, stateImageFile):
""" Creates a new sprite state with the associated name
and image. Useful to build multi-state sprites.
"""
#load the image
tempImage = pygame.image.load(stateImageFile)
tempImage.convert()
self.states[stateName] = tempImage
def setState(self, stateName):
""" attempts to set the sprite to the indicated state
(image)
"""
self.imageMaster = self.states[stateName]
self.rect = self.imageMaster.get_rect()
self.currentState = stateName
def getState(self):
""" returns the current state name
(default if no states have been explicitly set)
"""
return self.currentState
if pro.state == pro.ATTACKING:
if pro.collidesWith(baddySprites):
baddy.health -= 1
if baddy.health == 0:
baddy.reset()
elif pro.state != pro.ATTACKING:
if pro.collidesWith(baddySprites):
pro.heartPts -= 1
baddySprites is a sprite group, so I bet you have to use collidesGroup instead of collidesWith.
if pro.state == pro.ATTACKING:
if pro.collidesGroup(baddySprites):
baddy.health -= 1
if baddy.health == 0:
baddy.reset()
elif pro.state != pro.ATTACKING:
if pro.collidesGroup(baddySprites):
pro.heartPts -= 1
But even if you do this, it seems like you'll still have problems. Namely, this code only ever deducts health from baddy and not baddy1. I'm assuming sprite groups support iteration. If so, you should perform collision detection independently on each enemy.
for enemy in baddySprites:
if pro.state == pro.ATTACKING:
if pro.collidesWith(enemy):
enemy.health -= 1
if enemy.health == 0:
enemy.reset()
elif pro.state != pro.ATTACKING:
if pro.collidesWith(enemy):
pro.heartPts -= 1
Just what the title says...the ball sprite will often collide and ghost through the paddle and alien sprite causing it to stall or get stuck. Trying to figure out a way to fix this so the game operates better. This is my only attempt at making a game so far just trying to polish it more.
class Alien(games.Sprite):
"""
An alien which moves.
"""
image = games.load_image("crab.jpg")
def __init__(self, y, speed = 2, odds_change = 250):
""" Initialize alien. """
super(Alien, self).__init__(image = Alien.image,
x = games.screen.width/2,
y = y,
dx = speed)
self.odds_change = odds_change
self.timer = 0
def update(self):
""" Determine if direction needs to be reversed. """
if self.left < 0 or self.right > games.screen.width:
self.dx = -self.dx
elif random.randrange(self.odds_change) == 0:
self.dx = -self.dx
self.check_collide()
if self.timer > 0:
self.timer -= 1
##if ball hits alien add points
def check_collide(self):
for ball in self.overlapping_sprites:
if self.timer == 0:
self.timer = 100
Paddle.score.value+=10
ball.handle_collide()
else:
ball.handle_collide2()
class Paddle(games.Sprite):
##load up paddle sprite
image=games.load_image("platform.jpg")
score=games.Text(value=0, size=75, color = color.white, top=5,
right=games.screen.width - 20, is_collideable = False)
def __init__(self, theY=games.screen.height - 25):
super(Paddle, self).__init__(image=Paddle.image, angle = 0,
y = theY,
x=games.mouse.x,
left=20)
games.screen.add(Paddle.score)
def update(self):
self.x=games.mouse.x
if self.left<0:
self.x=0
if self.right>games.screen.width:
self.x=games.screen.width
self.check_collide()
def check_collide(self):
for ball in self.overlapping_sprites:
ball.handle_collide2()
class Ball(games.Sprite):
image=games.load_image("ball.jpg")
speed=2
def __init__(self, x=100, y=70):
super(Ball, self).__init__(image=Ball.image,
x=x, y=y,
dx=Ball.speed, dy=Ball.speed)
##make the ball bounce off walls
def update(self):
if self.right>games.screen.width:
self.dx=-self.dx
if self.left<0:
self.dx=-self.dx
if self.top<0:
self.dy=-self.dy
if self.bottom>games.screen.height:
self.end_game()
self.destroy()
##ball gets faster as score rises
def handle_collide(self):
if Paddle.score.value == 30:
self.dx *= 2
self.dy *= 2
if Paddle.score.value == 60:
self.dx *= 2
self.dy *= 2
self.dy=-self.dy
def handle_collide2(self):
self.dy=-self.dy
I have asked multiple people I know who create games with python but none of them could come up with a better solution.