Time Delay and keeping track of the of # of click - python

I' am trying to make a program using pygame. The program involves two tiles that switch colours and turn back into their original color once the two tiles have been clicked and after a 1-second delay. My problem is that whenever I tried to implement the pygame.time.delay , it delays the whole system and also affects the scoring mechanism of the program. I tried solving this problem by writing the codes found in handle_color_change and update methods in the game class
Any suggestions to fix this problem is greatly appreciated
import pygame,time,random
# User-defined functions
def main():
# for initializing all pygame modules
pygame.init()
# this creates the pygame display window
surface_width = 500
surface_height = 400
surface = pygame.display.set_mode((surface_width,surface_height))
# this sets the caption of the window to 'Pong'
pygame.display.set_caption('Painting')
# creates a game object
game = Game(surface, surface_width, surface_height)
# this starts the game loop by calling the play method found in the game object
game.play()
# quits pygame and cleans up the pygame window
pygame.quit()
# User-defined classes
class Game:
# an object in this class represents the complete game
def __init__(self,surface,surface_width,surface_height):
# # Initialize a Game.
# - self is the Game to initialize
# - surface is the display window surface object
# - surface_width is the display width size
# - surface_height is the display height size
# attributes that are needed to run any game
self.surface = surface
self.surface_width = surface_width
self.surface_height = surface_height
self.close_clicked = False
self.surface_color = pygame.Color('black')
# attributes that are needed to run this specific game
self.FPS = 60
self.game_clock = pygame.time.Clock()
self._continue = True
self.score = [0,0]
self.max_mismatch = 5
# Game specific objects
self.default_color = 'white'
self.color_options = ('blue' , 'red', 'yellow', 'green')
self.tile_width = 50
self.tile_height = 150
self.tile_left = Tile( self.default_color, (self.surface_width/3) - self.tile_width, (self.surface_height/2)/ 2 , self.tile_width, self.tile_height , self.surface)
self.tile_right = Tile(self.default_color, self.surface_width/2 + self.tile_width, (self.surface_height/2)/ 2
,self.tile_width, self.tile_height , self.surface)
def play(self):
# this is main game loop
# plays the game until the players has closed the window or the score of a players equals the max score
# - self is the game that should be continued or not
while not self.close_clicked:
self.main_handle_events()
self.draw()
self.update()
self.game_clock.tick(self.FPS)
def draw(self):
# this draws the circle and the rectangles that are needed for this specific game
# -self is the Game to draw
self.surface.fill(self.surface_color)
self.tile_left.draw()
self.tile_right.draw()
self.display_score_match()
self.display_score_mismatch(self.surface_width)
pygame.display.update() # makes the updated surface appear on the display
def update(self):
events = pygame.event.get()
if self.handle_color_change(events):
pygame.time.delay(1000)
self.tile_left.set_color(self.default_color)
self.tile_right.set_color(self.default_color)
self.update_score()
def main_handle_events(self):
# handles each user events by changing the game state appropriately
# -self is the Game of whose events are handled
events = pygame.event.get()
for event in events:
if event.type == pygame.QUIT:
self.close_clicked = True
if event.type == pygame.MOUSEBUTTONDOWN:
self.handle_color_change(event)
#self.update_score()
#self.handle_color_change(event)
def display_score_match(self):
text_string = 'Match: ' + str(self.score[0])
text_colour = pygame.Color('white')
text_font = pygame.font.SysFont('Times New Roman',25)
text_image = text_font.render(text_string, True, text_colour)
text_pos = [0,0]
self.surface.blit(text_image, text_pos)
def display_score_mismatch(self, surface_width):
text_string = 'Mismatch: ' + str(self.score[1])
text_colour = pygame.Color('white')
text_font = pygame.font.SysFont('Times New Roman',25)
text_image = text_font.render(text_string, True, text_colour)
text_pos = [(surface_width - text_image.get_width()), 0]
self.surface.blit(text_image, text_pos)
def handle_color_change(self, event):
tile_clicked = 0
change_white = False
if event.button == 1 and self.tile_left.inside_tile(event.pos) == True:
self.tile_left.set_color(random.choice(self.color_options))
tile_clicked += 1
if event.button == 1 and self.tile_right.inside_tile(event.pos) == True:
self.tile_right.set_color(random.choice(self.color_options))
tile_clicked +=1
if tile_clicked == 2:
change_white = True
tile_clicked = 0
return change_white
def update_score(self):
if self.tile_left.color_match(self.tile_right) == True:
self.score[0] = self.score[0] + 1
else:
self.score[1] = self.score[1] + 1
class Tile:
def __init__(self, rect_color, rect_left, rect_top, rect_width, rect_height,surface):
# Initialize a rectabgle which is used as a paintbrush.
# - self is the rectangle to initialize
# - rect_color is the pygame.Color of the dot
# - rect_height is the int length of the rectangle in the y axis
# - rect_width is the int width of the rectangle in the x axis
# - rect_left is the int coordinate position of the rectangle in the x axis
# - rect_top is the int coordinate position of the rectangle in the y axis
# - rect_velocity is a list of x and y components and the speed of which the rectangles can move
self.rect_colour = pygame.Color(rect_color)
self.rect_height = rect_height
self.rect_width = rect_width
self.rect_left = rect_left
self.rect_top = rect_top
self.surface = surface
self.rect_parameters = pygame.Rect(rect_left, rect_top, rect_width, rect_height)
def draw(self):
# draws the rectangle on the surface
# - self is the rectangle
pygame.draw.rect(self.surface, self.rect_colour, self.rect_parameters)
def inside_tile(self, position):
inside = False
if self.rect_parameters.collidepoint(position):
inside = True
return inside
def set_color(self, color):
self.rect_colour = pygame.Color(color)
def color_match(self, other_tile):
match = False
if self.rect_colour == other_tile.rect_colour:
match = True
return match
main()

