How to make enemy follow player? Pygame - python

I know this has been asked before but I can't get it to work with my code.
I think this is where to have the enemy follow the player should be. I'm not sure how exactly to implement it.
I want the enemies to follow the player and when they run into the player the game ends. I also want this to be able to work with multiple enemies as well.
# player class
class player:
def __init__(self, x, y, w, h, xChange, yChange, vel):
self.x = x # plater x value
self.y = y # player y value
self.w = w # player w (width)
self.h = h # player h (height)
self.xChange = xChange # player xChange (to add to x value to move player horizontally)
self.yChange = yChange # player yChange (to aad to y value to move player vertically)
self.vel = vel # velocity of player (needed for collision)
# enemy class
class enemy:
def __init__(self, x, y, w, h):
self.x = x # enemy x value
self.y = y # enemy y value
self.w = w # enemy w (width) value
self.h = h # enemy h (height) value
# ----------------------------------------------------------------
"""enemy's x value (random value) (we pick 750 because the enemy width is 50 and
the screen width is 800. If we set the random value to 800, then the enemy has a
chance of spawning outside of the screen.)"""
enemyX = random.randint(0, 700)
"""enemy's y value (random value) (we pick 540 because the enemy height is 60
and the screen height is 600. If we set the random value to 600, the enemy has a
chance of spawning outside of the screen.)"""
enemyY = random.randint(0, 540) # enemy's y value
score = 0 # score set to 0. Will update in while loop.
rec = player(50, 50, 24, 32, 0, 0, 5) # the player's values (x, y, w, h, xChange, yChange, vel)
redRec = enemy(enemyX, enemyY, 24, 32) # the enemy's values (x, y, w, h)
# mainloop #
def mainloop():
global running, score, intro, sprite, next_zombie_time
while running:
"""keeps filling window with the background image"""
window.blit(background, (0, 0))
pygame.time.delay(25) # delay
for event in pygame.event.get(): # for every event in game
if event.type == pygame.QUIT: # if I exit the game
quitGame()
if event.type == pygame.KEYUP: # if any keys are let go
if event.key == pygame.K_a: # if key a
rec.xChange = 0 # set xChange to 0 (stop moving rec)
if event.key == pygame.K_d: # if key d
rec.xChange = 0 # set xChange to 0 (stop moving rec)
if event.key == pygame.K_w: # if key w
rec.yChange = 0 # set xChange to 0 (stop moving rec)
if event.key == pygame.K_s: # if key s
rec.yChange = 0 # set xChange to 0 (stop moving rec)
if event.type == pygame.KEYDOWN: # if any keys are pressed
if event.key == pygame.K_F4: # if key F4
pygame.quit() # set running to false
if event.key == pygame.K_a: # if key a
rec.xChange += -5 # add -5 to xChange (move rec left)
sprite = spriteLeft
if event.key == pygame.K_d: # if key a
rec.xChange += 5 # adds 5 to xChange (move rec right)
sprite = spriteRight
if event.key == pygame.K_w: # if key a
#adds -5 to yChange (moves rec up). Yes, this is supposed to say up.
rec.yChange += -5
sprite = spriteUp
if event.key == pygame.K_s: # if key a
# adds 5 to yChange (moves rec down). Yes, this is supposed to say down.
rec.yChange += 5
sprite = spriteDown
# pause key to pause game
if event.key == pygame.K_o: # if key o
running = False # set running to false
intro = False # intro set to False
pauseMenu() # pauseMenu is called
rec.x += rec.xChange # add rec's xChange to x (to do the moving)
rec.y += rec.yChange # adds rec's yChange to y (to do the moving)
# ----------------BOUNDARIES------------------------------
if rec.x <= 0: # if rec's x is less than or equal to 0 (if tries to escape screen)
rec.x = 0 # rec's x is set to 0 so it won't go off screen.
"""(we pick 750 because the player width is 50 and the screen width is 800.
If we set it to 800, then the player can go outside of screen."""
if rec.x >= 750: # if rec's x is greater than or equal to 750 (if tries to escape screen)
rec.x = 750 # set rec's x to 750 so it won't go off screen
if rec.y <= 0: # if rec's y is less than or equal to 0 (if tries to escape screen)
rec.y = 0 # set rec's y to 0 so it won't go off screen
"""we pick 540 because the player height is 60 and the screen height is 600.
If we set it to 600, then the player can go outside of screen"""
if rec.y >= 540: # if rec'y is greater than or equal to 540 (if tries to escape screen)
rec.y = 540 # set rec's y to 540 so it won't go off screen
#enemy.update(delta_time, player)
collisions = detCollision(rec.x, rec.y, rec.w, rec.h, redRec.x, redRec.y, redRec.w, redRec.h)
# activate the redrawWin function
redrawWin(collisions)

