Pygame, my circle turns to a rect after I stored it in a variable, how do I prevent that from happening - pygame

I need to store a Circle in a variable but after I've done that it has turned into a rect
circle_1 = pygame.draw.circle(screen, (0, 0, 0), (300, 300), 30)
Print(circle_1)
the print returns
<rect(270, 270, 60, 60)>
but I can't work with that.
My circle is predefined but it won't show it on the canvas, here is an example of the problem
> import pygame, sys
>
>
> pygame.init() screen = pygame.display.set_mode((600, 600))
> predefined_circle = pygame.draw.circle(screen,(0, 0, 0),(300, 300), 30)
>
> def update():
> screen.fill((200, 0, 0))
> while 1:
> for event in pygame.event.get():
> if event.type == pygame.QUIT: sys.exit()
> # It shows my circle if I dirctly tip pygame.draw.circle(screen,(0, 0, 0),(300, 300), 30) into it
> predefined_circle
> pygame.display.update()
>
> update()
So that you can better relate to what I'm trying to achieve here is the code of what I'm doing but it is not necessary to read as I've already tried to explain it as best as I can above.
Please note the comments should explain everything that the block of code below it is doing.
# Creating the canvas which can paint any wanted Object from another class
class Canvas:
# Initialising the screen and setting all needed variables
def __init__(self, painting):
pygame.init()
self.screen_size = (600, 600)
self.background = (25, 255, 255)
self.screen = pygame.display.set_mode(self.screen_size)
self.paint = painting
# Let the user set the name of the canvas
def set_screen_name(self):
return self.screen
# Draw the everything you want to
def update(self):
# Paint the canvas
self.screen.fill(self.background)
# Make the game be quittable
while 1:
for event in pygame.event.get():
if event.type == pygame.QUIT: sys.exit()
# Draw the defined Circle and then update the Canvas
# it only draws a circle if directly tip pygame.draw.circle(surface, color, position, radius)
self.paint
pygame.display.update()
# Draw any circle you like
class Cir:
# Get all the required Information's to Draw a circle
def __init__(self, canvas, what_color, position, radius, line=0):
self.can = canvas
self.color = what_color
self.pos = position
self.r = radius
self.line = line
self.cir = None
# Create the circle with the acquired Information's
def create(self):
self.cir = pygame.draw.circle(self.can, self.color, self.pos, self.r, self.line)
return self.cir
# So far there is no Surface for the Cir class
# And there is no Object that cloud be painted for the Canvas class
# I initialise a canvas instance without anything that needs to be painted
get_surface = Canvas(None)
# Now I can access set_screen_name from the Canvas class and give the surface a name
# Which the Cir class can now use as a surface
screen = get_surface.set_screen_name()
c1 = pygame.draw.circle(screen, (0,0,0), (300, 300), 30)
print(c1)
# I'm initialising a circle
init_circle = Cir(screen, (0, 255, 0), (300, 300), 30)
# Create the initialised circle
circle_1 = init_circle.create()
# Give the Canvas class the created circle
paint = Canvas(circle_1)
# Draw the circle
paint.update()

My circle turns to a rect.
Actually, no, it doesn't. As per the documentation for those drawing functions, the intent of the calls is to draw something immediately, not to give you an object you can draw later:
Draw several simple shapes to a Surface.
From analysis of your question, it sounds like you believe that you are storing the act of drawing the circle so that it can be done later. That is not the case. Instead, what you are doing is actually drawing the circle and saving the result of that drawing action - evaluating the result later on will not actually draw, or redraw, the circle.
So, if the draw function is not returning something for later drawing, what is it returning? That can also be found in the above-mentioned documentation:
The functions return a rectangle representing the bounding area of changed pixels.
In other words, it's telling you the smallest rectangle that was changed by the drawing action - this will be a square with sides the same length as the diameter and centered around the same point.
Obviously, the authors of PyGame thought this information may be handy for some purpose, just not the purpose of redrawing the circle :-)
One way to do what you're trying to achieve would be to simply have a function to draw the "predefined" circle and call that instead of trying to evaluate the rectangle returned from a previous call.

Related

How to remove surface from window while the game is running

