i've been looking into game development in python and i'm very new to it, i tryed to copy a space invaders and here is what i came up with, it is massively inspired from tutorials and code i found online.
What im trying to do know is to get the background moving so i thought it would be a good idea to create 2 sprites with 2 background image and make them move one after the other. As soon as one is out of the screen it should reappear at the bottom and so on. But it does not work i am not sure what is going wrong. Here is what i have to far:
#!/usr/bin/env python
from helpers import *
class Space1(pygame.sprite.Sprite):
def __init__(self, i):
pygame.sprite.Sprite.__init__(self)
self.image, self.rect = load_image("space.png", 10)
self.dx = -5
self.reset(i)
def update(self, i):
self.rect.top += self.dx
if i == 1:
if self.rect.top <= -600:
self.__init__(i)
else:
if self.rect.top <= -1200:
self.__init__(i)
def reset(self, i):
if i == 1:
self.rect.top = 1
else:
self.rect.top = 300
class Space2(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image, self.rect = load_image("space.png", 10)
self.dx = -5
self.reset()
def update(self):
self.rect.top += self.dx
if self.rect.top <= -1200:
self.__init__()
def reset(self):
self.rect.top = 600
class Player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image, self.rect = load_image('ship.gif', -1)
self.x_dist = 5
self.y_dist = 5
self.lasertimer = 0
self.lasermax = 5
self.rect.centery = 400
self.rect.centerx = 400
def update(self):
key = pygame.key.get_pressed()
# Movement
if key[K_UP]:
self.rect.centery += -3
if key[K_DOWN]:
self.rect.centery += 3
if key[K_RIGHT]:
self.rect.centerx += 3
if key[K_LEFT]:
self.rect.centerx += -3
# Lasers
if key[K_SPACE]:
self.lasertimer = self.lasertimer + 1
if self.lasertimer == self.lasermax:
laserSprites.add(Laser(self.rect.midtop))
self.lasertimer = 0
# Restrictions
self.rect.bottom = min(self.rect.bottom, 600)
self.rect.top = max(self.rect.top, 0)
self.rect.right = min(self.rect.right, 800)
self.rect.left = max(self.rect.left, 0)
class Laser(pygame.sprite.Sprite):
def __init__(self, pos):
pygame.sprite.Sprite.__init__(self)
self.image, self.rect = load_image("laser.png", -1)
self.rect.center = pos
def update(self):
if self.rect.right > 800:
self.kill()
else:
self.rect.move_ip(0, -15)
class Enemy(pygame.sprite.Sprite):
def __init__(self, centerx):
pygame.sprite.Sprite.__init__(self)
self.image, self.rect = load_image("alien.png", -1)
self.rect = self.image.get_rect()
self.dy = 8
self.reset()
def update(self):
self.rect.centerx += self.dx
self.rect.centery += self.dy
if self.rect.top > 600:
self.reset()
# Laser Collisions
if pygame.sprite.groupcollide(enemySprites, laserSprites, 1, 1):
explosionSprites.add(EnemyExplosion(self.rect.center))
# Ship Collisions
if pygame.sprite.groupcollide(enemySprites, playerSprite, 1, 1):
explosionSprites.add(EnemyExplosion(self.rect.center))
explosionSprites.add(PlayerExplosion(self.rect.center))
def reset(self):
self.rect.bottom = 0
self.rect.centerx = random.randrange(0, 600)
self.dy = random.randrange(5, 10)
self.dx = random.randrange(-2, 2)
class EnemyExplosion(pygame.sprite.Sprite):
def __init__(self, pos):
pygame.sprite.Sprite.__init__(self)
self.image, self.rect = load_image("enemyexplosion.png", -1)
self.rect.center = pos
self.counter = 0
self.maxcount = 10
def update(self):
self.counter = self.counter + 1
if self.counter == self.maxcount:
self.kill()
class PlayerExplosion(pygame.sprite.Sprite):
def __init__(self, pos):
pygame.sprite.Sprite.__init__(self)
self.image, self.rect = load_image("enemyexplosion.png", -1)
self.rect.center = pos
self.counter = 0
self.maxcount = 10
def update(self):
self.counter = self.counter + 1
if self.counter == self.maxcount:
self.kill()
exit()
def main():
# Initialize Everything
pygame.init()
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption('UoN Invaders')
# Create The Backgound
background = pygame.Surface(screen.get_size())
background = background.convert()
background.fill((000, 000, 000))
# Display The Background
screen.blit(background, (0, 0))
pygame.display.flip()
# Start Music
music = pygame.mixer.music.load ("data/spacequest.mp3")
pygame.mixer.music.play(-1)
# Initialize Game Objects
global clock
clock = pygame.time.Clock()
i = 0
i += 1
space1 = Space1(i)
space2 = Space2()
global player
player = Player()
# Render Objects
# Space
space1 = pygame.sprite.RenderPlain((space1))
space2 = pygame.sprite.RenderPlain((space2))
# Player
global playerSprite
playerSprite = pygame.sprite.RenderPlain((player))
# Enemy
global enemySprites
enemySprites = pygame.sprite.RenderPlain(())
enemySprites.add(Enemy(200))
enemySprites.add(Enemy(300))
enemySprites.add(Enemy(400))
# Projectiles
global laserSprites
laserSprites = pygame.sprite.RenderPlain(())
# Collisions
global enemyExplosion
enemyExplosion = pygame.sprite.RenderPlain(())
global playerExplosion
playerExplosion = pygame.sprite.RenderPlain(())
global explosionSprites
explosionSprites = pygame.sprite.RenderPlain(())
# Main Loop
going = True
while going:
clock.tick(60)
# Input Events
for event in pygame.event.get():
if event.type == QUIT:
going = False
elif event.type == KEYDOWN and event.key == K_ESCAPE:
going = False
# Update
space1.update(i)
space2.update()
player.update()
enemySprites.update()
laserSprites.update()
enemyExplosion.update()
playerExplosion.update()
explosionSprites.update()
screen.blit(background, (0, 0))
# Draw
space1.draw(screen)
space2.draw(screen)
playerSprite.draw(screen)
enemySprites.draw(screen)
laserSprites.draw(screen)
enemyExplosion.draw(screen)
playerExplosion.draw(screen)
explosionSprites.draw(screen)
pygame.display.flip()
pygame.quit()
if __name__ == '__main__':
main()
class Space(pygame.sprite.Sprite):
def __init__(self, num):
pygame.sprite.Sprite.__init__(self)
self.rect.top = num * 600
self.image, self.rect = load_image("space.png", 10)
self.dx = -5
self.reset()
def update(self):
self.rect.top += self.dx
if self.rect.top <= -1200:
self.reset()
def reset(self):
self.rect.top = 600
So this is a class to store your sprite in. I don't believe you need 2, because you can have a list of all of your instances of space for example in main, instead of having...
space1 = Space1(i)
space2 = Space2()
I would recommend doing something like.
spaces = []
for x in range(2):
spaces.append(Space(x))
and then change
space1 = pygame.sprite.RenderPlain((space1))
space2 = pygame.sprite.RenderPlain((space2))
to
for space in spaces:
space = pygame.sprite.RenderPlain((space))
then instead of
space1.update(i)
space2.update()
do
for space in spaces:
space.update()
finally instead of
space1.draw(screen)
space2.draw(screen)
do
for space in spaces:
space.draw(screen)
I hope that this works for you, as I see this as a reasonable way to store your instances of space and allows you to more easily have the code with less code. If you have any problems implementing this, please just leave a comment and I will help you fix it.
Cheers!
well i didnt not use your method and i tried this it seems to work fine, however there is a samll gap between the 2 instances of Space, i'm trying to fix it know. Here is the modified code:
#!/usr/bin/env python
from helpers import *
print("hello world")
class Space1(pygame.sprite.Sprite):
def __init__(self, i):
pygame.sprite.Sprite.__init__(self)
self.image, self.rect = load_image("space.png", 10)
self.dxy = -5
self.reset(i)
def update(self, i):
self.rect.top += self.dxy
if self.rect.top <= -600:
self.__init__(i)
def reset(self, i):
if i == 0:
self.rect.top = 1
else:
self.rect.top = 600
class Space2(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image, self.rect = load_image("space.png", 10)
self.dx = -5
self.reset()
def update(self):
self.rect.top += self.dx
if self.rect.top <= -600:
self.__init__()
def reset(self):
self.rect.top = 600
class Player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image, self.rect = load_image('ship.png', -1)
self.x_dist = 5
self.y_dist = 5
self.lasertimer = 0
self.lasermax = 5
self.rect.centery = 400
self.rect.centerx = 400
def update(self):
key = pygame.key.get_pressed()
# Movement
if key[K_UP]:
self.rect.centery += -3
if key[K_DOWN]:
self.rect.centery += 3
if key[K_RIGHT]:
self.rect.centerx += 3
if key[K_LEFT]:
self.rect.centerx += -3
# Lasers
if key[K_SPACE]:
self.lasertimer = self.lasertimer + 1
if self.lasertimer == self.lasermax:
laserSprites.add(Laser(self.rect.midtop))
self.lasertimer = 0
# Restrictions
self.rect.bottom = min(self.rect.bottom, 600)
self.rect.top = max(self.rect.top, 0)
self.rect.right = min(self.rect.right, 800)
self.rect.left = max(self.rect.left, 0)
class Laser(pygame.sprite.Sprite):
def __init__(self, pos):
pygame.sprite.Sprite.__init__(self)
self.image, self.rect = load_image("laser.png", -1)
self.rect.center = pos
def update(self):
if self.rect.right > 800:
self.kill()
else:
self.rect.move_ip(0, -15)
class Enemy(pygame.sprite.Sprite):
def __init__(self, centerx):
pygame.sprite.Sprite.__init__(self)
self.image, self.rect = load_image("alien.png", -1)
self.rect = self.image.get_rect()
self.dy = 8
self.reset()
def update(self):
self.rect.centerx += self.dx
self.rect.centery += self.dy
if self.rect.top > 600:
self.reset()
# Laser Collisions
if pygame.sprite.groupcollide(enemySprites, laserSprites, 1, 1):
explosionSprites.add(EnemyExplosion(self.rect.center))
# Ship Collisions
if pygame.sprite.groupcollide(enemySprites, playerSprite, 1, 1):
explosionSprites.add(EnemyExplosion(self.rect.center))
explosionSprites.add(PlayerExplosion(self.rect.center))
def reset(self):
self.rect.bottom = 0
self.rect.centerx = random.randrange(0, 600)
self.dy = random.randrange(5, 10)
self.dx = random.randrange(-2, 2)
class EnemyExplosion(pygame.sprite.Sprite):
def __init__(self, pos):
pygame.sprite.Sprite.__init__(self)
self.image, self.rect = load_image("enemyexplosion.png", -1)
self.rect.center = pos
self.counter = 0
self.maxcount = 10
def update(self):
self.counter = self.counter + 1
if self.counter == self.maxcount:
self.kill()
class PlayerExplosion(pygame.sprite.Sprite):
def __init__(self, pos):
pygame.sprite.Sprite.__init__(self)
self.image, self.rect = load_image("enemyexplosion.png", -1)
self.rect.center = pos
self.counter = 0
self.maxcount = 10
def update(self):
self.counter = self.counter + 1
if self.counter == self.maxcount:
self.kill()
exit()
def main():
pygame.init()
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption('UoN Invaders')
background = pygame.Surface(screen.get_size())
background = background.convert()
background.fill((000, 000, 000))
screen.blit(background, (0, 0))
pygame.display.flip()
music = pygame.mixer.music.load ("data/spacequest.mp3")
pygame.mixer.music.play(-1)
global clock
clock = pygame.time.Clock()
i = 0
space1 = Space1(i)
space2 = Space2()
global player
player = Player()
space1 = pygame.sprite.RenderPlain((space1))
space2 = pygame.sprite.RenderPlain((space2))
global playerSprite
playerSprite = pygame.sprite.RenderPlain((player))
global enemySprites
enemySprites = pygame.sprite.RenderPlain(())
enemySprites.add(Enemy(200))
enemySprites.add(Enemy(300))
enemySprites.add(Enemy(400))
global laserSprites
laserSprites = pygame.sprite.RenderPlain(())
global enemyExplosion
enemyExplosion = pygame.sprite.RenderPlain(())
global playerExplosion
playerExplosion = pygame.sprite.RenderPlain(())
global explosionSprites
explosionSprites = pygame.sprite.RenderPlain(())
# Main Loop
going = True
while going:
clock.tick(60)
for event in pygame.event.get():
if event.type == QUIT:
going = False
elif event.type == KEYDOWN and event.key == K_ESCAPE:
going = False
# Update
i += 1
space1.update(i)
space2.update()
player.update()
enemySprites.update()
laserSprites.update()
enemyExplosion.update()
playerExplosion.update()
explosionSprites.update()
screen.blit(background, (0, 0))
# Draw
space1.draw(screen)
space2.draw(screen)
playerSprite.draw(screen)
enemySprites.draw(screen)
laserSprites.draw(screen)
enemyExplosion.draw(screen)
playerExplosion.draw(screen)
explosionSprites.draw(screen)
pygame.display.flip()
while going == False:
gameover()
def gameover():
print("hello world")
if __name__ == '__main__':
main()
Related
I have been trying to create a game form scratch in Python using Pygame. I followed a tutorial for the most part and have hit a wall. The game is a simple platformer and I'm having trouble adding in collisions. I have looked at many other examples but cant seem to find where abouts to add in the pygame.sprite.collide_rect command and how to get it to stop the players y velocity so that he stops ontop of a platform. Any help would be greatly appreciated! Thanks.
The code
import random
import pygame as pg
pg.init()
icon = pg.image.load("soldier.png")
pg.display.set_icon(icon)
backgroundimage = pg.image.load("background.png")
FPS = 50
Width = 512
Height = 384
Red = (255, 0, 0)
#vec = pg.math.Vector2
class platform(pg.sprite.Sprite):
def __init__(self, x, y):
pg.sprite.Sprite.__init__(self)
self.image = pg.image.load("plat.png")
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
class Player(pg.sprite.Sprite):
def __init__(self):
pg.sprite.Sprite.__init__(self)
self.image = pg.image.load("Spaceman.png")
self.rect = self.image.get_rect()
self.rect.center = (Width / 2, Height / 2)
self.vx = 0
self.vy = 0
#self.pos = vec(Width / 2, Height / 2)
self.acc = 0.2
def update(self):
self.vy = self.vy + self.acc
self.vx = 0
#self.acc = vec(0, 0.5)
keys = pg.key.get_pressed()
if keys[pg.K_SPACE]:
while self.vy > 0:
self.vy = -5 + self.acc
if keys[pg.K_LEFT]:
self.vx = -5
self.image = pg.image.load("Spaceman2.png")
if keys[pg.K_RIGHT]:
self.vx = 5
self.image = pg.image.load("Spaceman.png")
self.rect.x += self.vx
self.rect.y += self.vy
if self.rect.left > Width:
self.rect.right = 0
if self.rect.right < 0:
self.rect.left = Width
class Game:
def __init__(self):
pg.init()
pg.mixer.init()
self.screen = pg.display.set_mode((Width,Height))
pg.display.set_caption("OH NOOOO")
self.clock = pg.time.Clock()
self.running = True
def new(self):
self.all_sprites = pg.sprite.Group()
self.platforms = pg.sprite.Group()
self.soldier = pg.sprite.Group()
self.player = Player()
self.all_sprites.add(self.player)
self.soldier.add(self.player)
p1 = platform(200, 200)
self.all_sprites.add(p1)
self.platforms.add(p1)
self.run()
def run(self):
self.playing = True
while self.playing:
self.clock.tick(FPS)
self.events()
self.update()
self.draw()
def update(self):
self.screen.blit(backgroundimage,[0,0])
self.all_sprites.update()
def events(self):
for event in pg.event.get():
if event.type == pg.QUIT:
if self.playing:
self.playing = False
self.running = False
def draw(self):
self.all_sprites.draw(self.screen)
pg.display.flip()
def show_start_screen(self):
pass
def show_go_screen(self):
pass
g = Game()
g.show_start_screen()
while g.running:
g.new()
g.show_go_screen()
pg.quit()
This is all of the code so far and I've made three groups within this code; all_sprites, soldier and platforms but cant figure out how to make soldier and platforms groups collide. Any help is greatly appreciated thanks!
Use pygame.sprite.groupcollide() to detect the collision of pygame.sprite.Sprite objects in 2 separate pygame.sprite.Groups:
if pygame.sprite.groupcollide(self.soldier, self.platforms, False, Fasle):
print("hit")
For some reason, all my enemies are slowing down after they collide with the bullet. Why is this occurring? I have attached a GIF below. This game is incomplete and I am fairly new to pygame. Give any advice as needed. I made a previous post with a different, please reply to that as well if possible.
Attached GIF
import pygame, sys, random
from pygame.locals import *
pygame.init()
screen_height = 870
screen_width = 1530
screen = pygame.display.set_mode((screen_width, screen_height))
fpsfunc = pygame.time.Clock()
enemy_speed = 6
enemy_motion = 1
SPAWNENEMY = USEREVENT + 1
SPAWNEASY = USEREVENT + 2
SPAWNMED = USEREVENT + 3
SPAWNHARD = USEREVENT + 4
pygame.time.set_timer(SPAWNENEMY, 1000)
cenx = screen_width / 2
ceny = screen_height / 2
gen1 = [cenx-200,ceny-200,cenx-100,ceny-100,cenx,ceny,cenx+100,ceny+100,cenx+200,ceny+200]
gen2 = []
gen3 = []
choices = [gen1]
class Player(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.surf = pygame.Surface((50, 50))
self.surf.fill((255, 0, 0))
self.rect = self.surf.get_rect(center=(screen_width / 2,
screen_height - 100))
def update(self, pressed_keys):
if pressed_keys[K_d]:
self.rect.move_ip(3, 0)
if pressed_keys[K_a]:
self.rect.move_ip(-3, 0)
if pressed_keys[K_w]:
self.rect.move_ip(0, -5)
if pressed_keys[K_s]:
self.rect.move_ip(0, 2)
if self.rect.left <= 0:
self.rect.left = 0
if self.rect.top <= 0:
self.rect.top = 0
if pressed_keys[K_SPACE]:
bullet_sprites.add(player.create_bullet())
def create_bullet(self):
return PlayerBullet(self.rect.x + 25, self.rect.y)
class PlayerBullet(pygame.sprite.Sprite):
def __init__(self, pos_x, pos_y):
super().__init__()
self.image = pygame.Surface((10, 30))
self.image.fill((0, 255, 255))
self.rect = self.image.get_rect(center=(pos_x, pos_y))
def update(self):
self.rect.y -= 5
if self.rect.y + 30 < 0:
self.kill()
class Enemy(pygame.sprite.Sprite):
def __init__(self, cenx, ceny):
super(Enemy, self).__init__()
self.image = pygame.Surface((35,35))
self.image.fill((0,0,255))
self.rect = self.image.get_rect(center = (cenx, ceny))
def update(self):
global enemy_motion, enemy_speed, cycle
if pygame.sprite.spritecollide(self, bullet_sprites, True):
self.kill()
for enemy in enemy_sprites:
if enemy not in enemy_dir_data:
enemy_dir_data[enemy] = 1
if enemy.rect.right >= screen_width:
enemy_dir_data[enemy] = -1
if enemy.rect.left <= 0:
enemy_dir_data[enemy] = 1
enemy.rect.x += enemy_speed * enemy_dir_data[enemy]
def createnem(pos1, pos2):
return Enemy(pos1, pos2)
player = Player()
bullet_sprites = pygame.sprite.Group()
enemy_sprites = pygame.sprite.Group()
enemy_dir_data = {}
start_ticks=pygame.time.get_ticks()
while True:
seconds=(pygame.time.get_ticks()-start_ticks)/1000
for event in pygame.event.get():
if event.type == KEYDOWN:
if event.key == K_ESCAPE:
pygame.quit()
sys.exit()
if event.type == MOUSEBUTTONDOWN:
bullet_sprites.add(player.create_bullet())
#print(seconds)
if seconds >= 10:
enemy_sprites.empty()
if len(enemy_sprites) == 0:
selected = random.choice(choices)
for create in range(0,len(selected),2):
x = create
y = create + 1
print(selected[x],selected[y])
enemy_sprites.add(createnem(gen1[x], gen1[y]))
enemy_dir_data = {}
start_ticks=pygame.time.get_ticks()
pressed_keys = pygame.key.get_pressed()
screen.fill((0, 0, 0))
screen.blit(player.surf, player.rect)
bullet_sprites.draw(screen)
enemy_sprites.draw(screen)
player.update(pressed_keys)
enemy_sprites.update()
bullet_sprites.update()
pygame.display.update()
fpsfunc.tick(60)
The speed of your enemies depends on the number of enemies because of the for loop in Enemy.update. update is called once for each enemy in the Group. Because of the for loop each enemy is moved multiple times per frame.
Read about Method Objects and move just the "self" instance object in update:
class Enemy(pygame.sprite.Sprite):
# [...]
def update(self):
global enemy_motion, enemy_speed, cycle
if pygame.sprite.spritecollide(self, bullet_sprites, True):
self.kill()
if self not in enemy_dir_data:
enemy_dir_data[self] = 1
if self.rect.right >= screen_width:
enemy_dir_data[self] = -1
if self.rect.left <= 0:
enemy_dir_data[self] = 1
self.rect.x += enemy_speed * enemy_dir_data[self]
pygame.sprite.Group.draw() and pygame.sprite.Group.update() are methods which are provided by pygame.sprite.Group.
The former delegates the to the update mehtod of the contained pygame.sprite.Sprites - you have to implement the method. See pygame.sprite.Group.update():
Calls the update() method on all Sprites in the Group [...]
The later uses the image and rect attributes of the contained pygame.sprite.Sprites to draw the objects - you have to ensure that the pygame.sprite.Sprites have the required attributes. See pygame.sprite.Group.draw():
Draws the contained Sprites to the Surface argument. This uses the Sprite.image attribute for the source surface, and Sprite.rect. [...]
You don't need the dictionary enemy_dir_data at all. Instead add an attribute enemy_dir to the Enemy class:
class Enemy(pygame.sprite.Sprite):
def __init__(self, cenx, ceny):
super(Enemy, self).__init__()
self.image = pygame.Surface((35,35))
self.image.fill((0,0,255))
self.rect = self.image.get_rect(center = (cenx, ceny))
self.enemy_dir = 1
def update(self):
global enemy_motion, enemy_speed, cycle
if pygame.sprite.spritecollide(self, bullet_sprites, True):
self.kill()
if self.rect.right >= screen_width:
self.enemy_dir = -1
if self.rect.left <= 0:
self.enemy_dir = 1
self.rect.x += enemy_speed * self.enemy_dir
Try:
pygame.sprite.Sprite.kill(self)
Instead of self.kill
After about of hour of disaster and modification on my code, I can't seem to get my enemy sprite to move back and forth on a platform. So any help would be greatly appreciated :D
Test File, Crafted Super Fast....
import pygame, sys, os
GAME_TITLE = "GAME"
WINDOW_WIDTH, WINDOW_HEIGHT = 1280, 720
BLACK = (0, 0, 0)
BLUE = (0, 255, 0)
RED = (255, 0, 0)
FPS = 60
ENEMY_ACC = 0.3
ENEMY_FRICTION = -0.12
ENEMY_GRAVITY = 0.5
vec = pygame.math.Vector2
class Platform(pygame.sprite.Sprite):
def __init__(self, game, x, y, w, h):
pygame.sprite.Sprite.__init__(self)
self.game = game
self.group = self.game.platform_list
self.image = pygame.Surface((1280, 100))
self.rect = self.image.get_rect()
pygame.draw.rect(self.image, RED, (x, y, w, h))
class Enemy(pygame.sprite.Sprite):
def __init__(self, game, pos):
pygame.sprite.Sprite.__init__(self)
self.game = game
self.group = self.game.enemy_list
self.image = pygame.Surface((100, 100))
pygame.draw.rect(self.image, BLUE, (200, 200, 100, 100))
self.rect = self.image.get_rect()
self.pos = vec(pos)
self.rect.center = self.pos
self.vel = vec(0, 0)
self.acc = vec(0, 0)
self.direction = "R"
self.engage = False
def update(self):
hits = pygame.sprite.spritecollide(self, self.game.platform_list, False)
if hits:
plat_right = hits[0].rect.right
plat_left = hits[0].rect.left
self.pos.y = hits[0].rect.top
self.vel.y = 0
if self.direction == "R" and not self.engage:
if self.rect.right >= plat_right:
self.direction = "L"
self.acc.x = -ENEMY_ACC
if self.direction == "L" and not self.engage:
if self.rect.left <= plat_left:
self.direction = "R"
self.acc.x = -ENEMY_ACC
self.acc.x += self.vel.x * ENEMY_FRICTION
self.vel += self.acc
self.pos += self.vel + 0.5 * self.acc
self.rect.midbottom = self.pos
self.acc = vec(0, ENEMY_GRAVITY)
class Game:
def __init__(self):
pygame.init()
pygame.mixer.init()
pygame.font.init()
os.environ['SDL_VIDEO_CENTERED'] = '1'
pygame.display.set_caption(GAME_TITLE)
self.window = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT))
self.clock = pygame.time.Clock()
self.platform_list = pygame.sprite.Group()
self.enemy_list = pygame.sprite.Group()
def run(self):
enemy = Enemy(self, (200, 500))
self.enemy_list.add(enemy)
platform = Platform(self, 0, 620, 1280, 100)
self.platform_list.add(platform)
while True:
self.events()
self.update()
self.draw()
def events(self):
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
def update(self):
self.enemy_list.update()
def draw(self):
self.window.fill(BLACK)
self.platform_list.draw(self.window)
self.enemy_list.draw(self.window)
pygame.display.flip()
def main():
g = Game()
g.run()
if __name__ == "__main__":
main()
Here's an example without the acc attribute. Just accelerate the self.vel by adding the ENEMY_GRAVITY, update the self.pos and self.rect, then if it collides with the platform see if the right or left edge is reached and then just invert the velocity.
I've set the ENEMY_GRAVITY to 3 and call clock.tick(30) to limit the frame rate. The direction attribute doesn't seem to be necessary anymore.
class Enemy(pygame.sprite.Sprite):
def __init__(self, game, pos):
pygame.sprite.Sprite.__init__(self)
self.game = game
self.group = self.game.enemy_list
self.image = pygame.Surface((60, 60))
self.image.fill((30, 90, 200))
self.rect = self.image.get_rect(midbottom=pos)
self.pos = vec(pos)
self.vel = vec(3, 0)
self.engage = False
def update(self):
self.vel.y += ENEMY_GRAVITY
self.pos += self.vel
self.rect.midbottom = self.pos
hits = pygame.sprite.spritecollide(self, self.game.platform_list, False)
for plat in hits:
self.pos.y = plat.rect.top
self.rect.bottom = plat.rect.top
self.vel.y = 0
if self.vel.x > 0 and not self.engage:
if self.rect.right >= plat.rect.right:
self.vel = vec(-3, 0) # Reverse the horizontal velocity.
elif self.vel.x < 0 and not self.engage:
if self.rect.left <= plat.rect.left:
self.vel = vec(3, 0) # Reverse the horizontal velocity.
This question already has answers here:
Created multiple instances of the same image using a loop, can I move each instance of the image independently?
(1 answer)
What is the difference between pygame sprite and surface?
(1 answer)
Closed 1 year ago.
I am a Pygame newbie and am learning as I go. I would like to become familiar with normal Pygame conventions. I would like you Pygame experts go over my code and let me know if there are any places I should modify my code to follow best practice.
The point of the game is to collect as many pills as possible. Yellows are worth 10, reds 20, blue 30 and black 40. First one to reach 15,000 wins. The ships are controlled using ‘wasd’ and ‘updownleftright’. The complete code is posted below.
Some areas of concern I am looking at are the following:
Where I created two separate classes to store the score information. I feel that I could do the same job with one class, but am a bit confused on how that would look since I am using a TextGroup and it needs to be passed both Ship objects on the call to TextGroup.update().
class Text(Entity):
def __init__(self, text, size, color, position, font=None):
Entity.__init__(self)
self.color = color
self.position = position
self.font = pygame.font.Font(font, size)
self.text = text
self.image = self.font.render(str(text), 1, self.color)
self.rect = self.image.get_rect()
self.rect.move_ip(position[0]-self.rect.width/2, position[1])
class Mass_Left(Text):
def __init__(self, text, size, color, position, font=None):
Text.__init__(self, text, size, color, position, font=None)
def update(self, ship_left, ship_right):
self.text = "mass: " + str(ship_left.density-169)
self.image = self.font.render(str(self.text), 1, self.color)
self.rect = self.image.get_rect()
self.rect.move_ip(self.position[0]-self.rect.width/2, self.position[1])
class Mass_Right(Text):
def __init__(self, text, size, color, position, font=None):
Text.__init__(self, text, size, color, position, font=None)
def update(self, ship_left, ship_right):
self.text = "mass: " + str(ship_right.density-169)
self.image = self.font.render(str(self.text), 1, self.color)
self.rect = self.image.get_rect()
self.rect.move_ip(self.position[0]-self.rect.width/2, self.position[1])
Also, here in the method ‘moveShip()’ where I am checking if self.player is ‘left’ or ‘right’. I feel that there should be a way to do this by passing the class a function when the Ship object is created that will take appropriate action and different action depending on whether it’s the right or left ship.
def moveShip(self):
key = pygame.key.get_pressed()
if self.player == 'left' and (key[pygame.K_w] or key[pygame.K_s] or key[pygame.K_a] or key[pygame.K_d]):
if key[pygame.K_w]:
self.rect.y -= self.speed
if key[pygame.K_s]:
self.rect.y += self.speed
if key[pygame.K_a]:
self.rect.x -= self.speed
if key[pygame.K_d]:
self.rect.x += self.speed
# Adjust Player 2 Speed
if self.player == 'right' and (key[pygame.K_UP] or key[pygame.K_DOWN] or key[pygame.K_LEFT] or key[pygame.K_RIGHT]):
if key[pygame.K_UP]:
self.rect.y -= self.speed
if key[pygame.K_DOWN]:
self.rect.y += self.speed
if key[pygame.K_LEFT]:
self.rect.x -= self.speed
if key[pygame.K_RIGHT]:
self.rect.x += self.speed
Same issue here with the method ‘moveInbounds()’
Same issue in the method ‘winGame()’.
The function ‘genRandom’ generates a tuple that contains a random x value for the Pills and a random density value between 1-4. I am using string concatenation, then doing a type conversion, but I’m sure there’s a more straight forward way to generate a random tuple.
def genRandom(size):
xval_density = []
for j in range(size):
length = str(random.randrange(0, (WIN_W/2) - PILL_WIDTH))
stup = '('
stup = stup + str(length)
stup = stup + ", "
stup = stup + random.choice('1111111111111111111122222334')
stup = stup + ')'
tup = literal_eval(stup)
xval_density.append(tup)
return xval_density
I’m also uncomfortable using so many global variables such as PILL_COUNT and TIMER. So if there is a best practice in that situation, I’d be happy to know about it.
Here's the complete code:
import sys, pygame, os, random, math, time
from ast import literal_eval
# Force static position of screen
os.environ['SDL_VIDEO_CENTERED'] = '1'
# Runs imported module
pygame.init()
# Constants
LEFT = 'left'
RIGHT = 'right'
YELLOW = (255, 255, 0)
RED = (255,0,0)
BLUE = (0,0,153)
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
SHIP_WIDTH = 13
SHIP_HEIGHT = 13
PILL_WIDTH = 7
PILL_HEIGHT = 25
PILL_MAX_SIZE = 3000
PILL_COUNT = 0
TIMER = 0
WIN_W = 1200
WIN_H = 670
class Entity(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
class Text(Entity):
def __init__(self, text, size, color, position, font=None):
Entity.__init__(self)
self.color = color
self.position = position
self.font = pygame.font.Font(font, size)
self.text = text
self.image = self.font.render(str(text), 1, self.color)
self.rect = self.image.get_rect()
self.rect.move_ip(position[0]-self.rect.width/2, position[1])
class Mass_Left(Text):
def __init__(self, text, size, color, position, font=None):
Text.__init__(self, text, size, color, position, font=None)
def update(self, ship_left, ship_right):
self.text = "mass: " + str(ship_left.density-169)
self.image = self.font.render(str(self.text), 1, self.color)
self.rect = self.image.get_rect()
self.rect.move_ip(self.position[0]-self.rect.width/2, self.position[1])
class Mass_Right(Text):
def __init__(self, text, size, color, position, font=None):
Text.__init__(self, text, size, color, position, font=None)
def update(self, ship_left, ship_right):
self.text = "mass: " + str(ship_right.density-169)
self.image = self.font.render(str(self.text), 1, self.color)
self.rect = self.image.get_rect()
self.rect.move_ip(self.position[0]-self.rect.width/2, self.position[1])
class Ship(Entity):
def __init__(self, x, y, player):
Entity.__init__(self)
self.win = False
self.speed = 5
self.player = player
self.density = SHIP_WIDTH * SHIP_HEIGHT
self.old_density = 144
self.densityIncrease = False
self.image = pygame.Surface((SHIP_WIDTH, SHIP_HEIGHT)).convert()
self.rect = pygame.Rect(x, y, SHIP_WIDTH, SHIP_HEIGHT)
def moveShip(self):
key = pygame.key.get_pressed()
if self.player == 'left' and (key[pygame.K_w] or key[pygame.K_s] or key[pygame.K_a] or key[pygame.K_d]):
if key[pygame.K_w]:
self.rect.y -= self.speed
if key[pygame.K_s]:
self.rect.y += self.speed
if key[pygame.K_a]:
self.rect.x -= self.speed
if key[pygame.K_d]:
self.rect.x += self.speed
# Adjust Player 2 Speed
if self.player == 'right' and (key[pygame.K_UP] or key[pygame.K_DOWN] or key[pygame.K_LEFT] or key[pygame.K_RIGHT]):
if key[pygame.K_UP]:
self.rect.y -= self.speed
if key[pygame.K_DOWN]:
self.rect.y += self.speed
if key[pygame.K_LEFT]:
self.rect.x -= self.speed
if key[pygame.K_RIGHT]:
self.rect.x += self.speed
def moveInbounds(self):
# Keep Ship Movement Inbounds
if self.rect.y < WIN_H/15:
self.rect.y = WIN_H/15
if self.rect.y > WIN_H - self.rect.height:
self.rect.y = WIN_H - self.rect.height
if self.player == 'left':
if self.rect.x < 0:
self.rect.x = 0
if self.rect.x > WIN_W/2 - self.rect.width:
self.rect.x = WIN_W/2 - self.rect.width
elif self.player == 'right':
if self.rect.x < WIN_W/2:
self.rect.x = WIN_W/2
if self.rect.x > WIN_W - self.rect.width:
self.rect.x = WIN_W - self.rect.width
def checkCollisions(self, pillGroup):
collisions = pygame.sprite.spritecollide(self, pillGroup, True)
for key in collisions:
self.density += key.density
def grow(self):
if self.old_density < self.density:
self.old_density = self.density
self.rect.width = self.rect.height = math.sqrt(self.density)
self.image = pygame.transform.scale(self.image, (self.rect.width, self.rect.height))
def update(self, pillGroup):
# Ship Movement
self.moveShip()
self.moveInbounds()
self.checkCollisions(pillGroup)
self.grow()
def winGame(self):
if self.win:
if TIMER % 5 == 0:
self.rect.width += 20
self.rect.height += 10
if self.player == 'left':
self.rect.x -= 4
elif self.player == 'right':
self.rect.x -= 10
if self.player == 'left':
self.rect.y -= 5
elif self.player == 'right':
self.rect.y -= 5
self.image = pygame.transform.scale(self.image, (self.rect.width, self.rect.height))
self.density += 378
else:
if TIMER % 5 == 0:
if self.rect.width == 0:
pass
elif self.rect.width > 10:
self.rect.width -= 5
self.rect.height -= 5
if self.density >= 0:
self.density -= self.density/3
self.image = pygame.transform.scale(self.image, (self.rect.width, self.rect.height))
elif self.rect.width <= 10:
self.rect.width -= 1
self.rect.height -= 1
if self.density > 0:
self.density -= 2
self.image = pygame.transform.scale(self.image, (self.rect.width, self.rect.height))
if self.density - 169 < 0:
self.density = 169
def check_done(self):
if self.rect.height > WIN_H*1.5 and self.rect.width > WIN_W * 1.5:
return False
else:
return True
class Pill(Entity):
def __init__(self, xval, density):
Entity.__init__(self)
self.speed = 3
self.density = density
self.image = pygame.Surface((PILL_WIDTH, PILL_HEIGHT)).convert()
self.image.fill(self.setColor(density))
self.rect = self.image.get_rect()
self.rect = self.rect.move(xval, WIN_H/15)
def setColor(self, density):
if density == 50:
return YELLOW
elif density == 100:
return RED
elif density == 150:
return BLUE
elif density == 200:
return BLACK
def update(self):
if self.rect.y > WIN_H:
self.kill()
else:
self.rect = self.rect.move((0, self.speed))
def addPill(pillGroup, xvalue, density):
global PILL_COUNT, PILL_MAX_SIZE, TIMER
if PILL_COUNT + 1 < PILL_MAX_SIZE and TIMER % 10 == 0:
pill = Pill(100, density)
pill2 = Pill(100 + WIN_W/2, density)
pillGroup.add(pill, pill2)
PILL_COUNT += 1
def genRandom(size):
xval_density = []
for j in range(size):
length = str(random.randrange(0, (WIN_W/2) - PILL_WIDTH))
stup = '('
stup = stup + str(length)
stup = stup + ", "
stup = stup + random.choice('1111111111111111111122222334')
stup = stup + ')'
tup = literal_eval(stup)
xval_density.append(tup)
return xval_density
def loseGame(left, right):
if left.density > 1500 or right.density > 1500:
if left.density > 1500:
left.win = True
elif right.density > 1500:
right.win = True
return False
else:
return True
def main():
# Initialize variables
global TIMER, PILL_COUNT
fps = 60
pygame.display.set_caption('Pong')
screen = pygame.display.set_mode((WIN_W, WIN_H), pygame.SRCALPHA)
clock = pygame.time.Clock()
play = game_done = True
xval_density = genRandom(PILL_MAX_SIZE)
# Create Game Objects
ship_left = Ship((WIN_W/4) - (SHIP_WIDTH/2), WIN_H - (SHIP_HEIGHT * 4), 'left')
ship_right = Ship((WIN_W/1.3) - (SHIP_WIDTH/2), WIN_H - (SHIP_HEIGHT * 4), 'right')
score1 = Mass_Left("mass: " + str(ship_left.density-1), 40, BLACK, (WIN_W/5, 10))
score2 = Mass_Right("mass: " + str(ship_right.density-1), 40, BLACK, (WIN_W/1.25, 10))
vert_partition = pygame.Surface((1, WIN_H))
hori_partition = pygame.Surface((WIN_W, 1))
# Create Groups
shipGroup = pygame.sprite.Group()
shipGroup.add(ship_left, ship_right)
pillGroup = pygame.sprite.Group()
textGroup = pygame.sprite.Group()
textGroup.add(score1, score2)
# Gameplay
while play:
# Checks if window exit button pressed
for event in pygame.event.get():
if event.type == pygame.QUIT: sys.exit()
# Keypresses
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
pygame.quit()
sys.exit()
# Update Groups
shipGroup.update(pillGroup)
pillGroup.update()
textGroup.update(ship_left, ship_right)
# Adding Pills
addPill(pillGroup, xval_density[PILL_COUNT][0], xval_density[PILL_COUNT][1]*50)
# Print Groups
screen.fill(WHITE)
pillGroup.draw(screen)
shipGroup.draw(screen)
textGroup.draw(screen)
screen.blit(vert_partition, (WIN_W/2, WIN_H/15))
screen.blit(hori_partition, (0, WIN_H/15))
play = loseGame(ship_left, ship_right)
TIMER += 1
# Limits frames per iteration of while loop
clock.tick(fps)
# Writes to main surface
pygame.display.flip()
# Gameplay
while game_done:
ship_left.winGame()
ship_right.winGame()
# Updating
pillGroup.update()
textGroup.update(ship_left, ship_right)
# Adding Pills
addPill(pillGroup, xval_density[PILL_COUNT][0], xval_density[PILL_COUNT][1]*50)
# Print Groups
screen.fill(WHITE)
pillGroup.draw(screen)
shipGroup.draw(screen)
textGroup.draw(screen)
screen.blit(vert_partition, (WIN_W/2, WIN_H/15))
screen.blit(hori_partition, (0, WIN_H/15))
game_done = ship_left.check_done() and ship_right.check_done()
TIMER += 1
# Limits frames per iteration of while loop
clock.tick(fps)
# Writes to main surface
pygame.display.flip()
if __name__ == "__main__":
main()
this is my first post. I need to make a working game for my final project. Basically the idea is to have the character collide with an Item sprite and Have a random item display on the screen telling the player what the item is and where to take it.
#Initialize
import pygame
import random
pygame.init()
#Display
screen = pygame.display.set_mode((1180, 900))
class PaperBoy(pygame.sprite.Sprite):
def __init__(self,startY):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("paperboy.gif")
self.rect = self.image.get_rect()
self.image = self.image.convert()
self.rect.centery = startY
self.dx= 300
self.dy= 300
def update(self):
#adjust x/y to dx/dy
self.rect.centerx = self.rect.centerx+self.dx
self.rect.centery = self.rect.centery+self.dy
#check Boundaries
#Check right
if self.rect.centerx >= 670:
self.rect.centerx =670
#Check left
elif self.rect.centerx <= 220:
self.rect.centerx = 220
#Check Bottom
if self.rect.centery >= 700:
self.rect.centery = 700
#Check Top
elif self.rect.centery <= 200:
self.rect.centery = 200
def moveUp(self):
self.dx=0
self.dy=-5
def moveDown(self):
self.dx =0
self.dy =5
def moveLeft(self):
self.dx =-5
self.dy = 0
def moveRight(self):
self.dx =5
self.dy =0
"""
mousex, mousey = pygame.mouse.get_pos()
self.rect.centery = mousey
#Check X boundary.
if mousex >= 670:
self.rect.right = 670
elif mousex <= 210:
self.rect.left = 210
else:
self.rect.centerx = mousex
#Check Y boundary.
if mousey >= 670:
self.rect.top = 670
elif mousey >= 250:
self.rect.top = 250
if mousex >= 250 and mousey >= 220:
self.rect.left = 250
self.rect.top = 670
else:
self.rect.centery = mousey
"""
class Parcel(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("parcel.png")
self.rect = self.image.get_rect()
self.image = self.image.convert()
def update(self):
self.rect.center = (200,600)
"""(random.randint(300,800)),(random.randint(300,800))"""
"""
================================HUD======================================
"""
#Green Y
class ItemHUD(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("itemhud.png")
self.rect = self.image.get_rect()
self.image = self.image.convert()
def update(self):
self.rect.center = (950,200)
#Red A
class WhereHUD(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("where.png")
self.rect = self.image.get_rect()
self.image = self.image.convert()
def update(self):
self.rect.center = (950, 350)
#Small Green
class TimeHUD(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("timehud.png")
self.rect = self.image.get_rect()
self.image = self.image.convert()
def update(self):
self.rect.center = (915, 850)
#Yellow
class GoldHUD(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("gold.png")
self.rect = self.image.get_rect()
self.image = self.image.convert()
def update(self):
self.rect.center = (915, 700)
"""
=================================HUD OBJECTS==============================
"""
"""
------------------------------------MAIN-----------------------------------
"""
def main():
pygame.display.set_caption("A Link to the Parcel")
background = pygame.image.load('village.png').convert()
allSprites=pygame.sprite.Group()
parcel = Parcel()
#Heads up Display
itemHud = ItemHUD()
timeHud = TimeHUD()
goldHud = GoldHUD()
whereHud = WhereHUD()
#Player
paperboy = PaperBoy(200)
#Sprites added to AllSprites Group
allSprites.add(paperboy)
allSprites.add(parcel)
allSprites.add(itemHud)
allSprites.add(timeHud)
allSprites.add(goldHud)
allSprites.add(whereHud)
font = pygame.font.Font(None, 25)
goldSack = 0
clock = pygame.time.Clock()
keepGoing = True
while keepGoing:
clock.tick(30)
for event in pygame.event.get():
if event.type == pygame.QUIT:
keepGoing = False
elif event.type == pygame.KEYUP:
if event.key==pygame.K_UP:
paperboy.moveUp()
elif event.key==pygame.K_DOWN:
paperboy.moveDown()
elif event.key==pygame.K_LEFT:
paperboy.moveLeft()
elif event.key==pygame.K_RIGHT:
paperboy.moveRight()
fontTitle = font.render("A Link to the Parcel", True, (255,255,255,))
screen.blit(background, (0, 0))
screen.blit(fontTitle, [925,100])
allSprites.clear(screen, background,)
allSprites.update()
allSprites.draw(screen)
pygame.display.flip()
pygame.quit()
if __name__ == "__main__":
main()
If I understand what you mean, you have a bunch of Sprite types, and you want to choose one at random. To do that, just put them all in a list, use random.choice to pick one, and then instantiate it. Like this:
class OneKindOfParcel(Parcel): # etc.
class AnotherKindOfParcel(Parcel): # etc.
class AThirdKindOfParcel(Parcel): # etc.
parcels = [OneKindOfParcel, AnotherKindOfParcel, AThirdKindOfParcel]
# … later …
parceltype = random.choice(parcels)
parcel = parceltype()
allSprites.add(parcel)
You probably want to give it a location, and display something about its name and location, and so on, but I think you know how to do all that.
Quick and dirty is a list of items and then use random.choice from the random module.