Related
I had an earlier post deleted because there are similar posts. I appreciate that but because I am so inexperienced with pygame (I literally began using it last week), I cant make heads or tails of the code. Also, I am finding it difficult to apply to my game. Firstly, I do not need it to relate to a moving player character as they will always travel from a set position (400, 450). Also, I preferably need it to do this when the left mouse button is pressed but if it is easier to use a key then that is fine. I simply do not have the expertise to use past posts and apply it to my program. Thanks. Just to clarify, my game is going to be a duck hunt-like target shooter game.
#Setting window dimensions and caption (Module 1)
pygame.init()
window = pygame.display.set_mode((800, 575))
pygame.display.set_caption("TARGET PRACTICE")
#Colour variables (Module 1)
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (200, 0, 0)
GREEN = (0, 200, 0)
BLUE = (0, 0, 200)
exec = True
#Target class created (Module 5)
class Target:
def __init__(self, x, y, h, w, v):
self.x = x
self.y = y
self.h = h
self.w = w
self.v = v
#Instantiation of targets (Module 5)
target_1 = Target(0, 80, 60, 40, 0.5)
target_2 = Target(0, 100, 60, 40, 0.5)
target_3 = Target(0, 50, 60, 40, 0.5)
target_4 = Target(0, 75, 60, 40, 0.5)
target_5 = Target(0, 45, 60, 40, 0.5)
target_6 = Target(0, 85, 60, 40, 0.5)
#Declaring variables to be used in the while loop (Module 5)
clock = 0
target_2_threshold = 500
target_3_threshold = 1000
target_4_threshold = 1500
target_5_threshold = 2000
target_6_threshold = 2500
#Setting player sprite dimension variables (Module 6)
player_sprite_x = 357.5
player_sprite_y = 450
player_sprite_h = 125
player_sprite_w = 85
while exec:
pygame.time.delay(1)
for event in pygame.event.get():
if event.type == pygame.QUIT:
exec = False
#Defines movement of targets and sets delay between drawings (Module 5)
clock += 1
target_1.x += target_1.v
if clock > target_2_threshold:
target_2.x += target_2.v
if clock > target_3_threshold:
target_3.x += target_3.v
if clock > target_4_threshold:
target_4.x += target_4.v
if clock > target_5_threshold:
target_5.x += target_5.v
if clock > target_6_threshold:
target_6.x += target_6.v
#Fill the background (Module 5)
window.fill(RED)
#Redraw each target in every frame (Module 5)
pygame.draw.rect(window, BLUE, (target_1.x, target_1.y, target_1.h, target_1.w))
if clock > target_2_threshold:
pygame.draw.rect(window, BLUE, (target_2.x, target_2.y, target_2.h, target_2.w))
if clock > target_3_threshold:
pygame.draw.rect(window, BLUE, (target_3.x, target_3.y, target_3.h, target_3.w))
if clock > target_4_threshold:
pygame.draw.rect(window, BLUE, (target_4.x, target_4.y, target_4.h, target_4.w))
if clock > target_5_threshold:
pygame.draw.rect(window, BLUE, (target_5.x, target_5.y, target_5.h, target_5.w))
if clock > target_6_threshold:
pygame.draw.rect(window, BLUE, (target_6.x, target_6.y, target_6.h, target_6.w))
#Draw the player sprite (Module 6)
pygame.draw.rect(window, BLUE, (player_sprite_x, player_sprite_y, player_sprite_w, player_sprite_h))
pygame.display.update()
pygame.quit()
Here are some brief notes to get you pointed in the right direction. People often write questions that amount to "Gimme teh codez!", so very open and broad questions are often greeted (quite rightly) with negativity.
So...
First you need to grab the mouse cursor. This is achieved by handling the mouse events in your main event-loop:
while exec:
#pygame.time.delay(1) # <-- DON'T DO THIS
for event in pygame.event.get():
if event.type == pygame.QUIT:
exec = False
elif event.type == pygame.MOUSEBUTTONUP: # mouse button released
mouse_position = pygame.mouse.get_pos()
# TODO: handle mouse click at <mouse_position>
Note: I like to use MOUSEBUTTONUP rather than DOWN, because this is how modern GUIs tend to work - on-screen operations activate on release, but it's up to you.
Now you have the click-position, how do you launch a bullet from the bottom-centre of the screen to the mouse co-ordinates?
Well, first what is bottom-centre? It's half the width of the window, and maybe some factor from the bottom. In PyGame the top-left corner of the screen is (0, 0), and the bottom-left is ( 0, window_height-1 ). I always store the window size in variables, so computing this position is easy, and it frees the code from having to be modified if the window/screen size changes.
WINDOW_WIDTH = 800
WINDOW_HEIGHT = 575
# Firing position is 98% bottom, middle of the window/screen.
start_position = ( ( WINDOW_WIDTH // 2, int( WINDOW_HEIGHT * 0.98 ) )
So then given a mouse-click, the code needs to make the bullet travel from start_position to mouse_position. This can be calculated, by working out an x and y speed which should be applied, each update, to the projectile. Obviously a projectile travelling straight down has a speed of ( 0, something ), and a projectile going directly right has a speed of ( something, 0 ). Obviously if travelling up or left, the something would be negative because of the way PyGame lays-out its co-ordinates. The point is that there are separate components of change: x for the horizontal movement, and y for the vertical .
To calculate this x and y speed (technically a velocity vector), the code needs to determine the line from start to end, and then normalise it (a fancy way of saying "divide it by it's length").
I'm assume you know the line-length formula already, so this part should be easy. It makes the calculations simpler if the code temporarily offsets both points as-if the origin was at (0,0), since the direction is still the same.
Once the code has the velocity vector (x, y), each animation update-cycle simply add these component-speeds to the co-ordinate of the projectile. You may need to store the co-ordinates as real numbers, since adding small amounts (e.g.: 0.2) to an integer tends to throw away the change. This is because some-integer + 0.2 -> some-integer.
Anyway, see how you go with this information. If trouble persists ... ask another question!
Use event pygame.MOUSEBUTTONDOWN to get mouse click and its position.
You can use pygame.math.Vector2() to create vector between mouse and player. And then you can use normalize() to create value which you can use as bullet's direction.
And keep in variable or list bullet's position and bullet's direction
all_bullets = []
while exec:
pygame.time.delay(1)
for event in pygame.event.get():
if event.type == pygame.QUIT:
exec = False
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1:
print("[shoot!] mouse position:", event.pos)
dx = event.pos[0] - (player_sprite_x+ player_sprite_w//2)
dy = event.pos[1] - player_sprite_y
direction = pygame.math.Vector2(dx, dy).normalize()
bullet = {'x': player_sprite_x+42, 'y': player_sprite_y, 'direction': direction}
all_bullets.append(bullet)
Later you can use for-loop to move every bullet on list and keep only bullets which are still on screen
all_bullets_keep = []
for item in all_bullets:
item['x'] += item['direction'][0] # item['direction'][0] * 2
item['y'] += item['direction'][1] # item['direction'][1] * 2
# keep bullet if it is still on screen
if 0 < item['x'] < 800 and 0 < item['y'] < 575:
all_bullets_keep.append(item)
all_bullets = all_bullets_keep
#print(len(all_bullets), end='\r')
Finally you can use for-loop to draw all bullets
for item in all_bullets:
pygame.draw.rect(window, BLUE, (item['x']-5, item['y']-5, 10, 10))
It still need to check collision with targets but it would be easier if you would keep targets on list and use pygame.Rect()` to keep position and size because it has special methods to check collisions.
So now you have work to use lists with tagets and pygame.Rect()
import pygame
pygame.init()
window = pygame.display.set_mode((800, 575))
pygame.display.set_caption("TARGET PRACTICE")
#Colour variables (Module 1)
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (200, 0, 0)
GREEN = (0, 200, 0)
BLUE = (0, 0, 200)
exec = True
#Target class created (Module 5)
class Target:
def __init__(self, x, y, h, w, v):
self.x = x
self.y = y
self.h = h
self.w = w
self.v = v
#Instantiation of targets (Module 5)
target_1 = Target(0, 80, 60, 40, 0.5)
target_2 = Target(0, 100, 60, 40, 0.5)
target_3 = Target(0, 50, 60, 40, 0.5)
target_4 = Target(0, 75, 60, 40, 0.5)
target_5 = Target(0, 45, 60, 40, 0.5)
target_6 = Target(0, 85, 60, 40, 0.5)
#Declaring variables to be used in the while loop (Module 5)
clock = 0
target_2_threshold = 500
target_3_threshold = 1000
target_4_threshold = 1500
target_5_threshold = 2000
target_6_threshold = 2500
#Setting player sprite dimension variables (Module 6)
player_sprite_x = 357.5
player_sprite_y = 450
player_sprite_h = 125
player_sprite_w = 85
all_bullets = []
while exec:
pygame.time.delay(1)
for event in pygame.event.get():
if event.type == pygame.QUIT:
exec = False
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1:
print("[shoot!] mouse position:", event.pos)
dx = event.pos[0] - (player_sprite_x+ player_sprite_w//2)
dy = event.pos[1] - player_sprite_y
direction = pygame.math.Vector2(dx, dy).normalize()
bullet = {'x': player_sprite_x+42, 'y': player_sprite_y, 'direction': direction}
all_bullets.append(bullet)
#Defines movement of targets and sets delay between drawings (Module 5)
clock += 1
target_1.x += target_1.v
if clock > target_2_threshold:
target_2.x += target_2.v
if clock > target_3_threshold:
target_3.x += target_3.v
if clock > target_4_threshold:
target_4.x += target_4.v
if clock > target_5_threshold:
target_5.x += target_5.v
if clock > target_6_threshold:
target_6.x += target_6.v
all_bullets_keep = []
for item in all_bullets:
item['x'] += item['direction'][0] # item['direction'][0] * 2
item['y'] += item['direction'][1] # item['direction'][1] * 2
# keep bullet if it is still on screen
if 0 < item['x'] < 800 and 0 < item['y'] < 575:
all_bullets_keep.append(item)
all_bullets = all_bullets_keep
#print(len(all_bullets), end='\r')
#Fill the background (Module 5)
window.fill(RED)
#Redraw each target in every frame (Module 5)
pygame.draw.rect(window, BLUE, (target_1.x, target_1.y, target_1.h, target_1.w))
if clock > target_2_threshold:
pygame.draw.rect(window, BLUE, (target_2.x, target_2.y, target_2.h, target_2.w))
if clock > target_3_threshold:
pygame.draw.rect(window, BLUE, (target_3.x, target_3.y, target_3.h, target_3.w))
if clock > target_4_threshold:
pygame.draw.rect(window, BLUE, (target_4.x, target_4.y, target_4.h, target_4.w))
if clock > target_5_threshold:
pygame.draw.rect(window, BLUE, (target_5.x, target_5.y, target_5.h, target_5.w))
if clock > target_6_threshold:
pygame.draw.rect(window, BLUE, (target_6.x, target_6.y, target_6.h, target_6.w))
for item in all_bullets:
pygame.draw.rect(window, BLUE, (item['x']-5, item['y']-5, 10, 10))
#Draw the player sprite (Module 6)
pygame.draw.rect(window, BLUE, (player_sprite_x, player_sprite_y, player_sprite_w, player_sprite_h))
pygame.display.update()
pygame.quit()
I am currently coding a simple computer game for a school coursework project and have encountered an issue in pygame. In my game, the player simply has to shoot at targets which fly through the air, almost a reverse space invaders. My current issue is that I can not have two shapes in the arena at once without one of them flickering and lagging. Below is my code, any help is welcomed. Cheers.
Also, does anyone know how I can add delay between each drawing being drawn. As I insinuated above, I aim to have targets fairly space out without overlaps but I do want multiple on the screen at the same time. Cheers.
import pygame
#Setting window dimensions and caption (Module 1)
pygame.init()
window = pygame.display.set_mode((800, 575))
pygame.display.set_caption("TARGET PRACTICE")
#Colour variables (Module 1)
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (200, 0, 0)
GREEN = (0, 200, 0)
BLUE = (0, 0, 200)
#Target coordinates and dimensions (Module 3)
#target_x = 0
#target_y = 80
#target_w = 60
#target_h = 40
#target_v = 1
exec = True
class Target:
def __init__(self, x, y, h, w, v):
self.x = x
self.y = y
self.h = h
self.w = w
self.v = v
target_1 = Target(0, 80, 60, 40, 0.1)
target_2 = Target(0, 100, 60, 40, 0.1)
target_3 = Target(0, 50, 60, 40, 1)
clock = 0
while exec:
pygame.time.delay(1)
for event in pygame.event.get():
if event.type == pygame.QUIT:
exec = False
#Target Movement (Module 4)
window.fill(RED)
#Background and target drawing (Module 2)
target_1.x += target_1.v
pygame.draw.rect(window, BLUE, (target_1.x, target_1.y, target_1.h, target_1.w))
clock += 1
if clock%2 == 0:
target_2.x += target_2.v
pygame.draw.rect(window, BLUE, (target_2.x, target_2.y, target_2.h, target_2.w))
pygame.display.update()
pygame.quit()```
target_2 is flashing, because it is just drawn in every 2nd frame. Change the position of target_2 in every 2nd frame, but draw it in every frame:
while exec:
# [...]
# update positions
target_1.x += target_1.v
if clock%2 == 0:
target_2.x += target_2.v
clock += 1
# clear window (fill in rED)
window.fill(RED)
# draw all the objects of the scene in every frame
pygame.draw.rect(window, BLUE, (target_1.x, target_1.y, target_1.h, target_1.w))
pygame.draw.rect(window, BLUE, (target_2.x, target_2.y, target_2.h, target_2.w))
# update display
pygame.display.update()
Note, the scene has to be redrawn in every frame. First the background is cleared filled window.fill(RED), then the entire scene has to be drawn and finally the display has to be updated (pygame.display.update()).
If you want to delay the target_2, then you have to draw and update the position after clock has reached a certain limit:
clock = 0
target_2_threshold = 500
while exec:
# [...]
# update positions
clock += 1
target_1.x += target_1.v
if clock > target_2_threshold and clock % 2 == 0:
target_2.x += target_2.v
# clear window (fill in rED)
window.fill(RED)
# draw all the objects of the scene in every frame
pygame.draw.rect(window, BLUE, (target_1.x, target_1.y, target_1.h, target_1.w))
if clock > target_2_threshold:
pygame.draw.rect(window, BLUE, (target_2.x, target_2.y, target_2.h, target_2.w))
# update display
pygame.display.update()
I created a window with a width and height of 800 pixels using pygame then drew rectangles with size 32 to make the window a 25x25 grid. What I want to do is change the color of the rectangle I click to change.
My Code:
def createGrid():
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 800
BLOCK_SIZE = 32
WHITE = (255,255,255)
pygame.init()
frame = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("PathFinder")
frame.fill(WHITE)
for y in range(SCREEN_HEIGHT):
for x in range(SCREEN_WIDTH):
rect = pygame.Rect(x*BLOCK_SIZE, y*BLOCK_SIZE, BLOCK_SIZE - 1, BLOCK_SIZE - 1)
pygame.draw.rect(frame, (0,250,0), rect)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
pygame.display.update()
First you should create list with all rects and they colors.
Next you should draw all rect inside while loop.
And finally you have to use event.MOUSEBUTTONDOWN to get mouse click and compare mouse position with every rect on list and change color on list.
import pygame
import sys
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 800
BLOCK_SIZE = 32
WHITE = (255,255,255)
pygame.init()
frame = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("PathFinder")
# create list with all rects
all_rects = []
for y in range(0, SCREEN_HEIGHT, BLOCK_SIZE):
row = []
for x in range(0, SCREEN_WIDTH, BLOCK_SIZE):
rect = pygame.Rect(x, y, BLOCK_SIZE-1, BLOCK_SIZE-1)
row.append([rect, (0, 255, 0)])
all_rects.append(row)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
elif event.type == pygame.MOUSEBUTTONDOWN:
# check which rect was clicked and change its color on list
for row in all_rects:
for item in row:
rect, color = item
if rect.collidepoint(event.pos):
if color == (0, 255, 0):
item[1] = (255, 0, 0)
else:
item[1] = (0, 255, 0)
# draw all in every loop
frame.fill(WHITE)
for row in all_rects:
for item in row:
rect, color = item
pygame.draw.rect(frame, color, rect)
pygame.display.flip()
Eventually you could draw all rects before loop and use event to draw new rect on clicked rect. But you still need list with all rects to check which rect was click and what is its color.
It depends on what your code is intended to do? This example shows how to paint to the primary display surface but doesn't keep track of which colour is where.
import pygame
white = (255, 255, 255)
red = (255, 0, 0)
size = 32
pygame.init()
s = pygame.display.set_mode((800, 800))
s.fill(white)
# press escape to exit example
while True:
e = pygame.event.get()
if pygame.key.get_pressed()[pygame.K_ESCAPE]: break
x = int(pygame.mouse.get_pos()[0] / size) * size
y = int(pygame.mouse.get_pos()[1] / size) * size
if pygame.mouse.get_pressed()[0]:
pygame.draw.rect(s, red, (x, y, size, size), 0)
pygame.display.update()
pygame.time.Clock().tick(60)
pygame.quit()
I'm trying to create an excel document look alike using recursion in pygame. I got the first if statement to fill the top row of the screen, and looking for it to go down by 50 (height of my rectangle) each time and keep going until it hits the edge of my screen again, filling the screen completely. I did another for loop to try this but it stops and misses one rectangle at (0,0), is there any way to do this in one loop so the screen will fill and make a bunch of columns and rows? Thanks.
"""
Recursively draw rectangles.
Sample Python/Pygame Programs
Simpson College Computer Science
http://programarcadegames.com/
http://simpson.edu/computer-science/
"""
import pygame
# Colors
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
def recursive_draw(x, y, width, height):
""" Recursive rectangle function. """
pygame.draw.rect(screen, BLACK,
[x, y, width, height],
1)
# Is the rectangle wide enough to draw again?
if(x < 750):
# Scale down
x += 150
y = 0
width = 150
height = 50
# Recursively draw again
recursive_draw(x, y, width, height)
if (x < 750):
# Scale down
x += 0
y += 50
width = 150
height = 50
# Recursively draw again
recursive_draw(x, y, width, height)
pygame.init()
# Set the height and width of the screen
size = [750, 500]
screen = pygame.display.set_mode(size)
pygame.display.set_caption("My Game")
# Loop until the user clicks the close button.
done = False
# Used to manage how fast the screen updates
clock = pygame.time.Clock()
# -------- Main Program Loop -----------
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
# Set the screen background
screen.fill(WHITE)
# ALL CODE TO DRAW SHOULD GO BELOW THIS COMMENT
recursive_draw(0, 0, 150, 50)
# ALL CODE TO DRAW SHOULD GO ABOVE THIS COMMENT
# Go ahead and update the screen with what we've drawn.
pygame.display.flip()
# Limit to 60 frames per second
clock.tick(60)
# Be IDLE friendly. If you forget this line, the program will 'hang'
# on exit.
pygame.quit()
I'd first add a base case so that the function returns when the bottom of the screen is reached. Add the width to x until the right side is reached and when it is there, increment y += height and reset x = 0 to start drawing the next row.
import pygame
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
def recursive_draw(x, y, width, height):
"""Recursive rectangle function."""
pygame.draw.rect(screen, BLACK, [x, y, width, height], 1)
if y >= 500: # Screen bottom reached.
return
# Is the rectangle wide enough to draw again?
elif x < 750-width: # Right screen edge not reached.
x += width
# Recursively draw again.
recursive_draw(x, y, width, height)
else:
# Increment y and reset x to 0 and start drawing the next row.
x = 0
y += height
recursive_draw(x, y, width, height)
pygame.init()
size = [750, 500]
screen = pygame.display.set_mode(size)
clock = pygame.time.Clock()
screen.fill(WHITE)
recursive_draw(0, 0, 150, 50)
done = False
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
pygame.display.flip()
clock.tick(60)
pygame.quit()
It would be easier to use nested for loops to draw the grid:
def draw_grid(x, y, width, height, size):
for y in range(0, size[1], height):
for x in range(0, size[0], width):
pygame.draw.rect(screen, BLACK, [x, y, width, height], 1)
The following line of code, I have borrowed as an example to use. What it does is it takes a string and prints it out in pygame, and the line will follow wherever the mouse goes. I simply was wondering how to change the string line for a draw.circle, I keep getting an error. Cheers! Code Below, the string in question is the one saying, "the last button pressed is".
from pygame import *
init()
size = width, height = 800, 600
screen = display.set_mode(size)
button = 0
BLACK = (0, 0, 0)
RED = (255, 255, 255)
font = font.SysFont("Times New Roman",30)
def drawScene(screen, mx, my, button):
draw.rect(screen, BLACK, (0, 0, width, height))
# Draw circle if the left mouse button is down.
string = "The last button pressed is " + str(button) + "."
text = font.render(string, 1, RED)
size = font.size(string)
screen.blit(text, Rect(mx, my, size[0], size[1]))
display.flip()
running = True
myClock = time.Clock()
mx = my = 0
# Game Loop
while running:
for evnt in event.get(): # checks all events that happen
if evnt.type == QUIT:
running = False
if evnt.type == MOUSEBUTTONDOWN:
mx, my = evnt.pos
button = evnt.button
if evnt.type == MOUSEMOTION:
mx, my = evnt.pos
drawScene(screen, mx, my, button)
myClock.tick(60) # waits long enough to have 60 fps
quit()
This will draw a 5 pixel radius circle at mx, my:
pygame.draw.circle(screen, BLACK, (mx, my), 5)