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 !
Related
I am trying to draw a checkerboard. However when I call the 'drawBoard' function it returns a black screen. I think the problem may be this line here:
colour = con.BOARD_COLOURS[((r+c) % 2)]
I was attempting to index the different colour values using either 0 or 1 in order to alternate the colour of the squares as they are drawn. Could any one explain where I am going wrong. Below is the full code:
#Defining constants
# RGB colours
BOARD_COLOURS = [(245, 222, 179),(34, 139, 34)]
# Dimensions
ROWS = 8
COLS = 8
DIMS = 600
SQUARE = 600 // 8
class Board:
def __init__(self):
pass
def drawBoard(self, screen):
for r in range(con.ROWS):
for c in range(con.ROWS):
colour = con.BOARD_COLOURS[((r+c) % 2)]
pg.draw.rect(screen, colour, pg.Rect(c*con.SQUARE, r*con.SQUARE, con.SQUARE, con.SQUARE))
def main():
t = 60
clock = pg.time.Clock()
interface = pg.display.set_mode((con.DIMS, con.DIMS))
pg.init()
run = True
while run:
clock.tick(t)
for ev in pg.event.get():
if ev.type == pg.QUIT:
run = False
b = Board()
b.drawBoard(interface)
pg.quit()
main()
You need to update the display. Update the display by calling either pygame.display.update() or pygame.display.flip() after drawing the scene:
def main():
t = 60
clock = pg.time.Clock()
interface = pg.display.set_mode((con.DIMS, con.DIMS))
pg.init()
b = Board()
run = True
while run:
clock.tick(t)
for ev in pg.event.get():
if ev.type == pg.QUIT:
run = False
interface.fill(0)
b.drawBoard(interface)
pygame.display.flip()
pg.quit()
main()
The typical PyGame application loop has to:
limit the frames per second to limit CPU usage with pygame.time.Clock.tick
handle the events by calling either pygame.event.pump() or pygame.event.get().
update the game states and positions of objects dependent on the input events and time (respectively frames)
clear the entire display or draw the background
draw the entire scene (blit all the objects)
update the display by calling either pygame.display.update() or pygame.display.flip()
To drag the objects you have to implement the MOUSEBUTTONDOWN, MOUSEBUTTONUP and MOUSEMOTION event:
def main():
t = 60
clock = pg.time.Clock()
interface = pg.display.set_mode((DIMS, DIMS))
pg.init()
pg.display.set_caption("Checkers Game")
dragitem = None
run = True
b = Board()
while run:
clock.tick(t)
for ev in pg.event.get():
if ev.type == pg.QUIT:
run = False
if ev.type == pg.MOUSEBUTTONDOWN:
col = ev.pos[0] * COLS // DIMS
row = ev.pos[1] * ROWS // DIMS
if b.board[row][col] > 1:
dragitem = (row, col, b.board[row][col])
b.board[row][col] = (row + col) % 2 == 0
dragpos = ev.pos
if ev.type == pg.MOUSEBUTTONUP:
new_col = ev.pos[0] * COLS // DIMS
new_row = ev.pos[1] * ROWS // DIMS
if b.board[new_row][new_col] < 2:
b.board[new_row][new_col] = dragitem[2]
else:
b.board[dragitem[0]][dragitem[1]] = dragitem[2]
dragpos = None
dragitem = None
if ev.type == pg.MOUSEMOTION and dragitem:
dragpos = ev.pos
interface.fill(0)
b.drawBoard(interface)
b.drawPiece(interface)
if dragitem:
pg.draw.circle(interface, PIECE_COLOUR[dragitem[2]-2], dragpos, RADIUS)
pg.display.flip()
pg.quit()
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()
I am trying to learn pyGame. And I ran into a problem. I have a rectangle which can be moved with arrow buttons. Then I created another thread which generates smaller rectangles which can be picked up. But when I run my game, the small generated rectangles blink too much. How can I make them be stable? I think I dont understand time concept here well. Could someone explain me etc
My code:
import pygame
import random
import threading
import thread
import sys
import time
pygame.init()
screen = pygame.display.set_mode((400, 300))
done = False
is_blue = True
entityList = []
x = 30
y = 30
clock = pygame.time.Clock()
class Entity():
def __init__(self, x, y):
self.x = x
self.y = y
def getX(self):
return self.x
def getY(self):
return self.y
def drawStuff(entityList):
# pygame.draw.rect(screen, (255, 100, 0), pygame.Rect(55, 45, 10, 10))
for x in range (0, entityList.__len__()):
pygame.draw.rect(screen, (255, 100, 0), pygame.Rect(entityList[x].getX(), entityList[x].getY(), 10, 10))
pygame.display.flip()
clock.tick(60)
class EntityManager(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self):
while True:
entityList = generateEntities()
drawStuff(entityList)
def endJob(self):
thread.exit()
time.sleep(2)
def detect_collision(x,y):
if x > 340:
x -= 1
if y > 240:
y -= 1
if y < 0:
y += 1
if x < 0:
x += 1
return x,y
def generateEntities():
itemlist = []
for x in range (0,4):
x = random.randint(1,339)
y = random.randint(1,239)
entity = Entity(x,y)
itemlist.append(entity)
return itemlist
entityList = generateEntities()
a = EntityManager()
a.setDaemon(True)
a.start()
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE:
is_blue = not is_blue
pressed = pygame.key.get_pressed()
if pressed[pygame.K_UP]:
y -= 1
x,y = detect_collision(x, y)
if pressed[pygame.K_DOWN]:
y += 1
x,y = detect_collision(x, y)
if pressed[pygame.K_LEFT]:
x -= 1
x,y = detect_collision(x, y)
if pressed[pygame.K_RIGHT]:
x += 1
x,y = detect_collision(x, y)
screen.fill((0, 0, 0))
if is_blue: color = (0, 128, 255)
else: color = (255, 100, 0)
pygame.draw.rect(screen, color, pygame.Rect(x, y, 60, 60))
pygame.display.flip()
clock.tick(60)
clock.tick() actually means FPS (frames per second); instead of changing it, change another variable.
Watch this tutorial, it might help you understand FPS.
Issues about smoothness are sometimes about the size of your object and how many pixels you are moving it when key events are True. Be careful about that; imagine you have a little box and you are trying to move it. If you move it with big steps, it seems 'laggy', but if you move it with little steps then it may look smoother. Hope you understand what I mean.
For your second question please check this documentation.
Okay, I figured it out now. OMG, I feel such an idiot after this. But I forgive myself that I am also doing parallell learning of Python. I used to code in Java more.
Anyway: problem was that my drawStuff function drew things in function but when it left this function, it did nothing. Changes were made locally. I solved this problem by using global keyword. I made in my code changes that thread computes entity coordinates and then main loop does update. This leads me to another idea that maybe there should be some game info object which gets updated all the time and all rectangles info go to that game info object.
Anyway, soon I will edit this post code too to see what i did
So I'm really new to programming and I'm working on a game that is sort of like Cookie Clicker, but with a twist (Mining). It is made in python/pygame. Anyways I have an image of a boulder and I want to add a rock to my inventory in the game every time i click on it.
Someone helped me out setting up the point_collide in my class. I will confess and say I don't fully understand how it works but its supposed to detect if my mouse is on a non-transparent part of my rock image.
I want the game to only give you a rock if your clicking on the NON-transparent parts of the boulder image I have blitted to the middle of the screen.
Short Question: How can I setup the game to register clicks only on my masked image?
PS: I know it'd be better to learn the fundamentals of programming first, but I have learned so much just by diving straight into a project (it keeps me going and alot more fun then reading a book).
Link to code: https://www.refheap.com/88634
The Code:
import pygame, sys
from pygame.locals import *
from datetime import datetime
if (__name__ == "__main__"):
pygame.init()
pygame.font.init()
pygame.display.set_caption("Miner Click")
clock = pygame.time.Clock()
screen = pygame.display.set_mode((960,600))
width = 960
height = 600
GREEN = (0,255,0)
RED = (255,0,0)
BLUE = (0,0,255)
BLACK = (0,0,0)
WHITE = (255,255,255)
BROWN = (84,27,1)
GREY = (198,198,198)
greenbg = pygame.image.load("greenbg.jpg").convert()
rockbutton = pygame.image.load("rockbutton.png").convert_alpha()
woodbutton = pygame.image.load("woodbutton.png").convert_alpha()
pygame.mouse.set_visible(True)
pick = pygame.image.load("pick.png").convert_alpha()
axe = pygame.image.load("axesmall.png").convert_alpha()
rockwidth = 544
rockheight = 274
clicks = 0
wood = 0
stonefont = pygame.font.SysFont("verdana", 29, True)
woodfont = pygame.font.SysFont("verdana", 29, True)
clicktext = stonefont.render('Rock: ' +str(clicks), 2, (GREY))
woodtext = woodfont.render('Wood: ' +str(wood), 2, (BROWN))
boxsize = clicktext.get_rect()
RocksX = 125
WoodX = 113
class Rock(pygame.sprite.Sprite):
def __init__(self, color = BLUE, width = 544, height = 274):
super(Rock, self ).__init__()
self.image = pygame.Surface((width, height))
self.set_properties()
self.image.fill(color)
def set_properties(self):
self.rect = self.image.get_rect()
self.origin_x = self.rect.centerx
self.origin_y = self.rect.centery
def set_position(self, x, y):
self.rect.x = 250
self.rect.y = 230
def set_image(self, filename = None):
if (filename != None):
self.image = pygame.image.load(filename).convert_alpha()
def point_collide(self, point):
x, y = point
x -= self.rect.x
y -= self.rect.y
try:
return self.mask.get_at((x,y))
except IndexError:
return False
#below is my clueless attempt at getting it to work
def checkForCursorPressed(x,y):
if pygame.mouse.get_pressed() and pygame.mouse.get_pos() == (x,y):
clicks+=1
coordfont = pygame.font.SysFont("verdana", 12, True)
rock_group = pygame.sprite.Group()
rock = Rock()
rock.set_image("rock.png")
rock.set_position(width/2, height/2)
rock_group.add(rock)
while True:
clock.tick(60)
screen.fill((255,255,255))
screen.blit(greenbg, (0,0))
x,y = pygame.mouse.get_pos()
coords = x,y
now = datetime.now()
date = '%s/%s/%s' % (now.month, now.day, now.year)
label = coordfont.render("Coordinates: "+str(coords), 1, (GREY))
date = coordfont.render("Date: "+str(date), 1, (GREY))
screen.blit(date, (650,10))
screen.blit(label, (790, 10))
screen.blit(rockbutton, (25,25))
screen.blit(woodbutton, (25,100))
clicktext = stonefont.render(' ' +str(clicks), 2, (GREY))
woodtext = woodfont.render(' ' +str(wood), 2, (BROWN))
screen.blit(clicktext, [RocksX,38])
screen.blit(woodtext, [139,WoodX])
rock_group.draw(screen)
screen.blit(pick, (x-10,y-50))
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == KEYDOWN and event.key == K_ESCAPE:
sys.exit()
pygame.display.update()
#in case i need the below again
#if x>249 and x<(795) and y>210 and y<(484):
Clicking will create a MOUSEBUTTONDOWN event, so you should be able to deal with clicks within your event-processing loop, e.g.:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == KEYDOWN and event.key == K_ESCAPE:
sys.exit()
elif event.type == MOUSEBUTTONDOWN:
click_position = event.pos
if rock.point_collide(click_position):
print('Clicked within the rock')
clicks += 1
# Any other events that have to happen
# when the rock is clicked
I also don't understand how point_collide() works and I get an AttributeError: 'Rock' does not have attribute 'mask'.
So I would use an other possibility to detect if one clicked on non-transparent parts of the image, using colorkey.
The colorkey defines the transparent color when blitting. In my case it's white:
def set_image(self, filename = None):
...
#sets colorkey to white, depends on the image
self.image.set_colorkey((255,255,255))
New version of point_collide():
def point_collide(self, point):
x, y = point
x -= self.rect.x
y -= self.rect.y
#detects if click hits the image
if 0 <= x < self.image.get_width():
if 0 <= y < self.image.get_height():
#detects if color at clicking position != colorkey-color(transparent)
if self.image.get_at((x,y))[0:3] != self.image.get_colorkey()[0:3]:
return True
return False
How to get mouse events has been answered yet.
Ok I got it working :)
If anyone else is trying to do something similar I'll explain here.
Put the below in your code (this solves the problem 'Rock has no attribute mask' error I got)
self.mask = pygame.mask.from_surface(self.image)
Here is where I put it in my code (in my set_image def)
def set_image(self, filename = None):
if (filename != None):
self.image = pygame.image.load(filename).convert_alpha()
self.mask = pygame.mask.from_surface(self.image)
Now combine this code with Marius and it works perfectly!
I'm trying to make a function when the mouse button is up, it changes the picture of the ghosts to one single image.
Problem being, I have no idea what to call upon (Hence the ??? in the script). It's hard because the ghosts are created via a loop. Can anyone possibly help?
Maybe i need to change the ghosts into sprites? could you help with that too?
import pygame
import random
import sys
class Ball:
def __init__(self,X,Y,imagefile):
self.velocity = [3,3]
self.ball_image = pygame.image.load (imagefile). convert() ### i want this image to change
self.ball_boundary = self.ball_image.get_rect (center=(X,Y))
self.sound = pygame.mixer.Sound ('Thump.wav')
if __name__ =='__main__':
width = 800
height = 600
background_colour = 0,0,0
GHOST_IMAGE = ["images/blue-right.png", "images/red-right.png", "images/orange-right.png", "images/pink-right.png"]
GHOST_IMAGETWO = ["images/blue-left.png", "images/red-left.png", "images/orange-left.png", "images/pink-left.png"]
pygame.init()
frame = pygame.display.set_mode((width, height))
pygame.display.set_caption("Bouncing Ball animation")
num_balls = 4
ball_list = []
for i in range(num_balls):
ball_list.append( Ball(random.randint(0, width),random.randint(0, height), (GHOST_IMAGE[i]) ))
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit(0)
elif event.type == pygame.MOUSEBUTTONUP:
??? = pygame.image.load("images/vulnerable.png").convert() ###i know that this is where and what i need to change it to, but dont know what instance name to call upon.
frame.fill(background_colour)
for ball in ball_list:
if ball.ball_boundary.left < 0 or ball.ball_boundary.right > width:
ball.sound.play()
ball.velocity[0] = -1 * ball.velocity[0]
if ball.ball_boundary.top < 0 or ball.ball_boundary.bottom > height:
ball.sound.play()
ball.velocity[1] = -1 * ball.velocity[1]
ball.ball_boundary = ball.ball_boundary.move (ball.velocity)
frame.blit (ball.ball_image, ball.ball_boundary)
pygame.display.flip()
One way is to just iterate over the ball_list and change each ball:
elif event.type == pygame.MOUSEBUTTONUP:
image = pygame.image.load("images/vulnerable.png").convert()
for ball in ball_list:
ball.ball_image = image
Another way would be implement the image-changing behaviour directly in the Ball class:
class Ball:
def __init__(self,X,Y,imagefile):
self.vulnerable = False
self.velocity = [3,3]
self.normal_ball_image = pygame.image.load (imagefile). convert()
self.v_ball_image = pygame.image.load("images/vulnerable.png").convert()
self.ball_image = self.normal_ball_image
self.ball_boundary = self.ball_image.get_rect (center=(X,Y))
self.sound = pygame.mixer.Sound ('Thump.wav')
def toggle(self):
self.vulnerable = not self.vulnerable
self.ball_image = self.v_ball_image if self.vulnerable else self.normal_ball_image
And in your loop:
elif event.type == pygame.MOUSEBUTTONUP:
for ball in ball_list:
ball.toggle()