I'm making a rhythm game.
When a "beat" (arrow) hits a certain coordinate, it disappears.
If the corresponding key is being pressed, the game adds to the player's score.
But when I run my code, it doesn't add to the score.
Here's my code:
import pygame
import time
import itertools
pygame.init()
SCREENWIDTH = 1000
SCREENHEIGHT = 650
screen = pygame.display.set_mode([SCREENWIDTH, SCREENHEIGHT])
screen.fill((255, 123, 67))
pygame.draw.rect(screen, (0, 255, 188), (0, 50, 1000, 650), 0)
myfont = pygame.font.SysFont('Ink Free', 30)
background = screen.copy()
clock = pygame.time.Clock()
stageon = True
sprites = pygame.sprite.Group()
class Player(pygame.sprite.Sprite):
sprite = pygame.image.load("Sprites/lee.png")
def __init__(self, *groups):
super().__init__(*groups)
self.image = Player.sprite
self.rect = self.image.get_rect(topleft=(445, 550))
self.pos = pygame.Vector2(self.rect.topleft)
self.score = 0
def update(self):
key = pygame.key.get_pressed()
dist = 3
if key[pygame.K_DOWN]:
self.rect.y += dist
elif key[pygame.K_UP]:
self.rect.y -= dist
if key[pygame.K_RIGHT]:
self.rect.x += dist
elif key[pygame.K_LEFT]:
self.rect.x -= dist
player = Player(sprites)
beatgroup = pygame.sprite.Group()
class Beat(pygame.sprite.Sprite):
def __init__(self, ticks, image):
super().__init__(beatgroup)
self.image = image
self.rect = self.image.get_rect()
self.pos = pygame.Vector2(730, 100)
self.path = itertools.cycle(((730, 100), (850, 100),))
self.next_point = pygame.Vector2(next(self.path))
self.speed = 2
self.ticks = 200
def update(self):
move = self.next_point - self.pos
move_length = move.length()
if move_length != 0:
move.normalize_ip()
move = move * self.speed
self.pos += move
key = pygame.key.get_pressed()
if self.pos == (850, 100):
self.kill()
#here's the problem area
if self.pos == (850, 100) and key[pygame.K_DOWN] and self.image == pygame.image.load("Sprites/down.png"):
player.score += 10
elif self.pos == (850, 100) and key[pygame.K_UP] and self.image == pygame.image.load("Sprites/up.png"):
player.score += 10
elif self.pos == (850, 100) and key[pygame.K_LEFT] and self.image == pygame.image.load("Sprites/left.png"):
player.score += 10
elif self.pos == (850, 100) and key[pygame.K_RIGHT] and self.image == pygame.image.load("Sprites/right.png"):
player.score += 10
if move.length() == 0 or move_length < self.speed:
self.next_point = pygame.Vector2(next(self.path))
self.rect.topleft = self.pos
class Beat_gen(pygame.sprite.Sprite):
def __init__(self, order):
super().__init__(beatgroup)
self.image = pygame.image.load("Sprites/beat_cropped.png")
self.rect = self.image.get_rect(topleft=(730, 100))
self.start_time = pygame.time.get_ticks()
print(self.start_time)
self.order = []
self.picorder = []
for i in order:
self.order.append(i[0])
self.picorder.append(i[1])
self.currentbeat = 0
self.LastBeat = 0
def update(self):
if self.currentbeat == len(self.order):
stageon = False
else:
time_gone = pygame.time.get_ticks() - self.start_time
if time_gone >= self.order[self.currentbeat] or self.currentbeat == 0:
self.start_time = pygame.time.get_ticks()
Beat(self.order[self.currentbeat], self.picorder[self.currentbeat])
self.currentbeat += 1
self.LastBeat = pygame.time.get_ticks()
class Hit_Line(pygame.sprite.Sprite):
def __init__(self):
super().__init__(beatgroup)
self.image = pygame.image.load("Sprites/hit-line.png")
self.rect = self.image.get_rect(topleft=(873, 60))
def update(self):
self.image.draw()
beatgen = Beat_gen([(820, pygame.image.load("Sprites/left.png")), (410, pygame.image.load("Sprites/right.png")),(410, pygame.image.load("Sprites/left.png")),
(410, pygame.image.load("Sprites/right.png")),(410, pygame.image.load("Sprites/left.png")), (410, pygame.image.load("Sprites/right.png")),
(410, pygame.image.load("Sprites/left.png")), (410, pygame.image.load("Sprites/right.png")),(410, pygame.image.load("Sprites/left.png")),
(410, pygame.image.load("Sprites/right.png")),(410, pygame.image.load("Sprites/left.png")), (410, pygame.image.load("Sprites/right.png")),
(410, pygame.image.load("Sprites/left.png")), (410, pygame.image.load("Sprites/right.png")),(410, pygame.image.load("Sprites/left.png")),
(410, pygame.image.load("Sprites/right.png"))])
def main():
while stageon:
for events in pygame.event.get():
if events.type == pygame.QUIT:
pygame.quit()
return
sprites.update()
beatgroup.update()
screen.blit(background, (0, 0))
sprites.draw(screen)
beatgroup.draw(screen)
pygame.display.update()
clock.tick(100)
if __name__ == '__main__':
main()
Also, a note the way I tested to see if the score WAS going up was that in my original code, the score is displayed on screen. I also printed it to console.
And to prove it wasn't my bad rhythm game skills, I held down one key for a while (right now it just goes left right left right)
I've run through my code several times trying to find a problem with it, but I couldn't find it.
Any explanation would be appreciated.
Thanks :)
It's future me! How I solved it was that I added another attribute to the Beat class called direction, which is a string containing either "left", "right", "up", or "down".
To get this assigned, I went to the part where the Beat_gen gets initialised and in each tuple, I added another value, like so:
(820, pygame.image.load("Sprites/left.png"), "left"). Then I went up to the Beat_gen class and added a list called dirorder (direction order) and made a loop iterate through each tuple, get the third value and append it to the list, as done for the image and the timing.
Then added this to the Beat initialisation as an argument to assign it; self.dirorder[self.currentbeat]
Related
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
I want to flip these sprites so that they're facing left. That way I can use the sprite images for my player running left when I press K_LEFT. Currently have it where if you press the right arrow your player moves right and animates.
self.sprites = []
self.sprites.append(py.image.load('I1.png'))
self.sprites.append(py.image.load('R0.png'))
self.sprites.append(py.image.load('R1.png'))
self.sprites.append(py.image.load('R2.png'))
self.sprites.append(py.image.load('R3.png'))
self.sprites.append(py.image.load('R4.png'))
self.sprites.append(py.image.load('R5.png'))
self.current_sprite = 0
self.image = self.sprites[self.current_sprite]
self.rect = self.image.get_rect()
self.rect.center = (x, y)
self.hit_rect = PLAYER_RECT_HIT
self.hit_rect.center = self.rect.center
self.pos = vec(x, y)
self.vel = vec(0, 0)
self.acc = vec(0, 0)
self.is_animating = False
def get_keys(self):
self.acc = vec(0, PLAYER_GRAVITY)
keys = py.key.get_pressed()
if keys[py.K_RIGHT]:
self.acc.x += PLAYER_ACC
self.animate()
if keys[py.K_LEFT]:
self.acc.x -= PLAYER_ACC
def animate(self):
self.is_animating = True
def update(self):
self.get_keys()
if self.is_animating:
self.current_sprite += 0.2
if self.current_sprite >= len(self.sprites):
self.current_sprite = 0
self.is_animating = False
self.image = self.sprites[int(self.current_sprite)]
self.pos += self.vel
self.hit_rect.centerx = self.pos.x
wall_collision(self, self.game.walls, 'x')
self.hit_rect.centery = self.pos.y
wall_collision(self, self.game.walls, 'y')
self.rect.center = self.hit_rect.center
# Apply Friction
self.acc.x += self.vel.x * PLAYER_FRICTION
# Equation of motion
self.vel += self.acc
self.pos += self.vel + 0.5 * self.acc
I would try
pygame.transform.flip()
https://www.pygame.org/docs/ref/transform.html#pygame.transform.flip
When you blit to screen you could check if 'left key' was pressed and transform.flip
'flip(Surface, xbool, ybool) -> Surface'
eg.
if key == 'left':
self.screen.blit(pygame.transform.flip(self.image, False, True), self.rect)
example code:
import pygame
import pygame.sprite
import sys
class WalkingSprite(pygame.sprite.Sprite):
""" A class to manage an animated sprite. """
def __init__(self):
super(WalkingSprite, self).__init__()
self.direction = True
self.images = []
for number in range(1, 11):
self.images.append(pygame.image.load(f'images/walking_sprite{number}.png'))
self.index = 0
self.image = self.images[self.index]
self.rect = self.image.get_rect()
self.rect.x, self.rect.y = 300, 200
def update(self):
self.index += 1
if self.index >= len(self.images):
self.index = 0
if self.direction:
self.image = self.images[self.index]
else:
self.image = pygame.transform.flip(self.images[self.index], True, False)
BG_COLOR = pygame.Color('white')
pygame.init()
screen = pygame.display.set_mode((800, 600))
walking_sprite = WalkingSprite()
sprite_group = pygame.sprite.Group(walking_sprite)
clock = pygame.time.Clock()
while True:
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
walking_sprite.direction = False
if event.key == pygame.K_RIGHT:
walking_sprite.direction = True
if event.type == pygame.QUIT:
sys.exit()
sprite_group.update()
screen.fill(BG_COLOR)
sprite_group.draw(screen)
pygame.display.update()
clock.tick(15)
Transform.rotate is what your looking for.
for sprite in self.sprites :
pygame.transform.rotate (sprite, angle)
I'm trying to make a bullet-hell style game and I've almost got it to work except the opponent won't change from shooting one bullet pattern to another.
It's supposed to shoot 3 blue bullets 8 times, then switch to shooting 2 purple bullets 8 times. There's a sequence of patterns but I've only got two.
So it should iterate through each pattern every time the current pattern shoots a certain amount of times. When all the patterns are done it should stop shooting completely.
I've seen people try to make these but it's always java and I'm on python.
The code is very long but I can't cut it down any more. The original is in multiple files but I've put it into one script. It's virtually impossible to simplify.
import sys
import time
import itertools
import pygame
from pygame.locals import *
class Player(pygame.sprite.Sprite):
#this sprite variable is a placeholder
sprite = pygame.image.load("Sprites/player.png")
def __init__(self, *groups):
super().__init__(*groups)
self.image = Player.sprite
self.rect = self.image.get_rect(topleft=(445, 550))
self.pos = pygame.Vector2(self.rect.topleft)
def update(self):
key = pygame.key.get_pressed()
dist = 3
if key[pygame.K_DOWN]:
self.rect.y += dist
elif key[pygame.K_UP]:
self.rect.y -= dist
if key[pygame.K_RIGHT]:
self.rect.x += dist
elif key[pygame.K_LEFT]:
self.rect.x -= dist
class Spell:
def __init__(self, bullet, pattern, speed, loop, tick_delay):
self.bullet = bullet
self.pattern = pattern
self.speed = speed
self.loop = loop
self.tick_delay = tick_delay
class Opponent(pygame.sprite.Sprite):
def __init__(self, sprite, sequence, *groups):
super().__init__(*groups)
self.image = sprite
self.rect = self.image.get_rect(topleft=(425, 30))
self.pos = pygame.Vector2(self.rect.topleft)
self.path = itertools.cycle((self.rect.topleft, ))
self.next_point = pygame.Vector2(next(self.path))
self.speed = 1
self.ticks = 1000
self.queue = []
self.sequence = sequence
self.spellno = 0
self.currentspell = sequence[self.spellno]
def update(self):
#this function basically does most of the stuff in this class
move = self.next_point - self.pos
move_length = move.length()
if move_length != 0:
move.normalize_ip()
move = move * self.speed
self.pos += move
#later on down the line i want to make the opponent sprite move
if move.length() == 0 or move_length < self.speed:
self.next_point = pygame.Vector2(next(self.path))
self.rect.topleft = self.pos
for i in range(0, self.currentspell.loop):
if pygame.time.get_ticks() - self.ticks > self.currentspell.tick_delay:
self.ticks = pygame.time.get_ticks()
self.shoot()
time_gone = pygame.time.get_ticks() - self.ticks
for bullet in self.queue:
if bullet[0] <= time_gone:
Bullet(self.rect.center, bullet[1], self.currentspell.bullet, sprites, bullets)
self.queue = [bullet for bullet in self.queue if bullet[0] > time_gone]
return
def shoot(self):
pattern = self.currentspell.pattern
self.queue = pattern
class Bullet(pygame.sprite.Sprite):
def __init__(self, pos, direction, image, *groups):
super().__init__(*groups)
self.image = image
self.rect = self.image.get_rect(topleft=pos)
self.direction = direction
self.pos = pygame.Vector2(self.rect.topleft)
def update(self):
self.pos += self.direction
self.rect.topleft = (self.pos.x, self.pos.y)
if not screen.get_rect().colliderect(self.rect):
self.kill()
sprites = pygame.sprite.Group()
bullets = pygame.sprite.Group()
opponentgroup = pygame.sprite.Group()
mi1 = Spell(pygame.image.load("Sprites/lightblue-glowey.png"),(
(0, pygame.Vector2(-0.5, 1) * 4),
(0, pygame.Vector2(0, 1) * 4),
(0, pygame.Vector2(0.5, 1) * 4)),
10, 8, 340
)
mi2 = Spell(pygame.image.load("Sprites/purple-glowey.png"),(
(0, pygame.Vector2(1, 1) * 4),
(0, pygame.Vector2(-1, 1) * 4)),
4, 8, 340
)
minty_spells = [mi1, mi2]
player = Player(sprites)
Minty = Opponent(pygame.image.load("Sprites/minty.png"), minty_spells, opponentgroup)
opponents = [Minty]
pygame.init()
SCREENWIDTH = 1000
SCREENHEIGHT = 650
screen = pygame.display.set_mode([SCREENWIDTH, SCREENHEIGHT])
screen.fill((255, 123, 67))
pygame.draw.rect(screen, (0, 255, 188), (50, 50, 900, 575), 0)
background = screen.copy()
clock = pygame.time.Clock()
currentopponent = 0
def closegame():
pygame.quit()
return
def stage(opponent, background, bgm):
currentopponent = opponent
for spell in opponents[opponent].sequence:
op = opponents[opponent]
op.update()
op.spellno += 1
def main():
running = True
while running:
for events in pygame.event.get():
if events.type == pygame.QUIT:
pygame.quit()
return
# update all sprites
sprites.update()
sprites.add(opponents[currentopponent])
# draw everything
screen.blit(background, (0, 0))
stage(0, "", "") # "" means placeholder. i'm working on them
sprites.draw(screen)
pygame.display.update()
clock.tick(100)
if __name__ == '__main__':
main()
Original Code and assets on my GitHub: https://github.com/E-Lee-Za/Eleeza-Crafter-The-Game
Here's a working (and simplified) version of your code. The loop attribute of the current spell gets decremented every time when the bullets are created. When loop is 0,
the self.spellno is incremented and the spell gets changed, otherwise if the spellno is >= len(self.sequence), self.currentspell gets set to None so that it stops shooting (just add if self.currentspell is not None to the conditional statement).
import pygame
class Spell:
def __init__(self, bullet, pattern, speed, loop, tick_delay):
self.bullet = bullet
self.pattern = pattern
self.speed = speed
self.loop = loop
self.tick_delay = tick_delay
class Opponent(pygame.sprite.Sprite):
def __init__(self, sprite, sequence, *groups):
super().__init__(*groups)
self.image = sprite
self.rect = self.image.get_rect(topleft=(425, 30))
self.start_time = pygame.time.get_ticks()
self.sequence = sequence
self.spellno = 0
self.currentspell = sequence[self.spellno]
def update(self):
time_gone = pygame.time.get_ticks() - self.start_time
# Only create bullets if self.currentspell is not None.
if self.currentspell is not None and time_gone > self.currentspell.tick_delay:
self.start_time = pygame.time.get_ticks()
for bullet in self.currentspell.pattern:
if bullet[0] <= time_gone:
Bullet(self.rect.center, bullet[1], self.currentspell.bullet, sprites, bullets)
# Decrement the loop attribute of the current spell and
# switch to the next spell when it's <= 0. When all spells
# are done, set self.currentspell to None to stop shooting.
self.currentspell.loop -= 1
if self.currentspell.loop <= 0:
self.spellno += 1
if self.spellno >= len(self.sequence):
self.currentspell = None
else:
self.currentspell = self.sequence[self.spellno]
class Bullet(pygame.sprite.Sprite):
def __init__(self, pos, direction, image, *groups):
super().__init__(*groups)
self.image = image
self.rect = self.image.get_rect(topleft=pos)
self.direction = direction
self.pos = pygame.Vector2(self.rect.topleft)
def update(self):
self.pos += self.direction
self.rect.topleft = (self.pos.x, self.pos.y)
if not screen.get_rect().colliderect(self.rect):
self.kill()
sprites = pygame.sprite.Group()
bullets = pygame.sprite.Group()
opponentgroup = pygame.sprite.Group()
img = pygame.Surface((30, 40))
img.fill((0, 100, 200))
mi1 = Spell(
img,
((0, pygame.Vector2(-0.5, 1) * 4), (0, pygame.Vector2(0, 1) * 4),
(0, pygame.Vector2(0.5, 1) * 4)),
10, 8, 340
)
img2 = pygame.Surface((30, 30))
img2.fill((110, 0, 220))
mi2 = Spell(
img2,
((0, pygame.Vector2(1, 1) * 4), (0, pygame.Vector2(-1, 1) * 4)),
4, 8, 340
)
minty_spells = [mi1, mi2]
img3 = pygame.Surface((30, 50))
img3.fill((220, 0, 200))
Minty = Opponent(img3, minty_spells, opponentgroup)
sprites.add(Minty)
pygame.init()
SCREENWIDTH = 1000
SCREENHEIGHT = 650
screen = pygame.display.set_mode([SCREENWIDTH, SCREENHEIGHT])
screen.fill((255, 123, 67))
pygame.draw.rect(screen, (0, 255, 188), (50, 50, 900, 575), 0)
background = screen.copy()
clock = pygame.time.Clock()
def main():
while True:
for events in pygame.event.get():
if events.type == pygame.QUIT:
pygame.quit()
return
sprites.update()
screen.blit(background, (0, 0))
sprites.draw(screen)
pygame.display.update()
clock.tick(100)
if __name__ == '__main__':
main()
I'm trying to make objects from the Angryball class randomly spawn from the right border and move from right to left outside the screen. The idea is that these ogjects must spawn on random y coordinates from the window's right border (better if i can make them seem like if they were coming from outside the border, but that's another point i will check later) and then move at speed 5 until the reach the opposite border. Once one object gets out, another one is spawned again.
I don't have any error when i run this but it doesn't work as expected :) Can you help me figuring this out?
import pygame
import os
import random
size = width, height = 750, 422
screen = pygame.display.set_mode(size)
img_path = os.path.join(os.getcwd())
background_image = pygame.image.load('background.jpg').convert()
bg_image_rect = background_image.get_rect()
pygame.mixer.pre_init(44100, 16, 2, 4096)
pygame.display.set_caption("BallGame")
class Ball(object):
def __init__(self):
self.image = pygame.image.load("ball.png")
self.image_rect = self.image.get_rect()
self.image_rect.x
self.image_rect.y
self.facing = 'LEFT'
def handle_keys(self):
key = pygame.key.get_pressed()
dist = 5
if key[pygame.K_DOWN] and self.image_rect.y < 321:
self.facing = 'DOWN'
self.image_rect.y += dist
elif key[pygame.K_UP] and self.image_rect.y > 0:
self.facing = 'UP'
self.image_rect.y -= dist
if key[pygame.K_RIGHT] and self.image_rect.x < 649:
self.facing = 'RIGHT'
self.image_rect.x += dist
elif key[pygame.K_LEFT] and self.image_rect.x > 0:
self.facing = 'LEFT'
self.image_rect.x -= dist
def draw(self, surface):
if self.facing == "RIGHT":
surface.blit(pygame.transform.flip(self.image, True, False),(self.image_rect.x,self.image_rect.y))
elif self.facing == "DOWN":
surface.blit(pygame.image.load("ball_down.png"),(self.image_rect.x,self.image_rect.y))
if self.facing == "UP":
surface.blit(pygame.image.load("ball_up.png"),(self.image_rect.x,self.image_rect.y))
elif self.facing == "LEFT":
surface.blit(self.image,(self.image_rect.x,self.image_rect.y))
mob_images = [pygame.image.load("image1.png").convert_alpha(),pygame.image.load("image2.png").convert_alpha(),pygame.image.load("image3.png").convert_alpha(),pygame.image.load("image4.png").convert_alpha(),pygame.image.load("image5.png").convert_alpha()]
mob_image = random.choice(mob_images)
class Angryball(pygame.sprite.Sprite):
def __init__(self, image, pos_x, pos_y):
super(Angryball, self).__init__()
self.image = image
self.rect = self.image.get_rect()
self.rect.x = pos_x
self.rect.y = pos_y
self.facing = 'LEFT'
def update(self, screen):
if self.rect.x <= 0:
self.rect.right = screen.get_rect().width
self.rect.top = random.randint(0, screen.get_rect().height)
else:
self.rect.move_ip(-5, 0)
pygame.init()
screen = pygame.display.set_mode((750, 422))
ball = Ball()
angryball = Angryball(mob_image, 700, random.randrange(400))
sprites = pygame.sprite.Group()
sprites.add(angryball)
clock = pygame.time.Clock()
pygame.mixer.music.load("bg_music.mp3")
pygame.mixer.music.play(-1, 0.0)
running = True
while running:
esc_key = pygame.key.get_pressed()
for event in pygame.event.get():
if esc_key[pygame.K_ESCAPE]:
pygame.display.quit()
pygame.quit()
running = False
ball.handle_keys()
sprites.update(screen)
sprites.draw(screen)
screen.blit(background_image, bg_image_rect)
screen.blit(background_image, bg_image_rect.move(bg_image_rect.width, 0))
bg_image_rect.move_ip(-2, 0)
if bg_image_rect.right <= 0:
bg_image_rect.x = 0
ball.draw(screen)
pygame.display.update()
clock.tick(60)
Use pygame's Sprites.
Let Angryball inherit from pygame.sprite.Sprite and rename image_rect to rect.
Give it an update function like this:
def update(self, screen):
if self.rect.x <= 0:
self.rect.right = screen.get_rect().width
self.rect.top = random.randint(0, screen.get_rect().height)
else:
self.rect.move_ip(-5, 0)
Instead of manually blitting it to the screen, use a Group:
angryball = Angryball(mob_image, 700, random.randrange(400))
sprites = pygame.sprite.Group()
sprites.add(angryball)
and in your main loop simply call
sprites.update(screen)
sprites.draw(screen)
Your code does not work because you try to use angryball_image_rect in the line before you actually declare it.
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.