How to call a particular object's method and have affect only on that object which is a value of a key of a dictionary in pygame? - python

I am a new learner of programming. I am practiceing python and writing a litle game using pygame. I have ten circle on the pygame window and each have a counter. I want to increase the counter by 1 when it's circle is clicked. I first created group using .sprite.Group(), but I could not get the desired result. Because then the counter does not even get update. So I create two list, one for the circle and one for the counter. And for each circle in the circle list I created a dictionary taking the circle as the key and each counter in the circle list is the value of the circle. Now when the circle got clicked then the all counter gets updated, not the counter that the circle holds. But goal is to get the specific counter updated for it's circle.
(hole == circle)
dig_hole.py(This is the main file.)
import pygame
import sys
from pygame.sprite import Group
from counter import Counter
from hole import Hole
from settings import Settings
class DigHole:
def __init__(self):
pygame.init()
self.settings = Settings()
self.screen = pygame.display.set_mode((self.settings.screen_width, self.settings.screen_height))
pygame.display.set_caption("Dig Hole")
pygame.display.set_icon(Hole(self).image)
self.count = Counter(self)
self.counter_group = list()
self.holes = list()
self.dict = dict()
self._create_holes()
self.hole = Hole(self)
self.mouse_pos = (0, 0)
def run_dig_hole(self):
while True:
self._check_events()
self._update_screen()
def _check_events(self):
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_q:
sys.exit()
elif event.type == pygame.MOUSEBUTTONDOWN:
self.mouse_pos = pygame.mouse.get_pos()
self._check_hole_clicked_events(self.mouse_pos)
def _check_hole_clicked_events(self, mouse_pos):
for key in self.dict:
if key.rect.collidepoint(mouse_pos):
self.dict[key].count_clock += 1
self.dict[key].prep_counter()
self.count.prep_counter()
def _create_holes(self):
for row_number in range(2):
for hole_number in range(5):
self._create_hole(row_number, hole_number)
for hole in self.holes:
counter = Counter(self)
counter.counter_rect.midbottom = hole.rect.midtop
self.counter_group.append(counter)
for hole in self.holes:
for counter in self.counter_group:
self.dict[hole] = counter
def _create_hole(self, row_number, hole_number):
hole = Hole(self)
hole_width, hole_height = hole.rect.size
available_space_x = self.settings.screen_width - (2 * hole_width)
available_space_y = self.settings.screen_height - (2 * hole_height)
hole.x =(((available_space_x // 5) - hole_width) // 2) + (available_space_x // 5) * hole_number
hole.rect.x = hole.x
hole.rect.y = 2 * hole.rect.height + (available_space_y - (4 * hole_height)) * row_number
self.holes.append(hole)
def _update_screen(self):
self.screen.fill(self.settings.bg_color)
for key in self.dict:
key.draw()
for key in self.dict:
self.dict[key].counter_rect.midbottom = key.rect.midtop
self.dict[key].show_counter()
self.count.show_counter()
pygame.display.flip()
if __name__ == '__main__':
dh = DigHole()
dh.run_dig_hole()
hole.py
import pygame
from pygame.sprite import Sprite
class Hole():
def __init__(self, dh):
# super().__init__()
self.screen = dh.screen
self.image = pygame.image.load("images/circle.bmp")
self.rect = self.image.get_rect()
self.rect.x = self.rect.width
self.rect.y = self.rect.height
self.x = float(self.rect.x)
def draw(self):
self.screen.blit(self.image, self.rect)
counter.py
import pygame.font
from pygame.sprite import Sprite
class Counter():
def __init__(self, dh):
# super().__init__()
self.screen = dh.screen
self.screen_rect = self.screen.get_rect()
self.settings = dh.settings
self.count_clock = 0
self.text_color = (30, 30, 30)
self.font = pygame.font.SysFont(None, 48)
self.prep_counter()
def prep_counter(self):
counter_str = str(self.count_clock)
self.counter_image = self.font.render(counter_str, True, self.text_color, self.settings.bg_color)
self.counter_rect = self.counter_image.get_rect()
self.counter_rect.right = self.screen_rect.right - 20
self.counter_rect.top = 20
def show_counter(self):
self.screen.blit(self.counter_image, self.counter_rect)
Thank you.This is the window of the progeam. Here all circles are gets update but one is clicked.

The issue is that, in _create_holes, you set the counters of each circle to be the same Counter object.
for hole in self.holes:
for counter in self.counter_group:
self.dict[hole] = counter
Unrolling the inner loop, this is the same as
for hole in self.holes:
self.dict[hole] = self.counter_group[0]
self.dict[hole] = self.counter_group[1]
self.dict[hole] = self.counter_group[2]
...
self.dict[hole] = self.counter_group[-1]
The first assignments are all immediately overwritten, so this code is setting every self.dict value to self.counter_group[-1]. What you want to do instead is
for hole, counter in zip(self.holes, self.counter_group):
self.dict[hole] = counter
which iterates over both self.holes and self.counter_group simultaneously. In your case, you can actually rewrite this as
self.dict = dict(zip(self.holes, self.counter_group))
which is nicer.
I’m not sure, but I think you intend self.count to be a total. If this is the case, it won’t quite work: you’re missing a line from _check_hole_clicked_events. It should look like this:
def _check_hole_clicked_events(self, mouse_pos):
for key in self.dict:
if key.rect.collidepoint(mouse_pos):
self.dict[key].count_clock += 1
self.dict[key].prep_counter()
self.count.count_clock += 1
self.count.prep_counter()
As a side note, I noticed you also wrote list() and dict() to create empty lists and dicts. It’s more efficient and idiomatic just to write literals ([] and {}).

Related

I am making snake in pygame and python is giving me an error that one of my objects is actually a string, but its not meant to be

The error that shows up in python right now is
Traceback (most recent call last):
File "C:\Users\Eduardo.Graglia\AppData\Local\Programs\Python\Python37\Start.py", line 66, in <module>
snake = snake(self) # create an instance
File "C:\Users\Eduardo.Graglia\AppData\Local\Programs\Python\Python37\Start.py", line 17, in __init__
food.image = pygame.image.load("food.png")
AttributeError: 'str' object has no attribute 'image'
I am very new to python and pygame, so most of the code I've been using is stuff I've taken and adapted from stack overflow
What I was trying to do with the food object was make it so that it was all in one player class because I am confused on how to call methods from other classes and use objects from other classes in one class. If anyone has any alternatives to what I've done please tell me.
import pygame
import os
import time
import math
import random
length = 650
width = 400
headx = 0
heady = 0
class snake(object):
def __init__(self, food):
self.image = pygame.image.load("head.png")
self.x = 0
self.y = 0
food.image = pygame.image.load("food.png")
food.x = 0
food.y = 0
def handle_keys(self):
""" Handles Keys """
key = pygame.key.get_pressed()
dist = 25 # distance moved in 1 frame, try changing it to 5
if key[pygame.K_DOWN]: # down key
self.y += dist # move down
heady = heady - dist
elif key[pygame.K_UP]: # up key
self.y -= dist # move up
heady = heady - dist
if key[pygame.K_RIGHT]: # right key
self.x += dist # move right
headx = headx - dist
elif key[pygame.K_LEFT]: # left key
self.x -= dist # move left
headx = headx - dist
def fooddrop(food, surface):
newfoodx = 1
newfoody = 1
while newfoodx % 25 != 0 or newfoodx !=0:
newfoodx = random.randint(0,650)
while newfoody % 25 != 0 or newfoody !=0:
newfoody = random.randint(0,400)
food.x = newfoodx
food.y = newfoody
surface.blit(food.image,(food.x,food.y))
def draw(self, surface):
""" Draw on surface """
# blit yourself at your current position
surface.blit(self.image, (self.x, self.y))
self = 'null'
food = 'null'
pygame.init()
screen = pygame.display.set_mode((length, width))
snake = snake(self) # create an instance
food = snake(food)
running = True
while running:
# handle every event since the last frame.
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit() # quit the screen
running = False
if snake.y > width or snake.y < 0 or snake.x > length or snake.x < 0: #Border Created
pygame.quit() # quit the screen
running = False
time.sleep(0.125)
snake.handle_keys() # handle the keys
screen.fill((0,0,0)) # fill the screen with white
snake.draw(screen) # draw the snake to the screen
snake.fooddrop(screen)
pygame.display.update() # update the screen
clock.tick(40)
Feel free to completely destroy my code because I am new and need to learn.
Thanks in advance!
You have to distinguish between Classes and instances of classes (objects).
Create a class Snake (or whatever name). Please use a leading capital letter for the name of the class to avoid confusions (see Style Guide for Python Code - Class Names).
The constructor of the class has 1 parameter (beside self), the name of the image:
class Snake(object):
def __init__(self, imagename):
self.image = pygame.image.load(imagename)
self.x = 0
self.y = 0
Create 2 instances of the class:
snake = Snake("head.png")
food = Snake("food.png")

Is there a way to change my_sprite or my_group to change the animation displayed?

I'm making animated sprites in pygame, and would like help finding a way to flip from one to the other? The current code looks something like this:
class normal(pygame.sprite.Sprite):
def __init__(self):
#etc, list of images to create the animation
class tall(pygame.sprite.Sprite):
def __init__(self):
#rinse and repeat with a different set of images
I already have an idea for how to trigger the change via keystroke. But I'm not sure which variable to change, and to what. When I try to change with the following code, nothing happens
fps = 25
pygame.init()
my_sprite = normal()
my_group = pygame.sprite.Group(my_sprite)
#etc until we get to the part where it changes
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_RETURN:
if my_sprite == normal():
my_sprite = tall()
fps = 30
else:
my_sprite = normal()
fps = 25
I'm not sure exactly what isn't working in my code as it doesn't come back with an error. Can someone point me in the right direction?
It's not working because when the code calls normal() it's creating a new instance of an object. So the call:
if my_sprite == normal():
Is saying "is my existing sprite object == this new sprite object", which is never true. You can use the python function to type() of the object to do the same thing, or add your own type-function as I have presented in the code below.
I would track the state of the sprite inside the class, and use some functions grow() and shrink() to change the size automatically.
class GrowingSprite( pygame.sprite.Sprite, x, y ):
def __init__( self ):
#etc, list of images to create the animation
self.image_short = ... # load "short" image
self.image_tall = ... # load "tall" image
# Set the initial state
self.image = self.image_short # start short
self.rect = self.image.get_rect()
self.rect.centerx = x
self.rect.centery = y
self.state = 'short'
def getState( self ):
return self.state
def grow( self ):
self.state = 'tall'
self.image = self.image_tall
current_x = self.rect.centerx # preserve existing location
current_y = self.rect.centery
self.rect = self.image.get_rect()
self.rect.center = ( current_x, current_y )
def shrink( self ):
self.state = 'short'
self.image = self.image_short
current_x = self.rect.centerx # preserve existing location
current_y = self.rect.centery
self.rect = self.image.get_rect()
self.rect.center = ( current_x, current_y )
I found a workaround that is a bit redundant, but it makes it possible to expand it in case there are more groups
It requires making two groups that can be pulled back and fourth, and changing the foo.draw(screen) to the new group. This is how it looks
nml_sprite = normal()
tal_sprite = tall()
tg = 1 #this is nothing more than a switch to trigger the change
tal_group = pygame.sprite.Group(tal_sprite)
nml_group = pygame.sprite.Group(nml_sprite)
cursprite = nml_group #this variable determines which sprite set is loaded
...
...
if event.key == pygame.K_RETURN:
if tg == 1:
curspt = tal_group
tg = 2
else:
curspt = nml_group
tg = 2
...
...
nml_group.update()
tal_group.update()
curspt.draw(screen)
...

How to make a wave timer in pygame

So, I'm totally new to programming (been doing it for a couple of months) and decided to try coding a game.
On that note, a big thanks to Chris Bradfield for his series of tutorials in pygame coding, they are absolutely great!
However, now that I'm done with the tutorials and need to work on my own, I've come across a problem. I'm making a top-down shooter and making it wave-based. So, when zombies in one wave die, I want to show a timer that counts down until the next wave begins. I THINK I'm down the right path atm, let me show you what I'm working with.
def new(self)
'''
self.timer_flag = False
self.x = threading.Thread(target=self.countdown, args=(TIME_BETWEEN_WAVES,))
'''
def countdown(self, time_between_waves):
self.wave_timer = time_between_waves
for i in range(TIME_BETWEEN_WAVES):
while self.timer_flag:
self.wave_timer -=
time.sleep(1)
def update(self)
'''
self.countdown_has_run = False
if len(self.mobs) == 0:
self.timer_flag = True
if not self.countdown_has_run:
self.countdown_has_run = True
self.x.start()
'''
Now, I also draw my timer when the timer_flag is True, but it doesn't decrement, so I assume the problem lies somewhere in calling/starting the threaded countdown function?
Also, it's my first time posting here, so please let me know what to do to format better etc for you to be able to help
Don't bother with threads. No need to make your live complicated.
Usually, you use a Clock anyway in your game (if not, you should start using it) to limit the framerate, and to ensure that your world moves at a constant rante (if not, you should start doing it).
So if you want to trigger something in, say, 5 seconds, just create a variable that holds the value 5000, and substract the time it took to process your last frame (which is returned by Clock.tick):
clock = pygame.time.Clock()
dt = 0
timer = 5000
while True:
...
timer -= dt
if timer <= 0:
do_something()
dt = clock.tick(60)
I hacked together a simple example below. There, I use a simple class that is also a Sprite to draw the remaining time to the screen. When the timer runs out, it calls a function that creates a new wave of zombies.
In the main loop, I check if there's no timer running and no zombies, and if that's the case, a new timer is created.
Here's the code:
import pygame
import pygame.freetype
import random
# a dict that defines the controls
# w moves up, s moves down etc
CONTROLS = {
pygame.K_w: ( 0, -1),
pygame.K_s: ( 0, 1),
pygame.K_a: (-1, 0),
pygame.K_d: ( 1, 0)
}
# a function that handles the behaviour a sprite that
# should be controled with the keys defined in CONTROLS
def keyboard_controlled_b(player, events, dt):
# let's see which keys are pressed, and create a
# movement vector from all pressed keys.
move = pygame.Vector2()
pressed = pygame.key.get_pressed()
for vec in (CONTROLS[k] for k in CONTROLS if pressed[k]):
move += vec
if move.length():
move.normalize_ip()
move *= (player.speed * dt/10)
# apply the movement vector to the position of the player sprite
player.pos += move
player.rect.center = player.pos
# a function that let's a sprite follow another one
# and kill it if they touch each other
def zombie_runs_to_target_b(target):
def zombie_b(zombie, events, dt):
if target.rect.colliderect(zombie.rect):
zombie.kill()
return
move = target.pos - zombie.pos
if move.length():
move.normalize_ip()
move *= (zombie.speed * dt/10)
zombie.pos += move
zombie.rect.center = zombie.pos
return zombie_b
# a simple generic sprite class that displays a simple, colored rect
# and invokes the given behaviour
class Actor(pygame.sprite.Sprite):
def __init__(self, color, pos, size, behavior, speed, *grps):
super().__init__(*grps)
self.image = pygame.Surface(size)
self.image.fill(color)
self.rect = self.image.get_rect(center=pos)
self.pos = pygame.Vector2(pos)
self.behavior = behavior
self.speed = speed
def update(self, events, dt):
self.behavior(self, events, dt)
# a sprite class that displays a timer
# when the timer runs out, a function is invoked
# and this sprite is killed
class WaveCounter(pygame.sprite.Sprite):
font = None
def __init__(self, time_until, action, *grps):
super().__init__(grps)
self.image = pygame.Surface((300, 50))
self.image.fill((3,2,1))
self.image.set_colorkey((3, 2, 1))
self.rect = self.image.get_rect(topleft=(10, 10))
if not WaveCounter.font:
WaveCounter.font = pygame.freetype.SysFont(None, 32)
WaveCounter.font.render_to(self.image, (0, 0), f'new wave in {time_until}', (255, 255, 255))
self.timer = time_until * 1000
self.action = action
def update(self, events, dt):
self.timer -= dt
self.image.fill((3,2,1))
WaveCounter.font.render_to(self.image, (0, 0), f'new wave in {int(self.timer / 1000) + 1}', (255, 255, 255))
if self.timer <= 0:
self.action()
self.kill()
def main():
pygame.init()
screen = pygame.display.set_mode((600, 480))
screen_rect = screen.get_rect()
clock = pygame.time.Clock()
dt = 0
sprites_grp = pygame.sprite.Group()
zombies_grp = pygame.sprite.Group()
wave_tm_grp = pygame.sprite.GroupSingle()
# the player is controlled with the keyboard
player = Actor(pygame.Color('dodgerblue'),
screen_rect.center,
(32, 32),
keyboard_controlled_b,
5,
sprites_grp)
# this function should be invoked once the timer runs out
def create_new_wave_func():
# let's create a bunch of zombies that follow the player
for _ in range(15):
x = random.randint(0, screen_rect.width)
y = random.randint(-100, 0)
Actor((random.randint(180, 255), 0, 0),
(x, y),
(26, 26),
zombie_runs_to_target_b(player),
random.randint(2, 4),
sprites_grp, zombies_grp)
while True:
events = pygame.event.get()
for e in events:
if e.type == pygame.QUIT:
return
# no timer, no zombies => create new timer
if len(wave_tm_grp) == 0 and len(zombies_grp) == 0:
WaveCounter(5, create_new_wave_func, sprites_grp, wave_tm_grp)
sprites_grp.update(events, dt)
screen.fill((80, 80, 80))
sprites_grp.draw(screen)
pygame.display.flip()
dt = clock.tick(60)
if __name__ == '__main__':
main()
It looks to me you are lacking the mechanism to check how many mobs are left on screen. I imagine it could be something like this:
class CountdownClock:
def __init__(self):
self.start_no = 1
self.time_between_waves = 5
self.t = threading.Thread(target=self.check_mobs_left)
self.t.start()
def check_mobs_left(self):
self.mobs = ["mob" for _ in range(randint(2, 7))] #generate 2-7 mobs per level
print (f"Wave {self.start_no} : Total {len(self.mobs)} mobs found!")
while self.mobs:
print (f"Still {len(self.mobs)} mobs left!")
time.sleep(1)
del self.mobs[-1] #simulate mob kill - remove this line from your actual setting
self.next_wave(self.time_between_waves)
self.time_between_waves +=2 #increased time for each wave
def next_wave(self,time_between_waves):
self.time_left = time_between_waves
print(f"Wave {self.start_no} cleared!")
self.start_no += 1
while self.time_left:
print (f"Next wave in...{self.time_left}")
self.time_left -=1
time.sleep(1)
self.t = threading.Thread(target=self.check_mobs_left)
self.t.start()
a = CountdownClock()
You will have this up constantly without the need to call the method every round and set flag and stuff.

Controlling pygame animation through text input

I need to create a fighting game that gives prompts and accepts input through text, such as a raw input and then performs the animation, while still have the characters animated, e.g. moving back and forth in a ready to fight stance. How would I go about this?
Please note that this is not going to be your typical answer. StackOverflow is to help after all that you can do on your part when you are stuck, it's not meant as a place to come for code, but since I'm assuming other people new to programming will also be confused on things such as these. So I'm going to write some code, and some psuedo code, just so that you get the just of what you would do in such a scenario.
# TODO put your imports up here
pygame.init()
clock = pygame.time.Clock()
gameSurface = pygame.display.set_mode((600, 400)) # 2/3 aspect ratio
FPS = 40 # Set to your own Frames per second
class Animator:
def __init__(self, surface, rows, cols, time_between_frames, on_finish):
self.images = []
self.current_image = 0
self.time_between_frames = time_between_frames # time animator waits before changing surface
self.current_time # tracks time for frames to change
self.on_finish = on_finish # function to call when animation finishes
surf_width = (surface.get_width() / cols) # calculate width
surf_height = (surface.get_height() / rows) # calculate height
for x in range(cols):
for y in range(rows):
surf = pygame.Surface(surface.get_size()) # temp surface
from_rect = pygame.Rect(x * surf_width, y * surf_height, surf_width, surf_height) # rect to blit from
surf.blit(surface, (0,0), from_rect) # draw to temp surface
self.images.append(surf) # add temp surface to the images list
def update(delta):
self.current_time += delta # update current time
if (self.current_time >= self.time_between_frames): # if time to switch surfaces
self.current_time -= self.time_between_frames # take away time from current time
self.current_image += 1 # change image
if self.current_image >= len(self.images): # if current image would throw an out of bounds exception
self.current_image = 0 # reset the current image to the first
def get_frame(self):
return self.images[self.current_image]
class Player:
resting = 0
abdomenKick = 1
def __init__(self, x, y):
self.x = x
self.y = y
self.action = Player.resting
self.restingAnimation = Animation(pygame.image.load("resting.png"), 2, 3, 500)
self.abdomenKickAnimation = Animation(pygame.image.load("abdomenKick.png"), 4, 6, 50)
self.currentAnimation = self.restingAnimation
def update(self, delta):
self.currentAnimation.update(delta)
def draw(self, surface):
surface.blit(self.currentAnimation.get_frame(), (self.x, self.y))
def abdomenKick(self):
self.currentAnimation = self.restingAnimation
class Game:
def __init__(self):
self.player = Player()
def update(self, delta):
self.player.update(delta)
def draw_screen(self, surface):
self.player.draw(surface)
def gameLoop():
game = Game()
while True:
for event in pygame.event.get():
if event.type == KEYDOWN:
if event.key == A:
game.player.abdomenKick() #Or whatever move you have
game.update(clock.get_rawtime())
game.draw_screen()
clock.tick(FPS)
So here is just a brief showcase you can call it of what this might look like.

Why is this small (155 lines-long) Pacman game on Python running so slow?

I have already cut everything I could from the main loop. I also optimized collisions for dynamic and static objects, reducing considerably the number of iterations. But it is still slow on his machine. I'll post the entire file for the case someone wants to test it, but you can just jump to the main loop at "while Exit==false:".
import pygame
from pyeuclid import Vector2
from math import sin,cos,pi
from random import random
class Thing:
def __init__(self,pos):
self.pos = pos
things.append(self)
def update(self): pass
def draw(self,img): pass
def collide(self,who): pass
class DynamicThing(Thing):
def __init__(self,pos):
Thing.__init__(self,pos)
self.vel = Vector2(0,0)
self.lastPos = pos
self.col = (255,255,0)
self.r = 12
dynamic_things.append(self)
def update(self):
self.lastPos = self.pos
self.pos = self.pos + self.vel
def draw(self,img):
pygame.draw.circle(img, (0,0,0), [int(n) for n in self.pos], self.r, self.r)
pygame.draw.circle(img, self.col, [int(n) for n in self.pos], self.r-2, self.r-2)
def collide(self,obj):
Thing.collide(self,obj)
if isinstance(obj,Wall):
self.pos = self.lastPos
class Wall(Thing):
def draw(self,img):
x,y = self.pos.x, self.pos.y
pygame.draw.rect(img, (90,90,200), (x-16,y-16,32,32), 0)
class Pacman(DynamicThing):
def __init__(self):
DynamicThing.__init__(self,Vector2(32*9+16,32*12+16))
self.col = (255,255,0)
def update(self):
DynamicThing.update(self)
if (keyPressed[pygame.K_LEFT]): self.vel.x = -1
if (keyPressed[pygame.K_RIGHT]): self.vel.x = 1
if (keyPressed[pygame.K_DOWN]): self.vel.y = 1
if (keyPressed[pygame.K_UP]): self.vel.y = -1
if (self.vel.x==-1 and not keyPressed[pygame.K_LEFT]): self.vel.x = 0
if (self.vel.x==1 and not keyPressed[pygame.K_RIGHT]): self.vel.x = 0
if (self.vel.y==1 and not keyPressed[pygame.K_DOWN]): self.vel.y = 0
if (self.vel.y==-1 and not keyPressed[pygame.K_UP]): self.vel.y = 0
def collide(self,obj):
DynamicThing.collide(self,obj)
if isinstance(obj,Ghost):
self.pos = Vector2(32*9+16,32*12+16)
class Ghost(DynamicThing):
def __init__(self):
DynamicThing.__init__(self,Vector2(32*9+16,32*10+16))
self.col = (int(random()*255),int(random()*255),int(random()*255))
self.vel = Vector2(0,-2)
def update(self):
DynamicThing.update(self)
if random()<0.01:
self.vel = [Vector2(2,0),Vector2(-2,0),Vector2(0,2),Vector2(0,-2)][int(random()*4)]
def collide(self,obj):
DynamicThing.collide(self,obj)
if isinstance(obj,Wall):
self.vel = [Vector2(2,0),Vector2(-2,0),Vector2(0,2),Vector2(0,-2)][int(random()*4)]
def thingAtPos(pos):
tile_pos = Vector2(int(pos.x/32),int(pos.y/32))
return map[tile_pos.y][tile_pos.x]
# initializate stuff
pygame.init()
clock = pygame.time.Clock()
screen = pygame.display.set_mode([32*19,32*22])
points_in_unit_circle_border = [Vector2(cos(float(a)/8*2*pi),sin(float(a)/8*2*pi)) for a in xrange(8)]
things = []
dynamic_things = []
exit = False
map = [[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
[1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1],
[1,0,1,1,0,1,1,1,0,1,0,1,1,1,0,1,1,0,1],
[1,0,1,1,0,1,1,1,0,1,0,1,1,1,0,1,1,0,1],
[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
[1,0,1,1,0,1,0,1,1,1,1,1,0,1,0,1,1,0,1],
[1,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,1],
[1,1,1,1,0,1,1,1,0,1,0,1,1,1,0,1,1,1,1],
[1,1,1,1,0,1,0,0,0,0,0,0,0,1,0,1,1,1,1],
[1,1,1,1,0,1,0,1,1,0,1,1,0,1,0,1,1,1,1],
[1,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,1],
[1,1,1,1,0,1,0,1,1,1,1,1,0,1,0,1,1,1,1],
[1,1,1,1,0,1,0,0,0,0,0,0,0,1,0,1,1,1,1],
[1,1,1,1,0,1,0,1,1,1,1,1,0,1,0,1,1,1,1],
[1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1],
[1,0,1,1,0,1,1,1,0,1,0,1,1,1,0,1,1,0,1],
[1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1],
[1,1,0,1,0,1,0,1,1,1,1,1,0,1,0,1,0,1,1],
[1,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,1],
[1,0,1,1,1,1,1,1,0,1,0,1,1,1,1,1,0,0,1],
[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]]
#create pacman, walls, ghosts
pacman = Pacman()
for y in xrange(len(map)):
for x in xrange(len(map[y])):
if (map[y][x]==1):
map[y][x] = Wall(Vector2(x*32+16,y*32+16))
for i in xrange(4):
Ghost()
while exit==False:
clock.tick(45)
screen.fill([255,255,255])
keyPressed = pygame.key.get_pressed()
# events
for event in pygame.event.get():
if event.type == pygame.QUIT:
exit = True
if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
exit = True
# more ghosts
if random()<0.001: Ghost()
# updates e draws
for thing in things:
thing.update()
thing.draw(screen)
# collisions
for A in dynamic_things:
#dynamic vs dynamic
for B in dynamic_things:
if A!=B and abs(A.pos-B.pos)<(A.r+B.r):
A.collide(B)
B.collide(A)
#dynamic vs walls
for circle_point in points_in_unit_circle_border:
thing_in_a_border = thingAtPos(A.pos+circle_point*12)
if isinstance(thing_in_a_border,Wall):
A.collide(thing_in_a_border)
pygame.display.flip()
pygame.quit ()
You are redrawing and fliping the whole screen in every loop. I didn't test your program, but on the pacman I know, there are only 5 moving sprites on the screen, you should try to only blit those every frame (and of course if something else changes, that too). And don't display.flip(), just update the areas of the screen that you changed (that normally speeds up a lot).
Of course you need to stop blanking the screen every frame for that, and there will be much management of what to update. There is some extra support for dirty sprites in pygame http://www.pygame.org/docs/ref/sprite.html#pygame.sprite.DirtySprite that help you with that. Or you could maybe just update all 'active' sprites by blanking the position they where and redrawing them in the new position (and obviously everything that also is in those two areas). Collect the effected rects in a list and pass that to update_rects() instead of flipping the screen. There should be no need in drawing the walls in a pacman game in every frame...
Probably not a big source of slowness, but "while exit==False:" requires a little more bytecode to execute than "while not exit:".

Categories