pygame.MOUSEBUTTONDOWN only working every few times [duplicate] - python

This question already has an answer here:
Faster version of 'pygame.event.get()'. Why are events being missed and why are the events delayed?
(1 answer)
Closed 5 months ago.
I'm trying to create a game in pygame where you are a rectangle trying to shoot a bullet towards another rectangle, when you click a mousebutton. I'm using this with the pygame.MOUSEBUTTONDOWN, as you can see here:
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONDOWN and ammunition > 0:
bullets.append(Bullet(*bullet_pos))
ammunition -= 1
**I'll include more code for reference later.
However, when I click the mouse button, it only shoots a bullet every few times I click. I checked multiple times, and there doesn't seem to be a pattern of when it does/doesn't shoot. It shoots about every 4-5 times, but that's just an estimate.
Full code, for reference:
import pygame
import math
import random
import time
pygame.init()
# Setting all variables to use later \/
width, height = 798, 552
white = pygame.Color('white')
black = pygame.Color('black')
green = pygame.Color('green')
blue = pygame.Color('blue')
red = pygame.Color('red')
grey = pygame.Color('gray')
yellow = pygame.Color('yellow')
orange = pygame.Color('orange')
azure = pygame.Color('azure')
size_x = 100
size_y = 50
pos_x = 100
pos_y = 275
size_x_2 = 100
size_y_2 = 50
pos_x_2 = random.randint(0, 798)
pos_y_2 = random.randint(0, 552)
pos_x_3 = random.randint(0, 798)
pos_y_3 = random.randint(0, 552)
window = pygame.display.set_mode((width, height))
bullet_pos_y = random.randint(0, 552)
bullet_pos_x = 798
ammunition = 5
enemy_health = 1000
player_health = 50
font = pygame.font.SysFont('Times New Roman', 32)
shooting_x = 0
shooting_y = 0
# Setting All Variables to use later /\
class Bullet:
def __init__(self, x, y):
self.pos = (x, y)
self.dir = (shooting_x - x, shooting_y - y)
length = math.hypot(*self.dir)
if length == 0.0:
self.dir = (0, -1)
else:
self.dir = (self.dir[0]/length, self.dir[1]/length)
angle = math.degrees(math.atan2(-self.dir[1], self.dir[0]))
self.bullet = pygame.Surface((27.5, 17.5)).convert_alpha()
self.bullet.fill((red))
self.bullet = pygame.transform.rotate(self.bullet, angle)
self.speed = 1
def update(self):
self.pos = (self.pos[0] + self.dir[0] * self.speed, self.pos[1] + self.dir[1] * self.speed)
def draw(self, surf):
bullet_rect = self.bullet.get_rect(center = self.pos)
surf.blit(self.bullet, bullet_rect)
bullets = []
keys = pygame.key.get_pressed()
# EVERYTHING BELOW THIS IS IN THE GAME LOOP, EVERYTHING ABOVE ISN'T
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
window.fill(black)
# Drawing Everything
character = pygame.draw.rect(window, white, (pos_x, pos_y, size_x, size_y))
enemy = pygame.draw.rect(window, blue, (shooting_x, shooting_y, size_x, size_y))
coin = pygame.draw.circle(window, yellow, [pos_x_2, pos_y_2], 15)
# Setting Text
enemy_health_text = font.render(str(enemy_health), True, white, blue)
enemy_health_textRect = enemy_health_text.get_rect()
enemy_health_textRect.center = (enemy.center)
player_health_text = font.render(str(player_health), True, black, white)
player_health_textRect = enemy_health_text.get_rect()
player_health_textRect.center = (character.center[0] + 9, character.center[1])
ammunition_text = font.render("ammunition remaining: " + str(ammunition), True, azure, black)
ammunition_textRect = ammunition_text.get_rect()
ammunition_textRect.center = (205, 25)
bullet_pos = character.center
enemy_pos = enemy.center
# Shooting a bullet
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONDOWN and ammunition > 0:
bullets.append(Bullet(*bullet_pos))
ammunition -= 1
# Enemy Dealing Damage to Player
if enemy.colliderect(character):
player_health -= 1
white = pygame.Color('red')
if not enemy.colliderect(character):
player_health -= 0
white = pygame.Color('white')
mouse_pos_x, mouse_pos_y = pygame.mouse.get_pos()
pos_x, pos_y = pygame.mouse.get_pos()
# If Character collides with coin
if character.colliderect(coin):
pos_x_2 = random.randint(0, 798)
pos_y_2 = random.randint(100, 552)
num = random.randint(0, 20)
if num == 17:
yellow = pygame.Color('purple')
ammunition += 5
if num != 17:
yellow = pygame.Color('yellow')
ammunition += 2
elif enemy.colliderect(coin):
pos_x_2 = random.randint(0, 798)
pos_y_2 = random.randint(100, 552)
enemy_health += 3
# Setting the Enemy Movement
if shooting_x < pos_x_2:
shooting_x += 0.1
if shooting_x > pos_x_2:
shooting_x -= 0.1
if shooting_y < pos_y_2:
shooting_y += 0.1
if shooting_y > pos_y_2:
shooting_y -= 0.1
# Updating/Drawing Bullets
for bullet in bullets:
bullet.update()
''' WORK ON THIS '''
if not window.get_rect().collidepoint(bullet.pos):
bullets.remove(bullet)
for bullet in bullets:
bullet.draw(window)
# Making sure the player doesn't leave boundaries
if pos_y >= 552:
pos_y = 552
if pos_y <= 0:
pos_y = 0
if pos_x <= 0:
pos_x = 0
if pos_x >= 700:
pos_x = 700
# Drawing all text on screen
window.blit(ammunition_text, ammunition_textRect)
window.blit(enemy_health_text, enemy_health_textRect)
window.blit(player_health_text, player_health_textRect)
pygame.display.update()