Never use a delay in your application loop. Use the application loop. Compute the point in time when the rectangles have to change color back. Change the color after the current time is greater than the calculated point of time.
In pygame the system time can be obtained by calling pygame.time.get_ticks(), which returns the number of milliseconds since pygame.init() was called. See pygame.time module.
Add 2 attributes self.tile_clicked = 0 and self.turn_white_time = 0 to the class Game:
class Game:
def __init__(self,surface,surface_width,surface_height):
# [...]
self.tile_clicked = []
self.turn_white_time = 0
Compute the the point in time when the rectangles have to change color back after the 2nd rectangle was clicked:
class Game:
# [...]
def handle_color_change(self, event):
if len(self.tile_clicked) < 2:
if 1 not in self.tile_clicked:
if event.button == 1 and self.tile_left.inside_tile(event.pos) == True:
self.tile_left.set_color(random.choice(self.color_options))
self.tile_clicked.append(1)
if 2 not in self.tile_clicked:
if event.button == 1 and self.tile_right.inside_tile(event.pos) == True:
self.tile_right.set_color(random.choice(self.color_options))
self.tile_clicked.append(2)
if len(self.tile_clicked) == 2:
delay_time = 1000 # 1000 milliseconds == 1 second
self.turn_white_time = pygame.time.get_ticks() + delay_time
get_ticks() returns the current time. A time is just a number. get_ticks() + delay_time is a time in the future. When the program is running, the current time is continuously retrieved and compared with turn_white_time. At some point the current time is greater than turn_white_time and the color of the rectangles is changed.
Change back to the white color after the current time is greater than the calculated point of time in update:
class Game:
# [...]
def update(self):
current_time = pygame.time.get_ticks()
if len(self.tile_clicked) == 2 and current_time > self.turn_white_time:
self.tile_left.set_color(self.default_color)
self.tile_right.set_color(self.default_color)
self.tile_clicked = []
Complete example:
import pygame,time,random
# User-defined functions
def main():
# for initializing all pygame modules
pygame.init()
# this creates the pygame display window
surface_width = 500
surface_height = 400
surface = pygame.display.set_mode((surface_width,surface_height))
# this sets the caption of the window to 'Pong'
pygame.display.set_caption('Painting')
# creates a game object
game = Game(surface, surface_width, surface_height)
# this starts the game loop by calling the play method found in the game object
game.play()
# quits pygame and cleans up the pygame window
pygame.quit()
# User-defined classes
class Game:
# an object in this class represents the complete game
def __init__(self,surface,surface_width,surface_height):
# # Initialize a Game.
# - self is the Game to initialize
# - surface is the display window surface object
# - surface_width is the display width size
# - surface_height is the display height size
# attributes that are needed to run any game
self.surface = surface
self.surface_width = surface_width
self.surface_height = surface_height
self.close_clicked = False
self.surface_color = pygame.Color('black')
# attributes that are needed to run this specific game
self.FPS = 60
self.game_clock = pygame.time.Clock()
self._continue = True
self.score = [0,0]
self.max_mismatch = 5
# Game specific objects
self.default_color = 'white'
self.color_options = ('blue' , 'red', 'yellow', 'green')
self.tile_width = 50
self.tile_height = 150
self.tile_left = Tile( self.default_color, (self.surface_width/3) - self.tile_width, (self.surface_height/2)/ 2 , self.tile_width, self.tile_height , self.surface)
self.tile_right = Tile(self.default_color, self.surface_width/2 + self.tile_width, (self.surface_height/2)/ 2
,self.tile_width, self.tile_height , self.surface)
self.tile_clicked = []
self.turn_white_time = 0
def play(self):
# this is main game loop
# plays the game until the players has closed the window or the score of a players equals the max score
# - self is the game that should be continued or not
while not self.close_clicked:
self.main_handle_events()
self.draw()
self.update()
self.game_clock.tick(self.FPS)
def draw(self):
# this draws the circle and the rectangles that are needed for this specific game
# -self is the Game to draw
self.surface.fill(self.surface_color)
self.tile_left.draw()
self.tile_right.draw()
self.display_score_match()
self.display_score_mismatch(self.surface_width)
pygame.display.update() # makes the updated surface appear on the display
def update(self):
current_time = pygame.time.get_ticks()
if len(self.tile_clicked) == 2 and current_time > self.turn_white_time:
self.tile_left.set_color(self.default_color)
self.tile_right.set_color(self.default_color)
self.tile_clicked = []
def main_handle_events(self):
# handles each user events by changing the game state appropriately
# -self is the Game of whose events are handled
events = pygame.event.get()
for event in events:
if event.type == pygame.QUIT:
self.close_clicked = True
if event.type == pygame.MOUSEBUTTONDOWN:
self.handle_color_change(event)
#self.update_score()
#self.handle_color_change(event)
def display_score_match(self):
text_string = 'Match: ' + str(self.score[0])
text_colour = pygame.Color('white')
text_font = pygame.font.SysFont('Times New Roman',25)
text_image = text_font.render(text_string, True, text_colour)
text_pos = [0,0]
self.surface.blit(text_image, text_pos)
def display_score_mismatch(self, surface_width):
text_string = 'Mismatch: ' + str(self.score[1])
text_colour = pygame.Color('white')
text_font = pygame.font.SysFont('Times New Roman',25)
text_image = text_font.render(text_string, True, text_colour)
text_pos = [(surface_width - text_image.get_width()), 0]
self.surface.blit(text_image, text_pos)
def handle_color_change(self, event):
if len(self.tile_clicked) < 2:
if 1 not in self.tile_clicked:
if event.button == 1 and self.tile_left.inside_tile(event.pos) == True:
self.tile_left.set_color(random.choice(self.color_options))
self.tile_clicked.append(1)
if 2 not in self.tile_clicked:
if event.button == 1 and self.tile_right.inside_tile(event.pos) == True:
self.tile_right.set_color(random.choice(self.color_options))
self.tile_clicked.append(2)
if len(self.tile_clicked) == 2:
delay_time = 1000 # 1000 milliseconds == 1 second
self.turn_white_time = pygame.time.get_ticks() + delay_time
def update_score(self):
if self.tile_left.color_match(self.tile_right) == True:
self.score[0] = self.score[0] + 1
else:
self.score[1] = self.score[1] + 1
class Tile:
def __init__(self, rect_color, rect_left, rect_top, rect_width, rect_height,surface):
# Initialize a rectabgle which is used as a paintbrush.
# - self is the rectangle to initialize
# - rect_color is the pygame.Color of the dot
# - rect_height is the int length of the rectangle in the y axis
# - rect_width is the int width of the rectangle in the x axis
# - rect_left is the int coordinate position of the rectangle in the x axis
# - rect_top is the int coordinate position of the rectangle in the y axis
# - rect_velocity is a list of x and y components and the speed of which the rectangles can move
self.rect_colour = pygame.Color(rect_color)
self.rect_height = rect_height
self.rect_width = rect_width
self.rect_left = rect_left
self.rect_top = rect_top
self.surface = surface
self.rect_parameters = pygame.Rect(rect_left, rect_top, rect_width, rect_height)
def draw(self):
# draws the rectangle on the surface
# - self is the rectangle
pygame.draw.rect(self.surface, self.rect_colour, self.rect_parameters)
def inside_tile(self, position):
inside = False
if self.rect_parameters.collidepoint(position):
inside = True
return inside
def set_color(self, color):
self.rect_colour = pygame.Color(color)
def color_match(self, other_tile):
match = False
if self.rect_colour == other_tile.rect_colour:
match = True
return match
main()

Related

How to create and move 2 or more objects simultaneously

