4
\$\begingroup\$

This is a simple game I am making in the arcade library of Python. I am quite new to object-oriented programming as well as game development. I feel like a lot of the code is inefficient, so I was hoping to get some improvements on the code. Any kind of suggestion is appreciated.

import os
import random
import arcade

SPRITE_SCALING = 0.5
SPRITE_SCALING_COIN = 0.3
SPRITE_NATIVE_SIZE = 128
SPRITE_SIZE = int(SPRITE_NATIVE_SIZE * SPRITE_SCALING)

SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
SCREEN_TITLE = "Help Nick Adams!"

# Physics
MOVEMENT_SPEED = 5
JUMP_SPEED = 12
GRAVITY = 0.5


class Room:
    # Class to hold info about rooms/levels

    def __init__(self):
        self.wall_list = self.goal_list = self.enemy_list = self.victory_sprite = None
        self.collectedCoins = 0
        self.numCoins = 0
        self.background = None


def setup_room_1():
    room = Room()
    room.wall_list = arcade.SpriteList(use_spatial_hash=True)
    room.enemy_list = arcade.SpriteList()
    room.goal_list = arcade.SpriteList(use_spatial_hash=True)
    room.victory_sprite = arcade.SpriteList(use_spatial_hash=True)

    # Draw platforms and ground
    for x in range(0, SCREEN_WIDTH, SPRITE_SIZE):
        wall = arcade.Sprite(":resources:images/tiles/grassMid.png", SPRITE_SCALING)

        wall.bottom = 0
        wall.left = x
        room.wall_list.append(wall)

    for x in range(SPRITE_SIZE * 3, SPRITE_SIZE * 8, SPRITE_SIZE):
        wall = arcade.Sprite(":resources:images/tiles/grassMid.png", SPRITE_SCALING)

        wall.bottom = SPRITE_SIZE * 3
        wall.left = x
        room.wall_list.append(wall)

    # Draw the crates
    for x in range(0, SCREEN_WIDTH, SPRITE_SIZE * 5):
        wall = arcade.Sprite(":resources:images/tiles/boxCrate_double.png", SPRITE_SCALING)

        wall.bottom = SPRITE_SIZE
        wall.left = x
        room.wall_list.append(wall)

    # Draw an enemy 1
    enemy = arcade.Sprite(":resources:images/enemies/wormGreen.png", SPRITE_SCALING)

    enemy.bottom = SPRITE_SIZE
    enemy.left = SPRITE_SIZE * 2

    enemy.change_x = 2
    room.enemy_list.append(enemy)

    # -- Draw enemy2 on the platform
    enemy = arcade.Sprite(":resources:images/enemies/wormGreen.png", SPRITE_SCALING)

    enemy.bottom = SPRITE_SIZE * 4
    enemy.left = SPRITE_SIZE * 4

    # Set boundaries for enemy
    enemy.boundary_right = SPRITE_SIZE * 8
    enemy.boundary_left = SPRITE_SIZE * 3
    enemy.change_x = 2
    room.enemy_list.append(enemy)

    # Set up coins
    for pos in [[128, 96], [418, 300], [670, 150]]:
        goal = arcade.Sprite(":resources:images/items/coinGold.png", SPRITE_SCALING)
        goal.center_x = pos[0]
        goal.center_y = pos[1]
        room.goal_list.append(goal)
        room.numCoins += 1

    # Set up checkpoint/level clear
    flag = arcade.Sprite(":resources:images/tiles/signExit.png", SPRITE_SCALING)
    flag.center_x = 770
    flag.center_y = 96
    room.victory_sprite.append(flag)

    # Load the background image for this level.
    room.background = arcade.load_texture(":resources:images/backgrounds/abstract_1.jpg")

    return room


