Make pygame draw cubes wherever the user clicks - python

I want to make a kind of "level editor" as an exercise.
I've got this code:
import pygame
running = True
pygame.init()
screen = pygame.display.set_mode((800, 500))
class Cube:
def update(self):
self.cx, self.cy = pygame.mouse.get_pos()
self.square = pygame.Rect(self.cx, self.cy, 50, 50)
def draw(self):
pygame.draw.rect(screen, (255, 0, 0), self.square)
cube = Cube()
drawing_cube = False
drawing_cube2 = False
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.MOUSEBUTTONDOWN:
cube.update()
drawing_cube = True
screen.fill((0, 255, 0))
if drawing_cube:
cube.draw()
pygame.display.flip()
pygame.quit()
However, it doesn't create multiple squares; it just re-locates the already created square.

It's doing exactly what you told it to do: update the one and only Cube object in the game, and then redraw the screen and that one object. If you want multiple cubes, you have to create each one. Perhaps something like this:
cube_list = [] # List of all cubes
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.MOUSEBUTTONDOWN: # Make a new cube
cube = Cube()
cube.update()
cube_list.append(cube)
drawing_cube = True
screen.fill((0, 255, 0))
if drawing_cube: #New cube made; draw them all.
for cube in cube_list:
cube.draw()
pygame.display.flip()
Now, as an exercise for the student, can you simplify this so that it merely adds the most recent cube to the existing screen, instead of redrawing the entire game area for each new cube?

Thanks, Prune! but for anyone that is reading this after me, here is the full code:
import pygame
cube_list = []
running = True
pygame.init()
screen = pygame.display.set_mode((800, 500))
class Cube:
def update(self):
self.cx, self.cy = pygame.mouse.get_pos()
self.square = pygame.Rect(self.cx, self.cy, 50, 50)
def draw(self):
pygame.draw.rect(screen, (255, 0, 0), self.square)
cube = Cube()
drawing_cube = False
drawing_cube2 = False
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.MOUSEBUTTONDOWN: # Make a new cube
cube = Cube()
cube.update()
cube_list.append(cube)
drawing_cube = True
screen.fill((0, 255, 0))
if drawing_cube: #New cube made; draw them all.
for cube in cube_list:
cube.draw()
pygame.display.flip()
pygame.quit()
Cheers!
P.S I've marked your answer as accepted

Related

Why the formated variable is not being updated on the screen and how to fix it

The key space adds +2 to the variable player.mana, when pressed i see the variable incrementing on the console, but the formated string text on the screen don't. How to show it correctly on the screen?
import pygame
import sys
pygame.init()
clock = pygame.time.Clock()
font = pygame.font.SysFont('Arial', 30)
screen = pygame.display.set_mode((640, 480))
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
class Players:
def __init__(self, mana):
self.mana = mana
player = Players(0)
text_player_mana = font.render(f'Mana: {player.mana}', True, WHITE)
running = True
while running:
screen.fill(BLACK)
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
keys = pygame.key.get_pressed()
if event.type == pygame.KEYDOWN:
if keys[pygame.K_ESCAPE]:
running = False
if keys[pygame.K_SPACE]:
player.mana += 2
screen.blit(text_player_mana, (200, 200))
print(player.mana)
pygame.display.update()
pygame.quit()
sys.exit()
The variable and the rendered Surface are not tied. The Surface does not magically change when you change the variable. You need to re-render the Surface when the variable changes:
text_player_mana = font.render(f'Mana: {player.mana}', True, WHITE)
running = True
while running:
screen.fill(BLACK)
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
keys = pygame.key.get_pressed()
if event.type == pygame.KEYDOWN:
if keys[pygame.K_ESCAPE]:
running = False
if keys[pygame.K_SPACE]:
player.mana += 2
# player.mana has changed, so text_player_mana needs to be rendered
text_player_mana = font.render(f'Mana: {player.mana}', True, WHITE)
screen.blit(text_player_mana, (200, 200))
print(player.mana)
pygame.display.update()

Mouse coordinates not changing - python