The function pygame.event.get() returns an object with all events that happens. It clears all events afterwards so nothing will be executed twice when an event occurs. So you shouldn't call it twice without checking all needed events both times. Some events might get ignored when you don't call pygame.event.get() often because it has a limit of 128 events.
As a solution you can save the events in a variable (Some events might be delayed):
events = pygame.event.get()
for event in events:
if event.type == pygame.QUIT:
run = False
...
for event in events:
if event.type == pygame.MOUSEBUTTONDOWN and ammunition > 0:
bullets.append(Bullet(*bullet_pos))
ammunition -= 1
Or you only have one event loop:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
elif event.type == pygame.MOUSEBUTTONDOWN and ammunition > 0:
bullets.append(Bullet(*bullet_pos))
ammunition -= 1
...

Related

Collision doesn't work. Upon collision, it is supposed to end the game. (instead both objects pass straight through each other)

I'm working on a space shooter game where you have to dodge asteroids and shoot them. Right now, I'm working on collision for the asteroids. I'm just testing one asteroid for now, but the asteroid passes straight through the ship and doesn't end the game like I want it to.
Here's the code:
import pygame
pygame.init()
#initalizing all the clunky variables
size = (900,700)
BLACK = (0, 0, 30)
RED = (255, 0, 0)
YELLOW = (0, 255, 0)
x_pos = 450
y_pos = 600
global x_pos
global y_pos
direct = 0
w, h = 100, 100
screen = pygame.display.set_mode(size)
klok = pygame.time.Clock()
#main ship image and its rotations
ship = pygame.image.load('u-sniper.png')
shipL = pygame.transform.rotate(ship, 270)
shipR = pygame.transform.rotate(ship, 90)
shipD = pygame.transform.rotate(ship, 180)
#init hitbox
hitbox = ship.get_rect()
hitbox.center = w//2,h//2
#funct for drawing ship
def drawShip():
if direct == 0:
screen.blit(ship, [x_pos,y_pos])
if direct == 1:
screen.blit(shipR, [x_pos,y_pos])
if direct == 2:
screen.blit(shipD, [x_pos,y_pos])
if direct == 3:
screen.blit(shipL, [x_pos,y_pos])
#asteroid obstacles (these are meant to collide with the ship)
class asteroid:
def __init__(self,x,y,spawn):
self.x = x
self.y = y
self.spawn = spawn
def drawA(self):
if self.spawn == 1:
pygame.draw.circle(screen, RED, (self.x,self.y), 30)
def moveA(self):
self.y += 8
if self.y > 650:
self.spawn = 0
done = False
roid = asteroid(450,0,1)
#asteroid hitbox init
rect_asteroid = (roid.x, roid.y, 30, 30)
#here is where its going wrong, collision dosent register
def checkForCollisions():
collide = pygame.Rect.colliderect(hitbox,rect_asteroid)
if collide == True:
done = True
#loop
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
screen.fill(BLACK)
drawShip()
roid.drawA()
roid.moveA()
#calling fuction, but it dosent work
checkForCollisions()
#if branch that moves the ship
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP:
y_pos -= 5
direct = 0
if event.key == pygame.K_DOWN:
y_pos += 5
direct = 2
if event.key == pygame.K_RIGHT:
x_pos += 5
direct = 3
if event.key == pygame.K_LEFT:
x_pos -= 5
direct = 1
#collision between screen boundaries
if x_pos > 850:
x_pos -= 6
if x_pos < -50:
x_pos += 6
if y_pos > 650:
y_pos -= 6
if y_pos < 0:
y_pos += 6
pygame.display.flip()
klok.tick(60)
pygame.quit()
I tried multiple colliderect functions, but it just results in one thing: the ship and the asteroid pass straight through each other.
The pygame.Rect objects do not magically update their position when you change the coordinates used to draw the object. You need to update the location stored in the pygame.Rect objects before collision detection. Or just create the objects in checkForCollisions:
def checkForCollisions():
hitbox.topleft = (x_pos, y_pos)
rect_asteroid = (roid.x, roid.y, 30, 30)
collide = hitbox.colliderect(rect_asteroid)
return collide