def setup_room_2():
    room = Room()
    room.wall_list = arcade.SpriteList(use_spatial_hash=True)
    room.enemy_list = arcade.SpriteList()
    room.goal_list = arcade.SpriteList(use_spatial_hash=True)
    room.victory_sprite = arcade.SpriteList(use_spatial_hash=True)

    # Set up walls
    for y in range(0, 800, 200):
        for x in range(100, 700, 64):
            wall = arcade.Sprite(":resources:images/tiles/boxCrate_double.png", SPRITE_SCALING)
            wall.center_x = x
            wall.center_y = y
            room.wall_list.append(wall)

    for pos in [[35, 40], [765, 80], [35, 280], [765, 480]]:
        wall = arcade.Sprite(":resources:images/tiles/grassHalf.png", SPRITE_SCALING)
        wall.center_x = pos[0]
        wall.center_y = pos[1]
        room.wall_list.append(wall)

    # Create the coins
    for i in range(50):

        # Create the coin instance
        # Coin image from kenney.nl
        goal = arcade.Sprite(":resources:images/items/coinGold.png", SPRITE_SCALING_COIN)

        # Boolean variable if we successfully placed the coin
        coin_placed_successfully = False

        # Keep trying until success
        while not coin_placed_successfully:
            # Position the coin
            goal.center_x = random.randrange(100, 700)
            goal.center_y = random.randrange(SCREEN_HEIGHT)

            # See if the coin is hitting a wall
            wall_hit_list = arcade.check_for_collision_with_list(goal, room.wall_list)

            # See if the coin is hitting another coin
            coin_hit_list = arcade.check_for_collision_with_list(goal, room.goal_list)

            if len(wall_hit_list) == 0 and len(coin_hit_list) == 0:
                coin_placed_successfully = True
                room.numCoins += 1

        # Add the coin to the lists
        room.goal_list.append(goal)

    # Draw an enemy1
    enemy = arcade.Sprite(":resources:images/enemies/fly.png", SPRITE_SCALING_COIN)

    enemy.bottom = SPRITE_SIZE
    enemy.left = SPRITE_SIZE * 2

    enemy.boundary_right = SPRITE_SIZE * 8 + 60
    enemy.boundary_left = SPRITE_SIZE * 1 + 60
    enemy.change_x = 3
    room.enemy_list.append(enemy)

    # Draw a enemy2
    enemy = arcade.Sprite(":resources:images/enemies/fly.png", SPRITE_SCALING_COIN)

    enemy.bottom = SPRITE_SIZE * 4
    enemy.left = SPRITE_SIZE * 4

    enemy.boundary_right = SPRITE_SIZE * 8
    enemy.boundary_left = SPRITE_SIZE * 3
    enemy.change_x = 4
    room.enemy_list.append(enemy)

    # Draw a enemy3
    enemy = arcade.Sprite(":resources:images/enemies/fly.png", SPRITE_SCALING_COIN)

    enemy.bottom = SPRITE_SIZE * 7.2
    enemy.left = SPRITE_SIZE * 4

    enemy.boundary_right = SPRITE_SIZE * 8 + 80
    enemy.boundary_left = SPRITE_SIZE * 3
    enemy.change_x = 5.2
    room.enemy_list.append(enemy)

    # Draw victory point
    flag = arcade.Sprite(":resources:images/tiles/signExit.png", SPRITE_SCALING)
    flag.center_x = 765
    flag.center_y = 545
    room.victory_sprite.append(flag)

    # Load the background image for this level.
    room.background = arcade.load_texture(":resources:images/backgrounds/abstract_1.jpg")

    return room


def setup_room_3():
    room = Room()
    room.wall_list = arcade.SpriteList(use_spatial_hash=True)
    room.goal_list = arcade.SpriteList()
    room.victory_sprite = arcade.SpriteList(use_spatial_hash=True)

    # Draw boundaries
    for y in (0, SCREEN_HEIGHT - SPRITE_SIZE):
        # Loop for each box going across
        for x in range(0, SCREEN_WIDTH, SPRITE_SIZE):
            wall = arcade.Sprite(":resources:images/tiles/stoneCenter.png", SPRITE_SCALING)
            wall.left = x
            wall.bottom = y
            room.wall_list.append(wall)

        # Create left and right column of boxes
    for x in (0, SCREEN_WIDTH - SPRITE_SIZE):
        # Loop for each box going across
        for y in range(SPRITE_SIZE, SCREEN_HEIGHT - SPRITE_SIZE, SPRITE_SIZE):
            wall = arcade.Sprite(":resources:images/tiles/stoneCenter.png", SPRITE_SCALING)
            wall.left = x
            wall.bottom = y
            room.wall_list.append(wall)

    # Create boxes in the middle
    for x in range(128, SCREEN_WIDTH, 134):
        for y in range(140, SCREEN_HEIGHT - 50, 170):
            wall = arcade.Sprite(":resources:images/tiles/brickGrey.png", 0.4)
            wall.center_x = x
            wall.center_y = y
            # wall.angle = 45
            room.wall_list.append(wall)

    # Create coins
    for i in range(50):
        coin = arcade.Sprite(":resources:images/enemies/fishPink.png", 0.25)
        coin.center_x = random.randrange(100, 700)
        coin.center_y = random.randrange(100, 500)
        while coin.change_x == 0 and coin.change_y == 0:
            coin.change_x = random.randrange(-4, 5)
            coin.change_y = random.randrange(-4, 5)

        room.goal_list.append(coin)

    room.background = arcade.load_texture(":resources:images/backgrounds/abstract_1.jpg")

    return room