I was trying to create a function that will load an image and move the image downwards while loading another image and still moving with the same speed as the first. But I don't know how to do it, all I can do for now is to load the next image when the y coordinate of the first is at 500
def aliens():
s = 0
i = "345678"
c = 1
b = int("".join(random.sample(i, c)))
h = 1
while h < 2:
while s <= b:
alien = pygame.image.load("ali.jpg")
alien = pygame.transform.scale(alien, (100, 100))
pos = "0123456789"
l = 3
x = int("".join(random.sample(pos, l)))
y = 0
if x > 500:
h = 3
crash = False
s +=1
while not crash :
scn.blit(alien, (x, y))
y +=2
pygame.display.flip()
if y > 500:
crash = True
The core of your problem is the fact that you are trying to do too many things in the same function. Try to follow the SRP (Single Responsability Principle).
Personnally I would use OOP (Object Oriented Programming) here but I don't know if you are familar with the concept, so first let's stick to the basics.
Here is how I would separate things up, if I wasn't allowed to use OOP:
First I would define two constants:
ALIEN_SIZE = 100
ALIEN_IMG = pg.image.load("green_square.png").convert()
ALIEN_IMG = pg.transform.scale(ALIEN_IMG, (ALIEN_SIZE, ALIEN_SIZE))
Since you are always using the same values, there is no need to load your image all the time. Loading your image just once will have a very positive impact on game's performances.
(Note that I used a diffrent image since you did not provide the image you use but feel free to put the link to your image back)
Then I would define an alien list:
aliens = []
I don't know how you plan to spawn aliens in your project ? For this example I used a custom event with a timer:
# Custom event
SPAWN_ALIEN = pg.USEREVENT
pg.time.set_timer(SPAWN_ALIEN, 2000)
# This function handles all pygame events
def handle_evt():
for evt in pg.event.get():
if evt.type == pg.QUIT:
exit()
if evt.type == SPAWN_ALIEN:
spawn_alien()
# This function spawns an alien
def spawn_alien():
pos = [rd.randint(0, screen.get_width() - ALIEN_SIZE), 0]
aliens.append(pos)
To make aliens move, I use a update() function:
# update aliens position and check if they leave the screen
def update():
for alien in aliens:
move_alien(alien)
if out_of_screen(alien):
kill_alien(alien)
# This function update alien's y position
def move_alien(alien):
alien[1]+=1
# This function remove the given alien from the aliens list
def kill_alien(alien):
aliens.remove(alien)
# This function returns True if the given position is outside screen's boundries
def out_of_screen(pos):
x, y = pos
if x < 0 or x > screen.get_width():
return True
elif y < 0 or y > screen.get_height():
return True
return False
Finally to render things :
def draw():
# Clear screen
screen.fill((0,0,0))
# Draw aliens
for alien in aliens:
screen.blit(ALIEN_IMG, alien)
Here is the full code, so you can see how everything is interacting :
# General imports
import pygame as pg
import random as rd
import sys
# Init
pg.init()
# Vars & consts
screen = pg.display.set_mode((500, 500))
pg.display.set_caption("Example")
FPS = 60
clock = pg.time.Clock()
ALIEN_SIZE = 100
ALIEN_IMG = pg.image.load("green_square.png").convert()
ALIEN_IMG = pg.transform.scale(ALIEN_IMG, (ALIEN_SIZE, ALIEN_SIZE))
# Custom event
SPAWN_ALIEN = pg.USEREVENT
pg.time.set_timer(SPAWN_ALIEN, 2000)
# Main functions
def update():
for alien in aliens:
move_alien(alien)
if out_of_screen(alien):
kill_alien(alien)
def draw():
# Clear screen
screen.fill((0,0,0))
# Draw aliens
for alien in aliens:
screen.blit(ALIEN_IMG, alien)
def handle_evt():
for evt in pg.event.get():
if evt.type == pg.QUIT:
exit()
if evt.type == SPAWN_ALIEN:
spawn_alien()
def exit():
pg.quit()
sys.exit()
# Other functions
def spawn_alien():
pos = [rd.randint(0, screen.get_width() - ALIEN_SIZE), 0]
aliens.append(pos)
def move_alien(alien):
alien[1]+=1
def kill_alien(alien):
aliens.remove(alien)
def out_of_screen(pos):
x, y = pos
if x < 0 or x > screen.get_width():
return True
elif y < 0 or y > screen.get_height():
return True
return False
# Main loop
if __name__ == '__main__':
aliens = []
spawn_alien()
while True:
handle_evt()
update()
draw()
clock.tick(FPS)
pg.display.flip()
And just in case, here is an OOP approach :
# General imports
import pygame as pg
import random as rd
import sys
# Init
pg.init()
# Vars
screen = pg.display.set_mode((500, 500))
pg.display.set_caption("Example")
FPS = 60
clock = pg.time.Clock()
# Class
class Alien():
LIST = []
SIZE = 100
IMG = pg.image.load("green_square.png").convert()
IMG = pg.transform.scale(IMG, (SIZE, SIZE))
def __init__(self, screen_width):
self.img = Alien.IMG
self.pos = (rd.randint(0, screen_width - Alien.SIZE), 0)
# Add alien to the list
Alien.LIST.append(self)
def move(self):
x, y = self.pos
self.pos = (x, y+1)
def has_crashed(self, boundries):
x, y = self.pos
if x < 0 or x > boundries[0]:
return True
elif y < 0 or y > boundries[1]:
return True
return False
def kill(self):
Alien.LIST.remove(self)
# Custom event
SPAWN_ALIEN = pg.USEREVENT
pg.time.set_timer(SPAWN_ALIEN, 2000)
# Main functions
def update():
for alien in Alien.LIST:
alien.move()
if alien.has_crashed(screen.get_size()):
alien.kill()
def draw():
# Clear screen
screen.fill((0,0,0))
# Draw aliens
for alien in Alien.LIST:
screen.blit(alien.img, alien.pos)
def handle_evt():
for evt in pg.event.get():
if evt.type == pg.QUIT:
exit()
if evt.type == SPAWN_ALIEN:
spawn_alien()
def exit():
pg.quit()
sys.exit()
# Other functions
def spawn_alien():
Alien(screen.get_width())
# Main loop
if __name__ == '__main__':
while True:
handle_evt()
update()
draw()
clock.tick(FPS)
pg.display.flip()
I hope all those informations can help you solve your problem. It's hard to know if this, really is the behavior you wanted for your aliens. That's because your variables names are not exactly explicit. One letter variable names are rarely a good idea. But anyway even if you need them to move a little diffrently, you can take inspiration of this code organization.
Have fun !

pygame rect doesn't change position while i give it a new position and update it

