首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >游戏中的蛇游戏

游戏中的蛇游戏
EN

Code Review用户
提问于 2017-12-21 12:36:22
回答 1查看 5.2K关注 0票数 4

我在Python的蛇游戏上工作了一个月左右。它运行得很好(我已经得到了一些很好的帮助),但由于我是游戏初学者,我希望得到比我更好的程序员的反馈。任何提高性能、可读性和/组织的东西都是受欢迎的。

请记住,声音位于一个单独的文件夹中,除非您有文件,否则此代码不会在您的计算机上工作。我只想看一遍代码。

GitHub

代码语言:javascript
复制
import pygame
import sys
import time
import random
import collections
import itertools
import os
def main():
    """Snake v 1.59"""
    score = 0  # Initial score
    speed = pygame.time.Clock()
    direction = "R"  # Initial direction
    snake_position = collections.deque([100, 50])  # Initial snake position
    snake_body = collections.deque([[100, 50], [90, 50], [100, 50]])  # Initial snake body
    # It places the food randomly, excluding the border
    food_position = [random.randrange(1, 72) * 10, random.randrange(1, 46) * 10]
    food_spawn = True
    # Will define the colors
    white = pygame.Color("white")
    red = pygame.Color("red")
    green = pygame.Color("green")
    black = pygame.Color("black")
    orange = pygame.Color("orange")
    grey = pygame.Color("light grey")
    # Game surface
    player_screen = pygame.display.set_mode((720, 460))  # Set screen size
    pygame.display.set_caption("Snake v.1.38")  # Set screen title and version

    def initializer():
        """ Checks the mistakes, and closes the program if it does while
        printing on the console how many bugs it has, also initializes
        the mixers, and game """
        pygame.mixer.pre_init(44100, -16, 1, 512)
        pygame.mixer.init()
        bugs = pygame.init()
        if bugs[1] > 0:
            print("There are", bugs[1], "bugs! quiting.....")
            time.sleep(3)
            sys.exit("Closing program")
        else:
            print("The game was initialized")

    def game_sound(s):
        """ Include the game sfx and music"""
        if s == 0:
            directory = os.path.dirname(os.path.realpath(sys.argv[0]))
            full_path = os.path.join(directory, "background.ogg")
            pygame.mixer.music.load(full_path)
            pygame.mixer.music.play(-1)
        elif s == 1:
            directory = os.path.dirname(os.path.realpath(sys.argv[0]))
            full_path = os.path.join(directory, "eating.wav")
            pygame.mixer.Sound(full_path).play()
        elif s == 2:
            directory = os.path.dirname(os.path.realpath(sys.argv[0]))
            full_path = os.path.join(directory, "game-over.wav")
            pygame.mixer.Sound(full_path).play()

    def you_lose():
        """ When the players loses, it will show a red message in times new
         roman font with 44 px size in a rectangle box"""
        font_game_over = pygame.font.SysFont("times new roman", 44)
        game_over_surface = font_game_over.render("Game over :(", True, red)
        game_over_position = game_over_surface.get_rect()
        game_over_position.midtop = (360, 15)
        player_screen.blit(game_over_surface, game_over_position)
        game_sound(2)
        scoring()
        pygame.display.flip()  # Updates the screen, so it doesnt freeze
        quiting()

    def pause_menu():
        """It displays the pause menu"""
        player_screen.fill(white)
        pause_font = pygame.font.SysFont("times new roman", 44)
        pause_surface = pause_font.render("Paused", True, black)
        pause_position = pause_surface.get_rect()
        pause_position.midtop = (360, 15)
        player_screen.blit(pause_surface, pause_position)
        pygame.display.flip()

    def quiting():
        """ When this function is called, it will wait 4 seconds and exit"""
        time.sleep(4)
        pygame.quit()
        sys.exit()

    def scoring():
        """ It will shows the score after the game over in times new
        roman font with 16px size and black color in a rectangle box"""
        score_font = pygame.font.SysFont("times new roman", 16)
        score_surface = score_font.render("Score : {}".format(score), True, black)
        score_position = score_surface.get_rect()
        score_position.midtop = (360, 80)
        player_screen.blit(score_surface, score_position)

    initializer()
    game_sound(0)
    paused = False
    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                quiting()
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_p:  # Pausing/ unpausing
                    paused = not paused
                    if paused:
                        pygame.mixer.music.pause()
                        pause_menu()
                    else:
                        pygame.mixer.music.unpause()
                # Choose direction by user input, block opposite directions
                key_right = event.key in (pygame.K_RIGHT, pygame.K_d)
                key_left = event.key in (pygame.K_LEFT, pygame.K_a)
                key_down = event.key in (pygame.K_DOWN, pygame.K_s)
                key_up = event.key in (pygame.K_UP, pygame.K_w)
                if key_right and direction != "L":
                    direction = "R"
                elif key_left and direction != "R":
                    direction = "L"
                elif key_down and direction != "U":
                    direction = "D"
                elif key_up and direction != "D":
                    direction = "U"
                elif event.key == pygame.K_ESCAPE:
                    quiting()  # It will quit when esc is pressed

        # Simulates the snake movement(together with snake_body_pop)
        if not paused:
            if direction == "R":
                snake_position[0] += 10
            elif direction == "L":
                snake_position[0] -= 10
            elif direction == "D":
                snake_position[1] += 10
            elif direction == "U":
                snake_position[1] -= 10
            # Body mechanics
            snake_body.appendleft(list(snake_position))
            if snake_position == collections.deque(food_position):
                score += 1  # Every food taken will raise the score by 1
                game_sound(1)
                food_spawn = False  # It removes the food from the board
            else:
                # If the food is taken it will not remove the last body piece(raising snakes size)
                snake_body.pop()
            if food_spawn is False:  # When a food is taken it will respawn randomly
                food_position = [random.randrange(1, 72) * 10, random.randrange(1, 46) * 10]
            food_spawn = True  # It will set the food to True again, to keep the cycle
            # Drawing
            player_screen.fill(grey)  # Set the background to grey
            for position in snake_body:  # Snake representation on the screen
                pygame.draw.rect(player_screen, green, pygame.Rect(position[0], position[1], 10, 10))
            # Food representation on the screen
            pygame.draw.rect(player_screen, orange, pygame.Rect(food_position[0], food_position[1], 10, 10))
            if snake_position[0] not in range(0, 711) or snake_position[1] not in range(0, 451):
                you_lose()  # Game over when the Snake hit a wall
            for block in itertools.islice(snake_body, 1, None):
                if snake_position == collections.deque(block):
                    you_lose()  # Game over when the Snake hits itself
            pygame.display.flip()  # It constantly updates the screen
            speed.tick(26)  # It sets the speed to a playable value


