So I tried creating Conway's game of life in python with pygame. I made this without watching any tutorials, which is probably why it is so broken. It seems to be working fine, but when I creates a glider it seems to just break after a few generations. I looked at some other posts about my problem and added their solutions but that didn't make it work either. I know this is a lot to ask for, but can someone at least identify the problem.
Here is my code. I expected the glider to function as do they are supposed to, but it ended up just breaking in a few generations
Code:
main.py:
from utils import *
from grid import Grid
running = True
t = Grid(30)
while running:
pygame.display.set_caption(f'Conways Game of Life <Gen {t.generations}>')
clock.tick(200)
screen.fill(background_colour)
if not t.started:
t.EditMode()
else:
t.Update()
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
pygame.display.flip()`
grid.py:
import cell
from utils import *
class Grid:
def __init__(self, size):
self.cells = []
self.cellSize = size
self.generations = 0
self.tick = 1
self.started = False
self.GenerateGrid()
def GenerateGrid(self):
x, y = 0, 0
while y < screen.get_height():
while x < screen.get_width():
c = cell.Cell(self, (x,y), self.cellSize)
self.cells.append(c)
x+=self.cellSize
x = 0
y+=self.cellSize
def EditMode(self):
self.Draw()
if self.started:
return
for cell in self.cells:
if pygame.mouse.get_pressed()[0]:
if cell.rect.collidepoint(pygame.mouse.get_pos()):
cell.state = 1
if pygame.mouse.get_pressed()[2]:
if cell.rect.collidepoint(pygame.mouse.get_pos()):
cell.state = 0
keys = pygame.key.get_pressed()
if keys[pygame.K_RETURN]:
self.started = True
def Draw(self):
for cell in self.cells:
cell.Draw()
def Update(self):
self.Draw()
self.tick -= 0.05
if self.tick < 0:
for cell in self.cells:
cell.UpdateState()
for cell in self.cells:
cell.state = cell.nextState
self.tick = 1
self.generations+=1
cell.py
from utils import *
class Cell:
def __init__(self, grid, position:tuple, size):
self.grid = grid
self.size = size
self.position = pygame.Vector2(position[0], position[1])
self.rect = pygame.Rect(self.position.x, self.position.y, self.size, self.size)
self.state = 0
self.nextState = self.state
def Draw(self):
pygame.draw.rect(screen, (0,0,0), self.rect)
if self.state == 0:
pygame.draw.rect(screen, (23,23,23), (self.position.x+4, self.position.y+4, self.size-4, self.size-4))
else:
pygame.draw.rect(screen, (255,255,255), (self.position.x+4, self.position.y+4, self.size-4, self.size-4))
def UpdateState(self):
rect = pygame.Rect(self.position.x-self.size, self.position.y-self.size, self.size*3, self.size*3)
pygame.draw.rect(screen, (0,0,0), rect)
targetCells = []
for c in self.grid.cells:
if rect.colliderect(c.rect):
targetCells.append(c)
livingAmt = 0
for c in targetCells:
if c.rect.x == self.rect.x and c.rect.y == self.rect.y:
continue
if c.state == 1:
livingAmt+=1
if self.state == 1:
if livingAmt > 3 or livingAmt <2:
self.nextState = 0
if self.state ==0:
if livingAmt == 3:
self.nextState =1
utils.py
import pygame
background_colour = (23, 23, 23)
screen = pygame.display.set_mode((900, 900))
clock = pygame.time.Clock()
running = True
Your function UpdateState both counts a cell's neighbors and updates the cell's state. Since you call that function in a loop, both are done together, which does not work, as explained here. You must split the "count" phase from the "update state" phase.
Related
I imagine this is a simple fix and have seen similar questions, but this is really frustrating me. Basically when you run the game, it only advances the simulation on a keypress, and not at a set framerate.
https://pastebin.com/aP6LsMMA
The main code is:
pg.init()
clock = pg.time.Clock()
FPS = 10
# ...
Game = Control()
while not Game.done:
Game.main_loop()
pg.display.update()
clock.tick(FPS)
pg.quit()
and the event handler method - within the Control class - is:
def event_handler(self):
for event in pg.event.get():
if event.type == pg.QUIT:
self.done = True
elif event.type == pg.KEYDOWN:
self.scene.process_event(event)
The weird thing to me is that, at the bottom of the pastebin, is my old testing code that is not done with classes for Scenes/Control. However, to my eye it should work exactly the same. I've tried putting in the clock both in and out of the Control class to no avail.
Any help (and general tips!) greatly appreciated.
Thanks
process_event() should only change variables which depend on events but it shouldn't update other values which should be updated in every frame. You have to move some elements to new method update() and execute it in every loop.
More or less
class Scene:
def process_event(self, event):
pass
def update(self):
pass
class GamePlayState(Scene):
def process_event(self, event):
self.snake.get_key(event)
def update(self):
self.snake.update()
self.snake.food_check(self.apple)
self.snake.collision_check()
if self.snake.alive == False:
print("GAME OVER")
print(self.snake.points)
self.done = True
class Control:
def update(self):
self.scene.update()
def main_loop(self):
self.event_handler()
self.update()
self.scene_checker()
self.draw()
Full working code
import pygame as pg
import sys
import random
import queue
# TODO: Walls, queue for keypress, scene stuff, 2 player, difficulty(?)
""" ######################
PREAMBLE
###################### """
""" Dictionaries for direction/velocity mapping - stolen from https://github.com/Mekire """
DIRECT_DICT = {"left" : (-1, 0), "right" : (1, 0),
"up" : (0,-1), "down" : (0, 1)}
KEY_MAPPING = {pg.K_LEFT : "left", pg.K_RIGHT : "right",
pg.K_UP : "up", pg.K_DOWN : "down"}
OPPOSITES = {"left" : "right", "right" : "left",
"up" : "down", "down" : "up"}
""" Colour Mapping """
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
DARK_GREY = (70, 70, 70)
GREY = (211, 211, 211)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
COLOUR_MAP = {"snake": GREEN, "apple": RED, "wall": BLACK, "surface": GREY, "background": DARK_GREY }
""" ################
CLASSES
################ """
""" ####################### Object Classes ########################## """
class Square:
""" All other objects in the game will be built up from this """
def __init__(self, pos, colour, length):
self.xi, self.yi = pos # i for index, p for pixel
self.colour = colour
self.length = length
def display(self):
xp, yp = self.sq_to_pixs(self.xi, self.yi) # (x = left side, y = top edge)
pg.draw.rect(screen, self.colour, (xp, yp, self.length, self.length), 0)
def sq_to_pixs(self, x, y):
# Converts index of square to pixel coords
px = (x+1)*(2*MARGIN + SQUARE_SIZE) - MARGIN - SQUARE_SIZE
py = (y+1)*(2*MARGIN + SQUARE_SIZE) - MARGIN
return (px, py)
def index_coords(self): # TODO - remove for direct ref?
return (self.xi, self.yi)
class Arena:
""" A grid within which the game takes place """
def __init__(self, size, square_length, colour):
self.size = size # i.e. number of squares = size**2 for square arena
self.length = square_length # i.e. per square dimension
self.colour = colour
self.squares = [ [] for i in range(self.size) ]
for y in range(self.size):
for x in range(self.size):
self.squares[y].append(Square((x,y), self.colour, self.length))
def display(self):
for y in self.squares:
for square in y:
square.display()
class Snake:
""" Class for the agent(s) """
def __init__(self, pos, colour, square_length):
self.xi, self.yi = pos
self.colour = colour
self.size = 3
self.length = square_length
self.direction = "right"
self.direction_queue = queue.Queue(4) # TODO
self.points = 0
self.growing = False
self.alive = True
self.squares = []
for x in range(self.size): # horizontal initial orientation
self.squares.append(Square((self.xi - x, self.yi), self.colour, self.length))
def display(self):
for square in self.squares:
square.display()
def food_check(self, apple):
if self.squares[0].index_coords() == apple.square.index_coords():
self.growing = True
self.points += apple.points_value
apple.respawn([self])
def collision_check(self, walls = None):
xh, yh = self.squares[0].index_coords()
body = self.squares[-1:0:-1] # going backwards thru array as forwards [0:-1:1] didnt work...
def _collide(obstacles):
for sq in obstacles:
_x, _y = sq.index_coords()
if (_x == xh) and (_y == yh):
self.alive = False
_collide(body)
if walls is not None:
_collide(walls)
def update(self):
# Add new head based on velocity and old head
velocity = DIRECT_DICT[self.direction]
head_coords = [ (self.squares[0].index_coords()[i] + velocity[i]) for i in (0,1) ]
# Wrap around screen if reach the end
for i in (0, 1):
if head_coords[i] < 0:
head_coords[i] = SQUARES_PER_ARENA_SIDE - 1
elif head_coords[i] > SQUARES_PER_ARENA_SIDE - 1:
head_coords[i] = 0
self.squares.insert(0, Square(head_coords, self.colour, self.length))
if self.growing:
self.growing = False
else:
del self.squares[-1]
"""
def queue_key_press(self, key):
for keys in KEY_MAPPING:
if key in keys:
try:
self.direction_queue.put(KEY_MAPPING[keys], block=False)
break
except queue.Full:
pass
"""
class Player(Snake):
""" Human controlled snake via arrow keys """
def __init__(self, pos, colour, size):
Snake.__init__(self, pos, colour, size)
def get_key(self, event):
if event.type == pg.KEYDOWN and event.key in KEY_MAPPING:
new_direction = KEY_MAPPING[event.key]
if new_direction != OPPOSITES[self.direction]:
self.direction = new_direction
class Apple:
""" Food our (veggie) snake is greedily after """
def __init__(self, colour, length, points_value, snake):
self.colour = colour
self.length = length
self.xi, self.yi = self._rand_coords()
self.points_value = points_value
self.square = Square((self.xi, self.yi), self.colour, self.length)
def _rand_coords(self):
rand_num = lambda x: random.randint(0, x)
_x = rand_num(SQUARES_PER_ARENA_SIDE-1)
_y = rand_num(SQUARES_PER_ARENA_SIDE-1)
return _x, _y
def respawn(self, obstacles):
_x, _y = self._rand_coords()
for ob in obstacles:
for sq in ob.squares:
while sq.index_coords() == (_x, _y):
_x, _y = self._rand_coords()
self.square.xi, self.square.yi = _x, _y
def display(self):
self.square.display()
""" ################ SCENES ####################### """
class Scene:
""" Overload most of this - barebones structure
A bit pointless in current state but easily expanded """
def __init__(self):
self.done = False
def when_activated(self):
pass
def reset(self):
self.done = False
def render(self):
pass
def process_event(self, event):
pass
def update(self):
pass
class StartUp(Scene):
def __init__(self):
Scene.__init__(self)
def render(self):
# test placeholder
pass
def when_activated(self):
print("Press any key to continue")
def process_event(self, event):
if event.type == pg.KEYDOWN:
self.done = True
class GamePlayState(Scene):
def __init__(self):
Scene.__init__(self)
self.arena = Arena(SQUARES_PER_ARENA_SIDE, SQUARE_SIZE, COLOUR_MAP["surface"])
self.snake = Player(SNAKE_START, COLOUR_MAP["snake"], SQUARE_SIZE)
self.apple = Apple(COLOUR_MAP["apple"], SQUARE_SIZE, 1, self.snake)
self.font = pg.font.SysFont("courier new", 50)
def render(self):
screen.fill(COLOUR_MAP["background"])
self.arena.display()
self.apple.display()
self.snake.display()
text = self.font.render(str(self.snake.points), True, [255,255,255])
screen.blit(text, (500, 400))
def process_event(self, event):
self.snake.get_key(event)
def update(self):
self.snake.update()
self.snake.food_check(self.apple)
self.snake.collision_check()
if self.snake.alive == False:
print("GAME OVER")
print(self.snake.points)
self.done = True
""" ################## CONTROL CLASS #########################"""
class Control:
def __init__(self):
#self.clock = pg.time.Clock()
#self.fps = FPS
self.done = False
self.scene_array = [StartUp(), GamePlayState()]
self.scene_index = 0 # dirty way whilst dict method needs tinkering
#self.scene_dict = {"START": StartUp(), "GAME": GamePlayState()} #TODO
self.scene = self.scene_array[self.scene_index]
#self.scene = self.scene_dict["START"]
self.scene.when_activated()
def event_handler(self):
for event in pg.event.get():
if event.type == pg.QUIT:
self.done = True
elif event.type == pg.KEYDOWN:
self.scene.process_event(event)
def scene_checker(self):
if self.scene.done:
self.scene.reset() # for reuse - TODO
self.scene_index = (self.scene_index + 1) % len(self.scene_array)
self.scene = self.scene_array[self.scene_index]
self.scene.when_activated()
#self.scene = self.scene_dict[self.scene.next]
def update(self):
self.scene.update()
def draw(self):
self.scene.render()
def main_loop(self):
self.event_handler()
self.update()
self.scene_checker()
self.draw()
""" ################ RUN GAME ################ """
""" Game paramaters """
SQUARE_SIZE = 20 # pixels
SQUARES_PER_ARENA_SIDE = 20 # squares
MARGIN = 2 # pixels
SNAKE_START = (int(SQUARES_PER_ARENA_SIDE/2), int(SQUARES_PER_ARENA_SIDE/2)) # square coords
pg.init()
clock = pg.time.Clock()
# Square.display() and a few others need a direct reference to "screen" TODO implement better
w, h = 620, 620
SCREEN_SIZE = [w, h]
FPS = 10
screen = pg.display.set_mode(SCREEN_SIZE)
Game = Control()
while not Game.done:
Game.main_loop()
pg.display.update()
clock.tick(FPS)
pg.quit()
I wrote this physics simulation in pygame and the collision mechanism is not working properly. It seems to work when I collide the player character with the wall from above the wall or from the left and not work for collisions from the bottom or from the right
I have been trying to find this bug for some time but I just have no clue as to what might cause this. I am using python 3.7.3 and pygame 1.9.5 (latest versions as of date)
I am sorry for pasting an entire file but I just have no Idea where the problem is
import pygame # import the pygame library to have access to game building tools
import math
# these variables will be used to hold game objects and draw them
rigid_bodies = []
g = 100
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
PURPLE = (127, 0, 255)
RED = (200, 0, 0)
GREEN = (0, 200, 0)
BLUE = (0, 0, 200)
class RigidBody(pygame.Rect):
"""
represents a rectangular object that acts according to newton's laws of motion
"""
def __init__(self, canvas, color, m, u, x, y, w, h):
"""
called automatically when a new object is created to initialize the object
:param canvas: the canvas on which to draw the object
:param color: the color of the object
:param m: the mass of the object
:param u: Coefficient of friction
:param x: the starting position of the object on the x axis
:param y: the starting position of the object on the y axis
:param w: the width of the object
:param h: the height of the object
"""
super().__init__(x, y, w, h) # initialize the parent Rect object
self.canvas = canvas
self.color = color
self.m = m
self.u = u
self.x_speed = 0 # the speed of the object on the x axis
self.y_speed = 0 # the speed of the object on the y axis
def apply_force(self, axis, F, initiator=None):
"""
used to apply force on the object
:param axis: the axis of the force
:param F: the amount of force to apply
:param initiator: the object that is applying the force
"""
a = F / self.m # calculate the acceleration the object should have
if axis == 'y':
self.y_speed += a
elif axis == 'x':
self.x_speed += a
if initiator:
initiator.apply_force(axis, -1 * F) # apply normal force
print('colliding')
def inertia(self):
"""
shall be run every frame to make the object move according to its speed
if possible and take the necessary steps if not
"""
# go:
self.x += self.x_speed
self.y += self.y_speed
for body in rigid_bodies:
if self.colliderect(body): # if collide with another object:
self.x -= self.x_speed # go back
self.y -= self.y_speed
body.apply_force('x', self.m * self.x_speed, self) # and apply force on that object
body.apply_force('y', self.m * self.y_speed, self)
break
def draw(self):
"""
shall be run every frame to draw the object on the canvas
"""
pygame.draw.rect(self.canvas, self.color, (self.x, self.y, self.w, self.h))
class Controller:
def __init__(self, character, F):
"""
initialize the controller object
:param character: the character to control
:param F: the force to apply to the character for every frame a button is being pressed
"""
self.character = character
self.up = 0 # whether to move up or not
self.down = 0 # whether to move down or not
self.left = 0 # whether to move left or not
self.right = 0 # whether to move right or not
self.F = F
def stop(self):
"""
stops applying force on the object
"""
self.up = 0
self.down = 0
self.left = 0
self.right = 0
def run(self):
"""
shall be run every frame to apply force on the character according to user input
"""
self.character.apply_force('y', -self.F * self.up)
self.character.apply_force('y', self.F * self.down)
self.character.apply_force('x', -self.F * self.left)
self.character.apply_force('x', self.F * self.right)
def main():
"""
the main function contains the main loop
that runs repeatedly while the game is running
"""
crashed = False # tells if the program crashed or if the window was closed
pygame.init() # required to use pygame
canvas = pygame.display.set_mode((1000, 700)) # define the canvas
clock = pygame.time.Clock() # will be used to limit the number of times a loop runs per second
pygame.display.set_caption('the dot game V2')
character = RigidBody(canvas, WHITE, 1000, 0.3, 500, 500, 20, 50) # initialize the character
player = Controller(character, 500) # initialize the controller
rigid_bodies.append(RigidBody(canvas, WHITE, math.inf, 0, 300, 300, 300, 20)) # initialize the wall
while not crashed:
# handle inputs:
for event in pygame.event.get():
if event.type == pygame.QUIT:
crashed = True
elif event.type == pygame.MOUSEBUTTONUP:
pass
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP:
player.up = 1
elif event.key == pygame.K_DOWN:
player.down = 1
elif event.key == pygame.K_LEFT:
player.left = 1
elif event.key == pygame.K_RIGHT:
player.right = 1
elif event.type == pygame.KEYUP:
player.stop()
player.run()
character.inertia()
canvas.fill(BLACK)
character.draw()
for body in rigid_bodies:
body.draw()
pygame.display.update()
clock.tick(60)
if __name__ == '__main__':
main()
I suspect that the problem is with either the "inertia" or the "apply_force" functions but I just cant figure out WHAT is the problem with those functions
The character should stop moving every time it hits the wall, but when it hits the wall from below or from the right it gets stuck and can only move up or to the left
The issue is caused by casting a floating point value to int and can be solved by:
stored_pos = (self.x, self.y)
self.x += self.x_speed
self.y += self.y_speed
for body in rigid_bodies:
if self.colliderect(body): # if collide with another object:
self.x, self.y = stored_pos
Note, that
self.x -= self.x_speed
self.y -= self.y_speed
is not the inverse operation of
self.x += self.x_speed
self.y += self.y_speed
e.g: a = 2 and b = 0.5
int(a + b) == int(2 + 0.5) == 2
int(a - b) == int(2 - 0.5) == 1
The solution is to store the original values of self.x and self.y
stored_pos = (self.x, self.y)
and to restore it in the case of a collision:
self.x, self.y = stored_pos
I created an AI in python/pygame but even after spending hours of debugging, I could not find why the individuals(dots) are not getting mutated. After few generations, all the individuals just overlap each other and follow the same exact path. But after mutation they should move a little bit differently.
Here is what a population size of 10 looks like after every 2-3 generations..
Image 1 Image 2 Image 3
As you can see, just after few generations they just overlap and all the individuals in the population move together, following exact same path! We need mutations!!!
I would be really grateful to you if you could find any mistake. Thank!
I saw the code from: https://www.youtube.com/watch?v=BOZfhUcNiqk&t
and tried to make it in python. Here's my code
import pygame, random
import numpy as np
pygame.init()
width = 800
height = 600
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption("The Dots")
FPS = 30
clock = pygame.time.Clock()
gameExit = False
grey = [30, 30, 30]
white = [255, 255, 255]
black = [0, 0, 0]
red = [255, 0, 0]
goal = [400, 10]
class Dot():
def __init__(self):
self.x = int(width/2)
self.y = int(height - 150)
self.r = 3
self.c = black
self.xVel = self.yVel = 0
self.xAcc = 0
self.yAcc = 0
self.dead = False
self.steps = 0
self.reached = False
self.brain = Brain(200)
def show(self):
pygame.draw.circle(screen, self.c, [int(self.x), int(self.y)], self.r)
def update(self):
if (self.x >= width or self.x <= 0 or self.y >= height or self.y <= 0):
self.dead = True
elif (np.sqrt((self.x-goal[0])**2 + (self.y-goal[1])**2) < 5):
self.reached = True
if not self.dead and not self.reached:
if len(self.brain.directions) > self.steps:
self.xAcc = self.brain.directions[self.steps][0]
self.yAcc = self.brain.directions[self.steps][1]
self.steps += 1
self.xVel += self.xAcc
self.yVel += self.yAcc
if self.xVel > 5:
self.xVel = 5
if self.yVel > 5:
self.yVel = 5
self.x += self.xVel
self.y += self.yVel
else: self.dead = True
def calculateFitness(self):
distToGoal = np.sqrt((self.x-goal[0])**2 + (self.y-goal[1])**2)
self.fitness = 1/(distToGoal**2)
return self.fitness
def getChild(self):
child = Dot()
child.brain = self.brain
return child
class Brain():
def __init__(self, size):
self.size = size
self.directions = []
self.randomize()
def randomize(self):
self.directions.append((np.random.normal(size=(self.size, 2))).tolist())
self.directions = self.directions[0]
def mutate(self):
for i in self.directions:
rand = random.random()
if rand < 1:
i = np.random.normal(size=(1, 2)).tolist()[0]
class Population():
def __init__(self, size):
self.size = size
self.dots = []
self.fitnessSum = 0
for i in range(self.size):
self.dots.append(Dot())
def show(self):
for i in self.dots:
i.show()
def update(self):
for i in self.dots:
i.update()
def calculateFitness(self):
for i in self.dots:
i.calculateFitness()
def allDead(self):
for i in self.dots:
if not i.dead and not i.reached:
return False
return True
def calculateFitnessSum(self):
self.fitnessSum = 0
for i in self.dots:
self.fitnessSum += i.fitness
def SelectParent(self):
rand = random.uniform(0, self.fitnessSum)
runningSum = 0
for i in self.dots:
runningSum += i.fitness
if runningSum > rand:
return i
def naturalSelection(self):
newDots = []
self.calculateFitnessSum()
for i in self.dots:
parent = self.SelectParent()
newDots.append(parent.getChild())
self.dots = newDots
def mutate(self):
for i in self.dots:
i.brain.mutate()
test = Population(100)
while not gameExit:
for event in pygame.event.get():
if event.type == pygame.QUIT:
gameExit = True
screen.fill(white)
if test.allDead():
#Genetic Algorithm
test.calculateFitness()
test.naturalSelection()
test.mutate()
else:
test.update()
test.show()
pygame.draw.circle(screen, red, goal, 4)
clock.tick(FPS)
pygame.display.update()
pygame.quit()
Thanks for any help!
I didn't go through the whole code, but over here
def mutate(self):
for i in self.directions:
rand = random.random()
if rand < 1:
i = np.random.normal(size=(1, 2)).tolist()[0]
you are trying to assign a new value to i (which is an iterater), so it won't change anything, which explains why you'r having trouble with the mutations.
You should have something like this:
def mutate(self):
for i in range(len(self.directions)):
rand = random.random()
if rand < 1:
self.directions[i] = np.random.normal(size=(1, 2)).tolist()[0]
or you can use list comprehensions
https://docs.python.org/3/tutorial/datastructures.html#list-comprehensions
I'm in a game programming class and I'm supposed to make a game where there are sprites falling down from the top of the screen that you catch or avoid. Right now I'm working on making the objects fall and they were working at first but then I screwed something up because now they appear patchily/randomly disappear while falling down the screen. I've tried changing various things in the code to fix the problem but I've been stuck for a while.
I'm pretty certain that I messed something up either blitting the coal/candy to the screen or adding new ones to fall once others disappear, but I included a large portion of my code just in case something there is messing it up. I'll highlight the likely messed up sections below.
Oh, I also always really appreciate comments on how to make code more concise/efficient, but the teacher expects the code to be similar to how he taught us, so if you do have a specific fix, if it could keep the code similar to how it is right now I'd really appreciate it!
Thank you so much!
#Setting up time/timer
time = 6000
TICKTOCK = 0
pygame.time.set_timer (TICKTOCK+1, 10)
#Class for candy
class Candy:
def __init__(self):
self.x = random.randint(0, SCREEN_WIDTH - 150)
self.y = random.randint(-1000, 0)
self.image_Candy = pygame.image.load('konpeito.png')
self.height = self.image_Candy.get_height()
self.width = self.image_Candy.get_width()
def collide (self, sprite):
selfRect = pygame.Rect(self.x, self.y, self.width, self.height)
spriteRect = pygame.Rect(sprite.x, sprite.y, sprite.width, sprite.height)
if selfRect.colliderect(spriteRect):
return True
else:
return False
#class for coal
class Coal:
def __init__(self):
self.x = random.randint(0, SCREEN_WIDTH - 150)
self.y = random.randint(-1000, SCREEN_HEIGHT)
self.image_Coal = pygame.image.load('coal.png')
self.width = self.image_Coal.get_width()
self.height = self.image_Coal.get_height()
def collide (self, sprite):
selfRect = pygame.Rect(self.x, self.y, self.width, self.height)
spriteRect = pygame.Rect(sprite.x, sprite.y, sprite.width, sprite.height)
if selfRect.colliderect(spriteRect):
return True
else:
return False
#class for sootsprite (collects candy and coal)
class Sootsprite:
def __init__(self):
self.x = random.randint(0, SCREEN_WIDTH)
self.y = random.randint(0, SCREEN_HEIGHT)
self.image_bowl = pygame.image.load('sootsprite.png')
self.height = self.image_bowl.get_height()
self.width = self.image_bowl.get_width()
clock = pygame.time.Clock()
fps = 10
#Creating candies and rocks
bowl = []
for i in range (15):
candyInstance = Candy()
bowl.append(candyInstance)
rocks = []
for i in range (8):
coalInstance = Coal()
rocks.append(coalInstance)
catch = Sootsprite()
playground.fill(cyan)
Game_Over = False
while not Game_Over:
font = pygame.font.SysFont(None, 30)
endfont = pygame.font.SysFont(None, 100)
text = font.render('Points: ' + str(total) + '/15', True, black)
playground.blit(text, (0,0))
timer = font.render('Time remaining: ' + str(time), True, black)
playground.blit(timer, (0, 40))
end = endfont.render("GAME OVER." + str(total) + " POINTS EARNED", True, black)
This is where I blitted things onscreen and potentially screwed up:
playground.blit(catch.image_bowl, (catch.x, catch.y))
playground.blit(candyInstance.image_Candy, (candyInstance.x, candyInstance.y))
playground.blit(coalInstance.image_Coal, (coalInstance.x, coalInstance.y))
for event in pygame.event.get():
if event.type == pygame.QUIT:
Game_Over = True
if event.type == TICKTOCK+1:
time -=1
#ends game when time is over
if time < 0:
playground.blit(end, (300, 400))
#moving sootsprite with mouse
if event.type == MOUSEMOTION:
(catch.x, catch.y) = pygame.mouse.get_pos()
#making candy fall
for candyInstance in bowl:
if candyInstance.x <= 0:
candyInstance.x += 0
elif candyInstance.x >= 0 :
candyInstance.x -=0
if candyInstance.y <= (SCREEN_HEIGHT+150):
candyInstance.y += 20
elif candyInstance.y > (SCREEN_HEIGHT + 150) :
candyInstance.y = -100
this is where I'm removing things and adding new ones and might've messed up:
#removing candy when collected
for candyInstance in bowl:
if candyInstance.collide(catch):
bowl.remove(candyInstance)
total += 1
candyInstance = Candy()
bowl.append(candyInstance)
#making coal fall
for coalInstance in rocks:
if coalInstance.x <= 0:
coalInstance.x += 0
elif coalInstance.x >= 0 :
coalInstance.x -=0
if coalInstance.y <= (SCREEN_HEIGHT + 200):
coalInstance.y += 20
elif coalInstance.y > (SCREEN_HEIGHT + 200) :
coalInstance.y = -100
this is also a place where I removed objects and added new ones:
#removing coal when collected
for coalInstance in rocks:
if coalInstance.collide(catch):
rocks.remove(coalInstance)
total -= 1
coalInstance = Coal()
rocks.append(coalInstance)
pygame.display.flip()
playground.fill(cyan)
clock.tick (fps)
pygame.quit()
You blit only one candy. Use for loop to blit all candies.
for candyInstance in bowl:
playground.blit(candyInstance.image_Candy, (candyInstance.x, candyInstance.y))
You have the same problem with rocks
for coalInstance in rocks:
playground.blit(coalInstance.image_Coal, (coalInstance.x, coalInstance.y))
I have no clue how I would implement a game menu into the game I've made, I was thinking about having buttons for instructions credits and a 'play game' button. So would someone mind helping me out in figuring how to make a simple menu in pygame or livewires? Thanks in advance :)
This is the complete code to my game:
# Asteroid Dodger
# Player must avoid asteroids
# make the score a global variable rather than tied to the asteroid.
import pygame
from livewires import games, color
import math, random
#score
games.init(screen_width = 640, screen_height = 480, fps = 50)
score = games.Text(value = 0, size = 25, color = color.green,
top = 5, right = games.screen.width - 10)
games.screen.add(score)
#lives
lives = games.Text(value = 3, size = 25, color = color.green,
top = 5, left = games.screen.width - 620)
games.screen.add(lives)
#inventory
inventory=[]
#Asteroid images
images = [games.load_image("asteroid_small.bmp"),
games.load_image("asteroid_med.bmp"),
games.load_image("asteroid_big.bmp")]
class Ship(games.Sprite):
"""
A Ship controlled by player that explodes when it by Asteroids.
"""
image = games.load_image("player.bmp")
VELOCITY_STEP = .05
def __init__(self):
""" Initialize Ship object """
super(Ship, self).__init__(image = Ship.image,
bottom = games.screen.height)
def update(self):
global inventory
""" uses A and D keys to move the ship """
if games.keyboard.is_pressed(games.K_a):
self.dx -= Ship.VELOCITY_STEP * 2
if games.keyboard.is_pressed(games.K_d):
self.dx += Ship.VELOCITY_STEP * 2
if self.left < 0:
self.left = 0
if self.right > games.screen.width:
self.right = games.screen.width
self.check_collison()
def ship_destroy(self):
self.destroy()
new_explosion = Explosion(x = self.x, y = self.y)
games.screen.add(new_explosion)
def check_collison(self):
""" Check for overlapping sprites in the ship. """
global lives
for items in self.overlapping_sprites:
items.handle_caught()
if lives.value <=0:
self.ship_destroy()
class Explosion(games.Animation):
sound = games.load_sound("explosion.wav")
images = ["explosion1.bmp",
"explosion2.bmp",
"explosion3.bmp",
"explosion4.bmp",
"explosion5.bmp",
"explosion6.bmp",
"explosion7.bmp",
"explosion8.bmp",
"explosion9.bmp"]
def __init__(self, x, y):
super(Explosion, self).__init__(images = Explosion.images,
x = x, y = y,
repeat_interval = 4, n_repeats = 1,
is_collideable = False)
Explosion.sound.play()
class Asteroid(games.Sprite):
global lives
global score
global inventory
"""
A asteroid which falls through space.
"""
image = games.load_image("asteroid_med.bmp")
speed = 3
def __init__(self, x,image, y = 10):
""" Initialize a asteroid object. """
super(Asteroid, self).__init__(image = image,
x = x, y = y,
dy = Asteroid.speed)
def update(self):
""" Check if bottom edge has reached screen bottom. """
if self.bottom>games.screen.height:
self.destroy()
score.value+=10
def handle_caught(self):
if lives.value>0:
lives.value-=1
self.destroy_asteroid()
if lives.value <= 0:
self.destroy_asteroid()
self.end_game()
def destroy_asteroid(self):
self.destroy()
def die(self):
self.destroy()
def end_game(self):
""" End the game. """
end_message = games.Message(value = "Game Over",
size = 90,
color = color.red,
x = games.screen.width/2,
y = games.screen.height/2,
lifetime = 5 * games.screen.fps,
after_death = games.screen.quit)
games.screen.add(end_message)
class Spawner(games.Sprite):
global images
"""
Spawns the asteroids
"""
image = games.load_image("spawner.bmp")
def __init__(self, y = 10, speed = 5, odds_change = 50):
super(Spawner, self).__init__(image = Spawner.image,
x = games.screen.width / 2,
y = y,
dx = speed)
self.odds_change = odds_change
self.time_til_drop = 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_drop()
self.check_for_lives()
def check_drop(self):
""" Decrease countdown or drop asteroid and reset countdown. """
if self.time_til_drop > 0:
self.time_til_drop -= 0.7
else:
asteroid_size = random.choice(images)
new_asteroid = Asteroid(x = self.x,image = asteroid_size)
games.screen.add(new_asteroid)
# makes it so the asteroid spawns slightly below the spawner
self.time_til_drop = int(new_asteroid.height * 1.3 / Asteroid.speed) + 1
def check_for_lives(self):
droplives = random.randrange(0, 4000)
if droplives == 5:
lifetoken = Extralives(x = self.x)
g ames.screen.add(lifetoken)
class Extralives(games.Sprite):
global lives
image = games.load_image('addlives.png')
speed = 2
sound = games.load_sound("collectlives.wav")
def __init__(self,x,y = 10):
""" Initialize a asteroid object. """
super(Extralives, self).__init__(image = Extralives.image,
x = x, y = y,
dy = Extralives.speed)
def update(self):
""" Check if bottom edge has reached screen bottom. """
if self.bottom>games.screen.height:
self.destroy()
def handle_caught(self):
Extralives.sound.play()
lives.value+=1
self.destroy()
def main():
""" Play the game. """
bg = games.load_image("space.jpg", transparent = False)
games.screen.background = bg
the_spawner = Spawner()
games.screen.add(the_spawner)
pygame.mixer.music.load("Jumpshot.ogg")
pygame.mixer.music.play()
the_ship = Ship()
games.screen.add(the_ship)
games.mouse.is_visible = False
games.screen.event_grab = True
games.screen.mainloop()
#starts the game
main()
The Pygbutton module provides a way to create buttons in Pygame programs. You can download it via "pip install pygbutton". There are demos on the github: https://github.com/asweigart/pygbutton
Try this code out:
Game Menu function
def game_intro():
intro = True
while intro:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
gameDisplay.fill(white)
titleText = gameDisplay.blit(title, (170, 200)) # title is an image
titleText.center = ((display_width / 2), (display_height / 2))
# button(x, y, w, h, inactive, active, action=None)
button(100, 350, 195, 80, startBtn, startBtn_hover, game_loop)
button(300, 350, 195, 80, creditsBtn, creditsBtn_hover, #Your function)
pygame.display.update()
clock.tick(15)
you can call this menu above your game loop.
Button function
Pygame doesn't have buttons but it is pretty easy to make one!
def button(x, y, w, h, inactive, active, action=None):
mouse = pygame.mouse.get_pos()
click = pygame.mouse.get_pressed()
if x + w > mouse[0] > x and y + h > mouse[1] > y:
gameDisplay.blit(active, (x, y))
if click[0] == 1 and action is not None:
action()
else:
gameDisplay.blit(inactive, (x, y))
you can call this function inside your game menu like this:
#Example function call
button(340, 560, 400, 200, randomBtn, randomBtn_hover, random_func)
Here's what each parameter means in button():
x: x-coordinate of button
y: y-coordinate of button
w: button width(in pixels)
h: button height(in pixels)
active: the picture of the button when it is active(e.g when the mouse is hovering ver it)
inactive: the picture of the button when it is idle
action: the function to be executed when the button is pressed
Note: It is better to make a button function since it is easier to make one and it saves a lot of time
Hope this helped!