I'm working on python project right now using pygame library and I need help with this project I make. I want my tank to shoot bullets and so those bullets ricochet from the walls. What is the best way possible to do this?
I'm sorry that my code looks so messy, I've been watching different youtube tutorials and they all do differently.
Here is my code
import pygame
pygame.init()
# ======================= Variables =======================
# ------------------------ Screen -------------------------
screenWidth = 1060
screenHeight = 798
screenSize = (screenWidth, screenHeight)
display = pygame.display.set_mode((screenWidth, screenHeight))
pygame.display.set_caption("Tank game")
bg = pygame.image.load("sprites/background/background1.png")
bg = pygame.transform.scale(bg, (screenWidth, screenHeight))
# ------------------------ Player -------------------------
# ------------------------ Enemy -------------------------
# ----------------------- Other ---------------------------
red = (155, 0, 0)
clock = pygame.time.Clock()
fps = 60
# ========================= Clases ========================
class player(pygame.sprite.Sprite):
def __init__(self, location, angle, vel, x, y):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("sprites/player/player_tank.png")
self.x = x
self.y = y
self.vel = vel
self.angle = angle
self.rect = self.image.get_rect()
self.rect.left, self.rect.top = location
if self.angle == 360:
self.angle = 0
def rotate(self):
rot_image = pygame.transform.rotate(self.image, self.angle)
rot_rect = rot_image.get_rect(center=self.rect.center)
return rot_image, rot_rect
def moving_after_angle_change(self):
x = round(math.cos(math.radians(self.angle + 90)), 1) * self.vel
y = round(math.sin(math.radians(self.angle - 90)), 1) * self.vel
return x, y
class enemy(pygame.sprite.Sprite):
def __init__(self, x, y, width, height, end):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("sprites/enemy/enemy_tank.png")
self.x = x
self.y = y
self.width = width
self.height = height
self.end = end
self.path = [self.x, self.y, self.end]
self.vel = 5
def draw(self, display):
self.move()
display.blit(self.image, (self.x, self.y))
def move(self):
if self.vel > 0:
pass
# Bullet
bullet = pygame.image.load("sprites/bullet/bullet.png")
bullet = pygame.transform.scale(bullet, (16, 16))
bullet_x = 0
bullet_y = 480
bullet_x_change = 0
bullet_y_change = 10
bullet_state = 'ready'
# ======================= Functions =======================
def redrawGameWindow():
display.blit(bg, (0, 0))
display.blit(player_tank.image, player_tank.rect)
display.blit(enemy_tank.image, (enemy_tank.x, enemy_tank.y))
#display.blit(bullet, (player_tank.x + 160, player_tank.y + 100))
pygame.display.flip()
def fireBullet(x, y):
global bullet_state
bullet_state = 'fire'
display.blit(bullet, (x + 16, y + 10))
player_location = [70, 570]
player_angle = 270
player_angle_change = 0
player_vel = 0
player_x_change = 0
player_y_change = 0
player_tank_x_change_store = 0
player_tank_y_change_store = 0
run = True
while run:
clock.tick(fps)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP:
player_vel = 2
elif event.key == pygame.K_DOWN:
player_vel = -2
elif event.key == pygame.K_LEFT:
player_angle_change = 2
elif event.key == pygame.K_RIGHT:
player_angle_change = -2
if event.key == pygame.K_SPACE:
display.blit(bullet, (player_tank.x + 160, player_tank.y + 100))
if event.type == pygame.KEYUP:
if event.key == pygame.K_UP:
player_vel = 0
elif event.key == pygame.K_DOWN:
player_vel = 0
elif event.key == pygame.K_LEFT:
player_angle_change = 0
elif event.key == pygame.K_RIGHT:
player_angle_change = 0
player_angle += player_angle_change
player_tank = player(player_location, player_angle, player_vel, player_x_change, player_y_change)
enemy_tank = enemy(800, 170, 64, 64, 700)
player_tank.image, player_tank.rect = player_tank.rotate()
player_tank.x_change, player_tank.y_change = player_tank.moving_after_angle_change()
player_tank_x_change_store += player_tank.x_change
player_tank_y_change_store += player_tank.y_change
player_tank.rect.centerx += player_tank_x_change_store
player_tank.rect.centery += player_tank_y_change_store
# Bullet movement
if bullet_state == "fire":
fireBullet(player_tank.x, bullet_y)
bullet_y -= bullet_y_change
redrawGameWindow()
pygame.quit()
quit()
One way to do this is to make the bullets velocity flip when it hits a wall. Now I'm assuming the walls are the edge of the screen so what you need to do is in the loop, check if the bullet is about to hit a wall (the edge of the screen) and if it is, flip its x_change and y_change. So something like this:
if bullet_y <= 0:
bullet_y_change *= -1
bullet_y = 0
if bullet_y >= screenHeight:
bullet_y_change *= -1
bullet_y = screenHeight
if bullet_x <= 0:
bullet_x_change *= -1
bullet_x = 0
if bullet_x >= screenWidth:
bullet_x_change *= 1
bullet_x = screenWidth
Here's a full example, based on an old answer:
import pygame
class Player(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.Surface((32, 32))
self.image.fill((0, 0, 0))
self.image.set_colorkey((0, 0, 0))
pygame.draw.polygon(self.image, pygame.Color('dodgerblue'), ((0, 0), (32, 16), (0, 32)))
self.org_image = self.image.copy()
self.angle = 0
self.direction = pygame.Vector2(1, 0)
self.rect = self.image.get_rect(center=(200, 200))
self.pos = pygame.Vector2(self.rect.center)
def update(self, events, dt):
for e in events:
if e.type == pygame.KEYDOWN:
if e.key == pygame.K_SPACE:
self.groups()[0].add(Projectile(self.rect.center, self.direction.normalize()))
pressed = pygame.key.get_pressed()
if pressed[pygame.K_a]:
self.angle += 3
if pressed[pygame.K_d]:
self.angle -= 3
self.direction = pygame.Vector2(1, 0).rotate(-self.angle)
self.image = pygame.transform.rotate(self.org_image, self.angle)
self.rect = self.image.get_rect(center=self.rect.center)
class Projectile(pygame.sprite.Sprite):
def __init__(self, pos, direction):
super().__init__()
self.image = pygame.Surface((8, 8))
self.image.fill((0, 0, 0))
self.image.set_colorkey((0, 0, 0))
pygame.draw.circle(self.image, pygame.Color('orange'), (4, 4), 4)
self.rect = self.image.get_rect(center=pos)
self.direction = direction
self.pos = pygame.Vector2(self.rect.center)
self.lives = 15
def update(self, events, dt):
# Bounding box of the screen
screen_r = pygame.display.get_surface().get_rect()
# where we would move next
next_pos = self.pos + self.direction * dt
# we hit a hall
if not screen_r.contains(self.rect):
# after 15 hits, destroy self
self.lives -= 1
if self.lives == 0:
return self.kill()
# horizontal reflection
if next_pos.x > screen_r.right or next_pos.x < screen_r.left:
self.direction.x *= -1
# vertical reflection
if next_pos.y > screen_r.bottom or next_pos.y < screen_r.top:
self.direction.y *= -1
# move after applying reflection
next_pos = self.pos + self.direction * dt
# set the new position
self.pos = next_pos
self.rect.center = self.pos
def main():
pygame.init()
screen = pygame.display.set_mode((500, 500))
sprites = pygame.sprite.Group(Player())
clock = pygame.time.Clock()
dt = 0
while True:
events = pygame.event.get()
for e in events:
if e.type == pygame.QUIT:
return
sprites.update(events, dt)
screen.fill((30, 30, 30))
sprites.draw(screen)
pygame.display.update()
dt = clock.tick(60)
if __name__ == '__main__':
main()
See how using the Vector2 class makes this kind of tasks very easy to solve.
Also, look at the proper usage of the Sprite class. Each Sprite minds its own business: the Player class handles rotating the player and creating projectiles, Projectile handles moving the projectiles and bouncing of the wall.
Related
This question already exists:
I want to do an explosion animation. I have 4 png files number 0 - 3. In my debris class I loaded them into an image list using a for loop [duplicate]
Closed 1 year ago.
I want to do an explosion animation. I have 4 png files number 0 - 3. In my debris class I loaded them into an image list using a for loop. Then I made an explosion function but when I load my pygame windows and start shooting my cement block sprites, my cement block sprites disappear which is fine but they don't do the exploding animation I want. How can I fix this problem. Would appreciate the help.
My code:
import random
import pygame
import pygame.freetype
pygame.init()
#screen settings
WIDTH = 1000
HEIGHT = 400
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("AutoPilot")
screen.fill((255, 255, 255))
#fps
FPS = 120
clock = pygame.time.Clock()
#load images
bg = pygame.image.load('background/street.png').convert_alpha() # background
bullets = pygame.image.load('car/bullet.png').convert_alpha()
debris_img = pygame.image.load('debris/cement.png')
#define game variables
shoot = False
#player class
class Player(pygame.sprite.Sprite):
def __init__(self, scale, speed):
pygame.sprite.Sprite.__init__(self)
self.bullet = pygame.image.load('car/bullet.png').convert_alpha()
self.bullet_list = []
self.speed = speed
#self.x = x
#self.y = y
self.moving = True
self.frame = 0
self.flip = False
self.direction = 0
self.score = 0
#load car
self.images = []
img = pygame.image.load('car/car.png').convert_alpha()
img = pygame.transform.scale(img, (int(img.get_width()) * scale, (int(img.get_height()) * scale)))
self.images.append(img)
self.image = self.images[0]
self.rect = self.image.get_rect()
self.update_time = pygame.time.get_ticks()
self.movingLeft = False
self.movingRight = False
self.rect.x = 465
self.rect.y = 325
#draw car to screen
def draw(self):
screen.blit(self.image, (self.rect.centerx, self.rect.centery))
#move car
def move(self):
#reset the movement variables
dx = 0
dy = 0
#moving variables
if self.movingLeft and self.rect.x > 33:
dx -= self.speed
self.flip = True
self.direction = -1
if self.movingRight and self.rect.x < 900:
dx += self.speed
self.flip = False
self.direction = 1
#update rectangle position
self.rect.x += dx
self.rect.y += dy
#shoot
def shoot(self):
bullet = Bullet(self.rect.centerx + 18, self.rect.y + 30, self.direction)
bullet_group.add(bullet)
#check collision
def collision(self, debris_group):
for debris in debris_group:
if pygame.sprite.spritecollide(debris, bullet_group, True):
debris.health -= 1
if debris.health <= 0:
self.score += 1
debris.kill()
#player stats
def stats(self):
myfont = pygame.font.SysFont('comicsans', 30)
scoretext = myfont.render("Score: " + str(self.score), 1, (0,0,0))
screen.blit(scoretext, (100,10))
#bullet class
class Bullet(pygame.sprite.Sprite):
def __init__(self, x, y, direction):
pygame.sprite.Sprite.__init__(self)
self.speed = 5
self.image = bullets
self.rect = self.image.get_rect()
self.rect.center = (x,y)
self.direction = direction
def update(self):
self.rect.centery -= self.speed
#check if bullet has gone off screen
if self.rect.centery < 1:
self.kill()
#debris class
class Debris(pygame.sprite.Sprite):
def __init__(self,scale,speed):
pygame.sprite.Sprite.__init__(self)
self.scale = scale
self.x = random.randrange(100,800)
self.speed_y = 10
self.y = 15
self.speed = speed
self.vy = 0
self.on_ground = True
self.move = True
self.health = 4
self.max_health = self.health
self.alive = True
self.velocity = random.randrange(1,2)
self.speed_x = random.randrange(-3,3)
self.moving_down = True
#load debris
self.image = debris_img
self.rect = self.image.get_rect()
self.rect.x = random.randrange(100, 800)
self.rect.y = random.randrange(-150, -100)
self.rect.center = (self.x,self.y)
#load explosion
self.images = []
for i in range(4):
self.images.append(pygame.image.load(f'explosion/{i}.png'))
self.index = 0
self.img = self.images[self.index]
#spawn new debris
def spawn_new_debris(self):
self.rect.x = random.randrange(100, 800)
self.rect.y = random.randrange(-150, -100)
self.velocity = random.randrange(1, 2)
self.speed_x = random.randrange(-3, 3)
#respawn debris when they go of the screen
def boundaries(self):
if self.rect.left > WIDTH + 10 or self.rect.right < -10 or self.rect.top > HEIGHT + 10:
self.spawn_new_debris()
#update image
def update(self):
self.rect.y += self.velocity
self.rect.x += self.speed_x
self.boundaries()
#make debris fall down
def falldown(self):
self.rect.centery += self.velocity
if self.moving_down and self.rect.y > 350:
self.kill()
#explosion
def explode(self):
if self.max_health <= 0:
self.index += 1
if self.index >= len(self.images):
self.index = 0
self.img = self.images[self.index]
######################CAR/DEBRIS##########################
player = Player(1,5)
##########################################################
#groups
bullet_group = pygame.sprite.Group()
debris_group = pygame.sprite.Group()
all_sprites = pygame.sprite.Group()
for i in range(50):
d = Debris(1,5)
debris_group.add(d)
all_sprites.add(d)
#game runs here
run = True
while run:
#draw street
screen.blit(bg, [0, 0])
#update groups
bullet_group.update()
bullet_group.draw(screen)
debris_group.update()
debris_group.draw(screen)
#draw car
player.draw()
player.move()
player.collision(debris_group)
player.stats()
#update all sprites
all_sprites.update()
all_sprites.draw(screen)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
#check if key is down
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
run = False
if event.key == pygame.K_a:
player.movingLeft = True
if event.key == pygame.K_d:
player.movingRight = True
if event.key == pygame.K_SPACE:
player.shoot()
shoot = True
#check if key is up
if event.type == pygame.KEYUP:
if event.key == pygame.K_a:
player.movingLeft = False
if event.key == pygame.K_d:
player.movingRight = False
#update the display
pygame.display.update()
pygame.display.flip()
clock.tick(FPS)
pygame.quit()
So, I'm a beginner to Python and Pygame and would like some help. I'm trying to make a game similar to Asteroids and I've made most of this program by looking around at other examples on the internet. However, I'm stuck on this problem: how do I get an enemy sprite to follow the player sprite? I've googled how to do so and tried implementing the same thing on my code but the sprite just stays in one place and doesn't follow the player. I've used the vector stuff to create the player sprite and I still barely understand how that works. I'm sort of on a tight schedule so I don't have time to thoroughly understand this stuff but I intend to later. Sorry if I haven't explained my code properly but I'm still trying to understand how Pygame works and most of this code is basically just copied.
import random, math, pygame, time, sys
from pygame.locals import *
from pygame.math import Vector2
######Setting up Variables
class settings:
fps = 30
windowwidth = 590
windowheight = 332
class ship:
HEALTH = 3
SPEED = 4
SIZE = 25
class colours:
black = (0, 0, 0)
white = (255, 255, 255)
###########################
######################################################################################
class Player(pygame.sprite.Sprite):
def __init__(self,image_file , pos=(0,0)):
super(Player, self).__init__()
self.image = pygame.image.load(image_file)
self.image = pygame.transform.scale(self.image, (25,25))
self.original_image = self.image
self.rect = self.image.get_rect(center=pos)
self.position = Vector2(pos)
self.direction = Vector2(1, 0)
self.speed = 0
self.angle_speed = 0
self.angle = 0
def update(self):
if self.angle_speed != 0:
self.direction.rotate_ip(self.angle_speed)
self.angle += self.angle_speed
self.image = pygame.transform.rotate(self.original_image, -self.angle)
self.rect = self.image.get_rect(center=self.rect.center)
self.position += self.direction * self.speed
self.rect.center = self.position
class Enemy(pygame.sprite.Sprite):
def __init__(self, image_file, pos=(0,0)):
super(Enemy, self).__init__()
self.image = pygame.image.load(image_file)
self.image = pygame.transform.scale(self.image, (25, 25))
self.original_image = self.image
self.rect = self.image.get_rect(center=pos)
self.speed = 1
def move_towards_player(self, Player):
dx, dy = self.rect.x - Player.rect.x, self.rect.y - Player.rect.y
dist = math.hypot(dx, dy)
dx, dy = dx/dist, dy/dist
self.rect.x += dx * self.speed
self.rect.y += dy * self.speed
class Background(pygame.sprite.Sprite):
def __init__(self, image_file, location):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load(image_file)
self.rect = self.image.get_rect()
self.rect.left, self.rect.top = location
def main():
pygame.init()
screen = pygame.display.set_mode((settings.windowwidth, settings.windowheight))
pygame.display.set_caption('Final Game')
background = Background('space.jpg',[0,0])
global player, enemy
player = Player('SpaceShip.png',[200,100])
playersprite = pygame.sprite.RenderPlain((player))
enemy = Enemy('Enemy Hexagon.png',[300,150])
enemysprite = pygame.sprite.RenderPlain((enemy))
fpsClock = pygame.time.Clock()
intro = True
while intro == True:
myFont = pygame.font.SysFont('freesansbold.ttf', 75)
otherFont = pygame.font.SysFont('freesansbold.ttf', 30)
SurfaceFont = myFont.render("Space Destroyer", True, (colours.white))
SurfaceFont2 = otherFont.render("Press Space to Start", True, (colours.white))
for event in pygame.event.get():
if event.type == KEYDOWN:
if event.key == K_SPACE:
intro= False
screen.blit(SurfaceFont,(50,50))
screen.blit(SurfaceFont2,(125,125))
pygame.display.flip()
screen.blit(background.image, background.rect)
while intro == False:
fpsClock.tick(settings.fps)
for event in pygame.event.get():
enemy.rect.x
if event.type == QUIT:
pygame.quit()
sys.exit()
elif event.type == KEYDOWN:
if event.key == K_UP:
player.speed += 4
if event.key == K_LEFT:
player.angle_speed = -4
if event.key == K_RIGHT:
player.angle_speed = 4
if player.position.y < 0:
player.position = (player.position.x ,332)
elif player.position.y > settings.windowheight:
player.position = (player.position.x, 0)
elif player.position.x < 0:
player.position = (590, player.position.y)
elif player.position.x > settings.windowwidth:
player.position = (0, player.position.y)
elif event.type == KEYUP:
if event.key == K_LEFT:
player.angle_speed = 0
if event.key == K_RIGHT:
player.angle_speed = 0
if event.key == K_UP:
player.speed = 0
screen.fill(colours.white)
screen.blit(background.image, background.rect)
enemysprite.draw(screen)
enemysprite.update()
playersprite.draw(screen)
playersprite.update()
pygame.display.update()
playersprite.update()
pygame.display.flip()
if __name__ == '__main__': main()
You never called move_towards_player. Try adding this:
...
enemysprite.draw(screen)
enemysprite.update()
playersprite.draw(screen)
playersprite.update()
enemy.move_towards_player(player)
...
import math
import random
import pygame
from pygame.locals import *
import sys
def events():
for event in pygame.event.get():
if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE):
pygame.quit()
sys.exit()
pygame.init()
W = 700
H = 400
updater = pygame.time.Clock()
display = pygame.display.set_mode((700, 400))
pygame.display.set_caption("Skating_Game")
x = y = 0
surface = pygame.image.load("man2.png")
pygame.display.set_icon(surface)
class player:
def __init__(self, velocity, maxJumpRange):
self.velocity = velocity
self.maxJumpRange = maxJumpRange
def setLocation(self, x, y):
self.x = x
self.y = y
self.xVelocity = 0
self.jumping = False
self.jumpCounter = 0
self.falling = True
def keys(self):
k = pygame.key.get_pressed()
if k[K_LEFT]:
self.xVelocity = -self.velocity
elif k[K_RIGHT]:
self.xVelocity = self.velocity
else:
self.xVelocity = 0
if k[K_SPACE] and not self.jumping and not self.falling:
self.jumping = True
self.jumpCounter = 0
def move(self):
self.x += self.xVelocity
if self.jumping:
self.y -= self.velocity
self.jumpCounter += 1
if self.jumpCounter == self.maxJumpRange:
self.jumping = False
self.falling = True
elif self.falling:
if self.y <= H - 60 and self.y + self.velocity >= H - 60:
self.y = H - 60
self.falling = False
else:
self.y += self.velocity
def draw(self):
display = pygame.display.get_surface()
character = pygame.image.load("man3.png").convert_alpha()
display.blit(character, (self.x, self.y - 100))
#pygame.draw.circle(display, (255, 255, 255), (self.x, self.y - 25), 25, 0)
def do(self):
self.keys()
self.move()
self.draw()
P = player(3, 50)
P.setLocation(350, 0)
BLACK = ( 0, 0, 0)
g=0
font = pygame.font.SysFont("Plump", 30)
obstacle = pygame.image.load("obstacle.png").convert_alpha()
background = pygame.image.load("Road.png").convert()
x = 0
while True:
events()
rel_x = x % background.get_rect().width
display.blit(background, (rel_x - background.get_rect().width,0))
if rel_x < 700:
display.blit(background, (rel_x, 0))
x -= 1
g += 0.01
pygame.draw.rect(display, (255,255,255,128), [rel_x, 275, 150, 50])
display.blit(obstacle, (rel_x, 250))
text = font.render("Score: "+str(int(g)), True, (255, 255, 255))
display.blit(text, (0,0))
P.do()
if P.rect.collidepoint(self.x,self.y):
pygame.quit()
pygame.display.update()
updater.tick(200)
So if the player collides with the obstacle image the game should stop. How do i do this? I have made a class for the player and the obstacle is just an image which is constantly moving.
I was thinking maybe I could track the x and y coordinate of the player and obstacle and when their radius overlaps the game could stop.
Here's a working (simplified) version of your program with some comments. You have to create rects for the obstacle and the player and then check if the rects collide with the help of the colliderect method.
import sys
import pygame
from pygame.locals import *
pygame.init()
W = 700
H = 400
updater = pygame.time.Clock()
display = pygame.display.set_mode((700, 400))
PLAYER_IMAGE = pygame.Surface((30, 50))
PLAYER_IMAGE.fill(pygame.Color('dodgerblue1'))
class Player:
def __init__(self, x, y, velocity, maxJumpRange):
self.velocity = velocity
self.maxJumpRange = maxJumpRange
self.image = PLAYER_IMAGE # Give the player an image.
# Create a rect with the size of the PLAYER_IMAGE and
# pass the x, y coords as the topleft argument.
self.rect = self.image.get_rect(topleft=(x, y))
self.x = x
self.y = y
self.xVelocity = 0
self.jumping = False
self.jumpCounter = 0
self.falling = True
def keys(self):
k = pygame.key.get_pressed()
if k[K_LEFT]:
self.xVelocity = -self.velocity
elif k[K_RIGHT]:
self.xVelocity = self.velocity
else:
self.xVelocity = 0
if k[K_SPACE] and not self.jumping and not self.falling:
self.jumping = True
self.jumpCounter = 0
def move(self):
self.x += self.xVelocity
if self.jumping:
self.y -= self.velocity
self.jumpCounter += 1
if self.jumpCounter == self.maxJumpRange:
self.jumping = False
self.falling = True
elif self.falling:
if self.y >= H - 160: # Simplified a little.
self.y = H - 160
self.falling = False
else:
self.y += self.velocity
# Update the position of the rect, because it's
# used for the collision detection.
self.rect.topleft = self.x, self.y
def draw(self, display):
# Just draw the image here.
display.blit(self.image, (self.x, self.y))
def do(self):
self.keys()
self.move()
player = Player(350, 0, 3, 50)
obstacle = pygame.Surface((150, 50))
obstacle.fill(pygame.Color('sienna1'))
# Create a rect with the size of the obstacle image.
obstacle_rect = obstacle.get_rect()
g = 0
x = 0
FPS = 60 # Cap the frame rate at 60 or 30 fps. 300 is crazy.
while True:
for event in pygame.event.get():
if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE):
pygame.quit()
sys.exit()
# --- Update the game ---
player.do()
rel_x = x % display.get_width()
x -= 7
g += 0.01
obstacle_rect.topleft = rel_x, 250 # Update the position of the rect.
# --- Draw everything ---
display.fill((30, 30, 30))
display.blit(obstacle, (rel_x, 250))
if g > 30:
display.blit(obstacle, (rel_x+350, 250))
# Check if the obstacle rect and the player's rect collide.
if obstacle_rect.colliderect(player.rect):
print("Game over!") # And call pygame.quit and sys.exit if you want.
# Draw the image/surface of the player onto the screen.
player.draw(display)
# Draw the actual rects of the objects (for debugging).
pygame.draw.rect(display, (200, 200, 0), player.rect, 2)
pygame.draw.rect(display, (200, 200, 0), obstacle_rect, 2)
pygame.display.update()
updater.tick(FPS)
Pygame rectangles include a collidepoint and colliderect method that allows you to check to see if something intersects with a rectangle. So you could have rectangles drawn beneath the obstacle and check to see if the player's coordinates intersect with the rectangle. Like this:
if self.rect.collidepoint(self.x,self.y):
pygame.quit()
In pygame I have a projectile being shot from one character sprite to another which I would like to determine whether there is a collision or not. That is a collision between a shot projectile and another character I will call TRUMP. I have found an equation in a tutorial that is the best example of an arching trajectory that I can accomplish. If that equation could be helped it would be awesome.
def fireshell(self, elapsedTime):
fire = True
FPS = 60 # frames per second setting
fpsClock = pg.time.Clock()
print("fire", self.pos.x, self.pos.y)
fireX = int(self.pos.x)
fireY = int(self.pos.y)
print("fireX", fireX, "fireY", fireY, "elapsed time", elapsedTime)
power = elapsedTime*.0005
x = int(self.pos.x)
y = int(self.pos.y) - 100
while fire:
for event in pg.event.get():
if event.type == pg.QUIT:
pg.quit()
quit()
pg.draw.circle(self.screen, RED, (x, y), 5)
x -= int(-(elapsedTime*6))
y += int((((x - fireX)*0.015)**2) - ((elapsedTime*2)/(12 - elapsedTime )))
print("X:", x,"Y:", y)
if y > HEIGHT or x > WIDTH:
fire = False
pg.display.update()
self.clock.tick(20)
My character sprite who I would like to check for collisions with the projectile is here:
class TRUMP(pg.sprite.Sprite):
def __init__(self, spritesheet, all_sprites, mudballGroup, jetsGroup):
pg.sprite.Sprite.__init__(self)
self.spritesheet = spritesheet
self.all_sprites = all_sprites
self.mudballGroup = mudballGroup
self.jetsGroup = jetsGroup
self.current_frame2 = 0
self.last_update2 = 0
self.load_images()
self.image = self.TRUMP_fingers_l
self.rect = self.image.get_rect()
self.rect.center = (WIDTH *3/4), (589)
self.rect.centerx = (WIDTH *3/4)
self.rect.centery = 589
self.rect.centerx = (WIDTH*5/6)
self.rect.centery = 589
self.pos = vec((WIDTH/2), (HEIGHT/2))
self.vel = vec(0, 0)
self.acc = vec(0, 0)
self.dir = 0
To get a ballistic trajectory, you can just add a GRAVITY constant to the y-value of the velocity vector each frame.
For the collision detection you can use pygame.sprite.spritecollide again (you already know how that works).
Here's a complete example:
import sys
import pygame as pg
GRAVITY = 3
class Player(pg.sprite.Sprite):
def __init__(self, pos, color):
super().__init__()
self.image = pg.Surface((50, 30))
self.image.fill(color)
self.rect = self.image.get_rect(center=pos)
self.pos = pg.math.Vector2(pos)
self.vel = pg.math.Vector2()
def update(self):
self.pos += self.vel
self.rect.center = self.pos
class Projectile(pg.sprite.Sprite):
def __init__(self, pos, color, target):
super().__init__()
self.image = pg.Surface((7, 5))
self.image.fill(color)
self.rect = self.image.get_rect(center=pos)
self.pos = pg.math.Vector2(pos)
direction = target - self.pos # Vector to the target.
# Normalize, then scale direction to adjust the speed.
self.vel = direction.normalize() * 33
def update(self):
self.pos += self.vel
self.vel.y += GRAVITY
self.rect.center = self.pos
if self.rect.y > 580:
self.kill()
class Game:
def __init__(self):
self.fps = 30
self.screen = pg.display.set_mode((800, 600))
pg.display.set_caption('Ballistic trajectory')
self.clock = pg.time.Clock()
self.bg_color = pg.Color(90, 120, 100)
self.green = pg.Color('aquamarine2')
self.blue = pg.Color(30, 90, 150)
self.font = pg.font.Font(None, 30)
self.player = Player((100, 300), self.green)
self.player2 = Player((400, 300), self.blue)
self.all_sprites = pg.sprite.Group(self.player, self.player2)
self.projectiles = pg.sprite.Group()
self.collisions = 0
self.done = False
def run(self):
while not self.done:
self.handle_events()
self.run_logic()
self.draw()
self.clock.tick(self.fps)
def handle_events(self):
for event in pg.event.get():
if event.type == pg.QUIT:
self.done = True
if event.type == pg.MOUSEBUTTONDOWN:
if event.button == 1:
proj = Projectile(
self.player.rect.center, pg.Color('sienna2'), event.pos)
self.projectiles.add(proj)
self.all_sprites.add(proj)
if event.type == pg.KEYDOWN:
if event.key == pg.K_a:
self.player.vel.x = -3
if event.key == pg.K_d:
self.player.vel.x = 3
if event.key == pg.K_w:
self.player.vel.y = -3
if event.key == pg.K_s:
self.player.vel.y = 3
if event.type == pg.KEYUP:
if event.key == pg.K_a and self.player.vel.x == -3:
self.player.vel.x = 0
if event.key == pg.K_d and self.player.vel.x == 3:
self.player.vel.x = 0
if event.key == pg.K_w and self.player.vel.y == -3:
self.player.vel.y = 0
if event.key == pg.K_s and self.player.vel.y == 3:
self.player.vel.y = 0
def run_logic(self):
self.all_sprites.update()
hits = pg.sprite.spritecollide(self.player2, self.projectiles, True)
for collided_sprite in hits:
self.collisions += 1
def draw(self):
self.screen.fill(self.bg_color)
self.all_sprites.draw(self.screen)
txt = self.font.render('Collisions {}'.format(self.collisions), True, self.green)
self.screen.blit(txt, (20, 20))
pg.display.flip()
if __name__ == '__main__':
pg.init()
game = Game()
game.run()
pg.quit()
sys.exit()
Hello I am currently making a survival shooter but cannot find any way on how to do my collisions! I am trying to make the player collide with the floors but with no success. Here is an image of the map (I want to collide with moon blocks):
http://i.stack.imgur.com/R2apz.jpg
Here is an image of moon blocks with no Background:
http://i.stack.imgur.com/W9H2m.png
And finally here is my code:
import pygame
import random
gravity = .5
jump_time = 2000
x = 0
y = 0
# Player
class Player(pygame.sprite.Sprite):
def __init__(self, x, y, gravity):
# Player dimensions and position
self.gravity = gravity
# Player image and animation
self.images = []
self.images.append(pygame.image.load('images/player.png'))
self.images.append(pygame.image.load('images/player2.png'))
#~ self.images.append(pygame.image.load('ball1.png'))
#~ self.images.append(pygame.image.load('ball2.png'))
self.maxImage = len(self.images)
self.currentImage = 0
#~ self.rect = pygame.Rect(x, y, 80, 80)
self.rect = self.images[0].get_rect()
self.rect.x = x
self.rect.y = y
self.timeTarget = 10
self.timeNum = 1
self.velX = 0
self.velY = 0
# Jump and gravity
self.vSpeed = 3
self.jumpForce = 15
self.maxVspeed = 3
self.isJumping = False
# Jump inputs
def handle_events(self, event):
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
if not self.isJumping:
self.isJumping = True
elif event.key == pygame.K_a:
self.velX = -5
elif event.key == pygame.K_d:
self.velX = +5
elif event.type == pygame.KEYUP:
if event.key in (pygame.K_a, pygame.K_d):
self.velX = 0
# PLayer updates
def update(self, ground):
keys = pygame.key.get_pressed()
# Jumping
self.vSpeed += gravity
if self.vSpeed > self.maxVspeed:
self.vSpeed = self.maxVspeed
self.rect.y += self.vSpeed
if self.rect.y >= ground.y:
self.vSpeed = 0
self.rect.y = ground.y
self.isJumping = False
if keys[pygame.K_SPACE]:
if not self.isJumping:
self.isJumping = True
if self.isJumping:
if pygame.time.get_ticks() < jump_time:
self.isJumping == True
else:
self.isJumping = False
self.vSpeed -= self.jumpForce
#print "isJumping:", self.isJumping
# Animations
if self.timeNum == self.timeTarget:
self.currentImage += 1
if self.currentImage >= self.maxImage:
self.currentImage = 0
self.timeNum = 0
self.rect.centerx += self.velX
self.rect.centery += self.velY
# Screen wrap
if self.rect.right > 1280:
self.rect.left = 0
elif self.rect.left < 0:
self.rect.right = 1280
# Player rendering
def render(self, surface):
surface.blit(self.images[self.currentImage], self.rect)
#----------------------------------------------------------------------
class Zombie():
def __init__(self, x, y):
self.image = pygame.image.load('images/zombie.png')
#~ self.image = pygame.image.load('ball2.png')
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
self.direction_left = True
def update(self, surface_rect):
if self.direction_left:
self.rect.x -= 1
if self.rect.left <= surface_rect.left:
self.direction_left = not self.direction_left
else:
self.rect.x += 1
if self.rect.right >= surface_rect.right:
self.direction_left = not self.direction_left
def render(self, surface):
surface.blit(self.image, self.rect)
#----------------------------------------------------------------------
class Background():
def __init__(self):
self.image = pygame.image.load('images/arena2.jpg')
#~ self.image = pygame.image.load('background.jpg')
self.rect = self.image.get_rect()
def render(self, surface):
surface.blit(self.image, self.rect)
#----------------------------------------------------------------------
class Game():
def __init__(self):
pygame.init()
# A few variables
self.gravity = .50
self.ground = pygame.Rect(0, 640, 1280, 80)
# Screen
size = (1280, 720)
self.screen = pygame.display.set_mode(size)
pygame.display.set_caption('Moon Survival!')
# Moon / Background
self.moon = Background()
# Zombies
self.zombies = []
for i in range(10):
self.zombies.append( Zombie(random.randint(0,1280), random.randint(0,720)) )
# Player
self.player = Player(25, 320, self.gravity)
# Font for text
self.font = pygame.font.SysFont(None, 72)
# Pause - center on screen
self.pause_text = self.font.render("PAUSE", -1, (255,0,0))
self.pause_rect = self.pause_text.get_rect(center = self.screen.get_rect().center)
def run(self):
clock = pygame.time.Clock()
# "state machine"
RUNNING = True
PAUSED = False
GAME_OVER = False
# Game loop
while RUNNING:
# (all) Events
for event in pygame.event.get():
if event.type == pygame.QUIT:
RUNNING = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
RUNNING = False
elif event.key == pygame.K_p:
PAUSED = not PAUSED
# Player/Zomies events
if not PAUSED and not GAME_OVER:
self.player.handle_events(event)
# (all) Movements / Updates
if not PAUSED and not GAME_OVER:
self.player.update(self.ground)
for z in self.zombies:
z.update(self.screen.get_rect())
# (all) Display updating
self.moon.render(self.screen)
for z in self.zombies:
z.render(self.screen)
self.player.render(self.screen)
if PAUSED:
self.screen.blit(self.pause_text, self.pause_rect)
pygame.display.update()
# FTP
clock.tick(100)
# --- the end ---
pygame.quit()
#---------------------------------------------------------------------
Game().run()
Any help will be greatly appreciated, thank you.
I made a very similar game for PyWeek #17 called "Miner".
Here's a screenshot: http://media.pyweek.org/dl/17/powrtoch/miner_ss.png (you can see the similarity!)
You can find the relevant code on GitHub here: https://github.com/marcusmoller/pyweek17-miner/blob/master/miner/engine.py#L202-L220:
def checkCollision(self, sprite, xVel, yVel):
for x in range(len(level.levelStructure)):
for y in range(len(level.levelStructure[x])):
block = level.levelStructure[x][y]
if block is not None:
if pygame.sprite.collide_rect(sprite, block):
if xVel < 0:
sprite.rect.x = block.rect.x + block.rect.w
if xVel > 0:
sprite.rect.x = block.rect.x - sprite.rect.w
if yVel < 0:
sprite.rect.y = block.rect.y + block.rect.h
if yVel > 0 and not sprite.onGround:
sprite.onGround = True
sprite.rect.y = block.rect.y - sprite.rect.h
You could do something like this using collide_rect:
# See if the Sprite block collides with anything in the Ground block_list
for block in groundBlocks:
if(collide_rect(player, block)):
# do not fall
else:
# fall
where all Block you can walk on are in the groundBlocks list.