Two arcs while using Pygame tranform.flip - pygame

This is my first real code project. I am trying to create a Crash gambling project for a math project at School.
My current problem is that when I flip my arc it draws 2 separate arcs. I don't know why it does this but if anyone can help it would be much appreciated.
Here is my code:
import pygame
from math import sin, cos, radians
grid = pygame.image.load('grid3.jpg')
wn = pygame.display.set_mode((600, 600))
wn2 = pygame.display.set_mode((600, 600))
clock = pygame.time.Clock()
r = 600
a = 0
b = 0
def x_y(r, i, a, b):
return (int(r * cos(radians(i)) + a), int(r * sin(radians(i)) + b))
for i in range(0, 90, 1):
clock.tick(30)
pygame.draw.line(wn, (255, 255, 255), x_y(r, i, a, b), x_y(r, i+1, a, b), 10)
wn2.blit(wn, (0,0))
wn.blit(grid,(0,0))
wn2.blit(pygame.transform.rotate(wn2, -90), (0, 0))
wn = pygame.transform.flip(wn2, True, False)
pygame.display.update()

When you draw onto a display Surface, it won't remove the other existing elements. Take this code as an example:
import pygame
from pygame.locals import *
screen = pygame.display.set_mode((320, 240))
pygame.draw.rect(screen, (0, 255, 0), Rect(50, 20, 100, 100))
pygame.draw.rect(screen, (255, 0, 0), Rect(120, 100, 100, 100))
pygame.display.flip() # same as pygame.display.update() (with no arguments)
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
exit()
When executing this, you will see 2 squares appear, the second one being drawn not preventing the first one from still appearing, even though you updated the screen.
As a display surface is also a transparent surface, blitting a display Surface onto another will not erase its content.
To do that, you can call the Surface.fill function.
A few optional (but some I recommend) improvements to your code:
Implement a game loop to your game/simulation to namely get the user events and preventing your game from constantly crashing after a while
Use pygame.display.flip() instead of pygame.display.update() (the latter is useful when you want to only update part of the screen)
You are creating 2 screen surfaces, but that's not possible. wn and wn2 are referring to the same surface. Use pygame.Surface for creating other surfaces.
Try to leave the main display surface as it is, and work with other surfaces rather than changing the entire screen by transforming it, as this can be confusing when debugging.
I hope that helped!

Related

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()

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

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.

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.

Pygame - Limiting instances of sprites

I'm using an example regarding sprite generation for a space scroller shootup that I'm developing. By slowly trying to understand how it works, I've managed to get multiple sprites to transverse across the screen. However, there are many sprites that are generated.
So what I'm having trouble with is limiting the initial number of sprites instead of the multitude that the code produces. I thought of using if sprites.__len__ < 10: sprites.add(drone) but when I tried that, it didn't work.
My thinking was that each time it looped, it would check the number of sprites in the group and if it was less then 10, add a sprite to the group until it hit 10. That way if it went off screen or is destroyed, then it would keep doing the check and keeping it constant.
This is the player class:
class Player(pygame.sprite.Sprite):
def __init__(self, *groups):
super(Player, self).__init__(*groups)
self.image = pygame.image.load('player.png')
self.rect = pygame.rect.Rect((screen_width, (random.randrange(0,screen_height))), self.image.get_size())
self.dx = -10
self.pos = random.randrange(0,screen_height)
def update(self):
self.rect.centerx += self.dx
if self.rect.right < 0:
self.kill()
and this is the section regarding the adding of the sprite.
sprites.update()
screen.fill((200, 200, 200))
sprites.draw(screen)
drone = Player()
self.y = random.randrange(0,screen_height)
sprites.add(drone)
pygame.display.flip()
It's probably obvious, but I'm still learning so guidance would be great.
Second question - More of a confirmation of thought. If I don't want the sprite to be half drawn on the bottom of the screen. Do I basically say that if self.rect.bottom > screen_height, then position the sprite # screen_height
Full source: http://pastebin.com/PLRVHtxz
EDIT - I think I've solved it, just need to make the sprites run smoother.
while 1:
clock.tick(40)
numberAlien = 5
for event in pygame.event.get():
if event.type == pygame.QUIT:
return
if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
return
sprites.update()
screen.fill((200, 200, 200))
sprites.draw(screen)
drone = Player()
if sprites.__len__() < numberAlien:
self.y = random.randrange(0,screen_height)
sprites.add(drone)
pygame.display.flip()
You could subclass SpriteGroup, add a new field of the total number of sprites, and check in the add method to see if it can be added.
You shouldn't test check any variables with __.
As for the movement, i believe, you do not see a smooth movement because of clock.tick(40).
It waits for 40ms until it resumes running. You could reduce tick to 10, and tune the dx you change for the sprites, or try a more universal approach.
A call to clock.tick() returns amount of ms since the last call. This will be your time delta. You can then have a global SPEED. The amount of pixels to be moved would be calculated from SPEED * delta.

Most simple algorithm to reverse direction on collision

I teach Computer Programming. For first course students I'm looking for most easy algorithm to change direction on collide (with window frame). This is my actual code:
import sys, pygame
pygame.init()
speed_x = 1
speed_y = 1
black = (0, 0, 0)
width, height = 320, 240
size = ( width, height )
screen = pygame.display.set_mode(size)
display_surface = pygame.display.get_surface()
display_rectangle = display_surface.get_rect()
ball_img = pygame.image.load("data/ball.gif")
ball = ball_img.convert_alpha()
ballrect = ball.get_rect()
while 1:
for event in pygame.event.get():
if event.type == pygame.QUIT: sys.exit()
if not display_rectangle.contains( ballrect.move([speed_x, 0]) ):
speed_x *= -1
else:
ballrect = ballrect.move([speed_x, 0])
if not display_rectangle.contains( ballrect.move([0, speed_y]) ):
speed_y *= -1
else:
ballrect = ballrect.move([0, speed_y])
screen.fill(black)
screen.blit(ball, ballrect)
pygame.display.flip()
pygame.time.delay(30)
This code works fine, my question is if someone know about a easy algorithm or clear algorithm to reverse direction using contains or any other collide pygame test.
Some times I think that an approach is most easy for students and a new clear approach appears.
All suggestions are welcome
Some suggestions? Thanks!
I think your approach is fine. Your code models the velocity of the ball using the 2-vector [speed_x, speed_y]. Bouncing off a (horizontal or vertical) wall involves negating the appropriate component of the vector.
As long as your students have a background in introductory physics (velocity, momentum, collisions) then your code should be understandable.
Your code probably works ok (I haven't tried, but it seems fine), but I have mixed feelings about it. You check the x-movement then the y-movement, which is ok, but the Rect.contains() tests both x and y so it seems a bit redundant to me.
And depending on your students background, it kind of hides what you are doing.
I think I'd like testing everything manually :
if display_rectangle.left<=ballrect.left+speed_x or display_rectangle.right<=ballrect.right+speed_x
speed_x*=-1
if display_rectangle.top<=ballrect.top+speed_y or display_rectangle.bottom<=ballrect.bottom+speed_y
speed_y*=-1
ballrect.move([speed_x, speed_y])
BTW : why is it ballrect and not ball_rect ?