Limiting the amount of circles that spawn in

im fairly new to coding and python, i was messing around with pygame and i was wondering if theres a way i could limit the amount of circles that spawn in this game im making? when i run the code, it just spawns in circles all over the place really fast. i tried doing the time.sleep thing, but all it does is slow down the entire game.
import pygame
import random
import time
pygame.init()
y = 0
x = 0
point = 0
is_blue = True
WHITE = (255, 255, 255)
clock = pygame.time.Clock()
screen = pygame.display.set_mode([500, 500])
def food():
pygame.draw.circle(screen, WHITE, (random.randint(1, 400), random.randint(1, 400)), 5)
done = False
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
if event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE:
is_blue = not is_blue
pygame.display.set_caption("Collect the balls to win!")
pressed = pygame.key.get_pressed()
if pressed[pygame.K_UP]:
y -= 3
if pressed[pygame.K_DOWN]:
y += 3
if pressed[pygame.K_LEFT]:
x -= 3
if pressed[pygame.K_RIGHT]:
x += 3
if x <= -1:
x = 0
if x >= 441:
x = 440
if y <= -1:
y = 0
if y >= 441:
y = 440
screen.fill((0, 0, 0))
if is_blue:
color = (0, 128, 255)
else:
color = (255, 100, 0)
pygame.draw.rect(screen, color, pygame.Rect(x, y, 30, 30))
food()
pygame.display.flip()
clock.tick(144)
You have to use a list. Create a list for the food positions:
food_list = []
Fill the list in a loop:
while len(food_list) < 10:
food_list.append((random.randint(1, 400), random.randint(1, 400)))
Draw the foods in the list in a loop:
for food_pos in food_list:
pygame.draw.circle(screen, WHITE, food_pos, 5)
You can also spawn the food with an time interval. Use pygame.time.get_ticks() to measure the time. Define a time interval after which a new object should appear. Create an object when the point in time is reached and calculate the point in time for the next object:
food_list = []
time_interval = 1000 # 1000 milliseconds == 1 seconds
next_food_time = pygame.time.get_ticks()
done = False
while not done:
# [...]
current_time = pygame.time.get_ticks()
if current_time > next_food_time and len(food_list) < 10:
next_food_time += time_interval
food_list.append((random.randint(1, 400), random.randint(1, 400)))
See also Spawning multiple instances of the same object concurrently in python
Complete example:
import pygame
import random
import time
pygame.init()
y = 0
x = 0
point = 0
is_blue = True
WHITE = (255, 255, 255)
clock = pygame.time.Clock()
screen = pygame.display.set_mode([500, 500])
food_list = []
time_interval = 1000 # 1000 milliseconds == 1 seconds
next_food_time = pygame.time.get_ticks()
done = False
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
if event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE:
is_blue = not is_blue
pygame.display.set_caption("Collect the balls to win!")
pressed = pygame.key.get_pressed()
if pressed[pygame.K_UP]:
y -= 3
if pressed[pygame.K_DOWN]:
y += 3
if pressed[pygame.K_LEFT]:
x -= 3
if pressed[pygame.K_RIGHT]:
x += 3
if x <= -1:
x = 0
if x >= 441:
x = 440
if y <= -1:
y = 0
if y >= 441:
y = 440
current_time = pygame.time.get_ticks()
if current_time > next_food_time and len(food_list) < 10:
next_food_time += time_interval
food_list.append((random.randint(1, 400), random.randint(1, 400)))
screen.fill((0, 0, 0))
if is_blue:
color = (0, 128, 255)
else:
color = (255, 100, 0)
pygame.draw.rect(screen, color, pygame.Rect(x, y, 30, 30))
for food_pos in food_list:
pygame.draw.circle(screen, WHITE, food_pos, 5)
pygame.display.flip()
clock.tick(144)

How do you move the circle to the mouse position [duplicate]

