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()
Related
I make a program where I generate circles of random size and position, by means of classes in python, I have managed to generate the circles as I wish but all collide with each other, so I have created a method so that this does not happen but it generates an error that I can not identify "'CreaCir' object has no attribute 'radio'", so I do not know what I'm doing wrong, the terminal tells me that the error is in the circle class in the collision method in the return, if someone could mark my error and give me advice to fix it I would appreciate it.
my code is as follows. first my circle class where I contain the collision method:
class Circulo(PosGeo):
def __init__(self, x, y, r):
self.x = x
self.y = y
self.radio = r
self.cx = x+r
self.cy = y+r
def dibujar(self, ventana):
pg.draw.circle(ventana, "white", (self.x, self.y), self.radio, 1)
def update(self):
pass
def colisionC(self, c2):
return self.radio + c2.radio > sqrt(pow(self.cx - c2.cx, 2) + pow(self.cy - c2.cy, 2))#where I get the error
now a part of my main code:
class CreaCir:
def __init__(self, figs):
self.i = 1
self.r = randint(5, 104)
self.x = randint(0, 500 + self.r)
self.y = randint(0, 300 + self.r)
self.creOne = Circulo(self.x, self.y, self.r)
self.figs.append(self.creOne)
def update(self):
if self.i <100:
choca = False
self.r = randint(5, 104)
self.x = randint(0, 500 + self.r)
self.y = randint(0, 300 + self.r)
self.creOne = Circulo(self.x, self.y, self.r)
for j in range (self.i):
choca = self.creOne.colisionC(self.figs[j])#here is where I use the collision method to my object
if choca == True:
break
if choca == False:
self.figs.append(self.creOne)
self.i+=1
def dibujar(self, ventana):
pass
called:
class Ventana:
def __init__(self, Ven_Tam= (700, 500)):
pg.init()
self.ven_tam = Ven_Tam
self.ven = pg.display.set_caption("Linea")
self.ven = pg.display.set_mode(self.ven_tam)
self.ven.fill(pg.Color('#404040'))
self.figs = []
self.figs.append(CreaCir(self.figs))
self.reloj = pg.time.Clock()
def check_events(self):
for event in pg.event.get():
if event.type == pg.QUIT or (event.type == pg.KEYDOWN and event.key == pg.K_ESCAPE):
quit()
pg.display.flip()
def run(self):
while True:
self.check_events()
for fig in self.figs:
fig.update()
for fig in self.figs:
fig.dibujar(self.ven)
self.reloj.tick(30)
if __name__ == '__main__':
ven = Ventana()
ven.run()
The cause of the error is that you have put a CreaCir object in the figs list. So the first item in figs (self.creOne.colisionC(self.figs[j])) is a CreaCir object and this object has no radio.
Just remove that line of code, it is absolutely unnecessary.
self.figs.append(CreaCir(self.figs))
Create the CreaCir in run, but don't put it in the list:
class Ventana:
def __init__(self, Ven_Tam= (700, 500)):
pg.init()
self.ven_tam = Ven_Tam
self.ven = pg.display.set_caption("Linea")
self.ven = pg.display.set_mode(self.ven_tam)
self.ven.fill(pg.Color('#404040'))
self.figs = []
self.reloj = pg.time.Clock()
def check_events(self):
# [...]
def run(self):
cirCreater = CreaCir(self.figs)
while True:
self.check_events()
cirCreater.update()
for fig in self.figs:
fig.dibujar(self.ven)
self.reloj.tick(30)
To detect the collision of 2 circles, you need to calculate the distance between the centers of the circles and check if the distance is greater than the sum of the radii of the circles. The 3rd argument of pygame.draw.circle is the center of the circle, but not the top left of the bounding box of the circle:
pg.draw.circle(ventana, "white", (self.x, self.y), self.radio, 1)
pg.draw.circle(ventana, "white", (self.cx, self.cy), self.radio, 1)
Complete example:
import pygame as pg
import math, random
class Circulo:
def __init__(self, x, y, r):
self.x, self.y = x, y
self.radio = r
self.cx, self.cy = x+r, y+r
def dibujar(self, ventana):
pg.draw.circle(ventana, "white", (self.cx, self.cy), self.radio, 1)
def colisionC(self, c2):
dx = self.cx - c2.cx
dy = self.cy - c2.cy
return self.radio + c2.radio > math.sqrt(dx*dx + dy*dy)
class CreaCir:
def __init__(self, figs):
self.figs = figs
def update(self):
if len(self.figs) < 100:
choca = False
r = random.randint(5, 104)
x = random.randint(0, 700 - r*2)
y = random.randint(0, 500 - r*2)
creOne = Circulo(x, y, r)
for fig in self.figs:
choca = creOne.colisionC(fig)
if choca == True:
break
if choca == False:
self.figs.append(creOne)
class Ventana:
def __init__(self, Ven_Tam= (700, 500)):
pg.init()
self.ven_tam = Ven_Tam
self.ven = pg.display.set_caption("Linea")
self.ven = pg.display.set_mode(self.ven_tam)
self.figs = []
self.reloj = pg.time.Clock()
def check_events(self):
for event in pg.event.get():
if event.type == pg.QUIT or (event.type == pg.KEYDOWN and event.key == pg.K_ESCAPE):
quit()
def run(self):
cirCreater = CreaCir(self.figs)
while True:
self.check_events()
cirCreater.update()
self.ven.fill(pg.Color('#404040'))
for fig in self.figs:
fig.dibujar(self.ven)
pg.display.flip()
self.reloj.tick(30)
if __name__ == '__main__':
ven = Ventana()
ven.run()
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.
I have been following a guide to make my game. However, when I try to run the program I get a syntax Error with the Class Circle and not sure why? I can't figure out the reason and I'm sure there are more issue afterward the Class Circle but can't fix it.
import pygame
from pygame.locals import *
from uagame import Window
import time
import random
def main():
window = Window('Pong', 500, 400)
window.set_auto_update(False)
game = Game(window)
game.play()
window.close()
class Rect:
def __init__(self, center, radius, color, window):
self.center = center
self.radius = radius
self.color = color
self.window = window
def draw(self):
pygame.draw.rect(self.window.get_surface(), self.color, Rect((100, 200), (20, 30))
class Circle: # Syntax Error: class Circle:: <string>, line 28, pos 5
def __init__(self, center, radius, color, window):
self.center = center
self.radius = radius
self.color = color
self.window = window
def draw(self):
pygame.draw.circle(self.window.get_surface(), self.color, self.center,self.radius)
def get_color(self):
return self.color
def move(self):
window_size = (self.window.get_width() , self.window.get_height())
for index in range(0,2):
self.center[index] = (self.center[index] + 1) % window_size[index]
def enlarge(self,increment):
self.radius = self.radius + increment
class Game:
def __init__(self, window):
self.window = window
self.bg_color = pygame.Color('black')
self.fg_color = 'green'
self.rect_color = 'green'
self.pause_time = 0.02
self.close_clicked = False
self.continue_game = True
self.circle = Ball([200,200], 20, pygame.Color(self.fg_color),window)
self.rect = Rect([100,200], 50 , pygame.Color(self.fg_color),window)
self.radius_increment = 5
def play(self):
while not self.close_clicked:
self.handle_event()
self.draw()
if self.continue_game:
self.update()
self.decide_continue()
time.sleep(self.pause_time)
def handle_event(self):
event = pygame.event.poll()
if event.type == QUIT:
self.close_clicked = True
elif event.type == MOUSEBUTTONUP and self.continue_game:
self.handle_mouse_up()
def handle_mouse_up(self):
self.circle.enlarge(self.radius_increment)
def draw(self):
self.window.clear()
self.circle.draw()
self.rect.draw()
if not self.continue_game:
self.window.set_bg_color(self.fg_color)
self.window.clear()
self.window.update()
def update(self):
self.circle.move()
def decide_continue(self):
pass
main()
Your error actually lies in the line above. See that your code is missing a parenthesis to close the rect() function. Always remember to count the parenthesis on long functions like this.
def draw(self):
pygame.draw.rect(self.window.get_surface(), self.color, Rect((100, 200), (20, 30))) #<-
I am creating a list of blocks that i want to loop through to print to the screen in pygame using the pygame.blit function. However an error is thrown that says (argument 1 must be pygame.Surface, not block).
I have made other games in pygame printing objects to a screen but never putting them in a list first. Any help would be greatly appreciated.
import pygame
import time
class block:
def __init__(self, image, posX, posY, name):
#Take passed image and assign to block#
self.image = image
#Create a rect for block for collisions#
# self.rect = self.image.get_rect()
#Create a positio from passed integers#
self.posX = posX
self.posY = posY
#Set id of the block#
self.name = name
def draw(self, screen):
screen.blit(self.image, (self.posX, self.posY))
def getName(self):
return self.name
class worldGeneration():
def __init__(self):
self.blocks = []
def worldGen(self):
grassImg = pygame.image.load("grass.png")
dirtImg = pygame.image.load("dirt.png")
for x in range(0, 640, 32):
block1 = block(grassImg, 0 + x ,416, x)
self. blocks.append(block1)
for i in range(416, 480, 32):
block1 = block(dirtImg, 0 + x ,448, x)
self.blocks.append(block1)
def getBlocks(self, x):
return self.blocks[x]
def draw(self, x, screen):
screen.blit(self.blocks[x])
def gameUpdate(screen, world):
screen.fill((147,200,250))
for x in range (0, len(world.blocks)):
world.draw(x, screen)
pygame.display.flip()
def gameLoop():
height = 480
width = 640
screen = pygame.display.set_mode((width, height))
clock = pygame.time.Clock()
world = worldGeneration()
world.worldGen()
running = True
while running:
clock.tick(60)
pygame.display.set_caption(str(clock.get_fps()))
for e in pygame.event.get():
if e.type == pygame.QUIT:
running = False
if e.type == pygame.KEYDOWN and e.key == pygame.K_ESCAPE:
running = False
gameUpdate(screen, world)
gameLoop()
Do it that way:
Create a surface()
Blit block by block to it, changing coordinates accordingly
Get the screen surface() and blit in the resulting one
flip() the display!
Instead of
class worldGeneration():
def __init__(self):
self.blocks = []
def worldGen(self):
grassImg = pygame.image.load("grass.png")
dirtImg = pygame.image.load("dirt.png")
for x in range(0, 640, 32):
block1 = block(grassImg, 0 + x ,416, x)
self. blocks.append(block1)
for i in range(416, 480, 32):
block1 = block(dirtImg, 0 + x ,448, x)
self.blocks.append(block1)
def getBlocks(self, x):
return self.blocks[x]
def draw(self, x, screen):
screen.blit(self.blocks[x])
you can do
class worldGeneration():
def __init__(self):
self.blocks = []
def worldGen(self):
grassImg = pygame.image.load("grass.png")
dirtImg = pygame.image.load("dirt.png")
for x in range(0, 640, 32):
block1 = block(grassImg, 0 + x ,416, x)
self. blocks.append(block1)
for i in range(416, 480, 32):
block1 = block(dirtImg, 0 + x ,448, x)
self.blocks.append(block1)
def getBlocks(self, x):
return self.blocks[x]
def draw(self, x, screen):
self.blocks[x].draw(screen)
blit takes a Surface as first argument, you provided a block instance. That's what the error message tells you. But since you already implemented a draw method on block, simply use that, since it does what you want.
Also, you could go further and change
def gameUpdate(screen, world):
screen.fill((147,200,250))
for x in range (0, len(world.blocks)):
world.draw(x, screen)
pygame.display.flip()
to simply
def gameUpdate(screen, world):
screen.fill((147,200,250))
world.draw(screen)
pygame.display.flip()
and change worldGeneration.draw to
def draw(self, screen):
for block in self.blocks:
block.draw(screen)
No need to get the len of blocks, loop through the range, and pass the index to the function when you could simply iterate over blocks with a for loop.
Hey guys am developing a game with pygame. The idea of the game is that when a user click on start button on the menu(which appear on the first before starting a game) he must see the two balls bouncing on the pygame window.
For this i have two python files.
bounceball.py
This python file makes the two ball bounce on the pygame window which is made by me.The code of bounceball.py is
here.(Sorry for pasting it on pastebin since its a long code)
menu.py
This python file creates a menu which i have found from the internet and this also works fine.The code of menu.py is here
But the Problem is that when a user clicks on the Start button from the menu it didnt do anything .The thing i want is when a user clicks on start button from menu he must see the ball boucing which i have coded in bounceball.py on the pygame window.How can i link my bounceball.py to the menu.
I have tried to acheive this by many methods but it didnt helped me ..
Hope you guys can help me in acheieving this ..Any help would be appreciated ..Thanks in advance
It could be done better but at least it works.
menu.py
#!/usr/bin/python
import sys
import pygame
import bounceball
#----------------------------------------------------------------------
WHITE = (255, 255, 255)
RED = (255, 0, 0)
BLACK = ( 0, 0, 0)
#----------------------------------------------------------------------
class MenuItem(pygame.font.Font):
def __init__(self, text, font=None, font_size=30,
font_color=WHITE, (pos_x, pos_y)=(0, 0)):
pygame.font.Font.__init__(self, font, font_size)
self.text = text
self.font_size = font_size
self.font_color = font_color
self.label = self.render(self.text, 1, self.font_color)
self.width = self.label.get_rect().width
self.height = self.label.get_rect().height
self.dimensions = (self.width, self.height)
self.pos_x = pos_x
self.pos_y = pos_y
self.position = pos_x, pos_y
def is_mouse_selection(self, (posx, posy)):
if (posx >= self.pos_x and posx <= self.pos_x + self.width) and \
(posy >= self.pos_y and posy <= self.pos_y + self.height):
return True
return False
def set_position(self, x, y):
self.position = (x, y)
self.pos_x = x
self.pos_y = y
def set_font_color(self, rgb_tuple):
self.font_color = rgb_tuple
self.label = self.render(self.text, 1, self.font_color)
#----------------------------------------------------------------------
class GameMenu():
def __init__(self, screen, items, funcs, bg_color=BLACK, font=None, font_size=30,
font_color=WHITE):
self.screen = screen
self.scr_width = self.screen.get_rect().width
self.scr_height = self.screen.get_rect().height
self.bg_color = bg_color
self.clock = pygame.time.Clock()
self.funcs = funcs
self.items = []
for index, item in enumerate(items):
menu_item = MenuItem(item, font, font_size, font_color)
# t_h: total height of text block
t_h = len(items) * menu_item.height
pos_x = (self.scr_width / 2) - (menu_item.width / 2)
pos_y = (self.scr_height / 2) - (t_h / 2) + (index * menu_item.height)
menu_item.set_position(pos_x, pos_y)
self.items.append(menu_item)
self.mouse_is_visible = True
self.cur_item = None
def set_mouse_visibility(self):
if self.mouse_is_visible:
pygame.mouse.set_visible(True)
else:
pygame.mouse.set_visible(False)
def set_keyboard_selection(self, key):
"""
Marks the MenuItem chosen via up and down keys.
"""
for item in self.items:
# Return all to neutral
item.set_italic(False)
item.set_font_color(WHITE)
if self.cur_item is None:
self.cur_item = 0
else:
# Find the chosen item
if key == pygame.K_UP and \
self.cur_item > 0:
self.cur_item -= 1
elif key == pygame.K_UP and \
self.cur_item == 0:
self.cur_item = len(self.items) - 1
elif key == pygame.K_DOWN and \
self.cur_item < len(self.items) - 1:
self.cur_item += 1
elif key == pygame.K_DOWN and \
self.cur_item == len(self.items) - 1:
self.cur_item = 0
self.items[self.cur_item].set_italic(True)
self.items[self.cur_item].set_font_color(RED)
# Finally check if Enter or Space is pressed
if key == pygame.K_SPACE or key == pygame.K_RETURN:
text = self.items[self.cur_item].text
self.funcs[text]()
def set_mouse_selection(self, item, mpos):
"""Marks the MenuItem the mouse cursor hovers on."""
if item.is_mouse_selection(mpos):
item.set_font_color(RED)
item.set_italic(True)
else:
item.set_font_color(WHITE)
item.set_italic(False)
def run(self):
mainloop = True
while mainloop:
# Limit frame speed to 50 FPS
self.clock.tick(50)
mpos = pygame.mouse.get_pos()
for event in pygame.event.get():
if event.type == pygame.QUIT:
mainloop = False
if event.type == pygame.KEYDOWN:
self.mouse_is_visible = False
self.set_keyboard_selection(event.key)
if event.type == pygame.MOUSEBUTTONDOWN:
for item in self.items:
if item.is_mouse_selection(mpos):
self.funcs[item.text]()
if pygame.mouse.get_rel() != (0, 0):
self.mouse_is_visible = True
self.cur_item = None
self.set_mouse_visibility()
# Redraw the background
self.screen.fill(self.bg_color)
for item in self.items:
if self.mouse_is_visible:
self.set_mouse_selection(item, mpos)
self.screen.blit(item.label, item.position)
pygame.display.flip()
#----------------------------------------------------------------------
def run_bounceball():
print "run bounceball"
bounceball.run(screen)
#----------------------------------------------------------------------
if __name__ == "__main__":
pygame.init()
# Creating the screen
screen = pygame.display.set_mode((300, 300), 0, 32)
menu_items = ('Start', 'Quit')
funcs = {'Start': run_bounceball,
'Quit': sys.exit}
pygame.display.set_caption('Game Menu')
gm = GameMenu(screen, funcs.keys(), funcs)
gm.run()
bounceball.py
import pygame
import math
from itertools import cycle
#----------------------------------------------------------------------
# some simple vector helper functions, stolen from http://stackoverflow.com/a/4114962/142637
def magnitude(v):
return math.sqrt(sum(v[i]*v[i] for i in range(len(v))))
def add(u, v):
return [ u[i]+v[i] for i in range(len(u)) ]
def sub(u, v):
return [ u[i]-v[i] for i in range(len(u)) ]
def dot(u, v):
return sum(u[i]*v[i] for i in range(len(u)))
def normalize(v):
vmag = magnitude(v)
return [ v[i]/vmag for i in range(len(v)) ]
#----------------------------------------------------------------------
class Ball(object):
def __init__(self, path, screen):
self.x, self.y = (0, 0)
self.speed = 2.5
self.color = (200, 200, 200)
self.path = cycle(path)
self.set_target(next(self.path))
self.screen = screen
#property
def pos(self):
return self.x, self.y
# for drawing, we need the position as tuple of ints
# so lets create a helper property
#property
def int_pos(self):
return map(int, self.pos)
#property
def target(self):
return self.t_x, self.t_y
#property
def int_target(self):
return map(int, self.target)
def next_target(self):
self.set_target(self.pos)
self.set_target(next(self.path))
def set_target(self, pos):
self.t_x, self.t_y = pos
def update(self):
# if we won't move, don't calculate new vectors
if self.int_pos == self.int_target:
return self.next_target()
target_vector = sub(self.target, self.pos)
# a threshold to stop moving if the distance is to small.
# it prevents a 'flickering' between two points
if magnitude(target_vector) < 2:
return self.next_target()
# apply the balls's speed to the vector
move_vector = [c * self.speed for c in normalize(target_vector)]
# update position
self.x, self.y = add(self.pos, move_vector)
def draw(self):
pygame.draw.circle(self.screen, self.color, self.int_pos, 4)
#----------------------------------------------------------------------
def run(screen):
#pygame.init() # no need it - inited in menu.py
#screen = pygame.display.set_mode((300, 300)) # no need it - created in menu.py
clock = pygame.time.Clock()
quit = False
path = [(26, 43),
(105, 110),
(45, 225),
(145, 295),
(266, 211),
(178, 134),
(250, 56),
(147, 12)]
path2 = [(26, 43),
(105, 10),
(45, 125),
(150, 134),
(150, 26),
(107, 12)]
ball = Ball(path, screen)
ball.speed = 1.9
ball2 = Ball(path2, screen)
ball2.color = (200, 200, 0)
balls = [ball, ball2]
while not quit:
quit = pygame.event.get(pygame.QUIT)
pygame.event.poll()
map(Ball.update, balls)
screen.fill((0, 0, 0))
map(Ball.draw, balls)
pygame.display.flip()
clock.tick(60)