Pygame Platformer滚动错误

时间:2016-06-16 17:58:21

标签: python pygame

因此,在我的引擎上工作时,我想在那里添加一个敌人,听起来很简单。即使我的敌人在游戏中,也没有违反物理定律(大多数情况下),奇怪的是我给了他0控制运动,但是当我移动时,敌人一直跟着玩家精灵。

现在玩了一下我已经注意到当玩家移动时敌人会锁定到滚动的视图框,因此当玩家跳到下方视图框时,敌人会略微跳跃。

我此刻并没有试图给他任何人工智能,敌人只需要与玩家一起产生,掉到平台上并在玩家离开时静止不动。

整个代码:

from pygame import *
import time
import pygame
# from colours import *
# from textObjects import small, medium, large

###########################################################################
#   COLOURS AND TEXT OBJECTS                                              #
###########################################################################

black = pygame.Color(0, 0, 0)
grey = pygame.Color(128, 128, 128)
white = pygame.Color(255, 255, 255)

red = pygame.Color(255, 0, 0)

green = pygame.Color(0, 255, 0)

light_blue = pygame.Color(201, 242, 255)
blue = pygame.Color(0, 0, 255)

green_yellow = pygame.Color(212, 255, 0)
yellow = pygame.Color(255, 251, 0)
orange = pygame.Color(255, 166, 0)
orange_red = pygame.Color(255, 85, 0)

pygame.font.init()
small = pygame.font.SysFont(None, 25)
medium = pygame.font.SysFont(None, 50)
large = pygame.font.SysFont(None, 80)

###########################################################################
#   CLASSES                                                               #
###########################################################################


class Player(pygame.sprite.Sprite):
# Initialise function
    def __init__(self, color=blue, width=32, height=48, health=100):
        # I assume super() inherits everything from the block class
        super(Player, self).__init__()
        # Set the image to a Surface of size width and height
        self.image = pygame.Surface((width, height))
        # Fill the image with the default color of blue
        self.image.fill(color)
        # Use the Surface of the image to get the rectangular co-ordinates
        self.set_properties()
        # Create speed for x and y
        self.speed_x = 0
        self.speed_y = 0
        # Create health
        self.health = 100
        self.level = None

    def set_properties(self):
        self.rect = self.image.get_rect()
        # Create an x and y origin position (Centered the mouse on the sprite)
        self.origin_x = self.rect.centerx
        self.origin_y = self.rect.centery
        self.speed = 5
        # Create total travel distance to check the player's position on the map
        self.travel_distance_x = 0
        self.travel_distance_y = 0

    # Function to easily set the position of any block object on the center
    def set_position(self, x, y):
        self.rect.x = x - self.origin_x
        self.rect.y = y - self.origin_y

    # Function made to print the position on the map
    def print_position(self):
        self.travel_distance_x += self.speed_x
        self.travel_distance_y += self.speed_y
        # print self.travel_distance_x, self.travel_distance_y

    def set_level(self, level):
        self.level = level
        self.set_position(level.player_start_x, level.player_start_y)

    def set_image(self, filename=None):
        if filename != None:
            self.image = pygame.image.load(filename).convert()
            self.set_properties()

    def update(self, collidable=pygame.sprite.Group(), event=True):
        self.experience_gravity()
        self.event = True
        self.rect.x += self.speed_x

        collision_list = pygame.sprite.spritecollide(self, collidable, False)
        for collided_object in collision_list:
            # Right direction
            if self.speed_x > 0:
                self.rect.right = collided_object.rect.left
            # Left direction
            elif self.speed_x < 0:
                self.rect.left = collided_object.rect.right
        self.rect.y += self.speed_y
        collision_list = pygame.sprite.spritecollide(self, collidable, False)
        for collided_object in collision_list:
            # Down direction
            if self.speed_y > 0:
                self.rect.bottom = collided_object.rect.top
                self.speed_y = 0
            # Up direction
            elif self.speed_y < 0:
                self.rect.top = collided_object.rect.bottom
                self.speed_y = 0
        if not event == None:
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_LEFT or event.key == pygame.K_a:
                    self.speed_x = -self.speed
                    # self.change_speed(-self.speed, 0)
                if event.key == pygame.K_RIGHT or event.key == pygame.K_d:
                    self.speed_x = self.speed
                    # self.change_speed(self.speed, 0)
                if event.key == pygame.K_UP or event.key == pygame.K_w:
                    if len(collision_list) >= 1:
                        self.speed_y = -(self.speed) * 2
                        # self.change_speed(0, -self.speed * 2)
                if event.key == pygame.K_DOWN:
                    # self.change_speed(0, self.speed)
                    pass
            if event.type == pygame.KEYUP:
                if event.key == pygame.K_LEFT or event.key == pygame.K_a:
                    if self.speed_x < 0:
                        self.speed_x = 0
                if event.key == pygame.K_RIGHT or event.key == pygame.K_d:
                    if self.speed_x > 0:
                        self.speed_x = 0

    def experience_gravity(self, gravity=0.35):
        if self.speed_y == 0:
            self.speed_y = 1
        else:
            self.speed_y += gravity


