So, i have a pygame.circle that i would like to move. I have it moving etc, but it just duplicated the image and doesn't remove the previous. I understand the concept of "Blit" and understand it copies an array of pixels over. So i thought i would try redrawing my whole game, here's what i have:
if event.type == pygame.KEYDOWN and event.key == pygame.K_a:
diceRoll = random.randint(1, 4)
diceRollLabel = myFont.render(str(diceRoll), 1, black)
window.blit(diceRollLabel, (750, 40))
window.fill(black)
game()
count1 = pygame.draw.circle(window, (black),(150, countY - 72 * diceRoll), 25, 0)
game = False
game2 = True
print("Test")
player1Text = myFont.render(("Player twos turn!"), 1, black)
window.blit(player1Text, (650, 750))
pygame.display.update()
break
When it calls "game()" it should recall the function that contains all of the game screen, so the texture etc. but for some reason, it doesn't do anything? The screen just goes black?
it says "Bool object not callable" but my function isn't a boolean?
Fill the screen at the start of the loop.
def draw():
screen.fill(Color('black'))
# draw
pygame.display.flip()
You've set game as a Boolean
game = False
So when you call "game()" it's the same as "False()" which is the reason for your error.
You also fill the screen black after blitting the diceRollLabel (in black), and you then seem to draw a black circle on a black screen.
Full code would be helpful.
Related
I'm making a basic game where I have a surface and everytime I click on the surface it moves 5 pixels to the right. The program is working just fine without the checkCollide(event) function, but when I put the that condition it doesn't move. What is wrong?
My code until now is this
import pygame, sys
from pygame.locals import *
pygame.init()
DISPLAYSURF = pygame.display.set_mode((300,300))
def checkCollide(event):
k = 0
a,b = event.pos
x = P1[0].get_rect()
if x.collidepoint(a,b):
return True
return False
CP1 = [(150, 150)
,(155, 150)
,(160, 150)
,(165, 150)
,(170, 150)
,(175, 150)
,(180, 150)
,(185, 150)
,(190, 150)]
statp1_1 = 0
WHITE = (255,255,255)
DISPLAYSURF.fill(WHITE)
while True: # the main game loop
P1 = [pygame.image.load('PAzul.png'),CP1[statp1_1],statp1_1]
DISPLAYSURF.blit(P1[0], P1[1])
e = pygame.event.get()
for event in e:
if event.type == MOUSEBUTTONUP:
a = checkCollide(event)
if a:
DISPLAYSURF.fill(WHITE)
statp1_1 +=1
if event.type == QUIT:
pygame.quit()
sys.exit()
pygame.display.update()
Thank you
Check your logic in these lines of your function:
x = P1[0][0].get_rect()
if x.collidepoint(a,b):
return True
return False
Your code hinges on this bit:
a = checkCollide(event)
if a:
DISPLAYSURF.fill(WHITE)
So you're never evaluating this piece to be true.
I just realized what was wrong. When I do x = P1[0].get_rect() it creates a surface with topleft at (0,0).
What I needed to do was change the position of the rectangle using x.topleft = P1[1]
I've got some tips for you. First store the rect in the P1 list (it contains only the image and the rect in the following example, but maybe you could also add the statp1_1 index to it). Now we can just move this rect, if the user clicks on it (in the example I set the topleft attribute to the next point). Read the comments for some more tips. One thing you need to fix is to prevent the game from crashing when the statp1_1 index gets too big.
import sys
import pygame
pygame.init()
DISPLAYSURF = pygame.display.set_mode((300, 300))
WHITE = (255, 255, 255)
# Don't load images in your while loop, otherwise they have to
# be loaded again and again from your hard drive.
# Also, convert loaded images to improve the performance.
P1_IMAGE = pygame.image.load('PAzul.png').convert() # or .convert_alpha()
# Look up `list comprehension` if you don't know what this is.
CP1 = [(150+x, 150) for x in range(0, 41, 5)]
statp1_1 = 0
# Now P1 just contains the image and the rect which stores the position.
P1 = [P1_IMAGE, P1_IMAGE.get_rect(topleft=CP1[statp1_1])]
clock = pygame.time.Clock() # Use this clock to limit the frame rate.
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.MOUSEBUTTONUP:
if P1[1].collidepoint(event.pos):
print('clicked')
statp1_1 += 1
# Set the rect.topleft attribute to CP1[statp1_1].
P1[1].topleft = CP1[statp1_1]
DISPLAYSURF.fill(WHITE)
DISPLAYSURF.blit(P1[0], P1[1]) # Blit image at rect.topleft.
pygame.display.update()
clock.tick(30) # Limit frame rate to 30 fps.
I'm trying to get a circle to move based on a dice roll of 1-5.
Eg. if it rolls a 4, it will move the circle to a total of 580 - (55 * 4) pixels.
However I can't make it so the circle will basically clear the old version of itself dynamically, any ideas?
Also, I am trying to make it so the prompt message saying what number on the dice you have rolled clear itself after the user has seen it. so like pressing a button to make the program know that you have seen the dice number and wishes to proceed, however I can't do that without clearing the entire game window and when the game window clears, it will forget the old circle coordinates which makes the counters move back to the starting position again...
What I've attempted as requested -
while True:
for event in pygame.event.get():
if event.type==pygame.KEYDOWN and event.key==pygame.K_SPACE:
diceRoll = random.randint(1,5)
diceAlert = font.render("You rolled a: "+str(diceRoll), True, red)
moveCounter = font.render("Please click 1 or 2 to select the counter", True, red)
screen.blit(diceAlert, [665, 35])
screen.blit(moveCounter, [665, 70])
pygame.display.update()
if event.type==pygame.KEYDOWN and event.key==pygame.K_1:
if diceRoll == 1:
cover = int(0)
if diceRoll == 2:
cover = int(2)
if diceRoll == 3:
cover = int(2)
if diceRoll == 4:
cover = int(3)
counter1 = pygame.draw.circle(screen, white, (250, 580-(cover*55)), 15, 0)
counter1 = pygame.draw.circle(screen, red, (250, var[0]-55*diceRoll), 15, 0)
window()
pygame.display.update()
You seem to be bliting a circle only if a button has been pressed. This forces you to use your screen, as your only information of the state of your game. Instead you should separate those 2.
Here is a template of a pygame game:
init()
while not finished:
draw()
update()
input()
These 4 functions do different things. You should not do any drawing in the input method.
Let's go through these functions:
init() initializes pygame, screen, loads files, and creates any instances that are needed. In your case here you can create a variable that will store the current circle position.
draw() clears the screen with fill(), and draws all the sprites on screen. In your case, his will the the circle position, and draw it accordingly.
update() this updates sprites on the screen, checks collisions etc. You don't have anything that will use this function, since your game is static, it does not change in time.
input() takes input, and changes the state of objects. In your case, you would test to see if a button was pressed, and if so, change the circle position.
After tidying up your code it will look like this:
circle_pos = (0,0)
circle_pos2 = (0,0)
while True:
screen.blit(diceAlert, [665, 35])
screen.blit(moveCounter, [665, 70])
counter1 = pygame.draw.circle(screen, white, circle_pos, 15, 0)
counter1 = pygame.draw.circle(screen, red, circle_pos2, 15, 0)
pygame.display.update()
for event in pygame.event.get():
if event.type==pygame.KEYDOWN and event.key==pygame.K_SPACE:
diceRoll = random.randint(1,5)
diceAlert = font.render("You rolled a: "+str(diceRoll), True, red)
moveCounter = font.render("Please click 1 or 2 to select the counter", True, red)
if event.type==pygame.KEYDOWN and event.key==pygame.K_1:
if diceRoll == 1:
cover = int(0)
if diceRoll in (2,3):
cover = int(2)
if diceRoll == 4:
cover = int(3)
circle_pos = (250, 580-(cover*55))
The code is not fully functional, since I did not know what you are trying to accomplish. I hope this template will get you going.
In Pygame, I have wrote a Minesweeper clone. However, when I blit the final image stating YOU LOSE or YOU WIN, I get this result:
I'm sure you notice the thick black line surrounding the text. Here is the function in which the image is blitted onto the window:
def play():
SIZE = (WIDTH, HEIGHT) = (16, 16)
MINES = 40
PIXELS_PER_CELL = 30
pygame.init()
screen = pygame.display.set_mode((WIDTH * PIXELS_PER_CELL,
HEIGHT * PIXELS_PER_CELL))
pygame.display.set_caption("PyMines")
board = create_board(SIZE, MINES)
board.draw(screen)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
elif (event.type == pygame.MOUSEBUTTONDOWN and board.is_playing and
not board.is_solved):
board.mouse_handler(event, screen)
message = None
if not board.is_playing:
board.show_mines(screen)
message = pygame.image.load("images/lose.png").convert_alpha()
elif board.is_solved:
message = pygame.image.load("images/win.png").convert_alpha()
if message:
message = pygame.transform.scale(message, (screen.get_width(),
screen.get_height() //
5))
screen.blit(message, (0, 0))
pygame.display.update()
As I am not sure which part of the code you should be looking at, here is the full code.
Another reason why I think this behaviour is so bizarre, is that when I first created PyMines, the image blitted perfectly like so (as you can see, there is a very slight shadow to the text):
This however, is not a optimized version, as after each cycle, the whole board is redrawn (so it takes a very long time on a 16x16 board as shown in the first image, so I used a 9x9 - but the results are the same). Here is the play() function of the original version:
def play():
SIZE = (WIDTH, HEIGHT) = (9, 9)
MINES = 10
PIXELS_PER_CELL = 30
pygame.init()
screen = pygame.display.set_mode((WIDTH * PIXELS_PER_CELL,
HEIGHT * PIXELS_PER_CELL))
pygame.display.set_caption("PyMines")
board = create_board(SIZE, MINES)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
elif (event.type == pygame.MOUSEBUTTONDOWN and board.is_playing and
not board.is_solved):
board.mouse_handler(event, screen)
message = None
if not board.is_playing:
board.show_mines()
message = pygame.image.load("lose.png").convert_alpha()
elif board.is_solved:
message = pygame.image.load("win.png").convert_alpha()
board.draw(screen)
if message:
message = pygame.transform.scale(message, (screen.get_width(),
screen.get_height() //
5))
screen.blit(message, (0, 0))
pygame.display.update()
I would attach a link to the full code, but pastebin is down, so here is the full code for the original game without the strange black line.
EDIT: I have already tried dropping the convert_alpha() and adding convert() or even nothing at all.
.convert():
NOTHING:
Why are all these black lines there, how do I get rid of them and which version (convert/convert_alpha/NOTHING) should I use (and how to decide which one to use).
The text has a black shadow with an alpha channel. In your original version, you render the board, then render the text, and the shadow gets blended with the board.
In the revised version, you render the board, then repeatedly render the text over it. On the first pass, it renders correctly, with the shadow blending with the board. On the second pass, the shadow blends with the shadow you've already rendered, making a slightly darker shadow. On the next pass, the shadow gets slightly darker, and so on.
You can't use alpha blending without keeping tight control over what you're blending over. Each time you render the text, you'll need to render at least the section of the board behind the text, if not the full board.
In my previous question For Loop Functions in Python,
I had trouble with putting functions that contained a command to draw a line for a hangman game. It didn't exactly draw the line, and I first suspected it was a problem with the for loop or the functions. Now I realize there is somewhat a glitch with Pygame.
I have tried solving the problem by using this code in the country, CANADA:
b2 = font.render(str(letters[1]), True, (red))
screen.blit(b2, (bPosition))
if hangman1x == -500 and hangman1y == -500:
hangman1x = (775, 250)
hangman1y = (775, 50)
pygame.draw.line(screen, black, (hangman1x), (hangman1y), (5))
pygame.display.flip()
time.sleep(0.5)
bPosition = -500, -500
b1.x, b1.y = -500, -500
if hangman1x == (775, 250) and hangman1y == (775, 50):
print 'hi'
width = 6
pygame.draw.line(screen, black, (hangman1x), (hangman1y), (5))
print 'yay'
pygame.display.flip()
Now here's the weird thing.
When you press the B blitted onto the screen, it turns red, like its meant to, draws the line perfectly fine, but disappears, when the B disappears, and I understand why. After that, I added that extra if code. (Notice that both pygame.draw.line(s) are the same), It prints hi and yay in the shell, but it does not keep the line. Anyway to solve this?
After you are calling pygame.draw.line() you are probably redrawing your screen completely white, this will draw over the line and hide it. Instead of drawing lines like you are, I would build a hangman class draw from that
class Hangman():
def __init__(self):
self.lines = 0 #Number of lines to be drawn
def draw(self,screen):
#TODO draw to screen based on self.lines
#More code setting up pygame
drawlist = []
myMan = Hangman()
drawlist.append(myMan)
#mainloop
while 1:
screen.fill('#000000')
for item in drawlist:
item.draw(screen)
This way you are redrawing you hangman every frame, and thus he is always being showed
EDIT Added a running example
#!/usr/bin/python
import pygame
pygame.init()
class Hangman():
def __init__(self):
self.lines = 0 #Number of lines to be drawn
def hang(self):
self.lines += 1
def draw(self,screen):
for x in range(self.lines):
coord1 = (x*10,20)
coord2 = (x*10,50)
pygame.draw.line(screen,(0,0,0),coord1,coord2)
size = screenWidth,screenHeight = 200,70
screen = pygame.display.set_mode(size)
pygame.display.flip()
myman = Hangman()
drawlist = []
drawlist.append(myman)
#mainloop
running = True
while running:
#EVENT HANDLING#
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.KEYDOWN:
if event.key == 32: #Spacebar
myman.hang()
#DRAWING#
screen.fill((255,255,255))
for item in drawlist:
item.draw(screen)
pygame.display.flip()
I'm new to stackoverflow, but was hoping for a little insight from more advanced programmers. I am switching majors to Computer Science next semester and am taking an intro class learning some beginner's Python programming. I have already finished the program below (the assignment was to make a program that draws ovals on the window surface by filling in some of the professor's code, not too bad at all) but I wanted to add a little something extra: As you can see, I have the color of the ovals set to be random, but it stays the same until the program is restarted entirely i.e. all of the ovals are that particular color for the length of the program. With the code written the way it is, I can't figure out a way to get the color to change for each oval. Keep in mind, this is all for kicks, but if anyone's feeling especially helpful or creative, I'm curious to see what you have to say. Let me know if I can expound on anything. Thanks!
import pygame, random, sys
WINDOWWIDTH = 700
WINDOWHEIGHT = 700
BACKGROUNDCOLOR = (150,160,100)
#A different color every run
OVAL_COLOR = (random.randint (0,255),random.randint (0,255),
random.randint (0,255))
pygame.init()
windowSurface = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))
pygame.display.set_caption("Mobile Ovals")
#The draw variable is used later to indicate the mouse is still pressed
ovals = []
completedOvals = []
finished = False
draw = False
startXY = (-1, -1)
while not finished:
for event in pygame.event.get():
if event.type == pygame.QUIT or (event.type == pygame.KEYUP and
event.key == pygame.K_ESCAPE):
finished = True
elif event.type == pygame.KEYDOWN:
pressed = pygame.key.get_pressed()
if pressed[pygame.K_F4] and (pressed[pygame.K_LALT] or
pressed[pygame.K_RALT]):
finished = True
elif event.type == pygame.MOUSEBUTTONDOWN:
startXY = event.pos
draw = True
elif event.type == pygame.MOUSEBUTTONUP:
draw = False
for oval in ovals:
completedOvals.append (oval)
if draw == True:
del ovals [:]
#The above function ensures only one oval is onscreen at any given time
endXY = event.pos
width = (abs(endXY[0]-startXY[0]))
height = (abs(endXY[1]-startXY[1]))
#The code below allows the user to drag any direction
if endXY[0] < startXY[0]:
left = endXY[0]
else:
left = startXY[0]
if endXY[1] < startXY[1]:
top = endXY[1]
else:
top = startXY[1]
ovals.append (pygame.Rect (left, top, width, height))
windowSurface.fill(BACKGROUNDCOLOR)
for oval in ovals:
pygame.draw.ellipse(windowSurface, OVAL_COLOR, oval)
for completedOval in completedOvals:
pygame.draw.ellipse(windowSurface, OVAL_COLOR, completedOval)
pygame.display.update()
pygame.quit()
Your problem is quite simple. You set OVAL_COLOR once. But every time you make reference to the variable OVAL_COLOR, you're not creating a new random color, you're re-using the RGB color that was randomly generated when you created the variable.
Now, the way your program is structured, you maintain a list of all complete ovals that you're re-drawing every time the draw variable is set to true. If you place the OVAL_COLOR variable inside the for loop, you will update the color with every mouse movement, changing the color of the oval being drawn, as well as the color of all the old ovals being re-drawn.
The solution to have a new random oval color is to set the variable OVAL_COLOR when the mouse button goes down. That way, the oval color won't change as you drag the mouse to adjust the oval. But, given the current structure of the program, you'll need to save the oval colors assigned to completed ovals, or you'll still have the oval color change each time.
When the mouse button is pressed down, we want a new random color for our circle. Generate a random value, which will be used every time the circle is re-drawn.
elif event.type == pygame.MOUSEBUTTONDOWN:
startXY = event.pos
OVAL_COLOR = (random.randint (0,255),random.randint (0,255),
random.randint (0,255))
draw = True
When the mouse button is released, save the coordinates for the oval, along with the color that it was drawn with.
elif event.type == pygame.MOUSEBUTTONUP:
draw = False
# print len(ovals) # (always ==1)
completedOvals.append ((ovals[-1], OVAL_COLOR))
When we iterate through these completed ovals, draw them with the same color each time.
for (completedOval, color) in completedOvals:
pygame.draw.ellipse(windowSurface, color, completedOval)
Create a simple Oval() class, that contains it's color, and size.
import pygame
from pygame.locals import *
class Oval(object):
"""handle, and draw basic ovals. stores Rect() and Color()"""
def __init__(self, startXY, endXY):
self.color = Color(random.randint(0,255), random.randint(0,255), random.randint(0,255))
self.rect = Rect(0,0,1,1)
self.coord_to_oval(startXY, endXY)
def draw(self):
pygame.draw.ellipse(windowSurface, self.color, self.rect)
def coord_to_oval(self, startXY, endXY):
width = (abs(endXY[0]-startXY[0]))
height = (abs(endXY[1]-startXY[1]))
#The code below allows the user to drag any direction
if endXY[0] < startXY[0]:
left = endXY[0]
else:
left = startXY[0]
if endXY[1] < startXY[1]:
top = endXY[1]
else:
top = startXY[1]
self.rect = Rect(left, top, width, height)
# main loop
while not finished:
for event in pygame.event.get():
# events, and creation:
# ... your other events here ...
elif event.type == MOUSEBUTTONDOWN:
startXY = event.pos
draw = True
elif event.type ==MOUSEBUTTONUP:
# on mouseup, create instance.
endXY = event.pos
oval_new = Oval(startXY, endXY)
completedOvals.append(oval_new)
# draw them:
for oval in ovals:
oval.draw()
for oval in completedOvals:
oval.draw()
I mostly left out your non-completed ovals. Was that to show the size before clicking?