okay so i'm fairly new to programming and i'm making a game based on the arcade game alien invasion. but i want to make an intro with moving text to show credits at the start because it's fun and good practice to learn. but i cant seem to make it work. i want the text to fly from the side of the screen to middle stop there then fly away again
so here is the function that draws the texts.
from Settings import *
import sys
class TextsObjects:
def __init__(self):
self.setting = Settings()
self.intro_settings = IntroSettings()
self.display_intro_1 = DisplayText(
self.intro_settings.intro_text_1,
self.intro_settings.intro_text_1_pos_x,
self.intro_settings.intro_text_1_pos_y,
self.intro_settings.font_size,
self.setting.light_grey
)
self.display_intro_2 = DisplayText(
self.intro_settings.intro_text_2,
self.intro_settings.intro_text_2_pos_x,
self.intro_settings.intro_text_2_pos_y,
self.intro_settings.font_size,
self.setting.light_grey
)
self.display_intro_title = DisplayText(
self.intro_settings.intro_text_title,
self.intro_settings.intro_text_title_pos_x,
self.intro_settings.intro_text_title_pos_y,
self.intro_settings.font_size,
self.setting.light_grey)
class Intro:
def __init__(self, screen, setting):
self.screen = screen
self.setting = setting
self.i_setting = IntroSettings()
self.intro_text = TextsObjects()
self.clock = pygame.time.Clock()
self.speed = 50 * setting.time
self.screen_c_left = setting.screen_width / 2 - self.speed / 2
self.screen_c_right = setting.screen_width / 2 + self.speed / 2
def intro_update(self):
# reset the background so you don't get multiple drawings onscreen
self.screen.fill(self.setting.dark_blueish)
if 0 <= self.i_setting.intro_text_1_pos_x <= \
self.setting.screen_width:
# Draw the first text
self.intro_text.display_intro_1.draw_me_with_shadow(
self.screen)
if self.screen_c_left < self.i_setting.intro_text_2_pos_x < \
self.screen_c_right:
# Draw the second text
self.intro_text.display_intro_2.draw_me_with_shadow(
self.screen)
pygame.display.update()
def show_intro(self):
self.i_setting.intro_text_1_pos_x += self.speed
# show the intro for self.intro_time amount of time
for frames in range(0, self.setting.fps * 3):
# test for events
for event in pygame.event.get():
print(event)
# when the window is closed stop the game loop
if event.type == pygame.QUIT:
sys.exit()
self.intro_update()
self.setting.clock.tick(self.setting.fps)
and here are the classes that are relevant:
class Settings:
def __init__(self):
# screen size
self.screen_width = 800
self.screen_height = 500
# set clock and fps
self.clock = pygame.time.Clock()
self.fps = 60
# calculate the seconds in every frame
self.time = 1 / self.fps
class IntroSettings:
def __init__(self):
self.setting = Settings()
self.font_size = 100
self.timer_font_size = 10
self.intro_text_1_pos_x = (self.setting.screen_width / 2) - 100
self.intro_text_1_pos_y = \
(self.setting.screen_height / 2 - self.font_size)
self.intro_text_1 = "A game by:"
self.intro_text_2_pos_x = (self.setting.screen_width / 2)
self.intro_text_2_pos_y = \
(self.setting.screen_height / 2 + self.font_size)
self.intro_text_2 = "Mark Olieman"
self.intro_text_title_pos_x = (self.setting.screen_width / 2)
self.intro_text_title_pos_y = (self.setting.screen_height + self.font_size)
self.intro_text_title = "Alien Invasion!"
self.intro_text_timer_pos_x = 20
self.intro_text_timer_pos_y = 20
self.speed = -2
class DisplayText:
def __init__(self, text, pos_x, pos_y, font_size, text_color):
self.font = pygame.font.Font("freesansbold.ttf", font_size)
self.text_surf = self.font.render(text, True, text_color)
self.text_rect = self.text_surf.get_rect()
self.text_rect.center = (pos_x, pos_y)
self.shadow_color = (0, 0, 0)
self.text_surf_shadow = self.font.render(text, True, self.shadow_color)
self.text_rect_shadow = self.text_surf.get_rect()
self.text_rect_shadow.center = (pos_x + 10, pos_y + 5)
def draw_me(self, screen):
screen.blit(self.text_surf, self.text_rect)
def draw_me_with_shadow(self, screen):
screen.blit(self.text_surf_shadow, self.text_rect_shadow)
screen.blit(self.text_surf, self.text_rect)
i am sorry for the amount of code but because i don't know where the problem lies i want you guys to have all the relevant information so at last here is my main game loop
import pygame
from Settings import *
from Entities import *
from Game_functions import *
from Menu import *
# make the modules available
setting = Settings()
screen = pygame.display.set_mode((setting.screen_width, setting.screen_height))
pygame.display.set_caption("Alien Invasion, By Mark Olieman")
intro = Intro(screen, setting)
player = Player(screen, setting)
bullet = Bullet(screen, setting, player)
intro_set = IntroSettings()
# initizialize pygame
pygame.init()
pygame.mixer.init()
# initizialize the screen and set a caption
# function to start gaming
def gaming():
bullets = pygame.sprite.Group()
intro.show_intro()
intro_set.intro_text_1_pos_x += intro.speed
print(intro_set.intro_text_1_pos_x)
# Game Loop
while True:
# check events and make actions happen
events(bullets, player, screen, setting)
# update everything and draw bullets
update(player, setting, bullets, screen)
# update screen at certain fps
pygame.display.update()
setting.clock.tick(setting.fps)
# Start the game
gaming()
# Stop the game
pygame.quit()
I found two mistakes in Intro.show_intro()
you add speed before loop but you have to do it inside loop
you add speed to self.i_setting.intro_text_1_pos_x but you have to add
self.intro_text.display_intro_1.text_rect.x += self.speed
self.intro_text.display_intro_1.text_rect_shadow.x += self.speed
Correct version which moves text (and shadow) "A game by:"
def show_intro(self):
#self.i_setting.intro_text_1_pos_x += self.speed
# show the intro for self.intro_time amount of time
for frames in range(0, self.setting.fps * 3):
# test for events
for event in pygame.event.get():
print(event)
# when the window is closed stop the game loop
if event.type == pygame.QUIT:
sys.exit()
# you have to add speed inside loop
#self.i_setting.intro_text_1_pos_x += self.speed
self.intro_text.display_intro_1.text_rect.x += self.speed
self.intro_text.display_intro_1.text_rect_shadow.x += self.speed
self.intro_update()
self.setting.clock.tick(self.setting.fps)
There can be another problem - rect.x can keep only integer values but speed has value 0.83 so when you add 0.83 then it can be rounded to 1 so it may move faster then you expect - or it can move too far (or may move not so smooth as you expected). If speed will be smaller then 0.5 then it may even round it to 0 so it will not move.
You may have to keep position in separated values as float values, change them and copy to rect.x only before drawing.
Full code which I run in one file - I removed some imports and elements which I hadn't (ie, Player)
import sys
class TextsObjects:
def __init__(self):
self.setting = Settings()
self.intro_settings = IntroSettings()
self.display_intro_1 = DisplayText(
self.intro_settings.intro_text_1,
self.intro_settings.intro_text_1_pos_x,
self.intro_settings.intro_text_1_pos_y,
self.intro_settings.font_size,
self.setting.light_grey
)
self.display_intro_2 = DisplayText(
self.intro_settings.intro_text_2,
self.intro_settings.intro_text_2_pos_x,
self.intro_settings.intro_text_2_pos_y,
self.intro_settings.font_size,
self.setting.light_grey
)
self.display_intro_title = DisplayText(
self.intro_settings.intro_text_title,
self.intro_settings.intro_text_title_pos_x,
self.intro_settings.intro_text_title_pos_y,
self.intro_settings.font_size,
self.setting.light_grey)
class Intro:
def __init__(self, screen, setting):
self.screen = screen
self.setting = setting
self.i_setting = IntroSettings()
self.intro_text = TextsObjects()
self.clock = pygame.time.Clock()
self.speed = 150 * setting.time
print(self.speed)
self.screen_c_left = setting.screen_width / 2 - self.speed / 2
self.screen_c_right = setting.screen_width / 2 + self.speed / 2
def intro_update(self):
# reset the background so you don't get multiple drawings onscreen
self.screen.fill(self.setting.dark_blueish)
if 0 <= self.i_setting.intro_text_1_pos_x <= self.setting.screen_width:
# Draw the first text
self.intro_text.display_intro_1.draw_me_with_shadow(self.screen)
if self.screen_c_left < self.i_setting.intro_text_2_pos_x < self.screen_c_right:
# Draw the second text
self.intro_text.display_intro_2.draw_me_with_shadow(self.screen)
pygame.display.update()
def show_intro(self):
#self.i_setting.intro_text_1_pos_x += self.speed
# show the intro for self.intro_time amount of time
for frames in range(0, self.setting.fps * 3):
# test for events
for event in pygame.event.get():
print(event)
# when the window is closed stop the game loop
if event.type == pygame.QUIT:
sys.exit()
#self.i_setting.intro_text_1_pos_x += self.speed
self.intro_text.display_intro_1.text_rect.x += self.speed
self.intro_text.display_intro_1.text_rect_shadow.x += self.speed
self.intro_update()
self.setting.clock.tick(self.setting.fps)
class Settings:
def __init__(self):
# screen size
self.screen_width = 800
self.screen_height = 500
# set clock and fps
self.clock = pygame.time.Clock()
self.fps = 60
# calculate the seconds in every frame
self.time = 1 / self.fps
self.light_grey = (128,128,128)
self.dark_blueish = (0,0,255)
class IntroSettings:
def __init__(self):
self.setting = Settings()
self.font_size = 100
self.timer_font_size = 10
self.intro_text_1_pos_x = (self.setting.screen_width / 2) - 100
self.intro_text_1_pos_y = \
(self.setting.screen_height / 2 - self.font_size)
self.intro_text_1 = "A game by:"
self.intro_text_2_pos_x = (self.setting.screen_width / 2)
self.intro_text_2_pos_y = \
(self.setting.screen_height / 2 + self.font_size)
self.intro_text_2 = "Mark Olieman"
self.intro_text_title_pos_x = (self.setting.screen_width / 2)
self.intro_text_title_pos_y = (self.setting.screen_height + self.font_size)
self.intro_text_title = "Alien Invasion!"
self.intro_text_timer_pos_x = 20
self.intro_text_timer_pos_y = 20
self.speed = -2
class DisplayText:
def __init__(self, text, pos_x, pos_y, font_size, text_color):
self.font = pygame.font.Font("freesansbold.ttf", font_size)
self.text_surf = self.font.render(text, True, text_color)
self.text_rect = self.text_surf.get_rect()
self.text_rect.center = (pos_x, pos_y)
self.shadow_color = (0, 0, 0)
self.text_surf_shadow = self.font.render(text, True, self.shadow_color)
self.text_rect_shadow = self.text_surf.get_rect()
self.text_rect_shadow.center = (pos_x + 10, pos_y + 5)
def draw_me(self, screen):
screen.blit(self.text_surf, self.text_rect)
def draw_me_with_shadow(self, screen):
screen.blit(self.text_surf_shadow, self.text_rect_shadow)
screen.blit(self.text_surf, self.text_rect)
import pygame
#from Entities import *
#from Game_functions import *
#from Menu import *
# initizialize pygame
pygame.init()
pygame.mixer.init()
# make the modules available
setting = Settings()
screen = pygame.display.set_mode((setting.screen_width, setting.screen_height))
pygame.display.set_caption("Alien Invasion, By Mark Olieman")
intro = Intro(screen, setting)
#player = Player(screen, setting)
#bullet = Bullet(screen, setting, player)
intro_set = IntroSettings()
# initizialize the screen and set a caption
# function to start gaming
def gaming():
bullets = pygame.sprite.Group()
intro.show_intro()
intro_set.intro_text_1_pos_x += intro.speed
print(intro_set.intro_text_1_pos_x)
# Game Loop
while True:
# check events and make actions happen
#events(bullets, player, screen, setting)
# update everything and draw bullets
#update(player, setting, bullets, screen)
# update screen at certain fps
pygame.display.update()
setting.clock.tick(setting.fps)
# Start the game
gaming()
# Stop the game
pygame.quit()

