Я просто взломщик сценариев и идиот, когда дело доходит до инструментов командной строки. Но я ДЕЙСТВИТЕЛЬНО хотел упаковать свою игру на Python в исполняемый файл Windows. Я боролся с pyinstaller в течение нескольких дней, и прочитал все темы других, которые также пытаются выяснить это, и я также прочитал все документы по pyinstaller несколько раз. Ни одно решение не сработало для меня, но после долгих проб и ошибок я, наконец, наткнулся на рецепт успеха и хочу поделиться им на случай, если другие разработчики пигмеев будут биться с этим. Решение содержит биты и кусочки нескольких потоков.
Как создать одиночный exe-файл из многофайловой Python-игры Python с несколькими каталогами ресурсов с помощью pyinstaller в Windows:
Сначала установите pyinstaller.
Откройте командную строку Windows, введите:
pip install pyinstaller
Создайте игру на Python. Скажем, основной файл игры называется main_game_script.py, расположенный в
C:\Users\blueb\Documents\python\game_dir
Добавьте эту функцию в main_game_script.py. Обязательно импортируйте os и sys. (Я бы никогда не понял, что мне нужно добавить эту функцию в мою игру, но она работает, так что спасибо тому, кто опубликовал ее в какой-то другой теме)
import sys
import os
def resource_path(relative_path):
try:
# PyInstaller creates a temp folder and stores path in _MEIPASS
base_path = sys._MEIPASS
except Exception:
base_path = os.path.abspath(".")
return os.path.join(base_path, relative_path)
Загрузка изображений в игру , вызовите эту функцию, например, так:
asset_url = resource_path('assets/chars/hero.png')
hero_asset = pygame.image.load(asset_url)
Когда ваша игра будет готова к переносу в EXE, откройте командную строку Windows. Перейдите в основной каталог игры и введите:
pyinstaller --onefile main_game_script.py
Это будет выглядеть так:
C:\Users\blueb\Documents\python\game_dir>pyinstaller --onefile main_game_script.py
Это создаст четыре важных элемента:
- a main_game_script.spec file in the game_dir
- a 'build' dir in the game_dir
- a 'dist' directory in the game_dir
- a main_game_script.exe will be created in the 'dist' directory.
Выполнение команды выдает массу предупреждений и логов. Я не знаю, что все это значит. Но до тех пор, пока в итоговой выходной строке говорится об успехе, вы, вероятно, хороши. Ваш exe не будет работать пока. Во-первых, вы должны изменить файл .spec (который теперь существует в game_dir), чтобы добавить необходимые пути к игровым активам в exe:
Откройте файл main_game_script.spec в блокноте или в любом другом месте. Замените пустой список данных [] на пути к каталогам ресурсов, например, такие (используя кортежи!):
datas=[('assets/chars/*','assets/chars'),('assets/tiles/*.png','assets/tiles'),('assets/fonts/*','assets/fonts')],
Первое значение каждого кортежа - это фактические имена файлов, которые вы хотите импортировать. Второе значение - это относительный путь от вас main_game_script.py Синтаксис и пути должны быть идеальными, иначе они не будут работать и могут не выдавать ошибку
Сохраните и закройте файл 'main_game_script.spec'. Вернитесь в командную строку Windows. Тип:
pyinstaller main_game_script.spec
Примерно так:
C:\Users\blueb\Documents\python\game_dir>pyinstaller main_game_script.spec
Это каким-то образом встраивает пути dir активов в exe, который вы уже построили
После всего этого (которое Это займет всего пару минут после того, как вы это сделаете 100 раз), у вас, наконец, будет один EXE-файл вашей игры с питоном, который включает в себя все игровые ресурсы. Если вы все сделали правильно, дважды щелкните по EXE, чтобы поиграть в игру с питоном.
Примечание: чтобы понять все правильно, я потратил пару дней. Я начал с простого test_game_script.py с минимальным количеством кода и методично итерировал, добавляя больше путей к ресурсам и сложности с каждой итерацией, чтобы я мог изолировать, что было сломано и почему. Если ваша игра не конвертируется в EXE должным образом, начните с простого 5-строчного игрового сценария с одним активом и медленно наращивайте его до тех пор, пока у вас не будет работающего конвейера, затем примените этот функционирующий конвейер к активным играм. В противном случае изолировать проблему будет невозможно.
Для диалоговых окон я переключил от blockUI до JQuery UI. Я думаю, что это лучше.
Вот расширение, с которым я столкнулся и изменил немного. Смотря на него теперь, я думаю, что это на самом деле немного грязно и могло использовать уборку (существуют некоторые неиспользуемые переменные, я думаю),
$.fn.vCenter = function(options) {
var pos = {
sTop : function() {
return window.pageYOffset || $.boxModel && document.documentElement.scrollTop || document.body.scrollTop;
},
wHeight : function() {
if ($.browser.opera || ($.browser.safari && parseInt($.browser.version) > 520)) {
return window.innerHeight - (($ (document).height() > window.innerHeight) ? getScrollbarWidth() : 0);
} else if ($.browser.safari) {
return window.innerHeight;
} else {
return $.boxModel && document.documentElement.clientHeight || document.body.clientHeight;
}
}
};
return this.each(function(index) {
if (index == 0) {
var $this = $(this),
$w = $(window),
topPos = ($w.height() - $this.height()) / 2 + $w.scrollTop()
;
if (topPos < 0 && (options == undefined || !options.allowNegative)) topPos = 0;
$this.css({
position: 'absolute',
marginTop: '0',
top: topPos //pos.sTop() + (pos.wHeight() / 2) - (elHeight / 2)
});
}
});
};
$.fn.hCenter = function(options) {
return this.each(function(index) {
if (index == 0) {
var $this = $(this),
$d = $(document),
leftPos = ($d.width() - $this.width()) / 2 + $d.scrollLeft()
;
if (leftPos < 0 && (options == undefined || !options.allowNegative)) leftPos = 0;
$this.css({
position: "absolute",
left: leftPos
});
}
});
};
$.fn.hvCenter = function(options) {
return this.vCenter(options).hCenter(options);
};
Использование:
$('#verticalCenter').vCenter();
$('#horizontalCenter').hCenter();
$('#bothCentered').hvCenter();