首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >2D基本“避敌”类游戏

2D基本“避敌”类游戏
EN

Code Review用户
提问于 2018-01-09 23:10:27
回答 1查看 216关注 0票数 2

我正在努力学习如何成为一名优秀的程序员。我认为这意味着让我的代码更容易为其他人所读。我真的很希望得到任何反馈。我不太清楚我最缺乏的是什么,所以这是我所能得到的最精确的。

目前,我已经将我的程序分成了五个部分:

  1. 游戏:运行游戏的主程序
  2. 'entitie.py':描述实体如何工作(包括敌人实体和玩家实体).*
  3. ‘屏幕. of’:描述屏幕的工作方式,负责闪烁文本和绘制所有实体
  4. ‘level s.py’:描述敌人是如何在每个等级中产卵的,以及达到下一个级别时会发生什么
  5. ‘gamics.py’:真的只是一个文件,我打算把我所有的函数都扔到其他地方,不适合其他地方。

我听说过‘继承’,我想最好把敌人和玩家实体分割起来,使用一个更基本的实体的重叠,但我从来没有这样做过,我觉得代码的可读性会变差。

game.py:

代码语言:javascript
复制
"""
Written by Nathan van 't Hof
9 January 2018

Main file used to control the game.
"""

from entitie import Entity
import screen
import pygame
import sys
import os
import time
import levels


def init():
    player = Entity('player', x=300, y=500)
    entities = []
    return player, entities


def update(player, entities):
    player.update_movement()
    for entity in entities:
        entity.update_movement()
    return player, entities

# initialize values
pygame.mixer.pre_init(44100, 16, 2, 4096)
pygame.init()
player, entities = init()
playing = True

# play music
pygame.mixer.music.load(os.path.join(os.getcwd(), "background.mp3"))
pygame.mixer.music.set_volume(0.5)
pygame.mixer.music.play(-1)

# set display
display = screen.display()

# set basic values
level = 0
list_levels = [levels.level_0, levels.level_1, levels.level_2, levels.level_3, levels.level_4, levels.level_5]
previous_addition = time.time()
dt = 1

while playing:
    # update values
    player, entities = update(player, entities)
    player.update_score()
    level, entities, difficulty = levels.check_level_up(player.score, level, entities)

    # check if player wants to leave
    for event in pygame.event.get():
        if event.type == pygame.QUIT or event.type == pygame.KEYDOWN:
            playing = False

    # check if player died
    restart = player.check_die(entities, display)
    if restart > 0:
        level = 0
        entities = []

    # allow player to move
    player.interact(display.WIDTH, display.HEIGHT)

    # add enemies
    if time.time() - previous_addition > dt:
        entities, dt = list_levels[level](entities, display, player.x, player.y, player.score, difficulty)
        previous_addition = time.time()

    # draw all the levels
    entities = display.draw(entities, player)

# exit properly
pygame.quit()
sys.exit()

entitie.py

代码语言:javascript
复制
"""
Written by Nathan van 't Hof
9 January 2018

This is an entity class which can both be used for the player
as well as for enemies.

Player is a square whose velocity is dependant on an acceleration, dependant on distance of mouse in regards to
player object
Enemies are constant velocity squares

If player collides with a non-same color enemy, one life is retracted
"""

import game_mechanics
import time
import pygame
import win32api
from random import choice
import ctypes

# to ensure the mouse lines up with the pixels
user32 = ctypes.windll.user32
user32.SetProcessDPIAware()

possible_colors = [(250,0,0), (0,250,0), (0,0,250)]