if __name__ == "__main__":
    main()
EN

回答 1

Code Review用户

回答已采纳

发布于 2017-12-21 14:55:26

感谢您的分享。其中大部分看起来不错,代码也是可以理解的。你的标识符选得很好。

请运行flake8。它会要求你增加一些空白行,诸如此类的事情。此外,PEP-008要求单行文档字符串以“.”结尾。句号。

将变量排除在全局范围之外是件好事。但是"main()是新的全局“是一种奇怪的风格。考虑至少将一些def移到顶层,或者创建一个SnakeGame类。

你有很多幻数幻数。有些人基本上没问题,比如pre_init(44100, -16, 1, 512)。但是,像10像素的网格分辨率和网格大小这样的东西确实需要是常量,比如GRID_WIDTH = 72或GRID_SIZE = (72,46)。重构这个。现在重建它。

也许有机会引入一个显示类,它知道网格分辨率的“秘密”是10 of。游戏中很多人会在小于GRID_SIZE的游戏空间坐标中操纵食物+蛇,并要求显示器在大得多的屏幕空间中绘制点。手里拿着这个,想象一下在董事会中加入一个蓝色药丸(食物)和一个红色药丸。吃蓝色会使显示器在X轴周围翻转镜像,而红色会在Y轴周围翻转。你的游戏的其他部分不会知道或者不关心视觉效果。

这是非常好的:bugs = pygame.init()。但是请考虑将其重新定义为pass, fail = pygame.init(),因为bugs[1]有点模糊(我不得不查阅文档)。

别再重复了。考虑为game_sound()编写一个助手函数,以筛选出常见的表达式。

参考sys.argv0 in game_sound是一种反模式。尽早将其解析出来,并将其存储在一个命名变量中。

您的评论非常具体,例如在you_lose()上。请不要在评论中说出代码已经说得很清楚了。在某个时候,您将使它成为42 point字体,然后您将记得更新评论,或者您将有一个评论谎言。将概括性放入英语注释中,并将具体内容放入代码中。

相同的文字文本被重复几次。您应该添加一个显式常量,如字体=‘时间新罗马’。

像pause_position.midtop和score_position.midtop这样的东西可以使用像GRID_WIDTH * 0.5这样的表达式。

看来food_spawn的名字不对,应该是food_spawned。当食物是假的时候会产生更多的食物是令人惊讶的。也许像out_of_food这样的名字会更清晰一些。

您将food_position定义为一个列表,[x, y]。请给我一个元组,(x, y)。列表是可变长度的,不像元组。(在某个时候,您可能支持多个食物位置的列表,而产卵检查将测试它的长度是否为零。)

算法精化

建模方向

我建议您将key_{up、right、down、左侧}解析为从{-1、0、1}提取的dx和dy值。并以同样的方式对电流方向进行建模。这会减少“倒退”吗?做两次测试。如果将方向作为索引0..3存储到以下内容:

代码语言:javascript
复制
    delta = [(0, -1), (1, 0), (0, 1), (-1, 0)]

然后“倒退?”只是对(direction + 2) % 4的一次测试。(这是plus len(delta)/2,mod len(增量))。

推进蛇只是在snake_position中添加(dx,dy)。

检测碰撞

这似乎比必要的昂贵:

代码语言:javascript
复制
        for block in itertools.islice(snake_body, 1, None):
            if snake_position == collections.deque(block):
                you_lose()  # Game over when the Snake hits itself

考虑与set一起维护snake_body。(这是定义Snake类的一个机会。)当像素被添加到snake_body的头部时,也将它添加到集合中(恒定时间,不依赖于蛇的长度)。从尾部移除像素时,将其从set中移除。现在,您可以执行单个集合成员资格查询,而不是线性扫描。

当然,已经存在一个可以在常数(小于线性)时间内查询的数据结构。屏幕。当检测到碰撞时,询问显示是否正在移动到蛇色像素上。

票数 2
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/183343

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档