I'm making this space invaders type game with pygame and I have this issue where, enemies keep spawning while the time is stopped. I need a fix that removes those enemies from the window ( I use .blit to display my surfaces) right after the time is started again.
E.G:
def draw_on_window(enemy):
window.blit(enemy)
def main():
# Command that clears the window of "Enemy surfaces" somehow?? :
# window.remove(enemy) ??
run = True
while run:
draw_on_window(blue_enemy)
#game
In pygame you don't remove something from a surface like the window. window.blit(image) draws an image to the window surface.
To clear everything you have to fill the window black.
In pygame you do the following things your main loop normally:
fill the screen black window.fill((0, 0, 0))
draw everything again
handle all events (eg. QUIT, MOUSEBUTTONDOWN...)
let the clock tick at a specified frame rate
enemy_coords = [(5, 9), (3, 4)] # just an example
clock = pygame.time.Clock()
max_fps = 60
while run:
window.fill((0, 0, 0)) # fill the window with black
for coord in enemy_coords:
window.blit(enemy_image, coord)
do_events...
pygame.display.update()
clock.tick(max_fps)
All you need to do is manage the enemies in a list and remove the enemies from the list (see How to remove items from a list while iterating?). Draw only the enemies that are in the list.
e.g.: represent the enemies by pygame.Rect objects and remove them when they are out of the window:
def draw_on_window(enemies):
for enemy_rect in enemies:
window.blit(enemy_image, enemy_rect)
def main():
enemies = []
enemies.append(pygame.Rect(x1, y1, w, h))
enemies.append(pygame.Rect(x2, y2, w, h))
run = True
while run:
# [...]
for enemy in enemies[:]:
if not enemies.colliderect(window.get_rect())
enemies.remove(enemy)
# [...]
draw_on_window(enemies)
pygame.dispaly.update()
You can remove all items from a list with the clear() method:
enemies.clear()

Draw detection radius around square sprite [duplicate]

