为使用pygame模块制作的python游戏文件创建可执行安装程序时出错

问题描述

我使用Python的 pygame 模块制作了一个游戏。我想做一个文件的可执行安装程序,这样任何人都可以玩该游戏,而无需安装python或pygame。我使用模块 cx-freeze 为我的游戏文件创建可执行文件。我将用于制作可执行安装程序的代码存储在名为 setup.py 文件中,并将其与游戏文件以及游戏所需的所有其他文件(例如图像,声音等)保存在同一目录中。 。 当我执行命令python setup.py build创建可执行文件时,我开始收到警告,最后发生了此错误

Traceback (most recent call last):
  File "setup.py",line 5,in <module>
    cx_Freeze.setup(
  File "C:\Users\Armaan Barak\AppData\Local\Programs\Python\python38\lib\site-packages\cx_Freeze\dist.py",line 342,in setup
    distutils.core.setup(**attrs)
  File "C:\Users\Armaan Barak\AppData\Local\Programs\Python\python38\lib\distutils\core.py",line 148,in setup
    dist.run_commands()
  File "C:\Users\Armaan Barak\AppData\Local\Programs\Python\python38\lib\distutils\dist.py",line 966,in run_commands
    self.run_command(cmd)
  File "C:\Users\Armaan Barak\AppData\Local\Programs\Python\python38\lib\distutils\dist.py",line 985,in run_command
    cmd_obj.run()
  File "C:\Users\Armaan Barak\AppData\Local\Programs\Python\python38\lib\distutils\command\build.py",line 135,in run
    self.run_command(cmd_name)
  File "C:\Users\Armaan Barak\AppData\Local\Programs\Python\python38\lib\distutils\cmd.py",line 313,in run_command
    self.distribution.run_command(command)
  File "C:\Users\Armaan Barak\AppData\Local\Programs\Python\python38\lib\distutils\dist.py",in run_command
    cmd_obj.run()
  File "C:\Users\Armaan Barak\AppData\Local\Programs\Python\python38\lib\site-packages\cx_Freeze\dist.py",line 217,in run
    freezer.Freeze()
  File "C:\Users\Armaan Barak\AppData\Local\Programs\Python\python38\lib\site-packages\cx_Freeze\freezer.py",line 645,in Freeze
    self._WriteModules(fileName,self.finder)
  File "C:\Users\Armaan Barak\AppData\Local\Programs\Python\python38\lib\site-packages\cx_Freeze\freezer.py",line 536,in _WriteModules
    sourcePackageDir = os.path.dirname(module.file)
  File "C:\Users\Armaan Barak\AppData\Local\Programs\Python\python38\lib\ntpath.py",line 223,in dirname
    return split(p)[0]
  File "C:\Users\Armaan Barak\AppData\Local\Programs\Python\python38\lib\ntpath.py",line 185,in split
    p = os.fspath(p)
TypeError: expected str,bytes or os.pathLike object,not nonetype

显示的警告是:

running build
running build_exe
pygame 1.9.6
Hello from the pygame community. https://www.pygame.org/contribute.html
C:\Users\Armaan Barak\AppData\Local\Programs\Python\python38\lib\site-packages\setuptools\distutils_patch.py:25: UserWarning: distutils was imported before Setuptools. This usage is discouraged and may exhibit undesirable behaviors or errors. Please use Setuptools' objects directly or at least import Setuptools first.
  warnings.warn(
C:\Users\Armaan Barak\AppData\Local\Programs\Python\python38\lib\site-packages\pygame\examples\chimp.py:32: SyntaxWarning: "is" with a literal. Did you mean "=="?
  if colorkey is -1:
C:\Users\Armaan Barak\AppData\Local\Programs\Python\python38\lib\site-packages\pygame\tests\test_utils\__init__.py:162: SyntaxWarning: "is not" with a literal. Did you mean "!="?
  (rect.left is not 0 and [(rect.left-1,rect.top)] or []) +
copying c:\users\armaan barak\appdata\local\programs\python\python38\python38.dll -> build\exe.win-amd64-3.8\python38.dll
copying c:\users\armaan barak\appdata\local\programs\python\python38\vcruntime140.dll -> build\exe.win-amd64-3.8\vcruntime140.dll
copying c:\users\armaan barak\appdata\local\programs\python\python38\python3.dll -> build\exe.win-amd64-3.8\python3.dll
copying C:\Users\Armaan Barak\AppData\Local\Programs\Python\python38\lib\site-packages\cx_Freeze\bases\Console.exe -> build\exe.win-amd64-3.8\SpaceInvader2.exe
*** WARNING *** unable to create version resource
version must be specified

设置文件中的代码

import cx_Freeze

executable = [cx_Freeze.Executable('SpaceInvader2.py')]

cx_Freeze.setup(
    name="Space Invaders 2",options={
        "build_exe": {
            "packages": [
                "pygame","random","math","pygame.mixer","time"
            ],"include_files": [
                "spaceship.png","space.png","home_screen.png","ammunition.png","alien.png","alien2.png","alien3.png","alien4.png","player.png","settings.png","controls.png","game_over.png","background.wav","laser.wav","explosion.wav"
            ]
        }
    },executables=executable
)

游戏文件中的代码

import pygame 
import random
import math
from pygame import mixer
import time

# Game Screen Initialization
pygame.init()
screen = pygame.display.set_mode((800,600))

# Loading Images
icon = pygame.image.load('spaceship.png')
game_background = pygame.image.load('space.png')
home_screen_img = pygame.image.load('home_screen.png')
bullet = pygame.image.load('ammunition.png')
alien_img = pygame.image.load('alien.png')
alien_img2 = pygame.image.load('alien2.png')
alien_img3 = pygame.image.load('alien3.png')
alien_img4 = pygame.image.load('alien4.png')
player_img = pygame.image.load('player.png')
settings_img = pygame.image.load('settings.png')
controls_img = pygame.image.load('controls.png')
game_over_img = pygame.image.load('game_over.png')

# Loading sounds
mixer.music.load('background.wav')
mixer.music.play(-1)

bullet_sound = mixer.sound('laser.wav')
explosion_sound = mixer.sound('explosion.wav')

# Title and Icon
pygame.display.set_caption('Space Invader')
pygame.display.set_icon(icon)

# Bullet variables
bullet_x = 0
bullet_y = 480
bullet_x_change = 0
bullet_y_change = 4
bullet_state = 'ready'

# Player variables
player_x = 370
player_y = 480
player_x_change = 0

# Enemy variables
enemy_img = []
enemy_x = []
enemy_y = []
enemy_x_change = []
enemy_y_change = []
num_of_enemies = 13

# Creating 10 enemy objects
for i in range(num_of_enemies):

    enemy_img.append(random.choice([alien_img,alien_img2,alien_img3,alien_img4]))
    enemy_x.append(random.randint(0,735))
    enemy_y.append(random.randint(50,200))
    enemy_x_change.append(2)
    enemy_y_change.append(40)

# score variables
score_value = 0
font = pygame.font.Font('freesansbold.ttf',24)
over_font = pygame.font.Font('freesansbold.ttf',80)

text_X = 10
text_y = 10

# Game Functions
def show_score(x,y):
    score = font.render('score: ' + str(score_value),True,(165,240,67))
    screen.blit(score,(x,y))

def game_over_text():
    over_text = over_font.render('GAME OVER!',(255,0))
    screen.blit(over_text,(160,250))

def player(x,y):
    screen.blit(player_img,y))

def enemy(x,y,i):
    screen.blit(enemy_img[i],y))

