Hello I am trying to create towers for my Tower Defense game but every time i select a new tower the old one gets removed. I do not want this to be removed and I am sure there is a simple way to do this but I cannot find it. Here is my code. Thank you for any help.
def displayTower():
global bx, by
click = pygame.mouse.get_pressed()
Background.blit(redTower, (mx-bx,my-by))
Background.blit(redTower, (530,650))
while intro == 1:
mousePos = pygame.mouse.get_pos()
mousePressed = pygame.mouse.get_pressed()
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
if 530 < mousePos[0] < 590 and 650 < mousePos[1] < 710:
if mousePressed[0] == 1:
clicked = True
if clicked == True:
mx, my = pygame.mouse.get_pos()
bx = 30
by = 30
if mousePressed[0] == 0:
Background.blit(redTower, (mx-bx,my-by))
tx = mx - bx
ty = my - by
clicked = False
displayTower()
For one thing, you are calling displayTower() outside of a while loop, so it never gets executed. So you are blitting only one tower at a time, not two.
You each time have to blit all the screen or blit a portion of it and update only the rects affected by the change.
Yes, what you blitted should stay on, but you cannot count on it without proper updating when you wish.
To be secure, you should use internal surface, then blit it over the screen surface when you are done blitting and drawing. Anyway, what should the background variable contain? A screen or your surface?
So the second thing is that you never update the screen. You have to use either pygame.display.flip() or pygame.display.update().
And, do use events to get mouse position, it's more clever. Also add a sleep or pygame.time.clock() to regulate fps, this is almost a busy loop what you wrote.
Related
I am making a reaction time test (with a twist) in pygame.
I understand that the code in my event loop for mouse_clicks[0] (ie: left mouse button) can register whether or not you've clicked in the area for a small circle. However, I wish to have another action for left mouse button once I figure out how to code the event loop for when a big circle flashes on the screen. So, pretty much the same action for mouse_clicks[0] but with "if math.sqrt(sqx + sqy) < CIRCLE_RADIUS_LARGE". How could I achieve this using the pygame event loop. This is my first independent pygame so any help would be greatly appreciated. Much love, cLappy.
while running:
Clock.tick(FPS)
event_list = pygame.event.get()
mouse_clicks = pygame.mouse.get_pressed()
for event in event_list:
if mouse_clicks[2]:
pygame.display.update()
SCREEN.fill(BLACK)
circle(BLUE, CIRCLE_RADIUS_SMALL)
if mouse_clicks[0]:
x = pygame.mouse.get_pos()[0]
y = pygame.mouse.get_pos()[1]
sqx = (x - 640) ** 2
sqy = (y - 360) ** 2
if math.sqrt(sqx + sqy) < CIRCLE_RADIUS_SMALL:
reset_circle(BLUE, CIRCLE_RADIUS_SMALL)
pygame.display.update()
if event.type == pygame.QUIT:
running = False
You must draw the objects in the application loop, but not in the event loop. The typical PyGame application loop has to:
limit the frames per second to limit CPU usage with pygame.time.Clock.tick
handle the events by calling 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 calling either pygame.display.update() or pygame.display.flip()
Minimal example
import pygame, math
pygame.init()
SCREEN = pygame.display.set_mode((400, 400))
CIRCLE_RADIUS_SMALL = 20
circles = []
clock = pygame.time.Clock()
running = True
while running:
clock.tick(100)
event_list = pygame.event.get()
for event in event_list:
if event.type == pygame.QUIT:
running = False
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1:
circles.append(event.pos)
if event.button == 3:
for center in circles[:]:
dx = center[0] - event.pos[0]
dy = center[1] - event.pos[1]
if math.sqrt(dx*dx + dy*dy) < CIRCLE_RADIUS_SMALL:
circles.remove(center)
SCREEN.fill("black")
for center in circles:
pygame.draw.circle(SCREEN, "blue", center, CIRCLE_RADIUS_SMALL)
pygame.display.update()
I have made a sort of cookie clicker like game in pygame, however when I click the mouse once loads of points are added to the score. I assume this is because of the game loop, however I would like to know how to make this stop, and make it add 1 to the score for every click, no matter how long the mouse button is held down.
Here's an example that only increments the score on mouse button down events:
import pygame
pygame.init()
pygame.font.init()
clock = pygame.time.Clock()
screen = pygame.display.set_mode([320,240])
sys_font = pygame.font.SysFont(pygame.font.get_default_font(), 18)
pygame.display.set_caption("Clicker")
clicks = 0 # initialise the score counter
done = False
while not done:
#Events
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
elif event.type == pygame.MOUSEBUTTONDOWN:
clicks += 1
#Graphics
screen.fill(pygame.color.Color("white"))
score_txt = sys_font.render(f"Clicks: {clicks}", True, pygame.color.Color("blue"))
screen.blit(score_txt, (20, 220))
#Frame Change
pygame.display.update()
clock.tick(60)
pygame.quit()
Use 2 variables like is_mouse_clicked and was_mouse_clicked_previously.
In the initialization of the game (even before the first loop), assign False to both of them.
At the beginning of the game loop assign value of is_mouse_clicked to was_mouse_clicked_previously
Then, load the information whether the mouse button is being pressed to is_mouse_clicked variable
Then, add the point if the values of is_mouse_clicked and was_mouse_clicked_previously differ.
Option 1: Adding points inside if is_mouse_clicked and not was_mouse_clicked_previously: will increase the score right away (the moment you start pressing the button)
Option 2: Adding points inside if not is_mouse_clicked and was_mouse_clicked_previously: will increase the score a bit later (the moment you release the button)
I used the drawing commands to create a little stick figure at basically 100,100 on a 700,500 screen and I want to move it around with the mouse. The mouse shows up way left and right of the stick figure.
To do this is simple.
For example,
cat_image = "cat.png"
mouse_c = pygame.image.load(mouse_image).convert_alpha()
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
x,y = pygame.mouse.get_pos()
x -= mouse_c.get_width()/2
y -= mouse_c.get_height()/2
screen.blit(mouse_c,(x,y))
pygame.display.update()
You need to subtract half the height and width to get to the center.
i am trying to delete a sprite whenever I click on it, however, i can't get the oringinal item off of the screen. it seems once i render, or 'draw' it there, it stays there permentaelly. How can I change the following code to get rid of the 'blit' i produced in the class once it's clicked on?
import pygame
pygame.init()
window = pygame.display.set_mode((1152,720))
background = pygame.image.load("images/emptyroom.jpg")
image=pygame.image.load("images/smallkey.png")
b = window.blit(pygame.image.load("images/emptyroom.jpg"), (0,0))
spritestay = True
# d = window.blit(pygame.image.load("images/leglamp.png"), (15,500))
class HiddenObject(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.i1 = pygame.image.load("images/smallkey.png")
self.i2 = pygame.image.load("images/Clear.png")
self.rect=self.i1.get_rect()
def draw(self, x, y):
while spritestay == True:
d = window.blit(self.i1, (x,y))
#d.collidepoint(pygame.mouse.get_pos())
while spritestay == False:
d = window.blit(self.i1, (x,y))
pygame.display.set_caption("Eye Spy Game Test")
gameLoop = True
while gameLoop:
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONDOWN:
spritestay = False
if event.type == pygame.MOUSEBUTTONUP:
spritestay = True
if (event.type==pygame.QUIT):
gameLoop=False
yotestvar = HiddenObject()
d = yotestvar.draw(100,100)
pygame.display.flip()
pygame.quit()
There are quite a few issues with your code.
Blitting should not be done before the main loop.
It would be better if the Hidden Object new its position. Change it by adding a position in the constructor, and use it in the draw method.
def __init__(self,pos):
...
self.pos = pos
def draw(self):
window.blit(self.i1, self.pos)
Your draw method never finishes. You need to replace the while statements with if statements. Also you are not drawing the second image when spritestay is False. I guess you wanted to blit Clear.png.
if spritestay:
window.blit(self.i1, (x,y))
else:
window.blit(self.i2, (x,y))
If Clear.png is just an empty image, there is no point of blitting it at all. So this becomes:
if spritestay:
window.blit(self.i1, (x,y))
You do not return anything from the draw function, so after
d = yotestvar.draw(100,100)
d is None. You don't use d anywhere so you can remove that.
You create the HiddenObject on every frame. I think that it is much better to move it before the loop.
Last issue is the fact that you do not fill the screen with any color. This might be the real issue you have been strugling with. Here is how blitting works.
You are a painter, and in front of you is a canvas. If you take your brush and draw something, how can you get rid of it? Well the only way is to draw something on top of it.
In pygame we use the fill method to fill a whole surface with a certain color.
screen.fill((0,0,0)) # fills screen with black color
So in order to have something on the screen, you need to call screen.fill and then draw any other objects you wish to see.
In pygame, once you blit to the screen you cannot simply delete chosen objects. Instead you must clear your screen after every draw and then draw the new updated objects. If for example, you were making something more, you would have to draw it and then erase it, move it very slightly, draw it then erase it again and so on.
Therefore, it is recommended that you clear your screen at the end every time you loop through a while loop. It would look something as follows for your case:
yotestvar = HiddenObject()
while gameLoop:
window.fill((255, 255, 255)) # (255, 255, 255) RGB value for WHITE
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONDOWN:
spritestay = False
if event.type == pygame.MOUSEBUTTONUP:
spritestay = True
if (event.type==pygame.QUIT):
gameLoop=False
yotestvar.draw(100,100)
pygame.display.update()
pygame.quit()
I found a solution to make a sprite move when you hold a key down. The problem is that it forces writing ugly duplicated code. The current solution I found is:
for event in pygame.event.get():
if event.type == KEYDOWN:
keystate = pygame.key.get_pressed()
while keystate[K_RIGHT]:
screen.fill((255,255,255))
pygame.event.get()
for sprite in sprites:
rimage = sprite[1].getimage()
if sprite[2] is None:
x+=3
sprite[1].update(time)
screen.blit(rimage, (x,y))
if sprite[1].isfinished() == True:
sprite[1].reset()
last_dir = "right"
if x >= screen_width - rimage.get_width():
x = screen_width - rimage.get_width()
#update player sprite movement
#update player sprite animation
#update rest of game map
keystate = pygame.key.get_pressed()
time = pygame.time.get_ticks()
pygame.display.update()
The problem is that the while keystate block. It has to be repeated for each direction and the game world needs to be updated in each while block. That is five places where the same code needs to be duplicated....4 directions plus if a key is not pressed. I could wrap it in a function but I was wondering if there was a better way to handle holding down a button in pygame.
The usual way program in pygame is use the events to update the direction, then write the update position code outside events, that way you don't need replicated code.
clock = pygame.time.Clock()
direction = (0,0)
while True: # main loop
for event in pygame.event.get():
if event.type == KEYDOWN:
if event.key == K_RIGHT:
direction = (3, 0)
elif event.key == K_LEFT:
direction = (-3, 0)
elif event.key == K_UP:
direction = (0, 3)
elif event.key == K_DOWN:
direction = (0, -3)
else:
print "Unrecognized key"
if event.type == KEYUP:
direction = (0, 0)
screen.fill((255,255,255))
for sprite in sprites:
rimage = sprite[1].getimage()
if sprite[2] is None:
# Check if new position is inside the screen
new_pos = x + direction[0], y + direction[1]
if new_pos[0] + rimage.get_width() < screen_width:
x = new_pos[0]
if new_pos[1] + rimage.get_height() < screen_height:
y = new_pos[1]
# Draw the sprite
sprite[1].update(time)
screen.blit(rimage, (x,y))
if sprite[1].isfinished() == True:
sprite[1].reset()
last_dir = direction
#update player sprite movement
#update player sprite animation
#update rest of game map
time = pygame.time.get_ticks()
pygame.display.update()
clock.tick(40) # Keep game running at 40 fps
If you want you can achieve the same with keystate, in such case you don't need to process key events.
Pygame suggests or implies the division of programs into 3 parts:
The event handling, updating and drawing.
As pmoleri already said, you simply change the direction of the movement.
In the update function, you should pass in a delta time parameter, to move all the sprites according to the time passed. It is quite important, since the other technique doesn't take into account the variable speed of the processor. Games in DOS have been made this way, so now we need emulators to artificially slow down the processor. The draw part simply draws all the sprites.
This way you have a clear division between these 3 distinc parts in games: player input, game logic(movement, collision, actions etc.) and drawing.
Additionally, pygame.key.get_pressed() can be used.
This returns a list of all the keys currently being held down.
keys = pygame.key.get_pressed()
if keys[pygame.K_w]:
# Move Up
if keys[pygame.K_a]:
# Move Down
... etc ...
This does not need to be in the event section, but probably where the player updates.
This can make the code less clunky and (possible) be more efficient
-Hope I helped!