class Enemy(pygame.sprite.Sprite):
# Initialise function
    def __init__(self, color=red, width=32, height=48, health=100):
        # I assume super() inherits everything from the block class
        super(Enemy, self).__init__()
        # Set the image to a Surface of size width and height
        self.image = pygame.Surface((width, height))
        # Fill the image with the default color of blue
        self.image.fill(color)
        # Use the Surface of the image to get the rectangular co-ordinates
        self.set_properties()
        # Create speed for x and y
        self.speed_x = 0
        self.speed_y = 0
        # Create health
        self.health = 100
        self.level = None

    def set_properties(self):
        self.rect = self.image.get_rect()
        # Create an x and y origin position (Centered the mouse on the sprite)
        self.origin_x = self.rect.centerx
        self.origin_y = self.rect.centery
        self.speed = 5
        # Create total travel distance to check the player's position on the map
        self.travel_distance_x = 0
        self.travel_distance_y = 0

    # Function to easily set the position of any block object on the center
    def set_position(self, x, y):
        self.rect.x = x - self.origin_x
        self.rect.y = y - self.origin_y

    # Function made to print the position on the map
    def print_position(self):
        self.travel_distance_x += self.speed_x
        self.travel_distance_y += self.speed_y
        print self.travel_distance_x, self.travel_distance_y

    def set_level(self, level):
        self.level = level
        self.set_position(level.enemy_start_x, level.enemy_start_y)

    def set_image(self, filename=None):
        if filename != None:
            self.image = pygame.image.load(filename).convert()
            self.set_properties()

    def update(self, collidable=pygame.sprite.Group(), event=True):
        self.experience_gravity()
        self.event = True
        self.rect.x += self.speed_x

        collision_list = pygame.sprite.spritecollide(self, collidable, False)
        for collided_object in collision_list:
            # Right direction
            if self.speed_x > 0:
                self.rect.right = collided_object.rect.left
            # Left direction
            elif self.speed_x < 0:
                self.rect.left = collided_object.rect.right
        self.rect.y += self.speed_y
        collision_list = pygame.sprite.spritecollide(self, collidable, False)
        for collided_object in collision_list:
            # Down direction
            if self.speed_y > 0:
                self.rect.bottom = collided_object.rect.top
                self.speed_y = 0
            # Up direction
            elif self.speed_y < 0:
                self.rect.top = collided_object.rect.bottom
                self.speed_y = 0
        if not event == None:
            pass

    def experience_gravity(self, gravity=0.35):
        if self.speed_y == 0:
            self.speed_y = 1
        else:
            self.speed_y += gravity



class Block(pygame.sprite.Sprite):
    def __init__(self, x, y, width, height, color=blue):
        # I assume super() inherits everything from the block class
        super(Block, self).__init__()
        # Set the image to a Surface of size width and height
        self.image = pygame.Surface((width, height))
        # Fill the image with the default color of blue
        self.image.fill(color)
        # Get rectangle object of the block
        self.rect = self.image.get_rect()
        # Assign x and y co-ordinates of the block
        self.rect.x = x
        self.rect.y = y

    def experience_gravity(self, gravity=0.35):
        if self.speed_y == 0:
            self.speed_y = 1
        else:
            self.speed_y += gravity

class Level(object):
    def __init__(self, player_object):
        self.object_list = pygame.sprite.Group()
        self.player_object = player_object
        self.player_start = self.player_start_x, self.player_start_y = 80, 150
        self.enemy_start = self.enemy_start_x, self.enemy_start_y = 300, 200

        self.world_shift_x = 0
        self.world_shift_y = 0

        self.left_viewbox = screen_width / 2 - screen_width / 8
        self.right_viewbox = screen_width / 2 + screen_width / 8
        self.up_viewbox = screen_height / 3
        self.down_viewbox = screen_height / 2 # + screen_height / 12

    def update(self):
        self.object_list.update()

    def draw(self, screen):
        screen.fill(white)
        self.object_list.draw(screen)

    def shift_world(self, shift_x, shift_y):
        self.world_shift_x += shift_x
        self.world_shift_y += shift_y
        for each_object in self.object_list:
            each_object.rect.x += shift_x
            each_object.rect.y += shift_y


    def scroll(self):
        if self.player_object.rect.x <= self.left_viewbox:
            view_difference = self.left_viewbox - self.player_object.rect.x
            self.player_object.rect.x = self.left_viewbox
            self.shift_world(view_difference, 0)

        if self.player_object.rect.x >= self.right_viewbox:
            view_difference = self.right_viewbox - self.player_object.rect.x
            self.player_object.rect.x = self.right_viewbox
            self.shift_world(view_difference, 0)

        if self.player_object.rect.y <= self.up_viewbox:
            view_difference = self.up_viewbox - self.player_object.rect.y
            self.player_object.rect.y = self.up_viewbox
            self.shift_world(0, view_difference)

        if self.player_object.rect.y >= self.down_viewbox:
            view_difference = self.down_viewbox - self.player_object.rect.y
            self.player_object.rect.y = self.down_viewbox
            self.shift_world(0, view_difference)