def fire_bullet(x,y):
    global bullet_state
    bullet_state = 'fire'
    screen.blit(bullet,(x + 16,y + 10))

def is_collision(enemyX,enemyY,bulletX,bulletY):
    distance = math.sqrt((math.pow(enemyX - bulletX,2)) + (math.pow(enemyY-bulletY,2)))
    return distance < 27

# Button colors and fonts
color = (0,0)
color_light = (100,149,237)
color_dark = (138,43,226)

# Fonts
game_name_font = pygame.font.SysFont("Chiller",140)
btn_font = pygame.font.SysFont('Corbel Bold',34)
random_font = pygame.font.SysFont('Arial',60)
info_btn = pygame.font.SysFont('Century Gothic',24)
controls_font = pygame.font.SysFont('Consolas',15)
credits_btn = pygame.font.SysFont('Consolas',18)

# Texts
game_text = game_name_font.render('Space Invaders!',255,0))
play_text = btn_font.render('Play Game',color)
controls_text = btn_font.render('Controls',color)
credits_text = btn_font.render('Credits',color)
settings_text = btn_font.render('Settings',color)
quit_text = btn_font.render('Quit Game',color)
version_text = btn_font.render('Version 2.8',255))
back_text = btn_font.render('Go Back',color)
under_development_text = random_font.render('This area is under development',(0,0))

