First time building a game in python, let alone pygame. When I launch the code the window with the desired BG comes up, and my Sprite loads where I want. However, the Sprite doesn't move despite all efforts. I've scoured this site and other for resolutions but to no avail.
Note: I am using an upgraded mid-2009 mac book with OS X 10.11.6 and I am launching the code via terminal (python boss_fight.py). Also, I downloaded[tag: pygame] via homebrew.
Other things I've tried:
elif then if vs. if then if statements
Adding print functions after if statements regarding key input to see if the input is registered, does not print.
List item adding print functions after if statements regarding key input to see if input is registered, does not print.
Updating pygame
Updating python
Launching with command pythonw boss_fight.py (this yields: ImportError: No module named pygame) which is weird because running the prompt python boss_fight.py runs the game.
I've tried a few other things but can't remember them all
Here's the code:
import pygame # load pygame keywords
import sys # let python use your file system
import os # help python identify your OS
class Player(pygame.sprite.Sprite):
# Spawn a player
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.movex = 0
self.movey = 0
self.frame = 0
self.images = []
img = pygame.image.load(os.path.join('images','hero.png'))
self.images.append(img)
self.image = self.images[0]
self.rect = self.image.get_rect()
def control(self,x,y):
# control player movement
self.movex += x
self.movey += y
def update(self):
# Update sprite position
self.rect.x = self.rect.x + self.movex
self.rect.y = self.rect.y + self.movey
# moving left
if self.movex < 0:
self.frame += 1
if self.frame > 3 * ani:
self.frame = 0
self.image = self.images[self.frame//ani]
# moving right
if self.movex > 0:
self.frame += 1
if self.frame > 3 * ani:
self.frame = 0
self.image = self.images[(self.frame//ani)+4]
# Setup
worldx = 960
worldy = 720
fps = 40 # frame rate
ani = 4 # animation cycles
clock = pygame.time.Clock()
pygame.init()
main = True
BLUE = (25,25,200)
BLACK = (23,23,23 )
WHITE = (254,254,254)
ALPHA = (0,255,0)
world = pygame.display.set_mode([worldx,worldy])
backdrop = pygame.image.load(os.path.join('images','stage.jpeg')).convert()
backdropbox = world.get_rect()
player = Player() # spawn player
player.rect.x = 0
player.rect.y = 0
player_list = pygame.sprite.Group()
player_list.add(player)
steps = 10 # how fast to move
# Main loop
while main:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit(); sys.exit()
main = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
player.control(-steps,0)
print('left')
elif event.key == pygame.K_RIGHT:
player.control(steps,0)
print('right')
elif event.key == pygame.K_UP:
print('up')
elif event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT:
player.control(steps,0)
elif event.key == pygame.K_RIGHT:
player.control(-steps,0)
elif event.key == pygame.K_q:
pygame.quit()
sys.exit()
main = False
world.blit(backdrop, backdropbox)
player.update()
player_list.draw(world)
pygame.display.update()
clock.tick(fps)
The place where I think you've mistaken is where you're writing code for update
The way I do it is
def update(self):
# Update sprite position
self.rect.x = self.rect.x + self.movex
self.rect.y = self.rect.y + self.movey
# moving left
if self.movex < 0:
self.frame += 1
if self.frame > 3 * ani:
self.frame = 0
self.image = self.images[self.frame//ani]
# moving right
elif self.movex > 0:
self.frame += 1
if self.frame > 3 * ani:
self.frame = 0
self.image = self.images[(self.frame//ani)+4]
self.rect.topleft = self.rect.x, self.rect.y
Related
Right now my game works as so: My sprite can move right or left, jump and shoot fireballs (bullets). However, once my sprite walks past the limit of my background surface, it goes off-screen. So I want to make the background move along with my sprite. As soon as my player sprite moves about 50 pixels towards the edges of the screen, the background moves too. How do I create this with Pygame? I've found quite an amount of sources which shows you how to do this but with a plain colour background. But my background is an image I load into the game, so I would like to learn how to do so with an image as background. How to make it repeat once the sprite comes near the limit of both sides. I separated my codes into 3 different files: a Main.py, settings.py and Sprite1.py. Here's Main.py:
import pygame
import os
import sys
import time
from pygame import mixer
from Sprite1 import *
from settings import *
'''
Setup
'''
pygame.init()
clock = pygame.time.Clock()
pygame.mixer.music.load('.\\sounds\\Fairy.mp3')
pygame.mixer.music.play(-1, 0.0)
all_sprites = pygame.sprite.Group()
player = Player(all_sprites)
player.rect.x = 50
player.rect.y = 500
showStartScreen(surface)
'''
Main loop
'''
main = True
while main == True:
background = pygame.image.load(os.path.join('images', 'Bg.png'))
surface.blit(background, (0,0))
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
main = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT or event.key == ord('a'):
player.control(-steps,0)
if event.key == pygame.K_RIGHT or event.key == ord('d'):
player.control(steps,0)
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT or event.key == ord('a'):
player.control(steps,0)
if event.key == pygame.K_RIGHT or event.key == ord('d'):
player.control(-steps,0)
keys = pygame.key.get_pressed()
if not(isJump):
if keys[pygame.K_UP]:
isJump = True
else:
if jumpCount >= -10:
player.rect.y -= (jumpCount * abs(jumpCount)) * 1
jumpCount -= 2
else:
jumpCount = 10
isJump = False
# dt = time since last tick in milliseconds.
dt = clock.tick(60) / 1000
all_sprites.update(dt)
player.update(dt)
all_sprites.draw(surface) #refresh player position
pygame.display.flip()
Here's settings.py:
import pygame
isJump = False
jumpCount = 10
width = 960
height = 720
fps = 40 # frame rate
#ani = 4 # animation cycles
pygame.display.set_caption('B.S.G.')
surface = pygame.display.set_mode((width, height))
PLAYER_ACC = 0.5
PLAYER_FRICTION = -0.12
PLAYER_GRAV = 0.8
PLAYER_JUMP = 20
PLAYER_LAYER = 2
PLATFORM_LAYER = 1
steps = 10 # how fast to move
And here's Sprite1.py:
import pygame
import sys
import os
import time
from pygame import mixer
from pygame.locals import *
from settings import *
vec = pygame.math.Vector2
def showStartScreen(surface):
show = True
while (show == True):
background = pygame.image.load(os.path.join('images', 'Starting_scr.png'))
# rect = surface.get_rect()
surface.blit(background, (0,0))
pygame.display.flip()
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONDOWN:
show = False
class Player(pygame.sprite.Sprite):
def __init__(self, all_sprites):
pygame.sprite.Sprite.__init__(self)
self.movex = 0
self.movey = 0
self.frame = 0
self.health = 10
self.jumping = False
self.images = []
self.imagesleft = []
self.imagesright = []
self.direction = "right"
self.alpha = (0,0,0)
self.ani = 4 # animation cycles
self.all_sprites = all_sprites
self.add(self.all_sprites)
self.bullet_timer = .1
for i in range(1,5):
img = pygame.image.load(os.path.join('images','hero' + str(i) + '.png')).convert()
img.convert_alpha()
img.set_colorkey(self.alpha)
self.imagesright.append(img)
self.image = self.imagesright[0]
self.rect = self.image.get_rect()
for i in range(1,5):
img = pygame.image.load(os.path.join('images','hero' + str(i) + '.png')).convert()
img = pygame.transform.flip(img, True, False)
img.convert_alpha()
img.set_colorkey(self.alpha)
self.imagesleft.append(img)
self.image = self.imagesleft[0]
self.rect = self.image.get_rect()
def control(self,x,y):
'''
control player movement
'''
self.movex += x
self.movey -= y
def update(self, dt):
'''
Update sprite position
'''
self.rect.x = self.rect.x + self.movex
self.rect.y = self.rect.y + self.movey
# moving left
if self.movex < 0:
self.frame += 1
if self.frame > 3*self.ani:
self.frame = 0
self.image = self.imagesleft[self.frame//self.ani]
self.direction = "left"
# moving right
if self.movex > 0:
self.frame += 1
if self.frame > 3*self.ani:
self.frame = 0
self.image = self.imagesright[self.frame//self.ani]
self.direction = "right"
#enemy_hit_list = pygame.sprite.spritecollide(self,enemy_list, False)
#for enemy in enemy_hit_list:
#self.health -= 1
#print(self.health)
keys = pygame.key.get_pressed()
if keys[pygame.K_SPACE]:
self.bullet_timer -= dt # Subtract the time since the last tick.
if self.bullet_timer <= 0:
self.bullet_timer = 100 # Bullet ready.
if keys: # Left mouse button.
# Create a new bullet instance and add it to the groups.
if self.direction == "right":
Bullet([self.rect.x + self.image.get_width(), self.rect.y + self.image.get_height()/2], self.direction, self.all_sprites)
else:
Bullet([self.rect.x, self.rect.y + self.image.get_height()/2], self.direction, self.all_sprites)
self.bullet_timer = .1 # Reset the timer.
class Bullet(pygame.sprite.Sprite):
IMAGE = None
FLIPPED_IMAGE = None
def __init__(self, pos, direction, *sprite_groups):
super().__init__(*sprite_groups)
# cache images
if not Bullet.IMAGE:
Bullet.IMAGE = pygame.image.load(os.path.join('images','fireball.png'))
Bullet.FLIPPED_IMAGE = pygame.transform.flip(Bullet.IMAGE, True, False)
if direction == "right":
self.vel = pygame.math.Vector2(750, 0)
self.image = Bullet.IMAGE
else:
self.vel = pygame.math.Vector2(-750, 0)
self.image = Bullet.FLIPPED_IMAGE
self.pos = pygame.math.Vector2(pos)
self.rect = self.image.get_rect(center=pos)
def update(self, dt):
# Add the velocity to the position vector to move the sprite
self.pos += self.vel * dt
self.rect.center = self.pos # Update the rect pos.
if not pygame.display.get_surface().get_rect().colliderect(self.rect):
self.kill()
I'm open to any suggestions. Thanks beforehand!
Here's how I would approach this...
set up a scrolling background as in the tutorial and make sure you can get that working OK by moving the background back & forth instead of the player (just freeze the player's x coordinate in the center and move the background with your left/right keys.
Add some constants into your settings for an edge buffer (the number of x-increments you want the player to avoid the boundaries by
AFTER you get the key input (left or right) set up a conditional statement. For this, you will have to access the player's x-coordinate and compare it to the buffer. Well, either 0+buffer or width-buffer actually and then based on those cases either move the player or the background. See if you can get that working.
Then, you will realize that when you move the background, you are moving the frame of reference for everything else, meaning things like the fireball, so if you are moving the background left or right, you will need to apply those updates to the other objects as well so it looks correct.
If yer stuck, make those updates to code above & message me back in a comment.
My game has a sprite which can move left and right and shoot bullets. However, if I shoot out too many bullets (about 50 or so), my game starts lagging a lot. My sprite doesn't move fluently anymore and the game doesn't work well. So I think the problem to this is that all the bullets created are continuing to be run outside my screen. That's why I would like to know how to set a range limit to my bullet. Once past that limit it disappears and out of the program so my game won't lag. However, I am open to other suggestions like if there is a way to make my game no lag without setting a range limit. I have separated my game into 2 .py files a main.py and a Sprite1.py which I import to my main.
Here's my Sprite1.py file:
import pygame
import sys
import os
import time
from pygame import mixer
from pygame.locals import *
def showStartScreen(surface):
show = True
while (show == True):
background = pygame.image.load(os.path.join('images', 'Starting_scr.png'))
# rect = surface.get_rect()
surface.blit(background, (0,0))
pygame.display.flip()
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONDOWN:
show = False
class Player(pygame.sprite.Sprite):
def __init__(self, all_sprites):
pygame.sprite.Sprite.__init__(self)
self.movex = 0
self.movey = 0
self.frame = 0
self.images = []
self.imagesleft = []
self.imagesright = []
self.direction = "right"
# self.rect = self.image.get_rect(center=pos)
self.alpha = (0,0,0)
self.ani = 4 # animation cycles
self.all_sprites = all_sprites
self.add(self.all_sprites)
self.bullet_timer = .1
for i in range(1,5):
img = pygame.image.load(os.path.join('images','hero' + str(i) + '.png')).convert()
img.convert_alpha()
img.set_colorkey(self.alpha)
self.imagesright.append(img)
self.image = self.imagesright[0]
self.rect = self.image.get_rect()
for i in range(1,5):
img = pygame.image.load(os.path.join('images','hero' + str(i) + '.png')).convert()
img = pygame.transform.flip(img, True, False)
img.convert_alpha()
img.set_colorkey(self.alpha)
self.imagesleft.append(img)
self.image = self.imagesleft[0]
self.rect = self.image.get_rect()
def control(self,x,y):
'''
control player movement
'''
self.movex += x
self.movey -= y
def update(self, dt):
'''
Update sprite position
'''
self.rect.x = self.rect.x + self.movex
self.rect.y = self.rect.y + self.movey
# moving left
if self.movex < 0:
self.frame += 1
if self.frame > 3*self.ani:
self.frame = 0
self.image = self.imagesleft[self.frame//self.ani]
self.direction = "left"
# moving right
if self.movex > 0:
self.frame += 1
if self.frame > 3*self.ani:
self.frame = 0
self.image = self.imagesright[self.frame//self.ani]
self.direction = "right"
# self.rect.center = pygame.mouse.get_pos()
keys = pygame.key.get_pressed()
if keys[pygame.K_SPACE]:
self.bullet_timer -= dt # Subtract the time since the last tick.
if self.bullet_timer <= 0:
self.bullet_timer = 0 # Bullet ready.
if keys: # Left mouse button.
# Create a new bullet instance and add it to the groups.
if self.direction == "right":
Bullet([self.rect.x + self.image.get_width(), self.rect.y + self.image.get_height()/2], self.direction, self.all_sprites)
else:
Bullet([self.rect.x, self.rect.y + self.image.get_height()/2], self.direction, self.all_sprites)
self.bullet_timer = .1 # Reset the timer.
class Bullet(pygame.sprite.Sprite):
def __init__(self, pos, direction, *sprite_groups):
super().__init__(*sprite_groups)
self.image = pygame.image.load(os.path.join('images','fireball.png'))
self.rect = self.image.get_rect()
self.sound = pygame.mixer.music.play()
self.pos = pygame.math.Vector2(pos)
self.vel = pygame.math.Vector2(750, 0)
self.direction = direction
def update(self, dt):
# Add the velocity to the position vector to move the sprite
if self.direction == "right":
#self.vel = pygame.math.Vector2(750, 0)
self.image = pygame.image.load(os.path.join('images','fireball.png'))
self.rect = self.image.get_rect()
self.pos += self.vel * dt
else:
#self.vel = pygame.math.Vector2(-750, 0)
BULLET_IMG = pygame.image.load(os.path.join('images','fireball.png'))
self.image = pygame.transform.flip(BULLET_IMG, True, False)
self.rect = self.image.get_rect()
self.pos -= self.vel * dt
#print(self.pos)
self.rect.center = self.pos # Update the rect pos.
if self.rect.bottom <= 0:
self.kill()
And this is my main.py file:
import pygame
import os
import sys
import time
from pygame import mixer
import Sprite1
'''
Setup
'''
pygame.init()
width = 960
height = 720
fps = 40 # frame rate
#ani = 4 # animation cycles
clock = pygame.time.Clock()
pygame.display.set_caption('B.S.G.!!!')
surface = pygame.display.set_mode((width, height))
#pygame.mixer.music.load('.\\sounds\\Fairy.mp3')
#pygame.mixer.music.play(-1, 0.0)
pygame.mixer.music.load('.\\sounds\\Fireball.wav')
#direction = "right"
all_sprites = pygame.sprite.Group()
player = Sprite1.Player(all_sprites)
player.rect.x = 50
player.rect.y = 500
steps = 10 # how fast to move
Sprite1.showStartScreen(surface)
'''
Main loop
'''
main = True
while main == True:
background = pygame.image.load(os.path.join('images', 'Bg.png'))
surface.blit(background, (0,0))
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
main = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT or event.key == ord('a'):
player.control(-steps,0)
#direction = "left"
if event.key == pygame.K_RIGHT or event.key == ord('d'):
player.control(steps,0)
#direction = "right"
if event.key == pygame.K_UP or event.key == ord('w'):
player.rect.y -= 100
#player.rect.y -= 10
#player.movey == 10
#player.movey == -10
#time.sleep(1)
#player.control(0,-steps)
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT or event.key == ord('a'):
player.control(steps,0)
#direction = "left"
if event.key == pygame.K_RIGHT or event.key == ord('d'):
player.control(-steps,0)
#direction = "right"
if event.key == pygame.K_UP or event.key == ord('w'):
player.rect.y += 100
#player.movey == -10
if event.key == ord('q'):
pygame.quit()
sys.exit()
main = False
# dt = time since last tick in milliseconds.
dt = clock.tick(60) / 1000
all_sprites.update(dt)
player.update(dt)
all_sprites.draw(surface) #refresh player position
pygame.display.flip()
P.S.: don't mind how my sprite jumps, I just wrote it that way for now. I will be making him actually jump later on with gravity. Thanks beforehand.
You already check if the bullets leave the screen at the top:
if self.rect.bottom <= 0:
self.kill()
You could change it to
if not pygame.display.get_surface().get_rect().colliderect(self.rect):
self.kill()
to kill the sprite if it is not at the screen at all.
But your problem is actually this:
def update(self, dt):
# Add the velocity to the position vector to move the sprite
if self.direction == "right":
#self.vel = pygame.math.Vector2(750, 0)
self.image = pygame.image.load(os.path.join('images','fireball.png'))
self.rect = self.image.get_rect()
self.pos += self.vel * dt
else:
#self.vel = pygame.math.Vector2(-750, 0)
BULLET_IMG = pygame.image.load(os.path.join('images','fireball.png'))
self.image = pygame.transform.flip(BULLET_IMG, True, False)
self.rect = self.image.get_rect()
self.pos -= self.vel * dt
...
Here you're loading the fireball.png image once for every instance of the Bullet class
every frame. You aim for 60 fps, so when there are 50 Bullet instances you try to load the file 300 times per second from your disk.
Instead, you should load the image once at startup.
Here's how it could look like:
class Bullet(pygame.sprite.Sprite):
IMAGE = None
FLIPPED_IMAGE = None
def __init__(self, pos, direction, *sprite_groups):
super().__init__(*sprite_groups)
# cache images
if not Bullet.IMAGE:
Bullet.IMAGE = pygame.image.load(os.path.join('images','fireball.png'))
Bullet.FLIPPED_IMAGE = pygame.transform.flip(Bullet.IMAGE, True, False)
if direction == "right":
self.vel = pygame.math.Vector2(750, 0)
self.image = Bullet.IMAGE
else:
self.vel = pygame.math.Vector2(-750, 0)
self.image = Bullet.FLIPPED_IMAGE
# suspicious... Should use the Sound class instead
# self.sound = pygame.mixer.music.play()
self.pos = pygame.math.Vector2(pos)
self.rect = self.image.get_rect(center=pos)
def update(self, dt):
# Add the velocity to the position vector to move the sprite
self.pos += self.vel * dt
self.rect.center = self.pos # Update the rect pos.
if not pygame.display.get_surface().get_rect().colliderect(self.rect):
self.kill()
Also, you should only use pygame.mixer.music.play() for playing background music (since you can only play one file at once with music.play()). For sound effects, better use the Sound class.
You can check if the bullet is in the screen and remove it if it isn't.
for bullet in bullets[:]:
if not surface.get_rect().collidepoint(bullet.pos):
#remove the bullet
Alternatively, you can remove it after a set amount of time. Under your __init__ method of the bullet class, you can add start = time.time() to record when it spawns.
class Bullet(pygame.sprite.Sprite):
def __init__(self, pos, direction, *sprite_groups):
super().__init__(*sprite_groups)
self.image = pygame.image.load(os.path.join('images','fireball.png'))
self.rect = self.image.get_rect()
self.sound = pygame.mixer.music.play()
self.pos = pygame.math.Vector2(pos)
self.vel = pygame.math.Vector2(750, 0)
self.direction = direction
self.start = time.time()
Then in your main loop make a variable now = time.time() to keep track of the current time. You can determine how long you bullet has been around by taking away spawn time, which is self.start from now
def kill(self, now, lifetime):
if now - self.start > lifetime:
#remove bullet
I am trying to follow this tutorial https://opensource.com/article/19/11/simulate-gravity-python to allow my sprite to go to the bottom edge of the screen and nothing more. But following the tutorial code to the point where it says:
... Make your gravity function look like this:
def gravity(self):
self.movey += 3.2 # how fast player falls
if self.rect.y > worldy and self.movey >= 0:
self.movey = 0
self.rect.y = worldy-ty
even though the player doesn't stop at the bottom edge of the screen when using gravity.
I tried to add one more ty as suggested in the tutorial where it says
... An easy fix is to make your player sprite bounce higher by adding another -ty to its new Y position after it hits the bottom of the game world:
def gravity(self):
self.movey += 3.2 # how fast player falls
if self.rect.y > worldy and self.movey >= 0:
self.movey = 0
self.rect.y = worldy-ty-ty
so that my code looks like this:
import pygame
import sys
import os
'''
Objects
'''
class Enemy(pygame.sprite.Sprite):
'''
Spawn an enemy
'''
def __init__(self, x, y, img):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load(os.path.join('images', img))
#self.image.convert_alpha()
#self.image.set_colorkey(ALPHA)
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
self.counter = 0
def move(self):
'''
enemy movement
'''
distance = 20
speed = 8
if self.counter >= 0 and self.counter <= distance:
self.rect.x += speed
elif self.counter >= distance and self.counter <= distance * 2:
self.rect.x -= speed
else:
self.counter = 0
self.counter += 1
class Level:
def __init__(self):
self.enemy_list = pygame.sprite.Group() # create enemy group
def bad_1(self, lvl, eloc):
if lvl == 1:
enemy = Enemy(eloc[0],eloc[1],'yeti.png') # spawn enemy
self.enemy_list.add(enemy) # add enemy to group
if lvl == 2:
print("Level " + str(lvl) )
return self.enemy_list
def bad_2(self, lvl, eloc):
if lvl == 1:
enemy = Enemy(eloc[0],eloc[1],'spr.png') # spawn enemy
self.enemy_list.add(enemy) # add enemy to group
if lvl == 2:
print("Level " + str(lvl) )
return self.enemy_list
class Player(pygame.sprite.Sprite):
'''
Spawn a player
'''
def gravity(self):
self.movey += 3.2 # how fast player falls
if self.rect.y > worldy and self.movey >= 0:
self.movey = 0
self.rect.y = worldy-ty-ty-ty-ty-ty-ty
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.movex = 0
self.movey = 0
self.frame = 0
self.health = 10
self.frame = 0
self.images = []
for i in range(1, 5):
img = pygame.image.load(
os.path.join('images', 'hero' + str(i) + '.png')).convert()
img.convert_alpha()
img.set_colorkey(ALPHA)
self.images.append(img)
self.image = self.images[0]
self.rect = self.image.get_rect()
def control(self, x, y):
'''
control player movement
'''
self.movex += x
self.movey += y
def update(self):
'''
Update sprite position
'''
self.rect.x = self.rect.x + self.movex
self.rect.y = self.rect.y + self.movey
# moving left
if self.movex < 0:
self.frame += 1
if self.frame > 3 * ani:
self.frame = 0
self.image = self.images[self.frame // ani]
# collisions
enemy_hit_list = pygame.sprite.spritecollide(self, enemy_list, False)
for enemy in enemy_hit_list:
self.health -= 1
print(self.health)
# moving right
if self.movex > 0:
self.frame += 1
if self.frame > 3 * ani:
self.frame = 0
self.image = self.images[(self.frame // ani)]
'''
Setup
'''
worldx = 560
worldy = 420
fps = 40 # frame rate
ani = 4 # animation cycles
clock = pygame.time.Clock()
pygame.init()
main = True
BLUE = (25, 25, 200)
BLACK = (23, 23, 23)
WHITE = (254, 254, 254)
ALPHA = (0, 255, 0)
world = pygame.display.set_mode([worldx, worldy])
backdrop = pygame.image.load(os.path.join('images', 'stage.png')).convert()
backdropbox = world.get_rect()
player = Player() # spawn player
player.rect.x = 0
player.rect.y = 0
player_list = pygame.sprite.Group()
player_list.add(player)
steps = 10 # how fast to move
l = Level()
eloc = [200,20]
enemy_list = l.bad_1(1, eloc)
eloc = [100,10]
enemy_list = l.bad_2(1, eloc)
'''
Main loop
'''
while main == True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
main = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT or event.key == ord('a'):
player.control(-steps, 0)
if event.key == pygame.K_RIGHT or event.key == ord('d'):
player.control(steps, 0)
if event.key == pygame.K_UP or event.key == ord('w'):
print('jump')
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT or event.key == ord('a'):
player.control(steps, 0)
if event.key == pygame.K_RIGHT or event.key == ord('d'):
player.control(-steps, 0)
if event.key == ord('q'):
pygame.quit()
sys.exit()
main = False
# world.fill(BLACK)
world.blit(backdrop, backdropbox)
player.gravity() # check gravity
player.update()
player_list.draw(world) #refresh player position
enemy_list.draw(world)
for e in enemy_list:
e.move()
pygame.display.flip()
clock.tick(fps)
but the sprite doesn't stop at the bottom edge of the screen (again) and I get the following error when the code breaks by itself on execution:
Traceback (most recent call last):
File "game.py", line 188, in <module>
player.gravity() # check gravity
File "game.py", line 72, in gravity
self.rect.y = worldy-ty-ty-ty-ty-ty-ty
NameError: name 'ty' is not defined
If I try to decrease or increase (depending on how much bigger or smaller) the screen size where it says:
worldx = 560
worldy = 420
I get the following error:
Traceback (most recent call last):
File "game.py", line 188, in <module>
player.gravity() # check gravity
File "game.py", line 72, in gravity
self.rect.y = worldy-ty
NameError: name 'ty' is not defined
I have encountered this question Pygame Gravity Script but comparing I could not understand where the code failure arises.
The original images are from here https://opengameart.org/sites/default/files/opp2_sprites.zip but I have separated in imgur for easy explanation:
content folder images:
For enemy (sprit) is yeti.png: https://imgur.com/GNNcU6z
For background is stage.png: https://imgur.com/YyiEJ0q
and the image of the player:
spr.png: https://imgur.com/1fYXa7Y
In original code on https://opensource.com/article/19/11/simulate-gravity-python is
ty = 64 #tile size
but you don't have it in your code - and it gives your error.
But you could use rect.bottom and then you may not need ty
def gravity(self):
self.movey += 3.2 # how fast player falls
if self.rect.bottom > worldy and self.movey >= 0: # <-- uses bottom
self.movey = 0
self.rect.bottom = worldy # <-- uses bottom
BTW: If you will add plaforms to game then you can create platform at the bottom (below bottom border) and then it should stop player.
BTW: To stop on platform it would need
# collisions
enemy_hit_list = pygame.sprite.spritecollide(self, enemy_list, False)
for enemy in enemy_hit_list:
self.health -= 1
print(self.health)
plat_hit_list = pygame.sprite.spritecollide(self, plat_list, False)
for p in plat_hit_list:
#self.health -= 1
#print(self.health)
self.rect.bottom = p.rect.top
ground_hit_list = pygame.sprite.spritecollide(self, ground_list, False)
for g in ground_hit_list:
#self.health -= 1
#print(self.health)
self.rect.bottom = g.rect.top
It would be good also to reduce gravity speed because in very big speed it may jump below platform without checking collision with platform.
def gravity(self):
self.movey += 3.2 # how fast player falls
#print(self.rect.bottom, worldy)
# reduce speed so it will not jump out platfrom
if self.movey >= 15:
self.movey = 6
if self.rect.bottom > worldy and self.movey >= 0:
self.movey = 0
self.rect.bottom = worldy
#print(self.rect.bottom, worldy)
I've tried everything and cannot get how I check if my two blocks have collided.
Here's my code:
import pygame
import random
pygame.init()
display_width = 600
display_height = 300
class player:#Just info for player
width = 30
height = 30
x = display_width // 2
y = display_height - height - 5
a = x + 30
pic = 'SnakePart.png'
thing = pygame.image.load(pic)
def place(x,y):#function for placing player object
gameDisplay.blit(player.thing, (player.x,player.y))
class enemy:#enemy info class
width = 30
height = 30
x = random.randint(0,display_width - width)
y = 1
a = x + 30
pic = 'Apple.png'
thing = pygame.image.load(pic)
speed = 10#sets speed
def change_x():
enemy.x = random.randint(0,display_width - enemy.width)
def change_y():
enemy.y += enemy.speed
def make(x,y):#set up funtion
gameDisplay.blit(enemy.thing, (x,y))
def respawn():#reseting enemy entity
enemy.y = 1
gameDisplay.blit(enemy.thing, (enemy.x,enemy.y))
player.thing#uses the variables in the classes to set up the images for use
enemy.thing
black = (0,0,0)
white = (255,255,255)
player_height = 30
player_width = 30
clock = pygame.time.Clock()
x_change = 0#This is to make movment
gameDisplay = pygame.display.set_mode((display_width,display_height))
pygame.display.set_caption('Bullet Hell.')
dodged = 0#counter will be used in the more polished vesion.
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
if event.type == pygame.KEYDOWN:#Checks for keypress, to make player entity move
if event.key == pygame.K_RIGHT:
x_change = 5
if event.key == pygame.K_LEFT:
x_change = -5
if event.type == pygame.KEYUP:
if event.key == pygame.K_RIGHT or pygame.K_LEFT:
x_change = 0
player.x += x_change
gameDisplay.fill(black)
enemy.make(enemy.x,enemy.y)
player.place(player.x,player.y)
enemy.change_y()
if enemy.y > display_height:#Brings enemy back to top once it has gotten to th bottom
enemy.change_x()
dodged += 1
enemy.respawn()
pygame.display.update()
clock.tick(60)
It's all really the collision and I think I'll be good. Oh yeah, if you guys could also tell me how to display text that would be great!
Take a look at the pygame.Rect class and its collision detection methods. Give your objects rects as attributes which you get by calling the .get_rect() method of the images/surfaces. Update the coordinates of these rects each frame and in your main loop call player.rect.colliderect(enemy.rect) to check if the two rects collide.
import pygame
import random
pygame.init()
BLACK = pygame.Color('black')
display = pygame.display.set_mode((600, 300))
# Create a rect with the dimensions of the screen at coords (0, 0).
display_rect = display.get_rect()
clock = pygame.time.Clock()
# SNAKE_IMAGE = pygame.image.load('SnakePart.png').convert_alpha()
# APPLE_IMAGE = pygame.image.load('Apple.png').convert_alpha()
# Replacement images.
SNAKE_IMAGE = pygame.Surface((30, 30))
SNAKE_IMAGE.fill((30, 150, 0))
APPLE_IMAGE = pygame.Surface((30, 30))
APPLE_IMAGE.fill((150, 30, 0))
class Player:
def __init__(self):
self.x = display_rect.w // 2
self.y = display_rect.h - 30 - 5
self.image = SNAKE_IMAGE
# Create a rect with the size of the image at coords (0, 0).
self.rect = self.image.get_rect()
# Set the topleft coords of the rect.
self.rect.x = self.x
self.rect.y = self.y
self.x_change = 0
def update(self):
"""Move the player."""
self.x += self.x_change
# Always update the rect, because it's
# needed for the collision detection.
self.rect.x = self.x
def draw(self, display):
display.blit(self.image, self.rect)
class Enemy:
def __init__(self):
self.x = random.randint(0, display_rect.w - 30)
self.y = 1
self.image = APPLE_IMAGE
# You can also pass the coords directly to `get_rect`.
self.rect = self.image.get_rect(topleft=(self.x, self.y))
self.speed = 10
def change_x(self):
self.x = random.randint(0, display_rect.w - self.rect.w)
self.rect.x = self.x
def change_y(self):
self.y += self.speed
self.rect.y = self.y
def draw(self, display):
display.blit(self.image, self.rect)
def reset(self):
"""Reset self.y position."""
self.y = -30
player = Player()
enemy = Enemy()
dodged = 0
done = False
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT:
player.x_change = 5
if event.key == pygame.K_LEFT:
player.x_change = -5
if event.type == pygame.KEYUP:
if event.key == pygame.K_RIGHT or pygame.K_LEFT:
player.x_change = 0
# Game logic.
player.update()
enemy.change_y()
# Brings enemy back to top once it has gotten to th bottom
if enemy.y > display_rect.h:
enemy.change_x()
dodged += 1
enemy.reset()
# Check if the player and the rect collide.
if player.rect.colliderect(enemy.rect):
print('Collided!')
# Draw everything.
display.fill(BLACK)
enemy.draw(display)
player.draw(display)
pygame.display.update()
clock.tick(60)
pygame.quit()
I've changed some more things (btw, better call the images self.image not thing ;)) especially in the classes:
The attributes of the classes should be in __init__ methods to make them instance attributes instead of class attributes (which get shared by all instances). Also, check out how to create instances of your classes.
The methods should all have self as their first parameter (that's a reference to the instance). To access the attributes inside of the classes prepend them with self, e.g. self.x += self.x_change.
The player.x += x_change line fits better inside the Player class' update method, so you can just update the position and other player attributes by calling player.update().
There's a convention that class names should be uppercase MyClass whereas instances should have lowercase names my_instance.
I am building a game and I keep on running up against this error. I can't seem to fix it. I believe the problem is either in the function "main" at the bottom or in the classes "Level" and "Level01". If you find there is a way I can improve my code can you also tell me as I am just learning how to build games with OOP.
File "C:/Users/fabma/Documents/PythonGames/RPG/Scroller!.py", line 148, in main
currentLevel.drawer(display)
TypeError: drawer() missing 1 required positional argument: 'display1'
Here is my code:
import pygame
# Colours + Global constants
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
WIDTH = 800
HEIGHT = 600
SIZE = (WIDTH, HEIGHT)
# CLASSES
# Block is the common platform
class Block(pygame.sprite.Sprite):
def __init__(self, length, height, colour):
super().__init__()
# Making image
self.image = pygame.Surface([length, height])
self.image.fill(colour)
self.rect = self.image.get_rect()
# Setting Y coordinates
self.rect.y = HEIGHT * 0.95
class Player(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
# Is it touching the floor?
self.velocity = 0
self.standing = True
# Rendering image and creating some variables
self.height = 40
self.length = 40
self.sprite_x_change = 0
self.sprite_y_change = 0
self.image = pygame.Surface([self.height, self.length])
self.image.fill(GREEN)
self.rect = self.image.get_rect()
self.rect.y = HEIGHT * 0.884
self.level = None
# Mobility: Left, right, up and stop
def move_right(self):
self.sprite_x_change = 15
def move_left(self):
self.sprite_x_change = -15
def move_up(self, platform):
# Seeing if we hit anything if so then we can jump!
self.rect.y -= 2
hit_list = pygame.sprite.spritecollide(self, platform, False)
if len(hit_list) > 0 or self.rect.bottom >= HEIGHT - Block.height:
self.change_y = -10
def stop(self):
self.sprite_x_change = 0
def updater(self):
self.gravity()
platforms_hit = pygame.sprite.spritecollide(self, self.level.platforms, False)
for blocks in platforms_hit:
self.sprite_y_change = 0
# Going down
if self.sprite_y_change > 0:
self.rect.bottom = blocks.rect.top
self.velocity = 0
self.standing = True
# Going up
if self.sprite_y_change < 0:
self.rect.top = blocks.rect.bottom
self.standing = False
if self.sprite_x_change > 0:
self.rect.right = blocks.rect.left
if self.sprite_x_change < 0:
self.rect.left = blocks.rect.right
if self.sprite_x_change == 0 and self.sprite_y_change == 0:
self.rect.y = HEIGHT * 0.884
if self.standing == False:
self.velocity += 1
self.rect.x += self.sprite_x_change
self.rect.y += self.sprite_y_change
def gravity(self):
self.sprite_y_change += 0.980665*self.velocity
class Level:
def __init__(self):
# Creating groups
self.sprites = pygame.sprite.Group()
self.all_things = pygame.sprite.Group()
self.platforms = pygame.sprite.Group()
def drawer(self, display1):
display1.fill(BLUE)
self.all_things.draw(display1)
class Level01(Level):
def __init__(self, player1):
# Initialise level1
Level.__init__(self)
# Level01 things
block = Block(WIDTH, HEIGHT * 0.05, RED)
Level.all_things = self.all_things
self.sprites.add(player1)
self.platforms.add(block)
self.all_things.add(player1, block)
def main():
# Init pygame
pygame.init()
# Set screen
display = pygame.display.set_mode(SIZE)
# Creating FPS thingy
clock = pygame.time.Clock()
# Making levels + Player
player = Player()
level_1 = Level01(player)
# Choosing level
levelList = []
levelList.append(Level01)
currentLevelNumber = 0
currentLevel = levelList[currentLevelNumber]
# Game loop
loop = True
while loop == True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT:
player.move_right()
if event.key == pygame.K_LEFT:
player.move_left()
if event.key == pygame.K_UP:
player.move_up(currentLevel.platforms)
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT and player.sprite_x_change < 0:
player.stop()
if event.key == pygame.K_RIGHT and player.sprite_x_change > 0:
player.stop()
# Update things
currentLevel.all_things.update()
currentLevel.drawer(display)
# Refresh screen
clock.tick(30)
pygame.display.update()
pygame.quit()
if __name__ == "__main__":
main()
You need to create an instance of your level rather than just appending the class itself into your list:
levelList.append(Level01)
should be...
levelList.append(level_1)
As it stands, you're using the class object rather than an instance of it, which means that the display you're passing is getting put into the self argument (because the class object won't pass along an instance, because it's not one). Once you're calling it on an instance of the class, the self argument will be automatically passed and thus your display argument will get passed to the correct spot.