class MainGame(arcade.Window):

    def __init__(self):
        super().__init__(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)

        file_path = os.path.dirname(os.path.abspath(__file__))
        os.chdir(file_path)

        # Sprites and set up player
        self.player_list = self.rooms = self.player_sprite = self.physics_engine = None
        self.current_room = self.view_left = self.view_bottom = self.collectedCoins = self.score = 0
        self.game_over = False

        # Load sounds
        self.collect_goal_sound = arcade.load_sound(":resources:sounds/coin1.wav")
        self.jump_sound = arcade.load_sound(":resources:sounds/jump1.wav")

        # Starting room number
        self.current_room = 2

        # list of rooms
        self.rooms = []

        # Create the rooms
        room = setup_room_1()
        self.rooms.append(room)

        room = setup_room_2()
        self.rooms.append(room)

        room = setup_room_3()
        self.rooms.append(room)

        self.totalScore = 0

        self.set_mouse_visible(False)

    def setup(self):
        """ Set up the game and initialize the variables. """
        self.score = 0

        # -- Set up the player
        self.player_sprite = arcade.Sprite(":resources:images/animated_characters/female_person/femalePerson_idle.png",
                                           SPRITE_SCALING)

        # Player start position according to room number
        if self.current_room == 0:
            self.player_sprite.center_x = 64
            self.player_sprite.center_y = 270
        elif self.current_room == 1:
            self.player_sprite.center_x = 35
            self.player_sprite.center_y = 55
        self.player_list = arcade.SpriteList()
        self.player_list.append(self.player_sprite)

        if self.current_room == 2:
            self.player_sprite = arcade.Sprite(":resources:images/animated_characters/female_person"
                                               "/femalePerson_climb0.png", SPRITE_SCALING)
            self.player_sprite.center_x = 340
            self.player_sprite.center_y = 300
            self.player_list.append(self.player_sprite)

        # Create a physics engine
        self.physics_engine = arcade.PhysicsEnginePlatformer(self.player_sprite,
                                                             self.rooms[self.current_room].wall_list)

    def on_draw(self):

        arcade.start_render()
        arcade.draw_lrwh_rectangle_textured(0, 0,
                                            SCREEN_WIDTH, SCREEN_HEIGHT,
                                            self.rooms[self.current_room].background)

        self.rooms[self.current_room].wall_list.draw()
        self.rooms[self.current_room].goal_list.draw()
        if self.current_room != 2:
            self.rooms[self.current_room].enemy_list.draw()
            self.rooms[self.current_room].victory_sprite.draw()

        if self.current_room == 0:
            arcade.draw_text("Help Nick collect items for his coffee!", 250, 570, arcade.color.BLACK, 20)
        elif self.current_room == 1:
            arcade.draw_text("Help Nick collect grasshoppers!", 250, 570, arcade.color.BLACK, 20)
        elif self.current_room == 2:
            arcade.draw_text("Help Nick collect all the fish (use your mouse to catch them)", 250, 570,
                             arcade.color.BLACK, 20)

        output1 = f"Score: {self.score}"
        arcade.draw_text(output1, 10, 560, arcade.color.WHITE, 14)
        output2 = f"Total Score: {self.totalScore}"
        arcade.draw_text(output2, 10, 540, arcade.color.WHITE, 14)
        # Draw all the sprites.
        self.player_list.draw()

    def on_key_press(self, key, modifiers):
        if key == arcade.key.UP:
            if self.physics_engine.can_jump():
                self.player_sprite.change_y = JUMP_SPEED
        elif key == arcade.key.LEFT:
            self.player_sprite.change_x = -MOVEMENT_SPEED
        elif key == arcade.key.RIGHT:
            self.player_sprite.change_x = MOVEMENT_SPEED

    def on_key_release(self, key, modifiers):
        if key == arcade.key.LEFT or key == arcade.key.RIGHT:
            self.player_sprite.change_x = 0

    def on_mouse_motion(self, x, y, dx, dy):
        if self.current_room == 2:
            self.player_sprite.center_x = x
            self.player_sprite.center_y = y

    def on_update(self, delta_time):

        if not self.game_over:
            if self.current_room == 0 or self.current_room == 1:
                # Move the enemies
                self.rooms[self.current_room].enemy_list.update()

                # Check each enemy
                for enemy in self.rooms[self.current_room].enemy_list:
                    # If the enemy hit a wall, reverse
                    if len(arcade.check_for_collision_with_list(enemy, self.rooms[self.current_room].wall_list)) > 0:
                        enemy.change_x *= -1
                    # If the enemy hit the left boundary, reverse
                    elif enemy.boundary_left is not None and enemy.left < enemy.boundary_left:
                        enemy.change_x *= -1
                    # If the enemy hit the right boundary, reverse
                    elif enemy.boundary_right is not None and enemy.right > enemy.boundary_right:
                        enemy.change_x *= -1

                # Update the player using the physics engine
                self.physics_engine.update()

                # See if we hit any coins
                goal_hit_list = arcade.check_for_collision_with_list(self.player_sprite,
                                                                     self.rooms[self.current_room].goal_list)

                # Loop through each coin we hit (if any) and remove it
                for goal in goal_hit_list:
                    # Remove the coin
                    goal.remove_from_sprite_lists()
                    # Play a sound
                    arcade.play_sound(self.collect_goal_sound)
                    # Count number of coins collected
                    self.collectedCoins += 1
                    self.score += 1
                    self.totalScore += 1

                if self.player_sprite.center_x <= -10 or self.player_sprite.center_x >= 800:
                    self.player_sprite.change_x = 0
                    self.player_sprite.change_y = 0
                    self.player_sprite.center_x = 64
                    self.player_sprite.center_y = 270

                # See if the player hit a worm
                if len(arcade.check_for_collision_with_list(self.player_sprite,
                                                            self.rooms[self.current_room].enemy_list)) > 0:
                    self.game_over = True

                # See if the player hit the flag. If so, progress to next level
                if arcade.check_for_collision_with_list(self.player_sprite, self.rooms[
                    self.current_room].victory_sprite) and self.collectedCoins >= self.rooms[
                     self.current_room].numCoins:
                    # self.game_over = True
                    self.current_room += 1
                    self.setup()

            elif self.current_room == 2:

                for coin in self.rooms[self.current_room].goal_list:
                    coin.center_x += coin.change_x
                    walls_hit = arcade.check_for_collision_with_list(coin, self.rooms[self.current_room].wall_list)
                    for wall in walls_hit:
                        if coin.change_x > 0:
                            coin.right = wall.left
                        elif coin.change_x < 0:
                            coin.left = wall.right
                    if len(walls_hit) > 0:
                        coin.change_x *= -1

                    coin.center_y += coin.change_y
                    walls_hit = arcade.check_for_collision_with_list(coin, self.rooms[self.current_room].wall_list)
                    for wall in walls_hit:
                        if coin.change_y > 0:
                            coin.top = wall.bottom
                        elif coin.change_y < 0:
                            coin.bottom = wall.top
                    if len(walls_hit) > 0:
                        coin.change_y *= -1

                # Generate a list of all sprites that collided with the player.
                hit_list = arcade.check_for_collision_with_list(self.player_sprite,
                                                                self.rooms[self.current_room].goal_list)

                # Loop through each colliding sprite, remove it, and add to the score.
                for coin in hit_list:
                    coin.remove_from_sprite_lists()
                    arcade.play_sound(self.collect_goal_sound)
                    self.score += 1
                    self.totalScore += 1
                    if self.score >= 50:
                        self.game_over = True
                        self.current_room = 0
                        self.setup()


def main():
    window = MainGame()
    window.setup()
    arcade.run()


if __name__ == "__main__":
    main()
\$\endgroup\$
2
\$\begingroup\$

All the range(0, number) can be simplified to just range(number).


You can change

if self.current_room == 0 or self.current_room == 1:

to

if self.current_room in [0, 1]:

if len(wall_hit_list) == 0 and len(coin_hit_list) == 0:

can be

if not wall_hit_list and not coin_hit_list:

You can change

if len(arcade.check_for_collision_with_list(self.player_sprite,
                                            self.rooms[self.current_room].enemy_list)) > 0:
    self.game_over = True

to

if arcade.check_for_collision_with_list(self.player_sprite, self.rooms[self.current_room].enemy_list):
    self.game_over = True

\$\endgroup\$
1
  • \$\begingroup\$ if not (wall_hit_list or coin_hit_list): using de morgan's law \$\endgroup\$ – hjpotter92 Nov 10 '20 at 10:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.