class Entity:

    def __init__(self, type_entity, x=0, y=0, vx=0, vy=0, width=20):
        if type_entity == 'player':
            self.lives = 3
            self._input = True
            self.color = (0, 0, 250)
            self._width = 10
            self.high_score = 0

        if type_entity == 'enemy':
            self.lives = 1
            self._input = False
            self.color = choice(possible_colors)
            self._width = width

        self.score = 0

        self.x = x
        self._vx = vx
        self._ax = 0

        self.y = y
        self._vy = vy
        self._ay = 0

        self._last_updated = time.time()
        self._time_initiated = time.time()
        self._time_last_died = time.time()

        self._state_left_mouse = 0

    def update_movement(self):
        dt = time.time() - self._last_updated
        self._last_updated = time.time()
        self.x += self._vx * dt + 0.5 * self._ax * dt * dt
        self._vx += dt * self._ax
        self.y += self._vy * dt + 0.5 * self._ay * dt * dt
        self._vy += dt * self._ay

        # check if it is the player
        if self._input:

            # check if left mouse has been pressed, if so, it changes color
            new_state_left_mouse = win32api.GetKeyState(0x01)
            # Button state changed
            if new_state_left_mouse != self._state_left_mouse:
                self._state_left_mouse = new_state_left_mouse
                if new_state_left_mouse < 0:
                    self.color = (self.color[1], self.color[2], self.color[0])

    def update_score(self):
        self.score = int(time.time() - self._time_initiated)
        self.high_score = max(self.high_score, self.score)

    def check_die(self, entities, display):
        restart = 0

        # cannot immediately die
        if time.time() - self._time_last_died > 3:
            # check if any of the enemies cross the player
            for entity in entities:
                if entity.color != self.color:
                    if game_mechanics.collision_detect(self.x, entity.x, self.y, entity.y, self._width, entity._width, self._width, entity._width):
                        restart += self.die()
                        break
            # check if player is out of bounds
            if self.x > display.WIDTH or self.x < 0 or self.y > display.HEIGHT or self.y < 0 and restart == 0:
                restart = self.die()
        return restart

    def die(self):
        self._time_last_died = time.time()
        self.lives -= 1
        self.x = 500
        self.y = 500
        if self.lives == 0:
            self.restart()
            return 1
        return 0

    def restart(self):
        self.score = 0
        self.lives = 3
        self.x = 200
        self._vx = 0
        self._ax = 0

        self.y = 200
        self._vy = 0
        self._ay = 0

        self._last_updated = time.time()
        self._time_initiated = time.time()

    def interact(self, screen_x, screen_y):
        # only if the entity is the player (not really necessary, just an extra precaution)
        if self._input:
            # determine accelerations based on distance of mouse to player object
            x_mouse, y_mouse = win32api.GetCursorPos()
            dx = ((x_mouse - self.x)/screen_x)
            dy = ((y_mouse - self.y)/screen_y)

            self._ax = min(300000, 15000 * dx)
            self._ay = min(300000, 15000 * dy)

            # break hard when you deccelerate
            if self._ax * self._vx < 0 and self._ay * self._vy < 0:
                self._vx = self._vx * 0.8
                self._vy = self._vy * 0.8

    def draw(self, display):
        pygame.draw.rect(display, self.color, (self.x, self.y, self._width, self._width))

screen.py

代码语言:javascript
复制
"""
Written by Nathan van 't Hof
9 January 2018

The screen object all the objects are drawn on.
"""


import pygame
from win32api import GetSystemMetrics

class display:

    def __init__(self):

        self.BLACK = (0, 0, 0)
        self.WIDTH = GetSystemMetrics(0)
        self.HEIGHT = GetSystemMetrics(1)

        # full screen
        self.windowSurface = pygame.display.set_mode((self.WIDTH, self.HEIGHT), pygame.FULLSCREEN)
        self.windowSurface.fill(self.BLACK)

        self.font = pygame.font.Font(None, 32)

    def draw(self, entities, player):
        self.windowSurface.fill(self.BLACK)
        new_entities = []
        for entity in entities:
            entity.draw(self.windowSurface)
            # if entity is no longer in screen it is not added to the entity list, meaning it is no longer kept track of
            if not (entity.x > self.WIDTH or entity.x < -entity._width or
                    entity.y > self.HEIGHT or entity.y < -entity._width):
                new_entities.append(entity)
        player.draw(self.windowSurface)

        label = self.font.render('score : ' + str(player.score), 1, (250,250,250))
        self.windowSurface.blit(label, (20, 20))

        label = self.font.render('lives : ' + str(player.lives), 1, (250,250,250))
        self.windowSurface.blit(label, (20, 40))

        label = self.font.render('high-score : ' + str(player.high_score), 1, (250, 250, 250))
        self.windowSurface.blit(label, (20, 60))

        pygame.display.flip()
        return new_entities

