How to Move Sprite to a Specific Location Without Keys - python

Im trying to make a game with pygame where I can click a sprite then click somewhere on the screen for the sprite to move towards. So far, I'm able to click the sprite and get a response but I'm not sure how to tell the sprite to go to a given location where I click. I've seen something online with sprite.goal but I can't seem to make it work.
This is what I have
if event.type==pygame.MOUSEBUTTONDOWN:
pos=pygame.mouse.get_pos()
#White is a rectangle
if White.collidepoint(pos):
Moving=True
elif Moving==True:
#This is where I would tell it to move to pos

I'll show you a very simple example. Write a function that moves a point 1 step to a target point and returns the new position:
def move_to_target(pos, target):
x, y = pos
if x < target[0]:
x += 1
elif x > target[0]:
x -= 1
if y < target[1]:
y += 1
elif y > target[1]:
y -= 1
return (x, y)
Set a new target when the mouse button is pressed and call the function at each frame:
import pygame
pygame.init()
window = pygame.display.set_mode((400, 400))
clock = pygame.time.Clock()
def move_to_target(pos, target):
x, y = pos
if x < target[0]:
x += 1
elif x > target[0]:
x -= 1
if y < target[1]:
y += 1
elif y > target[1]:
y -= 1
return (x, y)
my_sprite = pygame.Surface((20, 20), pygame.SRCALPHA)
pygame.draw.circle(my_sprite, (255, 255, 0), (10, 10), 10)
pos = (200, 200)
target = (200, 200)
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.MOUSEBUTTONDOWN:
target = event.pos
pos = move_to_target(pos, target)
window.fill(0)
window.blit(my_sprite, pos)
pygame.display.flip()
clock.tick(100)
pygame.quit()
exit()
For variable speed and a straighter and smoother movement, you need to tweak the function. See How to make smooth movement in pygame.
import pygame
pygame.init()
window = pygame.display.set_mode((400, 400))
clock = pygame.time.Clock()
LERP_FACTOR = 0.05
minimum_distance = 0
maximum_distance = 100
def move_to_target(pos, target):
target_vector = pygame.math.Vector2(*target)
follower_vector = pygame.math.Vector2(*pos)
new_follower_vector = pygame.math.Vector2(*pos)
distance = follower_vector.distance_to(target_vector)
if distance > minimum_distance:
direction_vector = (target_vector - follower_vector) / distance
min_step = max(0, distance - maximum_distance)
max_step = distance - minimum_distance
step_distance = min_step + (max_step - min_step) * LERP_FACTOR
new_follower_vector = follower_vector + direction_vector * step_distance
return (new_follower_vector.x, new_follower_vector.y)
my_sprite = pygame.Surface((20, 20), pygame.SRCALPHA)
pygame.draw.circle(my_sprite, (255, 255, 0), (10, 10), 10)
pos = (200, 200)
target = (200, 200)
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.MOUSEBUTTONDOWN:
target = event.pos
pos = move_to_target(pos, target)
window.fill(0)
window.blit(my_sprite, pos)
pygame.display.flip()
clock.tick(100)
pygame.quit()
exit()

Related

Mouse not interacting with object correctly using collidepoint function

