How can i make sprites spawn on random locations using pygame? - python

Ive been trying to make a twist on a dodger-like game, where some blocks make you grow and others make you shrink. I'm also planning to add a red one of which is supposed to make you lose a life(you will have 3 lives starting out), and various others with their own attributes. However Ive hit a bump in the road in having the falling blocks spawn randomly, which is something that would be required in a broad range of games.
My plan is basically that i would want to have the blocks re-spawn at random locations each time and at some point i want the amount of falling blocks to increase as well to further mount the difficulty.
Here is my current progress. Any input greatly appreciated:
import pygame
import random
pygame.init()
win_width = 800
win_height = 600
window = pygame.display.set_mode((win_width,win_height))
pygame.display.set_caption("Roger Dodger")
black = (0,0,0)
white = (255,255,255)
red = (255,0,0)
orange = (255,127,0)
yellow = (255,255,0)
blue = (0,0,255)
clock = pygame.time.Clock()
class Collisions:
def __init__(self, x1,y1,w1,h1,x2,y2,w2,h2):
self.x1 = x1
self.y1 = y1
self.w1 = w1
self.h1 = h1
self.x2 = x2
self.y2 = y2
self.w2 = w2
self.h2 = h2
def checkCol(self):
if (self.x2 + self.w2 >= self.x1 >= self.x2 and self.y2 + self.h2 >= self.y1 >= self.y2):
return True
elif (self.x2 + self.w2 >= self.x1 + self.w1 >= self.x2 and self.y2 + self.h2 >= self.y1 >= self.y2):
return True
elif (self.x2 + self.w2 >= self.x1 >= self.x2 and self.y2 + self.h2 >= self.y1 + self.h1 >= self.y2):
return True
elif (self.x2 + self.w2 >= self.x1 + self.w1 >= self.x2 and self.y2 + self.h2 >= self.y1 + self.h1 >= self.y2):
return True
else:
return False
def yel_col(self):
if Collisions.checkCol(self):
return True
def ora_col(self):
if Collisions.checkCol(self):
return True
class Sprite:
def __init__(self,x,y,width,height, color):
self.x = x
self.y = y
self.width = width
self.height = height
self.color = color
def render(self,):
pygame.draw.rect(window,self.color,(self.x,self.y,self.width,self.height))
Sprite1=Sprite(win_width/2 ,win_height-60,30,30, blue)
moveX = 0
sprite2x = random.randrange(30, win_width, 30)
sprite3x = random.randrange(30, win_width, 30)
falling_pos = 0
gameLoop=True
while gameLoop:
for event in pygame.event.get():
if (event.type==pygame.QUIT):
gameLoop=False
if (event.type==pygame.KEYDOWN):
if (event.key==pygame.K_LEFT):
moveX = -3
if (event.key==pygame.K_RIGHT):
moveX = 3
window.fill(white)
ground = pygame.draw.rect(window, black, ((0, win_height-30), (win_width, 30)))
Sprite1.x+=moveX
falling_pos += 3
Sprite2=Sprite(sprite2x,falling_pos,30,30, orange)
Sprite3=Sprite(sprite3x, falling_pos, 30, 30, yellow)
collisions1=Collisions(Sprite1.x,Sprite1.y,Sprite1.width,Sprite1.height,Sprite2.x,Sprite2.y,Sprite2.width,Sprite2.height)
collisions2=Collisions(Sprite1.x,Sprite1.y,Sprite1.width,Sprite1.height,Sprite3.x,Sprite3.y,Sprite3.width,Sprite3.height)
Sprite1.render()
Sprite2.render()
Sprite3.render()
if collisions2.checkCol() and collisions2.yel_col():
if Sprite1.width and Sprite1.height > 30:
Sprite1.width -= 5
Sprite1.height -= 5
Sprite1.y += 5
if collisions1.checkCol() and collisions1.ora_col():
if Sprite1.width and Sprite1.height < 300:
Sprite1.width += 5
Sprite1.height += 5
Sprite1.y -= 5
if Sprite1.x < 0:
Sprite1.x = win_width
elif Sprite1.x > win_width:
Sprite1.x = 0
if falling_pos > win_height:
falling_pos = 0
pygame.display.flip()
clock.tick(120)
pygame.quit()

To make them spawn in random locations use random.randint(a, b) to assign the start position.

Related

How to display the inscription and pause the game in Ping Pong with pgzrun in Python