You need a few peices of information to do this. Firstly, you will need the distance between the player and the enemy. You can work out the distance using math.hypot function like this distance = (math.hypot(enemy.x - player.x, enemy.y - player.y) ). Then you would want to work out the angle between them in radians by using math.atan2 funciton. Note that this function takes y positional argument first. You can use this as follows
angle_radians = (math.atan2(enemy.y - player.y , enemy.x - player.x))
Now to get the enemy to move in the direction of the player, you can do this
enemy.y += math.sin(angle_radians)
enemy.x += math.cos(angle_radians)
See why this works http://setosa.io/ev/sine-and-cosine/.
You can even add a range on how close you want the player to be to the enemy when it starts following by setting a condition on distance between them like this.
if distance < range:
You can also control the speed by multiplying both the sin and cosine by the same number and this works because they are ratios.It can looks something like this
enemy.y += math.sin(angle_radians) * speed # Note both speeds must be the same nnumber
enemy.x += math.cos(angle_radians) * speed
For the last part of your question, if you want this to work for all the enemies, i would recommend adding your game objects to a list. For exmple, you can add enemies to a list
and make them follow the player. Lets say you make a list of all the enemies by doing this
all_enemies = []
for i in range(number of enemies you want):
all_enemies.append(enemy())
You could have an end result that looks something like this:
def Follow_player(self):
for e in all_enemies:
distance = (math.hypot(e.x - player.x, e.y - player.y) )
angle_radians = (math.atan2(e.y - player.y , e.x - player.x))
e.y += math.sin(angle_radians)
e.x += math.cos(angle_radians)
if distance < 1:
# they have collided
EDIT
Here's a link to a very good youtube video that describes all of this
https://www.youtube.com/watch?v=DVYDkHdsTIM

Related

space invader pygame make enemy disappear when shot

