Pygame - drop enemies randomly in time [duplicate] - python

This question already has an answer here:
How can one continuously generate and track several random objects with a time delay in pygame? [duplicate]
(1 answer)
Closed 2 years ago.
I'm trying to design my first game with Python and Pygame to improve my coding skills.
Basically, the player has 4 circles and the goal is to prevent the Cube (or enemies) from reaching the middle of the screen, by killing them with the keyboard arrows.
The game works fine : circles move perfectly, the cubes are going faster as the levels go up, the scoring system is ok, etc...
The last step I can't figure out is to make the Cubes drop a bit more randomly, otherwise it's too easy. Currently, when a cube is killed, it reappear instantly on one of the 4 edges of the screen. I figured out how to position the cube randomly, but the timing doesn't work.
I tried Python sleep, but it freezes the screen. I tried adding a line to specify a delay with "delay = random.random()", and adding "delay <0.1" if condition in an if statement. My last idea was to use a time counter, equals to clock.tick(), and adding a condition : timecounter >3000 in an if statement. All these attempts don't work for me.
Any idea ? Here's my code so far:
# ---------- Packages and Inits ----------
import pygame, random, math, sys
pygame.init()
# ---------- Settings ----------
SCREEN_WIDTH = 600
SCREEN_HEIGHT = 600
FPS = 60
SPEED = 1
CIRCLE_RADIUS = 50
ENEMY_SIZE = 40
# Colors
RED = (255,000,000)
BLUE = (000,000,255)
YELLOW = (255,255,000)
GREEN = (000,128,000)
BLACK = (000,000,000)
# ---------- Classes ----------
class Enemies:
def __init__( self, x, y, size=ENEMY_SIZE, thick=5, color=BLUE, speed=1, position="top"):
self.rect = pygame.Rect(0, 0, size, size)
if ( x == 0 and y == 0 ):
self.randomise()
self.rect.centerx = x
self.rect.centery = y
self.size = size
self.thick = thick
self.color = color
self.speed = speed
self.calcDirection()
self.position = position
def calcDirection( self ):
self.x_float = 1.0 * self.rect.centerx
self.y_float = 1.0 * self.rect.centery
# Determine direction vector from (x,y) to the centre of the screen
self.position_vector = pygame.math.Vector2( self.x_float, self.y_float )
self.velocity_vector = pygame.math.Vector2( SCREEN_WIDTH/2 - self.x_float, SCREEN_HEIGHT/2 - self.y_float )
self.velocity_vector = self.velocity_vector.normalize()
def update( self ):
x_delta = self.speed * self.velocity_vector[0]
y_delta = self.speed * self.velocity_vector[1]
self.x_float += x_delta
self.y_float += y_delta
self.rect.centerx = int( self.x_float )
self.rect.centery = int( self.y_float )
def draw( self, screen):
pygame.draw.rect(screen, self.color, self.rect )
def reachedPoint( self, x, y ):
return self.rect.collidepoint( x, y )
def randomise( self ):
self.rect.centerx = SCREEN_WIDTH//2
self.rect.centery = SCREEN_HEIGHT//2
side = random.randint( 0, 4 )
if ( side == 0 ):
self.rect.centery = SCREEN_HEIGHT
self.color = GREEN
self.position= "bot"
elif ( side == 1 ):
self.rect.centery = 0
self.color = YELLOW
self.position= "top"
elif ( side == 2 ):
self.rect.centerx = 0
self.color = BLUE
self.position= "left"
else:
self.rect.centerx = SCREEN_WIDTH
self.color = RED
self.position= "right"
self.calcDirection()
def set_speed( self, score):
if score < 25 : self.speed = 0.5
elif score < 50 : self.speed = 0.75
elif score < 100: self.speed = 1
elif score < 250: self.speed = 1.25
elif score < 500: self.speed = 1.5
else: self.speed = 2
return self.speed
class Circle:
def __init__(self, x, y, radius=CIRCLE_RADIUS, thick=5, color=BLUE, speed=SPEED, position="top"):
self.rect = pygame.Rect(0, 0, 2*radius, 2*radius)
self.rect.centerx = x
self.rect.centery = y
self.radius = radius
self.thick = thick
self.color = color
self.speed = speed
self.position = position
if speed >= 0:
self.directionX = 'right'
self.direction = 'up'
else:
self.directionX = 'left'
self.direction = 'down'
def draw(self, screen):
pygame.draw.circle(screen, self.color, self.rect.center, self.radius, self.thick)
def swing(self):
if self.position == "top":
self.rect.y -= self.speed
if self.rect.top <= 0 and self.direction == 'up':
self.direction = 'down'
self.speed = -self.speed
elif self.rect.bottom > int(SCREEN_HEIGHT/2) - self.radius and self.direction == 'down':
self.direction = 'up'
self.speed = -self.speed
if self.position == "bot":
self.rect.y -= self.speed
if self.rect.top < int(SCREEN_HEIGHT/2) + self.radius and self.direction == 'up':
self.direction = 'down'
self.speed = -self.speed
elif self.rect.bottom >= SCREEN_HEIGHT and self.direction == 'down':
self.direction = 'up'
self.speed = -self.speed
if self.position == "left":
self.rect.x -= self.speed
if self.rect.right > int(SCREEN_WIDTH/2) - self.radius and self.directionX == 'left':
self.directionX = 'right'
self.speed = -self.speed
elif self.rect.left <= 0 and self.directionX == 'right':
self.directionX = 'left'
self.speed = -self.speed
if self.position == "right":
self.rect.x -= self.speed
if self.rect.left < int(SCREEN_WIDTH/2) + self.radius and self.directionX == 'right':
self.directionX = 'left'
self.speed = -self.speed
elif self.rect.right >= SCREEN_WIDTH and self.directionX == 'left':
self.directionX = 'right'
self.speed = -self.speed
def isCollision(self, enemyX, enemyY, circleX, circleY):
distance = math.sqrt((math.pow(enemyX-circleX,2))+(math.pow(enemyY-circleY,2)))
if distance < 65:
return True
else:
return False
# ---------- Other Functions ----------
def set_number_of_enemies(score):
if score < 25 : number_of_enemies = 3
elif score < 50 : number_of_enemies = 4
elif score < 100: number_of_enemies = 5
elif score < 250: number_of_enemies = 6
elif score < 500: number_of_enemies = 7
else: number_of_enemies = 8
return number_of_enemies
# ---------- Main ----------
def main():
# Settings
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
screen_rect = screen.get_rect()
myFont = pygame.font.SysFont("monospace", 25)
clock = pygame.time.Clock()
game_over = False
# Variables
lifes = 5
score = 0
number_of_enemies = 3
# We create an empty list of enemies, as we want them to drop randomly
all_enemies = []
# Start with 4 circles
all_circles = [
Circle ( screen_rect.centerx , screen_rect.centery - 2*CIRCLE_RADIUS , position = "top" ),
Circle ( screen_rect.centerx , screen_rect.centery + 2*CIRCLE_RADIUS , position = "bot" ),
Circle ( screen_rect.centerx + 2*CIRCLE_RADIUS , screen_rect.centery , position = "right"),
Circle ( screen_rect.centerx - 2*CIRCLE_RADIUS , screen_rect.centery , position = "left" )]
while not game_over:
screen.fill(BLACK) # This has to be inside the while not game_over loop
time_counter = clock.tick()
print(time_counter)
# Circles
for c in all_circles:
c.draw(screen) # Place circles on the screen
c.swing() # Move circles from center to edges, and from edges to center, back and forth
# Set number of enemies
number_of_enemies = set_number_of_enemies(score) # Get the number of enemies we want, based on the score
if len(all_enemies) < number_of_enemies and time_counter > 3000: # Add if necessary
all_enemies.append(Enemies(int(SCREEN_WIDTH/2), 0, color = YELLOW, position = "top")) # We add a top enemy, but we randomise it right after
for e in all_enemies:
e.randomise()
time_counter = 0
# Enemies
for e in all_enemies:
e.draw(screen) # Place enemies on the screen
e.set_speed(score) # Set speed difficulty for enemies
e.update() # Move enemies from the edges of the screen towards the center
if ( e.reachedPoint( SCREEN_WIDTH//2, SCREEN_HEIGHT//2 ) ): # If the enemy reaches the middle, you lose a lifepoint and a new enemy is generated
lifes -=1
e.randomise()
# Scoring and lifepoints systems
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
for c in all_circles:
if event.type == pygame.KEYDOWN:
# LEFT
if event.key == pygame.K_LEFT and c.position == "left":
hits = [e for e in all_enemies if c.isCollision(e.rect.centerx,e.rect.centery,c.rect.centerx,c.rect.centery)]
if not hits:
lifes -=1
for e in hits:
if len(hits) == 1:
score +=1
if len(hits) == 2:
score +=5/len(hits)
if len(hits) == 3:
score +=10/len(hits)
e.randomise()
# RIGHT
if event.key == pygame.K_RIGHT and c.position == "right":
hits = [e for e in all_enemies if c.isCollision(e.rect.centerx,e.rect.centery,c.rect.centerx,c.rect.centery)]
if not hits:
lifes -=1
for e in hits:
if len(hits) == 1:
score +=1
if len(hits) == 2:
score +=5/len(hits)
if len(hits) == 3:
score +=10/len(hits)
e.randomise()
# TOP
if event.key == pygame.K_UP and c.position == "top":
hits = [e for e in all_enemies if c.isCollision(e.rect.centerx,e.rect.centery,c.rect.centerx,c.rect.centery)]
if not hits:
lifes -=1
for e in hits:
if len(hits) == 1:
score +=1
if len(hits) == 2:
score +=5/len(hits)
if len(hits) == 3:
score +=10/len(hits)
e.randomise()
# BOT
if event.key == pygame.K_DOWN and c.position == "bot":
hits = [e for e in all_enemies if c.isCollision(e.rect.centerx,e.rect.centery,c.rect.centerx,c.rect.centery)]
if not hits:
lifes -=1
for e in hits:
if len(hits) == 1:
score +=1
if len(hits) == 2:
score +=5/len(hits)
if len(hits) == 3:
score +=10/len(hits)
e.randomise()
# Game Over condition
if lifes == 0:
game_over = True
# Score / Lifes / Number of Enemies
print_lifes = myFont.render("Lifes:" + str(round(lifes)), 1, RED)
screen.blit(print_lifes, (10, SCREEN_HEIGHT-50))
print_score = myFont.render("Score:" + str(round(score)), 1, RED)
screen.blit(print_score, (10, 10))
print_enemies = myFont.render("# of Enemies:" + str(round(number_of_enemies)), 1, RED)
screen.blit(print_enemies, (10, 60))
pygame.display.update()
clock.tick(FPS)
main()
pygame.quit()

I recommend to use a timer event. Use pygame.time.set_timer() to repeatedly create an USEREVENT. e.g.:
milliseconds_delay = 3000
enemy_spawn_event = pygame.USEREVENT + 1
pygame.time.set_timer(enemy_spawn_event, milliseconds_delay)
Note, in pygame customer events can be defined. Each event needs a unique id. The ids for the user events have to start at pygame.USEREVENT. In this case pygame.USEREVENT+1 is the event id for the timer event, which spawns the enemies.
Create a new enemy when the event occurs in the event loop:
milliseconds_delay = 3000
enemy_spawn_event = pygame.USEREVENT + 1
pygame.time.set_timer(enemy_spawn_event, milliseconds_delay)
while not game_over:
# [...]
# Scoring and lifepoints systems
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
if event.type == enemy_spawn_event and len(all_enemies) < number_of_enemies:
all_enemies.append(Enemies(int(SCREEN_WIDTH/2), 0, color = YELLOW, position = "top"))
for e in all_enemies:
e.randomise()
time_counter = 0

Related

Sprite Jittering and Repeatedly Bliting Inside Pygame Project

I have been having this issue where when i run my code, the sprites keep jittering even though the screen doesn't force them to do this.
This is where I define the screen size
This is where I update my actions and draw
Here is the part where I load my images from the spritesheet
Any help will be much appreciated.
I tried importing the sprites from the spritesheet onto the game with the animations all working however there are no animations and the sprite continues to jitter up and down
if you would like the code for my fighter class please see below:
def load_images(self, sprite_sheet, animation_steps):
#extract the certain animation images from spritesheet
animation_list = []
for y, animation in enumerate(animation_steps):
temporary_img_list = []
for x in range(animation):
temporaryimg = sprite_sheet.subsurface(x , y , self.size, self.size)
temporary_img_list.append(pygame.transform.scale(temporaryimg, (self.size * self.image_scale, self.image_scale * self.size)))
animation_list.append(temporary_img_list)
print(animation_list)
return animation_list
def move(self, screen_base, screen_height, surface, target):
speed = 10
gravity = 2
dx = 0
dy = 0
self.running = False
self.attack_type = 0
#key-presses recall
key = pygame.key.get_pressed() = pygame.key.get_pressed()
#can only happen if no other attack is occuring
if self.attacking == False:
#movement defined here
if key[pygame.K_a]:
dx = -speed
self.running = True
if key[pygame.K_d]:
dx = speed
self.running = True
#jump is defined here
if key[pygame.K_w] and self.jump == False:
self.vel_y = -30
self.jump = True
#attack is defined here
if key[pygame.K_k] or key[pygame.K_j]:
self.attack(surface, target)
#determine which attack type was used when the key was pressed
if key[pygame.K_k]:
self.attack_type = 1
if key[pygame.K_j]:
self.attack_type = 2
#apply gravity for the jumps
self.vel_y += gravity
dy += self.vel_y
#this makes sure that the character does not go off the screen
if self.rect.left + dx < 0:
dx = -self.rect.left
if self.rect.right + dx > screen_base:
dx = screen_base - self.rect.right
if self.rect.bottom + dy > screen_height - 207:
self.vel_y = 0
self.jump = False
dy = screen_height - 207 - self.rect.bottom
#ensures the players face each other
if target.rect.centerx > self.rect.centerx:
self.flip = False
else:
self.flip = True
#apply the attack cooldown to balance the amount of attacks possible
if self.attack_cooldown > 0:
self.attack_cooldown -= 1
#update the player position constantly
self.rect.x += dx
self.rect.y += dy
#handle the animation updates
def update(self):
#check what action the player is currently doing
if self.health <= 0:
self.health = 0
self.alive = False
self.update_action(6)
elif self.hit == True:
self.update_action(5)
elif self.attacking == True:
if self.attack_type == 1:
self.update_action(5) # 3 = Attack
elif self.attack_type == 2:
self.update_action(6) # 4 = Attack2
elif self.jump == True:
self.update_action(7) # 7 = Jump
elif self.running == True:
self.update_action(2) # 2 = Run
else:
self.update_action(0) # 0 = Idle
animation_cooldown = 50
#update the image
self.image = self.animation_list[self.action][self.frameindex]
#check if enough time has passed since the last update
if pygame.time.get_ticks() - self.update_time > animation_cooldown:
self.frameindex += 1
self.updatetime = pygame.time.get_ticks()
#check if the animation has finished
if self.frameindex >= len(self.animation_list[self.action]):
#if the player is dead end the animation
if self.alive == False:
self.frameindex = len(self.animation_list[self.action]) - 1
else:
self.frameindex = 0
#check if an attack was executed
if self.action == 5 or self.action == 6:
self.attacking = False
self.attack_cooldown = 20
#check if damage was taken
if self.action == 7:
self.hit = False
#if the player was in the middle on an attack, then the attack is stopped
self.attacking= False
self.attack_cooldown = 20
def attack(self, surface, target):
if self.attack_cooldown == 0:
self.attacking = True
attacking_rect = pygame.Rect(self.rect.centerx - (2 * self.rect.width * self.flip), self.rect.y, 2 * self.rect.width, self.rect.height)
if attacking_rect.colliderect(target.rect):
target.health -= 5
target.hit = True
pygame.draw.rect(surface, (0, 255, 0), attacking_rect)
def update_action(self, new_action):
#check if the new action is different to the previous one
if new_action != self.action:
self.action = new_action
#update the animation settings
self.frameindex = 0
self.update_time = pygame.time.get_ticks()
def draw(self, surface):
img = pygame.transform.flip(self.image, self.flip, False)
#pygame.draw.rect(surface,(255, 0, 0), self.rect)
surface.blit(img, (self.rect.x - (self.offset[0] * self.image_scale), self.rect.y - (self.offset[1] * self.image_scale)))
Most likely the sprites are not cropped well from the sprite sheet. If there is random spacing on the cropped sprites (for example: the sprites are different sizes or some sprites have a border at the bottom and some sprites have a border at the top), then you get a jittery and wobbly character.

drop several cubes in pygame

I started Python last week and I'm trying to create a game to learn. There are 4 moving circles in the screen, moving from the middle of the screen to the 4 edges. I got this part I think.
Now I'm trying to drop cubes from the 4 edges to the middle. The goal is to stop those cubes from reaching the middle of the screen, by pressing keyboard arrows. If one cube reaches the center, it's game over.
I managed to drop 4 cubes to the middle at the same time, but the idea is to drop randomly a few cubes.
Any idea how I should proceed to create this ? or what should I learn to make this happen ?
here's my work so far:
# ---------- Packages ----------
import pygame, random
pygame.init()
# ---------- Constants ----------
SCREEN_WIDTH = 600
SCREEN_HEIGHT = 600
FPS = 60
SPEED = 1
SPEED_ENEMIES = 1
CIRCLE_RADIUS = 50
ENNEMY_SIZE = 40
red = (255,000,000)
blue = (000,000,255)
yellow = (255,255,000)
green = (000,128,000)
pink = (255,192,203)
black = (000,000,000)
transparent = (0, 0, 0, 0)
# ---------- Classes ----------
class Ennemies:
def __init__(self, x, y, size=ENNEMY_SIZE, thick=5, color=blue, speed=SPEED_ENEMIES):
self.rect = pygame.Rect(0, 0, 2*size, 2*size)
self.rect.centerx = x
self.rect.centery = y
self.size = size
self.thick = thick
self.color = color
self.speed = speed
if speed >= 0:
self.directionX = 'right'
self.direction = 'up'
else:
self.directionX = 'left'
self.direction = 'down'
def draw(self, screen):
pygame.draw.rect(screen, self.color,(self.rect.centerx,self.rect.centery,self.size,self.size))
def move_up(self):
self.rect.y -= self.speed
if self.rect.centery < int(SCREEN_HEIGHT/2) - self.size/2:
self.color = transparent
def move_down(self):
self.rect.y += self.speed
if self.rect.centery > int(SCREEN_HEIGHT/2) - self.size/2:
self.color = transparent
def move_left(self):
self.rect.x -= self.speed
if self.rect.centerx < int(SCREEN_WIDTH/2) - self.size/2:
self.color = transparent
def move_right(self):
self.rect.x += self.speed
if self.rect.centerx > int(SCREEN_WIDTH/2) - self.size/2:
self.color = transparent
class Circle:
def __init__(self, x, y, radius=CIRCLE_RADIUS, thick=5, color=blue, speed=SPEED):
self.rect = pygame.Rect(0, 0, 2*radius, 2*radius)
self.rect.centerx = x
self.rect.centery = y
self.radius = radius
self.thick = thick
self.color = color
self.speed = speed
if speed >= 0:
self.directionX = 'right'
self.direction = 'up'
else:
self.directionX = 'left'
self.direction = 'down'
def draw(self, screen):
pygame.draw.circle(screen, self.color, self.rect.center, self.radius, self.thick)
def swing_top(self):
self.rect.y -= self.speed
if self.rect.top <= 0 and self.direction == 'up':
self.direction = 'down'
self.speed = -self.speed
if self.rect.bottom > int(SCREEN_HEIGHT/2) - self.radius and self.direction == 'down':
self.direction = 'up'
self.speed = -self.speed
def swing_bot(self):
self.rect.y -= self.speed
if self.rect.top < int(SCREEN_HEIGHT/2) + self.radius and self.direction == 'up':
self.direction = 'down'
self.speed = -self.speed
if self.rect.bottom >= SCREEN_HEIGHT and self.direction == 'down':
self.direction = 'up'
self.speed = -self.speed
def swing_left(self):
self.rect.x -= self.speed
if self.rect.right > int(SCREEN_WIDTH/2) - self.radius and self.directionX == 'left':
self.directionX = 'right'
self.speed = -self.speed
if self.rect.left <= 0 and self.directionX == 'right':
self.directionX = 'left'
self.speed = -self.speed
def swing_right(self):
self.rect.x -= self.speed
if self.rect.left < int(SCREEN_WIDTH/2) + self.radius and self.directionX == 'right':
self.directionX = 'left'
self.speed = -self.speed
if self.rect.right >= SCREEN_WIDTH and self.directionX == 'left':
self.directionX = 'right'
self.speed = -self.speed
# ---------- Main ----------
def main():
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
screen_rect = screen.get_rect()
enemyTop = [
Ennemies(int(SCREEN_WIDTH/2)-ENNEMY_SIZE/2, 0, color=yellow)
]
enemyBot = [
Ennemies(int(SCREEN_WIDTH/2)-ENNEMY_SIZE/2, SCREEN_HEIGHT-ENNEMY_SIZE, color=green)
]
enemyLeft = [
Ennemies(0, int(SCREEN_HEIGHT/2)-ENNEMY_SIZE/2, color=blue)
]
enemyRight = [
Ennemies(SCREEN_WIDTH-ENNEMY_SIZE, int(SCREEN_HEIGHT/2)-ENNEMY_SIZE/2, color = red)
]
circleTop = [
Circle(screen_rect.centerx, screen_rect.centery - 2*CIRCLE_RADIUS)
]
circleBot = [
Circle(screen_rect.centerx, screen_rect.centery + 2*CIRCLE_RADIUS)
]
circleRight = [
Circle(screen_rect.centerx + 2*CIRCLE_RADIUS, screen_rect.centery)
]
circleLeft = [
Circle(screen_rect.centerx - 2*CIRCLE_RADIUS, screen_rect.centery)
]
clock = pygame.time.Clock()
game_over = False
while not game_over:
for event in pygame.event.get():
if event.type == pygame.QUIT:
game_over = True
for item in enemyTop:
item.move_down()
for item in enemyBot:
item.move_up()
for item in enemyLeft:
item.move_right()
for item in enemyRight:
item.move_left()
for item in circleTop:
item.swing_top()
for item in circleBot:
item.swing_bot()
for item in circleLeft:
item.swing_left()
for item in circleRight:
item.swing_right()
screen.fill(black)
for item in enemyTop:
item.draw(screen)
for item in enemyBot:
item.draw(screen)
for item in enemyLeft:
item.draw(screen)
for item in enemyRight:
item.draw(screen)
for item in circleTop:
item.draw(screen)
for item in circleBot:
item.draw(screen)
for item in circleLeft:
item.draw(screen)
for item in circleRight:
item.draw(screen)
pygame.display.update()
clock.tick(FPS)
main()
pygame.quit()
Thanks
I think the answer here is to modify your Enemies object so that it works better as a more generic object.
To this end I modified the class so that a "cube" starts at a random position at the side, and always heads into the middle (I'm, not sure if this is exactly what you planned, but it's my interpretation of your game description).
This is achieved by calculating a direction vector (since it could now be a diagonal line), and then adding this amount (times speed) to the location each "tick". One problem this creates, is that if the component of the direction is very small - say 0.1 pixels/tick, then the addition of this amount is always rounded away to nothing. So the class keeps the location as a floating point number, and only converts back to integer when the Rect is updated.
The code now keeps a list of Enemies objects. This makes it easy to iterate through this list, updating, drawing and checking positions. To add more enemies, simply add extras to this list - perhaps as levels get harder.
The next change was for the Enemy to start in a random position if the supplied co-ordinates are (0,0). This allows the code to re-set the old enemies to a new random starting position, or add new ones.
# ---------- Packages ----------
import pygame, random
pygame.init()
# ---------- Constants ----------
SCREEN_WIDTH = 600
SCREEN_HEIGHT = 600
FPS = 60
SPEED = 1
SPEED_ENEMIES = 1
CIRCLE_RADIUS = 50
ENEMY_SIZE = 40
red = (255,000,000)
blue = (000,000,255)
yellow = (255,255,000)
green = (000,128,000)
pink = (255,192,203)
black = (000,000,000)
transparent = (0, 0, 0, 0)
# ---------- Classes ----------
class Enemies:
def __init__(self, x, y, size=ENEMY_SIZE, thick=5, color=blue, speed=SPEED_ENEMIES):
self.rect = pygame.Rect(0, 0, size, size)
# if x and y are zero, choose a random starting point
if ( x == 0 and y == 0 ):
self.randomise()
self.rect.centerx = x
self.rect.centery = y
self.size = size
self.thick = thick
self.color = color
self.speed = speed
self.calcDirection()
def calcDirection( self ):
""" Work out the direction vector to (0,0) """
self.x_float = 1.0 * self.rect.centerx # keep these to stop rounding errors
self.y_float = 1.0 * self.rect.centery
# Determine direction vector from (x,y) to the centre of the screen
self.position_vector = pygame.math.Vector2( self.x_float, self.y_float )
self.velocity_vector = pygame.math.Vector2( SCREEN_WIDTH/2 - self.x_float, SCREEN_HEIGHT/2 - self.y_float )
self.velocity_vector = self.velocity_vector.normalize()
#print("Velocity Vector = "+str(self.velocity_vector) )
def update( self ):
x_delta = self.speed * self.velocity_vector[0]
y_delta = self.speed * self.velocity_vector[1]
self.x_float += x_delta
self.y_float += y_delta
self.rect.centerx = int( self.x_float )
self.rect.centery = int( self.y_float )
def draw(self, screen):
pygame.draw.rect(screen, self.color, self.rect )
def reachedPoint( self, x, y ):
return self.rect.collidepoint( x, y )
def randomise( self ):
""" Pick a random starting point """
self.rect.centerx = random.randint( 0, SCREEN_WIDTH )
self.rect.centery = random.randint( 0, SCREEN_HEIGHT )
side = random.randint( 0, 4 )
if ( side == 0 ): # top
self.rect.centery = SCREEN_HEIGHT
elif ( side == 1 ): # bottom
self.rect.centery = 0
elif ( side == 2 ): # left
self.rect.centerx = 0
else: # right
self.rect.centerx = SCREEN_WIDTH
self.calcDirection()
class Circle:
def __init__(self, x, y, radius=CIRCLE_RADIUS, thick=5, color=blue, speed=SPEED):
self.rect = pygame.Rect(0, 0, 2*radius, 2*radius)
self.rect.centerx = x
self.rect.centery = y
self.radius = radius
self.thick = thick
self.color = color
self.speed = speed
if speed >= 0:
self.directionX = 'right'
self.direction = 'up'
else:
self.directionX = 'left'
self.direction = 'down'
def draw(self, screen):
pygame.draw.circle(screen, self.color, self.rect.center, self.radius, self.thick)
def swing_top(self):
self.rect.y -= self.speed
if self.rect.top <= 0 and self.direction == 'up':
self.direction = 'down'
self.speed = -self.speed
if self.rect.bottom > int(SCREEN_HEIGHT/2) - self.radius and self.direction == 'down':
self.direction = 'up'
self.speed = -self.speed
def swing_bot(self):
self.rect.y -= self.speed
if self.rect.top < int(SCREEN_HEIGHT/2) + self.radius and self.direction == 'up':
self.direction = 'down'
self.speed = -self.speed
if self.rect.bottom >= SCREEN_HEIGHT and self.direction == 'down':
self.direction = 'up'
self.speed = -self.speed
def swing_left(self):
self.rect.x -= self.speed
if self.rect.right > int(SCREEN_WIDTH/2) - self.radius and self.directionX == 'left':
self.directionX = 'right'
self.speed = -self.speed
if self.rect.left <= 0 and self.directionX == 'right':
self.directionX = 'left'
self.speed = -self.speed
def swing_right(self):
self.rect.x -= self.speed
if self.rect.left < int(SCREEN_WIDTH/2) + self.radius and self.directionX == 'right':
self.directionX = 'left'
self.speed = -self.speed
if self.rect.right >= SCREEN_WIDTH and self.directionX == 'left':
self.directionX = 'right'
self.speed = -self.speed
# ---------- Main ----------
def main():
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
screen_rect = screen.get_rect()
# Start with 4 enemies
all_enemies = [
Enemies(random.randint(1,SCREEN_WIDTH), 1, color=yellow), # bottom
Enemies(random.randint(1,SCREEN_WIDTH), SCREEN_HEIGHT-ENEMY_SIZE, color=green), # top
Enemies(1, random.randint(1,SCREEN_HEIGHT), color=blue), # left
Enemies(SCREEN_WIDTH-ENEMY_SIZE, random.randint(1,SCREEN_HEIGHT), color = red) # right
]
circleTop = [
Circle(screen_rect.centerx, screen_rect.centery - 2*CIRCLE_RADIUS)
]
circleBot = [
Circle(screen_rect.centerx, screen_rect.centery + 2*CIRCLE_RADIUS)
]
circleRight = [
Circle(screen_rect.centerx + 2*CIRCLE_RADIUS, screen_rect.centery)
]
circleLeft = [
Circle(screen_rect.centerx - 2*CIRCLE_RADIUS, screen_rect.centery)
]
clock = pygame.time.Clock()
game_over = False
while not game_over:
for event in pygame.event.get():
if event.type == pygame.QUIT:
game_over = True
for item in circleTop:
item.swing_top()
for item in circleBot:
item.swing_bot()
for item in circleLeft:
item.swing_left()
for item in circleRight:
item.swing_right()
screen.fill(black)
for e in all_enemies:
e.update()
# Did the enemy make it to the centre?
if ( e.reachedPoint( SCREEN_WIDTH//2, SCREEN_HEIGHT//2 ) ):
# Game over?
print("Game Over?")
e.randomise()
e.draw( screen )
for item in circleTop:
item.draw(screen)
for item in circleBot:
item.draw(screen)
for item in circleLeft:
item.draw(screen)
for item in circleRight:
item.draw(screen)
pygame.display.update()
clock.tick(FPS)
main()
pygame.quit()

how to target a sprite randomly

I am trying to create a retro style 2d shooter game and am currently making a homing missile. I need to randomly select a sprite from a group (pygame.sprite.Group) and find out its x and y coords. I have already made it home in on these coords so I do not need help with that. My code is:
an Enemy class (they are all the same just with different sizes and pictures):
class EnemyShipLevel1(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.image.load("C:/Users /Me/PycharmProjects/Game folder/SpaceShip game/Enemy1.png")
self.rect = self.image.get_rect()
self.rect.y = -50
self.rect.centerx = random.randint(15, SCREEN_WIDTH-15)
self.change_y = 0
self.cooldown = 0
self.targetx = 500
self.health = 1
if self.targetx >= self.rect.centerx:
self.change_x = 2
else:
self.change_x = -2
def update(self):
self.rect.x += self.change_x
self.rect.y += self.change_y
if self.rect.centery < 200:
self.rect.centery += 2
self.get_target()
if self.targetx > self.rect.centerx+10:
self.change_x = 3
elif self.targetx < self.rect.centerx-10:
self.change_x = -3
else:
self.change_x = 0
self.cooldown -= 1
if self.rect.left < screen_rect.left:
self.change_x = -self.change_x
if self.rect.right > screen_rect.right:
self.change_x = -self.change_x
if self.cooldown < 0 and (self.rect.centerx-50 < self.targetx < self.rect.centerx+50) and player.trans is False:
self.cooldown = random.randint(15, 25)
laser = NormalLaser(self.rect.centery, self.rect.centerx, False, 0, 8)
all_sprite_list.add(laser)
enemy_laser_list.add(laser)
player_laser_hit_list = pygame.sprite.spritecollide(self, player_laser_list, True)
for laser in player_laser_hit_list:
if self.health < 1:
all_sprite_list.remove(self)
enemy_list.remove(self)
else:
self.health -= 1
def get_target(self):
self.targetx = player.rect.centerx
Targeting Laser class (I know you can't bend lasers):
class TargetingLaser(pygame.sprite.Sprite):
def __init__(self, start_x, start_y, dest_x, dest_y):
super().__init__()
self.floating_point_x = start_x
self.floating_point_y = start_y
self.x_diff = dest_x - start_x
self.y_diff = dest_y - start_y
self.angle = math.atan2(self.y_diff, self.x_diff)
self.velocity = 10
self.change_x = math.cos(self.angle) * self.velocity
self.change_y = math.sin(self.angle) * self.velocity
self.image = pygame.image.load("C:/Users/Minecraft/PycharmProjects/Game folder/SpaceShip game/Laser.png")
self.image = pygame.transform.rotate(self.image, -(math.degrees(self.angle)+90))
self.rect = self.image.get_rect()
self.rect.centerx = start_x
self.rect.centery = start_y
def update(self):
self.floating_point_y += self.change_y
self.floating_point_x += self.change_x
self.rect.y = int(self.floating_point_y)
self.rect.x = int(self.floating_point_x)
if self.rect.top < screen_rect.top:
player_laser_list.remove(self)
enemy_laser_list.remove(self)
all_sprite_list.remove(self)
if self.rect.bottom > screen_rect.bottom:
player_laser_list.remove(self)
all_sprite_list.remove(self)
enemy_laser_list.remove(self)
if self.rect.left < screen_rect.left:
player_laser_list.remove(self)
all_sprite_list.remove(self)
enemy_laser_list.remove(self)
if self.rect.right > screen_rect.right:
player_laser_list.remove(self)
all_sprite_list.remove(self)
enemy_laser_list.remove(self)
Game Loop:
all_sprite_list = pygame.sprite.Group()
enemy_list = pygame.sprite.Group()
player_list = pygame.sprite.GroupSingle()
player_laser_list = pygame.sprite.Group()
enemy_laser_list = pygame.sprite.Group()
live_list = pygame.sprite.Group()
health_bar_list = pygame.sprite.GroupSingle()
screen = pygame.display.set_mode([SCREEN_WIDTH, SCREEN_HEIGHT])
screen_rect = screen.get_rect()
pygame.display.set_caption('Game')
clock = pygame.time.Clock()
Play = True
player = Player(SCREEN_WIDTH/2, SCREEN_HEIGHT-30)
all_sprite_list.add(player)
player_list.add(player)
for counter in range(2):
enemy = EnemyShipLevel1()
all_sprite_list.add(enemy)
enemy_list.add(enemy)
live1 = LivesIcon(5, 0, 1)
live_list.add(live1)
live1 = LivesIcon(27, 30, 2)
live_list.add(live1)
live1 = LivesIcon(50, 0, 3)
live_list.add(live1)
live1 = LivesIcon(72, 30, 4)
live_list.add(live1)
live1 = LivesIcon(95, 0, 5)
live_list.add(live1)
all_sprite_list.add(live_list)
health_bar = PlayerHealthBar(0, 690)
all_sprite_list.add(health_bar)
health_bar_list.add(health_bar)
ship = EnemyShipLevel3()
all_sprite_list.add(ship)
enemy_list.add(ship)
while Play is True:
# Shooting
if player.shooting is True:
if player.laser_cooldown < 0:
player.laser_cooldown = 5
if player.last_shot == "left":
player.last_shot = "right"
laser = NormalLaser(player.rect.centery-2, player.rect.centerx + 11, True, 0, 8)
all_sprite_list.add(laser)
player_laser_list.add(laser)
else:
player.last_shot = "left"
laser = NormalLaser(player.rect.centery-2, player.rect.centerx - 11, True, 0, 8)
all_sprite_list.add(laser)
player_laser_list.add(laser)
if player.rocket_cooldown < 0:
player.rocket_cooldown = 15
for counter in range(10):
laser = TargetingLaser(player.rect.centerx,player.rect.centery, player.rect.centerx, 0)
all_sprite_list.add(laser)
player_laser_list.add(laser)
# Shooting
# Levels
if len(enemy_list) == 0:
all_sprite_list.remove(enemy_laser_list)
all_sprite_list.remove(player_laser_list)
enemy_laser_list.empty()
player_laser_list.empty()
player.level += 1
for counter in range(player.spawn_enemies1):
enemy = EnemyShipLevel1()
all_sprite_list.add(enemy)
enemy_list.add(enemy)
for counter in range(player.spawn_enemies2):
enemy2 = EnemyShipLevel2()
all_sprite_list.add(enemy2)
enemy_list.add(enemy2)
for counter in range(player.spawn_enemies3):
ship = EnemyShipLevel3()
all_sprite_list.add(ship)
enemy_list.add(ship)
if player.level == 2:
player.spawn_enemies1 = 3
player.spawn_enemies2 = 1
if player.level == 3:
player.spawn_enemies1 = 5
player.spawn_enemies2 = 2
if player.level == 4:
player.spawn_enemies1 = 10
player.spawn_enemies2 = 5
if player.level == 5:
player.spawn_enemies1 = 12
player.spawn_enemies2 = 8
player.spawn_enemies3 = 1
# Levels
# End Game
if player.lives == 0:
Play = False
# End Game
# Keys
for event in pygame.event.get():
if event.type == pygame.QUIT:
Play = False
elif event.type == pygame.KEYDOWN:
if (event.key == pygame.K_a) or (event.key == pygame.K_LEFT):
player.change_x = -5
elif (event.key == pygame.K_d) or (event.key == pygame.K_RIGHT):
player.change_x = 5
if event.key == pygame.K_SPACE:
player.shooting = True
elif event.type == pygame.KEYUP:
player.rotation = 0
player.change_x = 0
player.change_y = 0
if event.key == pygame.K_SPACE:
player.shooting = False
# Keys
all_sprite_list.update()
screen.fill(BLACK)
all_sprite_list.draw(screen)
enemy_laser_list.draw(screen)
player_laser_list.draw(screen)
pygame.display.update()
clock.tick(60)
quit()
Each time you kill all of the spawned enemies then the game spawns in more and adds them to enemy_list().
If you are asking how to randomly choose a sprite then use a random.randint(0, 30) (or the number of sprites) then do a long series of if statements. Example Code.
import random
import pygame
class Enemy:
def __init__()
random_sprite = random.randint(0, 1)
# Then put in two variables which equal to the png (I will use sprite1 and sprite2)
def sprite_render
if random_sprite == 0:
# Just blit sprite1
elif random_sprite == 1:
# Just blit sprite2
(The reason why I just commented the actions for the image stuff is because almost everyone does it in a different way and that is not the main point of this answer.)
You could assign a number attribute to each enemy.
For example, you could start off your enemy function like this:
class EnemyShipLevel1(pygame.sprite.Sprite):
number = None
def __init__(self,number):
super().__init__()
self.number = number
self.image = pygame.image.load("C:/Users /Me/PycharmProjects/Game folder/SpaceShip game/Enemy1.png")
Now when you spawn in the enemies, you would add the number attribute:
for counter in range(2): # change this number to use more than 2 enemies (I use 30 later)
enemy = EnemyShipLevel1(counter)
all_sprite_list.add(enemy)
enemy_list.add(enemy)
All you have to do is randomly choose the sprite:
choice = random.randrange(0,30) # between 0 and the number of enemies spawned
for i in enemy_list:
if i.number == choice:
# make i the target of the laser
target_x = i.rect.x
target_y = i.rect.y

Partial Collision Error

I am currently trying to make a version of Pong using pygame, but I am having some issues with detecting the collision between the ball and the paddle. I got the detection working with both paddles, but I soon came to realize that the left player cannot move their paddle when the ball collides with it without the ball passing through. The player on the right has no such issue, and keeping the left player still allows the ball to reflect normally.
import pygame, sys
from pygame.locals import *
pygame.init()
pygame.mixer.init()
pygame.font.init()
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
screenx = 1080
screeny = 720
clock = pygame.time.Clock()
for pygame.mouse.get_pos in (screenx, screeny):
pygame.mouse.set_visible(False)
# Blueprint for creating player character
class Player:
# Defines initial conditions for player
def __init__(self):
self.xpos = 0
self.ypos = 0
self.width = 20
self.length = 100
self.speed = 10
self.move = 0
self.upkey = 0
self.downkey = 0
self.score = 0
self.rect = pygame.Rect((self.xpos, self.ypos, self.width, self.length))
# if player character, defines paddle movement
def player_move(self, key):
if key[self.upkey]:
self.ypos -= self.speed
self.move = -1
elif key[self.downkey]:
self.ypos += self.speed
self.move = 1
else:
self.move = 0
# Creates screen boundary for players
def player_boundary(self, key):
if self.ypos == 0:
self.speed = 0
self.move = 0
if key[self.downkey]:
self.speed = 10
self.move = -1
elif self.ypos + self.length == screeny:
self.speed = 0
self.move = 0
if key[self.upkey]:
self.speed = 10
self.move = 1
# Draws player rectangle to surface
def draw(self, surface):
self.rect = pygame.draw.rect(surface, WHITE, (self.xpos, self.ypos, self.width, self.length))
# Blueprint for creating the ball
class Ball:
def __init__(self):
self.size = 15
self.speed = 10
self.vy = 0
self.vx = 10
self.ypos = screeny/2
self.xpos = 100
self.rect = pygame.Rect((self.xpos, self.ypos, self.size, self.size))
# Defines movement of ball (in xy components)
def ball_move(self):
self.ypos += self.vy
self.xpos += self.vx
# method for ball bouncing off of player paddle **
def ball_block(self, player):
if player.move > 0:
self.vy += player.speed * 0.3
self.vx = -((self.speed**2 - self.vy**2) ** .5)
elif player.move < 0:
self.vy = player.speed * -0.3
self.vx = -((self.speed**2 - self.vy**2) ** .5)
elif player.move == 0:
self.vx = -self.vx
# for reflecting off of screen boundaries **
def ball_reflect(self, screeny):
if self.ypos == 0:
self.vy = -self.vy
elif self.ypos == screeny - self.size:
self.vy = -self.vy
# Method for detecting when player has scored
def score(self):
if self.xpos == 0:
self.vx = -self.vx
print ('Score')
elif self.xpos == screenx - self.size:
self.vx = -self.vx
print ('Score')
# Draws ball to surface
def draw(self, surface):
self.rect = pygame.draw.rect(surface, WHITE, (self.xpos, self.ypos, self.size, self.size))
def setup():
# Assigns values to player 1 (left side)
player1 = Player()
player1.xpos = 50
player1.ypos = screeny/1.5
player1.upkey = K_w
player1.downkey = K_s
# Assigns values to player 2 (right side)
player2 = Player()
player2.xpos = screenx - player2.width - 50
player2.ypos = screeny/1.5
player2.upkey = K_UP
player2.downkey = K_DOWN
return player1, player2
# Main loop of the game
def main():
player1, player2 = setup()
ball = Ball()
play = True
while play:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
key = pygame.key.get_pressed()
if key[K_ESCAPE]:
play = False
if ball.rect.colliderect(player1): # For some reason, ball can go through P1 while moving
ball.ball_block(player1)
elif ball.rect.colliderect(player2):
ball.ball_block(player2)
# Had issue with ball going through bottom, fixed by adding screeny to dependencies
if ball.ypos == 0:
ball.ball_reflect(screeny)
elif ball.ypos == screeny - ball.size:
ball.ball_reflect(screeny)
player1.player_move(key)
player2.player_move(key)
ball.ball_move()
# Calls methods for player boundaries and ball scoring
player1.player_boundary(key)
player2.player_boundary(key)
ball.score() # Ball will only score if colliding head on and not on P2's side
# Draws objects to screen
surface.fill(BLACK)
player1.draw(surface)
player2.draw(surface)
ball.draw(surface)
pygame.display.update()
clock.tick(60)
# Sets up screen, calls main loop
surface = pygame.display.set_mode((screenx, screeny))
pygame.display.set_caption('PONG')
main()
As far as I can tell, the code for both players is identical, and yet only one player works in the way I intended it to. Any help would be very much appreciated, and thank you for your time.
EDIT: Fixed by adding a few lines to the ball_block method
def ball_block(self, player):
if player.move > 0:
self.vy += player.speed * 0.3
if self.vx < 0:
self.vx = ((self.speed**2 - self.vy**2) ** .5)
elif self.vx > 0:
self.vx = -((self.speed**2 - self.vy**2) ** .5)
elif player.move < 0:
self.vy = player.speed * -0.3
if self.vx < 0:
self.vx = ((self.speed**2 - self.vy**2) ** .5)
elif self.vx > 0:
self.vx = -((self.speed**2 - self.vy**2) ** .5)
elif player.move == 0:
self.vx = -self.vx
If the player is moving during the collision, you are always setting
self.vx = -((self.speed**2 - self.vy**2) ** .5)
so it's always negative

Add collison detection to enemy sprites?

i'd like to add the same collision detection used by the player sprite to the enemy sprites or 'creeps' ive added all the relevant code I can see yet collisons are still not being detected and handled, please find below the class, I have no idea what is wrong currently, the list of walls to collide with is 'wall_list'
import pygame
import pauseScreen as dm
import re
from pygame.sprite import Sprite
from pygame import Rect, Color
from random import randint, choice
from vec2d import vec2d
from simpleanimation import SimpleAnimation
import displattxt
black = (0,0,0)
white = (255,255,255)
blue = (0,0,255)
green = (101,194,151)
global currentEditTool
currentEditTool = "Tree"
global editMap
editMap = False
open('MapMaker.txt', 'w').close()
def draw_background(screen, tile_img):
screen.fill(black)
img_rect = tile_img.get_rect()
global rect
rect = img_rect
nrows = int(screen.get_height() / img_rect.height) + 1
ncols = int(screen.get_width() / img_rect.width) + 1
for y in range(nrows):
for x in range(ncols):
img_rect.topleft = (x * img_rect.width,
y * img_rect.height)
screen.blit(tile_img, img_rect)
def changeTool():
if currentEditTool == "Tree":
None
elif currentEditTool == "Rock":
None
def pauseGame():
red = 255, 0, 0
green = 0,255, 0
blue = 0, 0,255
screen.fill(black)
pygame.display.update()
if editMap == False:
choose = dm.dumbmenu(screen, [
'Resume',
'Enable Map Editor',
'Quit Game'], 64,64,None,32,1.4,green,red)
if choose == 0:
print("hi")
elif choose ==1:
global editMap
editMap = True
elif choose ==2:
print("bob")
elif choose ==3:
print("bob")
elif choose ==4:
print("bob")
else:
None
else:
choose = dm.dumbmenu(screen, [
'Resume',
'Disable Map Editor',
'Quit Game'], 64,64,None,32,1.4,green,red)
if choose == 0:
print("Resume")
elif choose ==1:
print("Dis ME")
global editMap
editMap = False
elif choose ==2:
print("bob")
elif choose ==3:
print("bob")
elif choose ==4:
print("bob")
else:
None
class Wall(pygame.sprite.Sprite):
# Constructor function
def __init__(self,x,y,width,height):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface([width, height])
self.image.fill(green)
self.rect = self.image.get_rect()
self.rect.y = y
self.rect.x = x
class insertTree(pygame.sprite.Sprite):
def __init__(self,x,y,width,height, typ):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("images/map/tree.png").convert()
self.image.set_colorkey(white)
self.rect = self.image.get_rect()
self.rect.y = y
self.rect.x = x
class insertRock(pygame.sprite.Sprite):
def __init__(self,x,y,width,height, typ):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("images/map/rock.png").convert()
self.image.set_colorkey(white)
self.rect = self.image.get_rect()
self.rect.y = y
self.rect.x = x
class Creep(pygame.sprite.Sprite):
""" A creep sprite that bounces off walls and changes its
direction from time to time.
"""
change_x=0
change_y=0
def __init__(
self, screen, creep_image, explosion_images,
field, init_position, init_direction, speed):
""" Create a new Creep.
screen:
The screen on which the creep lives (must be a
pygame Surface object, such as pygame.display)
creep_image:
Image (surface) object for the creep
explosion_images:
A list of image objects for the explosion
animation.
field:
A Rect specifying the 'playing field' boundaries.
The Creep will bounce off the 'walls' of this
field.
init_position:
A vec2d or a pair specifying the initial position
of the creep on the screen.
init_direction:
A vec2d or a pair specifying the initial direction
of the creep. Must have an angle that is a
multiple of 45 degres.
speed:
Creep speed, in pixels/millisecond (px/ms)
"""
Sprite.__init__(self)
self.screen = screen
self.speed = speed
self.field = field
self.rect = creep_image.get_rect()
# base_image holds the original image, positioned to
# angle 0.
# image will be rotated.
#
self.base_image = creep_image
self.image = self.base_image
self.explosion_images = explosion_images
# A vector specifying the creep's position on the screen
#
self.pos = vec2d(init_position)
# The direction is a normalized vector
#
self.direction = vec2d(init_direction).normalized()
self.state = Creep.ALIVE
self.health = 15
def is_alive(self):
return self.state in (Creep.ALIVE, Creep.EXPLODING)
def changespeed(self,x,y):
self.change_x+=x
self.change_y+=y
def update(self, time_passed, walls):
""" Update the creep.
time_passed:
The time passed (in ms) since the previous update.
"""
if self.state == Creep.ALIVE:
# Maybe it's time to change the direction ?
#
self._change_direction(time_passed)
# Make the creep point in the correct direction.
# Since our direction vector is in screen coordinates
# (i.e. right bottom is 1, 1), and rotate() rotates
# counter-clockwise, the angle must be inverted to
# work correctly.
#
self.image = pygame.transform.rotate(
self.base_image, -self.direction.angle)
# Compute and apply the displacement to the position
# vector. The displacement is a vector, having the angle
# of self.direction (which is normalized to not affect
# the magnitude of the displacement)
#
displacement = vec2d(
self.direction.x * self.speed * time_passed,
self.direction.y * self.speed * time_passed)
self.pos += displacement
# When the image is rotated, its size is changed.
# We must take the size into account for detecting
# collisions with the walls.
#
self.image_w, self.image_h = self.image.get_size()
bounds_rect = self.field.inflate(
-self.image_w, -self.image_h)
if self.pos.x < bounds_rect.left:
self.pos.x = bounds_rect.left
self.direction.x *= -1
elif self.pos.x > bounds_rect.right:
self.pos.x = bounds_rect.right
self.direction.x *= -1
elif self.pos.y < bounds_rect.top:
self.pos.y = bounds_rect.top
self.direction.y *= -1
elif self.pos.y > bounds_rect.bottom:
self.pos.y = bounds_rect.bottom
self.direction.y *= -1
# collision detection
old_x=bounds_rect.left
new_x=old_x+self.direction.x
bounds_rect.left = new_x
# hit a wall?
collide = pygame.sprite.spritecollide(self, walls, False)
if collide:
# yes
bounds_rect.left=old_x
old_y=self.pos.y
new_y=old_y+self.direction.y
self.pos.y = new_y
collide = pygame.sprite.spritecollide(self, walls, False)
if collide:
# yes
self.pos.y=old_y
elif self.state == Creep.EXPLODING:
if self.explode_animation.active:
self.explode_animation.update(time_passed)
else:
self.state = Creep.DEAD
self.kill()
elif self.state == Creep.DEAD:
pass
#------------------ PRIVATE PARTS ------------------#
# States the creep can be in.
#
# ALIVE: The creep is roaming around the screen
# EXPLODING:
# The creep is now exploding, just a moment before dying.
# DEAD: The creep is dead and inactive
#
(ALIVE, EXPLODING, DEAD) = range(3)
_counter = 0
def _change_direction(self, time_passed):
""" Turn by 45 degrees in a random direction once per
0.4 to 0.5 seconds.
"""
self._counter += time_passed
if self._counter > randint(400, 500):
self.direction.rotate(45 * randint(-1, 1))
self._counter = 0
def _point_is_inside(self, point):
""" Is the point (given as a vec2d) inside our creep's
body?
"""
img_point = point - vec2d(
int(self.pos.x - self.image_w / 2),
int(self.pos.y - self.image_h / 2))
try:
pix = self.image.get_at(img_point)
return pix[3] > 0
except IndexError:
return False
def _decrease_health(self, n):
""" Decrease my health by n (or to 0, if it's currently
less than n)
"""
self.health = max(0, self.health - n)
if self.health == 0:
self._explode()
def _explode(self):
""" Starts the explosion animation that ends the Creep's
life.
"""
self.state = Creep.EXPLODING
pos = ( self.pos.x - self.explosion_images[0].get_width() / 2,
self.pos.y - self.explosion_images[0].get_height() / 2)
self.explode_animation = SimpleAnimation(
self.screen, pos, self.explosion_images,
100, 300)
global remainingCreeps
remainingCreeps-=1
if remainingCreeps == 0:
print("all dead")
def draw(self):
""" Blit the creep onto the screen that was provided in
the constructor.
"""
if self.state == Creep.ALIVE:
# The creep image is placed at self.pos. To allow for
# smooth movement even when the creep rotates and the
# image size changes, its placement is always
# centered.
#
self.draw_rect = self.image.get_rect().move(
self.pos.x - self.image_w / 2,
self.pos.y - self.image_h / 2)
self.screen.blit(self.image, self.draw_rect)
# The health bar is 15x4 px.
#
health_bar_x = self.pos.x - 7
health_bar_y = self.pos.y - self.image_h / 2 - 6
self.screen.fill( Color('red'),
(health_bar_x, health_bar_y, 15, 4))
self.screen.fill( Color('green'),
( health_bar_x, health_bar_y,
self.health, 4))
elif self.state == Creep.EXPLODING:
self.explode_animation.draw()
elif self.state == Creep.DEAD:
pass
def mouse_click_event(self, pos):
""" The mouse was clicked in pos.
"""
if self._point_is_inside(vec2d(pos)):
self._decrease_health(3)
#begin new player
class Player(pygame.sprite.Sprite):
change_x=0
change_y=0
frame = 0
def __init__(self,x,y):
pygame.sprite.Sprite.__init__(self)
# LOAD PLATER IMAGES
# Set height, width
self.images = []
for i in range(1,17):
img = pygame.image.load("images/player/" + str(i)+".png").convert() #player images
img.set_colorkey(white)
self.images.append(img)
self.image = self.images[0]
self.rect = self.image.get_rect()
self.rect.y = y
self.rect.x = x
self.health = 15
self.image_w, self.image_h = self.image.get_size()
health_bar_x = self.rect.x - 7
health_bar_y = self.rect.y - self.image_h / 2 - 6
screen.fill( Color('red'),
(health_bar_x, health_bar_y, 15, 4))
screen.fill( Color('green'),
( health_bar_x, health_bar_y,
self.health, 4))
def changespeed(self,x,y):
self.change_x+=x
self.change_y+=y
def _decrease_health(self, n):
""" Decrease my health by n (or to 0, if it's currently
less than n)
"""
self.health = max(0, self.health - n)
if self.health == 0:
self._explode()
def update(self,walls):
# collision detection
old_x=self.rect.x
new_x=old_x+self.change_x
self.rect.x = new_x
# hit a wall?
collide = pygame.sprite.spritecollide(self, walls, False)
if collide:
# yes
self.rect.x=old_x
old_y=self.rect.y
new_y=old_y+self.change_y
self.rect.y = new_y
collide = pygame.sprite.spritecollide(self, walls, False)
if collide:
# yes
self.rect.y=old_y
# right to left
if self.change_x < 0:
self.frame += 1
if self.frame > 3*4:
self.frame = 0
# Grab the image, divide by 4
# every 4 frames.
self.image = self.images[self.frame//4]
# Move left to right.
# images 4...7 instead of 0...3.
if self.change_x > 0:
self.frame += 1
if self.frame > 3*4:
self.frame = 0
self.image = self.images[self.frame//4+4]
if self.change_y > 0:
self.frame += 1
if self.frame > 3*4:
self.frame = 0
self.image = self.images[self.frame//4+4+4]
if self.change_y < 0:
self.frame += 1
if self.frame > 3*4:
self.frame = 0
self.image = self.images[self.frame//4+4+4+4]
score = 0
# initialize pyGame
pygame.init()
# 800x600 sized screen
global screen
screen = pygame.display.set_mode([800, 600])
screen.fill(black)
#bg_tile_img = pygame.image.load('images/map/grass.png').convert_alpha()
#draw_background(screen, bg_tile_img)
#pygame.display.flip()
# Set title
pygame.display.set_caption('Test')
#background = pygame.Surface(screen.get_size())
#background = background.convert()
#background.fill(black)
# Create the player
player = Player( 50,50 )
player.rect.x=50
player.rect.y=50
movingsprites = pygame.sprite.RenderPlain()
movingsprites.add(player)
# Make the walls. (x_pos, y_pos, width, height)
global wall_list
wall_list=pygame.sprite.RenderPlain()
wall=Wall(0,0,10,600) # left wall
wall_list.add(wall)
wall=Wall(10,0,790,10) # top wall
wall_list.add(wall)
#wall=Wall(10,200,100,10) # poke wall
wall_list.add(wall)
wall=Wall(790,0,10,600) #(x,y,thickness, height)
wall_list.add(wall)
wall=Wall(10,590,790,10) #(x,y,thickness, height)
wall_list.add(wall)
f = open('MapMaker.txt')
num_lines = sum(1 for line in f)
print(num_lines)
lineCount = 0
with open("MapMaker.txt") as infile:
for line in infile:
f = open('MapMaker.txt')
print(line)
coords = line.split(',')
#print(coords[0])
#print(coords[1])
#print(coords[2])
#print(coords[3])
#print(coords[4])
if "tree" in line:
print("tree in")
wall=insertTree(int(coords[0]),int(coords[1]), int(coords[2]),int(coords[3]),coords[4])
wall_list.add(wall)
elif "rock" in line:
print("rock in")
wall=insertRock(int(coords[0]),int(coords[1]), int(coords[2]),int(coords[3]),coords[4] )
wall_list.add(wall)
width = 20
height = 540
height = height - 48
for i in range(0,23):
width = width + 32
name = insertTree(width,540,790,10,"tree")
#wall_list.add(name)
name = insertTree(width,height,690,10,"tree")
#wall_list.add(name)
CREEP_SPAWN_TIME = 200 # frames
creep_spawn = CREEP_SPAWN_TIME
clock = pygame.time.Clock()
bg_tile_img = pygame.image.load('images/map/grass.png').convert()
img_rect = bg_tile_img
FIELD_RECT = Rect(50, 50, 700, 500)
CREEP_FILENAMES = [
'images/player/1.png',
'images/player/1.png',
'images/player/1.png']
N_CREEPS = 3
creep_images = [
pygame.image.load(filename).convert_alpha()
for filename in CREEP_FILENAMES]
explosion_img = pygame.image.load('images/map/tree.png').convert_alpha()
explosion_images = [
explosion_img, pygame.transform.rotate(explosion_img, 90)]
creeps = pygame.sprite.RenderPlain()
done = False
#bg_tile_img = pygame.image.load('images/map/grass.png').convert()
#draw_background(screen, bg_tile_img)
totalCreeps = 0
remainingCreeps = 3
while done == False:
creep_images = pygame.image.load("images/player/1.png").convert()
creep_images.set_colorkey(white)
draw_background(screen, bg_tile_img)
if len(creeps) != N_CREEPS:
if totalCreeps < N_CREEPS:
totalCreeps = totalCreeps + 1
print(totalCreeps)
creeps.add(
Creep( screen=screen,
creep_image=creep_images,
explosion_images=explosion_images,
field=FIELD_RECT,
init_position=( randint(FIELD_RECT.left,
FIELD_RECT.right),
randint(FIELD_RECT.top,
FIELD_RECT.bottom)),
init_direction=(choice([-1, 1]),
choice([-1, 1])),
speed=0.01))
for creep in creeps:
creep.update(60,wall_list)
creep.draw()
for event in pygame.event.get():
if event.type == pygame.QUIT:
done=True
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
player.changespeed(-2,0)
creep.changespeed(-2,0)
if event.key == pygame.K_RIGHT:
player.changespeed(2,0)
creep.changespeed(2,0)
if event.key == pygame.K_UP:
player.changespeed(0,-2)
creep.changespeed(0,-2)
if event.key == pygame.K_DOWN:
player.changespeed(0,2)
creep.changespeed(0,2)
if event.key == pygame.K_ESCAPE:
pauseGame()
if event.key == pygame.K_1:
global currentEditTool
currentEditTool = "Tree"
changeTool()
if event.key == pygame.K_2:
global currentEditTool
currentEditTool = "Rock"
changeTool()
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT:
player.changespeed(2,0)
creep.changespeed(2,0)
if event.key == pygame.K_RIGHT:
player.changespeed(-2,0)
creep.changespeed(-2,0)
if event.key == pygame.K_UP:
player.changespeed(0,2)
creep.changespeed(0,2)
if event.key == pygame.K_DOWN:
player.changespeed(0,-2)
creep.changespeed(0,-2)
if event.type == pygame.MOUSEBUTTONDOWN and pygame.mouse.get_pressed()[0]:
for creep in creeps:
creep.mouse_click_event(pygame.mouse.get_pos())
if editMap == True:
x,y = pygame.mouse.get_pos()
if currentEditTool == "Tree":
name = insertTree(x-10,y-25, 10 , 10, "tree")
wall_list.add(name)
wall_list.draw(screen)
f = open('MapMaker.txt', "a+")
image = pygame.image.load("images/map/tree.png").convert()
screen.blit(image, (30,10))
pygame.display.flip()
f.write(str(x) + "," + str(y) + ",790,10, tree\n")
#f.write("wall=insertTree(" + str(x) + "," + str(y) + ",790,10)\nwall_list.add(wall)\n")
elif currentEditTool == "Rock":
name = insertRock(x-10,y-25, 10 , 10,"rock")
wall_list.add(name)
wall_list.draw(screen)
f = open('MapMaker.txt', "a+")
f.write(str(x) + "," + str(y) + ",790,10,rock\n")
#f.write("wall=insertRock(" + str(x) + "," + str(y) + ",790,10)\nwall_list.add(wall)\n")
else:
None
#pygame.display.flip()
player.update(wall_list)
movingsprites.draw(screen)
wall_list.draw(screen)
pygame.display.flip()
clock.tick(60)
pygame.quit()
Sprite collide is the correct way to do it, you are just handling the return data incorrectly. Basically, sprite collide will no return a boolean of if you are colliding or not with a member of the group. It returns a list of sprites. Basically, it will give you a list of sprites colliding with you in that group. Another thing to watch out for is that the list will also invariably include the sprite itself. After all, it is colliding with itself. Here is a functioning way to test if you are colliding with a wall.
def is_colliding_with_wall(): #this is a function that will determine if you are touching a wall. You have repeated code to test collisions with walls multiple times, so it is best just to have a function
collide = pygame.sprite.spritecollide(self, walls, False) #this gets the list, it is the exact same code as in yours
for collision in collide: #goes through the list, checking each collision
if collision != self: #filters out self collision
return True #if there is a collision that is not self, returns True, that there is a collision with a wall
return False #if it has checked all the collisions and there is only a self collision, then it is not colliding with a wall, so it returns False

Categories

Resources