My code
`
import pgzero.music
import pgzrun
import pygame
import time
from pgzero.actor import Actor
WIDTH = 600
HEIGHT = 700
RED = 200, 0, 0
Darkviolet = 104, 34, 139
COLOR1 = 71,60,139
WHITE = (255, 255, 255)
BOX = Rect((20, 100), (100,10))
RADIUS = 13
ball_speed_x = -3
ball_speed_y = 3
hearts = [Actor("heart", (20, 20)), Actor("heart", (55, 20)), Actor("heart", (90, 20))]
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __add(self, other):
return Vector(self.x + other.x, 0)
def __sub__(self, other):
return Vector(self.x - other.x, 0)
class Paddle:
def __init__(self, l, h, s1, s2):
self.position = [l, h]
self.size = s1, s2
def draw(self):
screen.draw.filled_rect(Rect(self.position, self.size), RED)
my_paddle = Paddle(200, 600, 100, 20)
class Rectangle:
def __init__(self,x, y, w, h):
self.x = x
self.y = y
self.h = h
self.w = w
def draw(self):
rect = Rect(self.x, self.y, self.h, self.w)
screen.draw.filled_rect(rect, "blue")
rectangles = []
rectan_count = 0
x = 30
y = 50
while rectan_count < 18:
rectangles.append(Rectangle(x, y, 20, 50))
rectan_count += 1
x += 80
if rectan_count == 7:
y += 30
x = 70
elif rectan_count == 13:
y += 30
x = 110
class Ball:
def __init__(self, x, y):
self.position = Vector(x, y)
def draw(self):
screen.draw.filled_circle((self.position.x, self.position.y), RADIUS, "darkviolet")
def update(self):
pass
ball = Ball(300, 300)
def draw():
global ball_speed_x, ball_speed_y
screen.clear()
my_paddle.draw()
ball.draw()
if hearts:
for heart in hearts:
heart.draw()
for rectangle in rectangles:
if (rectangle.x <= ball.position.x <= (rectangle.x + rectangle.w)) and (rectangle.y <= ball.position.y <= (rectangle.y + rectangle.h)):
ball_speed_y *= -1
rectangles.remove(rectangle)
rectangle.draw()
else:
screen.draw.text("Sorry, you lose!!", (150, 200), color="blue", fontsize=50)
quit()
def on_mouse_move(pos):
x = pos[0]
my_paddle.position[0] = x
def update_ball(dt, paddle_x, paddle_y):
global ball_speed_x, ball_speed_y
ball.position.x -= ball_speed_x
ball.position.y -= ball_speed_y
if (ball.position.x >= WIDTH) or (ball.position.x <= 0):
ball_speed_x *= -1
if (ball.position.y >= HEIGHT) or (ball.position.y <= 0):
ball_speed_y *= -1
if ((paddle_x + 100) >= ball.position.x >= paddle_x) and (paddle_y <= ball.position.y):
ball_speed_y *= -1
if ball.position.y >= HEIGHT:
ball.position.x = WIDTH // 2
ball.position.y = HEIGHT // 2
if hearts:
hearts.pop()
ball_speed_y += 1
def update(dt):
update_ball(dt, my_paddle.position[0], my_paddle.position[1])
pgzrun.go()`
I'm doing ping pong on pgzrun and I don't understand exactly how to make lose and win labels and have the game pause and the ball stop and then restart it again. And I still don't understand how to make rectangular barriers that my ball can bounce off. Many thanks in advance for your help!!

How can I add one point to a score when the scoring system is in a loop?

from kivy.config import Config
Config.set('graphics', 'width', '1050')
Config.set('graphics', 'height', '600')
from kivy.app import App
from kivy.properties import Clock
from kivy.uix.widget import Widget
from kivy.uix.image import Image
from kivy.core.window import Window
import random
class MainWidget(Widget):
from controls import _keyboard_closed, _on_keyboard_down, _on_keyboard_up
drone = None
SCORE = 0
clouds = []
balloons = []
current_speed_y = 0
current_speed_x = 0
drone_coordinates = [(0, 0), (0, 0)]
balloon_coordinates = [(0, 0), (0, 0)]
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.init_drone()
Clock.schedule_interval(self.init_clouds, 1 / 8)
Clock.schedule_interval(self.update, 1 / 600)
Clock.schedule_interval(self.init_balloons, 7)
self._keyboard = Window.request_keyboard(self._keyboard_closed, self)
self._keyboard.bind(on_key_down=self._on_keyboard_down)
self._keyboard.bind(on_key_up=self._on_keyboard_up)
def init_clouds(self, dt):
cloud_types = ["images/cloudbig.png", "images/cloudbig1.png", "images/cloudbig2.png"]
cloud_type = random.choice(cloud_types)
a = self.width
b = self.width - 100
x = random.randint(b, a)
y = random.randint(-100, self.height)
cloud = Image(source=cloud_type,
pos=(x, y))
self.add_widget(cloud)
self.clouds.append(cloud)
def move_clouds(self, *args):
out_cloud_list = [] # If cloud not in screen remove:
for child in self.children and self.clouds:
if child.pos[0] < -50:
out_cloud_list.append(child)
else:
child.pos[0] -= 3
for self.cloud in out_cloud_list:
self.remove_widget(self.cloud)
def init_drone(self):
self.drone = Image(source="images/drone.png",
pos=(100, 300))
self.add_widget(self.drone)
self.drone_coordinates[0] = self.drone.pos
def init_balloons(self, dt):
balloon_types = ["images/yellow_balloon.png", "images/green_balloon.png", "images/purple_balloon.png",
"images/red_balloon.png"]
balloon_type = random.choice(balloon_types)
x = random.randint(100, self.width)
y = random.randint(50, 150)
balloon = Image(source=balloon_type,
pos=(x, y))
self.add_widget(balloon)
self.balloons.append(balloon)
def move_balloons(self):
out_balloon_list = [] # If cloud not in screen remove:
for child in self.children and self.balloons:
if child.pos[0] < -50 or child.pos[1] > self.height-50:
out_balloon_list.append(child)
else:
child.pos[0] -= 2
child.pos[1] += 3
for self.balloon in out_balloon_list:
self.remove_widget(self.balloon)
def get_drone_coordinates(self):
x1, y1 = self.drone.pos
self.drone_coordinates[0] = (x1, y1)
x2, y2 = self.drone.pos[0] + self.drone.width, self.drone.pos[1] + self.drone.height
self.drone_coordinates[1] = (x2, y2)
def get_balloon_coordinates(self):
for balloon in self.balloons:
x1, y1 = balloon.pos[0], balloon.pos[1]
self.balloon_coordinates[0] = (x1, y1)
x2, y2 = balloon.pos[0] + balloon.width, balloon.pos[1] + balloon.height
self.balloon_coordinates[1] = (x2, y2)
def check_drone_collect_balloon(self):
self.get_drone_coordinates()
self.get_balloon_coordinates()
drone_xmax, drone_ymax = self.drone_coordinates[1][0], self.drone_coordinates[1][1]
balloon_xmin, balloon_ymin = self.balloon_coordinates[0][0], self.balloon_coordinates[0][1]
balloon_xmax, balloon_ymax = self.balloon_coordinates[1][0], self.balloon_coordinates[1][1]
drone_center_x = drone_xmax - self.drone.width/2
drone_center_y = drone_ymax - self.drone.height/2
if balloon_xmax >= drone_center_x >= balloon_xmin and balloon_ymax >= drone_center_y >= balloon_ymin:
for balloon in self.balloons:
self.remove_widget(balloon)
def update(self, dt):
self.move_clouds()
self.move_balloons()
self.check_drone_collect_balloon()
self.drone.pos[1] += self.current_speed_y
self.drone.pos[0] += self.current_speed_x
if self.drone.pos[1] <= 0:
self.drone.pos[1] = 0
elif self.drone.pos[1] >= self.height - 70:
self.drone.pos[1] = self.height - 70
if self.drone.pos[0] <= -10:
self.drone.pos[0] = -10
elif self.drone.pos[0] >= self.width - 80:
self.drone.pos[0] = self.width - 80
class BalloonGameApp(App):
def build(self):
Window.clearcolor = (.2, .6, .8, 1)
xd = MainWidget()
return xd
BalloonGameApp().run()
This is the entire code of my program, what I am trying to do is increment 1 to the variable SCORE every time a balloon is "collected".
def check_drone_collect_balloon(self):
self.get_drone_coordinates()
self.get_balloon_coordinates()
drone_xmax, drone_ymax = self.drone_coordinates[1][0], self.drone_coordinates[1][1]
balloon_xmin, balloon_ymin = self.balloon_coordinates[0][0], self.balloon_coordinates[0][1]
balloon_xmax, balloon_ymax = self.balloon_coordinates[1][0], self.balloon_coordinates[1][1]
drone_center_x = drone_xmax - self.drone.width/2
drone_center_y = drone_ymax - self.drone.height/2
if balloon_xmax >= drone_center_x >= balloon_xmin and balloon_ymax >= drone_center_y >= balloon_ymin:
for balloon in self.balloons:
self.remove_widget(balloon)
this function is the one that determines when a balloon is "collected" though when I try to add...
for balloon in self.balloons:
self.remove_widget(balloon)
self.SCORE += 1
print(self.SCORE)
I would expect that the self.SCORE variable would increase by 1 with every balloon that gets "collected" but what happens is that the variable's value increases a lot, up to 60 or even 100 or more with every singular balloon that gets removed. I'm assuming that this happens due to it being inside a for loop, but I don't exactly understand why it happens since the "self.balloons" list would only have 1 item i.e. 1 balloon --as a result of only 1 balloon appearing every 7 seconds-- in it so the loop should only be triggered once.
Are there any suggestions as to how I could make the scoring system work? Any help would be appreciated! Thanks in advance
EDIT:
if balloon_xmax >= drone_center_x >= balloon_xmin and balloon_ymax >= drone_center_y >= balloon_ymin:
for balloon in self.balloons:
self.balloons.remove(balloon)
self.remove_widget(balloon)
self.SCORE += 1
print(self.SCORE)
I added the function to remove balloons from the self.balloons list. But now the self.SCORE function is incremented twice so with each balloon that gets "collected" two is added to the score rather than one. How could I fix this without having to increment self.SCORE by .5