So I used the collidepoint function to test out whether or not my mouse is interacting or can interact with the images on the surface Surface but the variable mouse_pos does give out a position yet the mouse cannot ever collide with the object (see A is always false rather than true when the mouse hit the object). How do I solve this
Code:
import pygame
from sys import exit
pygame.init()
widthscreen = 1440 #middle 720
heightscreen = 790 #middle 395
w_surface = 800
h_surface = 500
midalignX_lg = (widthscreen-w_surface)/2
midalignY_lg = (heightscreen-h_surface)/2
#blue = player
#yellow = barrier
screen = pygame.display.set_mode((widthscreen,heightscreen))
pygame.display.set_caption("Collision Game")
clock = pygame.time.Clock()
test_font = pygame.font.Font('font/Pixeltype.ttf', 45)
surface = pygame.Surface((w_surface,h_surface))
surface.fill('Light Yellow')
blue_b = pygame.image.load('images/blue.png').convert_alpha()
blue_b = pygame.transform.scale(blue_b,(35,35))
yellow_b = pygame.image.load('images/yellow.png').convert_alpha()
yellow_b = pygame.transform.scale(yellow_b,(35,35))
text_surface = test_font.render('Ball Option:', True, 'White')
barrier_1_x = 0
barrier_1_surf = pygame.image.load('images/yellow.png').convert_alpha()
barrier_1_surf = pygame.transform.scale(barrier_1_surf,(35,35))
barrier_1_rect = barrier_1_surf.get_rect(center = (100, 350))
player_surf = pygame.image.load('images/blue.png').convert_alpha()
player_surf = pygame.transform.scale(player_surf,(35,35))
player_rect = player_surf.get_rect(center = (0,350))
while True:
#elements & update
#event loop
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
exit()
screen.blit(surface, (midalignX_lg,midalignY_lg))
screen.blit(blue_b,(150,250))
screen.blit(yellow_b, (150,300))
screen.blit(text_surface,(150, 200))
#barrier_1_x += 3
#if barrier_1_x > 800: barrier_1_x = 0
#barrier_1_rect.x += 3
#if barrier_1_rect.x > 800: barrier_1_rect.x = 0
barrier_1_rect.x += 2
if barrier_1_rect.right >= 820: barrier_1_rect.left = -10
player_rect.x += 3
if player_rect.right >= 820: player_rect.left = -10
surface = pygame.Surface((w_surface,h_surface))
surface.fill('Light Yellow')
surface.blit(barrier_1_surf, barrier_1_rect)
surface.blit(player_surf, player_rect)
'''if player_rect.colliderect(barrier_1_rect):
print('collision')'''
A = False;
mouse_pos = pygame.mouse.get_pos()
if player_rect.collidepoint(mouse_pos):
A = True
print(A)
pygame.display.update()
clock.tick(60)
I am not sure what else to do. i think it may be something wrong with the layering of the surface?
You are not drawing the objects on the screen, but on the surface. Therefore the coordinates of player_rect are relative to the surface and you also have to calculate the mouse position relative to the surface. The top left coordinate of the surface is (midalignX_lg, midalignY_lg):
while True:
# [...]
mouse_pos = pygame.mouse.get_pos()
rel_x = mouse_pos[0] - midalignX_lg
rel_y = mouse_pos[1] - midalignY_lg
if player_rect.collidepoint(rel_x, rel_y):
print("hit")

I can't draw all functions with my script

I made a script, which should take the user input of a mathematical function (f(x)=...) and draw it. I used pygame for that because I want to use that mechanic for a game.
I have to run the code of a function once without any output once, but after that, it flawlessly works
heres the code:
import pygame
def replace_x(function):
f = lambda x: eval(function)
return f
def convert_y(y_coords):
y_coords = 540 - y_coords
return y_coords
def convert_x(x_coord):
x_coord = x_coord + 960
return x_coord
# variables
background_colour = (255, 255, 255)
screen = pygame.display.set_mode((1920, 1080))
running = True
current_y = 0
previous_y = 0
pygame.init()
pygame.display.set_caption('Mathe Kreativarbeit')
screen.fill(background_colour)
pygame.display.flip()
function_input = input("Funktion: ")
function_input = function_input.replace("^", "**")
pygame.display.flip()
for x_coords in range(-15, 17):
f = replace_x(function_input)
current_y = convert_y(f(x_coords))
previous_y = convert_y(f(x_coords - 1))
start_pos = (convert_x((x_coords - 1) * 60), previous_y)
end_pos = (convert_x(x_coords * 60), current_y)
print(start_pos)
print(end_pos)
pygame.draw.aaline(screen, (0, 0, 0), start_pos, end_pos)
pygame.display.flip()
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
Create a list of points from the function:
f = replace_x(function_input)
pt_list = []
for x in range(-20, 20):
pt_list.append((x, f(x)))
Either print the list of points:
print(pt_list)
or print the list in a loop:
for pt in pt_list:
print(pt)
Create a list of screen coordinates from the list of point:
coord_list = []
for pt in pt_list:
x = round(convert_x(pt[0] * 20))
y = round(convert_y(pt[1]))
coord_list.append((x, y))
Draw the curve in the application loop with pygame.draw.aalines():
clock = pygame.time.Clock()
running = True
while running:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
pygame.draw.aalines(screen, (0, 0, 0), False, coord_list)
pygame.display.flip()
pygame.quit()
Example for input x**3:

get position of sprite, and move it in response to it being clicked on [duplicate]