# Controls text
controls_1 = controls_font.render('Left Arrow   :   Spaceship moves Left',(124,252,0))
controls_2 = controls_font.render('Right Arrow  :  Spaceship moves Right',0))
controls_3 = controls_font.render('Spacebar     :         Shoots bullets',0))

# Credits text
credits_line_1 = credits_btn.render("The games first version was made while learning from the pygame video",255))
credits_line_2 = credits_btn.render("of FreeCodeCamp.org. The successive updates in version 1 and 2 are",255))
credits_line_3 = credits_btn.render("made by Armaan Barak. No external code was copied. But help was taken",255))
credits_line_4 = credits_btn.render("from google. Images and icons shown this game are taken from freepik.com",255))
credits_line_5 = credits_btn.render("and flaticon.com respectively. The images were resized from",255))
credits_line_6 = credits_btn.render("reduceimages.com and the sounds are imported from github account of",255))
credits_line_7 = credits_btn.render("Mr. Attreya Bhatt. Direct Link to repository:",255))
credits_line_8 = credits_btn.render("https://github.com/attreyabhatt/Space-Invaders-Pygame.",255))
credits_line_9 = credits_btn.render("This game is free and open-source. User is independent for",255))
credits_line_10 = credits_btn.render("manipulating code but it's encouraged that you avoid cheating.",255))
credits_line_11 = credits_btn.render("Creator and Developer: Armaan Barak",255))

# Game loop variables
running = True
home_screen = True
settings = False
controls = False
game_screen = False
credits_screen = False
game_over_screen = False