I have a list with all my enemies and when the distance between the players bullet and the enemy is close enough, I can make a enemy disappear but its never the right one that was hit with the bullet, its always the enemy with the greatest x value(in my case the one at the end of the list) so my questions is how do I make the specific enemy disappear. here is my code for reference.
import pygame
import random
import math
# initalize pygame
pygame.init()
# creates game screen to display game
game_window = pygame.display.set_mode((800, 570))
# Title and icon for window
pygame.display.set_caption("Power of the Doctor")
icon = pygame.image.load('tardisShooter.png')
pygame.display.set_icon(icon)
# backgroun picture
background = pygame.image.load('SpaceTardissmall.png')
# player info
Playerimg = pygame.image.load('tardisShooter.png')
playerx = 370 # players x axis
playery = 470 # players y axis
playerx_change = 0 # adds constant change while added in loop
def player(x, y):
# blit draws image on screen: x axis y axis
game_window.blit(Playerimg, (x, y))
# enemy info
Enemyimg = []
enemyx = []
enemyy = []
enemyx_change = 1
enemyy_change = 0
num_enemies = 2
enemyspawnx = 50
enemyspawny = 200
for i in range(num_enemies):
Enemyimg.append(pygame.image.load('CybermanShooter.png'))
enemyx.append(enemyspawnx) # random x axis for enemy
enemyy.append(enemyspawny) # random y axis for enemy
enemyspawnx += 70
def enemy(x, y, i):
game_window.blit(Enemyimg[i], (x, y))
# bullet info
bulletimg = pygame.image.load('torpedo32.png')
bullety = playery
bulletx = playerx
bulletstate = "idle"
bullety_change = 5
def bullet(x, y):
global bulletstate
bulletstate = "FIRE"
game_window.blit(bulletimg, (x + 15.5, y - 25))
def collision(enemyx, enemyy, bulletx, bullety):
distance = math.sqrt((math.pow(bulletx - enemyx, 2)) + (math.pow(bullety - enemyy, 2)))
if distance < 27:
return True
else:
return False
rand = random.randint(0, num_enemies)
# timer info
bulletevent = pygame.USEREVENT
pygame.time.set_timer(bulletevent, 2000)
# get a rabdom number
# when the timer hits 2 secodsn spawsn bomb at the index of the random number that corresponds witht the enemy
# enemy bomb
bombimg = []
bombx = []
bomby = []
bomby_change = 1
bombstate = "idle"
num_bombs = 2
for i in range(num_bombs):
bombimg.append(pygame.image.load('bomb.png'))
bombx.append(enemyx[i])
bomby.append(enemyy[i])
def bomb(x, y, i):
global bombstate
bombstate = "fire"
game_window.blit(bombimg[i], (x + 15, y + 65))
# player hit detection
def enemyhit(bombx, bomby, x, y):
distance2 = math.sqrt((math.pow(bombx - x, 2)) + (math.pow(bomby - (y - 50), 2)))
if distance2 < 20:
return True
else:
return False
# loop to keep screen running
Running = True
while Running:
# background color
game_window.fill((0, 0, 0))
# background image in loop
game_window.blit(background, (0, 0))
# checks anything typed while game is running
for event in pygame.event.get():
# if they click x to quit close window
if event.type == pygame.QUIT:
Running = False
if event.type == bulletevent:
if bombstate == 'idle':
bomb(bombx[rand], bomby[rand], i)
bombx[rand] = enemyx[rand]
bomby[rand] = enemyy[rand]
# means key has been pressed(not released)
if event.type == pygame.KEYDOWN:
# escape key closes game
if event.key == pygame.K_ESCAPE:
Running = False
# right key moves player 5 spaces
if event.key == pygame.K_RIGHT:
playerx_change = 5
# left key moves players back 5 spaces
if event.key == pygame.K_LEFT:
playerx_change = -5
if event.key == pygame.K_SPACE:
bulletx = playerx
if bulletstate == "idle":
bullet(bulletx, bullety)
if event.type == pygame.KEYUP:
if event.key == pygame.K_RIGHT or event.key == pygame.K_LEFT:
playerx_change = 0
playerx += playerx_change # constantly adds user input to player model
# player bounds
if playerx >= 740:
playerx = 740
if playerx <= 0:
playerx = 0
# player fucntion draws image
player(playerx, playery)
# loops through all the items in the lists named
for i in range(num_enemies):
# draws all enemies
enemy(enemyx[i], enemyy[i], i)
enemyx[i] += enemyx_change
if enemyx[i] >= 735:
enemyx_change = -.8
enemyy[i] += 10
if enemyx[i] <= 0:
enemyx_change = .8
enemyy[i] += 10
# is state is fire
if bulletstate == "FIRE":
# draws torpepd at coordinates
bullet(bulletx, bullety)
bullety -= bullety_change # decreases y axis moving bullet
if bullety <= 0: # if goes past border
bulletstate = "idle" # ittl switch back to idle
bullety = playery # moves bullet y back to player y
for i in range(num_enemies):
collided = collision(enemyx[i], enemyy[i], bulletx, bullety)
if collided == True:
bulletstate = 'idle'
bullety = playery
Enemyimg.remove(Enemyimg[i])
num_enemies -= 1
# when enemy fires
if bombstate == 'fire':
bomb(bombx[rand], bomby[rand], i)
bomby[rand] += bomby_change
if bomby[rand] > 570:
bombstate = 'idle'
bomby[rand] = enemyy[rand]
rand = random.randint(0, 1)
# enemy bullets hit player
enemyhits = enemyhit(bombx[rand], bomby[rand], playerx, playery)
if enemyhits == True:
playerx = 370
bombstate = 'idle'
bomby[rand] = enemyy[rand]
# updates the screen in loop
pygame.display.update()
A big problem with your code is having different lists for each attribute of your enemies.
You wrote this :
Enemyimg = []
enemyx = []
enemyy = []
Meaning that you always need to sync those three lists at all time to get coherent results. Which you are not doing, when you "remove" an enemy, you just remove the img from Enemyimg and not removing its coords in enemyy and enemyx.
While you could manage to make it work with three lists, you are just overcomplicating things. Let me show you what you may want to do to make your life way easier :)
I'm going to assume, you don't know about OOP (Object Oriented Programming) yet ? I Would be a great time to use it but it can be a little complexe if you are just starting out with programming.
So let's introduce python's dict !
What you really want to do is have one single list :
enemies = []
But how to store everything in one list you might wonder ?
Well, it will be a list of dict. A dict looks something like that :
example = {'image': pygame.image.load('CybermanShooter.png'), 'x': enemyspawnx, 'y': enemyspawny}
(Note that if you always use the same image, you don't need to put it here, you can just draw your image at the position)
Now if you do something like :
print(example['x'])
It will print your enemy's x value.
So, to sum up :
You want to have one single list : enemies = []
To add an enemy you will do :
enemies.append({'image': pygame.image.load('CybermanShooter.png'), 'x': enemyspawnx, 'y': enemyspawny})
To detect your collisions :
# Your collision function
def collision(enemy, bulletx, bullety):
distance = math.sqrt((math.pow(bulletx - enemy['x'], 2)) + (math.pow(bullety - enemy['y'], 2)))
if distance < 27:
return True
return False
# Your loop (in 2 steps: 1 - find enemies to remove, 2 - actually remove them)
enemies_to_remove = []
for enemy in enemies :
if collision(enemy, bulletx, bullety):
bulletstate = 'idle'
bullety = playery
enemies_to_remove.append(enemy)
num_enemies -= 1
for enemy in enemies_to_remove:
enemies.remove(enemy)
To draw your enemies:
# Your draw_enemy function (which you called `enemy(...)` for some reason)
def draw_enemy(enemy):
game_window.blit(enemy['image'], (enemy['x'], enemy['y']))
# Inside the main loop
for enemy in enemies:
draw_enemy(enemy)
To update the position of enemies:
# Make a move_enemy function
def move_enemy(enemy):
# put your moving logic here
# Inside the main loop:
for enemy in enemies:
move_enemy(enemy)
Do you get the principle ?
I've tried to stay as close as your original code as possible so it's not too many information to take in at once.
But technically you should apply the same principle for your bullets and to some extend to your player.

Weird spawn location of enemies pygame

This is the code of a survive the horde type game I'm working on. I have been facing an issue where the alien(s) sometimes (enemy in code) randomly spawns at the bottom of the surface, even though I have specified their spawn location to be randomized (within a certain part of the surface).
What has made them spawn at the bottom of the surface, where they cannot be touched by bullets?
import pygame
import random
import math
# for initialising pygame (req for every pygame app)
pygame.init()
# making the basic window (dimensions must be written inside a tuple )
screen = pygame.display.set_mode((500, 500))
# background
background = pygame.image.load('C:/Users/aryan/Downloads/background.jpg')
# load and set the logo
logo = pygame.image.load('C:/Users/aryan/Downloads/bp.png') # directory of logo
pygame.display.set_icon(logo)
pygame.display.set_caption("space wars") # program name
# define a variable to control the main loop
running = True
# player
playerimg = pygame.image.load('C:/Users/aryan/Downloads/spaceship.png')
playerX = 218 # x and y coordinates of image
playerY = 350
playerxchange = 0 # this will be the change in movement in x direction of our image
playerychange = 0 # this will be the change in movement in y direction of our image
def player(x, y):
screen.blit(playerimg, (x, y)) # blit draws our image on the surface(basically the background)
# syntax for blit(imagename, (xcoordinate,ycoordinate))
class Enemy:
def __init__(self):
self.x = random.randint(0, 476)
self.y = random.randint(0, 300)
self.moveX = 0.2
self.moveY = 40
def move(self):
self.x += self.moveX
if self.y >= 476:
self.y = 476
self.moveY = 0
self.moveX = 0
if self.x <= 0:
self.moveX = 0.1
self.y += self.moveY
elif self.x >= 465:
self.moveX = -0.1
self.y += self.moveY
def draw(self):
screen.blit(enemyimg, (self.x, self.y))
# enemy
enemyimg = pygame.image.load('C:/Users/aryan/Downloads/enemy.png')
enemy_list = []
for i in range(5):
new_enemy = Enemy()
enemy_list.append(new_enemy)
# game over
overimg = pygame.image.load('C:/Users/aryan/Downloads/gameover.png')
# bullet
bulletimg = pygame.image.load('C:/Users/aryan/Downloads/bullet.png')
bulletX = 0
bulletY = 350
bulletxchange = 0
bulletychange = 1
bullet_state = "ready" # "ready" you cant see bullet on screen
# "fire" you can see bullet firing
bullets = [] # bullets is a list that contains the coordinates of every bullet
score = 0
font30 = pygame.font.SysFont(None, 30)
#class
# Functions
def enemy(x, y):
screen.blit(enemyimg, (x, y)) # blit draws our image on the surface(basically the background)
# syntax for blit(imagename, (xcoordinate,ycoordinate))
def firebullet(x, y):
global bullet_state
bullet_state = "ready"
bullets.append([x + 12, y + 6]) # Creating a new bullet
def iscollision(enemyX, enemyY, bulletX, bulletY):
distance = math.sqrt(math.pow(enemyX-bulletX, 2)+ math.pow(enemyY-bulletY,2)) # distance formula
if distance <= 20:
return True
else:
return False
def TextScore(game):
text2 = font30.render("Your Score is: " + str(game), True, (37, 97, 188))
screen.blit(text2, (10, 45))
# main loop
while running:
screen.fill((120, 120, 120)) # in order (r, g, b) . (0, 0, 0) is black (255, 0, 0) is red...
screen.blit(background, (0, 0))
# event handling, gets all event from the event queue
for event in pygame.event.get():
# only do something if the event is of type QUIT
if event.type == pygame.QUIT:
# change the value to False, to exit the main loop
running = False
# checking keystroke
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT:
playerxchange += 0.3 # change in movement will be 0.2 towards the right
if event.key == pygame.K_LEFT:
playerxchange -= 0.3 # change in movement will be 0.2 towards the right
if event.key == pygame.K_UP:
playerychange -= 0.3
if event.key == pygame.K_DOWN:
playerychange += 0.3
if event.key == pygame.K_SPACE:
bullet_state = "fire"
firebullet(playerX, playerY)
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT or event.key == pygame.K_DOWN or event.key == pygame.K_UP:
playerxchange = 0
playerychange = 0
playerY += playerychange
playerX += playerxchange # the value of playerx changes by +- 0.1 depending on keystroke
if playerX <= -64: # this teleports the spaceship from left end to right end
playerX = 564
elif playerX >= 564: # this teleports spaceship from right end to left
playerX = -64
if playerY >= 436: # this prevents spaceship from leaving vertically
playerY = 436
if playerY <= 0:
playerY = 0
# enemy movement
for enemy in enemy_list:
enemy.move()
# bullet movement
if bullet_state == "fire":
firebullet(playerX, playerY)
for bullet in bullets:
screen.blit(bulletimg, (bullet[0], bullet[1])) # Print a bullet
bullet[0] -= bulletxchange # Updates its position
bullet[1] -= bulletychange
if bullet[1] < 0:
bullets.remove(bullet)
# collision
for enemy in enemy_list:
for bullet in bullets: # Use a for-loop to iterate through all the bullets in the list.
collision = iscollision(enemy.x, enemy.y, bullet[0], bullet[1])
if collision: # Test if a single bullet collides with the enemy inside the loop.
score += 1
print(score)
bullets.remove(bullet) # Remove the bullet from the list when it collides with the enemy.
enemy.x = random.randint(0, 476) # if collision takes place, alien respawns
enemy.y = random.randint(0, 30)
TextScore(score)
player(playerX, playerY) # player method is called AFTER screen.fill otherwise the screen will fill after image has been blitted
for enemy in enemy_list: # new edit
enemy.draw()
pygame.display.update() # necessary for events to keep updating
The problem is that you are generating number out out of bounds defined in the move method of Enemy class. Specifically, the problem is with x for which the upper bound seems to be 465, but you are generating numbers up to 476.
I obviously can't test this, but replace
enemy.x = random.randint(0, 476) # if collision takes place, alien respawns
enemy.y = random.randint(0, 30)
with
enemy.x = random.randint(1, 464) # if collision takes place, alien respawns
enemy.y = random.randint(1, 30)
and the problem should go away.
After looking through your code, it seems like after a collision you set the respawn y coordinate to be between 0 and 30, which I assume is what you want. Although, your original y coordinate for spawning aliens goes from 0 to 300; this may be your issue as to why they are spawning farther down the screen?

Can't do projectile stuff in pygame correctly

Trying to enable shooting projectiles with pygame. After I hit space button it shoots only 1 projectile, fps drops from 100 to ~30, player character can't move and does not react to button presses. After projectile stops being animated fps returns back to 100 and I can move&shoot up to 3 projectiles (as planned) but fps drops again and I can't move until the last projectile reaches the border.
I had a bullet image being loaded instead of drawing bullet before and thought that the case was in it. But the result didn't change. I also tried re-ordering blit() functions but nothing changed as well.
class Game():
# Some game stuff and functions
def __init__(self):
self.x_default = 800
self.y_default = 600
self.screen = pygame.display.set_mode((self.x_default, self.y_default))
#self.background = pygame.image.load('IMGs/BCK2.bmp').convert()
self.title = 'Title'
self.fps = 100
class Projectile():
# This class is supposed to make projectiles
def __init__(self, x, y, speed_x=5, direction=1, width=10, height=10):
# x&y - starting positions of projectiles
# speed_x & speed_y - starting moving speeds of projectiles in # oX & oY
# a - speed increase over time (cancelled for now)
# direction - stays for its' name
# Directions: [2, 3, 1, 4, 5, 6, -1, 7] starting from 0:00 and moving towards the right side of the clock
self.x = x
self.y = y
self.speed_x = speed_x
self.direction = direction
self.width = width
self.height = height
self.img = pygame.Rect(self.x, self.y, self.width, self.height)
def move_projectile(self, x, speed_x, direction):
# Moves stuff
# For now direction works only in right-left (1 for right, -1 for left)
x += speed_x * direction
return x
while 1:
if pygame.event.poll().type == pygame.QUIT:
sys.exit()
for pr in projectiles:
if (pr.x >= -pr.width) and (pr.x <= Game().x_default):
pr.x = pr.move_projectile(x=pr.x, speed_x=pr.speed_x, direction=pr.direction)
else:
projectiles.remove(pr)
keys = pygame.key.get_pressed()
# ------------ oX moving --------------
if keys[pygame.K_LEFT]:
P.x -= P.speed
P.direction = -1
if keys[pygame.K_RIGHT]:
P.x += P.speed
P.direction = 1
# ------------ Shooting ----------------
if keys[pygame.K_SPACE]:
if len(projectiles) < 3:
if P.direction > 0:
projectiles.append(Projectile(x=P.x + P.width, y=P.y + P.height//2, direction=1))
else:
projectiles.append(Projectile(x=P.x, y=P.y + P.height//2, direction=-1))
# ---------- The blitting process --------------
G.screen.blit(G.background, (0, 0))
G.screen.blit(Block1.img, (Block1.x, Block1.y))
G.screen.blit(Block2.img, (Block2.x, Block2.y))
for pr in projectiles:
pygame.draw.rect(G.screen, (0, 0, 0), pr.img)
G.screen.blit(P.img, (P.x, P.y))
pygame.display.update()
pygame.time.delay(1000//G.fps)
See How can i shoot a bullet with space bar? and How do I stop more than 1 bullet firing at once?
keys[pygame.K_SPACE] is True as long SPACE is hold. This means multiple bullets are continuously generated as long the key is hold.
If you want to create a single bullet, when SPACE is pressed, then use the KEYDOWN event (see pygame.event).
The event only occurs once, for each time when the key is pressed. e.g.:
while 1:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
if len(projectiles) < 3:
if P.direction > 0:
projectiles.append(Projectile(x=P.x + P.width, y=P.y + P.height//2, direction=1))
else:
projectiles.append(Projectile(x=P.x, y=P.y + P.height//2, direction=-1))
keys = pygame.key.get_pressed()
# [...]

Collision Between two sprites - Python 3.5.2

I have an image of a ufo and a missile. I'm trying to get it to where if the missile hits the ufo they both would explode and disappear and then a few moments later another ufo would respawn but the collision code isn't working. can someone explain to me how to make the code work?
pygame.display.init()
pygame.font.init()
screen = pygame.display.set_mode((800, 600))
clock = pygame.time.Clock()
ufo = pygame.image.load("ufo.png")
rocket = pygame.image.load("rocket.png")
done = False
debug = False
fontObj = pygame.font.SysFont("Courier New", 20)
#Making my Empty Lists
missiles = [] #[x,y]
ufo_list = [] #[x,y,hspeed]
particle_list = []
#UFO Respawn Info
ufoRespawn = True
ufoHits = 0
ufoSpawnTimer = 0.0
ufoSpeed = 500.0
#MISSILE Info
launchX = 400
launchY = 550
missileSpeed = 100.0
missileDirection = 0
#creating the Starfield
myStars = [] # An (initially) empty list
for i in range(1000):
x = random.randint(0, 800)
y = random.randint(0, 600)
newStar = [x, y] # A 2-element list
myStars.append(newStar)
starSpeed = 100.0 # Rate of star movement (px / s)
starDirection = 0 # 0 = not moving, -1 = left, +1 = right
#input
while not done:
event = pygame.event.poll()
if event.type == pygame.QUIT:
done = True
keys = pygame.key.get_pressed()
if keys[pygame.K_ESCAPE]:
done = True
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_d:
debug = not debug
dt = clock.tick() /1000.0
#the missile range (making it disappear after it hits the top)
for missile in missiles:
missile[1] -= missileSpeed * dt
if missile[1] < 0: missiles.remove(missle)
#triangle following mouse position
mx, my = pygame.mouse.get_pos()
if mx > launchX:
launchX += .3
if mx < launchX:
launchX -= .3
#bullets firing when pressing with mouse
mbuttons = pygame.mouse.get_pressed()
if mbuttons [0]:
x = launchX
y = launchY
newMissiles = [x,y]
missiles.append(newMissiles)
#Creating the UFOs
ufoSpawnTimer -= dt
if ufoSpawnTimer <= 0:
if random.choice (("head", "tail")) == "head":
x = 0
hspeed = random.randint (10,50)
else:
x = 800
hspeed = random.randint (-50, -10)
y = random.randint (0,300)
new_ufo = [x,y,hspeed]
ufo_list.append(new_ufo)
ufoSpawnTimer = 5.0
#Moving the Starfield
for i in range(len(myStars)):
myStars[i][0] += starSpeed * dt * starDirection
if myStars[i][0] < 0: myStars[i][0] = 800
if myStars[i][0] > 800: myStars[i][0] = 0
screen.fill ((0,0,0))
#drawing the triangle a.k.a missle launcher :D
pygame.draw.polygon(screen, (255,255,255), [[launchX, launchY], [launchX + 10, launchY + 10], \
[launchX - 10, launchY + 10]], 3)
for missile in missiles:
x = int(missile[0])
y = int(missile[1])
screen.blit(rocket, (x,y))
#drawing the ufo
for v in ufo_list:
v[0] += v[2] * dt
screen.blit(ufo,(v[0],v[1]))
#Missle distance from UFO - NEED HELP ON THIS PORTION
#Hit Detection
missileDist = ((x - v[0]) ** 2 + (y - v[1]) ** 2) ** 0.5
if **????** :
ufoRespawn = True
ufoHits += 10
#drawing th starfield
for star in myStars:
x = int(star[0])
y = int(star[1])
pygame.draw.circle(screen, (255,255,255), (x,y), 2)
pygame.display.flip()
pygame.font.quit()
pygame.display.quit()
Well there are many different ways to detect collision, And it might be worth looking at libraries that would do so, but the simplest method by far is to use pygame.sprite.spritecollide().
But before I can show how to use the function, you need to know what a pygame.sprite.Group() is and what a sprite class is.
Basicly, what a pygame.sprite.Group() is, is a way to keep track of and hold multiple sprites. In your case, it seems making a missile group for your missiles would be the best choice.
So I would create a group to hold your missiles:
missiles_group = pygame.sprite.Group(). You can add missiles to the group by saying missiles_group.add(<sprite instance name>).
As for the sprite class, please see this answer I gave to a question. To be terse, a Sprite class is a modular way to create a sprite. Instead of using just a plain image, a sprite class would hold necessary methods and attributes of a sprite. I will be using a sprite class in my example below, so if more detail is needed, please read the answer I linked to above.
With that out of the way, and without going into too much detail, here is how you fill in each function parameter to the above function.
sprite: This is the sprite that will be tested against a group of sprites
group: This is the group that will be used to test with the sprite.
dokill: This is a boolean value. If set to true, each time the sprite parameter collides with something in the group parameter, and object from the group parameter will be deleted. And visa versa if the dokill argument is set to false.
The is one more parameter that the function takes, but for what you're trying to do, it is not needed.
Incorporating the above information, here is an example. The example creates a sprite and a list of sprites. Each time the sprite collides with a sprite from the group, HIT is printed to the screen:
import pygame #import the pygame module into the namespace <module>
WIDTH = 640 # define a constant width for our window
HEIGHT = 480 # define a constant height for our window
#create a pygame window, and
#initialize it with our WIDTH and HEIGHT constants
display = pygame.display.set_mode((WIDTH, HEIGHT))
clock = pygame.time.Clock() # create a game clock
class Sprite(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((20, 20))
self.image.fill((255, 0, 0))
self.rect = self.image.get_rect()
self.rect.x = WIDTH / 2
self.rect.y = HEIGHT / 2
self.vx = 0
self.vy = 0
def update(self):
self.vx = 0
self.vy = 0
key = pygame.key.get_pressed()
if key[pygame.K_LEFT]:
self.vx = -1
elif key[pygame.K_RIGHT]:
self.vx = 1
if key[pygame.K_UP]:
self.vy = -1
elif key[pygame.K_DOWN]:
self.vy = 1
self.rect.x += self.vx
self.rect.y += self.vy
# cretae a player sprite
player = Sprite()
# create a group to hold all of our sprites
sprites = pygame.sprite.Group()
# create a group to hold sprites we want to
# test collions against. These sprites will
# still be added to the sprites list
# but we need a seperate group to test for
# collisions against
collision_sprites = pygame.sprite.Group()
# add a sprite to out collison sprite group
# We also add the sprite to our sprites group
# that holds all sprites
tmp = Sprite()
tmp.update = lambda: None
sprites.add(tmp)
collision_sprites.add(tmp)
# add a player sprites to the player group
player.rect.x = 10
sprites.add(player)
running = True # our variable for controlling our game loop
while running:
for e in pygame.event.get(): # iterate ofver all the events pygame is tracking
clock.tick(60) # make our clock keep pour game at 60 FPS
if e.type == pygame.QUIT: # is the user trying to close the window?
running = False # if so break the loop
pygame.quit() # quit the pygame module
quit() # quit is for IDLE friendliness
sprites.update()
# here is where we test for collision
if pygame.sprite.spritecollide(player, collision_sprites, False):
print("HIT!")
display.fill((180, 180, 180)) # fill the pygame screen with white
sprites.draw(display)
pygame.display.flip() # update the screen
My example if fairly big, so take your time and step through it carefully. I tried to add as many good comments as I could. Good luck!

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