I am trying to make objects fall like they would on earth. I already got them to blit where I wanted them to but I can't seem to animate them.
This is the object that I want to fall
import pygame
class circle():
def __init__(self, screen):
planet_color = (255,0,0)
planet_radius = 20
self.screen = screen
ev = pygame.event.get()
self.image = pygame.image.load('../images/jupiter.bmp')
self.image = pygame.transform.scale(self.image, (80, 80))
def blitme(self):
self.x = pygame.mouse.get_pos()
self.rect = self.image.get_rect()
self.rect.center = self.x
self.screen.blit(self.image, self.rect)
And this is the code that runs it. When the mouse is clicked a little picture of Jupiter is made where the mouse was clicked. How do I get this image to fall?
import pygame
import gravfunc as gf
from gravfunc import circle
import sys
def run_game():
screen_height = 670
screen_width = 1270
pygame.init()
screen = pygame.display.set_mode((screen_width, screen_height))
screen.fill((10,10,30))
running = True
circ = circle(screen)
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_q:
sys.exit()
elif event.type == pygame.MOUSEBUTTONDOWN:
circ.blitme()
pygame.display.flip()
run_game()
Give your class a self.speed_y attribute and add the GRAVITY to it each frame to accelerate the object. I've also added a self.pos_y attribute because pygame.Rects can't have floating point numbers as their coordinates. So,
increase the speed
add the speed to the position (self.pos_y)
assign the self.pos_y to self.rect.y.
Since you are already using a class, I recommend to make it a pygame sprite subclass (inherit from pygame.sprite.Sprite). Then you can add all circles to a pygame.sprite.Group and update and draw them by calling sprite_group.update() and sprite_grop.draw(screen).
import pygame
GRAVITY = .2 # Pretty low gravity.
class Circle(pygame.sprite.Sprite):
def __init__(self, pos, screen):
super().__init__()
self.screen = screen
self.image = pygame.Surface((80, 80), pygame.SRCALPHA)
pygame.draw.circle(self.image, (30, 90, 150), (40, 40), 40)
self.rect = self.image.get_rect(center=pos)
self.pos_y = pos[1]
self.speed_y = 0
def update(self):
self.speed_y += GRAVITY
self.pos_y += self.speed_y
self.rect.y = self.pos_y
if self.pos_y > self.screen.get_height():
self.kill() # Remove off-screen circles.
def run_game():
pygame.init()
screen = pygame.display.set_mode((1270, 670))
clock = pygame.time.Clock()
running = True
circles = pygame.sprite.Group(Circle((600, 0), screen))
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
return
elif event.type == pygame.MOUSEBUTTONDOWN:
circles.add(Circle(event.pos, screen))
circles.update()
screen.fill((10, 10, 30))
circles.draw(screen)
pygame.display.flip()
clock.tick(60)
run_game()
pygame.quit()
Related
I'm practicing with pygame and don't know how to make my character move. If I put 'print' statement, it works, and prints whatever I want when I press 'a' for example, but character stays on his place. I know little about classes, so I think that's the problem
import pygame
pygame.init()
pygame.font.init()
width, height = 924, 500
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption('Priest Beast')
clock = pygame.time.Clock()
BG = pygame.transform.scale2x(pygame.image.load('art/background.png')).convert_alpha()
music = pygame.mixer.Sound('sound/music.mp3')
class Player(pygame.sprite.Sprite):
def __init__(self, x, y):
super().__init__()
self.image = pygame.transform.scale2x(pygame.image.load('art/Player.png')).convert_alpha()
self.rect = self.image.get_rect(center = (800, 300))
self.x = x
self.y = y
def move(self):
keys = pygame.key.get_pressed()
if keys[pygame.K_a]:
self.rect.x += 5
def update(self):
self.move()
#Loop and exit
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
exit()
# Sounds
music.play()
music.set_volume(0.1)
screen.blit(BG, (0, 0))
player = pygame.sprite.GroupSingle()
player.add(Player(800,200))
#Update everything
player.draw(screen)
player.update()
pygame.display.update()
clock.tick(60)
You continuously recreate the object in its initial position. You need to create the Sprite and Group before the application loop:
player = pygame.sprite.GroupSingle() # <-- INSERT
player.add(Player(800,200))
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
exit()
# [...]
screen.blit(BG, (0, 0))
# DELETE
# player = pygame.sprite.GroupSingle()
# player.add(Player(800,200))
# [...]
I am trying to resolve the cause of my triggered bullet disappearing if in a short period of seconds I fire a second bullet. I imagine that screen.fill(GRAY) might be a cause and it should be inside the Class Bullet() or maybe I need to use all_sprites_list.update() elsewhere.
Here is my MWE code:
import pygame
BLUE = (0, 0, 255)
GREEN = (0, 255, 0)
GRAY = (128,128,128)
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
class Cowboy(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.Surface([14,20])
self.image.fill(BLUE)
self.rect = self.image.get_rect()
class Bullet(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.image.load("bullet.png").convert()
self.rect = self.image.get_rect()
def update(self):
self.rect.y += -3
def main():
pygame.init()
size = [SCREEN_WIDTH, SCREEN_HEIGHT]
screen = pygame.display.set_mode(size, pygame.RESIZABLE)
all_sprites_list = pygame.sprite.Group()
bullet_list = pygame.sprite.Group()
enemies_list = pygame.sprite.Group()
bullet = Bullet()
enemy = Cowboy()
enemies_list.add(enemy)
all_sprites_list.add(enemy)
enemy.rect.y = 50
enemy.rect.x = 50
done = False
clock = pygame.time.Clock()
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
elif event.type == pygame.MOUSEBUTTONDOWN:
bullet_list.add(bullet)
all_sprites_list.add(bullet)
bullet.rect.x = 340
bullet.rect.y = 200
screen.fill(GRAY)
all_sprites_list.draw(screen)
clock.tick(60)
pygame.display.flip()
pygame.quit()
if __name__ == "__main__":
main()
You have to create a new instance of Bullet, when the mouse button is pressed and you have to invoke the update method of the bullets calling update on the bullet_list:
def main():
# [...]
# bullet = Bullet() <--- DELETE
# [...]
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
elif event.type == pygame.MOUSEBUTTONDOWN:
# create new bullet and add it to the groups
bullet = Bullet()
bullet.rect.center = enemy.rect.center
bullet_list.add(bullet)
all_sprites_list.add(bullet)
# update the bullets
bullet_list.update()
# [...]
import pygame
BLUE = (0, 0, 255)
GREEN = (0, 255, 0)
GRAY = (128,128,128)
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
class Cowboy(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.Surface([14,20])
self.image.fill(BLUE)
self.rect = self.image.get_rect()
class Bullet(pygame.sprite.Sprite):
def __init__(self, x, y):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("bullet.png").convert()
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
def update(self):
self.rect.y += -3
def main():
pygame.init()
size = [SCREEN_WIDTH, SCREEN_HEIGHT]
screen = pygame.display.set_mode(size, pygame.RESIZABLE)
all_sprites_list = pygame.sprite.Group()
bullet_list = pygame.sprite.Group()
enemies_list = pygame.sprite.Group()
bullet = Bullet(340, 240)
enemy = Cowboy()
enemies_list.add(enemy)
all_sprites_list.add(enemy)
enemy.rect.y = 50
enemy.rect.x = 50
done = False
clock = pygame.time.Clock()
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
elif event.type == pygame.MOUSEBUTTONDOWN:
bullet = Bullet(340, 200)
bullet_list.add(bullet)
all_sprites_list.add(bullet)
bullet.rect.x = 340
bullet.rect.y = 200
screen.fill(GRAY)
all_sprites_list.update()
all_sprites_list.draw(screen)
clock.tick(60)
pygame.display.flip()
pygame.quit()
if __name__ == "__main__":
main()
I'm writing a code where several enemies can spawn on the screen.
The enemy attribute used to take a color attribute that was used for all of the enemies, and I am now trying to change it to an image that I can load.
I am also interested in doing this so that I can get_rect() from the image that I will use for checking when the enemies are at a certain position on the screen.
The problem is that as I tried to replace the color attribute with an image attribute, the previous code was so dependent on loading up the enemies with the color attribute that I'm having a hard time getting the code to work.
With the current code I've written, it doesn't load the image properly.
This is the old (functional, using the color attribute) that I started with:
import sys
import pygame as pg
class Enemy:
def __init__(self, pos, color):
self.rect = pg.Rect(pos, (26, 45))
self.color = color
self.pos = pg.math.Vector2(pos)
def update(self, player):
vel = (player.center - self.pos).normalize() * 4
self.pos += vel
self.rect.center = self.pos
def draw(self, screen):
pg.draw.rect(screen, self.color, self.rect)
def main():
screen = pg.display.set_mode((640, 480))
bg_color = pg.Color('gray12')
player_color = pg.Color('dodgerblue1')
enemy_color = pg.Color('sienna1')
clock = pg.time.Clock()
player = pg.Rect((100, 300), (26, 50))
enemy_list = [Enemy((100, 300), enemy_color)]
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
elif event.type == pg.KEYDOWN:
if event.key == pg.K_f:
enemy_list.append(Enemy((400, 0), enemy_color))
keys = pg.key.get_pressed()
if keys[pg.K_a]:
player.x -= 5
elif keys[pg.K_d]:
player.x += 5
if keys[pg.K_w]:
player.y -= 5
elif keys[pg.K_s]:
player.y += 5
for enemy in enemy_list:
enemy.update(player)
screen.fill(bg_color)
pg.draw.rect(screen, player_color, player)
for enemy in enemy_list:
enemy.draw(screen)
pg.display.flip()
clock.tick(30)
if __name__ == '__main__':
pg.init()
main()
pg.quit()
sys.exit()
And the following is the code that I have tried to change, but as of now can't get to work:
import sys
import pygame as pg
import os
from os import path
pg.init()
screen = pg.display.set_mode((640, 480))
game_folder = os.path.dirname(__file__)
img_folder = os.path.join(game_folder, "images")
enemy_img = pg.image.load(path.join(img_folder, "goblin.png"))
class Enemy(pg.sprite.Sprite):
def __init__(self, pos, image):
pg.sprite.Sprite.__init__(self)
self.image = pg.transform.scale(enemy_img, (48,37))
self.image.set_colorkey((0,0,0))
self.rect = self.image.get_rect()
self.pos = pg.math.Vector2(pos)
def update(self, player):
vel = [-5,0]
self.pos += vel
self.rect.center = self.pos
def draw(self, screen):
screen.blit(Enemy.image, Enemy.rect)
def main():
bg_color = pg.Color('gray12')
player_color = pg.Color('dodgerblue1')
clock = pg.time.Clock()
player = pg.Rect((100, 300), (26, 50))
enemy_list = [Enemy((100, 300), enemy_img)]
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
elif event.type == pg.KEYDOWN:
if event.key == pg.K_f:
enemy_list.append(Enemy((400, 0), enemy_img))
keys = pg.key.get_pressed()
if keys[pg.K_a]:
player.x -= 5
elif keys[pg.K_d]:
player.x += 5
if keys[pg.K_w]:
player.y -= 5
elif keys[pg.K_s]:
player.y += 5
for enemy in enemy_list:
enemy.update(player)
screen.fill(bg_color)
#pg.draw.rect(screen, player_color, player)
for enemy in enemy_list:
enemy.draw(screen)
pg.display.flip()
clock.tick(30)
if __name__ == '__main__':
pg.init()
main()
pg.quit()
sys.exit()
Could someone please help me figure out what went wrong and what code I'm supposed to alter or add? Thank you very much for your time.
Your problen is this line:
screen.blit(Enemy.image, Enemy.rect)
Here you're trying to access the image class attribute of Enemy, but the Enemy class does not have such an attribute, and also no rect attribute.
You could fix this by using
screen.blit(self.image, self.rect)
which will use the instance attributes.
But since you already use the Sprite class, don't bother with drawing the sprites to the screen yourself and use a Group:
import sys
import pygame as pg
import os
from os import path
pg.init()
screen = pg.display.set_mode((640, 480))
game_folder = os.path.dirname(__file__)
img_folder = os.path.join(game_folder, "images")
enemy_img = pg.image.load(path.join(img_folder, "goblin.png"))
class Enemy(pg.sprite.Sprite):
def __init__(self, pos, image):
pg.sprite.Sprite.__init__(self)
self.image = pg.transform.scale(enemy_img, (48,37))
self.image.set_colorkey((0,0,0))
self.rect = self.image.get_rect()
self.pos = pg.math.Vector2(pos)
def update(self, player):
vel = [-5,0]
self.pos += vel
self.rect.center = self.pos
def main():
bg_color = pg.Color('gray12')
player_color = pg.Color('dodgerblue1')
clock = pg.time.Clock()
player = pg.Rect((100, 300), (26, 50))
enemy_list = pg.sprite.Group([Enemy((100, 300), enemy_img)])
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
elif event.type == pg.KEYDOWN:
if event.key == pg.K_f:
enemy_list.append(Enemy((400, 0), enemy_img))
keys = pg.key.get_pressed()
if keys[pg.K_a]:
player.x -= 5
elif keys[pg.K_d]:
player.x += 5
if keys[pg.K_w]:
player.y -= 5
elif keys[pg.K_s]:
player.y += 5
enemy_list.update(player)
screen.fill(bg_color)
enemy_list.draw(screen)
pg.display.flip()
clock.tick(30)
if __name__ == '__main__':
pg.init()
main()
pg.quit()
sys.exit()
The code works fine when you first load the game the two rectangles are there but when the player moves, the enemy rectangle disappears.
EXTENSION I am trying to get the enemy class to move up and down constantly without any keys needed to be pressed.
import pygame
import os
import random
from pygame.locals import * # Constants
import math
import sys
import random
pygame.init()
screen=pygame.display.set_mode((1280,720)) #(length,height)
screen_rect=screen.get_rect()
background = pygame.Surface(screen.get_size())
background = pygame.image.load('stage.png').convert()
screen.blit(background, (0, 0))
class Player(pygame.sprite.Sprite):
def __init__(self):
self.rect = pygame.draw.rect(screen, (0,0,128), (50,560,50,25)) #(colour)(x-position,y-position,width,height)
self.dist = 100
def draw_rect(self,x,y): # This is my code which should make the player move
screen.blit(background, (0, 0)) #If this isn't included then when the rectangle moves it's old positon will still be on the screen
self.rect = self.rect.move(x*self.dist, y*self.dist); pygame.draw.rect(screen, (0, 0, 128), self.rect)
pygame.display.update()
def handle_keys(self): # code to make the character move when the arrow keys are pressed
keys = pygame.key.get_pressed()
if keys[K_LEFT]:
self.draw_rect(-0.05,0)
elif keys[K_RIGHT]:
self.draw_rect(0.05,0)
elif keys[K_UP]:
self.draw_rect(0,-0.05)
elif keys[K_DOWN]:
self.draw_rect(0,0.05)
elif keys[K_SPACE]:
self.draw_rect(0.05,-0.05)
if self.rect.right > 1280:
self.rect.right = 1280
if self.rect.left < 0:
self.rect.left = 0
if self.rect.bottom > 720:
self.rect.bottom = 720
if self.rect.top < 0:
self.rect.top = 0
class Enemy(pygame.sprite.Sprite): # the enemy class which works fine
def __init__(self):
x = random.randint(50,450)
self.rect = pygame.draw.rect(screen, (128,0,0), (300,x,50,25))
player = Player()
enemy = Enemy()
def main(): #my main loop
running = True
while running:
player.handle_keys()
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
pygame.display.flip() #updates the whole screen
if __name__ == '__main__': main()
you are only drawing the sprites when the class is instatiated in the __init__()
you need to be drawing them every loop in the main()function right before pygame.display.flip()
as things are right now, neither the player nor the enemy should display beyond the first frame
You have to clear the screen every frame - you can do this by blitting the background - and draw the sprites afterwards. Separate the movement from the drawing code so that you can blit the sprites after the screen has been cleared.
class Player(pygame.sprite.Sprite):
def __init__(self):
self.rect = pygame.draw.rect(screen, (0,0,128), (50,560,50,25))
self.dist = 100
# Separate the movement and the drawing.
def move(self, x, y):
self.rect = self.rect.move(x*self.dist, y*self.dist)
def draw(self, screen):
pygame.draw.rect(screen, (0, 0, 128), self.rect)
def handle_keys(self):
keys = pygame.key.get_pressed()
if keys[K_LEFT]:
self.move(-0.05,0)
elif keys[K_RIGHT]:
self.move(0.05,0)
elif keys[K_UP]:
self.move(0,-0.05)
elif keys[K_DOWN]:
self.move(0,0.05)
elif keys[K_SPACE]:
self.move(0.05,-0.05)
if self.rect.right > 1280:
self.rect.right = 1280
if self.rect.left < 0:
self.rect.left = 0
if self.rect.bottom > 720:
self.rect.bottom = 720
if self.rect.top < 0:
self.rect.top = 0
class Enemy(pygame.sprite.Sprite):
def __init__(self):
self.x = random.randint(50,450) # x is now an attribute.
def draw(self, screen):
self.rect = pygame.draw.rect(screen, (128,0,0), (300, self.x, 50, 25))
def main():
clock = pygame.time.Clock() # A clock to limit the frame rate.
player = Player()
enemy = Enemy()
running = True
while running:
player.handle_keys()
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# Clear the screen every frame by blitting the background.
screen.blit(background, (0, 0))
# Then draw the enemy and the player.
enemy.draw(screen)
player.draw(screen)
pygame.display.flip()
clock.tick(60) # Limit the frame rate to 60 FPS.
I also recommend checking out how sprite groups work, then you can get rid of the draw methods and can just call sprite_group.draw(screen) to draw every contained sprite. Here's a tutorial.
I am trying to make objects fall like they would on earth. I already got them to blit where I wanted them to but I can't seem to animate them.
This is the object that I want to fall
import pygame
class circle():
def __init__(self, screen):
planet_color = (255,0,0)
planet_radius = 20
self.screen = screen
ev = pygame.event.get()
self.image = pygame.image.load('../images/jupiter.bmp')
self.image = pygame.transform.scale(self.image, (80, 80))
def blitme(self):
self.x = pygame.mouse.get_pos()
self.rect = self.image.get_rect()
self.rect.center = self.x
self.screen.blit(self.image, self.rect)
And this is the code that runs it. When the mouse is clicked a little picture of Jupiter is made where the mouse was clicked. How do I get this image to fall?
import pygame
import gravfunc as gf
from gravfunc import circle
import sys
def run_game():
screen_height = 670
screen_width = 1270
pygame.init()
screen = pygame.display.set_mode((screen_width, screen_height))
screen.fill((10,10,30))
running = True
circ = circle(screen)
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_q:
sys.exit()
elif event.type == pygame.MOUSEBUTTONDOWN:
circ.blitme()
pygame.display.flip()
run_game()
Give your class a self.speed_y attribute and add the GRAVITY to it each frame to accelerate the object. I've also added a self.pos_y attribute because pygame.Rects can't have floating point numbers as their coordinates. So,
increase the speed
add the speed to the position (self.pos_y)
assign the self.pos_y to self.rect.y.
Since you are already using a class, I recommend to make it a pygame sprite subclass (inherit from pygame.sprite.Sprite). Then you can add all circles to a pygame.sprite.Group and update and draw them by calling sprite_group.update() and sprite_grop.draw(screen).
import pygame
GRAVITY = .2 # Pretty low gravity.
class Circle(pygame.sprite.Sprite):
def __init__(self, pos, screen):
super().__init__()
self.screen = screen
self.image = pygame.Surface((80, 80), pygame.SRCALPHA)
pygame.draw.circle(self.image, (30, 90, 150), (40, 40), 40)
self.rect = self.image.get_rect(center=pos)
self.pos_y = pos[1]
self.speed_y = 0
def update(self):
self.speed_y += GRAVITY
self.pos_y += self.speed_y
self.rect.y = self.pos_y
if self.pos_y > self.screen.get_height():
self.kill() # Remove off-screen circles.
def run_game():
pygame.init()
screen = pygame.display.set_mode((1270, 670))
clock = pygame.time.Clock()
running = True
circles = pygame.sprite.Group(Circle((600, 0), screen))
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
return
elif event.type == pygame.MOUSEBUTTONDOWN:
circles.add(Circle(event.pos, screen))
circles.update()
screen.fill((10, 10, 30))
circles.draw(screen)
pygame.display.flip()
clock.tick(60)
run_game()
pygame.quit()