while running:

    # To loop through events untill event is QUIT
    for event in pygame.event.get():

        # QUIT Button
        if event.type == pygame.QUIT:
            running = False

        # Mouse key controls
        if event.type == pygame.MOUSEBUTTONDOWN:
            
            # Game is Over
            if game_over_screen:
                
                if 250 <= mouse[0] <= 385 and 520 <= mouse[1] <= 560:
                    home_screen = True
                    settings = False
                    controls = False
                    game_screen = False
                    credits_screen = False
                    game_over_screen = False

                    for i in range(num_of_enemies):
                        enemy_y[i] = random.randint(50,200)
                    
                    player_y = 480
                    bullet_y = 480

                if 420 <= mouse[0] <= 560 and 520 <= mouse[1] <= 560:
                    quit()

            # Play button clicked
            if 230 <= mouse[0] <= 370 and 270 <= mouse[1] <= 310:
                home_screen = False
                settings = False
                controls = False
                game_screen = True
                credits_screen = False

            # Instructions button clicked
            if 400 <= mouse[0] <= 540 and 270 <= mouse[1] <= 310:
                home_screen = False
                settings = False
                controls = True
                game_screen = False
                credits_screen = False

            # Settings button clicked
            if 230 <= mouse[0] <= 370 and 350 <= mouse[1] <= 390:
                home_screen = False
                settings = True
                controls = False
                game_screen = False
                credits_screen = False
            
            # Credits button clicked
            if 400 <= mouse[0] <= 540 and 350 <= mouse[1] <= 390:
                home_screen = False
                settings = False
                controls = False
                game_screen = False
                credits_screen = True

            # Quit button clicked
            if 315 <= mouse[0] <= 455 and 430 <= mouse[1] <= 470:
                quit()

            if settings or controls or credits_screen:

                if 50 <= mouse[0] <= 165 and 30 <= mouse[1] <= 70:
                    home_screen = True
                    settings = False
                    controls = False
                    game_screen = False
                    credits_screen = False

        # KeyBoard Controls
        if event.type == pygame.KEYDOWN:

            if event.key == pygame.K_LEFT:
                player_x_change = -3

            if event.key == pygame.K_RIGHT:
                player_x_change = 3
            
            if event.key == pygame.K_SPACE:
                if bullet_state == 'ready':
                    
                    bullet_sound.play()
                    bullet_x = player_x
                    fire_bullet(bullet_x,bullet_y)

        # Reseting player's x axis speed when left or right key is lifted
        if event.type == pygame.KEYUP:

            if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT:
               player_x_change = 0
    
    # Home Screen
    if home_screen:
        
        # displaying main screen
        screen.blit(home_screen_img,0))

        # Getting Mouse Position
        mouse = pygame.mouse.get_pos()

        # Changing color of text Boxes on the basis of cursor position
        # play game text
        if 230 <= mouse[0] <= 370 and 270 <= mouse[1] <= 310:
            pygame.draw.rect(screen,color_light,[230,270,140,40])
        
        else:
            pygame.draw.rect(screen,color_dark,40])

        # instructions text
        if 400 <= mouse[0] <= 540 and 270 <= mouse[1] <= 310:
            pygame.draw.rect(screen,[400,40])

        # settings text
        if 230 <= mouse[0] <= 370 and 350 <= mouse[1] <= 390:
            pygame.draw.rect(screen,350,40])

        # credits text
        if 400 <= mouse[0] <= 540 and 350 <= mouse[1] <= 390:
            pygame.draw.rect(screen,40])

        # quit game text
        if 315 <= mouse[0] <= 455 and 430 <= mouse[1] <= 470:
            pygame.draw.rect(screen,[315,430,40])

        # displaying text
        screen.blit(game_text,(90,100))

        # displaying buttons
        screen.blit(play_text,(240,280))

        screen.blit(controls_text,(421,280))

        screen.blit(settings_text,(253,360))

        screen.blit(credits_text,(427,360))

        screen.blit(quit_text,(325,440))

        # Version display
        screen.blit(version_text,580))

        # Updating Screen
        pygame.display.update()

    # Settings Screen
    elif settings:

        screen.blit(settings_img,0))

        mouse = pygame.mouse.get_pos()

        if 50 <= mouse[0] <= 165 and 30 <= mouse[1] <= 70:
            pygame.draw.rect(screen,[50,30,115,40])
        else:
            pygame.draw.rect(screen,40])

        screen.blit(back_text,(60,40))

        screen.blit(under_development_text,250))

        pygame.display.update()
    
    # Controls Screen
    elif controls:
        
        screen.blit(controls_img,40))

        screen.blit(controls_1,240))
        screen.blit(controls_2,290))
        screen.blit(controls_3,340))

        pygame.display.update()

    # Credits Screen
    elif credits_screen:

        screen.fill((85,107,47))

        mouse = pygame.mouse.get_pos()

        if 50 <= mouse[0] <= 165 and 30 <= mouse[1] <= 70:
            pygame.draw.rect(screen,40))

        screen.blit(credits_line_1,(40,150))
        screen.blit(credits_line_2,180))
        screen.blit(credits_line_3,210))
        screen.blit(credits_line_4,240))
        screen.blit(credits_line_5,270))
        screen.blit(credits_line_6,300))
        screen.blit(credits_line_7,330))
        screen.blit(credits_line_8,360))
        screen.blit(credits_line_9,390))
        screen.blit(credits_line_10,420))
        screen.blit(credits_line_11,450))

        pygame.display.update()

    # Game Screen
    else:

        # Adding background image
        screen.blit(game_background,0))

        # Player Movement
        player_x += player_x_change

        if player_x <= 0:
            player_x = 0
        elif player_x >= 736:
            player_x = 736

        # Handling movement of enemies
        for i in range(num_of_enemies):

            # Game Over
            if enemy_y[i] > 450:
                
                game_over_screen = True

                # Hiding Characters
                for j in range(num_of_enemies):
                    enemy_y[j] = 2000

                player_y = -2000
                bullet_y = -4000

                screen.blit(game_over_img,0))
                
                # Back button
                if 250 <= mouse[0] <= 385 and 520 <= mouse[1] <= 560:
                    pygame.draw.rect(screen,[250,520,40])
                else:
                    pygame.draw.rect(screen,40])

                # Quit Button
                if 420 <= mouse[0] <= 560 and 520 <= mouse[1] <= 560:
                    pygame.draw.rect(screen,[420,40])

                mouse = pygame.mouse.get_pos()

                screen.blit(back_text,(270,530))

                screen.blit(quit_text,(430,530))

            # Enemy Movement
            enemy_x[i] += enemy_x_change[i]

            if enemy_x[i] <= 0:
                enemy_x_change[i] = 2
                enemy_y[i] += enemy_y_change[i]
            elif enemy_x[i] >= 736:
                enemy_x_change[i] = -2
                enemy_y[i] += enemy_y_change[i]

            # Collision control
            collision = is_collision(enemy_x[i],enemy_y[i],bullet_x,bullet_y)

            if collision:
                
                explosion_sound.play()
                bullet_y = 480
                bullet_state = 'ready'
                score_value += 1
                enemy_x[i] = random.randint(0,735)
                enemy_y[i] = random.randint(50,200)

            # drawing enemy
            enemy(enemy_x[i],i)
        
        # Bullet Movement
        if bullet_y <= 0:
            bullet_y = 480
            bullet_state = 'ready'

        if bullet_state == "fire":
            fire_bullet(bullet_x,bullet_y)
            bullet_y -= bullet_y_change

        # drawing player
        player(player_x,player_y)

        # Version display
        screen.blit(version_text,580))

        # drawing score card
        show_score(text_X,text_y)

        # Updates screen
        pygame.display.update()