Can't get attack animation to work for arcade library with python

I'm using a tutorial from arcade.academy and the game runs fine so far (a guy walking around collecting coins and scoring points) but I want to have an attack animation occur when the user presses Q so I can turn some of the coins into enemies and make a little RPG and increase my knowledge.
This is what I've got so far but for some reason the self.is_attacking boolean either doesn't trigger or isn't linked up properly.
Here's what I've got so far.
import arcade
import random
import os
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
SCREEN_TITLE = "Move with a Sprite Animation Example"
COIN_SCALE = 0.5
COIN_COUNT = 50
CHARACTER_SCALING = 1
MOVEMENT_SPEED = 5
UPDATES_PER_FRAME = 7
# Constants used to track if the player is facing left or right
RIGHT_FACING = 0
LEFT_FACING = 1
def load_texture_pair(filename):
"""
Load a texture pair, with the second being a mirror image.
"""
return [
arcade.load_texture(filename, scale=CHARACTER_SCALING),
arcade.load_texture(filename, scale=CHARACTER_SCALING, mirrored=True)
]
class PlayerCharacter(arcade.Sprite):
def __init__(self):
# Set up parent class
super().__init__()
# Default to face-right
self.character_face_direction = RIGHT_FACING
# Used for flipping between image sequences
self.cur_texture = 0
self.attack_texture = 0
# Track our state
self.jumping = False
self.climbing = False
self.is_on_ladder = False
self.is_attacking = False
# Adjust the collision box. Default includes too much empty space
# side-to-side. Box is centered at sprite center, (0, 0)
self.points = [[-22, -64], [22, -64], [22, 28], [-22, 28]]
# --- Load Textures ---
# Images from Kenney.nl's Asset Pack 3
main_path = "images/Sprites/rogue like character/rogue like"
# main_path = "platform_tutorial/images/Female person/PNG/Poses/character_femalePerson"
# main_path = "platform_tutorial/images/Male person/PNG/Poses/character_malePerson"
# main_path = "platform_tutorial/images/Male adventurer/PNG/Poses/character_maleAdventurer"
# main_path = "platform_tutorial/images/Zombie/PNG/Poses/character_zombie"
# main_path = "platform_tutorial/images/Robot/PNG/Poses/character_robot"
# Load textures for idle standing
self.idle_texture_pair = load_texture_pair(f"{main_path} idle_Animation 1_0.png")
# Load textures for walking
self.walk_textures = []
for i in range(6):
texture = load_texture_pair(f"{main_path} run_Animation 1_{i}.png")
self.walk_textures.append(texture)
# Load textures for attacking
self.attack_textures = []
for i in range(10):
texture = load_texture_pair(f"{main_path} attack_Animation 1_{i}.png")
self.attack_textures.append(texture)
def update_animation(self, delta_time: float = 1/60):
# Figure out if we need to flip face left or right
if self.change_x < 0 and self.character_face_direction == RIGHT_FACING:
self.character_face_direction = LEFT_FACING
elif self.change_x > 0 and self.character_face_direction == LEFT_FACING:
self.character_face_direction = RIGHT_FACING
# Idle animation
if self.change_x == 0 and self.change_y == 0:
self.texture = self.idle_texture_pair[self.character_face_direction]
return
# Walking animation
self.cur_texture += 1
if self.cur_texture > 5 * UPDATES_PER_FRAME:
self.cur_texture = 0
self.texture = self.walk_textures[self.cur_texture // UPDATES_PER_FRAME][self.character_face_direction]
# Attacking animation
if self.is_attacking:
self.cur_texture += 1
if self.cur_texture > 9 * UPDATES_PER_FRAME:
self.cur_texture = 0
self.is_attacking = False
self.texture = self.attack_textures[self.cur_texture // UPDATES_PER_FRAME][self.character_face_direction]
class MyGame(arcade.Window):
# Main application class.
def __init__(self, width, height, title):
# Initializer
super().__init__(width, height, title)
# Set the working directory (where we expect to find files) to the same
# directory this .py file is in. You can leave this out of your own
# code, but it is needed to easily run the examples using "python -m"
# as mentioned at the top of this program.
file_path = os.path.dirname(os.path.abspath(__file__))
os.chdir(file_path)
# Set up the game and initialize the variables.
# Sprite lists
self.player_list = None
self.coin_list = None
# Set up the player
self.score = 0
self.player = None
self.is_attacking = False
def setup(self):
self.player_list = arcade.SpriteList()
self.coin_list = arcade.SpriteList()
# Set up the player
self.score = 0
self.player = PlayerCharacter()
self.player.center_x = SCREEN_WIDTH // 2
self.player.center_y = SCREEN_HEIGHT // 2
self.player.scale = 0.8
self.player_list.append(self.player)
for i in range(COIN_COUNT):
coin = arcade.AnimatedTimeSprite(scale=0.5)
coin.center_x = random.randrange(SCREEN_WIDTH)
coin.center_y = random.randrange(SCREEN_HEIGHT)
coin.textures = []
coin.textures.append(arcade.load_texture("images/items/Coingold.png", scale=COIN_SCALE))
coin.textures.append(arcade.load_texture("images/items/Coingold.png", scale=COIN_SCALE))
coin.textures.append(arcade.load_texture("images/items/Coingold.png", scale=COIN_SCALE))
coin.textures.append(arcade.load_texture("images/items/Coingold.png", scale=COIN_SCALE))
coin.textures.append(arcade.load_texture("images/items/Coingold.png", scale=COIN_SCALE))
coin.textures.append(arcade.load_texture("images/items/Coingold.png", scale=COIN_SCALE))
coin.cur_texture_index = random.randrange(len(coin.textures))
self.coin_list.append(coin)
# Set the background color
arcade.set_background_color(arcade.color.AMAZON)
def on_draw(self):
# Render the screen.
# This command has to happen before we start drawing
arcade.start_render()
# Draw all the sprites.
self.coin_list.draw()
self.player_list.draw()
# Put the text on the screen.
output = f"Score: {self.score}"
arcade.draw_text(output, 10, 20, arcade.color.WHITE, 14)
def on_key_press(self, key, modifiers):
# Called whenever a key is pressed.
if key == arcade.key.UP:
self.player.change_y = MOVEMENT_SPEED
elif key == arcade.key.DOWN:
self.player.change_y = -MOVEMENT_SPEED
elif key == arcade.key.LEFT:
self.player.change_x = -MOVEMENT_SPEED
elif key == arcade.key.RIGHT:
self.player.change_x = MOVEMENT_SPEED
elif key == arcade.key.Q:
self.is_attacking = True
def on_key_release(self, key, modifiers):
# Called when the user releases a key.
if key == arcade.key.UP or key == arcade.key.DOWN:
self.player.change_y = 0
elif key == arcade.key.LEFT or key == arcade.key.RIGHT:
self.player.change_x = 0
elif key == arcade.key.Q:
self.is_attacking = False
def on_update(self, delta_time):
# Movement and game logic
self.coin_list.update()
self.coin_list.update_animation()
self.player_list.update()
self.player_list.update_animation()
# Generate a list of all sprites that collided with the player.
hit_list = arcade.check_for_collision_with_list(self.player, self.coin_list)
# Loop through each colliding sprite, remove it, and add to the score.
for coin in hit_list:
coin.remove_from_sprite_lists()
self.score += 1
def main():
# Main method
window = MyGame(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
window.setup()
arcade.run()
if __name__ == "__main__":
main()
You can try setting
self.player.is_attacking=True

Memory game not matching tiles

I am making a memory game and I can't get the program to check if the tiles match and if they do match get the tiles to stay exposed, and keep the tiles exposed for one second if the tiles don't match. lines 116-122 I think that part of the program is supposed to check but it never returns true for is_matching(). I feel like I am supposed to check if the actual tiles themselves are equal to each other but to me, that seems counterproductive because the tile is just a location on the grid and the image is what's drawn on the tile??
thanks in advance
# Code Example 2
#
import pygame
import random
import time
# User-defined functions
def main():
# initialize all pygame modules (some need initialization)
pygame.init()
# create a pygame display window
pygame.display.set_mode((500, 400))
# set the title of the display window
pygame.display.set_caption('Tic Tac Toe')
# get the display surface
w_surface = pygame.display.get_surface()
# create a game object
game = Game(w_surface)
# start the main game loop by calling the play method on the game object
game.play()
# quit pygame and clean up the pygame window
pygame.quit()
# User-defined classes
class Game:
# An object in this class represents a complete game.
def __init__(self, surface):
# Initialize a Game.
# - self is the Game to initialize
# - surface is the display window surface object
# === objects that are part of every game that we will discuss
self.surface = surface
self.bg_color = pygame.Color('black')
self.FPS = 60
self.game_Clock = pygame.time.Clock()
self.close_clicked = False
self.continue_game = True
# === game specific objects
self.board = []
self.score = [0]
self.board_size = 4
self.create_board()
self.click = 0
self.exposed = 0
self.first_image= None
self.second_image= None
self.tile1 = None
self.tile2 = None
self.match = None
def create_board(self):
Tile.set_surface(self.surface)
# width = self.surface.get_width()//self.board_size
# height = self.surface.get_height() // self.board_size
# image is of type surface
self.images = []
new_image =['image1.bmp','image2.bmp','image3.bmp','image4.bmp', 'image5.bmp','image6.bmp','image7.bmp',
'image8.bmp','image1.bmp','image2.bmp','image3.bmp','image4.bmp', 'image5.bmp','image6.bmp',
'image7.bmp','image8.bmp']
for file_name in new_image:
image = pygame.image.load(file_name)
self.images.append(image)
# random.shuffle(self.images)
cover = pygame.image.load('image0.bmp')
#image1 = pygame.image.load(images_list)
width = image.get_width()
height = image.get_height()
for row_index in range(0, self.board_size):
row = []
for col_index in range(0, self.board_size):
x = width * col_index
y = height * row_index
imageindex = row_index * self.board_size + col_index
image = self.images[imageindex]
tile = Tile(x,y,image,cover)
row.append(tile)
self.board.append(row)
def play(self):
# Play the game until the player presses the close box.
# - self is the Game that should be continued or not.
while not self.close_clicked: # until player clicks close box
# play frame
self.handle_events()
self.draw()
if self.continue_game:
self.update()
self.decide_continue()
self.game_Clock.tick(self.FPS) # run at most with FPS Frames Per Second
def handle_events(self):
# Handle each user event by changing the game state appropriately.
# - self is the Game whose events will be handled
events = pygame.event.get()
for event in events:
if event.type == pygame.QUIT:
self.close_clicked = True
if event.type == pygame.MOUSEBUTTONUP and self.continue_game == True:
self.handle_mouse_up(event)
def update(self):
# Update the game objects for the next frame.
# - self is the Game to update
self.score[0] = pygame.time.get_ticks() // 1000
if self.first_image != None and self.match:
print(self.check_matching())
self.first_image = None
self.tile1 = None
self.second_image = None
self.tile2 = None
self.exposed += 1
print(self.exposed)
if self.first_image != None:
if self.second_image != None and not self.match:
# time.sleep(1)
print(self.check_matching())
self.tile1.hide_tile()
self.tile2.hide_tile()
self.second_image = None
self.tile2 = None
self.first_image = None
self.tile1 = None
def handle_mouse_up(self,event):
for row in self.board:
for tile in row:
valid_click = tile.select(event.pos)
if valid_click == True:
# self.number_exposed += 1
# time.sleep(1)
tile.expose_tile()
print(self.click)
if self.click == 0:
self.first_image = tile.image
self.tile1 = tile
elif self.click == 1:
self.second_image = tile.image
self.tile2 = tile
self.click += 1
print(self.first_image)
print(self.second_image)
if self.click > 1:
self.click = 0
def draw(self):
# Draw all game objects.
# - self is thae Game to draw
# draw tiles
self.surface.fill(self.bg_color) # clear the display surface first
for each_row in self.board:
for each_tile in each_row:
each_tile.draw()
self.draw_score()
pygame.display.update() # make the updated surface appear on the display
def draw_score(self):
# 1. Set the color
size = self.surface.get_width()
fg_color = pygame.Color('white')
# 2.create the font object
font = pygame.font.SysFont('', 70)
# 3 Create a text box by rendering the font
text_string = '' + str(self.score[0])
text_box = font.render(text_string, True, fg_color, self.bg_color)
surface_height = self.surface.get_width()
text_box_height = text_box.get_width()
location = (surface_height - text_box_height, 0)
# 4 Compute the location of the text box
#location = (430, 0)
# 5 Blit or pin the text box on the surface
self.surface.blit(text_box, location)
def decide_continue(self):
if self.exposed >= 1:
self.continue_game = False
def check_matching(self):
self.match = self.first_image == self.second_image
return self.match
# Check and remember if the game should continue
# - self is the Game to check
class Tile:
surface = None
border_size = 3
border_color = pygame.Color('black')
# An object in this class represents a Dot that moves
#classmethod
def set_surface(cls,game_surface):
cls.surface = game_surface
# instance method
def __init__(self,x , y, image, cover):
self.image = image
self.cover = cover
self.covered = True
width = self.image.get_width()
height = self.image.get_height()
self.rect = pygame.Rect(x, y, width, height)
def draw(self):
pygame.draw.rect(Tile.surface,Tile.border_color,self.rect,Tile.border_size)
Tile.surface.blit(self.image,self.rect)
if self.covered:
Tile.surface.blit(self.cover, self.rect)
else:
Tile.surface.blit(self.image, self.rect)
# Draw the dot on the surface
# - self is the Dot
def select(self, position):
valid_click = False
if self.rect.collidepoint(position):
if self.covered:
valid_click = True
self.expose_tile()
else:
valid_click = False
return valid_click
def expose_tile(self):
# if a tile is clicked this method will show the picture underneath that tile
self.covered = False
def hide_tile(self):
self.covered = True
def __eq__(self, other_tile):
if self.first_image == other_tile.image:
return True
else:
return False
main()
The tiles do not match, because what you actually do is to compare image objects:
self.match = self.first_image == self.second_image
but each image is loaded twice. For each image are generated to different objects, so they will never match.
Load each image once and use it for 2 matching tiles:
# define unique image names
new_image =['image1.bmp','image2.bmp','image3.bmp','image4.bmp',
'image5.bmp','image6.bmp','image7.bmp','image8.bmp']
# load each unique image
for file_name in new_image:
image = pygame.image.load(file_name)
self.images.append(image)
# create a list where each loaded image object is used twice
self.images = self.images + self.images
Furthermore, since the image names just differ in the number, the definition of the name list can be simplified:
new_image = ['image' + str(i) + '.bmp' for i in range(1,9)]
Extension according to the commend
what about the time delay
Completely remove self.first_image and self.second_image. Adapt Tile:
class Tile:
# [...]
def __eq__(self, other_tile):
return self.image == other_tile.image
Once tiles have been clicked then keep them stated in self.tile1 and self.tile2. When the 1st click occurs, then hide the exposed tiles, if they do not match:
if self.click == 0:
if self.tile1 != self.tile2:
if self.tile1:
self.tile1.hide_tile()
if self.tile2:
self.tile2.hide_tile()
When the 2nd click occurs then set the time (e.g. pygame.time.get_ticks() + 2000) when they have to be covered automatically:
elif self.click == 1:
self.tile2 = tile
self.hide_time = pygame.time.get_ticks() + 2000
Evaluate if the tiles have to be covered in update:
ticks = pygame.time.get_ticks()
if self.tile1 and self.tile2:
if self.tile1 != self.tile2 and self.hide_time > 0 and ticks > self.hide_time:
self.tile1.hide_tile()
self.tile2.hide_tile()
Changes to your code:
class Game:
def __init__(self, surface):
# [...]
self.hide_time = 0
# [...]
def update(self):
ticks = pygame.time.get_ticks()
self.score[0] = ticks // 1000
if self.tile1 and self.tile2:
if self.tile1 != self.tile2 and self.hide_time > 0 and ticks > self.hide_time:
self.tile1.hide_tile()
self.tile2.hide_tile()
def handle_mouse_up(self,event):
self.hide_time = 0
if self.click == 0:
if self.tile1 != self.tile2:
if self.tile1:
self.tile1.hide_tile()
if self.tile2:
self.tile2.hide_tile()
tiles = [t for row in self.board for t in row if t.select(event.pos) and not t.covered]
if any(tiles):
tile = tiles[0]
if self.click == 0:
self.tile1 = tile
elif self.click == 1:
self.tile2 = tile
self.hide_time = pygame.time.get_ticks() + 2000
tile.expose_tile()
self.click += 1
if self.click > 1:
self.click = 0
# [...]
def check_matching(self):
self.match = self.tile1.image == self.tile2.image
return self.match

AttributeError: 'OAndX' object has no attribute 'run'

As per topic, below is my code:
import pygame #provides Pygame framework.
import sys #provides sys.exit function
from pygame import *
from pygame.locals import * # Import constants used by PyGame
# game classes
class OAndX:
def __init__(self):
# Initialize Pygame
pygame.init()
# Create the clock to manage the game loop
self.clock = time.Clock()
display.set_caption("Noughts and Crosses")
# Create a windows with a resolution of 640 x 480
self.displaySize=(640,480)
self.screen=display.set_mode(self.displaySize)
# will either be 0 or X
self.player="0"
# The background class
class Background:
def __init__(self,displaySize):
self.image=Surface(displaySize)
# Draw a title
# Create the font we'll use
self.font=font.Font(None,(displaySize[0]/12))
self.text = self.font.render("Noughts and crosses",True,(Color("White")))
# Work out where to place the text
self.textRect = self.text.get_rect()
self.textRect.centerx=displaySize[0] / 2
# Add a little margin
self.textRect.top = displaySize[1] * 0.02
# Blit the text to the background image
self.image.blit(self.text, self.textRect)
def draw(self, display):
display.blit(self.image, (0, 0))
# A class for an individual grid square
class GridSquare(sprite.Sprite):
def __init__(self, position, gridSize):
# Initialise the sprite base class
super(GridSquare, self).__init__()
# We want to know which row and column we are in
self.position = position
# State can be “X”, “O” or “”
self.state = ""
self.permanentState = False
self.newState = ""
# Work out the position and size of the square
width = gridSize[0] / 3
height = gridSize[1] / 3
# Get the x and y co ordinate of the top left corner
x = (position[0] * width) - width
y = (position[1] * height) - height
# Create the image, the rect and then position the rect
self.image = Surface((width,height))
self.image.fill(Color("white"))
self.rect = self.image.get_rect()
self.rect.topleft = (x, y)
# The rect we have is white, which is the parent rect
# We will draw another rect in the middle so that we have
# a white border but a blue center
self.childImage = Surface(((self.rect.w * 0.9), (self.rect.h * 0.9)))
self.childImage.fill(Color("blue"))
self.childRect = self.childImage.get_rect()
self.childRect.center = ((width /2), (height / 2))
self.image.blit(self.childImage,self.childRect)
# Create the font we’ll use to display O and X
self.font = font.Font(None, (self.childRect.w))
class Grid:
def __init__(self, displaySize):
self.image=Surface(displaySize)
# Make a number of grid squares
gridSize = (displaySize[0] * 0.75,displaySize[1] * 0.75)
# Work out the co-ordinate of the top left corner of the grid so that it can be centered on the screen
self.position = ((displaySize[0] /2) - (gridSize[0] / 2),(displaySize[1] / 2) - (gridSize[1] / 2))
# An empty array to hold our grid squares in
self.squares = []
for row in range(1,4):
# Loop to make 3 rows
for column in range(1,4):
# Loop to make 3 columns
squarePosition = (column,row)
self.squares.append(GridSquare(squarePosition, gridSize))
# Get the squares in a sprite group
self.sprites = sprite.Group()
for square in self.squares:
self.sprites.add(square)
def draw(self, display):
self.sprites.update()
self.sprites.draw(self.image)
display.blit(self.image, self.position)
def update(self):
# Need to update if we need to set a new state
if (self.state != self.newState):
# Refill the childImage blue
self.childImage.fill(Color("blue"))
text = self.font.render(self.newState, True, (Color("white")))
textRect = text.get_rect()
textRect.center = ((self.childRect.w / 2),(self.childRect.h / 2))
# We need to blit twice because the new child image needs to be blitted to the parent image
self.childImage.blit(text,textRect)
self.image.blit(self.childImage, self.childRect)
# Reset the newState variable
self.state = self.newState
self.newState = ""
def setState(self, newState,permanent=False):
if not self.permanentState:
self.newState = newState
if permanent:
self.permanentState = True
def reset(self):
# Create an instance of our background and grid class
self.background =Background(self.displaySize)
self.grid = Grid(self.displaySize)
def getSquareState(self, column, row):
# Get the square with the requested position
for square in self.squares:
if square.position == (column,row):
return square.state
def full(self):
# Finds out if the grid is full
count = 0
for square in self.squares:
if square.permanentState ==True:
count += 1
if count == 9:
return True
else:
return False
def getWinner(self):
players = ["X", "O"]
for player in players:
# check horizontal spaces
for column in range (1, 4):
for row in range (1, 4):
square1 = self.grid.getSquareState(column, row)
square2 = self.grid.getSquareState((column + 1),row)
square3 = self.grid.getSquareState((column + 2), row)
# Get the player of the square (either O or X)
if (square1 == player) and (square2 == player) and (square3 == player):
return player
# check vertical spaces
for column in range (1, 4):
for row in range (1, 4):
square1 = self.grid.getSquareState(column, row)
square2 = self.grid.getSquareState(column, (row + 1))
square3 = self.grid.getSquareState(column, (row + 2))
# Get the player of the square (either O or X)
if (square1 == player) and (square2 == player) and (square3 == player):
return player
# check forwards diagonal spaces
for column in range (1, 4):
for row in range (1, 4):
square1 = self.grid.getSquareState(column, row)
square2 = self.grid.getSquareState((column + 1), (row - 1))
square3 = self.grid.getSquareState((column + 2), (row - 2))
# Get the player of the square (either O or X)
if (square1 == player) and (square2 == player) and (square3 == player):
return player
# check backwards diagonal spaces
for column in range (1, 4):
for row in range (1, 4):
square1 = self.grid.getSquareState(column, row)
square2 = self.grid.getSquareState((column + 1), (row + 1))
square3 = self.grid.getSquareState((column + 2), (row + 2))
# Get the player of the square (either O or X)
if (square1 == player) and (square2 == player) and (square3 == player):
return player
# Check if grid is full if someone hasn’t won already
if self.grid.full():
return "draw"
def winMessage(self, winner):
# Display message then reset the game to its initial state
# Blank out the screen
self.screen.fill(Color("Black"))
# Create the font we’ll use
textFont = font.Font(None, (self.displaySize[0] / 6))
textString = ""
if winner == "draw":
textString = "It was a draw!"
else:
textString = winner + " Wins!"
# Create the text surface
text = textFont.render(textString,True,(Color("white")))
textRect = text.get_rect()
textRect.centerx = self.displaySize[0] / 2
textRect.centery = self.displaySize[1] / 2
# Blit changes and update the display before we sleep
self.screen.blit(text, textRect)
display.update()
# time.wait comes from pygame libs
time.wait(2000)
# Set game to its initial state
self.reset()
def run(self):
while True:
# Our Game loop,Handle events
self.handleEvents()
# Draw our background and grid
self.background.draw(self. screen)
self.grid.draw(self.screen)
# Update our display
display.update()
# Limit the game to 10 fps
self.clock.tick(10)
def handleEvents(self):
# We need to know if the mouse has been clicked later on
mouseClicked = False
# Handle events, starting with quit
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
if event.type == MOUSEBUTTONUP:
mouseClicked = True
# Get the co ordinate of the mouse mousex, mousey = mouse.get_pos() ,These are relative to the top left of the screen,and we need to make them relative to the top left of the grid
mousex -= self.grid.position[0]
mousey -= self.grid.position[1]
# Find which rect the mouse is in
for square in self.grid.squares:
if square.rect.collidepoint((mousex, mousey)):
if mouseClicked:
square.setState(self.player, True)
# Change to next player
if self.player == "O":
self.player = "X"
else:
self.player = "O"
# Check for a winner
winner = self.getWinner()
if winner:
self.winMessage(winner)
else:
square.setState(self.player)
else:
# Set it to blank, only if
permanentState == False
square.setState("")
if __name__ == "__main__":
game = OAndX()
game.run()
I'm new to python language but I need to understand why caused to error "AttributeError: 'OAndX' object has no attribute 'run'" when I using python3 interpreter to run it.Appreciate thankful to anyone could help.
You should actually place your run method under you class OAndX method as follows:
class OAndX:
def __init__(self):
# Initialize Pygame
pygame.init()
# Create the clock to manage the game loop
self.clock = time.Clock()
display.set_caption("Noughts and Crosses")
# Create a windows with a resolution of 640 x 480
self.displaySize=(640,480)
self.screen=display.set_mode(self.displaySize)
# will either be 0 or X
self.player="0"
def run(self):
while True:
# Our Game loop,Handle events
self.handleEvents()
# Draw our background and grid
self.background.draw(self. screen)
self.grid.draw(self.screen)
# Update our display
display.update()
# Limit the game to 10 fps
self.clock.tick(10)

Categories

Resources