I've recently tried teaching myself Python and I've made a app using Pygame to generate random stats for D and D enemies. 2 Questions. What would be a cleaner or better solution be for updating the Pygame display after clicking a button? Because currently I have the loop redefining the main window variable after every click in an attempt to get the stats to stay on the display.
if zombie.isOver(pos):
win.fill((255, 255, 255))
menu.draw(win, (0, 0, 0))
Zombie()
pygame.display.update()
x -= 1
def drawWindow():
again.draw(win, (0, 0, 0))
After the button is pressed this gets redefined.
def drawWindow():
win.fill((255, 255, 255))
bandit.draw(win, (0, 0, 0))
zombie.draw(win, (0, 0, 0))
rat.draw(win, (0, 0, 0))
I did this because the win.fill would cover up the values printed and I couldn't get them to stay any other way.
2nd question is when I mash the buttons in the display, 2 out of the 3 current ones work fine, however the 3rd button's values stutter and it is not smooth like the other 2. I think this is being caused by the location of the values in the code, they are the last ones I added putting them further down in the code, but honestly I have no idea.
if rat.isOver(pos):
win.fill((255, 255, 255))
menu.draw(win, (0, 0, 0))
Rat()
pygame.display.update()
x /= 2
def drawWindow():
again.draw(win, (0, 0, 0))
if again.isOver(pos) and x == 5:
win.fill((255, 255, 255))
menu.draw(win, (0, 0, 0))
again.draw(win, (0, 0, 0))
Rat()
pygame.display.update()
def drawWindow():
again.draw(win, (0, 0, 0))
The button works the same as the other 2 but the values stutter. If anyone has an idea that would help it would be greatly appreciated. Thank You.
Ok, not sure if i completely understand how you are doing it but on way to do it is in the main loop, call drawWindow(). Which is this:
def drawWindow():
win.fill((255, 255, 255))
bandit.draw(win, (0, 0, 0))
zombie.draw(win, (0, 0, 0))
rat.draw(win, (0, 0, 0))
I'm assuming that that is drawing images of them and not the stats. have a bool that controls whether to show the stats like show_zombie_stats for each of them. Then in the main loop or drawWindow(), you can do an if statement like
if show_zombie_stats:
zombie.draw_stats()
then call pygame.display.update() at the end of the main loop.
Now you can change the show_zombie stats with the button with show_zombie stats = not show_zombie stats
Related
I would like to change the color of my rectangle if i clicked on this selfmade button. Unfortunately, I have managed to change the colour when my index finger points to this button. When I remove my index finger from the button, it returns to its old colour.
How can I implement it so, that when I move my index finger to this button and hold it for a certain second (for exl. 4 seconds), the purple colour of the button should change to green permanently. If I hold it again, my green button should change to purple permanently.
cv2.rectangle(image, (100, 300), (200, 200), (255, 0, 255), cv2.FILLED)
thumb = res.right_hand_landmarks[8]
if ((thumb.x < 0.40 and thumb.x >= 0.30) and
(thumb.y < 0.69 and thumb.y >= 0.64)):
cv2.rectangle(image, (100, 300), (200, 200), (0, 255, 0), cv2.FILLED)
Here, instead of just only depending on the position of the index finger, you can add another condition in the if statement, depending whether the variable (example, ift is either True or False. This might be confusing, but let me explain it with the example:
ift = False
# Keep the var 'False' if the index finger was not in the self-made button for 4 seconds or more, else 'True'
Now, for calculating the time, you can use built-in time module. Here's an example:
from time import time, sleep
val = time()
sleep(1)
print(int(time() - val)) # Converting to integer, as the default is set to 'float'
# OUTPUT
# 1
Here's the documentation to the time module where you can find time.time():
time Documentation
Now, I know for cv2, you can't practically use sleep as it will stop the whole iteration, so here's another work around, for example:
from time import time
var = False
x = time()
y = time()
while True:
if not var:
x = time()
# If the 'var' is False, it will refresh the 'time()' value in every iteration, but not if the value was True.
if int(time() - y) >= 4:
print(int(time() - x), int(time() - y))
break
# OUTPUT
# 0 4
Now, we know how we can calculate time passed using the logic above, let's implement it in your code:
from time import time
# Variables below outside the loop, so that it doesn't reset at every iteration
ift = False # Let's assume 'False' is for 'purple' and 'True' is for 'green'
inside = False # It's to check whether the finger is in or not
t = time() # If the below code (you provided) is in a function, then I prefer adding it in the function before the loop starts
# Everything under is assumed to be inside the loop (assuming you are using the loop)
thumb = res.right_hand_landmarks[8]
if ((thumb.x < 0.40 and thumb.x >= 0.30) and
(thumb.y < 0.69 and thumb.y >= 0.64)):
cv2.rectangle(image, (100, 300), (200, 200), (255, 0, 255), cv2.FILLED)
inside = True
else:
cv2.rectangle(image, (100, 300), (200, 200), (0, 255, 0), cv2.FILLED)
inside = False
t = time() # Refreshes the time, because the finger is no more inside
if inside and int(time() - t) >= 4:
ift = not ift # Changes the value of 'ift' to its opposite, i.e., if the value was False, it will become True and vice versa
if ift:
cv2.rectangle(image, (100, 300), (200, 200), (0, 255, 0), cv2.FILLED)
else:
cv2.rectangle(image, (100, 300), (200, 200), (255, 0, 255), cv2.FILLED)
The code mentioned above might or might not work as it is not tested. But, it is enough to provide you with the information on what logic you can use to solve your issue.
NOTE, that the code above can be reduced as it is expanded and there might be useless lines that can be reduced, but for the sake of explanation, it has been expanded.
This question already has an answer here:
Pygame is running slow
(1 answer)
Closed 2 years ago.
I am creating a 3d pong game using pygame. I wanted to add a thick black border layer to the rectangle to make it more stylish. Here's what I tried:
pygame.draw.rect(screen, (0,0,255), (x,y,150,150), 0)
pygame.draw.rect(screen, (0,0,0), (x-1,y-1,155,155), 1)
pygame.draw.rect(screen, (0,0,0), (x-2,y-2,155,155), 1)
pygame.draw.rect(screen, (0,0,0), (x-3,y-3,155,155), 1)
pygame.draw.rect(screen, (0,0,0), (x-4,y-4,155,155), 1)
It worked but as the game I am trying to create is a 3d game this method was time consuming. Please tell me if there is any inbuilt method in pygame to draw borders around a rectangle. Sorry for my English.
You could also put it in a function, and make it more concise with for loops. First, you'll note that the four rectangles you drew were in a nice, easy pattern, so you could compact the drawing of the four rectangles like this:
pygame.draw.rect(surface, (0,0,255), (x,y,150,150), 0)
for i in range(4):
pygame.draw.rect(surface, (0,0,0), (x-i,y-i,155,155), 1)
Then, because pygame draw functions do not need to be run in the global scope, you can put all this into a function:
def drawStyleRect(surface):
pygame.draw.rect(surface, (0,0,255), (x,y,150,150), 0)
for i in range(4):
pygame.draw.rect(surface, (0,0,0), (x-i,y-i,155,155), 1)
Then, in your mainloop, all you have to do is run:
while not done:
...
drawStyleRect(screen) # Or whatever you named the returned surface of 'pygame.display.set_mode()'
...
You could even put the drawing function in a separate module, if you really wanted to.
Create a function that creates a pygame.Surface object with per pixel alpha (SRCALPHA) and draw the rectangle and the border on the surface:
def create_rect(width, height, border, color, border_color):
surf = pygame.Surface((width+border*2, height+border*2), pygame.SRCALPHA)
pygame.draw.rect(surf, color, (border, border, width, height), 0)
for i in range(1, border):
pygame.draw.rect(surf, border_color, (border-i, border-i, width+5, height+5), 1)
return surf
Create all the surfaces before the main application loop and blit them in the loop:
rect_surf1 = create_rect(150, 150, 5, (0, 0, 255), (0, 0, 0))
# [...]
run = True
while run:
# [...]
screen.blit(rect_surf1, (x, y))
# [...]
i try to create ellipse eye with pygame, and i have problem: my pupils overflow over eyes :
and I search to get this result
my code is like this :
self.lastrect = self.display.fill((0,0,0),self.lastrect)
leftEye = pygame.draw.ellipse(self.display, (255, 255, 255), [int(self.startx -self.startx/2-self.eyeRadius),int(self.starty-self.eyeRadius*self.eyeRatio), self.eyeWidth,self.eyeHeight], 0)
rightEye = pygame.draw.ellipse(self.display, (255, 255, 255), [int(self.startx +self.startx/2-self.eyeRadius),int(self.starty-self.eyeRadius*self.eyeRatio), self.eyeWidth,self.eyeHeight], 0)
leftPupil = self.display.blit(self.pupil,(self.x-self.startx/2-self.rad,self.y-self.rad))
rightPupil = self.display.blit(self.pupil,(self.x+self.startx/2-self.rad,self.y-self.rad))
pygame.display.update([leftPupil,leftEye,rightPupil,rightEye,self.lastrect])
can you help me to find a solution to stop pupil's overflow outside Eye ?
While there's no doubt that masks would be the better way, I would do something different (and dumber). In short is that I would give the screen a white backround, blit the pupil, AND THEN blit the face (in your picture it is the black background without the eye slots. This may sound confusing so think about creating the same exact image but with a white background.
However, there are a lot of negatives to this if you apply it to a program with other shapes and images so you'll want to probably stick with masking anyways. But this could work
I made it like this :
mask = pygame.Surface(self.size, pygame.SRCALPHA)
mask.fill((254, 195, 172))
pygame.draw.ellipse(mask, (0, 0, 0), [
self.leftEllipseX, self.leftEllipseY, self.eyeWidth, self.eyeHeight], 0)
pygame.draw.ellipse(mask, (0, 0, 0), [
self.rightEllipseX, self.rightEllipseY, self.eyeWidth, self.eyeHeight], 0)
self.display.blit(mask, (0, 0), None, pygame.BLEND_RGB_MAX)
Here is my full code:
import pygame
pygame.init()
i = 0
x = 0
# Define the colors we will use in RGB format
BLACK = ( 0, 0, 0)
WHITE = (255, 255, 255)
BLUE = ( 0, 0, 255)
GREEN = ( 0, 255, 0)
RED = (255, 0, 0)
# Set the height and width of the screen
size = [600, 300]
screen = pygame.display.set_mode(size)
pygame.display.set_caption("Test")
#Loop until the user clicks the close button.
done = False
clock = pygame.time.Clock()
while not done:
clock.tick(10)
for event in pygame.event.get():
if event.type == pygame.QUIT:
done=True
screen.fill(WHITE)
for x in range(x, x+100, 10):
pygame.draw.rect(screen, BLACK, [x, 0, 10, 10], 1)
pygame.display.flip()
pygame.quit()
10 squares are drawn but they scroll across the window to the right and i dont know why. Is there any way I could stop this?
Thanks.
I realise now that it is not about the rectangle loop, but i have changed that to what has been suggested anyway.
Now that you have added more code I see where the problem is coming from and it's just as I suspected - you are doing a static translation the incorrect way namely you use the x (which you have defined outside your main loop) in your for x in range(x, x+100, 10):.
If you add a print(x) statement inside your for-loop you will be able to see that the x gets bigger and bigger, and bigger...This is perfect for adding dynamics to your scene but my guess is (based on your question) that you want to add 10 static rectangles to your scene.
In order to do that you need to reset your x every single time a new iteration of the while loop begins:
while not done:
clock.tick(10)
for event in pygame.event.get():
if event.type == pygame.QUIT:
done=True
screen.fill(WHITE)
# Every single iteration of the while loop will first reset the x to its initial value
# You can make x be any value you want your set of rectangles to start from
x = 0
# Now start adding up to x's initial value
for x in range(x, x+100, 10):
pygame.draw.rect(screen, BLACK, [x, 0, 10, 10], 1)
pygame.display.flip()
You can also omit the definition of x as a variable outside the for loop if you won't be using it to change the x coordinate where your first rectangle will start from and replace the range(x, x+100, 10) with range(CONST, CONST+100, 10) where CONST is a given value such as 0, 100, 1000 etc.
Every time your for loop runs the range is incremented, because the x is persistent. If you remove the x=0, reset it inside the while, or use a different variable in the for loop I think it will work.
x=0
while not done:
for x in range(x, x+100, 10):
pygame.draw.rect(screen, BLACK, [x, 0, 10, 10], 1)
I'm somewhat new to pygame and trying to figure out how to make a circle semi-transparent. The trick however is that the background for the circle also has to be transparent. Here is the code I'm talking about:
size = 10
surface = pygame.Surface(size, size), pygame.SRCALPHA, 32)
pygame.draw.circle(
surface,
pygame.Color("black"),
(int(size/2), int(size/2)),
int(size/2), 2)
I tried using surface.set_alpha(127) but that didn't work. I'm assuming because the surface is already transparent.
Any help is appreciated.
A couple things. First, your surface definition should crash, as it missing a parenthesis. It should be:
surface = pygame.Surface((size, size), pygame.SRCALPHA, 32)
I assume that somewhere later in your code, you have something to the effect of:
mainWindow.blit(surface, (x, y))
pygame.display.update() #or flip
Here is your real problem:
>>> import pygame
>>> print pygame.Color("black")
(0, 0, 0, 255)
Notice that 255 at the end. That means that pygame.Color("black") returns a fully opaque color. Whereas (0, 0, 0, 0) would be fully transparent. If you want to set the transparency, define the color directly. That would make your draw function look like:
pygame.draw.circle(
surface,
(0, 0, 0, transparency),
(int(size/2), int(size/2)),
int(size/2), 2)