我尝试使用命令python setup.py bdist_msi,但发生了同样的事情。

我正在使用python 3.8.6rc1版本,并且我也尝试使用pyinstaller,但是每当我尝试运行pyinstaller生成的可执行文件时,都会说无法执行游戏文件

我知道这很容易阅读,很难理解。但是我在这里严重需要一些帮助。 如果有人可以建议做什么或如何解决此问题,那将非常有帮助。请告诉我是否需要更多信息来为我提供解决方案。

P.S。所有游戏文件都保存在同一目录中,并且游戏运行无任何错误。我面对的唯一麻烦是在创建此问题的可执行安装程序时。

谢谢

解决方法

我以前没有使用过cxFreeze,所以我不能直接帮助您解决问题,但是我真的建议您使用pyinstaller来打包python程序。我以前在pygame中使用过它,并且效果很好,甚至不需要安装文件。

可以使用pip安装Pyinstaller,并打包您调用的文件: pyinstaller file.py,以及您可能需要的所有标志。通常,我使用pyinstaller file.py --onefile --noconsole来创建一个可执行文件。

Pyinstaller不会打包资产文件,因此您必须随项目一起提供它们,但是项目使用的所有python文件(包括库)都已打包,因此项目中不需要提供任何代码。

Pyinstaller有时可能很气质,所以我建议首先创建一个简单的hello world文件,并使用pyinstaller helloworld.py --onefile打包,然后在dist/文件夹中运行该文件。

docs对于pyinstaller并不总是很有帮助,但是我发现学习打包项目的最佳方法是慢慢增加尝试打包的项目的复杂性,并在打包后检查每个项目的工作。 / p>

如果您使用相对脚本和资产导入(例如pygame.image.load('image.png')),则需要将生成的.exe移动到原始脚本的位置(.exe通常在dist/文件夹,如果您使用相对导入并从那里运行.exe,则该文件夹将不起作用。

如果要添加图标,可以使用--icon标志,如下所示:pyinstaller file.py --icon=image.ico(图标文件必须是.ico文件-如果出现以下情况,您可以始终在线上进行转换:需要)