Python pygame how to set the FPS

I am making a python game and I don't know what should I set my FPS to. My game is stuck and not smooth. How do I know what should the FPS be?
This is my code:
http://bin.shortbin.eu:8080/x87bSUKUiN
After running your code I have also noticed frame rate drops which hurt the smoothness of the game.
There are two separate issues here:
1. The FPS drops
The FPS drops probably happen because of something you cannot control, like the garbage collector working.
Even though you have no control of such issues, you can in general improve the performance of your game. See the following screen shot of a profiler run of your game:
You can see that the largest part of the time is spent on blit. However, a very large part of the time is also being spent in get_y_list. The get_y_list method also uses large lists which create a lot of garbage for the garbage collector to later collect, thus causing a further performance hit.
As I understand it, the get_y_list method is part of a very crude method that you're using for collision detection, one which basically takes quadratic time. Your algorithm seems to divide each object into a large amount of 2d cells, and then test collision between each pair of cells. Instead you can just test box to box intersection. If you wish to have a complicated collision shape for the objects you can use other algorithms, the internet is full of them. See this for example: https://developer.mozilla.org/en-US/docs/Games/Techniques/2D_collision_detection
Using other algorithms for collision detection will greatly improve your performance.
2. The game becoming non-smooth when there's an FPS drop.
The x and y positions of your objects are being updated like this for example: player.x -= player.vx. The physically correct method would be: player.x -= player.vx * DELTA_T. Where DELTA_T is the time elapsed since the last frame. If you don't use the time elapsed since the last frame, the speed of motion of your characters becomes dependent on FPS. Which is exactly what we're seeing.
Where do I get DELTA_T you might ask. You do it directly when calling tick:
def main():
global DELTA_T
finish = False
background_x = 0
background_y = 0
background = pygame.image.load(BACKGROUND_IAMGE)
while not finish:
DELTA_T = clock.tick(REFRESH_RATE)
I tried adding the multiplication by DELTA_T but the game becomes unstable, because you assume that objects advance exactly vx 'cells' in each frame to detect collisions and floor penetrations. See my attempt below:
import pygame
pygame.init()
WINDOW_WIDTH = 700
WINDOW_HEIGHT = 500
SIZE = (WINDOW_WIDTH, WINDOW_HEIGHT)
SCREEN = pygame.display.set_mode(SIZE)
pygame.display.set_caption("Python Game")
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
PINK = (255, 0, 255)
BACKGROUND_IAMGE = 'back.png'
clock = pygame.time.Clock()
REFRESH_RATE = 30
FRAMES_DELAY = 3
PLAYER_HEIGHT = 72
PLAYER_WIDTH = 40
MUSHROOM_HEIGHT = 31
MUSHROOM_WIDTH = 40
BLOCK_HEIGHT = 30
BLOCK_WIDTH = 30
FLOOR_Y = 435
DELTA_T = 0
class Player:
def __init__(self, x, y):
self.__images = [pygame.image.load('mario1.png'), pygame.image.load('mario2.png'),
pygame.image.load('mario3.png'), pygame.image.load('mario4.png'),
pygame.image.load('mario5.png'), pygame.image.load('mario6.png')]
self.__current_image = self.__images[0]
self.x = x
self.y = y
self.vx = 5/33.
self.vy = 10/33.
self.__is_mid_air = False
self.__direction = "right"
self.__is_jumping = False
self.__MAX_JUMP = 20
self.__is_walking = False
self.__counter = 0
self.__jump_counter = 0
def reset_jump_counter(self):
self.__jump_counter = 0
def get_bottom_y(self):
return int(self.y + PLAYER_HEIGHT)
def set_walk(self, bol):
self.__is_walking = bol
def set_is_mid_air(self, bol):
self.__is_mid_air = bol
def is_mid_air(self):
return self.__is_mid_air
def get_image(self):
if self.__is_mid_air and self.__direction == "right":
self.__current_image = self.__images[4]
return self.__current_image
if self.__is_mid_air and self.__direction == "left":
self.__current_image = self.__images[5]
return self.__current_image
self.__counter += 1
if self.__counter > FRAMES_DELAY:
self.__counter = 0
if self.__counter == FRAMES_DELAY and self.__direction == "right":
if self.__is_walking and self.__current_image == self.__images[0]:
self.__current_image = self.__images[1]
else:
self.__current_image = self.__images[0]
if self.__counter == FRAMES_DELAY and self.__direction == "left":
if self.__is_walking and self.__current_image == self.__images[2]:
self.__current_image = self.__images[3]
else:
self.__current_image = self.__images[2]
return self.__current_image
def stand_still(self):
if self.__direction == "right":
self.__current_image = self.__images[0]
if self.__direction == "left":
self.__current_image = self.__images[2]
def set_jump(self, bol):
self.__is_jumping = bol
if not bol:
self.reset_jump_counter()
def check_jump(self):
if self.__jump_counter != self.__MAX_JUMP and self.__is_jumping:
self.y -= self.vy * DELTA_T
self.__jump_counter += 1
if self.__jump_counter >= self.__MAX_JUMP:
self.__is_jumping = False
self.__jump_counter = 0
def is_jumping(self):
return self.__is_jumping
def fall(self):
if not self.__is_jumping:
self.y += self.vy * DELTA_T
def get_direction(self):
return self.__direction
def turn_around(self):
if self.__direction == "right":
self.__direction = "left"
elif self.__direction == "left":
self.__direction = "right"
def get_x_list(self):
result = []
for i in range(PLAYER_WIDTH + 1):
result.append(self.x + i)
return result
def get_y_list(self):
result = []
for i in range(PLAYER_HEIGHT + 1):
result.append(self.y + i)
return result
def get_right_x(self):
return self.x + PLAYER_WIDTH
def is_crash(self, obj):
is_x_equals = False
for i in range(int(self.vx * DELTA_T+0.5)):
if self.x + PLAYER_WIDTH + i in obj.get_x_list():
is_x_equals = True
if self.x - i in obj.get_x_list():
is_x_equals = True
is_y_equals = False
for i in range(int(self.vy*DELTA_T+0.5)):
if self.y + PLAYER_HEIGHT + i in obj.get_y_list():
is_y_equals = True
if self.y - i in obj.get_y_list():
is_y_equals = True
return is_x_equals and is_y_equals
class Monster:
def __init__(self, x, y, vx, vy, monster_type):
self.__images = [pygame.image.load(monster_type + '1.png').convert(),
pygame.image.load(monster_type + '2.png').convert()]
if monster_type == "mushroom":
self.__width = MUSHROOM_WIDTH
self.__height = MUSHROOM_HEIGHT
self.__current_image = self.__images[0]
self.__FIRST_X = x
self.__FIRST_Y = y
self.__x = x
self.__y = y
self.__vx = vx
self.__vy = vy
self.__direction = "left"
self.__monster_type = monster_type
self.__counter = 0
def get_image(self):
self.__counter += 1
if self.__counter > FRAMES_DELAY:
self.__counter = 0
if self.__monster_type == "mushroom" and self.__counter == FRAMES_DELAY:
if self.__current_image == self.__images[1]:
self.__current_image = self.__images[0]
self.__current_image.set_colorkey(PINK)
else:
self.__current_image = self.__images[1]
self.__current_image.set_colorkey(PINK)
return self.__current_image
elif self.__monster_type == "mushroom" and self.__counter != FRAMES_DELAY:
self.__current_image.set_colorkey(PINK)
return self.__current_image
def get_x(self):
return self.__x
def get_vx(self):
return self.__vx
def get_vy(self):
return self.__vx
def get_y(self):
return self.__y
def step(self):
if self.__direction == "right":
self.__x += self.__vx * DELTA_T
elif self.__direction == "left":
self.__x -= self.__vx * DELTA_T
def get_direction(self):
return self.__direction
def turn_around(self):
if self.__direction == "right":
self.__direction = "left"
elif self.__direction == "left":
self.__direction = "right"
def get_x_list(self):
result = []
for i in range(MUSHROOM_WIDTH + 1):
result.append(self.__x + i)
return result
def get_y_list(self):
result = []
for i in range(MUSHROOM_HEIGHT + 1):
result.append(self.__y + i)
return result
def is_crash(self, obj):
is_x_equals = False
for i in range(int(self.__vx * DELTA_T+0.5)):
if self.__x + self.__width + i in obj.get_x_list():
is_x_equals = True
if self.__x - i in obj.get_x_list():
is_x_equals = True
is_y_equals = False
for i in range(int(self.__vy * DELTA_T+0.5)):
if self.__y + self.__height + i in obj.get_y_list():
is_y_equals = True
if self.__y - i in obj.get_y_list():
is_y_equals = True
return is_x_equals and is_y_equals
class Block:
def __init__(self, x, y, width=1, height=1):
self.__image = pygame.image.load("block.png")
self.__x = x
self.__y = y
self.__width = width
self.__height = height
def load_image(self, background_x):
for i in range(self.__width):
for j in range(self.__height):
SCREEN.blit(self.__image, (self.__x + (i * BLOCK_WIDTH) + background_x, self.__y + (j * BLOCK_HEIGHT)))
def get_x_list(self):
result = []
for i in range(BLOCK_WIDTH * self.__width + 1):
result.append(self.__x + i)
return result
def get_y_list(self):
result = []
for i in range(BLOCK_HEIGHT * self.__height + 1):
result.append(self.__y + i)
return result
def get_y(self):
return self.__y
def get_x(self):
return self.__x
def get_width(self):
return self.__width
def get_height(self):
return self.__height
def get_bottom_y(self):
return self.__y + (BLOCK_HEIGHT * self.__height)
def get_right_x(self):
return self.__x + self.__width * BLOCK_WIDTH
player = Player(140, FLOOR_Y - PLAYER_HEIGHT)
blocks = [Block(270, 280, 1, 1), Block(301, FLOOR_Y - BLOCK_HEIGHT * 8, 1, 8),
Block(600, FLOOR_Y - BLOCK_HEIGHT * 8, 2, 8)]
monsters = [Monster(380, FLOOR_Y - MUSHROOM_HEIGHT, 3/33., 3/33., "mushroom")]
def main():
global DELTA_T
finish = False
background_x = 0
background_y = 0
background = pygame.image.load(BACKGROUND_IAMGE)
while not finish:
DELTA_T = clock.tick(REFRESH_RATE)
SCREEN.blit(background, (background_x, background_y))
for event in pygame.event.get():
if event.type == pygame.QUIT:
finish = True
block_on_right = False
block_on_left = False
is_player_on_block = False
for block in blocks:
block.load_image(background_x)
for i in range(int(player.vy * DELTA_T+0.5)):
for x in player.get_x_list():
if player.get_bottom_y() + i == block.get_y() and x in block.get_x_list():
is_player_on_block = True
player.y += i
if player.y - i == block.get_bottom_y() and x in block.get_x_list():
player.set_jump(False)
player.y -= i
for i in range(int(player.vx*DELTA_T+0.5)):
for y in player.get_y_list():
if player.get_right_x() + i == block.get_x() and y in block.get_y_list():
block_on_right = True
player.x += (i - 1)
background_x -= (i - 1)
if player.x - i == block.get_right_x() and y in block.get_y_list():
block_on_left = True
player.x -= (i - 1)
background_x += (i - 1)
for monster in monsters:
if monster.is_crash(block):
monster.turn_around()
keys = pygame.key.get_pressed()
if (keys[pygame.K_UP] or keys[pygame.K_SPACE] or keys[pygame.K_w]) and not player.is_mid_air():
player.set_jump(True)
if (keys[pygame.K_RIGHT] or keys[pygame.K_d]) and not block_on_right:
if player.get_direction() != "right":
player.turn_around()
player.set_walk(True)
background_x -= player.vx * DELTA_T
player.x += player.vx * DELTA_T
if (keys[pygame.K_LEFT] or keys[pygame.K_a]) and not block_on_left:
if player.get_direction() != "left":
player.turn_around()
player.set_walk(True)
if background_x != 0:
background_x += player.vx * DELTA_T
player.x -= player.vx * DELTA_T
if not any(keys):
player.stand_still()
player.set_walk(False)
for monster in monsters:
monster.step()
SCREEN.blit(monster.get_image(), (background_x + monster.get_x(), monster.get_y()))
is_player_on_ground = False
for i in range(int(player.vy * DELTA_T+0.5)):
if player.get_bottom_y() + i == FLOOR_Y:
player.y += i
is_player_on_ground = True
if is_player_on_block or is_player_on_ground:
player.set_is_mid_air(False)
else:
player.set_is_mid_air(True)
player.fall()
player.check_jump()
player_image = player.get_image().convert()
player_image.set_colorkey(PINK)
SCREEN.blit(player_image, (player.x + background_x, player.y))
pygame.display.flip()
pygame.quit()
if __name__ == '__main__':
main()

