Pygame 2d tile scrolling edges don't load - python
Im trying to make a 2d game in pygame that kinda works like pokemon. Ive gotten really stuck on a problem now that I dont know how to solve.
When I move my character I scroll the map instead so the player stays centered. I've created an "animation" by offsetting the distance by 2 pixels at a time instead of moving the full tile size so I get smooth movemen. The problem I have is that when I move, the screen doesn't load the new tiles in the edges that i'm moving towards so the edges end up a white space until i've completed the full animation. I'll link my code and hopefully someone can help me :)
TILESIZE = 32
MAP_WIDTH = 25
MAP_HEIGHT = 25
class Player(pygame.sprite.Sprite):
def __init__(self, color, width, height):
# Call the parent class (Sprite) constructor
super().__init__()
self.name = "Player"
self.width = width
self.height = height
self.image = pygame.Surface([width, height])
self.image.fill(color)
self.rect = self.image.get_rect()
self.rect.x = int(TILESIZE * (MAP_WIDTH / 2)) - TILESIZE / 2
self.rect.y = int(TILESIZE * (MAP_HEIGHT / 2)) - TILESIZE / 2
class World:
def __init__(self):
self.shiftX = 0
self.shiftY = 0
self.tile_map = [ [DIRT for w in range(MAP_WIDTH)] for h in range(MAP_HEIGHT)]
for row in range(MAP_HEIGHT):
for column in range(MAP_WIDTH):
try:
if real_map[row + self.shiftY][column + self.shiftX] == 0:
tile = DIRT
elif real_map[row + self.shiftY][column + self.shiftX] == 1:
tile = GRASS
elif real_map[row + self.shiftY][column + self.shiftX] == 2:
tile = WATER
else:
tile = DIRT
self.tile_map[row][column] = tile
except:
self.tile_map[row][column] = WATER
def shiftWorld(self):
for row in range(MAP_HEIGHT):
for column in range(MAP_WIDTH):
try:
if real_map[row + self.shiftY][column + self.shiftX] == 0:
tile = DIRT
elif real_map[row + self.shiftY][column + self.shiftX] == 1:
tile = GRASS
elif real_map[row + self.shiftY][column + self.shiftX] == 2:
tile = WATER
else:
tile = DIRT
self.tile_map[row][column] = tile
except:
self.tile_map[row][column] = WATER
def okToMove(self, key):
if key[K_w]:
if self.tile_map[int(MAP_WIDTH/2 - 1)][int(MAP_HEIGHT/2)] != 2:
return True
elif key[K_s]:
if self.tile_map[int(MAP_WIDTH/2 + 1)][int(MAP_HEIGHT/2)] != 2:
return True
elif key[K_a]:
if self.tile_map[int(MAP_WIDTH/2)][int(MAP_HEIGHT/2) - 1] != 2:
return True
elif key[K_d]:
if self.tile_map[int(MAP_WIDTH/2)][int(MAP_HEIGHT/2) + 1] != 2:
return True
def start_game():
pygame.init()
clock = pygame.time.Clock()
#HÄR KAN VI MÅLA UPP MER
#SCREEN = pygame.display.set_mode((MAP_WIDTH*TILESIZE, MAP_HEIGHT*TILESIZE))
world = World()
SCREEN = pygame.display.set_mode((TILESIZE * (MAP_WIDTH-2), TILESIZE * (MAP_HEIGHT-4)))
running = True
player = Player(BLACK, 32, 32)
sprites = pygame.sprite.Group()
sprites.add(player)
movement = 0
offsetY = 0
offsetX = 0
animation_north = False
animation_south = False
animation_west = False
animation_east = False
while running:
for event in pygame.event.get():
if event.type==QUIT:
pygame.quit()
sys.exit()
elif event.type == pygame.KEYDOWN:
#Get keyinput and do whatever needs to be done
key = pygame.key.get_pressed()
if key[K_ESCAPE]:
pygame.quit()
sys.exit()
if animation_east or animation_north or animation_south or animation_west:
pass
else:
if key[K_w]:
okToMove = world.okToMove(key)
if okToMove == True:
animation_north = True
else:
pass
elif key[K_a]:
okToMove = world.okToMove(key)
if okToMove == True:
animation_west = True
elif key[K_s]:
okToMove = world.okToMove(key)
if okToMove == True:
animation_south = True
elif key[K_d]:
okToMove = world.okToMove(key)
if okToMove == True:
animation_east = True
if animation_north == True:
if movement == 32:
movement = 0
world.shiftY -= 1
world.shiftWorld()
offsetY = 0
animation_north = False
else:
offsetY += 4
movement += 4
if animation_south == True:
if movement == 32:
movement = 0
world.shiftY += 1
world.shiftWorld()
offsetY = 0
animation_south = False
intY = 0
else:
offsetY -= 4
movement += 4
if animation_west == True:
if movement == 32:
movement = 0
world.shiftX -= 1
world.shiftWorld()
offsetX = 0
animation_west = False
else:
offsetX += 4
movement += 4
if animation_east == True:
if movement == 32:
world.shiftX += 1
world.shiftWorld()
movement = 0
offsetX = 0
animation_east = False
else:
offsetX -= 4
movement += 4
SCREEN.fill(WHITE)
for row in range(MAP_HEIGHT):
for column in range(MAP_WIDTH):
SCREEN.blit(textures[world.tile_map[row][column]], (column*TILESIZE + offsetX, row*TILESIZE + offsetY))
sprites.draw(SCREEN)
pygame.display.update()
pygame.display.flip()
clock.tick(60)
start_game()
I am writing a similar game and I will share my logic with you.
my blocks are 32x32 so each block is 32 pixesl.
The outer border is the sprites screen and the inner square is the monitor.
You always have one sprite extra on all sides of the screen. now if you count the pixel movement on any side of the screen it's easy for you to keep track of when you need to draw the next row or column of sprites but not that they are always DRAWN OFF SCREEN. if my pixel movement is -8 (left movement) I draw a column of sprites on the right side just on the border os the screen but OUTSIDE the visible area. Same goes for the other side.
Here is some code from my program. This is the sprite adding code.
def add_sprites(self):
"""sprites are added to the group which appear on screen right. the column number is the value in ColDrawn. We selct columns from the list according to this value. Once the end of the column list is reached we start again from the first one. We cycle thru the list depending on the NumCycle[0] value."""
if self.ColDrawn < self.Columns_in_Dungeon - 1:
self.ColDrawn += 1
else: # all the columns drawn so increment the flag
self.ColDrawn = 0
self.NumCycle[1] += 1
if self.NumCycle[1] >= self.NumCycle[0]: # if the flag is equal to the number of cycles the screen is scrolled then set numcycle[2] to True
self.NumCycle[2] = True
else: # screen can be scrolled
spritecol = self.all_wall_sprite_columns_list[self.ColDrawn]
self.wallspritegroup.add(spritecol) # add column of sprites to the sprite group
return
and here is the sprite removing code.
def remove_sprites(self):
"""sprites are removed from the group as they exit from screen left."""
for sprt in self.wallspritegroup: # remove_basic sprites that move_basic off screen on left
if sprt.rect.x <= -48:
sprt.rect.x = self.screenw # reset the x position and
sprt.kill()
#spritegrp.remove_basic(sprt) # remove_basic the sprite from the sprite group
return
The code is quite easy to follow as I have commented them. Hope this helps.
Related
TypeError: start_new_game() missing 1 required positional argument: 'max_health' [closed]
Closed. This question needs details or clarity. It is not currently accepting answers. Want to improve this question? Add details and clarify the problem by editing this post. Closed 1 year ago. Improve this question So I have been trying to figure out why I keep getting this error, even though everything is run from my game_view.py module. Everytime I press the 1 key to start a game and test to see if the players health bar and hit points works, I get this error message. TypeError: start_new_game() missing 1 required positional argument: 'max_health' The sad part is, is that it reffers me to the start_view which doesn't have anything to do with anything else but running the start screen so players can choose to play solo or with another player. Below is the game_view and start_view so you guys can see where I'm going wrong. I wanted to test this out so this way I can add other enemies, bosses and power-ups later, but running into this error for the past several days is hindering progress. Anyway, code for both will be below. Thanks in advance for any and all help. start_view: import arcade from game_view import GameView class StartView(arcade.View): def on_show(self): # This is run once when we switch to this view arcade.set_background_color(arcade.csscolor.BLACK) # Reset the viewport, necessary if we have a scrolling game arcade.set_viewport(0, self.window.width, 0, self.window.height) def on_draw(self): # Draw this view arcade.start_render() line_height = 70 line_location = self.window.height - line_height * 2 arcade.draw_text("Space Defense Force", self.window.width / 2, line_location, arcade.color.WHITE, font_size=50, anchor_x="center", font_name="SF Atarian System") line_location -= line_height line_location -= line_height arcade.draw_text("1 - Start One Player Game", self.window.width / 2, line_location, arcade.color.WHITE, font_size=20, anchor_x="center", font_name="SF Atarian System") # if len(self.window.joysticks) > 1: # color = arcade.color.WHITE # else: # color = arcade.color.GRAY color = arcade.color.GRAY line_location -= line_height arcade.draw_text("2 - Start Two Player Game", self.window.width / 2, line_location, color, font_size=20, anchor_x="center", font_name="SF Atarian System") line_location -= line_height line_location -= line_height color = arcade.color.WHITE arcade.draw_text("Use joysticks to play, or arrow keys to move and number keys to fire.", self.window.width / 2, line_location, color, font_size=20, anchor_x="center", font_name="SF Atarian System") def on_key_press(self, symbol: int, modifiers: int): if symbol == arcade.key.KEY_1: game_view = GameView() game_view.start_new_game(1) self.window.show_view(game_view) elif symbol == arcade.key.KEY_2: game_view = GameView() game_view.start_new_game(2) self.window.show_view(game_view) game_view: import random import math import arcade from health import Health from game_over_view import GameOverView from typing import cast from arcade.experimental.shadertoy import Shadertoy from constants import * from asteroid_sprite import AsteroidSprite from ship_sprite import ShipSprite from bullet import Bullet from glow_line import GlowLine from glow_ball import GlowBall from explosion import ExplosionMaker from glow_image_sprite import GlowImageSprite from window import Window as window class GameView(arcade.View): # Main application class def __init__(self): super().__init__() # Sprite lists self.player_sprite_list = arcade.SpriteList() self.asteroid_list = arcade.SpriteList() self.bullet_list = arcade.SpriteList() self.ship_life_list = arcade.SpriteList() self.health_list = arcade.SpriteList() # Sounds self.laser_sound = arcade.load_sound(":resources:sounds/hurt5.wav") self.hit_sound1 = arcade.load_sound(":resources:sounds/explosion1.wav") self.hit_sound2 = arcade.load_sound(":resources:sounds/explosion2.wav") self.hit_sound3 = arcade.load_sound(":resources:sounds/hit1.wav") self.hit_sound4 = arcade.load_sound(":resources:sounds/hit2.wav") self.dead_sound = arcade.load_sound(":resources:sounds/gameover2.wav") self.glowball_shadertoy = Shadertoy.create_from_file(self.window.get_size(), "glow_ball.glsl") self.glowline_shadertoy = Shadertoy.create_from_file(self.window.get_size(), "glow_line.glsl") self.explosion_list = [] # for joystick in self.window.joysticks: # joystick.push_handlers(self) def start_new_game(self, player_count, max_health): #Set up the game and initialize the variables self.game_over = False arcade.set_background_color(arcade.csscolor.BLACK) # Sprite lists self.player_sprite_list = arcade.SpriteList() self.asteroid_list = arcade.SpriteList() self.bullet_list = arcade.SpriteList() self.ship_life_list = arcade.SpriteList() self.health_list = arcade.SpriteList() # if len(self.window.joysticks) > 0: # joystick = self.window.joysticks[0] # else: # joystick = None joystick = None player_sprite = ShipSprite(":resources:images/space_shooter/playerShip1_orange.png", SCALE, joystick, player_no=1, player_count=player_count, max_health=5) self.player_sprite_list.append(player_sprite) self.health_list.append(max_health) if player_count > 1: joystick = None # if len(self.window.joysticks) > 1: # joystick = self.window.joysticks[1] # else: # joystick = None player_sprite = ShipSprite(":resources:images/space_shooter/playerShip1_green.png", SCALE, joystick, player_no=2, player_count=player_count, max_health=5 ) self.player_sprite_list.append(player_sprite) self.health_list.append(max_health) # Set up the player for player in self.player_sprite_list: player.score = 0 player.lives = 3 # Set up the little icons that represent the player lives. cur_pos = 10 for i in range(self.player_sprite_list[0].lives): life = arcade.Sprite(":resources:images/space_shooter/playerLife1_orange.png", SCALE) life.center_x = cur_pos + life.width life.center_y = life.height cur_pos += life.width self.ship_life_list.append(life) if len(self.player_sprite_list) > 1: cur_pos = 100 for i in range(self.player_sprite_list[1].lives): life = arcade.Sprite(":resources:images/space_shooter/playerLife1_green.png", SCALE) life.center_x = cur_pos + life.width life.center_y = life.height cur_pos += life.width self.ship_life_list.append(life) # Make the asteroids image_list = (":resources:images/space_shooter/meteorGrey_big1.png", ":resources:images/space_shooter/meteorGrey_big2.png", ":resources:images/space_shooter/meteorGrey_big3.png", ":resources:images/space_shooter/meteorGrey_big4.png") for i in range(STARTING_ASTEROID_COUNT): image_no = random.randrange(4) enemy_sprite = AsteroidSprite(image_list[image_no], SCALE) enemy_sprite.guid = "Asteroid" enemy_sprite.center_y = random.randrange(BOTTOM_LIMIT, TOP_LIMIT) enemy_sprite.center_x = random.randrange(LEFT_LIMIT, RIGHT_LIMIT) enemy_sprite.change_x = random.random() * 2 - 1 enemy_sprite.change_y = random.random() * 2 - 1 enemy_sprite.change_angle = (random.random() - 0.5) * 2 enemy_sprite.size = 4 self.asteroid_list.append(enemy_sprite) def on_draw(self): # Render the screen. # This command has to happen before we start drawing arcade.start_render() # Draw all the sprites. self.asteroid_list.draw() self.ship_life_list.draw() for bullet in self.bullet_list: bullet.draw() self.bullet_list.draw() for explosion in self.explosion_list: explosion.render() self.player_sprite_list.draw() self.health_list.draw() # Put the text on the screen. output = f"Player 1 Score: {self.player_sprite_list[0].score}" arcade.draw_text(output, 10, 40, arcade.color.AMBER, font_size=15, font_name="Arcade") if len(self.player_sprite_list) > 1: output = f"Player 2 Score: {self.player_sprite_list[1].score}" arcade.draw_text(output, 500, 40, arcade.color.AMBER, font_size=15, font_name="Arcade") output = f"Asteroid Count: {len(self.asteroid_list)}" arcade.draw_text(output, 10, 80, arcade.color.AMBER, font_size=15, font_name="Arcade") for player in health_list: player.draw_health_number() player.draw_health_bar() # def on_joybutton_press(self, joystick, button): # # What player is this? # if joystick == self.window.joysticks[0]: # player_sprite = self.player_sprite_list[0] # else: # player_sprite = self.player_sprite_list[1] # if player_sprite.player_no == 1: # color = 255, 128, 128 # else: # color = 128, 255, 128 # if button == 0: # self.fire_circle(color, player_sprite, player_no=player_sprite.player_no) # elif button == 1: # self.fire_line(color, player_sprite, player_no=player_sprite.player_no) # elif button == 2: # bullet_sprite = GlowImageSprite(":resources:images/space_shooter/laserBlue01.png", # SCALE, # glowcolor=arcade.color.WHITE, # shadertoy=self.glowball_shadertoy, # player_no=player_sprite.player_no) # self.set_bullet_vector(bullet_sprite, 10, player_sprite) # arcade.play_sound(self.laser_sound) def on_key_press(self, symbol, modifiers): # Shoot if the player hit the space bar and we aren't respawning. if symbol == arcade.key.LEFT: self.player_sprite_list[0].change_angle = 3 elif symbol == arcade.key.RIGHT: self.player_sprite_list[0].change_angle = -3 elif symbol == arcade.key.UP: self.player_sprite_list[0].thrust = 0.15 elif symbol == arcade.key.DOWN: self.player_sprite_list[0].thrust = -.2 elif symbol == arcade.key.KEY_1: color = (255, 128, 128) self.fire_circle(color, self.player_sprite_list[0], player_no=0) elif symbol == arcade.key.KEY_2: color = (128, 255, 128) self.fire_circle(color, self.player_sprite_list[0], player_no=0) elif symbol == arcade.key.KEY_3: color = (128, 128, 255) self.fire_circle(color, self.player_sprite_list[0], player_no=0) elif symbol == arcade.key.KEY_4: color = (255, 128, 255) self.fire_circle(color, self.player_sprite_list[0], player_no=0) elif symbol == arcade.key.KEY_5: color = (255, 255, 255) self.fire_line(color, self.player_sprite_list[0], player_no=0) elif symbol == arcade.key.KEY_6: color = (64, 255, 64) self.fire_line(color, self.player_sprite_list[0], player_no=0) elif symbol == arcade.key.KEY_7: bullet_sprite = GlowImageSprite(":resources:images/space_shooter/laserBlue01.png", SCALE, glowcolor=arcade.color.WHITE, shadertoy=self.glowball_shadertoy, player_no=0) self.set_bullet_vector(bullet_sprite, 13, self.player_sprite_list[0]) arcade.play_sound(self.laser_sound) def fire_circle(self, bullet_color, player_sprite, player_no): bullet_sprite = GlowBall(glowcolor=bullet_color, radius=5, shadertoy=self.glowball_shadertoy, player_no=player_no) self.set_bullet_vector(bullet_sprite, 5, player_sprite) arcade.play_sound(self.laser_sound) def fire_line(self, bullet_color, player_sprite, player_no): bullet_sprite = GlowLine(glowcolor=bullet_color, shadertoy=self.glowline_shadertoy, player=player_sprite, player_no=player_no) self.set_bullet_vector(bullet_sprite, 13, player_sprite) arcade.play_sound(self.laser_sound) def set_bullet_vector(self, bullet_sprite, bullet_speed, player_sprite): bullet_sprite.change_y = \ math.cos(math.radians(player_sprite.angle)) * bullet_speed bullet_sprite.change_x = \ -math.sin(math.radians(player_sprite.angle)) \ * bullet_speed bullet_sprite.center_x = player_sprite.center_x bullet_sprite.center_y = player_sprite.center_y self.bullet_list.append(bullet_sprite) def on_key_release(self, symbol, modifiers): # Called whenever a key is released if symbol == arcade.key.LEFT: self.player_sprite_list[0].change_angle = 0 elif symbol == arcade.key.RIGHT: self.player_sprite_list[0].change_angle = 0 elif symbol == arcade.key.UP: self.player_sprite_list[0].thrust = 0 elif symbol == arcade.key.DOWN: self.player_sprite_list[0].thrust = 0 def split_asteroid(self, asteroid: AsteroidSprite): # Split an asteroid into chunks x = asteroid.center_x y = asteroid.center_y if asteroid.size == 4: for i in range(3): image_no = random.randrange(2) image_list = [":resources:images/space_shooter/meteorGrey_med1.png", ":resources:images/space_shooter/meteorGrey_med2.png"] enemy_sprite = AsteroidSprite(image_list[image_no], SCALE * 1.5) enemy_sprite.center_y = y enemy_sprite.center_x = x enemy_sprite.change_x = random.random() * 2.5 - 1.25 enemy_sprite.change_y = random.random() * 2.5 - 1.25 enemy_sprite.change_angle = (random.random() - 0.5) * 2 enemy_sprite.size = 3 self.asteroid_list.append(enemy_sprite) self.hit_sound1.play() elif asteroid.size == 3: for i in range(3): image_no = random.randrange(2) image_list = [":resources:images/space_shooter/meteorGrey_small1.png", ":resources:images/space_shooter/meteorGrey_small2.png"] enemy_sprite = AsteroidSprite(image_list[image_no], SCALE * 1.5) enemy_sprite.center_y = y enemy_sprite.center_x = x enemy_sprite.change_x = random.random() * 3 - 1.5 enemy_sprite.change_y = random.random() * 3 - 1.5 enemy_sprite.change_angle = (random.random() - 0.5) * 2 enemy_sprite.size = 2 self.asteroid_list.append(enemy_sprite) self.hit_sound2.play() elif asteroid.size == 2: for i in range(3): image_no = random.randrange(2) image_list = [":resources:images/space_shooter/meteorGrey_tiny1.png", ":resources:images/space_shooter/meteorGrey_tiny2.png"] enemy_sprite = AsteroidSprite(image_list[image_no], SCALE * 1.5) enemy_sprite.center_y = y enemy_sprite.center_x = x enemy_sprite.change_x = random.random() * 3.5 - 1.75 enemy_sprite.change_y = random.random() * 3.5 - 1.75 enemy_sprite.change_angle = (random.random() - 0.5) * 2 enemy_sprite.size = 1 self.asteroid_list.append(enemy_sprite) self.hit_sound3.play() elif asteroid.size == 1: self.hit_sound4.play() def on_update(self, x, delta_time): # Move everything self.asteroid_list.update() self.bullet_list.update() self.player_sprite_list.update() self.health_list.update() explosion_list_copy = self.explosion_list.copy() for explosion in explosion_list_copy: explosion.update(x) if explosion.time > .9: self.explosion_list.remove(explosion) for bullet in self.bullet_list: hit_list = arcade.check_for_collision_with_list(bullet, self.player_sprite_list) # If it did hit, get rid of sprite if len(hit_list) > 0: bullet.remove_from_lists() for player in hit_list: if not isinstance(player, ShipSprite): raise TypeError("List contents must be all ints") # Remove one health point player.cur_health -= 1 # Check Health if player.cur_health <= 0: arcade.play_sound(self.dead_sound) view=GameOverView self.window.show_view(view) else: # Not Dead arcade.play_sound(self.hit_sound1) assert isinstance(bullet, Bullet) asteroids = arcade.check_for_collision_with_list(bullet, self.asteroid_list) if len(asteroids) > 0: explosion = ExplosionMaker(self.window.get_size(), bullet.position) self.explosion_list.append(explosion) for asteroid in asteroids: assert isinstance(asteroid, AsteroidSprite) self.player_sprite_list[bullet.player_no - 1].score += 1 self.split_asteroid(cast(AsteroidSprite, asteroid)) # expected AsteroidSprite, got Sprite instead asteroid.remove_from_sprite_lists() bullet.remove_from_sprite_lists() # Remove bullet if it goes off-screen size = max(bullet.width, bullet.height) if bullet.center_x < 0 - size: bullet.remove_from_sprite_lists() if bullet.center_x > SCREEN_WIDTH + size: bullet.remove_from_sprite_lists() if bullet.center_y < 0 - size: bullet.remove_from_sprite_lists() if bullet.center_y > SCREEN_HEIGHT + size: bullet.remove_from_sprite_lists() for player in self.player_sprite_list: assert isinstance(player, ShipSprite, max_health) if not player.respawning: asteroids = arcade.check_for_collision_with_list(player, self.asteroid_list) if len(asteroids) > 0: if player.lives > 0: player.lives -= 1 player.respawn() self.split_asteroid(cast(AsteroidSprite, asteroids[0])) asteroids[0].remove_from_sprite_lists() self.ship_life_list.pop().remove_from_sprite_lists() elif len(asteroids) > 0: if player.health > 0: player.health -=1 player.respawn() self.split_asteroid(cast(AsteroidSprite, asteroids[0])) asteroids[0].remove_from_sprite_lists() self.ship_list_list.pop().remove_from_sprite_lists() else: arcade.play_sound(self.dead_sound) view = GameOverView() self.window.show_view(view) Sorry in advance if I have tons of code to go through. But like I said I am adding some features that wasn't in the Arcade Repository from github. I even set a separte module for the health bar and hit points so this way I can see what conflicts. But the error above has been a royal pain.Thanks again. P.S. Remarked out joysticks because it was conflicting with another module I built.
As your title says, your start game function requires 2 arguments: player_count and max_healh. And in your code, when using start_new_game, you aren't supplying the max_health argument, thus raising an error. (function) start_new_game(self, player_count, max_health)
Jumping creates a distance between man and the floor Pygame [duplicate]
This question already has answers here: How to simulate Jumping in Pygame for this particular code (1 answer) jumping too fast? (1 answer) How to make a circular object jump using pygame? [duplicate] (2 answers) Pygame Bouncy Ball Sinks Through Floor (1 answer) Closed 2 years ago. The full code: import glob import pygame import os os.environ['SDL_VIDEO_WINDOW_POS'] = "%d,%d" % (100, 40) class Man(pygame.sprite.Sprite): # just taking random image to get height and width after trans scale to be able to crop later (see lines 23/36) idle_images_list = glob.glob(r"C:\Users\aviro\Desktop\מחשבים\python projects\platform game\animation\fighter\PNG\PNG Sequences\Firing"+'\*') random_image = pygame.image.load(idle_images_list[0]) new_img_width = int(random_image.get_width()/2.5) new_img_height = int(random_image.get_height()/2.5) random_image_after_crop = random_image.subsurface((70, 35, new_img_width-125, new_img_height-45)) # to be able to use needed images indexes # getting idle animations list right_idle_list = [] left_idle_list = [] for images in idle_images_list: img = pygame.image.load(images) wid = img.get_width() hei = img.get_height() img = pygame.transform.scale(img, (int(wid/2.5), int(hei/2.5))) img = img.subsurface((70, 35, new_img_width-125, new_img_height-45)) right_idle_list.append(img) left_idle_list.append(pygame.transform.flip(img, True, False)) # getting movement animations list walk_animation_list = glob.glob(r"C:\Users\aviro\Desktop\מחשבים\python projects\platform game\animation\fighter\PNG\PNG Sequences\Run Firing"+'\*') walk_right_list = [] walk_left_list = [] for files in walk_animation_list: # setting the animation list img = pygame.image.load(files) wid = img.get_width() hei = img.get_height() img = pygame.transform.scale(img, (int(wid/2.5), int(hei/2.5))) # chaging scale img = img.subsurface((70, 35, new_img_width-125, new_img_height-45)) walk_right_list.append(img) walk_left_list.append(pygame.transform.flip(img, True, False)) # mirror list def __init__(self, x, y,): super(Man, self).__init__() self.walk_left_list = Man.walk_left_list self.walk_right_list = Man.walk_right_list self.width = Man.new_img_width self.height = Man.new_img_height self.hitbox = (x+55, y+35, self.width-125, self.height-45) # nothing special on those num, just Trial and error self.rect = Man.random_image_after_crop.get_rect() self.rect.x = x self.rect.y = y def game_redraw(): # print things in end for main loop global right global left screen.blit(bg_image, (0, 0)) if right: screen.blit(man.walk_right_list[frame_count//3], (man.rect.x, man.rect.y)) elif left: screen.blit(man.walk_left_list[frame_count//3], (man.rect.x, man.rect.y)) else: if last_action == "right": screen.blit(man.right_idle_list[frame_count//13], (man.rect.x, man.rect.y)) if last_action == "left": screen.blit(man.left_idle_list[frame_count//13], (man.rect.x, man.rect.y)) else: # just for the first move screen.blit(man.right_idle_list[frame_count//13], (man.rect.x, man.rect.y)) pygame.draw.rect(screen, RED, man.rect, 4) pygame.display.flip() right = False left = False def input_process(key): global right global left global last_action global man global frame_count global is_jump global neg global jump_count # if is_jump: if jump_count >= -10: # check if jumping if jump_count < 0: neg = -1 man.rect.y -= (jump_count ** 2) * neg * 0.5 jump_count -= 1 else: neg = 1 is_jump = False # if key[pygame.K_RIGHT] and man.rect.right + speed < w: if left: frame_count = 0 right = True left = False last_action = "right" man.rect.x += speed # if key[pygame.K_LEFT] and man.rect.left + speed > 0: if right: frame_count = 0 right = False left = True last_action = "left" man.rect.x -= speed # if not is_jump: if key[pygame.K_UP]: jump_count = 10 is_jump = True w = 1728 h = 972 pygame.init() RED = (255, 0, 0) images_folder = r'C:\Users\aviro\Desktop\מחשבים\python projects\platform game\images'+'\\' bg_image = images_folder+"background.png" # setting background image ground_height = h-143 # the high of the image ground man = Man(200, ground_height) man.rect.bottom = ground_height clock = pygame.time.Clock() Refresh_Rate = 54 speed = 5 right = False left = False frame_count = 0 finish = False last_action = "" # jumping ver is_jump = False jump_count = 10 neg = 1 # screen = pygame.display.set_mode((w, h)) bg_image = pygame.image.load(bg_image).convert() # can do convert only after setting surface. # main loop while not finish: if frame_count >= 51: frame_count = 0 for events in pygame.event.get(): if events.type == pygame.QUIT: finish = True key = pygame.key.get_pressed() input_process(key) game_redraw() frame_count += 1 clock.tick(Refresh_Rate) I'm trying to create a simple shooting-platform game, using pygame module. Everything works fine except the jumping. See in line 88. The player is jumping like an x^2 parabola. I added the *0.5 at the end of the line to make the player jump slower and lower, but when I do it this is what happened. Before jumping: After jumping: Look in the second picture. There's a distance between the floor and the player. When I remove the *0.5 everything works fine. Why?
Trying to delete sprite with kill(), but sprite isn't disappearing
This is my first time asking here, so sorry if I don't ask very well. Lately I've been trying to make a Terraria-type game where you can break/place blocks in a randomly generated landscape. While trying to implement the breaking-block mechanic (which is triggered by clicking on a block), I ran into an issue. The block no longer became solid (that's a good thing), but the block's image is still there, and I can walk right through it. A visual example: Before breaking block vs. After breaking block Here's the code Main file (hopefully only the relevant bits): # Imports import pygame as pg import json import sys import random import os from settings import * from world_handler import * # Initialize pygame pg.mixer.pre_init() pg.init() # Fonts # NOTE: put fonts in here # Helper functions def load_image(file_path): # Loads an image img = pg.image.load(file_path) return img def play_sound(sound, loops=0, maxtime=0, fade_ms=0): # Plays some audio if sound_on: sound.play(loops, maxtime, fade_ms) def play_music(): # Plays background music if sound_on: pg.mixer.music.play(-1) # File paths current_path = os.path.dirname(__file__) assets_path = os.path.join(current_path, "assets") image_path = os.path.join(assets_path, "img") # Images player_standing = load_image((os.path.join(image_path, "player", "standing", "player-standing.png"))) player_walking1 = load_image((os.path.join(image_path, "player", "walking", "player-walking1.png"))) player_walking2 = load_image((os.path.join(image_path, "player", "walking", "player-walking2.png"))) player_walking3 = load_image((os.path.join(image_path, "player", "walking", "player-walking3.png"))) player_walking4 = load_image((os.path.join(image_path, "player", "walking", "player-walking4.png"))) player_jumping = load_image((os.path.join(image_path, "player", "jumping", "player-jumping.png"))) player_images = {"walking": [player_walking1, player_walking2, player_walking3, player_walking4], "jumping": player_jumping, "standing": player_standing} block_images = {"Grass": load_image((os.path.join(image_path, "blocks", "grass.png"))), "Dirt": load_image((os.path.join(image_path, "blocks", "dirt.png"))), "Stone": load_image((os.path.join(image_path, "blocks", "stone.png")))} cursor_tracker = load_image((os.path.join(image_path, "misc", "clear-single-pixel.png"))) class Entity(pg.sprite.Sprite): def __init__(self, x, y, image): # Initialize an entity super().__init__() self.image = image self.rect = self.image.get_rect() self.rect.x = x self.rect.y = y self.vx = 0 self.vy = 0 def apply_gravity(self, world): # Let the enemy be affected by gravity self.vy += world.gravity self.vy = min(self.vy, world.terminal_velocity) class Block(Entity): def __init__(self, x, y, image): # Initialize the block super().__init__(x, y, image) class Cursor(Entity): def __init__(self, x, y, image): # Initialize the invisible mouse cursor object # This will be used to track where the mouse goes and if the mouse is on a block super().__init__(x, y, image) self.on_block = False def follow_mouse(self): # Make object follow the mouse self.mouse_x, self.mouse_y = pg.mouse.get_pos() self.rect.x = self.mouse_x self.rect.y = self.mouse_y def detect_block_collision(self, world): # Detects collsion between cursor tracker and a block hit_list = pg.sprite.spritecollide(self, world.blocks, True) if len(hit_list) > 0: pass def update(self, world): # Update the cursor object self.follow_mouse() world.active_sprites.add(self) class Player(Entity): def __init__(self, images): # Initialize the player super().__init__(0, 0, images["standing"]) # Images in each direction self.image_standing_right = images["standing"] self.image_standing_left = pg.transform.flip(self.image_standing_right, 1, 0) self.images_walking_right = images["walking"] self.images_walking_left = [pg.transform.flip(img, 1, 0) for img in self.images_walking_right] self.image_jumping_right = images["jumping"] self.image_jumping_left = pg.transform.flip(self.image_jumping_right, 1, 0) # Player variables self.running_images = self.images_walking_right self.image_index = 0 self.steps = 0 self.speed = 3.5 self.jump_power = 12 self.vx = 0 self.vy = 0 self.direction = "right" self.on_ground = True self.score = 0 self.health = 100 self.max_health = 100 self.invincibility = 0 def move_left(self): # Move to the left self.vx = -self.speed + 0.9 self.direction = "left" def move_right(self): # Move to the rightS self.vx = self.speed self.direction = "right" def stop(self): # Stop it right there self.vx = 0 def jump(self, blocks): # Jump up, jump up, and get down self.rect.y += 1 hit_list = pg.sprite.spritecollide(self, blocks, False) if len(hit_list) > 0: self.vy = -1 * self.jump_power self.rect.y -= 1 def check_world_boundaries(self, world): # Make sure the player doesn"t walk off the world if self.rect.left < 0: self.rect.left = 0 elif self.rect.right > world.width: self.rect.right = world.width def move_and_process_blocks(self, blocks): # Detect block collisions # Block side collisions self.rect.x += self.vx hit_list = pg.sprite.spritecollide(self, blocks, False) for block in hit_list: if self.vx > 0: self.rect.right = block.rect.left self.vx = 0 elif self.vx < 0: self.rect.left = block.rect.right self.vx = 0 self.on_ground = False # Block top and bottom collisions self.rect.y += self.vy + 1 # The +1 isn"t necessary, but it helps hit_list = pg.sprite.spritecollide(self, blocks, False) for block in hit_list: if self.vy > 0: self.rect.bottom = block.rect.top self.on_ground = True self.vy = 0 elif self.vy < 0: self.rect.top = block.rect.bottom self.on_ground = True self.vy = 0 def set_image(self): # Set images and animate if self.on_ground: if self.vx != 0: if self.direction == "right": self.walking_images = self.images_walking_right elif self.direction == "left": self.walking_images = self.images_walking_left self.steps = (self.steps + 1) % self.speed if self.steps == 0: self.image_index = (self.image_index + 1) % len(self.walking_images) self.image = self.walking_images[self.image_index] else: if self.direction == "right": self.image = self.image_standing_right elif self.direction == "left": self.image = self.image_standing_left else: if self.direction == "right": self.image = self.image_jumping_right elif self.direction == "left": self.image = self.image_jumping_left def die(self): # D E D pass def check_block_breaks(self, blocks): # Break a block # mouse_pos = pg.mouse.get_pos() # for block in blocks: # if block.rect.collidepoint(mouse_pos): # print("hi") pass def respawn(self, world): # Hey, you"re back! self.rect.x = world.start_x self.rect.y = world.start_y self.health = self.max_health self.invincibility = 0 self.direction = "right" def update(self, world): # Constantly update the player self.apply_gravity(world) self.move_and_process_blocks(world.blocks) self.check_world_boundaries(world) self.set_image() self.check_block_breaks(world.blocks) if self.health > 0: if self.invincibility > 0: self.invincibility -= 1 else: self.die() class Game(): def __init__(self): # Initialize the game itself self.window = pg.display.set_mode([WINDOWWIDTH, WINDOWHEIGHT]) pg.display.set_caption(TITLE) self.clock = pg.time.Clock() self.done = False self.reset() def start(self): # Start the whole thing up self.world = World(worlds[self.current_world]) self.cursor = Cursor(0, 0, cursor_tracker) self.world.reset() self.player.respawn(self.world) def reset(self): # Reset the game self.player = Player(player_images) self.current_world = 0 self.start() def update(self): # Update things in the game self.player.update(self.world) self.cursor.update(self.world) if self.player.health <= 0: self.player.respawn(self.world) def calculate_offset(self): # Calculate x/y coordinates after screen scrolls x = -1 * self.player.rect.centerx + WINDOWWIDTH / 2 if self.player.rect.centerx < WINDOWWIDTH / 2: x = 0 elif self.player.rect.centerx > self.world.width - WINDOWWIDTH / 2: x = -1 * self.world.width + WINDOWWIDTH y = -1 * self.player.rect.centery + WINDOWHEIGHT / 2 if self.player.rect.centery < WINDOWHEIGHT / 2: y = 0 elif self.player.rect.centery > self.world.height - WINDOWHEIGHT / 2: y = -1 * self.world.height + WINDOWHEIGHT return x, y def draw(self): # Draw sprites to the screen self.offset_x, self.offset_y = self.calculate_offset() self.world.active_layer.fill(TRANSPARENT) self.world.active_sprites.draw(self.world.active_layer) if self.player.invincibility % 3 < 2: self.world.active_layer.blit(self.player.image, [self.player.rect.x, self.player.rect.y]) self.window.blit(self.world.background_layer, [self.offset_x / 3, self.offset_y]) self.window.blit(self.world.inactive_layer, [self.offset_x, self.offset_y]) self.window.blit(self.world.active_layer, [self.offset_x, self.offset_y]) self.offset_cursor_x = self.cursor.rect.x - self.offset_x self.offset_cursor_y = self.cursor.rect.y - self.offset_y self.cursor.rect.x = self.offset_cursor_x self.cursor.rect.y = self.offset_cursor_y pg.display.update(0, 0, WINDOWWIDTH, WINDOWHEIGHT) def process_events(self): # Handle events (key presses, mouse clicks, etc) for event in pg.event.get(): if event.type == pg.QUIT: self.done = True elif event.type == pg.KEYDOWN: # Jump if event.key == JUMP: self.player.jump(self.world.blocks) # Debug reset elif event.key == pg.K_r: self.reset() # Debug close elif event.key == pg.K_q: self.done = True # Break a block if you click on it if event.type == pg.MOUSEBUTTONDOWN: # for block in self.world.blocks: # if block.rect.collidepoint(self.offset_cursor_x, self.offset_cursor_y): # block.kill() self.cursor.detect_block_collision(self.world) pressed = pg.key.get_pressed() if pressed[LEFT]: self.player.move_left() elif pressed[RIGHT]: self.player.move_right() else: self.player.stop() def loop(self): # Loop through essential functions while not self.done: self.process_events() self.update() self.draw() self.clock.tick(FPS) if __name__ == "__main__": # Begin the loop and pre-initialize the game game = Game() game.start() game.loop() pg.quit() sys.exit() World Handler (generated blocks and adds them to groups): Some parts are commented out because they serve no purpose yet, but will soon. # Imports import pygame as pg import json import random import os from settings import * from main import * # Initialize pygame pg.init() class World(): def __init__(self, file_path): # Initialize the world # Starting entities self.starting_blocks = [] # Entity groups self.blocks = pg.sprite.Group() # Sprite groups (active/inactive) self.active_sprites = pg.sprite.Group() self.inactive_sprites = pg.sprite.Group() # Read the world json file with open(file_path, "r") as f: data = f.read() map_data = json.loads(data) # World width and height self.width = map_data["width"] * GRID_SIZE self.height = map_data["height"] * GRID_SIZE # Player start position self.start_x = map_data["start"][0] * GRID_SIZE self.start_y = map_data["start"][1] * GRID_SIZE # Load blocks for item in map_data["blocks"]: x, y = item[0] * GRID_SIZE, item[1] * GRID_SIZE img = block_images[item[2]] self.starting_blocks.append(Block(x, y, img)) # Layers self.background_layer = pg.Surface([self.width, self.height], pg.SRCALPHA, 32) self.inactive_layer = pg.Surface([self.width, self.height], pg.SRCALPHA, 32) self.active_layer = pg.Surface([self.width, self.height], pg.SRCALPHA, 32) # Load background color if map_data["bg-color"] != "": self.background_layer.fill(map_data["bg-color"]) # Load background image # if map_data["bg-image"] != "": # bg_image = pg.image.load(map_data["bg-image"]).convert_alpha() # # if map_data["bg-fill-y"]: # h = bg_image.get_height() # w = int(bg_image.get_width() * WINDOWHEIGHT / h) # bg_image = pg.transform.scale(bg_image, (w, WINDOWHEIGHT)) # # if "top" in map_data["bg-position"]: # start_y = 0 # elif "bottom" in map_data["bg-postion"]: # start_y = self.height = bg_image.get_height() # # if map_data["bg-repeat-x"]: # for x in range(0, self.width, bg_image.get_width()): # self.background_layer.blit(bg_image, [x, start_y]) # else: # self.background_layer.blit(bg_image, [0, start_y]) # Load background music # pg.mixer.music.load(map_data["music"]) # Set the world's gravity strength and terminal velocity self.gravity = map_data["gravity"] self.terminal_velocity = map_data["terminal-velocity"] # Grass generator if map_data["gen-type"] == "earth": gen_loop = map_data["width"] x = 0 y = 56 * GRID_SIZE # The general y coordinate for block placing y_gen = 56 * GRID_SIZE # Stored to be referenced in order to make a smoother landscape for i in range (0, map_data["width"]): # Generate grass img = block_images["Grass"] self.starting_blocks.append(Block(x, y, img)) y += GRID_SIZE # Generate dirt for i in range(0, 6): img = block_images["Dirt"] self.starting_blocks.append(Block(x, y, img)) y += GRID_SIZE # Generate stone for i in range(1, int((self.height / GRID_SIZE))): img = block_images["Stone"] self.starting_blocks.append(Block(x, y, img)) y += GRID_SIZE y = y_gen x += GRID_SIZE gen_loop -= 1 # Randomly decide what the next grass' y will be in relation to the previous one random_grass = random.randint(0, 5) # The lowest point you'll find a block of grass lowest_grass_y = 53 * GRID_SIZE # How extreme the changes in block heights will be # 0 is flat, 1 will have pretty smooth terrain, while something like 10 would be super steep gen_extremity = 1 # Keep the grass at the same y if random_grass == 0 or random_grass == 1 or random_grass == 2 or random_grass == 3: gen_loop -= 1 if y <= lowest_grass_y: y += GRID_SIZE # Increase y elif random_grass == 4: y_gen += GRID_SIZE * gen_extremity if y <= lowest_grass_y: y += GRID_SIZE # Decrease y elif random_grass == 5: y_gen -= GRID_SIZE * gen_extremity if y <= lowest_grass_y: y += GRID_SIZE else: raise ValueError("How did we get here? Grass generator somehow generated an invalid number.") # Add starting entities to their groups self.blocks.add(self.starting_blocks) # Add sprites to inactive/active sprite groups self.inactive_sprites.add(self.blocks) # Does... something? for s in self.active_sprites: s.image.convert() for s in self.inactive_sprites: s.image.convert() # Draw inactive sprites to the inactive layer self.inactive_sprites.draw(self.inactive_layer) # Convert layers self.background_layer.convert() self.inactive_layer.convert() self.active_layer.convert() Setting file (to help you recreate the issue and find out what's going on # Imports import pygame as pg import os # File paths current_path = os.path.dirname(__file__) assets_path = os.path.join(current_path, 'assets') image_path = os.path.join(assets_path, 'img') # Window settings TITLE = "Mooncraft" WINDOWWIDTH = 960 WINDOWHEIGHT = 640 FPS = 60 GRID_SIZE = 32 # Options sound_on = True # Controls LEFT = pg.K_a RIGHT = pg.K_d JUMP = pg.K_SPACE # Colors TRANSPARENT = (0, 0, 0, 0) WHITE = (255, 255, 255) BLACK = (0, 0, 0) RED = (255, 0, 0) GREEN = (0, 255, 0) BLUE = (0, 0, 255) # World files worlds = [(os.path.join(assets_path, 'worlds', 'earth.json'))] If someone could explain what I'm doing wrong, it would be much appreciated. Update 2: Added player class and settings file to help anyone willing to assist me find the issue.
From just a glance, it looks like you might not be clearing the screen at any point in the update loop, but rather drawing over what was already there. This would result in the block still being visible, but not actually there. Try adding screen.fill(#Color here) before flipping the display. Also try using pygame.display.update() instead of pygame.display.flip()
You can do this: all_sprites = pg.sprite.Group() all_sprites.add(your_sprite) #your event: all_sprites.remove(your_sprite)
Scrolling camera
I've been tinkering with a scrolling camera that follows the player around and have got it to follow the player. The problem is that it moves slower than the player and because of this the player wont stay in the middle of the screen. I believe the problem is in the offset (cameraX) and have tried a few different values but haven't found any that work. Code: import pygame, sys, time, random, math from pygame.locals import * BACKGROUNDCOLOR = (255, 255, 255) WINDOWW = 800 WINDOWH = 600 PLAYERW = 66 PLAYERH = 22 FPS = 60 MOVESPEED = 3 YACCEL = 0.13 GRAVITY = 2 BLOCKSIZE = 30 pygame.init() screen = pygame.display.set_mode((WINDOWW, WINDOWH), 0, 32) mainClock = pygame.time.Clock() testLevel = [ (1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,), (1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,), (1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,), (1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,), (1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,), (1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,), (1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,), (1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,), (1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,), (1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,), (1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,), (1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,), (1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,), (1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,), (1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,), (1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,), (1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,), (1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,), (1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,), (1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,)] def createblock(length, height, color): tmpblock = pygame.Surface((length, height)) tmpblock.fill(color) tmpblock.convert() return tmpblock def terminate(): # Used to shut down the software pygame.quit() sys.exit() def add_level(lvl, bSize): # Creates the level based on a map (lvl) and the size of blocks bList = [] # List of every block bListDisp = [] # List of every block to display bTypeList = [] # List with corresponding type of block(wall, air, etc.) for y in range(len(lvl)): for x in range(len(lvl[0])): if lvl[y][x] == 0: # If the block type on lvl[y][x] is '0', write "air" down in the type list bTypeList.append("air") elif lvl[y][x] == 1: # If the block type on lvl[y][x] is '1', write "wall" down in the type list bTypeList.append("solid") bList.append(pygame.Rect((bSize * x), (bSize * y), bSize, bSize)) #Append every block that is registered bListDisp.append(pygame.Rect((bSize * x), (bSize * y), bSize, bSize)) #Append every block to display that is registered return bList, bListDisp, bTypeList player = pygame.Rect((WINDOWW/2), (WINDOWH - BLOCKSIZE*3), PLAYERW, PLAYERH) wallblock = createblock(BLOCKSIZE, BLOCKSIZE,(20,0,50)) lastTime = pygame.time.get_ticks() isGrounded = False vx = 0 vy = 0 allLevels = [testLevel] # A list containing all lvls(only one for now) maxLevel = len(allLevels) # Checks which level is the last currLevel = allLevels[0] # Current level(start with the first lvl) blockList, blockListDisp, blockTypeList = add_level(currLevel, BLOCKSIZE) # A list with every block and another list with the blocks types thrusters = True jumping = False falling = True while True: """COLLISION""" collision = False for i in range(len(blockTypeList)): if blockTypeList[i] == "solid": if player.colliderect(blockList[i]): collision = True if vx > 0 and not falling: player.right = blockListDisp[i].left vx = 0 print('Collide Right') if vx < 0 and not falling: player.left = blockListDisp[i].right vx = 0 print('Collide Left') if vy > 0: player.bottom = blockListDisp[i].top isGrounded = True falling = False vy = 0 print('Collide Bottom') if vy < 0: player.top = blockListDisp[i].bottom vy = 0 print('Collide Top') else: player.bottom += 1 if player.colliderect(blockList[i]): collision = True #isGrounded = True #falling = False player.bottom -= 1 if not collision: falling = True isGrounded = False # Input pressedKeys = pygame.key.get_pressed() # Checks which keys are being pressed timeDiff = pygame.time.get_ticks() - lastTime # Calculates time difference lastTime += timeDiff # Last time checked reset to current time # Shut-down if the ESC-key is pressed or the window is "crossed down" for event in pygame.event.get(): if event.type == QUIT or event.type == KEYDOWN and event.key == K_ESCAPE: terminate() """X-axis control""" if pressedKeys[ord('a')]: vx = -MOVESPEED if pressedKeys[ord('d')]: vx = MOVESPEED if not pressedKeys[ord('d')] and not pressedKeys[ord('a')]: vx = 0 """Y-axis control""" # Controls for jumping if pressedKeys[ord('w')] and thrusters == True: vy -= YACCEL * timeDiff; # Accelerate along the y-xis when "jumping", but not above/below max speed if vy <= -4: vy = -4 isGrounded = False # You are airborne jumping = True # You are jumping if event.type == KEYUP: # If you let go of the "jump"-button, stop jumping if event.key == ord('w') and vy < 0 and not isGrounded: jumping = False falling = True player.x += vx player.y += vy cameraX = player.x - WINDOWW/2 # Gravity if not isGrounded or falling: vy += 0.3 if vy > 80: vy = 80 screen.fill(BACKGROUNDCOLOR) for i in range(len(blockTypeList)): if blockTypeList[i] == "solid": screen.blit(wallblock, (blockListDisp[i].x-cameraX, blockListDisp[i].y)) #blit the wall-block graphics pygame.draw.rect(screen, (0, 0, 0), player) pygame.display.update() mainClock.tick(FPS)
You don't apply the camera-offset to the player itself, only to the wallblocks. So change pygame.draw.rect(screen, (0, 0, 0), player) to pygame.draw.rect(screen, (0, 0, 0), player.move(-cameraX, 0)) Some more notes: Using three lists (blockList, blockListDisp, blockTypeList) to keep track of your level is way to complex, use a single list :-) Change your add_level to: # use a dict to keep track of possible level blocks, so adding new ones becomes simple types = {0: "air", 1: "solid"} def add_level(lvl, bSize): # Creates the level based on a map (lvl) and the size of blocks for y in range(len(lvl)): for x in range(len(lvl[0])): # no more if/elif yield types[lvl[y][x]], pygame.Rect((bSize * x), (bSize * y), bSize, bSize) your lists to: blocks = list(add_level(currLevel, BLOCKSIZE)) # a single list which holds the type and rect for each block of the level Then your collision detection can look like this: while True: """COLLISION""" collision = False for type, rect in blocks: # list contains a tuple of type, rect if type == "solid": if player.colliderect(rect): collision = True if vx > 0 and not falling: player.right = rect.left # now you can always use the rect directly instead of accesing other lists vx = 0 print('Collide Right') ... Also, the drawing code becomes simpler: for type, rect in blocks: if type == "solid": screen.blit(wallblock, rect.move(-cameraX, 0)) #blit the wall-block graphics pygame.draw.rect(screen, (0, 0, 0), player.move(-cameraX, 0)) In the long run, you may want to use a class instead of a tuple, but that's another topic.
Trouble using the same image for multiple objects
I'm making a game with Pygame and I'm trying to animate asteroids that get hit with a bullet. The animation works fine if there's just one asteroid type on the screen. I run into trouble when there's two identical asteroids on the screen. It seems that the first one on is the only one that uses my animation. For example, say the game spawns two small asteroids, these are the exact same and I'm using glob.glob to get the 4 animation images, then I'll just use those images to create a little animation by looping over them. This works with one of the asteroids, the other will not play the animation. I'm thinking it must be because only one object can use specific images at any one time? I'm sorry for the terrible explaination. Here's the asteroid class I have and the call to create the animation: class Asteroid(pygame.sprite.Sprite): def __init__(self, screen, size): pygame.sprite.Sprite.__init__(self) self.screen = screen self.size = size self.pos_x = random.randint(100,900) pos_y= random.randint(100, 700) self.pos_y = -pos_y self.vel_y = 3 self.update_main = True self.animation_path = "" #load the image then put it above the screen, ready to drop if self.size == "small": self.asteroid_image = pygame.image.load("asteroid_small.tga") self.animation_path = "C:/Programming/Stupid Games/Alien Invasion/animations/asteroid_small/*.tga" elif self.size == "medium": self.asteroid_image = pygame.image.load("asteroid_medium.tga") self.animation_path = "C:/Programming/Stupid Games/Alien Invasion/animations/asteroid_medium/*.tga" elif self.size == "med_large": self.asteroid_image = pygame.image.load("asteroid_med_large.tga") self.animation_path = "C:/Programming/Stupid Games/Alien Invasion/animations/asteroid_med_large/*.tga" elif self.size == "small_med": self.asteroid_image = pygame.image.load("asteroid_small_med.png") elif self.size == "small_1": self.asteroid_image = pygame.image.load("small1.tga") self.animation_path = "C:/Programming/Stupid Games/Alien Invasion/animations/small/*.tga" else: self.asteroid_image = pygame.image.load("asteroid_large.tga") self.animation_path = "C:/Programming/Stupid Games/Alien Invasion/animations/asteroid_large/*.tga" #make the asteroid transparent corner = self.asteroid_image.get_at((0,0)) self.asteroid_image.set_colorkey(corner, RLEACCEL) #randomly rotate the image self.new_asteroid_image = pygame.transform.rotate(self.asteroid_image, random.randint(0,340)) self.rect = self.new_asteroid_image.get_rect() self.rect.x, self.rect.y = ((self.pos_x, self.pos_y)) self.start_animate = False self.times = 0 #load the asteroid animation self.animation = glob.glob(self.animation_path) self.animation.sort() self.animation_pos = 0 self.animation_max = len(self.animation)-1 self.animation_speed_init = 50 self.animation_speed = self.animation_speed_init def update(self): #if asteroid has left the screen, place it back to the top if self.rect.y > 900: pos_y = random.randint(100,500) self.rect.y = -pos_y self.rect.x = random.randint(100,900) self.rect.y += self.vel_y self.position = self.rect.x, self.rect.y self.screen.blit(self.new_asteroid_image.convert(), self.position) if self.start_animate == True: self.animation_speed += 25 if self.animation_speed > 60: self.update_main = False self.new_asteroid = pygame.image.load(self.animation[self.times]) corner = self.new_asteroid.get_at((0,0)) self.new_asteroid.set_colorkey(corner, RLEACCEL) self.new_asteroid_image = self.new_asteroid self.animation_speed = 0 self.animation_pos += 1 self.times += 1 if self.times > self.animation_max: self.kill() def start_animation(self): self.start_animate = True #create asteroids at the start of the game if keys[K_SPACE]: game_over = False player = Space_Ship("space_ship.tga", screen) asteroid_small = Asteroid(screen, "small") asteroid_large = Asteroid(screen, "large") asteroid_medium = Asteroid(screen, "medium") asteroid_med_large = Asteroid(screen, "med_large") asteroid_group.add(asteroid_small, asteroid_large, asteroid_medium, asteroid_med_large) player_group.add(player) score = 0 #add asteroids after the game has started if len(asteroid_group) < difficulty: random_asteroid_number = random.randint(0,5) if random_asteroid_number == 0: #large asteroid asteroid_large = Asteroid(screen, "large") asteroid_group.add(asteroid_large) elif random_asteroid_number == 1: #medium asteroid asteroid_medium = Asteroid(screen, "medium") asteroid_group.add(asteroid_medium) elif random_asteroid_number == 2: #medium large asteroid asteroid_med_large = Asteroid(screen, "med_large") asteroid_group.add(asteroid_med_large) elif random_asteroid_number == 3: #small medium asteroid asteroid_small_med = Asteroid(screen, "small_med") asteroid_group.add(asteroid_small_med) elif random_asteroid_number == 4: #small_1 asteroid asteroid_small_1 = Asteroid(screen, "small_1") asteroid_group.add(asteroid_small_1) else: #small asteroid asteroid_small = Asteroid(screen, "small") asteroid_group.add(asteroid_small) #bullet - asteroid collision collisions = pygame.sprite.groupcollide(bullet_group, asteroid_group, True, False) if collisions != None: for i in collisions.values(): for sprite in i: channel2.play(asteroid_audio) if sprite == asteroid_large: score += 3 sprite.start_animation() elif sprite == asteroid_small_1 or sprite == asteroid_med_large or sprite == asteroid_medium or sprite == asteroid_small: score += 1 sprite.start_animation() else: score += 1 sprite.kill()
Although I can't be sure because the bit where you call update is not shown, I have a few ideas of why it is doing what it is. It seems that the second asteroid's update is not being called. This could be for several reasons. The first reason is if you do not implement groups to update you asteroid. Basically, if you only call your single instance of the asteroid, the second one will not do anything, even show up on the screen. If this is the case, you will need to make a group, perhaps a render updates group but even a list would be fine. This would mean you could do something like: asteroids.update() or for asteroid in asteroids: asteroid.update() the above two codes assume that you have some kind of grouping of asteroids, called asteroids. Another possible problem, if you are implementing groups, is if the second asteroid is not getting added to this group. If you tell it to add itself to the group in the init function, this problem will not be there, however, if you call it outside of this function, you may not be doing so when you make your second asteroid. To address this problem just move the part where you add the asteroid into a group that will be updated to the init function of asteroids.