levels.py

代码语言:javascript
复制
"""
Written by Nathan van 't Hof
9 January 2018

Used to control the behaviour of new enemies per level.
"""


from entitie import Entity
from random import randint


def level_up(level, entities):
    level += 1
    if level == 1:
        for entity in entities:
            entity._vx = 50
    elif level == 3:
        for entity in entities:
            entity._vx = -80
    return level, entities


def check_level_up(score, level, entities):
    """
    Keeps track of what level the player is currently in,
    once the last level is reached the difficulty is ramped up and it starts over.
    """
    max_level = 5
    difficulty = 1 + (score / 80) * 0.2
    correct_level = (score % 80) / 15

    if level == 0 and score > 7:
        level, entities = level_up(level, entities)
    elif correct_level > level and level != max_level:
        level, entities = level_up(level, entities)
    elif correct_level < level:
        level = correct_level
    return level, entities, difficulty


def level_0(entities, display, x_play, y_play, score, difficulty):
    """
    Adds entities with 0 speed at random locations in the field
    """
    entity = Entity('enemy',
                            x=randint(0,int(display.WIDTH)),
                            y=randint(0,int(display.HEIGHT)),
                            width=randint(10,40) * difficulty
                            )
    entities.append(entity)
    dt = 0.1 / difficulty
    return entities, dt


def level_1(entities, display, x_play, y_play, score, difficulty):
    """
    Adds entities on the left of the field, moving towards the right.
    """
    entity = Entity('enemy',
                             x = 0,
                             y = randint(0,int(display.HEIGHT)),
                             vx = 80,
                             width = randint(10,100)* difficulty
                             )
    entities.append(entity)
    dt = 0.7/ difficulty
    return entities, dt


def level_2(entities, display, x_play, y_play, score, difficulty):
    """
    Adds entities on the left of the field, moving towards the right.
    """
    for i in range(2):
        entity = Entity('enemy',
                                x = 0,
                                y = randint(0,int(display.HEIGHT)),
                                vx = randint(70, 120),
                                width = randint(10,60)* difficulty
                                )
        entities.append(entity)
    dt = 0.7/ difficulty
    return entities, dt


def level_3(entities, display, x_play, y_play, score, difficulty):
    """
    Adds entities on the right of the field, moving towards the left.
    """
    for i in range(3):
        entity = Entity('enemy',
                                x = display.WIDTH,
                                y = randint(0,int(display.HEIGHT)),
                                vx = randint(-180, -90),
                                vy = randint(-30, 30),
                                width = randint(10,70)* difficulty
                                )
        entities.append(entity)
    dt = 0.4/ difficulty
    return entities, dt

def level_4(entities, display, x_play, y_play, score, difficulty):
    """
    Adds entities around the field, moving towards the player.
    """
    positions = [[randint(0, display.WIDTH), 0],
                 [randint(0, display.WIDTH), display.HEIGHT],
                 [0, randint(0, display.HEIGHT)],
                 [display.WIDTH, randint(0, display.HEIGHT)]]
    for position in positions:
        entity = Entity('enemy',
                                x = position[0],
                                y = position[1],
                                vx = (x_play - position[0]) / 8,
                                vy = (y_play - position[1]) / 8,
                                width = randint(10,60)* difficulty
                                )
        entities.append(entity)
    dt = 70./ score / difficulty
    return entities, dt