This question already has answers here:
How do I detect collision in pygame?
(5 answers)
Pygame collision with masks
(1 answer)
Closed 6 months ago.
I would like to draw sprites with 2 features: a base image (let's say a square image that I pass as argument) and a togglable view of their circular detection radius, for debug purposes and checking that collisions with a circular mask actually work.
Note: the main question is not about to detect collisions as in How do I detect collision in pygame?, but rather can a sprite have several surfaces attached to it and why is my draw_mask_outline function returning croppped circles? (see attached screenshots)
Here's an example of my class:
class Robot(pygame.sprite.Sprite):
def __init__(self, x, y, image, detection_radius, mouse_group, *groups):
super().__init__(*groups)
# extra image added for changing color if mouse is detected within
# detection radius
# I have used the mouse as a debug tool but in the end the Robot
# should have no knowledge of the other groups positions (and should
# not be able to compute a euclidean distance from a sprite group) but
# simply detect if a sprite of a certain sprite group has entered its
# radius
other_img = pygame.Surface((30, 30))
other_img.fill('black')
self.img_idx = 0
self.images = [image, other_img]
self.image = self.images[self.img_idx]
self.pos = pygame.Vector2(x, y)
self.rect = image.get_rect(center=self.pos)
self.detection_radius = detection_radius
self.mask_surface = pygame.Surface((detection_radius * 2, detection_radius * 2), pygame.SRCALPHA)
self.detection_mask = self.make_circle(self.notice_radius)
self.mask = pygame.mask.from_surface(
self.make_circle_surface(detection_radius))
sekf.mouse_group = mouse_group
def draw_mask_outline(self):
# this function should draw the outline of the circular detection mask
display_surf = pygame.display.get_surface()
pts = self.mask.outline()
for pt in pts:
x = pt[0] + self.rect.centerx
y = pt[1] + self.rect.centery
pygame.draw.circle(display_surf, 'black', (x, y), 1)
def make_circle_surface(self, radius):
# I used this function to generate the circular surface
mask_surf = pygame.Surface((radius * 2, radius * 2), pygame.SRCALPHA)
pygame.draw.circle(mask_surf, (0, 0, 0), (self.rect.centerx, self.rect.centery), radius)
return mask_surf
def collision(self):
# collision detection function, if mouse inside detection radius do something
if pygame.sprite.spritecollide(self, self.mouse_group, False, pygame.sprite.collide_mask):
pygame.draw.circle(pygame.display.get_surface(), (0, 0, 0), (self.rect.centerx, self.rect.centery), self.notice_radius, 1)
self.img_idx = 1
self.image = self.images[self.img_idx]
else:
self.img_idx = 0
self.image = self.images[self.img_idx]
def update(self):
# the overall update function where all the robot logic will happen
self.draw_mask_outline()
As fas as I've understood how the lib works, I should create a surface around the original image, big enough to fit the circle but this does not seem compatible with already having a self.image. Then I should draw a circle on that new surface.
Will this new surface follow the sprite when it moves too?
I managed to draw the circle inside the original image but this is not what is expected since the starting is smaller than the circle. In some cases, I also managed to generate surfaces, transform them to mask, then using the mask to generate an outline (hence the self.mask) but these outlines are always drawn far away from the sprite positions, are incomplete or completely missing...
Here are some screenshots of the current version and what I could achieve.
When the mouse is outside the detection ring, outlines are drawn with a severe offset, are sometimes incomplete and some robots have no outline.
Displaced and incomplete outlines
When the mouse is inside, the collision function draws the correct circle (but this is not the mask).
Collisions debug ring
Here are links to some solutions that I have tested and that did not work (if I've understood them correctly that is...):
pygame: mask non-image type surfaces
Pygame: Why i can't draw a circle sprite
how to create a circular sprite in pygame
OOP Pygame Circle
Ultimately the goal is to have the robots roam around and detect sprites from another group of robots that enter their detection radius.
Any help would be very much appreciated!

How do I draw an object on the screen inside of a sprite class?

So I'm trying to draw a rectangle with another rectangle on top, using Sprites. I made a class for the player and did some basic setup, but when I tried to blit the second rectangle on top, it didn't work. I did some testing and found out that I can't even draw lines or rectangles from inside this player class.
Here's my basic testing code:
import pygame as pg
pg.init()
width, height = 800, 800
screen = pg.display.set_mode((width, height))
run = True
class Player(pg.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pg.image.load("test_image.png")
self.rect = self.image.get_rect()
def update(self):
pg.draw.rect(screen, [0, 255, 0], (200, 200, 100, 50))
print("test")
def draw_window():
screen.fill((255, 255, 255))
playerGroup.draw(screen)
pg.display.update()
playerGroup = pg.sprite.GroupSingle()
playerGroup.add(Player())
while run:
for event in pg.event.get():
if event.type == pg.QUIT:
run = False
playerGroup.update()
draw_window()
This is what i get: image
The blue thing is the player image that gets drawn normally in the top left corner. The rect that i'm trying to draw inside the update() method however is nowhere to be seen, even though i can clearly see the method gets called with the print("test"). This isn't just true for pg.draw() but also for surface.blit()
Why is this and how do i fix it?
screen.fill((255, 255, 255)) fills the entire display with a white color. Anything previously drawn will be lost. You must call playerGroup.update() after clearing the display and before updating the display. e.g.:
def draw_window():
screen.fill((255, 255, 255))
playerGroup.update()
playerGroup.draw(screen)
pg.display.update()

Move a sprite, but cannot delete the precedent image of it [duplicate]

I'm building a pong game trying to get better at programming but Im having trouble moving the ball. When the move_right method is called the ellipse stretches to the right instead of moving to the right. I've tried putting the ball variable in the init method but that just makes it not move at all even though the variables should be changing on account of the move_right method. I have also tried setting the x and y positions as parameters in the Ball class,but that just stretches it also.
I don't understand why when I run the following code the ball I'm trying to move stretches to the right instead of moves to the right. Can someone explain why this is happening? I have tried everything I can think of but i can't get it to do what I want.
import pygame,sys
import random
class Ball:
def __init__(self):
self.size = 30
self.color = light_grey
self.x_pos = width/2 -15
self.y_pos = height/2 -15
self.speed = 1
#self.ball = pygame.Rect(self.x_pos, self.y_pos,self.size,self.size)
def draw_ball(self):
ball = pygame.Rect(self.x_pos, self.y_pos,self.size,self.size)
pygame.draw.ellipse(screen,self.color,ball)
def move_right(self):
self.x_pos += self.speed
class Player:
def __init__(self,x_pos,y_pos,width,height):
self.x_pos = x_pos
self.y_pos = y_pos
self.width = width
self.height = height
self.color = light_grey
def draw_player(self):
player = pygame.Rect(self.x_pos,self.y_pos,self.width,self.height)
pygame.draw.rect(screen,self.color,player)
class Main:
def __init__(self):
self.ball=Ball()
self.player=Player(width-20,height/2 -70,10,140)
self.opponent= Player(10,height/2-70,10,140)
def draw_elements(self):
self.ball.draw_ball()
self.player.draw_player()
self.opponent.draw_player()
def move_ball(self):
self.ball.move_right()
pygame.init()
size = 30
clock = pygame.time.Clock()
pygame.display.set_caption("Pong")
width = 1000
height = 600
screen = pygame.display.set_mode((width,height))
bg_color = pygame.Color('grey12')
light_grey = (200,200,200)
main = Main()
#ball = pygame.Rect(main.ball.x_pos, main.ball.y_pos,main.ball.size,main.ball.size)
#player = pygame.Rect(width-20,height/2 -70,10,140)
#opponent = pygame.Rect(10,height/2-70,10,140)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
#ball = pygame.Rect(main.ball.x_pos, main.ball.y_pos,main.ball.size,main.ball.size)
#pygame.draw.rect(screen,light_grey,player)
#pygame.draw.rect(screen,light_grey,opponent)
#pygame.draw.ellipse(screen,light_grey,ball)
main.draw_elements()
main.move_ball()
main.ball.x_pos += main.ball.speed
pygame.display.flip()
clock.tick(60)
You have to clear the display in every frame with pygame.Surface.fill:
while True:
# [...]
screen.fill(0) # <---
main.draw_elements()
main.move_ball()
main.ball.x_pos += main.ball.speed
pygame.display.flip()
# [...]
Everything that is drawn is drawn on the target surface. The entire scene is redraw in each frame. Therefore the display needs to be cleared at the begin of every frame in the application loop. The typical PyGame application loop has to:
handle the events by either pygame.event.pump() or pygame.event.get().
update the game states and positions of objects dependent on the input events and time (respectively frames)
clear the entire display or draw the background
draw the entire scene (blit all the objects)
update the display by either pygame.display.update() or pygame.display.flip()

Creating walls from surface data in Pygame

I'm new to the python language and pygame, what I'm trying to do is create a function which will convert the data from a 2D black and white image into areas where the player can or cannot move. In other words: I want to take a maze that I've drawn in a graphics program and use pygame to interpolate black and white images so that a player class object may move wherever the surface contains bytes of (255, 255, 255) and cannot move where the surface contains bytes of (0, 0, 0) First I need to be able to get the data. What I have so far is.
def GetMaze(M):
Get = M.get_view('2')
Sep = Get.raw
In this function M represents a black and white bitmap of a maze ('Maze.bmp'). I know what I have so far will grab the bytes in an unstructured block of bytes. What I need this function to do is iterate the bytes into lists of floors where the bytes equal (255, 255, 255) and walls where they equal (0, 0, 0) but I can't figure out how to get it to that point.
Using code found here http://www.petercollingridge.co.uk/book/export/html/550:
import sys, pygame
from pygame.locals import *
screen = pygame.display.set_mode((200,200))
imgsurf = pygame.Surface((200,200))
def init():
global mazearray
mazeimg = pygame.image.load("maze.bmp")
mazearray = pygame.surfarray.array3d(mazeimg)
def update():
global mazearray
screen.fill((0,0,0))
imgsurf.fill((255,255,255))
for x in range(200):
for y in range(200):
if mazearray[x][y][0] == 0 and mazearray[x][y][1] == 0 and mazearray[x][y][2] == 0:
pygame.draw.rect(imgsurf, (0,0,0), (x,y,2,2))
screen.blit(imgsurf, (0,0))
pygame.display.flip()
if __name__=="__main__":
init()
update()
while True:
events = pygame.event.get()
for event in events:
if event.type == pygame.QUIT:
sys.exit()
keys=pygame.key.get_pressed()
if keys[K_q]:
sys.exit()
Obviously you will need to change the sizes for your maze. Also, I hand drew the maze in GIMP, so the lines were wonky, hence the maze it drew was wonky.