Optimizing and abstracting in pygame [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 3 years ago.
Improve this question
What's the best way to abstract and fix this code? I'm relatively new to coding and pygame, and need some help finishing this project. It's due tomorrow, and any help would be greatly appreciated.
My goal is to create a rocket game where my spaceship should dodge blocks and collect coins, and ultimately winning the game after collecting a certain amount of coins and reaching the top.
So far, I'm having trouble with 3 main things:
I hate that I have many lines of the same code, and want to find a way to condense it.
I have a collision problem with a loophole that I can't fix, the spaceship passes through the block instead of colliding with it whenever it passes through the top half of it
I'm wondering what's the best way to add scoring after collecting the coins to create an if statement that will let the user win after collecting a certain amount and reaching the top
I've tried and failed to create a class to hold my characters, and don't know how to assign them different properties like speed
# goals - get the scoring done by adding 1 to the score if the spaceship collides with the block, then deleting the block and printing the new score
# figure out the collision problem, fix the loophole where the spaceship can pass through the block if it is above the bottom of the block
# figure out a way to reduce redundancy by making a class for the stars and the block
import pygame
import time
import random
pygame.init()
screenWidth = 700
screenHeight = 700
wn = pygame.display.set_mode((screenWidth, screenHeight))
pygame.display.set_caption("Maxym's First Adventure")
clock = pygame.time.Clock()
spaceshipImg = pygame.image.load('spaceship.png')
black = (0, 0, 0)
blue = (50, 120, 200)
def startmenu():
intro = True
while intro:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
wn.fill(blue)
messagedisplay("Spaceship")
pygame.display.update()
gameloop()
def thing(thingx, thingy, thingw, thingh, color):
pygame.draw.rect(wn, color, [thingx, thingy, thingw, thingh])
def coin(coinx, coiny, coinw, coinh, color):
pygame.draw.rect(wn, color, [coinx, coiny, coinw, coinh])
def spaceship(x, y):
wn.blit(spaceshipImg, (x, y))
def messagedisplay(text):
font = pygame.font.Font('freesansbold.ttf', 115)
wn.blit(font.render(text, True, (0, 0, 0)), (0, 0))
pygame.display.update()
time.sleep(2)
gameloop()
def win():
messagedisplay("You win")
def crash():
messagedisplay("You crashed")
def collect():
score +=1
def gameloop():
x = screenWidth * .45
y = screenHeight * .8
width = 75
height = 132
velocity = 50
starx = random.randrange(0, screenWidth)
stary = 0
starspeed = 30
starw = 50
starh = 50
star2x = random.randrange(0, screenWidth)
star2y = 0
star2speed = 50
star2w = 80
star2h = 80
star3x = random.randrange(0, screenWidth)
star3y = 0
star3speed = 70
star3w = 30
star3h = 30
star4x = random.randrange(0, screenWidth)
star4y = 0
star4speed = 30
star4w = 200
star4h = 40
coinx = random.randrange(0, screenWidth/2)
coiny = random.randrange(0, screenHeight)
coinw = 20
coinh = 20
coin2x = random.randrange(0, screenWidth/2)
coin2y = random.randrange(0, screenHeight)
coin2w = 20
coin2h = 20
coin3x = random.randrange(screenWidth/2, screenWidth)
coin3y = random.randrange(0, screenHeight)
coin3w = 20
coin3h = 20
coin4x = random.randrange(screenWidth/2, screenWidth)
coin4y = random.randrange(0, screenHeight)
coin4w = 20
coin4h = 20
coin5x = random.randrange(0, screenWidth)
coin5y = random.randrange(0, screenHeight)
coin5w = 20
coin5h = 20
run = True
while run:
pygame.time.delay(100)
wn.fill((255, 255, 255))
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
print(event)
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT] and x > velocity:
x -= velocity
if keys[pygame.K_RIGHT] and x < screenWidth-width-velocity:
x += velocity
if keys[pygame.K_UP] and y > velocity:
y -= velocity
if keys[pygame.K_DOWN] and y < screenHeight-height-velocity:
y += velocity
pygame.draw.rect(wn, (255, 255, 255), (x, y, width, height))
thing(starx, stary, starw, starh, black)
thing(star2x, star2y, star2w, star2h, black)
thing(star3x, star3y, star3w, star3h, black)
thing(star4x, star4y, star4w, star4h, black)
coin(coinx, coiny, coinw, coinh, blue)
coin(coin2x, coin2y, coin2w, coin2h, blue)
coin(coin3x, coin3y, coin3w, coin3h, blue)
coin(coin4x, coin4y, coin4w, coin4h, blue)
coin(coin5x, coin5y, coin5w, coin5h, blue)
stary += starspeed
star2y += star2speed
star3y += star3speed
star4y += star4speed
if stary > screenHeight:
stary = 0 - starh
starx = random.randrange(0, screenWidth)
if star2y > screenHeight:
star2y = 0 - star2h
star2x = random.randrange(0, screenWidth)
if star3y > screenHeight:
star3y = 0 - star3h
if star4y > screenHeight:
star4y = 0 - star4h
score = 0
if y + height >= stary and y + height <= stary + starh or y <= stary + starh and y + height >= stary + starh:
if x + width >= starx and x + width <= starx + starw or x <= starx + starw and x + width >= starx + starw:
crash()
if y + height >= star2y and y + height <= star2y + star2h or y <= star2y + star2h and y + height >= star2y + star2h:
if x + width >= star2x and x + width <= star2x + star2w or x <= star2x + star2w and x + width >= star2x + star2w:
crash()
if y + height >= star3y and y + height <= star3y + star3h or y <= star3y + star3h and y + height >= star3y + star3h:
if x + width >= star3x and x + width <= star3x + star3w or x <= star3x + star3w and x + width >= star3x + star3w:
crash()
if y + height >= star4y and y + height <= star4y + star4h or y <= star4y + star4h and y + height >= star4y + star4h:
if x + width >= star4x and x + width <= star4x + star4w or x <= star4x + star4w and x + width >= star4x + star4w:
crash()
if y + height >= coiny and y + height <= coiny + coinh or y <= coiny + coinh and y + height >= coiny + coinh:
if x + width >= coinx and x + width <= coinx + coinw or x <= coinx + coinw and x + width >= coinx + coinw:
collect()
if y + height >= coin2y and y + height <= coin2y + coin2h or y <= coin2y + coin2h and y + height >= coin2y + coin2h:
if x + width >= coin2x and x + width <= coin2x + coin2w or x <= coin2x + coin2w and x + width >= coin2x + coin2w:
collect()
if y + height >= coin3y and y + height <= coin3y + coin3h or y <= coin3y + coin3h and y + height >= coin3y + coin3h:
if x + width >= coin3x and x + width <= coin3x + coin3w or x <= coin3x + coin3w and x + width >= coin3x + coin3w:
collect()
if y + height >= coin4y and y + height <= coin4y + coin4h or y <= coin4y + coin4h and y + height >= coin4y + coin4h:
if x + width >= coin4x and x + width <= coin4x + coin4w or x <= coin4x + coin4w and x + width >= coin4x + coin4w:
collect()
if y + height >= coin5y and y + height <= coin5y + coin5h or y <= coin5y + coin5h and y + height >= coin5y + coin5h:
if x + width >= coin5x and x + width <= coin5x + coin5w or x <= coin5x + coin5w and x + width >= coin5x + coin5w:
collect()
if y <= 10 and score >= 3:
win()
spaceship(x, y)
pygame.display.update()
clock.tick(60)
startmenu()
gameloop()
pygame.quit()
quit()
Keep values in list instead of variables star, star2, etc. And then you can use for loop to make code shorter.
Instead of thingx, thingy, thingw, thingh (and similar) you should use pygame.Rect() and then you can have
def thing(thing_rect, color):
instead of
def thing(thingx, thingy, thingw, thingh, color):
and
pygame.draw.rect(wn, color, thing_rect)
instead of
pygame.draw.rect(wn, color, [thingx, thingy, thingw, thingh])
pygame.Rect has functions to check collisions so you don't have to write long code in if.
pygame.Rect has rect.bottom to get rect.y + rect.height and rect.right to get rect.x + rect.width
An immediate gain would be made from moving the "coin" into a simple data structure, then keep a list of them, rather than 6 quasi-static objects.
This allows you to loop over a list of coins, with only a single set of testing code. Consider the snippet:
MAX_COINS = 6
all_coins = []
def addCoin( colour=(0,30,240), coinw=20, coinh=20 ):
global all_coins
coinx = random.randrange(0, screenWidth/2)
coiny = random.randrange(0, screenHeight)
all_coins.append( [ colour, pygame.Rect( coinx, coiny, coinw, coinh ) ] )
def drawCoin( window, coin ):
coin_colour = coin[0]
coin_rect = coin[1]
pygame.draw.rect( window, coin_colour, coin_rect )
...
# Create 6 coins, or add more if one/some collected
for i in range( MAX_COINS - len( all_coins ) ):
addCoin()
# Draw all coins
for coin in all_coins:
drawCoin( screen, coin )
# Check coin collision with player
for coin in all_coins:
player_rect = pygame.Rect( x, y, width, height )
coin_rect = coin[1]
if ( pygame.Rect.colliderect( player_rect, coin_rect ) ):
all_coins.del( coin ) # remove the coin from the coin-list
collect()
This code presents a coin-solution which perhaps has a few less lines than the existing code, but now supports any number of coins, and protects the code from typo-bugs. Furthermore if the collision code needs to be changed, there is only one-set of logic to analyse and fix. A nearly identical change could be made for the Star objects (maybe even just store the type into the coin-like object, and use it for both).
Of course this code closely matches the workings of the already-existing PyGame Sprite library. Refactoring to use sprites would be an even better change.