class Level_01(Level):
    def __init__(self, player_object):
        super(Level_01, self).__init__(player_object)
        level = [
            #[x, y, width, height, color]
            [0, 0, 38, 899, black],
            [7, 874, 1592, 25, black],
            [1564, 0, 35, 887, black],
            [0, 0, 1593, 40, black],
            [330, 731, 282, 31, black],
            [898, 678, 307, 28, black],
            [603, 528, 280, 28, black],
            [1279, 616, 301, 32, black],
            [1046, 468, 194, 35, black],
            [208, 348, 306, 28, black],
            [708, 294, 335, 24, black],
            [22, 487, 170, 26, black]
        ]

        for block in level:
            block = Block(block[0], block[1], block[2], block[3], block[4])
            self.object_list.add(block)


class Camera(object):
    def __init__(self, camera_function, width, height):
        self.camera_function = camera_function
        self.state = Rect(0, 0, width, height)

    def apply(self, target):
        return target.rect.move(self.state.topleft)

    def update(self, target):
        self.state = self.camera_function(self.state, target.rect)


###########################################################################
#   TEXT AND UI FUNCTIONS                                                 #
###########################################################################


def set_message(text):
    global message, previous_message
    message = font.render(text, True, black, white)
    previous_message = message


def text_objects(text, color, size):
    if size == 'small':
        textSurface = small.render(text, True, color)
    if size == 'medium':
        textSurface = medium.render(text, True, color)
    if size == 'large':
        textSurface = large.render(text, True, color)
    return textSurface, textSurface.get_rect()


def display_message(text, color, y_displacement=0, size='small'):
    textSurface, textRectangle = text_objects(text, color, size)
    textRectangle.center = (screen_width / 2), (screen_height / 2) + y_displacement
    screen.blit(textSurface, textRectangle)


def health_bar(player_health):
    if player_health > 85:
        health_color = green
    elif player_health > 70:
        health_color = green_yellow
    elif player_health > 55:
        health_color = yellow
    elif player_health > 40:
        health_color = orange
    elif player_health > 25:
        health_color = orange_red
    else:
        health_color = red

    if player_health < 0:
        player_health = 0

    pygame.draw.rect(screen, health_color, (50, screen_height / 20, player_health, 25))

###########################################################################
#   INITIALISATION, SCREEN PROPERTIES, FPS                                #
###########################################################################

# Initialise pygame module
pygame.init()

# Initialise pygame font
pygame.font.init()

# Defining the screen size
screen_size = screen_width, screen_height = 800, 600

# Setting the display and getting the Surface object
screen = pygame.display.set_mode(screen_size)

# Getting the Clock object
clock = pygame.time.Clock()

# Setting a title to the window
pygame.display.set_caption("TODO make title")

# Defining variable for FPS
fps_limit = 60

# Clear the screen
screen.fill(white)

# Setting the FPS at which the game will run
clock.tick(fps_limit)

###########################################################################
#   MAIN LOOP, PAUSE AND DEATH FUNCTIONS                                  #
###########################################################################


def death():
    death = True
    while death:
        display_message("YOU DIED", red, size='large')
        pygame.display.update()
        time.sleep(1)
        death = False
        over = True
        game_exit = True


def pause():
    paused = True
    display_message("Paused", black)
    pygame.display.update()
    while paused:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                quit()
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_p:
                    paused = False
                elif event.key == pygame.K_q:
                    pygame.quit()
                    quit()


def game_intro():
    intro = True
    while intro:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                quit()
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_RETURN:
                    run=True
                    intro=False
                if event.key == pygame.K_ESCAPE:
                    pygame.quit()
                    quit()

        screen.fill(light_blue)
        display_message("Physics Engine v0.1", black, - screen_height / 5, 'large' )
        display_message("pre-alpha stage", grey, - screen_height / 10, 'small')
        display_message("Press ENTER to start or ESCAPE to close", black, screen_height / 8, 'small')
        pygame.display.update()