This question already has answers here:
pygame 2 dimensional movement of an enemy towards the player, how to calculate x and y velocity?
(1 answer)
Pygame make sprite walk in given rotation
(1 answer)
How to make smooth movement in pygame
(2 answers)
Closed 1 year ago.
I created a circle in pygame. The circle moves to wherever you click, but instead of "walking" over there, it just appears. I tried some ways, but it doesn't work. If you could find out a way to do it, that would be great. The moving function is in the move function in the Player class.
# import
import pygame
# initialize pygame
pygame.init()
# frame rate variables
FPS = 120
clock = pygame.time.Clock()
# game variables
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 800
mouse_pos = ''
# colors
BLUE = (0, 0, 255)
# activate screen
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption('Bonker')
class Player(pygame.sprite.Sprite):
def __init__(self, x, y):
# init the sprite class
pygame.sprite.Sprite.__init__(self)
self.x = x
self.y = y
self.mouse_x = 0
self.mouse_y = 0
def move(self):
# delta x and delta y
dx = 0
dy = 0
# extract info from tuple
(x, y) = mouse_pos
self.mouse_x = x
self.mouse_y = y
# create tuple destination and current position tuple
destination = (self.mouse_x, self.mouse_y)
current_pos = [self.x, self.y]
# draw the rectangle
if current_pos[0] >= SCREEN_WIDTH // 2:
dx = 10
self.x += dx
if current_pos[0] < SCREEN_WIDTH // 2:
dx = -10
self.x += dx
if current_pos[1] >= SCREEN_HEIGHT // 2:
dy = 10
self.y += dy
if current_pos[1] < SCREEN_HEIGHT // 2:
dy = -10
self.y += dy
pygame.draw.circle(screen, BLUE, (self.x, self.y), 20)
def draw(self):
# draw the circle
pygame.draw.circle(screen, BLUE, (SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2), 20)
# create instances
# player instance
player = Player(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2)
player.draw()
# main loop
run = True
while run:
# run frame rate
clock.tick(FPS)
# events
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.MOUSEBUTTONDOWN:
screen.fill((0, 0, 0))
mouse_pos = pygame.mouse.get_pos()
player.move()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
run = False
pygame.display.update()
pygame.quit()
I think some code is not needed
Help would be appreciated
This will move the circle to the mouse position. It doesn't move in one diagonal, but that can be changed.
# import
import pygame
# initialize pygame
pygame.init()
# frame rate variables
FPS = 120
clock = pygame.time.Clock()
# game variables
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 800
# colors
BLUE = (0, 0, 255)
# activate screen
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption('Bonker')
class Player(pygame.sprite.Sprite):
def __init__(self, x, y):
# init the sprite class
pygame.sprite.Sprite.__init__(self)
self.rect = pygame.Rect(0, 0, 40, 40)
self.rect.x = x
self.rect.y = y
self.radius = 20
self.destination = None
self.moving = False
self.dx = 0
self.dy = 0
def set_destination(self, pos):
self.destination = pos
# delta x and delta y
self.dx = self.destination[0] - self.rect.centerx
self.dy = self.destination[1] - self.rect.centery
self.moving = True
def move(self):
if self.rect.centerx != self.destination[0]:
if self.dx > 0:
self.rect.centerx += 1
elif self.dx < 0:
self.rect.centerx -= 1
if self.rect.centery != self.destination[1]:
if self.dy > 0:
self.rect.centery += 1
elif self.dy < 0:
self.rect.centery -= 1
elif self.rect.center == self.destination:
self.moving = False
def draw(self):
# draw the circle
pygame.draw.circle(screen, BLUE, self.rect.center, self.radius)
# create instances
# player instance
player = Player(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2)
player.draw()
# main loop
run = True
movetime = 100
move = False
while run:
# run frame rate
dt = clock.tick(FPS)
movetime -= dt
if movetime <= 0:
move = True
movetime = 400
# events
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.MOUSEBUTTONDOWN:
mouse_pos = pygame.mouse.get_pos()
player.set_destination(mouse_pos)
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
run = False
if player.moving:
player.move()
screen.fill((0, 0, 0))
player.draw()
pygame.display.update()
pygame.quit()

Update one sprite among a group of sprites [Pygame]