Tkinter Canvas game needs a slight rework

Here's the Code firstly - I'll get into the problems after. Although I'm sure you can spot plenty without needing to scroll to far down.
from tkinter import *
import random
import math
import time
test = random.randint(10,40)
print(test)
class Game():
global x0,y0,x1,y1,Game,debug, Player, mousex0, mousey0, mousex1,
mousey1, moveTowardMouse, rayCast, speed, frayCast, fx0, fy0,
fx1, fy1, Food
def move(event):
global x0,y0,x1,y1,mouseX0,mouseY0,mouseX1,mouseY1,fx0, fy0,
fx1, fy1,Food
mouseX0 = event.x - 10
mouseY0 = event.y - 10
mouseX1 = event.x + 10
mouseY1 = event.y + 10
Game.coords(rayCast, x0, y0, mouseX1, mouseY1)
if x0 != mouseX0 and x0 < mouseX0:
x0 = x0 + speed
x1 = x1 + speed
Game.coords(Player, x0, y0, x1, y1)
if x0 != mouseX0 and x0 > mouseX0:
x0 = x0 - speed
x1 = x1 - speed
Game.coords(Player, x0, y0, x1, y1)
if y0 != mouseY0 and y0 < mouseY0:
y0 = y0 + speed
y1 = y1 + speed
Game.coords(Player, x0, y0, x1, y1)
if y0 != mouseY0 and y0 > mouseY0:
y0 = y0 - speed
y1 = y1 - speed
Game.coords(Player, x0, y0, x1, y1)
Game.coords(frayCast, x0,y0, fx0,fy0)
if fx0 > x0 and (fx0 - x0) < 20:
fx0 = fx0 + 0.5
fx1 = fx1 + 0.5
Game.coords(Food, fx0,fy0,fx1,fy1)
if fx0 < x0 and (fx0 + x0) < 20:
fx0 = fx0 - 0.5
fx1 = fx1 - 0.5
Game.coords(Food, fx0,fy0,fx1,fy1)
if fy0 > y0 and (fy0 - y0) < 20:
fy0 = fy0 + 0.5
fy1 = fy1 + 0.5
Game.coords(Food, fx0,fy0,fx1,fy1)
if fy0 < y0 and (fy0 - y0) < 20:
fy0 = fy0 - 0.5
fy1 = fy1 - 0.5
Game.coords(Food, fx0,fy0,fx1,fy1)
if fx0 > x0 and (fx0 - x0) < 5:
if fy0 > y0 and (fy0 - y0) <5:
Game.delete(Food)
x0 = x0 - fx1
y0 = y0 - fy1
Game.coords(Player, x0,y0,x1,y1)
fx0 = 20
fy0 = 20
fx1 = test + 20
fy1 = test + 20
x0 = -50
y0 = -50
x1 = 50
y1 = 50
speed = 1
mouseX0 = 0
mouseY0 = 0
mouseX1 = 0
mouseY1 = 0
debug = "DEBUGGED"
module = Tk()
Game = Canvas(module, width=1000, height=1000)
Player = Game.create_oval(x0,y0,x1,y1,fill="blue")
Food = Game.create_oval(fx0, fy0, fx1, fy1, fill="red")
rayCast = Game.create_line(x0,y0,mouseX1,mouseY1)
frayCast = Game.create_line(x0,y0,mouseX1,mouseY1)
module.bind('<Motion>', move)
Game.pack()
module.mainloop()
So I'm having just a slight "oh snap" just a moment ago when I realised that my code was basically useless.
In the game I'm creating, I'm wanting the Player controlled sprite on the canvas to move at a slow speed towards the mouse. I googled how to get the mouse coordinates, it told me that I could use the event to get the coords in a function. However since getting those coords I've slowly put all the major sprite movement calculations in the same function resulting in a functional game... that only does something as long as your moving the mouse.
The idea is that the NPC-sprite is a random size and spawns in a random space on the canvas. It moves in random directions in a slow speed until it is within "20" of the player controlled sprite, in which case it moves (faster) away from the player-controlled sprite.
Aside from the fact that all this only happens when you move the mouse (and that I'm still using raycasting to get a trajectory for the sprites to follow, there are a few more issues I need help with.
Firstly, the random size of the NPC sprite works great. But it spawns exactly the same place every time.
Secondly, the NPC's "avoid the player" code seems to be... less than functional. Basically it works fine, but then it just keeps working even after the player has moved "20" away from the sprite.
Lastly, I'm having an issue with the coords of the sprites themselves. You see the raycasting reveals that the true coords for the sprites are not in the centre of their canvas representation of a circle, but instead in the top left corner of what would be an invisible square around said circle. I need this to be in the centre of the sprite rather than not otherwise the gameplay mechanics become a little bit buggy.
Back to the biggest issue (With the way updates work for sprite coords) I'm fine with setting up like Updates per Tick within the game and run all my calculations every tick, but then I wouldn't know how to get the mouse coords outside of using that event.
Long story short some help would be much appreciated.
Here is your improved code. I explained everything in the comments.
from tkinter import *
import random
import math
import time
class Game():
# This __init__ will run first when you run Game()
# Learn to always set up your programs this way.
def __init__(self):
test = random.randint(10,40)
print(test)
# Put self. before the name of every variable you might want to use outside this __init__ function.
# this way you don't need to define them separately in every function
# You defined these xs and ys by hand so of course it remains in the same position.
# define them randomly and you'll be fine.
self.fx0 = 20
self.fy0 = 20
self.fx1 = test + 20
self.fy1 = test + 20
self.x0 = -50
self.y0 = -50
self.x1 = 50
self.y1 = 50
self.speed = 1
self.mouseX0 = 0
self.mouseY0 = 0
self.mouseX1 = 0
self.mouseY1 = 0
self.debug = "DEBUGGED"
# Added these two variables
self.tick_intervals = 10
self.food_exists = True
self.module = Tk()
#Game is the name of your class don't use it here: (use game instead)
self.game = Canvas(self.module, width=1000, height=1000)
self.Player = self.game.create_oval(self.x0,self.y0,self.x1,self.y1,fill="blue")
self.Food = self.game.create_oval(self.fx0, self.fy0, self.fx1, self.fy1, fill="red")
self.rayCast = self.game.create_line(self.x0,self.y0,self.mouseX1,self.mouseY1)
self.frayCast = self.game.create_line(self.x0,self.y0,self.mouseX1,self.mouseY1)
self.game.pack()
self.move()
self.module.mainloop()
def move(self):
# To get mouse position on your whole screen: .winfo_pointerx() and .winfo_pointery()
# To get position of widget (self.game) on screen .winfo_rootx()
# x and y below are the same as mouse event.x and event.y without the need to bind anything to self.module
x = self.game.winfo_pointerx()-self.game.winfo_rootx()
y = self.game.winfo_pointery()-self.game.winfo_rooty()
# If you have a value you use more than 1 time,
# it's best to define it first then put that here
# instead of writing it out every time.
# this way you can change it very easily
# better define these in __init__ function with self.something = ...
self.mouseX0 = x - 10 # define var for 10
self.mouseY0 = y - 10
self.mouseX1 = x + 10
self.mouseY1 = y + 10
# You should also design a (visible or invisible) wall around the screen
# so your Player and Food can't run off the screen.
# Basically it's numbers and some if statements.
# If you don't put elif here x and y might get resized then resized again.
# but you only need to resize them once a tick.
# You don't need != here. < > are enough.
# Look up += -= *= /= functions.
if self.x0 < self.mouseX0:
self.x0 += self.speed
self.x1 += self.speed
elif self.x0 > self.mouseX0:
self.x0 -= self.speed
self.x1 -= self.speed
if self.y0 < self.mouseY0:
self.y0 += self.speed
self.y1 += self.speed
elif self.y0 > self.mouseY0:
self.y0 -= self.speed
self.y1 -= self.speed
# Need to call these once a tick and not every time you change x or y
self.game.coords(self.rayCast, self.x0, self.y0, self.mouseX1, self.mouseY1)
self.game.coords(self.Player,self.x0,self.y0,self.x1,self.y1)
# After you eat food this shouldn't run any more.
# This is why Player kept getting bigger and bigger
if self.food_exists:
if self.fx0 > self.x0 and (self.fx0 - self.x0) < 20: # define var for 20
self.fx0 += 0.5 # define var for 0.5
self.fx1 += 0.5
elif self.fx0 < self.x0 and (self.fx0 + self.x0) < 20:
self.fx0 -= 0.5
self.fx1 -= 0.5
if self.fy0 > self.y0 and (self.fy0 - self.y0) < 20:
self.fy0 += 0.5
self.fy1 += 0.5
elif self.fy0 < self.y0 and (self.fy0 - self.y0) < 20:
self.fy0 -= 0.5
self.fy1 -= 0.5
if self.fx0 > self.x0 and (self.fx0 - self.x0) < 5: # define var for 5
if self.fy0 > self.y0 and (self.fy0 - self.y0) <5:
self.game.delete(self.Food)
self.x0 -= self.fx1
self.y0 -= self.fy1
self.food_exists = False
self.game.coords(self.Food,self.fx0,self.fy0,self.fx1,self.fy1)
self.game.coords(self.frayCast, self.x0,self.y0, self.fx0,self.fy0)
# This automatically runs self.move after (self.tick_intevals) miliseconds
self.game.after(self.tick_intervals,self.move)
# This IF runs the Game() only if you run the script yourself.
# This way if you imported this script into another program it wouldn't run Game()
# Learn to always use this if for your programs
if __name__=='__main__':
Game()

Categories