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.

Categories