First post here. So I am trying to implement a Civilization type of movement game. At the moment, I have one sprite in a cell. I can click it and then if I click another grid, the sprite moves there. What I now want is to spawn 5-6 such sprites, and then do the same thing. Click on a sprite and then click another grid, and that specific sprite moves there without affecting the other sprites. I cannot seem to do that. I can spawn 5-6 random sprites at different grids, but when I click on one of them and then click another grid, all the other sprites are gone. The code is below (not the best as I am learning Pygame). I understand that I have to somehow only update the sprite that was clicked, but I am not sure how to do that.
import pygame
import random
WIDTH = 900
HEIGHT = 700
FPS = 2
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
TURN = "TeamOne"
def main():
# Pygame sprite Example
global x_lines
global y_lines
x_lines = [WIDTH-i*WIDTH/20 for i in range(20,0, -1)]
y_lines = [HEIGHT-j*HEIGHT/20 for j in range(20,0, -1)]
class TeamOne(pygame.sprite.Sprite):
# sprite for the Player
def __init__(self):
# this line is required to properly create the sprite
pygame.sprite.Sprite.__init__(self)
# create a plain rectangle for the sprite image
self.image = pygame.Surface((WIDTH / 20, HEIGHT / 20))
self.image.fill(GREEN)
# find the rectangle that encloses the image
self.rect = self.image.get_rect()
# center the sprite on the screen
self.rect.center = ((random.randint(1,19)*2+1)* WIDTH/ 40, (random.randint(1,19)*2+1)*HEIGHT/40)
def update(self, position):
# any code here will happen every time the game loop updates
(a, b) = position
for index, i in enumerate(x_lines):
if i > a:
self.rect.x = x_lines[index-1]
break
for index, j in enumerate(y_lines):
if j > b:
self.rect.y = y_lines[index-1]
break
# initialize pygame and create window
pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("A Game")
clock = pygame.time.Clock()
clicked_sprites = pygame.sprite.Group()
teamone_sprites = pygame.sprite.Group()
for i in range(5):
mob1 = TeamOne()
teamone_sprites.add(mob1)
# Game loop
running = True
j=0
while running:
# keep loop running at the right speed
clock.tick(FPS)
# Process input (events)
for event in pygame.event.get():
# check for closing window
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.MOUSEBUTTONDOWN and j == 0:
pos = pygame.mouse.get_pos()
for s in teamone_sprites:
if s.rect.collidepoint(pos):
#teamone_sprites.add(s)
clicked_sprites.add(s)
print (clicked_sprites)
j = 1
elif event.type == pygame.MOUSEBUTTONDOWN and j == 1:
new_pos = pygame.mouse.get_pos()
#teamone_sprites.update(new_pos)
clicked_sprites.update(new_pos)
j = 0
# Update
# Draw / render
## screen.fill(BLACK)
## draw_grid(screen)
##
## teamone_sprites.draw(screen)
##
##
##
## # *after* drawing everything, flip the display
## pygame.display.flip()
# Draw / render
screen.fill(BLACK)
draw_grid(screen)
teamone_sprites.draw(screen)
pygame.display.flip()
pygame.quit()
def draw_grid(screen):
for i in range(1, HEIGHT, int(HEIGHT/20)):
pygame.draw.line(screen, GREEN, (1,i) ,(WIDTH,i), 2)
for j in range(1, WIDTH, int(WIDTH/20)):
pygame.draw.line(screen, GREEN, (j,1) ,(j,HEIGHT), 2)
if __name__ == '__main__':
main()
Some tips for you:
Keep your main loop clean
Put logic where it belongs
Only call pygame.display.flip()/pygame.display.update() once
Don't use variable names like j
Since your game is grid based, you should have a way to translate between grid coordinates and screen coordinates
Here's a simple runnable example I hacked together (see the comments for some explanations):
import pygame
import random
WIDTH = 900
HEIGHT = 700
ROWS = 20
COLUMNS = 20
TILE_SIZE = WIDTH / COLUMNS, HEIGHT / ROWS
TILE_W, TILE_H = TILE_SIZE
FPS = 60
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
TURN = "TeamOne"
# some functions to translate grid <-> screen coordinates
def posToScreen(pos):
column, row = pos
return column * TILE_W, row * TILE_H
def screenToPos(pos):
column, row = pos
return column / TILE_W, row / TILE_H
def draw_grid(screen):
for i in range(1, HEIGHT, TILE_H):
pygame.draw.line(screen, GREEN, (1,i) ,(WIDTH,i), 2)
for j in range(1, WIDTH, TILE_W):
pygame.draw.line(screen, GREEN, (j,1) ,(j,HEIGHT), 2)
# a class that handles selecting units
class Cursor(pygame.sprite.Sprite):
def __init__(self, units, *groups):
pygame.sprite.Sprite.__init__(self, *groups)
# group of the units that can be controlled
self.units = units
# we create two images
# to indicate if we are selecting or moving
self.image = pygame.Surface(TILE_SIZE)
self.image.set_colorkey((43,43,43))
self.image.fill((43,43,43))
self.rect = self.image.get_rect()
self.selected_image = self.image.copy()
pygame.draw.rect(self.image, pygame.Color('red'), self.image.get_rect(), 4)
pygame.draw.rect(self.selected_image, pygame.Color('purple'), self.image.get_rect(), 4)
self.base_image = self.image
self.selected = None
def update(self):
# let's draw the rect on the grid, based on the mouse position
pos = pygame.mouse.get_pos()
self.rect.topleft = posToScreen(screenToPos(pos))
def handle_click(self, pos):
if not self.selected:
# if we have not selected a unit, do it now
for s in pygame.sprite.spritecollide(self, self.units, False):
self.selected = s
self.image = self.selected_image
else:
# if we have a unit selected, just set its target attribute, so it will move on its own
self.selected.target = posToScreen(screenToPos(pos))
self.image = self.base_image
self.selected = None
class TeamOne(pygame.sprite.Sprite):
def __init__(self, *groups):
pygame.sprite.Sprite.__init__(self, *groups)
self.image = pygame.Surface(TILE_SIZE)
self.image.fill(GREEN)
self.pos = random.randint(0, COLUMNS), random.randint(0, ROWS)
self.rect = self.image.get_rect(topleft = posToScreen(self.pos))
self.target = None
def update(self):
# do nothing until target is set
# (maybe unset it if we reached our target)
if self.target:
if self.rect.x < self.target[0]:
self.rect.move_ip(1, 0)
elif self.rect.x > self.target[0]:
self.rect.move_ip(-1, 0)
elif self.rect.y < self.target[1]:
self.rect.move_ip(0, 1)
elif self.rect.y > self.target[1]:
self.rect.move_ip(0, -1)
self.pos = screenToPos(self.rect.topleft)
def main():
pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("A Game")
clock = pygame.time.Clock()
all_sprites = pygame.sprite.LayeredUpdates()
team_ones = pygame.sprite.Group()
for i in range(5):
TeamOne(all_sprites, team_ones)
cursor = Cursor(team_ones, all_sprites)
# a nice, simple, clean main loop
running = True
while running:
clock.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# we could also pass all events to all sprites
# so we would not need this special clause for the cursor...
if event.type == pygame.MOUSEBUTTONDOWN:
cursor.handle_click(event.pos)
all_sprites.update()
screen.fill(BLACK)
draw_grid(screen)
all_sprites.draw(screen)
pygame.display.flip()
if __name__ == '__main__':
main()
I have pretty much the same Problem i have a healthbar for my enemie but i want all enemys to have one so its a spritegroup now if i want to change an attribute of one object out of my spritegroup i dont know how to properly access it. The Problem lays in the Healthbaranimation function. I tried self.healthbar.sprites() self.healthbar.sprites and spritedict nothing really semms to work. Is there an easy way to fix this? P.s sorry for my bad code It is my first real attempt making a small game
from os import path
import pygame
from elements.ammo import AMMO
from elements.bigenemy import BIGENEMY
from elements.enemy import ENEMY
from elements.player import PLAYER
from .base import BaseState
from elements.healthbar import HEALTHBAR
class Gameplay(BaseState):
def __init__(self):
super(Gameplay, self).__init__()
self.next_state = "GAME_OVER"
self.x, self.y = 100, 1030
self.playersprite = PLAYER((self.x, self.y))
self.bigenemy = pygame.sprite.GroupSingle(BIGENEMY())
self.bottomrect = pygame.Rect((0, 1030), (1920, 50))
self.enemysprite = ENEMY()
self.ammosprite = AMMO()
self.healthbar = pygame.sprite.Group(HEALTHBAR())
self.displayedimage = self.playersprite.image
self.displayedrect = self.playersprite.rect
self.highscore = self.load_data()
self.points = 0
self.scoretext = f"SCORE: {self.points}"
self.scoresurf = self.font.render(self.scoretext, True, "red")
self.nhstext = "NEW HIGHSCORE!"
self.nhssurf = self.font.render(self.nhstext, True, "red")
self.ammotext = f"AMMO:{self.playersprite.ammunition}"
self.ammosurf = self.font.render(self.ammotext, True, "red")
self.bulletgroup = pygame.sprite.Group()
self.time_active = 0
self.bigenemyexisting = True
def get_event(self, event):
if event.type == pygame.QUIT:
self.quit = True
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_LCTRL:
self.playersprite.crouching = True
elif event.key == pygame.K_SPACE:
self.playersprite.jumping = True
elif event.key == pygame.K_q and self.playersprite.ammunition != 0:
self.playersprite.shooting = True
elif event.type == pygame.KEYUP:
if event.key == pygame.K_ESCAPE:
self.done = True
elif event.key == pygame.K_LCTRL:
self.playersprite.crouching = False
elif event.key == pygame.K_q:
self.playersprite.shooting = False
def draw(self, surface):
surface.fill(pygame.Color("black"))
pygame.draw.rect(surface, "red", self.bottomrect)
surface.blit(self.displayedimage, (self.displayedrect))
surface.blit(self.enemysprite.image, (self.enemysprite.rect))
surface.blit(self.ammosprite.image, (self.ammosprite.rect))
self.healthbar.draw(surface)
self.bigenemy.draw(surface)
self.bulletgroup.draw(surface)
surface.blit(self.scoresurf, (0, 0))
surface.blit(self.ammosurf, (0, 1000))
if self.points > self.highscore: surface.blit(self.nhssurf, (1920 / 2 - 100, 1080 / 2))
def lost(self):
self.enemysprite.startposx = 1920
self.enemysprite.startposy = self.enemysprite.gettypeenemy()
self.enemysprite.speed = 10
self.highscorefunc()
self.points = 0
self.playersprite.ammunition = 30
def collidecheck(self):
self.playermask = pygame.mask.from_surface(self.displayedimage)
self.enemymask = pygame.mask.from_surface(self.enemysprite.image)
offsetx = self.enemysprite.rect.left - self.displayedrect.left
offsety = self.enemysprite.rect.top - self.displayedrect.top
if self.displayedrect.colliderect(self.enemysprite.rect):
if self.playermask.overlap(self.enemymask, (offsetx, offsety)):
self.lost()
self.done = True
elif self.enemysprite.rect.x < 0 and self.enemysprite.speed < 25:
self.points += 1
self.enemysprite.speed += 1
elif self.enemysprite.speed > 25:
self.enemysprite.speed += .5
elif self.displayedrect.colliderect(self.ammosprite.rect):
self.ammosprite.startposx = 2300
self.playersprite.ammunition += 30
elif pygame.sprite.groupcollide(self.bigenemy,self.bulletgroup,False,True):
self.bigenemy.sprite.health -= 10
def shooting(self, dt):
if self.playersprite.ammunition != 0:
if self.playersprite.shooting and not self.playersprite.jumping and not self.playersprite.crouching:
self.time_active += dt
if self.time_active >= 100:
self.bulletgroup.add(self.playersprite.createbullet())
self.time_active = 0
self.playersprite.ammunition -= 1
else:
self.playersprite.shooting = False
def highscorefunc(self):
if self.points > self.highscore:
self.highscore = self.points
with open(path.join(self.dir, self.HS_FILE), 'w') as f:
f.write(str(self.highscore))
def animation(self):
if not self.playersprite.shooting and not self.playersprite.jumping and not self.playersprite.crouching:
if self.playersprite.index >= len(self.playersprite.basicanimation):
self.playersprite.index = 0
self.displayedimage = self.playersprite.basicanimation[int(self.playersprite.index)]
self.playersprite.index += .1
elif self.playersprite.shooting and not self.playersprite.jumping:
if self.playersprite.index >= len(self.playersprite.shootanimation):
self.playersprite.index = 0
self.displayedimage = self.playersprite.shootanimation[int(self.playersprite.index)]
self.playersprite.index += .1
elif self.playersprite.jumping:
self.displayedimage = self.playersprite.imagejump
elif self.playersprite.crouching:
self.displayedimage = self.playersprite.slidingimage
def healthbaranimation(self):
if self.bigenemy.sprite.health < 90:
self.healthbar.spritedict.index = 1
if self.bigenemy.sprite.health < 80:
self.healthbar.sprite.index = 2
if self.bigenemy.sprite.health < 70:
self.healthbar.sprite.index = 3
if self.bigenemy.sprite.health < 60:
self.healthbar.sprite.index = 4
if self.bigenemy.sprite.health < 50:
self.healthbar.sprite.index = 5
if self.bigenemy.sprite.health < 40:
self.healthbar.sprite.index = 6
if self.bigenemy.sprite.health < 30:
self.healthbar.sprite.index = 7
if self.bigenemy.sprite.health < 20:
self.healthbar.sprite.index = 8
if self.bigenemy.sprite.health < 10:
self.healthbar.sprite.index = 9
def spawnbigenemies(self):
if self.bigenemyexisting:
if self.bigenemy.sprite.health < 3:
self.bigenemy.add(BIGENEMY())
self.bigenemyexisting = True
def update(self, dt):
try:
self.bigenemy.sprite.update()
except:
pass
self.healthbaranimation()
self.healthbar.update()
self.playersprite.jump()
self.animation()
self.shooting(dt)
self.bulletgroup.update()
self.enemysprite.update()
self.ammosprite.update()
self.collidecheck()
self.spawnbigenemies()
self.scoretext = f"SCORE: {self.points}"
self.scoresurf = self.font.render(self.scoretext, True, "black")
self.ammotext = f"AMMO:{self.playersprite.ammunition}"
self.ammosurf = self.font.render(self.ammotext, True, "red")