I'm making a chess game, but I'm completely stuck on the drag and drop element, there's a few guides out there but they're all either dragging shapes, or only dragging one image.
I've tried several variants of code, but all were 50+ lines just to move one .png and most were incredibly inefficient
pygame.init()
pygame.display.set_caption("Python Chess")
clock = pygame.time.Clock()
red = (213,43,67)
chew = pygame.image.load("chew.png")
gameDisplay.fill(red)
gameDisplay.blit(chew, (400, 400))
pygame.display.update()
drag = 0
if pygame.MOUSEBUTTONDOWN:
drag = 1
if pygame.MOUSEBUTTONUP:
drag = 0
gameExit = False
while not gameExit:
for event in pygame.event.get():
if event.type == pygame.QUIT:
gameExit = True
Image simply doesn't drag.
Let's walk through this step by step.
Step 1: Let's start with a basic skeleton of every pygame game:
import pygame
def main():
screen = pygame.display.set_mode((640, 480))
clock = pygame.time.Clock()
while True:
events = pygame.event.get()
for e in events:
if e.type == pygame.QUIT:
return
screen.fill(pygame.Color('grey'))
pygame.display.flip()
clock.tick(60)
if __name__ == '__main__':
main()
We create a window and then start a loop to listen for events and drawing the window.
So far, so good. Nothing to see here, let's move on.
Step 2: a chess board
So, we want a chess game. So we need a board. We create a list of lists to represent our board, and we create a Surface that draws our board on the screen. We want to always seperate our game's state from the actual drawing functions, so we create a board variable and a board_surf.
import pygame
TILESIZE = 32
def create_board_surf():
board_surf = pygame.Surface((TILESIZE*8, TILESIZE*8))
dark = False
for y in range(8):
for x in range(8):
rect = pygame.Rect(x*TILESIZE, y*TILESIZE, TILESIZE, TILESIZE)
pygame.draw.rect(board_surf, pygame.Color('black' if dark else 'white'), rect)
dark = not dark
dark = not dark
return board_surf
def create_board():
board = []
for y in range(8):
board.append([])
for x in range(8):
board[y].append(None)
return board
def main():
screen = pygame.display.set_mode((640, 480))
board = create_board()
board_surf = create_board_surf()
clock = pygame.time.Clock()
while True:
events = pygame.event.get()
for e in events:
if e.type == pygame.QUIT:
return
screen.fill(pygame.Color('grey'))
screen.blit(board_surf, (0, 0))
pygame.display.flip()
clock.tick(60)
if __name__ == '__main__':
main()
Step 3: Where's the mouse?
We need to know which piece we want to select, so we have to translate the screen coordinates (where's the mouse relative to the window?) to the world coordinates (which square of the board is the mouse pointing to?).
So if the board is not located at the origin (the position (0, 0)), we also have to take this offset into account.
Basically, we have to substract that offset (which is the position of the board on the screen) from the mouse position (so we have the mouse position relative to the board), and divide by the size of the squares.
To see if this works, let's draw a red rectangle on the selected square.
import pygame
TILESIZE = 32
BOARD_POS = (10, 10)
def create_board_surf():
board_surf = pygame.Surface((TILESIZE*8, TILESIZE*8))
dark = False
for y in range(8):
for x in range(8):
rect = pygame.Rect(x*TILESIZE, y*TILESIZE, TILESIZE, TILESIZE)
pygame.draw.rect(board_surf, pygame.Color('black' if dark else 'white'), rect)
dark = not dark
dark = not dark
return board_surf
def create_board():
board = []
for y in range(8):
board.append([])
for x in range(8):
board[y].append(None)
return board
def get_square_under_mouse(board):
mouse_pos = pygame.Vector2(pygame.mouse.get_pos()) - BOARD_POS
x, y = [int(v // TILESIZE) for v in mouse_pos]
try:
if x >= 0 and y >= 0: return (board[y][x], x, y)
except IndexError: pass
return None, None, None
def main():
screen = pygame.display.set_mode((640, 480))
board = create_board()
board_surf = create_board_surf()
clock = pygame.time.Clock()
while True:
events = pygame.event.get()
for e in events:
if e.type == pygame.QUIT:
return
piece, x, y = get_square_under_mouse(board)
screen.fill(pygame.Color('grey'))
screen.blit(board_surf, BOARD_POS)
if x != None:
rect = (BOARD_POS[0] + x * TILESIZE, BOARD_POS[1] + y * TILESIZE, TILESIZE, TILESIZE)
pygame.draw.rect(screen, (255, 0, 0, 50), rect, 2)
pygame.display.flip()
clock.tick(60)
if __name__ == '__main__':
main()
Step 4: Let's draw some pieces
Chess is boring without some pieces to move around, so let's create some pieces.
I just use a SysFont to draw some text instead of using real images, so everyone can just copy/paste the code and run it immediately.
We store a tuple (color, type) in the nested board list. Also, let's use some other colors for our board.
import pygame
TILESIZE = 32
BOARD_POS = (10, 10)
def create_board_surf():
board_surf = pygame.Surface((TILESIZE*8, TILESIZE*8))
dark = False
for y in range(8):
for x in range(8):
rect = pygame.Rect(x*TILESIZE, y*TILESIZE, TILESIZE, TILESIZE)
pygame.draw.rect(board_surf, pygame.Color('darkgrey' if dark else 'beige'), rect)
dark = not dark
dark = not dark
return board_surf
def get_square_under_mouse(board):
mouse_pos = pygame.Vector2(pygame.mouse.get_pos()) - BOARD_POS
x, y = [int(v // TILESIZE) for v in mouse_pos]
try:
if x >= 0 and y >= 0: return (board[y][x], x, y)
except IndexError: pass
return None, None, None
def create_board():
board = []
for y in range(8):
board.append([])
for x in range(8):
board[y].append(None)
for x in range(0, 8):
board[1][x] = ('black', 'pawn')
for x in range(0, 8):
board[6][x] = ('white', 'pawn')
return board
def draw_pieces(screen, board, font):
for y in range(8):
for x in range(8):
piece = board[y][x]
if piece:
color, type = piece
s1 = font.render(type[0], True, pygame.Color(color))
s2 = font.render(type[0], True, pygame.Color('darkgrey'))
pos = pygame.Rect(BOARD_POS[0] + x * TILESIZE+1, BOARD_POS[1] + y * TILESIZE + 1, TILESIZE, TILESIZE)
screen.blit(s2, s2.get_rect(center=pos.center).move(1, 1))
screen.blit(s1, s1.get_rect(center=pos.center))
def draw_selector(screen, piece, x, y):
if piece != None:
rect = (BOARD_POS[0] + x * TILESIZE, BOARD_POS[1] + y * TILESIZE, TILESIZE, TILESIZE)
pygame.draw.rect(screen, (255, 0, 0, 50), rect, 2)
def main():
pygame.init()
font = pygame.font.SysFont('', 32)
screen = pygame.display.set_mode((640, 480))
board = create_board()
board_surf = create_board_surf()
clock = pygame.time.Clock()
while True:
events = pygame.event.get()
for e in events:
if e.type == pygame.QUIT:
return
piece, x, y = get_square_under_mouse(board)
screen.fill(pygame.Color('grey'))
screen.blit(board_surf, BOARD_POS)
draw_pieces(screen, board, font)
draw_selector(screen, piece, x, y)
pygame.display.flip()
clock.tick(60)
if __name__ == '__main__':
main()
Step 5: Drag'n'Drop
For drag and drop we need two things:
we have to change the state of your game (going into the "drag-mode")
eventhandling to enter and leave the "drag-mode"
It's actually not that complicated. To enter the "drag-mode", we just set a variable (selected_piece) when the MOUSEBUTTONDOWN event occurs. Since we already have the get_square_under_mouse function, it's easy to know if there's actually a piece under the mouse cursor.
if selected_piece is set, we draw a line and the piece under the mouse cursor, and we keep track of the current square under the cursor in case the MOUSEBUTTONUP event occurs. If that's the case, we swap the position of the piece in our board.
import pygame
TILESIZE = 32
BOARD_POS = (10, 10)
def create_board_surf():
board_surf = pygame.Surface((TILESIZE*8, TILESIZE*8))
dark = False
for y in range(8):
for x in range(8):
rect = pygame.Rect(x*TILESIZE, y*TILESIZE, TILESIZE, TILESIZE)
pygame.draw.rect(board_surf, pygame.Color('darkgrey' if dark else 'beige'), rect)
dark = not dark
dark = not dark
return board_surf
def get_square_under_mouse(board):
mouse_pos = pygame.Vector2(pygame.mouse.get_pos()) - BOARD_POS
x, y = [int(v // TILESIZE) for v in mouse_pos]
try:
if x >= 0 and y >= 0: return (board[y][x], x, y)
except IndexError: pass
return None, None, None
def create_board():
board = []
for y in range(8):
board.append([])
for x in range(8):
board[y].append(None)
for x in range(0, 8):
board[1][x] = ('black', 'pawn')
for x in range(0, 8):
board[6][x] = ('white', 'pawn')
return board
def draw_pieces(screen, board, font, selected_piece):
sx, sy = None, None
if selected_piece:
piece, sx, sy = selected_piece
for y in range(8):
for x in range(8):
piece = board[y][x]
if piece:
selected = x == sx and y == sy
color, type = piece
s1 = font.render(type[0], True, pygame.Color('red' if selected else color))
s2 = font.render(type[0], True, pygame.Color('darkgrey'))
pos = pygame.Rect(BOARD_POS[0] + x * TILESIZE+1, BOARD_POS[1] + y * TILESIZE + 1, TILESIZE, TILESIZE)
screen.blit(s2, s2.get_rect(center=pos.center).move(1, 1))
screen.blit(s1, s1.get_rect(center=pos.center))
def draw_selector(screen, piece, x, y):
if piece != None:
rect = (BOARD_POS[0] + x * TILESIZE, BOARD_POS[1] + y * TILESIZE, TILESIZE, TILESIZE)
pygame.draw.rect(screen, (255, 0, 0, 50), rect, 2)
def draw_drag(screen, board, selected_piece, font):
if selected_piece:
piece, x, y = get_square_under_mouse(board)
if x != None:
rect = (BOARD_POS[0] + x * TILESIZE, BOARD_POS[1] + y * TILESIZE, TILESIZE, TILESIZE)
pygame.draw.rect(screen, (0, 255, 0, 50), rect, 2)
color, type = selected_piece[0]
s1 = font.render(type[0], True, pygame.Color(color))
s2 = font.render(type[0], True, pygame.Color('darkgrey'))
pos = pygame.Vector2(pygame.mouse.get_pos())
screen.blit(s2, s2.get_rect(center=pos + (1, 1)))
screen.blit(s1, s1.get_rect(center=pos))
selected_rect = pygame.Rect(BOARD_POS[0] + selected_piece[1] * TILESIZE, BOARD_POS[1] + selected_piece[2] * TILESIZE, TILESIZE, TILESIZE)
pygame.draw.line(screen, pygame.Color('red'), selected_rect.center, pos)
return (x, y)
def main():
pygame.init()
font = pygame.font.SysFont('', 32)
screen = pygame.display.set_mode((640, 480))
board = create_board()
board_surf = create_board_surf()
clock = pygame.time.Clock()
selected_piece = None
drop_pos = None
while True:
piece, x, y = get_square_under_mouse(board)
events = pygame.event.get()
for e in events:
if e.type == pygame.QUIT:
return
if e.type == pygame.MOUSEBUTTONDOWN:
if piece != None:
selected_piece = piece, x, y
if e.type == pygame.MOUSEBUTTONUP:
if drop_pos:
piece, old_x, old_y = selected_piece
board[old_y][old_x] = 0
new_x, new_y = drop_pos
board[new_y][new_x] = piece
selected_piece = None
drop_pos = None
screen.fill(pygame.Color('grey'))
screen.blit(board_surf, BOARD_POS)
draw_pieces(screen, board, font, selected_piece)
draw_selector(screen, piece, x, y)
drop_pos = draw_drag(screen, board, selected_piece, font)
pygame.display.flip()
clock.tick(60)
if __name__ == '__main__':
main()
Of course there's a lot that can be improved (like using better datatypes than tuples, extract common logic into functions etc), but this should give you a good start on how to implement such things.
Always keep in mind:
write a single game loop that handles events, game logic, and drawing
make sure to only call pygame.display.flip once per frame
seperate your game state from your drawing functions
never call time.sleep or pygame.time.wait
use the build-in classes like Vector2 and Rect, they'll make your live easier (I didn't the Sprite class in this code, but it's also very usefull)
use functions to clean up your code
avoid global variables, except for constants
PyGame is low-level library, not game engine, and you have to make almost all from scratch.
This example drags two images but for more images it could use list or pygame.sprite.Group with pygame.sprite.Sprites and then code is getting longer and longer but as I said PyGame is not game engine like ie. Godot Engine (which uses language similar to Python). Maybe with Pyglet could be easier because you don't have to write mainloop from scratch but it still need some work. In PyGame you have to write from scratch even main element - mainloop.
import pygame
# --- constants ---
RED = (213, 43, 67)
# --- main ---
pygame.init()
screen = pygame.display.set_mode((800,600))
chew1 = pygame.image.load("chew.png")
chew1_rect = chew1.get_rect(x=400, y=400)
chew2 = pygame.image.load("chew.png") # use different image
chew2_rect = chew1.get_rect(x=200, y=200)
drag = 0
# --- mainloop ---
clock = pygame.time.Clock()
game_exit = False
while not game_exit:
# - events -
for event in pygame.event.get():
if event.type == pygame.QUIT:
game_exit = True
elif event.type == pygame.MOUSEBUTTONDOWN:
drag = 1
elif event.type == pygame.MOUSEBUTTONUP:
drag = 0
elif event.type == pygame.MOUSEMOTION:
if drag:
chew1_rect.move_ip(event.rel)
chew2_rect.move_ip(event.rel)
# - draws -
screen.fill(RED)
screen.blit(chew1, chew1_rect)
screen.blit(chew2, chew2_rect)
pygame.display.update()
# - FPS -
clock.tick(30)
# --- end ---
pygame.quit()
EDIT: the same with Group and Sprite and now you have to only add images to group items and rest of the code doesn't need to be changed.
import pygame
# --- constants ---
RED = (213, 43, 67)
# --- classes ---
class Item(pygame.sprite.Sprite):
def __init__(self, image, x, y):
super().__init__()
self.image = pygame.image.load(image)
self.rect = self.image.get_rect(x=x, y=y)
def update(self, rel):
self.rect.move_ip(rel)
# --- main ---
pygame.init()
screen = pygame.display.set_mode((800,600))
items = pygame.sprite.Group(
Item("chew.png", 200, 200),
Item("chew.png", 400, 200),
Item("chew.png", 200, 400),
Item("chew.png", 400, 400),
)
drag = 0
# --- mainloop ---
clock = pygame.time.Clock()
game_exit = False
while not game_exit:
# - events -
for event in pygame.event.get():
if event.type == pygame.QUIT:
game_exit = True
elif event.type == pygame.MOUSEBUTTONDOWN:
drag = 1
elif event.type == pygame.MOUSEBUTTONUP:
drag = 0
elif event.type == pygame.MOUSEMOTION:
if drag:
items.update(event.rel)
# - draws -
screen.fill(RED)
items.draw(screen)
pygame.display.update()
# - FPS -
clock.tick(30)
# --- end ---
pygame.quit()
EDIT: this version use Group to move only clicked image(s). If you click in place where two (or more) images are overlapped then it will drag two (or more) images.
import pygame
# --- constants ---
RED = (213, 43, 67)
# --- classes ---
class Item(pygame.sprite.Sprite):
def __init__(self, image, x, y):
super().__init__()
self.image = pygame.image.load(image)
self.rect = self.image.get_rect(x=x, y=y)
def update(self, rel):
self.rect.move_ip(rel)
# --- main ---
pygame.init()
screen = pygame.display.set_mode((800,600))
items = pygame.sprite.Group(
Item("chew.png", 150, 50),
Item("chew.png", 400, 50),
Item("chew.png", 150, 300),
Item("chew.png", 400, 300),
)
dragged = pygame.sprite.Group()
# --- mainloop ---
clock = pygame.time.Clock()
game_exit = False
while not game_exit:
# - events -
for event in pygame.event.get():
if event.type == pygame.QUIT:
game_exit = True
elif event.type == pygame.MOUSEBUTTONDOWN:
dragged.add(x for x in items if x.rect.collidepoint(event.pos))
elif event.type == pygame.MOUSEBUTTONUP:
dragged.empty()
elif event.type == pygame.MOUSEMOTION:
dragged.update(event.rel)
# - draws -
screen.fill(RED)
items.draw(screen)
pygame.display.update()
# - FPS -
clock.tick(30)
# --- end ---
pygame.quit()
EDIT: similar program in Pyglet - it moves two images with less code.
Pyglet has point (0,0) in left bottom corner.
To create red background it has to draw rectangle (QUADS) in OpenGL.
import pyglet
window = pyglet.window.Window(width=800, height=600)
batch = pyglet.graphics.Batch()
items = [
pyglet.sprite.Sprite(pyglet.resource.image('chew.png'), x=200, y=100, batch=batch),
pyglet.sprite.Sprite(pyglet.resource.image('chew.png'), x=400, y=300, batch=batch),
]
#window.event
def on_draw():
#window.clear()
pyglet.graphics.draw(4, pyglet.gl.GL_QUADS, ('v2f', [0,0, 800,0, 800,600, 0,600]), ('c3B', [213,43,67, 213,43,67, 213,43,67, 213,43,67])) #RED = (213, 43, 67)
batch.draw()
#window.event
def on_mouse_drag(x, y, dx, dy, buttons, modifiers):
for i in items:
i.x += dx
i.y += dy
pyglet.app.run()

How to draw a line in pygame that moves up the screen

I have made a python game, and when you press space it shoots a bullet (a line). It is only able to shoot once though. I was wondering how I could make it shoot multiple times?
shotStartX = x + 30
shotStartY = y + 20
shotEndX = x + 30
shotEndY = y
shoot = False
while True:
if event.type == pygame.KEYDOWN:
elif event.key == pygame.K_SPACE:
shoot = True
gameDisplay.fill(blue)
if shoot:
pygame.draw.line(gameDisplay,red,(shotStartX,shotStartY),(shotEndX,shotEndY),5)
shotStartY -= 10
shotEndY -= 10
gameDisplay.blit(rocketImg,(x,y))
pygame.display.update()
clock.tick(30)
I would create two classes, bullet and bullets and limit the firing rate to a certain time. Try this:
import pygame
import time
# define some colors
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
W = 480
H = 720
FPS = 60
BULLET_SPEED = 5
BULLET_FIRING_RATE = 0.5 # seconds
class bullet:
def __init__(s, x, y): # x, y = starting point
s.x = x
s.y = y
s.dead = False
def draw(s):
global DS
global WHITE
pygame.draw.circle(DS, WHITE, (s.x, s.y), 10)
def move(s):
global BULLET_SPEED
s.y -= BULLET_SPEED
if s.y <= 0:
s.dead = True
class bullets:
def __init__(s):
s.container = []
s.lastBulletTime = time.time() -BULLET_FIRING_RATE
def add(s, x, y):
global BULLET_FIRING_RATE
now = time.time()
if now - s.lastBulletTime >= BULLET_FIRING_RATE:
s.container.append(bullet(x, y))
s.lastBulletTime = now
def do(s):
deadBullets = []
for b in s.container:
b.draw()
b.move()
if b.dead: deadBullets.append(b)
for db in deadBullets:
s.container.remove(db)
pygame.init()
DS = pygame.display.set_mode((W, H))
CLOCK = pygame.time.Clock()
BULLETS = bullets()
# press escape to exit
while True:
e = pygame.event.get()
if pygame.key.get_pressed()[pygame.K_ESCAPE]: break
mx, my = pygame.mouse.get_pos()
mb = pygame.mouse.get_pressed()
if mb[0]:
BULLETS.add(mx, my)
DS.fill(BLACK)
BULLETS.do()
pygame.draw.circle(DS, WHITE, (mx, my), 40)
pygame.display.update()
CLOCK.tick(FPS)
pygame.quit()

How to make moveable image rotate to follow mouse

What the title says, I have an image that I can control with W,S,A,D. But I want to make the player image that is movable rotate to face the position of my mouse. Here's the relevent code:
import pygame
import random
import time
import math
import sys
pygame.init()
#The size of the game window
display_width = 1280
display_height = 800
#Colors available
black = (0, 0, 0) #colours defined by RGB,
white = (255, 255, 255)
red = (200, 0, 0)
green = (0, 150, 0)
bright_red = (255, 0, 0)
bright_green =(0, 255, 0)
#This code opens up the Game window
gameDisplay = pygame.display.set_mode((display_width, display_height))
pygame.display.set_caption("Blockslayer")
clock = pygame.time.Clock()
pygame.mouse.set_visible(True)
#player character info
slayerImg = pygame.image.load('squareslayer.png').convert_alpha()
slayerWidth = 84
slayerHeight = 84
#mouse info
mouse_c = pygame.image.load("crosshair.png ").convert_alpha()
def crosshair(mousex,mousey):
mousex, mousey = pygame.mouse.get_pos()
small_ch = pygame.transform.scale(mouse_c, (20, 20))
gameDisplay.blit(small_ch, (mousex, mousey,))
print(mousex,mousey)
#player character
def slayer(x,y,):
#small_slayer = pygame.transform.scale(slayerImg, (120, 80,))
pos = pygame.mouse.get_pos()
angle = 360 - math.atan2(pos[1] - 84, pos[0] - 84) * 180 / math.pi
rotimage = pygame.transform.rotate((slayerImg), angle,)
rect = rotimage.get_rect(center=(x, y))
gameDisplay.blit(rotimage, rect,)
pygame.display.update()
#Game Logic
def block_game_loop():
x = (display_width * 0.45)
y = (display_height * 0.8)
pos = pygame.mouse.get_pos()
angle = 360 - math.atan2(pos[1] + x - 84, pos[0] + y - 84) * 180 / math.pi
rotimage = pygame.transform.rotate((slayerImg), angle,)
mousex, mousey = pygame.mouse.get_pos()
#def blockguy(blockguyX, blockguyY, blockguyW, blockguyH, ):
#blockguyX = random.randrange(0, 785)
#blockguyY = random.randrange (0, 600)
#blockguyW = 166
#blockguyH = 110
#blockguy_speed = 5
#Event handler
exit_game = False
while not exit_game:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
pressed = pygame.key.get_pressed()
if pressed[pygame.K_s]: y += 7
if pressed[pygame.K_w]: y -= 7
if pressed[pygame.K_a]: x -= 7
if pressed[pygame.K_d]: x += 7
gameDisplay.fill(white)
slayer(x, y,)
#Boundaries
if x > display_width:
x = 1275
if x < 0:
x = 5
if y > display_height:
y = 795
if y < 0:
y = 5
crosshair(mousex,mousey)
#blockguy(blockguyX, blockguyY, blockguyW, blockguyH, )
pygame.display.update()
clock.tick(60)
block_game_loop()
pygame.quit()
quit()
Code is pretty janked together because I don't really know wtf im doing, but here's how the thing works so far
Youtube video: https://www.youtube.com/watch?v=zShWAm4pSx8&feature=youtu.be
Take a look at this rotate function (read the comments).
import math
import pygame
pygame.init()
gray = (30, 30, 30)
display_width, display_height = (1280, 800)
gameDisplay = pygame.display.set_mode((display_width, display_height))
clock = pygame.time.Clock()
slayerImg = pygame.Surface((104, 84), pygame.SRCALPHA)
pygame.draw.polygon(slayerImg, (0, 120, 250), [(1, 1), (103, 42), (1, 83)])
def rotate(x, y, mouse_pos, image):
# Calculate x and y distances to the mouse pos.
run, rise = (mouse_pos[0]-x, mouse_pos[1]-y)
# Pass the rise and run to atan2 (in this order)
# and convert the angle to degrees.
angle = math.degrees(math.atan2(rise, run))
# Rotate the image (use the negative angle).
rotimage = pygame.transform.rotate(image, -angle)
rect = rotimage.get_rect(center=(x, y))
return rotimage, rect
def block_game_loop():
x = display_width * 0.45
y = display_height * 0.8
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
return
pressed = pygame.key.get_pressed()
if pressed[pygame.K_s]: y += 7
if pressed[pygame.K_w]: y -= 7
if pressed[pygame.K_a]: x -= 7
if pressed[pygame.K_d]: x += 7
mousex, mousey = pygame.mouse.get_pos()
# Boundaries
if x > display_width:
x = 1275
if x < 0:
x = 5
if y > display_height:
y = 795
if y < 0:
y = 5
gameDisplay.fill(gray)
rotimage, rect = rotate(x, y, (mousex, mousey), slayerImg)
gameDisplay.blit(rotimage, rect)
pygame.display.update()
clock.tick(60)
block_game_loop()
pygame.quit()
Addendum: Here's an example that shows you how you can shoot rotated bullets. You need some basic trigonometry knowledge, because you need to calculate the x- and y-velocity with math.cos and math.sin. That will give you a unit vector (with length 1) that you need to scale to the desired speed. Now you need to put the velocity list together with a rect, an extra position list and the rotated image into a list that represents a bullet object. To update the rect position you first have to add the velocity to the pos and then assign the pos to the rect center (you have to do it that way, because rects can only have ints as the x and y position).
I recommend to use pygame.math.Vector2s and pygame sprites and sprite groups instead of lists as you can see in the linked answer, because that's a lot nicer to read. You still need to add code to remove bullets, which would also be simpler to implement with sprites and sprite groups.
import math
import pygame as pg
from pygame.math import Vector2
pg.init()
screen = pg.display.set_mode((640, 480))
FONT = pg.font.Font(None, 24)
BLACK = pg.Color('black')
BULLET_IMAGE = pg.Surface((20, 11), pg.SRCALPHA)
pg.draw.polygon(BULLET_IMAGE, pg.Color('grey11'), [(0, 0), (20, 5), (0, 11)])
def update_bullets(bullets):
"""Add the velocity to the pos then assign pos to the rect center."""
for bullet_rect, pos, velocity, _ in bullets:
pos[0] += velocity[0]
pos[1] += velocity[1]
bullet_rect.center = pos
def draw_bullets(bullets, screen):
for bullet_rect, pos, _, image in bullets:
screen.blit(image, bullet_rect)
pg.draw.rect(screen, (200, 140, 0), bullet_rect, 1)
def main():
clock = pg.time.Clock()
# The cannon image and rect.
cannon_img = pg.Surface((60, 22), pg.SRCALPHA)
pg.draw.rect(cannon_img, pg.Color('grey19'), [0, 0, 35, 22])
pg.draw.rect(cannon_img, pg.Color('grey19'), [35, 6, 35, 10])
orig_cannon_img = cannon_img # Store orig image to preserve quality.
cannon = cannon_img.get_rect(center=(320, 240))
angle = 0 # Angle of the cannon.
# Add bullets to this list. Bullets will also be lists
# consisting of a pygame.Rect, the velocity and the image.
bullets = []
bullet_speed = 5
playing = True
while playing:
for event in pg.event.get():
if event.type == pg.QUIT:
playing = False
elif event.type == pg.MOUSEBUTTONDOWN:
# Left button fires a bullet from cannon center with
# current angle. Add the bullet to the bullets list.
if event.button == 1:
# Use cosine and sine to calculate the x and y
# velocity. Scale them by the desired speed.
velocity = (math.cos(math.radians(angle)) * bullet_speed,
math.sin(math.radians(angle)) * bullet_speed)
img = pg.transform.rotate(BULLET_IMAGE, -angle)
bullet_rect = img.get_rect(center=cannon.center)
# The extra pos list is needed because the pygame.Rect
# can only have ints as the x and y value. We still
# need the rect for collision detection.
pos = list(bullet_rect.center)
bullet = [bullet_rect, pos, velocity, img]
bullets.append(bullet)
update_bullets(bullets)
# Find angle to target (mouse pos).
x, y = Vector2(pg.mouse.get_pos()) - cannon.center
angle = math.degrees(math.atan2(y, x))
# Rotate the cannon image.
cannon_img = pg.transform.rotate(orig_cannon_img, -angle)
cannon = cannon_img.get_rect(center=cannon.center)
# Draw
screen.fill(pg.Color('darkseagreen4'))
draw_bullets(bullets, screen)
screen.blit(cannon_img, cannon)
txt = FONT.render('angle {:.1f}'.format(angle), True, BLACK)
screen.blit(txt, (10, 10))
pg.draw.line(
screen, pg.Color(150, 60, 20),
cannon.center, pg.mouse.get_pos(), 2)
pg.display.update()
clock.tick(30)
if __name__ == '__main__':
main()
pg.quit()
Here Big Fish, this is my code for that problem:
import math
sprite = sprite_new("spr_plasma_blast")
targetx = 0
targety = 0
m = 0
time = 0
speed = 8
if time == 0:
m = (targety - y)/(targetx - x)
xSpeed = math.sqrt((speed*speed)/((m*m) + 1))
if targetx < x:
xSpeed = -xSpeed
x = x + xSpeed
y = y + xSpeed*m
time = time + 1

Categories