I am trying to build a simple game, but I'm having trouble with the mouse as it's doesn't show that it's coordinates are changing.
Here's my code:
import pygame
pygame.init()
# Colors
white = (255, 255, 255)
black = (0, 0, 0)
blue = (0, 0, 255)
# Game Screen Dimensions
game_layout_length = 500
game_layout_width = 500
# Mouse Positions
pos = pygame.mouse.get_pos()
# Character Attributes
character_length = 10
character_width = 10
game_screen = pygame.display.set_mode((game_layout_width, game_layout_length))
game_close = False
game_lost = False
while not game_close:
while not game_lost:
for event in pygame.event.get():
if event.type == pygame.QUIT:
game_close = True
game_lost = True
if event.type == pygame.MOUSEBUTTONDOWN:
print(pos)
game_screen.fill(black)
pygame.display.update()
pygame.quit()
quit()
This is the result after clicking on multiple different parts of the screen:
pygame 2.1.2 (SDL 2.0.18, Python 3.8.2)
Hello from the pygame community. https://www.pygame.org/contribute.html
(0, 0)
(0, 0)
(0, 0)
(0, 0)
(0, 0)
Process finished with exit code 0
Also, is there a way to get a rectangle to follow my mouse? I tried doing pygame.mouse.rect(game_screen, blue, [pos, character_length, character_width]), but that just crashed my program.
So, the problem is that you are not refreshing the mouse position, because is not in the loop. All you need to do is to put the pos variable inside the loop.
Here is how should you do it:
if event.type == pygame.MOUSEBUTTONDOWN:
pos = pygame.mouse.get_pos()
print(pos)
game_screen.fill(black)
pygame.display.update()
This is the answer for your second question:
So, the issue there is that after you draw the rectangle, you are filling the screen with black, so, all the rectangles are covered up with the color black.
All you need to do is to delete game_screen.fill(black) and it will work.
Thanks, I successfully set a variable to my x and y coordinates but when I try to make something (like a rectangle) follow my mouse, it doesn't work or even show up. The program just executes a plain black screen.
Here's my code:
import pygame
pygame.init()
# Colors
white = (255, 255, 255)
black = (0, 0, 0)
blue = (0, 0, 255)
# Game Screen Dimensions
game_layout_length = 500
game_layout_width = 500
# Character Attributes
character_length = 10
character_width = 10
game_screen = pygame.display.set_mode((game_layout_width, game_layout_length))
game_close = False
game_lost = False
while not game_close:
while not game_lost:
for event in pygame.event.get():
if event.type == pygame.QUIT:
game_close = True
game_lost = True
if event.type == pygame.MOUSEBUTTONDOWN:
pos = pygame.mouse.get_pos()
character_x = pos[0]
character_y = pos[1]
pygame.draw.rect(game_screen, blue, [character_x, character_y, character_length, character_width])
game_screen.fill(black)
pygame.display.update()
pygame.quit()
quit()
I used a debug line to see if it was an issue with the variables, but those seemed to be working just fine, so I'm unsure where the issue is.

Trouble with pygame rendering

I've got an issue where pygame is only rendering ablack screen with a strange glitchy green line on it.
I'm following a tutorial on RealPython.com and everything went smoothly with the first attempt. Once I did the second attempt this is what renders, which is clearly wrong.
#sidescrolling air-shooter
import pygame
#import pygame locals
from pygame.locals import(
K_UP,
K_DOWN,
K_LEFT,
K_RIGHT,
K_ESCAPE,
KEYDOWN,
QUIT,
)
#constants for screen width/height
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
class Player(pygame.sprite.Sprite):
def __init__(self):
super(Player, self).__init__()
self.surf = pygame.Surface((75,25))
self.surf.fill((255, 255,255))
self.rect = self.surf.get_rect()
pygame.init()
#create screen
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
# Instantiate player
player = Player()
#keep the game running!
running = True
#loop time!
while running:
#look at all the events
for event in pygame.event.get():
#did the user hit a key?
if event.type == KEYDOWN:
#Was it escape? uh oh we gotta stop
if event.key == K_ESCAPE:
running = False
elif event.type ==QUIT:
running = False
#fill screen with white
screen.fill((255, 255, 255))
# Create a surface and pass in a tuple containing legth and width
surf = pygame.Surface((50, 50))
# Give the surface a color to separate it from the Background
surf.fill((0, 0, 0))
rect = surf.get_rect()
#This line says "Draw surf onto the screen at the center"
screen.blit(surf, (SCREEN_WIDTH/2, SCREEN_HEIGHT/2))
#Draw the player on the screen
screen.blit(player.surf, (SCREEN_WIDTH/2, SCREEN_HEIGHT/2))
pygame.display.flip()
pygame.quit()
I've even checked the source code for their project and it seems to match up.
It is a matter of Indentation. You need to draw the scene and update the display in the application loop instead of after the application loop:
#loop time!
while running:
#look at all the events
for event in pygame.event.get():
#did the user hit a key?
if event.type == KEYDOWN:
#Was it escape? uh oh we gotta stop
if event.key == K_ESCAPE:
running = False
elif event.type ==QUIT:
running = False
#-->| INDENTATION
#fill screen with white
screen.fill((255, 255, 255))
# Create a surface and pass in a tuple containing legth and width
surf = pygame.Surface((50, 50))
# Give the surface a color to separate it from the Background
surf.fill((0, 0, 0))
rect = surf.get_rect()
#This line says "Draw surf onto the screen at the center"
screen.blit(surf, (SCREEN_WIDTH/2, SCREEN_HEIGHT/2))
#Draw the player on the screen
screen.blit(player.surf, (SCREEN_WIDTH/2, SCREEN_HEIGHT/2))
pygame.display.flip()
pygame.quit()

Clicking on image will not work