Updating Score Pygame Pong

For whatever reason, the score display in my pong game is not updating. It just keeps saying "0". I checked to see if the score was actually being updated in the logic of the game and it is (printed it out to console). I have the text being "blitted" every time a new display is drawn so can anyone tell me why it won't update?
import pygame
import random
pygame.init()
# Create colors
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
# Create screen and set screen caption
size = (700, 500)
screen = pygame.display.set_mode(size)
pygame.display.set_caption("Pong")
# Loop until user clicks close button
done = False
# Used to manage how fast the screen is updated
clock = pygame.time.Clock()
# Create player class
class Player():
# Initialize player paddles
def __init__(self, x, y, color, height, width):
self.x = x
self.y = y
self.color = color
self.height = height
self.width = width
self.y_speed = 0
self.score = 0
self.font = pygame.font.SysFont('Calibri', 24, True, False)
self.display_score = self.font.render("Score: %d " % (self.score), True, WHITE)
# Updates with new position of paddle every frame
def draw(self, y):
pygame.draw.rect(screen, self.color, [self.x, y, self.height, self.width])
# Keeps paddle from going off screen
def keepOnScreen(self):
if self.y < 0:
self.y = 0
elif self.y > 410:
self.y = 410
# Create Ball class
class Ball():
# Initialize ball in the middle of the screen with no movement
def __init__(self, color, height, width):
self.x = 325
self.y = random.randrange(150, 350)
self.color = color
self.height = height
self.width = width
self.y_speed = 0
self.x_speed = 0
# Updates new position of ball every frame
def draw(self, x, y):
pygame.draw.rect(screen, self.color, [x, y, self.height, self.width])
# Create instances of both players and ball
player1 = Player(50, 100, WHITE, 25, 90)
player2 = Player(625, 100, WHITE, 25, 90)
ball = Ball(WHITE, 20, 20)
# --- Main Program Loop ---
while not done:
# --- Main event loop
for event in pygame.event.get(): # User did something
if event.type == pygame.QUIT: # If user clicked close
done = True # We are done so we exit this loop
if event.type == pygame.KEYDOWN: # Players utilize keyboard to move paddles
if event.key == pygame.K_w:
player1.y_speed = -6
if event.key == pygame.K_UP:
player2.y_speed = -6
if event.key == pygame.K_s:
player1.y_speed = 6
if event.key == pygame.K_DOWN:
player2.y_speed = 6
if event.key == pygame.K_SPACE: # Starts the ball movement
ball.x_speed = 3 * random.randrange(-1, 1, 2)
ball.y_speed = 3 * random.randrange(-1, 1, 2)
if event.type == pygame.KEYUP:
if event.key == pygame.K_w:
player1.y_speed = 0
if event.key == pygame.K_UP:
player2.y_speed = 0
if event.key == pygame.K_s:
player1.y_speed = 0
if event.key == pygame.K_DOWN:
player2.y_speed = 0
# Calculate the movement of the players
player1.y += player1.y_speed
player2.y += player2.y_speed
# Prevents paddles from going off-screen
player1.keepOnScreen()
player2.keepOnScreen()
# Checks to see if ball has made contact with paddle, then reverses direction of the ball
# Had to give a 4 pixel buffer since the ball won't always exactly hit the same part of paddle in x direction
if ball.x <= player1.x + 27 and (ball.x >= player1.x + 23):
if ball.y >= player1.y and (ball.y <= player1.y + 100):
ball.x_speed *= -1
if ball.x >= player2.x - 27 and (ball.x <= player2.x - 23):
if ball.y >= player2.y and (ball.y <= player2.y + 100):
ball.x_speed *= -1
# Checks to see if ball has made contact with top or bottom of screen
if ball.y <= 0 or ball.y >= 480:
ball.y_speed *= -1
# Calculates movement of the ball
ball.x += ball.x_speed
ball.y += ball.y_speed
# Updates score
if ball.x < 0:
player2.score += 1
ball.__init__(WHITE, 20, 20)
if ball.x > 700:
player1.score += 1
ball.__init__(WHITE, 20, 20)
# Set background
screen.fill(BLACK)
# Draw players and ball on screen
player1.draw(player1.y)
player2.draw(player2.y)
ball.draw(ball.x, ball.y)
screen.blit(player1.display_score, [0, 0])
screen.blit(player2.display_score, [615, 0])
# Update display
pygame.display.flip()
# Limit to 60 frames per second
clock.tick(60)
# Close the window and quit
pygame.quit()
It looks like the problem is you're setting each player's display_score only in their __init__ function.
# Initialize player paddles
def __init__(self, x, y, color, height, width):
...
self.score = 0
...
self.display_score = self.font.render("Score: %d " % (self.score), True, WHITE)
Because of the way you initialize the variable, changing the value of player.score does not change the value of player.display_score.
Solution 1
You could change the value of display_score when you change the player scores, which would be easily done through a function call:
def player_scores( player, ball ):
player.score += 1
player.display_score = player.font.render("Score: %d " % (player.score), True, WHITE)
ball.__init__(WHITE, 20, 20)
Then in your game loop:
# Updates score
if ball.x < 0:
player_scores( player2, ball )
if ball.x > 700:
player_scores( player1, ball )
Solution 2
You could render the score text when you display it, rather than creating it on the players. In your game loop:
screen.blit(player1.font.render("Score: %d " % (player1.score), True, WHITE), [0, 0])
screen.blit(player2.font.render("Score: %d " % (player2.score), True, WHITE), [615, 0])
This invalidates the display_score variable completely

Categories

Resources