def level_5(entities, display, x_play, y_play, score, difficulty):
    """
    Adds entities around the field, moving towards the player.
    Also adds entities going from left to right
    """
    positions = [[randint(0, display.WIDTH), 0],
                 [randint(0, display.WIDTH), display.HEIGHT],
                 [0, randint(0, display.HEIGHT)],
                 [display.WIDTH, randint(0, display.HEIGHT)]]
    for position in positions:
        entity = Entity('enemy',
                                x = position[0],
                                y = position[1],
                                vx = (x_play - position[0]) / 8,
                                vy = (y_play - position[1]) / 8,
                                width = randint(10,60)* difficulty
                                )
        entities.append(entity)

    for i in range(2):
        entity = Entity('enemy',
                                x = 0,
                                y = randint(0,int(display.HEIGHT)),
                                vx = randint(70, 120),
                                width = randint(10,60)* difficulty
                                )
        entities.append(entity)

    dt = 50./score / difficulty
    return entities, dt            

game_mechanics.py

代码语言:javascript
复制
"""
Written by Nathan van 't Hof
9 January 2018

This is used for extra mechanics that don't fit in anywhere else properly.

Currently only used to detect if two squares overlap.
"""


def collision_detect(x1, x2, y1, y2, w1, w2, h1, h2):
    """
    Check whether two rectangles (both parallel to the x-y axes) overlap

    :param x1: x value specified corner rectangle 1
    :param x2: x value specified corner rectangle 2
    :param y1: y value specified corner rectangle 1
    :param y2: y value specified corner rectangle 2
    :param w1: width rectangle 1
    :param w2: width rectangle 2
    :param h1: height rectangle 1
    :param h2: height rectangle 2
    :return: True if rectangles do overlap, else False
    """
    if x1 > x2 and x1 < x2 + w2 or x1 + w1 > x2 and x1 + w1 < x2 + w2:
        if y1 > y2 and y1 < y2 + h2 or y1 + h1 > y2 and y1 + h1 < y2 + h2:
            return True
    return False
EN

回答 1

Code Review用户

回答已采纳

发布于 2018-01-10 01:42:55

让我们从几件小事开始:

  1. 您的碰撞检测例程是不需要的,因为吡咯烷酮为长方形精灵。提供了一个碰撞检测器,只需调用任何最合适的obj.collideXXX风格。
  2. 您需要导入特定于windows的模块几次。首先,要注意游戏中有鼠标支撑,,所以您不需要调用Windows获取鼠标信息。接下来,请在界面后面隐藏特定于windows的模块和代码.理想情况下,以程序实际在windows上运行为条件。您可以为此使用platform模块

现在,在这些问题上,以下是一些没有具体顺序的建议:

  • 您的level_0level_1等函数在levels.py中应该是对象。返回元组是因为根据级别有多个数据块--这是对象的一个很好的指示符。类级别:通过类L1(级别):def create_enemies(自定义,显示):#.回复敌人延迟超时(自我,困难):返回0.1 /难度
  • 我建议您创建一个Player子类的Entity。实体类中存在太多不适用于敌人的特定于玩家的代码。您可能还想创建一个Enemy子类,但这可能不是必要的。先试一试,看看你能得到什么。
  • 您的check_level_up需要大量输入,并返回大量输出。你应该把它变成玩家的方法。如果看起来合适的话,您也可以将级别设置为player属性。或者,只需执行如下操作: if player.level_up():player.level += 1 cur_level = Levelsplayer.level
  • 您的display.draw函数不应该过滤仍然活着的敌人。display对象不应该知道这方面的任何事情。使用与主屏幕的碰撞检测来确定画什么,让敌人在屏幕外移动时杀死自己。(也就是说,让实体类来处理它,因为您是在讨论特定于实体的数据。)
票数 3
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

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

复制
相关文章

相似问题

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