I am using pygame to create a fully customizable enigma machine in python. One thing I decided to implement early is a help function. When I was testing this, nothing would show up on the console. Here is the code for the image clicking (not all of the code)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
pygame.display.quit()
if event.type == pygame.MOUSEBUTTONDOWN:
x, y = event.pos
if img.get_rect().collidepoint(x, y):
print('test')
How do I make this work? All help would be useful.
When you call img.get_rect() you create a pygame.Rect with the size of the image/surface and the default topleft coordinates (0, 0), i.e. your rect is positioned at the top left corner of your screen. I suggest creating a rect instance for the img at the beginning of the program and use it as the blit position and for the collision detection. You can pass the topleft, center, x, y, etc., coordinates directly as an argument to get_rect: rect = img.get_rect(topleft=(200, 300)).
import pygame as pg
pg.init()
screen = pg.display.set_mode((640, 480))
clock = pg.time.Clock()
BG_COLOR = pg.Color('gray12')
img = pg.Surface((100, 50))
img.fill((0, 100, 200))
# Create a pygame.Rect with the size of the surface and
# the `topleft` coordinates (200, 300).
rect = img.get_rect(topleft=(200, 300))
# You could also set the coords afterwards.
# rect.topleft = (200, 300)
# rect.center = (250, 325)
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
elif event.type == pg.MOUSEBUTTONDOWN:
if rect.collidepoint(event.pos):
print('test')
screen.fill(BG_COLOR)
# Blit the image/surface at the rect.topleft coords.
screen.blit(img, rect)
pg.display.flip()
clock.tick(60)
pg.quit()

get_rect() over an image gets shifted in pygame

I'm trying to do a drag-and-drop mechanic in pygame and I'm being partly successful (thanks to answers to questions like this one and tutorials like this other one). The mechanic I'm using goes as follows: I update in every loop the position of the image once the event of pressing the button is detected (and only if the mouse is over the image). To do so, I created a rectangle object by just calling image.get_rect(), but it seems that this rectangle is shifted, with the center of the image laying in the bottom right of the rectangle. I annex both the code an the result:
import pygame, sys
from pygame.locals import *
FPS = 60
fpsClock = pygame.time.Clock()
def main():
pygame.init()
DS = pygame.display.set_mode((400, 400), 0, 32)
pygame.display.set_caption('Drag-n-drop that cat')
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
catImg = pygame.image.load('cat.png') # I load the image
catImgRectObj = catImg.get_rect() # I create the rect object
catx = 200
caty = 200
catImgRectObj.center = [catx, caty]
IsMousePressed = False
while True:
lastPos = catImgRectObj.center
DS.fill(WHITE)
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
elif event.type == MOUSEBUTTONDOWN:
IsMousePressed = True
elif event.type == MOUSEBUTTONUP:
IsMousePressed = False
if IsMousePressed and isMouseOverObj(catImgRectObj):
catImgRectObj.center = pygame.mouse.get_pos() #I update the center
else:
catImgRectObj.center = lastPos
pygame.draw.rect(DS, BLACK, catImgRectObj) #draw the rect object
DS.blit(catImg, catImgRectObj.center) #draw the cat.
pygame.display.update()
fpsClock.tick(FPS)
def isMouseOverObj(Obj):
return Obj.collidepoint(pygame.mouse.get_pos())
if __name__ == '__main__':
main()
Use
DS.blit(catImg, catImgRectObj)
instead of
DS.blit(catImg, catImgRectObj.center)
to draw the cat.
The catImgRectObj rect already describes where the cat image is, and if you use catImgRectObj.center to blit it on the screen, but shift its top left corner to the center of the desired area.
Also, I would use something like this:
import pygame, sys
from pygame.locals import *
FPS = 60
fpsClock = pygame.time.Clock()
def main():
pygame.init()
DS = pygame.display.set_mode((400, 400), 0, 32)
pygame.display.set_caption('Drag-n-drop that cat')
catImg = pygame.image.load('cat.png').convert_alpha()
catMask = pygame.mask.from_surface(catImg)
catImgRectObj = catImg.get_rect(center=(200, 200))
IsMousePressed = False
while True:
DS.fill(pygame.color.THECOLORS['white'])
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
elif event.type == MOUSEBUTTONDOWN and isMouseOverObj(catMask, catImgRectObj):
IsMousePressed = True
elif event.type == MOUSEBUTTONUP:
IsMousePressed = False
elif event.type == MOUSEMOTION and IsMousePressed:
catImgRectObj.move_ip(event.rel)
DS.blit(catImg, catImgRectObj)
pygame.display.update()
fpsClock.tick(FPS)
def isMouseOverObj(mask, rect):
mouse_pos = pygame.mouse.get_pos()
rel_pos = (mouse_pos[0] - rect.left, mouse_pos[1] - rect.top)
return rect.collidepoint(mouse_pos) and mask.get_at(rel_pos)
if __name__ == '__main__':
main()
to make the collision detection pixel perfect, simplify the code a bit, and to prevent the jumping once you click on the cat.

Categories

Resources