Collisions aren't being detected in pygame - python

I don't know why but collisions aren't being detected in my game. Its an asteroids style game and I want the bullets to destroy the asteroids, and the game to end when the ship gets hit by one, but they're passing through each other without doing any of that.
Here's my code:
import pygame
from math import sin, cos, pi
from random import randint
scr_width = 800
scr_height = 600
window = pygame.display.set_mode((scr_width, scr_height))
pygame.display.set_caption("Asteroids")
clock = pygame.time.Clock()
space_img = pygame.image.load("sprites/space.jpg")
red = (255, 0, 0)
class Ship:
def __init__(self, x, y):
self.x = x
self.y = y
self.width = 0
self.vel = 0
self.vel_max = 12
self.angle = 0
self.hitbox = (self.x, self.y, 10, 10)
self.ship_img = pygame.image.load("sprites/ship_off.png")
self.ship_img_copy = pygame.transform.rotate(self.ship_img, self.angle)
def draw(self):
self.ship_img = pygame.image.load("sprites/ship_off.png")
self.ship_img_copy = pygame.transform.rotate(self.ship_img, self.angle)
window.blit(self.ship_img_copy,
(self.x - (self.ship_img_copy.get_width()) / 2, self.y - (self.ship_img_copy.get_height()) / 2))
keys = pygame.key.get_pressed()
if keys[pygame.K_w]:
self.ship_img = pygame.image.load("sprites/ship_on.png")
self.ship_img_copy = pygame.transform.rotate(self.ship_img, self.angle)
window.blit(self.ship_img_copy,
(self.x - (self.ship_img_copy.get_width()) / 2, self.y - (self.ship_img_copy.get_height()) / 2))
# collision stuff
self.mask = pygame.mask.from_surface(self.ship_img_copy)
self.rect = pygame.Rect(self.x - (self.ship_img_copy.get_width()) / 2,
self.y - (self.ship_img_copy.get_height()) / 2,
self.ship_img_copy.get_width(), self.ship_img_copy.get_height())
def move(self):
keys = pygame.key.get_pressed()
# todo acceleration and thrust mechanics
if keys[pygame.K_w]:
self.vel = min(self.vel + 1, self.vel_max)
elif self.vel > 0:
self.vel = self.vel - 0.4
if keys[pygame.K_a]:
self.angle += 7
if keys[pygame.K_d]:
self.angle -= 7
self.x += self.vel * cos(self.angle * (pi / 180) + (90 * pi / 180))
self.y -= self.vel * sin(self.angle * (pi / 180) + 90 * (pi / 180))
# So that if it leaves one side it comes from the other
if self.y < 0:
self.y = (self.y - self.vel) % 600
elif self.y > 600:
self.y = (self.y + self.vel) % 600
elif self.x < 0:
self.x = (self.x - self.vel) % 800
elif self.x > 800:
self.x = (self.x + self.vel) % 800
class Asteroid:
def __init__(self):
self.ang_change = randint(1, 5)
self.ang = randint(0, 90) * (pi / 180)
y_values = [1, 599]
self.sx = randint(0, 800)
self.sy = y_values[randint(0, 1)]
# If object spawns from the top, it moves down instead of moving up and de-spawning immediately
if self.sy == y_values[0]:
self.neg = -1
else:
self.neg = 1
self.speed = randint(5, 10)
self.ang += self.ang_change
self.asteroid_angle = randint(0, 80)
self.asteroid_img = pygame.image.load("sprites/asteroid.png")
self.asteroid_copy = pygame.transform.rotate(self.asteroid_img, self.ang)
def generate(self):
self.ang += self.ang_change
self.asteroid_img = pygame.image.load("sprites/asteroid.png")
self.asteroid_copy = pygame.transform.rotate(self.asteroid_img, self.ang)
window.blit(self.asteroid_copy,
(self.sx - (self.asteroid_copy.get_width()) / 2, self.sy - (self.asteroid_copy.get_height()) / 2))
# collision stuff
self.mask = pygame.mask.from_surface(self.asteroid_copy)
self.rect = pygame.Rect(self.sx - (self.asteroid_copy.get_width()) / 2,
self.sy - (self.asteroid_copy.get_height()) / 2,
self.asteroid_copy.get_width(), self.asteroid_copy.get_height())
class Projectiles:
def __init__(self, x, y, angle):
self.x = x
self.y = y
self.angle = angle
self.vel = 20
self.bullet_body = pygame.image.load("sprites/bullet.png")
def draw(self):
self.bullet_body = pygame.image.load("sprites/bullet.png")
# collision stuff
self.rect = pygame.Rect(self.x - (self.bullet_body.get_width()) / 2,
self.y - (self.bullet_body.get_height()) / 2, 5, 5)
window.blit(self.bullet_body, (self.x - 2, self.y))
self.mask = pygame.mask.from_surface(self.bullet_body)
def redraw():
window.blit(space_img, (0, 0))
ship.draw()
for asteroid in asteroids:
asteroid.generate()
for bullet in bullets:
bullet.draw()
pygame.display.update()
# collision stuff
def collisions():
asteroids = pygame.sprite.Group()
bullets = pygame.sprite.Group()
if pygame.sprite.spritecollide(ship, asteroids, True, pygame.sprite.collide_mask):
print("hit")
pygame.quit()
pygame.sprite.groupcollide(asteroids, bullets, True, True, pygame.sprite.collide_mask)
# main loop
run = True
ship = Ship(400, 300)
next_fire = pygame.time.get_ticks() + 400
bullets = []
asteroids = []
while run:
clock.tick(60)
keys = pygame.key.get_pressed()
pygame.time.delay(35)
# collision stuff
collisions()
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if keys[pygame.K_SPACE]:
if len(bullets) < 11 and pygame.time.get_ticks() >= next_fire:
bullets.append(
Projectiles(round(ship.x + ship.width - 6.5 // 2), round(ship.y + ship.width - 6.5 // 2), ship.angle))
next_fire = pygame.time.get_ticks() + 400
for bullet in bullets:
if 800 > bullet.x > 0 and 600 > bullet.y > 0:
bullet.x += bullet.vel * cos(bullet.angle * (pi / 180) + 90 * (pi / 180))
bullet.y -= bullet.vel * sin(bullet.angle * (pi / 180) + 90 * (pi / 180))
else:
bullets.pop(bullets.index(bullet))
# To limit the number of asteroids on screen
if len(asteroids) < 5:
asteroids.append(Asteroid())
for asteroid in asteroids:
if 805 > asteroid.sx > 0 and 605 > asteroid.sy > 0:
asteroid.sx += asteroid.speed * cos(asteroid.asteroid_angle * (pi / 180) + 90 * (pi / 180))
asteroid.sy -= asteroid.speed * sin(asteroid.asteroid_angle * (pi / 180) + 90 * (pi / 180)) * asteroid.neg
if asteroid.sx < 0:
asteroid.sx = (asteroid.sx - asteroid.speed) % 805
elif asteroid.sx > 805:
asteroid.sx = (asteroid.sx + asteroid.speed) % 805
else:
asteroids.pop(asteroids.index(asteroid))
window.fill((0, 0, 0))
ship.move()
redraw()
pygame.quit()
Is it that I can't make a seperate function for collisions, or have I missed something else?

I couldn't test code but usually problem is that collision's function use values from self.rect but you don't use them but self.x and self.y to keep position.
You should do in __init__
self.rect = self.image.get_rect()
self.rect.center = (x, y)
and later you can add to self.rect.x and self.rect.y and it will automatically calculate position for
blit(self.image, self.rect)
and for collision with other object
self.rect.colliderect(other.rect)
or with mouse in event.MOUSEBUTTONDOWN
self.rect.collidepoint(event.pos)
to test if object was clicked (ie. button).
If you want to use pygame Group
asteroids = pygame.sprite.Group()
bullets = pygame.sprite.Group()
then you shouldn't create them inside collisions() but at start instead of lists
asteroids = []
bullets = []
and you should add objects to groups instead of appending to lists.
asteroids.add(Asteroid())
and you can even run function for all objects without using for-loop
asteroids.draw()

Related

Collisions still aren't getting detected in pygame

I've tried making some of the changes from the other question I had asked, here's the link:
Collisions aren't being detected in pygame
But anyway, I'm trying to make an asteroids style game, where the asteroids can be destroyed when shot by the player and the ship will be destroyed when hit by an asteroid. The problem is, my collisions aren't being detected at all and they're harmlessly passing through each other.
Since people couldn't run my code last time, here are the sprites I'm using:
ship_on =
ship_off =
space =
bullet =
asteroid =
and here's my code:
import pygame
from math import sin, cos, pi
from random import randint
scr_width = 800
scr_height = 600
window = pygame.display.set_mode((scr_width, scr_height))
pygame.display.set_caption("Asteroids")
clock = pygame.time.Clock()
space_img = pygame.image.load("sprites/space.jpg")
red = (255, 0, 0)
# todo object collisions
# todo shooting at larger intervals
# todo score system
# todo asteroid division
class Ship:
def __init__(self, x, y):
self.x = x
self.y = y
self.width = 0
self.vel = 0
self.vel_max = 12
self.angle = 0
self.image = pygame.image.load("sprites/ship_off.png")
self.image_copy = pygame.transform.rotate(self.image, self.angle)
self.mask = pygame.mask.from_surface(self.image_copy)
self.rect = pygame.Rect(self.x - (self.image_copy.get_width()) / 2,
self.y - (self.image_copy.get_height()) / 2,
self.image_copy.get_width(), self.image_copy.get_height())
def draw(self):
self.image = pygame.image.load("sprites/ship_off.png")
self.image_copy = pygame.transform.rotate(self.image, self.angle)
window.blit(self.image_copy,
(self.x - (self.image_copy.get_width()) / 2, self.y - (self.image_copy.get_height()) / 2))
keys = pygame.key.get_pressed()
if keys[pygame.K_w]:
self.image = pygame.image.load("sprites/ship_on.png")
self.image_copy = pygame.transform.rotate(self.image, self.angle)
window.blit(self.image_copy,
(self.x - (self.image_copy.get_width()) / 2, self.y - (self.image_copy.get_height()) / 2))
def move(self):
keys = pygame.key.get_pressed()
# todo acceleration and thrust mechanics
if keys[pygame.K_w]:
self.vel = min(self.vel + 1, self.vel_max)
elif self.vel > 0:
self.vel = self.vel - 0.4
if keys[pygame.K_a]:
self.angle += 7
if keys[pygame.K_d]:
self.angle -= 7
self.x += self.vel * cos(self.angle * (pi / 180) + (90 * pi / 180))
self.y -= self.vel * sin(self.angle * (pi / 180) + 90 * (pi / 180))
# So that if it leaves one side it comes from the other
if self.y < 0:
self.y = (self.y - self.vel) % 600
elif self.y > 600:
self.y = (self.y + self.vel) % 600
elif self.x < 0:
self.x = (self.x - self.vel) % 800
elif self.x > 800:
self.x = (self.x + self.vel) % 800
class Asteroid(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
y_values = [1, 599]
self.x = randint(0, 800)
self.y = y_values[randint(0, 1)]
# If object spawns from the top, it moves down instead of moving up and de-spawning immediately
if self.y == y_values[0]:
self.neg = -1
else:
self.neg = 1
self.speed = randint(5, 10)
self.ang = randint(0, 90) * (pi / 180)
self.ang_change = randint(1, 5)
self.asteroid_angle = randint(0, 80)
self.image = pygame.image.load("sprites/asteroid.png")
self.image_copy = pygame.transform.rotate(self.image, self.ang)
self.mask = pygame.mask.from_surface(self.image_copy)
self.rect = pygame.Rect(self.x - (self.image_copy.get_width()) / 2,
self.y - (self.image_copy.get_height()) / 2,
self.image_copy.get_width(), self.image_copy.get_height())
def generate(self):
self.ang += self.ang_change
self.image = pygame.image.load("sprites/asteroid.png")
self.image_copy = pygame.transform.rotate(self.image, self.ang)
window.blit(self.image_copy,
(self.x - (self.image_copy.get_width()) / 2, self.y - (self.image_copy.get_height()) / 2))
class Projectiles(pygame.sprite.Sprite):
def __init__(self, x, y, angle):
super().__init__()
self.x = x
self.y = y
self.angle = angle
self.vel = 20
self.image = pygame.image.load("sprites/bullet.png")
self.mask = pygame.mask.from_surface(self.image)
self.rect = self.rect = pygame.Rect(self.x - (self.image.get_width()) / 2,
self.y - (self.image.get_height()) / 2, 5, 5)
def draw(self):
window.blit(self.image, (self.x - 2, self.y))
def redraw():
window.blit(space_img, (0, 0))
ship.draw()
for asteroid in asteroids:
asteroid.generate()
for bullet in bullets:
bullet.draw()
pygame.display.update()
def collisions():
pygame.sprite.spritecollide(ship, asteroids, True, pygame.sprite.collide_mask)
pygame.sprite.groupcollide(asteroids, bullets, True, True, pygame.sprite.collide_mask)
# main loop
run = True
ship = Ship(400, 300)
next_fire = pygame.time.get_ticks() + 400
asteroids = pygame.sprite.Group()
bullets = pygame.sprite.Group()
while run:
clock.tick(60)
keys = pygame.key.get_pressed()
pygame.time.delay(35)
collisions()
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if keys[pygame.K_SPACE]:
if len(bullets) < 11 and pygame.time.get_ticks() >= next_fire:
bullets.add(
Projectiles(round(ship.x + ship.width - 6.5 // 2), round(ship.y + ship.width - 6.5 // 2), ship.angle))
next_fire = pygame.time.get_ticks() + 400
for bullet in bullets:
if 800 > bullet.x > 0 and 600 > bullet.y > 0:
bullet.x += bullet.vel * cos(bullet.angle * (pi / 180) + 90 * (pi / 180))
bullet.y -= bullet.vel * sin(bullet.angle * (pi / 180) + 90 * (pi / 180))
else:
bullet.kill()
# To limit the number of asteroids on screen
if len(asteroids) < 5:
asteroids.add(Asteroid())
for asteroid in asteroids:
if 800 > asteroid.x > 0 and 600 > asteroid.y > 0:
asteroid.x += asteroid.speed * cos(asteroid.asteroid_angle * (pi / 180) + 90 * (pi / 180))
asteroid.y -= asteroid.speed * sin(asteroid.asteroid_angle * (pi / 180) + 90 * (pi / 180)) * asteroid.neg
if asteroid.x < 0:
asteroid.x = (asteroid.x - asteroid.speed) % 800
elif asteroid.x > 800:
asteroid.x = (asteroid.x + asteroid.speed) % 800
else:
asteroid.kill()
window.fill((0, 0, 0))
ship.move()
redraw()
pygame.quit()
Hopefully, having sprites can help.
The operations spritecollide and groupcollide use the .rect attribute of a Sprite object to detect the collision.
Hence, when you change the position of a Sprite object (.x, .y), then you have to update the .rect attribute, too.
At the end of the method move of the class Ship:
class Ship:
# [...]
def move(self):
# [...]
self.rect.center = (self.x, self.y) # <---- ADD
And for the bullets and asteroids in the main application loop:
while run:
# [...]
for bullet in bullets:
if 800 > bullet.x > 0 and 600 > bullet.y > 0:
bullet.x += bullet.vel * cos(bullet.angle * (pi / 180) + 90 * (pi / 180))
bullet.y -= bullet.vel * sin(bullet.angle * (pi / 180) + 90 * (pi / 180))
bullet.rect.center = (bullet.x, bullet.y) # <---- ADD
else:
bullet.kill()
# To limit the number of asteroids on screen
if len(asteroids) < 5:
asteroids.add(Asteroid())
for asteroid in asteroids:
if 800 > asteroid.x > 0 and 600 > asteroid.y > 0:
asteroid.x += asteroid.speed * cos(asteroid.asteroid_angle * (pi / 180) + 90 * (pi / 180))
asteroid.y -= asteroid.speed * sin(asteroid.asteroid_angle * (pi / 180) + 90 * (pi / 180)) * asteroid.neg
if asteroid.x < 0:
asteroid.x = (asteroid.x - asteroid.speed) % 800
elif asteroid.x > 800:
asteroid.x = (asteroid.x + asteroid.speed) % 800
asteroid.rect.center = (asteroid.x, asteroid.y) # <---- ADD
else:
asteroid.kill()

A mask collision detection problem in pygame

I'm makin an asteroids-style game with pygame rn, but I've come across a problem when tryin to get the mask collisions to work. In order to check if they were colliding, I used a function to print "hit" every time a collision was detected. Problem was, when I ran the program it just started printing hit non-stop. So I think somehow it took the background to be a mask or something? Dunno really, I haven't worked with collisions before. I just want the game to end when the ship collides with an asteroid.
Anyway here's my code
import pygame
from math import sin, cos, pi
from random import randint
scr_width = 800
scr_height = 600
window = pygame.display.set_mode((scr_width, scr_height))
pygame.display.set_caption("Asteroids")
clock = pygame.time.Clock()
space_img = pygame.image.load("sprites/space.jpg")
red = (255, 0, 0)
class Ship:
def __init__(self, x, y):
self.x = x
self.y = y
self.width = 0
self.vel = 0
self.vel_max = 12
self.angle = 0
self.hitbox = (self.x, self.y, 10, 10)
self.ship_img = pygame.image.load("sprites/ship_off.png")
self.ship_img_copy = pygame.transform.rotate(self.ship_img, self.angle)
# collision stuff
self.rect = self.ship_img_copy.get_rect()
def draw(self):
self.ship_img = pygame.image.load("sprites/ship_off.png")
self.ship_img_copy = pygame.transform.rotate(self.ship_img, self.angle)
window.blit(self.ship_img_copy,
(self.x - (self.ship_img_copy.get_width()) / 2, self.y - (self.ship_img_copy.get_height()) / 2))
keys = pygame.key.get_pressed()
if keys[pygame.K_w]:
self.ship_img = pygame.image.load("sprites/ship_on.png")
self.ship_img_copy = pygame.transform.rotate(self.ship_img, self.angle)
window.blit(self.ship_img_copy,
(self.x - (self.ship_img_copy.get_width()) / 2, self.y - (self.ship_img_copy.get_height()) / 2))
# collision stuff
self.mask = pygame.mask.from_surface(self.ship_img_copy)
def move(self):
keys = pygame.key.get_pressed()
# todo acceleration and thrust mechanics
if keys[pygame.K_w]:
self.vel = min(self.vel + 1, self.vel_max)
elif self.vel > 0:
self.vel = self.vel - 0.4
if keys[pygame.K_a]:
self.angle += 7
if keys[pygame.K_d]:
self.angle -= 7
self.x += self.vel * cos(self.angle * (pi / 180) + (90 * pi / 180))
self.y -= self.vel * sin(self.angle * (pi / 180) + 90 * (pi / 180))
if self.y < 0:
self.y = (self.y - self.vel) % 600
elif self.y > 600:
self.y = (self.y + self.vel) % 600
elif self.x < 0:
self.x = (self.x - self.vel) % 800
elif self.x > 800:
self.x = (self.x + self.vel) % 800
class Asteroid:
def __init__(self):
self.ang_change = randint(1, 5)
self.ang = randint(0, 90) * (pi / 180)
y_values = [1, 599]
self.sx = randint(0, 800)
self.sy = y_values[randint(0, 1)]
if self.sy == y_values[0]:
self.neg = -1
else:
self.neg = 1
self.speed = randint(5, 10)
self.ang += self.ang_change
self.asteroid_angle = randint(0, 80)
self.asteroid_img = pygame.image.load("sprites/asteroid.png")
self.asteroid_copy = pygame.transform.rotate(self.asteroid_img, self.ang)
def generate(self):
self.ang += self.ang_change
self.asteroid_img = pygame.image.load("sprites/asteroid.png")
self.asteroid_copy = pygame.transform.rotate(self.asteroid_img, self.ang)
window.blit(self.asteroid_copy,
(self.sx - (self.asteroid_copy.get_width()) / 2, self.sy - (self.asteroid_copy.get_height()) / 2))
# collision stuff
self.mask = pygame.mask.from_surface(self.asteroid_copy)
self.rect = self.asteroid_copy.get_rect()
class Projectiles:
def __init__(self, x, y, angle):
self.x = x
self.y = y
self.angle = angle
self.vel = 20
def draw(self):
pygame.draw.rect(window, (255, 0, 0), (self.x, self.y, 5, 5))
def redraw():
window.blit(space_img, (0, 0))
ship.draw()
for asteroid in asteroids:
asteroid.generate()
for bullet in bullets:
bullet.draw()
pygame.display.update()
# collision stuff
def collisions():
collision = pygame.sprite.spritecollide(ship, asteroids, False)
for i in collision:
print("hit")
run = True
ship = Ship(400, 300)
next_fire = pygame.time.get_ticks() + 400
bullets = []
asteroids = []
while run:
clock.tick(60)
keys = pygame.key.get_pressed()
pygame.time.delay(35)
# collision stuff
collisions()
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if keys[pygame.K_SPACE]:
if len(bullets) < 11 and pygame.time.get_ticks() >= next_fire:
bullets.append(
Projectiles(round(ship.x + ship.width - 6.5 // 2), round(ship.y + ship.width - 6.5 // 2), ship.angle))
next_fire = pygame.time.get_ticks() + 400
for bullet in bullets:
if 800 > bullet.x > 0 and 600 > bullet.y > 0:
bullet.x += bullet.vel * cos(bullet.angle * (pi / 180) + 90 * (pi / 180))
bullet.y -= bullet.vel * sin(bullet.angle * (pi / 180) + 90 * (pi / 180))
else:
bullets.pop(bullets.index(bullet))
if len(asteroids) < 5:
asteroids.append(Asteroid())
for asteroid in asteroids:
if 800 > asteroid.sx > 0 and 600 > asteroid.sy > 0:
asteroid.sx += asteroid.speed * cos(asteroid.asteroid_angle * (pi / 180) + 90 * (pi / 180))
asteroid.sy -= asteroid.speed * sin(asteroid.asteroid_angle * (pi / 180) + 90 * (pi / 180)) * asteroid.neg
if asteroid.sx < 0:
asteroid.sx = (asteroid.sx - asteroid.speed) % 800
elif asteroid.sx > 800:
asteroid.sx = (asteroid.sx + asteroid.speed) % 800
else:
asteroids.pop(asteroids.index(asteroid))
window.fill((0, 0, 0))
ship.move()
redraw()
pygame.quit()
The collision detection uses self.rect as the hit box. The self.rect should match where you blit the sprite. You can fix the code with a couple changes:
In the Ship class (draw):
# collision stuff
self.mask = pygame.mask.from_surface(self.ship_img_copy)
self.rect = pygame.Rect(self.x - (self.ship_img_copy.get_width()) / 2, self.y - (self.ship_img_copy.get_height()) / 2,
self.ship_img_copy.get_width(),self.ship_img_copy.get_height())
In the Asteroid class (generate):
# collision stuff
self.mask = pygame.mask.from_surface(self.asteroid_copy)
#self.rect = self.asteroid_copy.get_rect()
self.rect = pygame.Rect(self.sx - (self.asteroid_copy.get_width()) / 2, self.sy - (self.asteroid_copy.get_height()) / 2,
self.asteroid_copy.get_width(),self.asteroid_copy.get_height())

Multiple sprites from a class are moving attached to each other, when they should be separately

I was making a program for asteroids, but when I got to spawning the asteroids, they were all clumped together and floating.
I want each of these asteroids to move separately in their own paths like they're supposed to
This is the code for my Asteroid class
class Asteroid:
def __init__(self):
self.ang_change = randint(1, 5)
self.ang = randint(0, 90) * (pi / 180)
y_values = [1, 599]
self.sx = randint(0, 800)
self.sy = y_values[randint(0, 1)]
# If object spawns from the top, it moves down instead of moving up and de-spawning immediately
if self.sy == y_values[0]:
self.neg = -1
else:
self.neg = 1
self.speed = randint(5, 10)
self.asteroid_angle = randint(0, 80)
def generate(self):
self.ang += self.ang_change
asteroid_img = pygame.image.load("sprites/asteroid.png")
asteroid_copy = pygame.transform.rotate(asteroid_img, self.ang)
window.blit(asteroid_copy,
(asteroid.sx - (asteroid_copy.get_width()) / 2, asteroid.sy - (asteroid_copy.get_height()) / 2))
and this is the whole code if anyone needs
import pygame
from math import sin, cos, pi
from random import randint
scr_width = 800
scr_height = 600
window = pygame.display.set_mode((scr_width, scr_height))
pygame.display.set_caption("Asteroids")
clock = pygame.time.Clock()
space_img = pygame.image.load("sprites/space.jpg")
class Ship:
def __init__(self, x, y):
self.x = x
self.y = y
self.width = 0
self.vel = 0
self.angle = 0
def draw(self):
ship_img = pygame.image.load("sprites/ship_off.png")
ship_img_copy = pygame.transform.rotate(ship_img, self.angle)
window.blit(ship_img_copy,
(self.x - (ship_img_copy.get_width()) / 2, self.y - (ship_img_copy.get_height()) / 2))
keys = pygame.key.get_pressed()
if keys[pygame.K_w]:
ship_img = pygame.image.load("sprites/ship_on.png")
ship_img_copy = pygame.transform.rotate(ship_img, self.angle)
window.blit(ship_img_copy,
(self.x - (ship_img_copy.get_width()) / 2, self.y - (ship_img_copy.get_height()) / 2))
def move(self, vel):
keys = pygame.key.get_pressed()
if keys[pygame.K_w]:
vel += 1
self.x += vel * cos(self.angle * (pi / 180) + (90 * pi / 180))
self.y -= vel * sin(self.angle * (pi / 180) + 90 * (pi / 180))
# So that if it leaves one side it comes from the other
if self.y < 0:
self.y = (self.y - vel) % 600
elif self.y > 600:
self.y = (self.y + vel) % 600
elif self.x < 0:
self.x = (self.x - vel) % 800
elif self.x > 800:
self.x = (self.x + vel) % 800
if keys[pygame.K_a]:
self.angle += 7
if keys[pygame.K_d]:
self.angle -= 7
class Asteroid:
def __init__(self):
self.ang_change = randint(1, 5)
self.ang = randint(0, 90) * (pi / 180)
y_values = [1, 599]
self.sx = randint(0, 800)
self.sy = y_values[randint(0, 1)]
# If object spawns from the top, it moves down instead of moving up and de-spawning immediately
if self.sy == y_values[0]:
self.neg = -1
else:
self.neg = 1
self.speed = randint(5, 10)
self.asteroid_angle = randint(0, 80)
def generate(self):
self.ang += self.ang_change
asteroid_img = pygame.image.load("sprites/asteroid.png")
asteroid_copy = pygame.transform.rotate(asteroid_img, self.ang)
window.blit(asteroid_copy,
(asteroid.sx - (asteroid_copy.get_width()) / 2, asteroid.sy - (asteroid_copy.get_height()) / 2))
class Projectiles:
def __init__(self, x, y, angle):
self.x = x
self.y = y
self.angle = angle
self.vel = 20
def draw(self):
pygame.draw.rect(window, (255, 0, 0), (self.x, self.y, 5, 5))
def redraw():
window.blit(space_img, (0, 0))
ship.draw()
for asteroid in asteroids:
asteroid.generate()
for bullet in bullets:
bullet.draw()
pygame.display.update()
run = True
ship = Ship(375, 225)
bullets = []
asteroids = []
while run:
keys = pygame.key.get_pressed()
pygame.time.delay(35)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if keys[pygame.K_SPACE]:
if len(bullets) < 11:
bullets.append(
Projectiles(round(ship.x + ship.width - 6.5 // 2), round(ship.y + ship.width - 6.5 // 2), ship.angle))
for bullet in bullets:
if 800 > bullet.x > 0 and 600 > bullet.y > 0:
bullet.x += bullet.vel * cos(bullet.angle * (pi / 180) + 90 * (pi / 180))
bullet.y -= bullet.vel * sin(bullet.angle * (pi / 180) + 90 * (pi / 180))
else:
bullets.pop(bullets.index(bullet))
if len(asteroids) < 5:
asteroids.append(Asteroid())
for asteroid in asteroids:
if 800 > asteroid.sx > 0 and 600 > asteroid.sy > 0:
asteroid.sx += asteroid.speed * cos(asteroid.asteroid_angle * (pi / 180) + 90 * (pi / 180))
asteroid.sy -= asteroid.speed * sin(asteroid.asteroid_angle * (pi / 180) + 90 * (pi / 180)) * asteroid.neg
if asteroid.sx < 0:
asteroid.sx = (asteroid.sx - asteroid.speed) % 800
elif asteroid.sx > 800:
asteroid.sx = (asteroid.sx + asteroid.speed) % 800
else:
asteroids.pop(asteroids.index(asteroid))
window.fill((0, 0, 0))
ship.move(0)
redraw()
clock.tick(60)
pygame.quit()
When you're bitting the asteroid copy, you're not using self. All the asteroids are being drawn to the same position.
Current code:
window.blit(asteroid_copy,
(asteroid.sx - (asteroid_copy.get_width()) / 2, asteroid.sy - (asteroid_copy.get_height()) / 2))
Change to:
window.blit(asteroid_copy,
(self.sx - (asteroid_copy.get_width()) / 2, self.sy - (asteroid_copy.get_height()) / 2))

How do i get my character to accelerate and decelerate instead of just moving with a certain velocity (in pygame)?

Can someone tell me what to change so that my ship uses thrust mechanics instead of static movement like it does now?
I want the movement to be like in asteroids, where it accelerates in the direction you're facing and the velocity caps off at some point, then if you stop accelerating, it's velocity slowly decreases until it stops.
rn if I press the forward button it just starts moving with its max velocity directly and stops immediately when I let go.
This is the code for my ship class
class Ship:
def __init__(self, x, y):
self.x = x
self.y = y
self.width = 0
self.vel = 0
self.angle = 0
def draw(self):
ship_img = pygame.image.load("sprites/ship_off.png")
ship_img_copy = pygame.transform.rotate(ship_img, self.angle)
window.blit(ship_img_copy,
(self.x - (ship_img_copy.get_width()) / 2, self.y - (ship_img_copy.get_height()) / 2))
keys = pygame.key.get_pressed()
if keys[pygame.K_w]:
ship_img = pygame.image.load("sprites/ship_on.png")
ship_img_copy = pygame.transform.rotate(ship_img, self.angle)
window.blit(ship_img_copy,
(self.x - (ship_img_copy.get_width()) / 2, self.y - (ship_img_copy.get_height()) / 2))
def move(self):
keys = pygame.key.get_pressed()
# todo acceleration and thrust mechanics
if keys[pygame.K_w]:
self.x += self.vel * cos(self.angle * (pi / 180) + (90 * pi / 180))
self.y -= self.vel * sin(self.angle * (pi / 180) + 90 * (pi / 180))
# So that if it leaves one side it comes from the other
if self.y < 0:
self.y = (self.y - self.vel) % 600
elif self.y > 600:
self.y = (self.y + self.vel) % 600
elif self.x < 0:
self.x = (self.x - self.vel) % 800
elif self.x > 800:
self.x = (self.x + self.vel) % 800
if keys[pygame.K_a]:
self.angle += 7
if keys[pygame.K_d]:
self.angle -= 7
I've tried but I couldn't do it, so here's my code
You have to change self.vel when w or s is pressed, but you have to change self.x and self.y in every frame, dependent on self.vel:
class Ship:
def __init__(self, x, y):
# [...]
self.vel = 0
self.max_vel = 10
# [...]
def move(self):
keys = pygame.key.get_pressed()
if keys[pygame.K_w]:
self.vel = min(self.vel+1, self.max_vel)
if keys[pygame.K_s]:
self.vel = max(self.vel-1, 0)
if keys[pygame.K_a]:
self.angle += 7
if keys[pygame.K_d]:
self.angle -= 7
self.x += self.vel * cos(self.angle * (pi / 180) + (90 * pi / 180))
self.y -= self.vel * sin(self.angle * (pi / 180) + 90 * (pi / 180))
if self.y < 0:
self.y = (self.y - self.vel) % 600
elif self.y > 600:
self.y = (self.y + self.vel) % 600
elif self.x < 0:
self.x = (self.x - self.vel) % 800
elif self.x > 800:
self.x = (self.x + self.vel) % 800

Bouncing pygame mask object on walls

I am rewriting a simple game. Before I just used e.g. pygame.draw.circle() to create and interact with objects.
However, there was a need to use masks for more complex tasks (like collision with polygons).
This is how I let the ball bounce on walls before:
def bounce(self):
# Right boundary
if self.x > width - self.radius:
self.x = 2 * (width - self.radius) - self.x
self.angle = - self.angle
self.velocity = Vector2(self.velocity) * elasticity
# Left boundary
elif self.x < self.radius:
self.x = 2 * self.radius - self.x
self.angle = - self.angle
self.velocity = Vector2(self.velocity) * elasticity
# Top boundary
if self.y > height - self.radius:
self.y = 2 * (height - self.radius) - self.y
self.angle = math.pi - self.angle
self.velocity = Vector2(self.velocity) * elasticity
# Bottom boundary
elif self.y < self.radius:
self.y = 2 * self.radius - self.y
self.angle = math.pi - self.angle
self.velocity = Vector2(self.velocity) * elasticity
But now with masks implemented, it only stops when it hits the wall but doesn't bounce off anymore.
Full code:
import pygame
from pygame.math import Vector2
import math
width = 1150
height = 800
# Colors
GOLD = (255, 215, 0)
drag = 0.999 # Between 0 and 1
elasticity = 0.75 # Between 0 and 1
pygame.init()
screen = pygame.display.set_mode((width, height))
clock = pygame.time.Clock()
# Define game object class
class Circle:
def __init__(self, coordinates, velocity, angle, radius, objectColor):
self.x = coordinates[0]
self.y = coordinates[1]
self.velocity = velocity
self.angle = angle
self.radius = radius
self.objectColor = objectColor
self.initSurface = pygame.Surface((self.radius*2, self.radius*2), pygame.SRCALPHA)
self.rectangle = self.initSurface.get_rect(center=Vector2(self.x, self.y))
self.surface = self.initSurface
self.mask = None
self.draw()
def draw(self):
pygame.draw.circle(self.initSurface, self.objectColor, [self.radius, self.radius], self.radius)
self.mask = pygame.mask.from_surface(self.initSurface)
def move(self):
self.x += self.velocity[0]
self.y += self.velocity[1]
self.velocity = Vector2(self.velocity) * drag
self.rectangle.center = Vector2(self.x, self.y)
def bounce(self):
# Right boundary
if self.x > width - self.radius:
self.x = 2 * (width - self.radius) - self.x
self.angle = - self.angle
self.velocity = Vector2(self.velocity) * elasticity
# Left boundary
elif self.x < self.radius:
self.x = 2 * self.radius - self.x
self.angle = - self.angle
self.velocity = Vector2(self.velocity) * elasticity
# Top boundary
if self.y > height - self.radius:
self.y = 2 * (height - self.radius) - self.y
self.angle = math.pi - self.angle
self.velocity = Vector2(self.velocity) * elasticity
# Bottom boundary
elif self.y < self.radius:
self.y = 2 * self.radius - self.y
self.angle = math.pi - self.angle
self.velocity = Vector2(self.velocity) * elasticity
class Polygon:
def __init__(self, coordinates, velocity, angle, pointList, objectColor):
self.x = coordinates[0]
self.y = coordinates[1]
self.velocity = velocity
self.angle = angle
self.pointList = pointList
self.objectColor = objectColor
self.initSurface = pygame.Surface((max(self.pointList, key=lambda item: item[0])[0],
max(self.pointList, key=lambda item: item[1])[1]), pygame.SRCALPHA)
self.rectangle = self.initSurface.get_rect(center=Vector2(self.x, self.y))
self.surface = self.initSurface
self.mask = None
self.draw()
def draw(self):
pygame.draw.polygon(self.initSurface, self.objectColor, self.pointList)
self.mask = pygame.mask.from_surface(self.initSurface)
def move(self):
self.x += self.velocity[0]
self.y += self.velocity[1]
self.rectangle.center = Vector2(self.x, self.y)
def rotate(self, angle):
self.angle += angle
self.velocity.rotate_ip(-angle)
surface = pygame.transform.rotate(self.initSurface, self.angle)
self.rectangle = surface.get_rect(center=self.rectangle.center)
# We need a new mask after the rotation.
self.mask = pygame.mask.from_surface(surface)
self.surface = surface
# Colliding game object particles
def collide(p1, p2):
offset = p1.rectangle[0] - p2.rectangle[0], p1.rectangle[1] - p2.rectangle[1]
overlap = myBall.mask.overlap(p1.mask, offset)
if overlap:
p2.velocity = Vector2(p1.velocity) * 1.4
# Images
BG_IMG = pygame.Surface((1150, 800))
BG_IMG.fill((30, 120, 30))
# Init Ball and car (input)
myBall = Circle(Vector2(575, 400), Vector2(0, 0), 0, 60, GOLD)
myInput = Polygon(Vector2(470, 370), Vector2(3, 0), 0, [(0, 0), (50, 10), (50, 20), (0, 30)], GOLD)
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
myInput.rotate(5)
elif keys[pygame.K_RIGHT]:
myInput.rotate(-5)
# Move the car
myInput.move()
# Move the ball
myBall.velocity *= .99 # Friction
myBall.move()
myBall.bounce()
# Car collision.
collide(myInput, myBall)
# Drawing
screen.blit(BG_IMG, (0, 0))
screen.blit(myBall.surface, myBall.rectangle)
screen.blit(myInput.surface, myInput.rectangle)
pygame.display.flip()
clock.tick(60)
pygame.quit()
What am I missing here? I can't see through anymore.
In the bounce method you make the the velocity smaller, you never flip it so, just add a minus sign and it should bounce back instead of moving slower and slower into the boundry
self.velocity = Vector2(-self.velocity) * elasticity

Categories

Resources