def main_loop():
    # Group all the currently active objects
    active_object_list = pygame.sprite.Group()

    # Set variable player to the Player() class
    player = Player()

    # Set variable enemy to the Enemy() class
    enemy = Enemy()

    # Add player to the active object list
    active_object_list.add(player, enemy)

    # Make a list for the levels and append Level_01 to that list with player as the handler (being the character in focus)
    level_list = []
    level_list.append(Level_01(player))

    current_level_number = 0
    current_level = level_list[current_level_number]

    # Set the starting co-ordinates
    player.set_level(current_level)
    enemy.set_level(current_level)

    # run = True
    over = False
    game_exit = False
    while not game_exit:
        if over == True:
            while over:
                for event in pygame.event.get():
                    if event.type == pygame.QUIT:
                        game_exit = True
                        over = False
                    if event.type == pygame.KEYDOWN:
                        if event.key == K_RETURN:
                            main_loop()
                        if event.key == K_ESCAPE:
                            pygame.quit()
                            quit()
                screen.fill(light_blue)
                display_message("Do you want to start over?", black, -screen_height / 8, size='large')
                display_message("Press RETURN to start over or ESCAPE to quit", black, screen_height / 8)
                pygame.display.update()
        current_events = pygame.event.get()
        if current_events:
            for event in current_events:
                if event.type == pygame.QUIT:
                    pygame.quit()
                    quit()
                    run = False
                if event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_p:
                        pause()
                    if event.key == pygame.K_k:
                        player.health -= 5

                # Update functions
                player.update(current_level.object_list, event)
                enemy.update(current_level.object_list, event)
                current_level.update()

        else:
            player.update(current_level.object_list, None)
            enemy.update(current_level.object_list, None)
            current_level.update()

        # Logic testing
        current_level.scroll()
        if player.health <= 0:
            player.health = 0
            over = True
            death()
        if player.travel_distance_y > 900:
            player.health = 0
            over = True
            death()

        # Draw everything
        current_level.draw(screen)
        active_object_list.draw(screen)
        health_bar(player.health)

        # Delay fps
        clock.tick(fps_limit)

        # Update screen
        pygame.display.update()

game_intro()
main_loop()

位于Level类中的视图框以检查玩家是否正在击中框的方式工作,如果是,则世界在玩家周围移动而不是在玩家中移动。

滚动功能

def shift_world(self, shift_x, shift_y):
        self.world_shift_x += shift_x
        self.world_shift_y += shift_y
        for each_object in self.object_list:
            each_object.rect.x += shift_x
            each_object.rect.y += shift_y


    def scroll(self):
        if self.player_object.rect.x <= self.left_viewbox:
            view_difference = self.left_viewbox - self.player_object.rect.x
            self.player_object.rect.x = self.left_viewbox
            self.shift_world(view_difference, 0)

        if self.player_object.rect.x >= self.right_viewbox:
            view_difference = self.right_viewbox - self.player_object.rect.x
            self.player_object.rect.x = self.right_viewbox
            self.shift_world(view_difference, 0)

        if self.player_object.rect.y <= self.up_viewbox:
            view_difference = self.up_viewbox - self.player_object.rect.y
            self.player_object.rect.y = self.up_viewbox
            self.shift_world(0, view_difference)

        if self.player_object.rect.y >= self.down_viewbox:
            view_difference = self.down_viewbox - self.player_object.rect.y
            self.player_object.rect.y = self.down_viewbox
            self.shift_world(0, view_difference)

值得注意的是Level和Level_01需要&#34; player_object&#34;作为输入,我认为在主循环中调用player.update()和enemy.update()时会调用它。

主循环

if current_events:
            for event in current_events:
                if event.type == pygame.QUIT:
                    pygame.quit()
                    quit()
                    run = False
                if event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_p:
                        pause()
                    if event.key == pygame.K_k:
                        player.health -= 5

                # Update functions
                player.update(current_level.object_list, event)
                enemy.update(current_level.object_list, event)
                current_level.update()

        else:
            player.update(current_level.object_list, None)
            enemy.update(current_level.object_list, None)
            current_level.update()

所以尽管玩家被用作第477行中关卡的处理程序:

level_list.append(Level_01(player))

我认为敌人受到视箱的影响,因为他也被视为&#34; player_object&#34;在滚动功能中。

如果有人能给我一些关于我做错的提示会非常有帮助,谢谢。

1 个答案:

答案 0 :(得分:0)

问题在于您的go to declaration,或者更确切地说是您的scroll功能。你移动shift_world中的每个对象。对于Level01,此列表仅包含块对象。在self.object_list中,然后移动每个块和玩家,但不是敌人。这意味着敌人的精灵停留在它的位置并且不会随着它应该的世界移动。因此它似乎移动,因为它相对于世界的位置发生了变化。事实上,它在画布上的同一位置保持blitted状态。

当玩家跳跃时,一旦世界向下移动,敌人就会在空中结束,然后重力将他拉回平台。当玩家向右移动时,敌人似乎会跟随,因为世界向左移动而敌人不会随之移动。

将敌人添加到scroll,它应该按预期工作。