Circle not being drawn at the correct position pygame - python

problem in my tic tac toe project once again, the circle isnt appearing where it should when I click a square, i tried multiple different coordinates but still cant figure it out, so if any of you know what to do then let me know, thanks.
import pygame, sys
import numpy as np
pygame.init()
screen_color = (28, 170, 156)
line_color = (23, 140, 135)
line_width = 9
screen = pygame.display.set_mode((550, 450))
pygame.display.set_caption("Tic Tac Toe")
screen.fill(screen_color)
board = np.zeros((3, 3))
def draw_lines():
#1st horizontal
pygame.draw.line(screen, line_color, (0, 135), (550, 135), line_width)
#2nd horizontal
pygame.draw.line(screen, line_color, (0, 300), (550, 300), line_width)
#1st vertical
pygame.draw.line(screen, line_color, (175, 0), (175, 450), line_width)
#2nd vertical
pygame.draw.line(screen, line_color, (370, 0), (370, 450), line_width)
def draw_figures():
for row in range(3):
for col in range(3):
if board[row][col] == 1:
pygame.draw.circle(screen, 'cyan', (int(col * 550/3), int(row * 450/3)), 60, 10)
def mark_square(row, col, player):
board[row][col] = player
def available_square(row, col):
if board[row][col] == 0:
return True
else:
return False
def is_board_full():
for row in range(3):
for col in range(3):
if board[row][col] == 0:
return False
print(is_board_full())
print(board)
draw_lines()
player = 1
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
if event.type == pygame.MOUSEBUTTONDOWN:
mouseX = event.pos[0] #X coordinate
mouseY = event.pos[1] #Y coordinate
clicked_row = int(mouseY * 3 // 450)
clicked_col = int(mouseX * 3 // 550)
#print(clicked_col)
#print(clicked_row)
if available_square(clicked_row, clicked_col):
if player == 1:
mark_square(clicked_row, clicked_col, 1)
player = 2
elif player == 2:
mark_square(clicked_row, clicked_col, 2)
player = 1
draw_figures()
print(board)
pygame.display.update()
(if there any other errors let me know too)
also let me know if getting the coordinates is trial and error or there is a specific way to do it, thanks!

The 3rd argument of pygame.draw.circle is the center of the circle. Draw the circle at (col + 0.5) and (row + 0.5):
pygame.draw.circle(screen, 'cyan', (int(col * 550/3), int(row * 450/3)), 60, 10)
pygame.draw.circle(screen, 'cyan', (int((col + 0.5) * 550/3), int((row+0.5) * 450/3)), 60, 10)

The problem is that the circle is centered on the grid and on on the middle of the squares.
You need to add an offset to the center like this:
pygame.draw.circle(screen, 'cyan', (int(col * 550/3 + x_offset), int(row * 450/3 + y_offset)), 60, 10)
I would also recommend you to create a variable called grid_width so that you don't need to have numbers like 550 or 450 everywhere in your programm.

Related

Can I use an image on a moving object within Pygame as opposed to to a color?

def star1():
global angle
x = int(math.cos(angle) * 100) + 800
y = int(math.sin(angle) * 65) + 400
pygame.draw.circle(screen, white, (x, y), 17)
angle += 0.02
The parameters pygame gives for the .draw.circle here only allow for an RGB color to be used to color the figure. I am wondering if there is a way I can pass an image as a color, or use a different method that allows me to place an image on the object/circle. The circles move around so the image also needs to stay with the circle as it moves.
Create a transparent pygame.Surface with the desired size
Draw a white circle in the middle of the Surface
Blend the image (my_image) on this Surface using the blend mode BLEND_RGBA_MIN:
size = 100
circular_image = pygame.Surface((size, size), pygame.SRCALPHA)
pygame.draw.circle(circular_image, (255, 255, 255), (size//2, size//2), size//2)
image_rect = my_image.get_rect(center = circular_image.get_rect().center)
circular_image.blit(my_image, image_rect, special_flags=pygame.BLEND_RGBA_MIN)
Minimal example:
import pygame
pygame.init()
window = pygame.display.set_mode((300, 300))
clock = pygame.time.Clock()
def create_circular_image(size, image):
clip_image = pygame.Surface((size, size), pygame.SRCALPHA)
pygame.draw.circle(clip_image, (255, 255, 255), (size//2, size//2), size//2)
image_rect = my_image.get_rect(center = clip_image.get_rect().center)
clip_image.blit(my_image, image_rect, special_flags=pygame.BLEND_RGBA_MIN)
return clip_image
def create_test_image():
image = pygame.Surface((100, 100))
ts, w, h, c1, c2 = 25, 100, 100, (255, 64, 64), (32, 64, 255)
[pygame.draw.rect(image, c1 if (x+y) % 2 == 0 else c2, (x*ts, y*ts, ts, ts))
for x in range((w+ts-1)//ts) for y in range((h+ts-1)//ts)]
return image
my_image = create_test_image()
circular_image = create_circular_image(100, my_image)
rect = circular_image.get_rect(center = window.get_rect().center)
run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
keys = pygame.key.get_pressed()
rect.x += (keys[pygame.K_RIGHT] - keys[pygame.K_LEFT]) * 5
rect.y += (keys[pygame.K_DOWN] - keys[pygame.K_UP]) * 5
window.fill((64, 64, 64))
window.blit(circular_image, rect)
pygame.display.flip()
pygame.quit()
exit()
See also:
how to make circular surface in PyGame
How to fill only certain circular parts of the window in PyGame?
Clipping
Write a function that draws an image on a given x, y and pass these x, y values used in the circle function.
Basicly it's just win.blit(image, (x,y)). Don't forget to subtruct radius value. So the image matchs with circle.

Draw over a pygame rect with a smaller width

I'm making a Sudoku Solver via pygame and I've been able to draw the whole board, however, while programming the section of code that deals with clicking on a tile, I made it so that the current tile would "light up" green so the user can know the current tile. However, I'm stuck figuring out how to draw over the green highlighted section when a user decides to click on a different tile. I'd like to remove that part entirely but, since it's a rect with a thickness of 4 and the base tiles have a thickness of 1, all I accomplish is having an ugly black line over the thick green line. I tried redrawing the whole board but I obtain similar results. Any help?
class Tile:
'''Represents each white tile/box on the grid'''
def __init__(self, value, window, x1, x2):
self.value = value #value of the num on this grid
self.window = window
self.active = False
self.rect = pygame.Rect(x1, x2, 60, 60) #dimensions for the rectangle
def draw(self, color, thickness):
'''Draws a tile on the board'''
pygame.draw.rect(self.window, color, self.rect, thickness)
pygame.display.flip()
def main():
board = Board(screen)
tiles = board.draw_board() #store the locations of all the tiles on the grid
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.MOUSEBUTTONUP:
mousePos = pygame.mouse.get_pos()
#board.draw_board() or (see next comment)
for i in range(9):
for j in range (9): #look for tile we clicked on
#tiles[i][j].draw((0,0,0),1)
#yield same results
if tiles[i][j].is_clicked(mousePos):
tiles[i][j].draw((50,205,50),4) #redraws that tile but with a highlighted color to show it's been clicked
break
main()
You could simply store the highlighted tile in a variable. I also don't think you need a Tile class at all, since a sudoku game state is basically just a list of list of numbers.
Here's a simple example. Note the comments:
import random
import pygame
TILE_SIZE = 64
# function to draw the grid
def draw_board():
board_surface = pygame.Surface((9*TILE_SIZE, 9*TILE_SIZE))
board_surface.fill((255, 255, 255))
for x in range(9):
for y in range(9):
rect = pygame.Rect(x*TILE_SIZE, y*TILE_SIZE, TILE_SIZE, TILE_SIZE)
pygame.draw.rect(board_surface, (0, 0, 0), rect, 1)
pygame.draw.line(board_surface, (0, 0, 0), (0, 3*TILE_SIZE), (9*TILE_SIZE, 3*TILE_SIZE), 5)
pygame.draw.line(board_surface, (0, 0, 0), (0, 6*TILE_SIZE), (9*TILE_SIZE, 6*TILE_SIZE), 5)
pygame.draw.line(board_surface, (0, 0, 0), (3*TILE_SIZE, 0), (3*TILE_SIZE, 9*TILE_SIZE), 5)
pygame.draw.line(board_surface, (0, 0, 0), (6*TILE_SIZE, 0), (6*TILE_SIZE, 9*TILE_SIZE), 5)
return board_surface
def main():
# standard pygame setup
pygame.init()
clock = pygame.time.Clock()
screen = pygame.display.set_mode((9*TILE_SIZE, 9*TILE_SIZE))
font = pygame.font.SysFont(None, 40)
# seperate the game state from the UI
# create a dummy 9x9 sudoku board
state = [[None for _ in range(10)] for _ in range(10)]
for _ in range(15):
x = random.randint(0, 9)
y = random.randint(0, 9)
state[y][x] = random.randint(1, 9)
# a variable to hold the selected tile's position on the board
# (from 0,0 to 8,8)
selected = None
# create the grid surface ONCE and reuse it to clear the screen
board_surface = draw_board()
while True:
for event in pygame.event.get():
pos = pygame.mouse.get_pos()
if event.type == pygame.QUIT:
return
# when the player clicks on a tile
# we translate the screen coordinates to the board coordinates
# e.g. a pos of (140, 12) is the tile at (2, 0)
if event.type == pygame.MOUSEBUTTONDOWN:
w_x, w_y = event.pos
selected = w_x // TILE_SIZE, w_y // TILE_SIZE
# clear everything by blitting the grid surface to the screen
screen.blit(board_surface, (0, 0))
# print all numbers in the state to the screen
# we use a Rect here so we can easily center the numbers
rect = pygame.Rect(0, 0, TILE_SIZE, TILE_SIZE)
for line in state:
for tile in line:
if tile != None:
tmp = font.render(str(tile), True, (0, 0, 0))
screen.blit(tmp, tmp.get_rect(center=rect.center))
rect.move_ip(TILE_SIZE, 0)
rect.x = 0
rect.move_ip(0, TILE_SIZE)
# if a tile is selected, we calculate the world coordinates from the board coordinates
# and draw a simple green rect
if selected:
rect = pygame.Rect(selected[0] * TILE_SIZE, selected[1] * TILE_SIZE, TILE_SIZE, TILE_SIZE)
pygame.draw.rect(screen, (0, 200, 0), rect, 5)
clock.tick(30)
pygame.display.flip()
main()
Also, you shouldn't call pygame.display.flip() outside your main loop. It can lead to effects like flickering or images not showing up correctly.
I see several options. I'll start with the ones that will require the least amount of code changes.
Draw your selection rect only inside your tile. Problem solved.
when you draw a tile, draw the border around it as well.
You might also want to store the selected tile in a variable, so you can some stuff with it later on.

change of impulse direction Pymunk (python)

Hi i would like to create a small drone simulator. Two engines on the left and right. I was based on examples from the pymunk library. But I have no idea how to add engine power to make it hit the point, i.e. make the object rotate. This can be done easily with the help of this library? Thank you for any help
import sys,math
import pygame
from pygame.locals import *
from pygame.color import *
import pymunk
from pymunk.vec2d import Vec2d
import pymunk.pygame_util
width, height = 690,400
fps = 60
dt = 1./fps
JUMP_HEIGHT = 16.*2
def main():
### PyGame init
pygame.init()
screen = pygame.display.set_mode((width,height))
clock = pygame.time.Clock()
running = True
### Physics stuff
space = pymunk.Space()
space.gravity = 0,-1000
draw_options = pymunk.pygame_util.DrawOptions(screen)
# box walls
static = [ pymunk.Segment(space.static_body, (10, 50), (680, 50), 3)
, pymunk.Segment(space.static_body, (680, 50), (680, 370), 3)
, pymunk.Segment(space.static_body, (680, 370), (10, 370), 3)
, pymunk.Segment(space.static_body, (10, 370), (10, 50), 3)
]
for s in static:
s.friction = 1.
s.group = 1
space.add(static)
# player
body = pymunk.Body(5, pymunk.inf)
body.position = 100,100
left = pymunk.Poly(body, [(-30,25), (0, 25), (0, 0), (-30, 0)] )
right = pymunk.Poly(body, [(0,0), (0, 25), (30, 25), (30, 0)] )
left.color = 46,25,1,46
right.color = 1,25,1,25
space.add(body, left, right)
while running:
for event in pygame.event.get():
if event.type == QUIT or \
event.type == KEYDOWN and (event.key in [K_ESCAPE, K_q]):
running = False
elif event.type == KEYDOWN and event.key == K_LEFT:
# here is the problem
jump_v = math.sqrt(2.0 * JUMP_HEIGHT * abs(space.gravity.y))
power = max(min(jump_v, 1000), 10) * 1.5
impulse = power * Vec2d(0, 3)
impulse.rotate(body.angle - 45)
body.apply_impulse_at_world_point(impulse, (0,0) )
elif event.type == KEYDOWN and event.key == K_RIGHT:
# here is the problem
jump_v = math.sqrt(2.0 * JUMP_HEIGHT * abs(space.gravity.y))
power = max(min(jump_v, 1000), 10) * 1.5
impulse = power * Vec2d(0, 3)
impulse.rotate(body.angle + 45)
body.apply_impulse_at_world_point(impulse, (0,0) )
### Clear screen
screen.fill(pygame.color.THECOLORS["black"])
### Helper lines
for y in [50,100,150,200,250,300]:
color = pygame.color.THECOLORS['darkgrey']
pygame.draw.line(screen, color, (10,y), (680,y), 1)
# ### Draw stuff
space.debug_draw(draw_options)
pygame.display.flip()
### Update physics
space.step(dt)
clock.tick(fps)
if __name__ == '__main__':
sys.exit(main())
Your main issue is that your Body has infinite moment, which means that it can't rotate. Try creating the Body as something like:
body = pymunk.Body(5, 500)
You also need to apply the impulse somewhere that makes sense. Try:
body.apply_impulse_at_local_point(impulse, (5, 0))
and:
body.apply_impulse_at_local_point(impulse, (-5, 0))
for left and right.

Draw a shape which displays until next click

I am trying to show that a tile on a chessboard is selected by outlining it whit a green colour.
I have managed to draw the outline when that specific tile area has been clicked, but the outline disappears after a few milliseconds.
So, I thought had something to do with updating the display (a function that I am already calling inside the highlight_tile() method).
here is the main loop
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
# mouse handling
for unit in white_army:
if event.type == pygame.MOUSEBUTTONDOWN:
mouse_position = pygame.mouse.get_pos()
if unit.tile_area.collidepoint(mouse_position):
highlight_tile(unit.tile_area)
else:
pygame.display.update()
and here the complete code
import pygame
import sys
from coordinator import coordinator
# set up the display
pygame.init()
window_size = (800, 800)
game_window = pygame.display.set_mode(size=window_size)
pygame.display.set_caption('My Game')
# defines classes and related methods
class WhiteSquare:
def __init__(self):
self.height = int(window_size[0] / 8)
self.width = int(window_size[1] / 8)
self.white_square = pygame.Surface((self.height, self.width))
self.white_square.fill((255, 255, 255))
class BlackSquare:
def __init__(self):
self.height = int(window_size[0] / 8)
self.width = int(window_size[1] / 8)
self.black_square = pygame.Surface((self.height, self.width))
self.black_square.fill((0, 0, 0))
class ChessBoard:
def __init__(self):
self.ws = ws
self.bs = bs
self.white_columns = white_columns
self.black_columns = black_columns
def draw(self):
for w_columns in self.white_columns:
game_window.blit(self.ws.white_square, w_columns)
for b_columns in self.black_columns:
game_window.blit(self.bs.black_square, b_columns)
# declare letters and numbers
letters = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H']
numbers = ['1', '2', '3', '4', '5', '6', '7', '8']
# create coordinates
coordinates = []
for item_letter in letters:
letter = item_letter
for item_number in numbers:
number = item_number
coordinates.append(letter + number)
# create coordinates values components
x_values = []
for number in range(0, 800, 100):
x = number
x_values.append(x)
y_values = []
for number in range(0, 800, 100):
y = number
y_values.append(y)
# create coordinate values
coordinate_values = []
for x in x_values:
for y in y_values:
coordinate_values.append((x, y))
# assign values to coordinates
squares_coordinates = dict(zip(coordinates, coordinate_values))
# Background for units
class CircleSurface:
def __init__(self):
self.circle_surface = pygame.Surface((100, 100), flags=pygame.SRCALPHA)
pygame.draw.circle(self.circle_surface, (255, 0, 0), (50, 50), 45)
# define colours
black = (0, 0, 0)
white = (255, 255, 255)
gold = (153, 153, 0)
green = (0, 255, 0)
dark_green = (0, 200, 0)
class Unit:
def __init__(self, colour, position):
# define Unit colour
self.colour = colour
# define Unit position
self.position = position
class Knight(Unit):
def __init__(self, colour, position):
# draw circle, inline, and outline
super().__init__(colour, position)
self.center_x = position[0]
self.center_y = position[1]
self.colour = colour
self.position = position
# define tile position
self.tile_area = pygame.Rect(position[0] - 50, position[1] - 50, 100, 100)
circle_radius = 40
self.circle = pygame.draw.circle(game_window, colour, self.position, circle_radius)
self.circle_outline = pygame.draw.circle(game_window, gold, self.position, circle_radius, 5)
self.circle_inline = pygame.draw.circle(game_window, gold, self.position, (circle_radius - 7), 2)
# draw letter
pygame.font.init()
my_font_size = 50
my_font = pygame.font.SysFont('Time New Roman', my_font_size)
text_surface = my_font.render('K', 1, gold)
center_text = text_surface.get_rect(center=(self.center_x, self.center_y))
game_window.blit(text_surface, center_text)
class Archer(Unit):
def __init__(self, colour, first_point, second_point, third_point):
self.colour = colour
self.first_point = first_point
self.second_point = second_point
self.third_point = third_point
self.position = [self.first_point, self.second_point, self.third_point]
super().__init__(colour, self.position)
self.center_x = self.second_point[0]
self.center_y = (self.second_point[1] + ((self.first_point[1] - self.second_point[1]) / 2)) + 10
self.inline_position = [(self.first_point[0] + 10, self.first_point[1] - 5),
(self.second_point[0], self.second_point[1] + 10),
(self.third_point[0] - 10, self.third_point[1] - 5)]
# define_tile_centre
self.tile_area = pygame.Rect(self.first_point[0] - 10, self.second_point[1] - 10, 100, 100)
if self.colour == black:
self.first_point = self.first_point[0], self.first_point[1] - 80
self.second_point = self.second_point[0], self.second_point[1] + 80
self.third_point = self.third_point[0], self.third_point[1] - 80
self.position = [self.first_point, self.second_point, self.third_point]
self.center_y = (self.second_point[1] + ((self.first_point[1] - self.second_point[1])
/ 2)) - 10
self.inline_position = [(self.first_point[0] + 10, self.first_point[1] + 5),
(self.second_point[0], self.second_point[1] - 10),
(self.third_point[0] - 10, self.third_point[1] + 5)]
self.triangle = pygame.draw.polygon(game_window, colour, self.position)
self.triangle_outline = pygame.draw.polygon(game_window, gold, self.position, 5)
self.triangle_inline = pygame.draw.polygon(game_window, gold, self.inline_position, 2)
# draw letter
pygame.font.init()
my_font_size = 50
my_font = pygame.font.SysFont('Time New Roman', my_font_size)
text_surface = my_font.render('A', 1, gold)
center_text = text_surface.get_rect(center=(self.center_x, self.center_y))
game_window.blit(text_surface, center_text)
class Pikeman(Unit):
def __init__(self, colour, position):
# draw circle, inline, and outline
super().__init__(colour, position)
self.dimension = (80, 80)
self.center_x = position[0] + self.dimension[0] / 2
self.center_y = position[1] + self.dimension[1] / 2
self.colour = colour
self.position = position
# define_tile_centre
self.tile_area = pygame.Rect(position[0] - 10, position[1] - 10, 100, 100)
self.position_and_dimension = self.position + self.dimension
self.inline_position_and_dimension = (self.position[0] + 5, self.position[1] + 5),\
(self.dimension[0] - 10, self.dimension[1] - 10)
self.square = pygame.draw.rect(game_window, colour, self.position_and_dimension, )
self.square_outline = pygame.draw.rect(game_window, gold, self.position_and_dimension, 5)
self.square_inline = pygame.draw.rect(game_window, gold, self.inline_position_and_dimension, 2)
# draw letter
pygame.font.init()
my_font_size = 50
my_font = pygame.font.SysFont('Time New Roman', my_font_size)
text_surface = my_font.render('P', 1, gold)
center_text = text_surface.get_rect(center=(self.center_x, self.center_y))
game_window.blit(text_surface, center_text)
def highlight_tile(tile_area):
pygame.draw.rect(game_window, dark_green, tile_area, 5)
pygame.display.update()
# Sets and gets the coordinates for black and white squares
coordinator = coordinator()
black_columns = coordinator[2] + coordinator[3]
white_columns = coordinator[0] + coordinator[1]
# Creates needed objects
ws = WhiteSquare()
bs = BlackSquare()
cb = ChessBoard()
cs = CircleSurface()
# Event loop (outer)
while 1:
# Draws the chessboard
cb.draw()
# set up white units
white_knight_1 = Knight(white, (150, 650))
white_knight_2 = Knight(white, (650, 650))
white_archer_1 = Archer(white, (210, 790), (250, 710), (290, 790))
white_archer_2 = Archer(white, (310, 790), (350, 710), (390, 790))
white_archer_3 = Archer(white, (410, 790), (450, 710), (490, 790))
white_archer_4 = Archer(white, (510, 790), (550, 710), (590, 790))
white_pikeman_1 = Pikeman(white, (210, 610))
white_pikeman_2 = Pikeman(white, (310, 610))
white_pikeman_3 = Pikeman(white, (410, 610))
white_pikeman_4 = Pikeman(white, (510, 610))
white_army = [white_knight_1, white_knight_2,
white_archer_1, white_archer_2, white_archer_3, white_archer_4,
white_pikeman_1, white_pikeman_2, white_pikeman_3, white_pikeman_4]
# set up black units
black_knight_1 = Knight(black, (150, 150))
black_knight_2 = Knight(black, (650, 150))
black_archer_1 = Archer(black, (210, 90), (250, 10), (290, 90))
black_archer_2 = Archer(black, (310, 90), (350, 10), (390, 90))
black_archer_3 = Archer(black, (410, 90), (450, 10), (490, 90))
black_archer_4 = Archer(black, (510, 90), (550, 10), (590, 90))
black_pikeman_1 = Pikeman(black, (210, 110))
black_pikeman_2 = Pikeman(black, (310, 110))
black_pikeman_3 = Pikeman(black, (410, 110))
black_pikeman_4 = Pikeman(black, (510, 110))
# Event loop (inner)
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
# mouse handling
for unit in white_army:
if event.type == pygame.MOUSEBUTTONDOWN:
mouse_position = pygame.mouse.get_pos()
if unit.tile_area.collidepoint(mouse_position):
highlight_tile(unit.tile_area)
else:
pygame.display.update()
pygame.display.update()
EDIT
I have managed to not make it disappear, but now I want it to disappear when the mouse is being clicked again.
here is the new block of code updated
if event.type == pygame.MOUSEBUTTONDOWN:
for unit in white_army:
mouse_position = pygame.mouse.get_pos()
if unit.tile_area.collidepoint(mouse_position):
print(mouse_position)
highlight_tile(unit.tile_area)
I tried with a while loop but it just makes the game crash, does not look like a good idea.
So, I thought had something to do with updating the display (a function that I am already calling inside the highlight_tile() method)
Of course.
Create a variable (selected_unit) which states the unit which is currently selected. If the mouse button is pressed the clear the state and set the new selection state according to the mouse position.
Once the variable is selected, then highlight the shape in the main application loop rather than in the event loop. e.g.:
selected_unit = None
while 1:
# Draws the chessboard
cb.draw()
# set up white units
# [...]
# set up black units
# [...]
# Event loop (inner)
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
# mouse handling
if event.type == pygame.MOUSEBUTTONDOWN:
mouse_position = pygame.mouse.get_pos()
if selected_unit:
# reset the selection
selected_unit = None
else:
# select a new unit
for unit in white_army:
if unit.tile_area.collidepoint(mouse_position):
selected_unit = unit
# highlight the selected unit
if selected_unit:
highlight_tile(selected_unit.tile_area)
# update the dispaly
pygame.display.update()

Pygame Stop Light Event [duplicate]

This question already exists:
Need Help Creating A Stop Light Event In Pygame (Python 3) [closed]
Closed 9 years ago.
I'm trying to create the last example in lab # 8 for the Program Arcade Games Book.
The last thing I'm trying to implement is a stoplight that changes colors based on three events and timers.
I understand that I have to use pygame.time.set_timer() at one point, but I haven't been exposed to event handling in Pygame yet. I don't know how I should go about making three separate events that would turn each corresponding traffic light its bright color.
This is what I have so far, and if you omit the line 258 to 266, the animation works using Pygame and Python 3 (I tried concatenating the "events" but its just not working obviously).
Here's a revised version where I tried to use timers to literally just change the color instead of making separate events but its not working in this case either ;(
import random
import math
# Requirements:
# Modify the prior Create-a-Picture lab, or start a new one.
# Animate the image. Try one or more of the following:
# Move an item across the screen.
# Move an item back and forth.
# Move up/down/diagonally.
# Move in circles.
# Have a person wave his/her arms.
# Create a stoplight that changes colors.
# import statement
import pygame
# Define colors:
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
DULLRED = (153, 50, 51)
GREEN = (0, 255, 0)
DULLGREEN = (55, 154, 54)
BLUE = (0, 0, 255)
LIGHTBLUE = (103, 255, 246)
YELLOW = (252, 255, 31)
DULLYELLOW = (156, 157, 50)
# initialize pygame:
pygame.init()
# screen:
size = (700, 500)
screen = pygame.display.set_mode(size)
# Set the caption:
pygame.display.set_caption("Chapter 8 Lab")
# Clock:
clock = pygame.time.Clock()
# FPS:
FPS = 60
# boolean variable for game:
done = False
# For loop for white circles:
whiteCircleList = []
for i in range(25):
x = random.randrange(0, 700)
y = random.randrange(0, 50)
whiteCircleList.append([x,y])
# For Loop for Blue Circles:
blueCircleList = []
for i in range(100):
circleX = random.randrange(0, 500)
circleY = random.randrange(0, 700)
blueCircleList.append([circleX, circleY])
# Light Blue Circle For Loop:
lightBlueCircleList = []
for i in range(100):
circleX = random.randrange(0, 500)
circleY = random.randrange(0, 700)
lightBlueCircleList.append([circleX, circleY])
# Surfboard's Rectangle (x-pos, y-pos, x-length, y-length):
surfboardRect = pygame.Rect(325, 225, 50, 150)
boardY = 255.
rectYChange = -5
phase = 0
# Diagonal Rectangle in Top Left Corner:
topLeftDiagonalRect = pygame.Rect(0, 0, 10, 10)
# Diagonal Rectangle in Top Right Corner:
topRightDiagonalRect = pygame.Rect(500, 1, 10, 10)
# Diagonal Rectangle Vectors for Top Left Rectangle:
topLeftDiagonalRectXChange = 5
topLeftDiagonalRectYChange = 5
# Diagonal Rectangle Vectors for Top Right Rectangle:
topRightDiagonalRectXChange = -5
topRightDiagonalRectYChange = -5
# Angle for Hand Rotation
handAngle = 0
# Variable for Traffic Light Cover:
currentTopColor = DULLRED
currentMiddleColor = DULLYELLOW
currentBottomColor = GREEN
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
# Game Logic:
phase += 1
if phase > 180:
phase = phase * -1
# Save exact position as a float to avoid floating point errors:
boardY += math.cos(math.radians(phase))*2
surfboardRect.y = int(boardY)
# Clear the screen:
screen.fill(RED)
# Drawing Code:
# Falling Down circles:
for i in range(len(whiteCircleList)):
pygame.draw.circle(screen, WHITE, [whiteCircleList[i][0], whiteCircleList[i][1]], 3)
whiteCircleList[i][1] += 5
# If the rectangles have hit the bottom of the screen, make them appear 10 pixels above the top:
if whiteCircleList[i][1] > 450:
x = random.randrange(0, 700)
whiteCircleList[i][0] = x
y = random.randrange(-50, -10)
whiteCircleList[i][1] = y
# Red Falling Up Circles:
for i in range(len(blueCircleList)):
pygame.draw.circle(screen, BLUE, [blueCircleList[i][0], blueCircleList[i][1]], 5, 5)
blueCircleList[i][1] -= 5
if blueCircleList[i][1] < 50:
circleX = random.randrange(0,700)
circleY = random.randrange(400, 500)
blueCircleList[i][0] = circleX
blueCircleList[i][1] = circleY
# Light Blue Falling Up Circles:
for i in range(len(lightBlueCircleList)):
pygame.draw.circle(screen, LIGHTBLUE, [lightBlueCircleList[i][0], lightBlueCircleList[i][1]], 3, 3)
lightBlueCircleList[i][1] -= 5
if lightBlueCircleList[i][1] < 50:
circleX = random.randrange(0, 700)
circleY = random.randrange(400, 450)
lightBlueCircleList[i][0] = circleX
lightBlueCircleList[i][1] = circleY
# Revised Surfboard Rectangle Code:
pygame.draw.rect(screen, BLACK, surfboardRect, 0)
# Top Left Diagonal Rectangle Code:
pygame.draw.rect(screen, BLACK, topLeftDiagonalRect, 0)
# Add The Top Left Diagonal Rectangle Change Vectors
topLeftDiagonalRect.x += topLeftDiagonalRectXChange
topLeftDiagonalRect.y += topLeftDiagonalRectYChange
# Top and Bottom Screen Collision:
if topLeftDiagonalRect.y >= 500 or topLeftDiagonalRect.y <= 0:
topLeftDiagonalRectYChange = topLeftDiagonalRectYChange * -1
# Left and Right Screen Collision:
if topLeftDiagonalRect.x <= 0 or topLeftDiagonalRect.x >= 700:
topLeftDiagonalRectXChange = topLeftDiagonalRectXChange * -1
# Draw the top right rectangle:
pygame.draw.rect(screen, BLACK, topRightDiagonalRect, 0)
# Add the change vectors for the Top Right Rectangle:
topRightDiagonalRect.x += topRightDiagonalRectXChange
topRightDiagonalRect.y += topRightDiagonalRectYChange
# Top and Bottom Screen Collision:
if topRightDiagonalRect.y <= 0 or topRightDiagonalRect.y >= 500:
topRightDiagonalRectYChange = topRightDiagonalRectYChange * -1
# Left and Right Screen Collision:
if topRightDiagonalRect.x <= 0 or topRightDiagonalRect.x >= 700:
topRightDiagonalRectXChange = topRightDiagonalRectXChange * -1
# Person Waving His Arms:
# Head:
pygame.draw.circle(screen, WHITE, [575, 300], 15)
# Body:
pygame.draw.rect(screen, BLUE, [560, 315, 30, 60], 0)
# Left Rotating Hand:
# Left Hand's Original Dimensions:
# pygame.draw.line(screen, WHITE, [560, 315], [540, 295], 5)
# Original Hand's x position based on the rotating circle idea:
# handX = 40 * math.sin(handAngle) + 560
# Original Hand's y position based on the rotating circle idea:
# handY = 40 * math.cos(handAngle) + 315
handPosition = (40 * math.sin(handAngle) + 560, 40 * math.cos(handAngle) + 315)
pygame.draw.line(screen, WHITE, [560, 315], handPosition, 4)
# Increase the hand angle by 0.05 Radians:
handAngle = handAngle + 0.05
# Reset the angle after a full sweep:
pi = 3.141592653
if handAngle > 2 * pi:
handAngle = handAngle - 2*pi
# Right Immobile Hand:
pygame.draw.line(screen, WHITE, [590, 315], [590, 340], 4)
# Left Leg:
pygame.draw.rect(screen, WHITE, [560, 375, 10, 20], 0)
# Right Leg:
pygame.draw.rect(screen, WHITE, [580, 375, 10, 20], 0)
# Left Shoe Ellipse
# Ellipse Notes: ellipse(Surface, color, Rect, width=0) -> Rect
pygame.draw.ellipse(screen, BLACK, [550, 390, 20, 15], 0)
# Right Shoe Ellipse:
pygame.draw.ellipse(screen, BLACK, [580, 390, 20, 15], 0)
# Add in a changing traffic light
# Rectangle for Traffic Light:
pygame.draw.rect(screen, WHITE, [50, 350, 50, 100], 0)
# Traffic Light Post:
pygame.draw.rect(screen,BLACK, [65, 450, 20, 40], 0)
# Top light:
pygame.draw.circle(screen, currentTopColor, [75, 370], 12)
# Second light:
pygame.draw.circle(screen, currentMiddleColor, [75, 400], 12)
# Third light:
pygame.draw.circle(screen, currentBottomColor, [75, 430], 12)
# Three events must be cycled to change each of the traffic light colors
# from their dull versions to their fullest color forms
# The question is, can this be achieved through a timer?
# 60 frames per second is on the timer itself so every 180 frames would mean
# 3 seconds, or every 300 frames would mean 5 seconds
# DOCS on set timer: set_timer(eventid, milliseconds)
## turnRedLightOn = pygame.draw.circle(screen, RED, [75, 370], 12)
## + pygame.draw.circle(screen, DULLYELLOW, [75, 400], 12)
## + pygame.draw.circle(screen, DULLGREEN, [75, 430], 12)
# Turn the top light red and all other lights dull every three seconds
pygame.time.set_timer(currentTopColor = RED, 3000)
pygame.time.set_timer(currentMiddleColor = DULLYELLOW, 3000)
pygame.time.set_timer(currentBottomColor = DULLGREEN, 3000)
# Turn the middle light yellow and all other lights dull every six seconds
pygame.time.set_timer(currentTopColor = DULLRED, 6000)
pygame.time.set_timer(currentMiddleColor = YELLOW, 6000)
pygame.time.set_timer(currentBottomColor = DULLGREEN, 6000)
# Turn the bottom light green and all other lights dull every nine seconds
pygame.time.set_timer(currentTopColor = DULLRED, 9000)
pygame.time.set_timer(currentMiddleColor = DULLYELLOW, 9000)
pygame.time.set_timer(currentBottomColor = GREEN, 9000)
# Update the screen:
pygame.display.flip()
# Clock FPS:
clock.tick(FPS)
# Quit after main game loop ends:
pygame.quit()
The set_timer shouldn't execute in every cycle of the main loop, you can call it outside and it will repeat itself every 3000ms.
# Current Light
light_on = 0 # 0-red 1-yellow 2-green
LIGHTS_EVENT = pygame.USERVENT + 0 # Event code for Lights change
pygame.time.set_timer(LIGHTS_EVENT, 3000)
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
if event.type == LIGHTS_EVENT:
light_on += 1
if light_on == 3:
light_on = 0
currentTopColor = DULLRED
currentMiddleColor = DULLYELLOW
currentBottomColor = DULLGREEN
if light_on == 0:
currentTopColor = RED
if light_on == 1:
currentMiddleColor = YELLOW
if light_on == 2:
currentBottomColor = GREEN
# Top light:
...

Categories