Kivy游戏窗口无Python报错却未响应后崩溃问题求助
Kivy游戏初始化GameController时窗口崩溃问题解决
问题描述
使用Kivy开发游戏,三个基础菜单屏幕单独运行及屏幕间切换均正常,但点击“开始游戏”按钮、初始化GameController类并切换到GameScreen时窗口崩溃。尝试用time.sleep(3)延迟加载菜单无效,仅不初始化GameController时程序可正常运行。
原代码
Python脚本
#importing all of the libraries I will be using from kivy.app import App from kivy.uix.boxlayout import BoxLayout from kivy.uix.button import Button from kivy.uix.label import Label from kivy.graphics import Color, Rectangle from kivy.core.window import Window from kivy.logger import Logger from kivy.uix.screenmanager import ScreenManager, Screen from kivy.lang import Builder from kivy.properties import ObjectProperty, StringProperty, NumericProperty from kivy.uix.widget import Widget import random import time class GameController: def __init__(self, num_players): self.game_deck=Deck() self.players=[] self.num_players=num_players self.shuffled_deck=self.shuffle_deck(self.game_deck) def shuffle_deck(self,deck): shuffled_deck=[] while len(deck.cards)!=0: temp=0 for i in range(len(deck.cards)-1): r=random.randint(0,len(deck.cards)-1) temp=deck.cards.pop(r) shuffled_deck.append(temp) return shuffled_deck class Deck: def __init__(self): self.cards=[] for suit in ['hearts', 'diamonds', 'spades', 'clubs']: for face in range(1,14): image=str(str(suit)+str(face)+'.png') card=Card(suit, face, image) self.cards.append(card) def get_deck(self): return self.cards class Card: def __init__(self,face,suit,image): self.face=face self.suit=suit self.image=image class Players: def __init__(self,Username,number): self.deck=GameController.deal self.name=Username self.is_in=True self.number=number ################################################################################## class MainMenu(Screen): def close_game(self): App.get_running_app().stop() #When this procedure is called, it closes the app Window.close() class SingleMenu(Screen): def start_game(self): game_screen =GameScreen(name='game_screen') self.manager.current ='game_screen' time.sleep(3) num_players =3 game_controller =GameController(num_players) class GameScreen(Screen): player_cards=NumericProperty(None) current_player=StringProperty(None) class IrishSnapsApp(App): def build(self): #This builds the actual application sm=ScreenManager() sm.add_widget(MainMenu(name='main_menu')) #adds the widget under the name main_Menu sm.add_widget(SingleMenu(name='single_menu'))#adds the widget under the name single_menu sm.add_widget(GameScreen(name='game_screen')) sm.current='main_menu' #starts with the application up with the 'main_menu' return sm root=IrishSnapsApp()#creates the root widget for the app root.run()#runs the app
Kv文件
<MainMenu>: BoxLayout: canvas.before: Color: rgba:0.098,0.301,0.2,1 Rectangle: pos: self.pos size: self.size orientation:'vertical' padding: 50 Label: text: 'Irish Snaps' size_hint: 1, 0.2 font_size: 30 Button: id: Singleplayer text: 'Singleplayer' size_hint: 0.5, 0.2 font_size: 24 background_color: (0.811,0.745,0.549,1) pos_hint: {"center_x":0.5} on_press: root.manager.current= 'single_menu' Button: size_hint: 0.5, 0.1 font_size: 24 background_color: (0.811,0.745,0.549,0) Button: id: Multiplayer text: 'Multiplayer' size_hint: 0.5, 0.2 font_size: 24 background_color: (0.811,0.745,0.549,1) pos_hint: {"center_x":0.5} Button: size_hint: 0.5, 0.1 font_size: 24 background_color: (0.811,0.745,0.549,0) Button: id: Statistics text: 'Statistics' size_hint: 0.5, 0.2 font_size: 24 background_color: (0.811,0.745,0.549,1) pos_hint: {"center_x": 0.5} Button: size_hint: 0.5, 0.1 font_size: 24 background_color: (0.811,0.745,0.549,0) Button: id: Closegame text: 'Close game' size_hint: 0.5, 0.2 font_size: 24 background_color: (0.811,0.745,0.549,1) pos_hint: {"center_x":0.5} on_press: root.close_game() <SingleMenu>: BoxLayout: canvas.before: Color: rgba:0.098,0.301,0.2,1 Rectangle: pos: self.pos size: self.size orientation:'vertical' padding: 50 Label: text: 'SinglePlayer' size_hint: 1, 0.2 font_size: 30 Button: id: Start_game text: 'Start game' size_hint: 0.5, 0.2 font_size: 24 background_color: (0.811,0.745,0.549,1) pos_hint: {"center_x":0.5} on_press: root.start_game() Button: size_hint: 0.5, 0.1 font_size: 24 background_color: (0.811,0.745,0.549,0) Button: id: Number_Of_Bots text: 'Number Of Bots' size_hint: 0.5, 0.2 font_size: 24 background_color: (0.811,0.745,0.549,1) pos_hint: {"center_x":0.5} Button: size_hint: 0.5, 0.1 font_size: 24 background_color: (0.811,0.745,0.549,0) Button: id: Difficulty text: 'Difficulty' size_hint: 0.5, 0.2 font_size: 24 background_color: (0.811,0.745,0.549,1) pos_hint: {"center_x": 0.5} Button: size_hint: 0.5, 0.1 font_size: 24 background_color: (0.811,0.745,0.549,0) Button: id: Back text: 'Back' size_hint: 0.5, 0.2 font_size: 24 background_color: (0.811,0.745,0.549,1) pos_hint: {"center_x":0.5} on_press: root.manager.current= 'main_menu' <GameScreen>: FloatLayout: canvas.before: Color: rgba:0.098,0.301,0.2,1 Rectangle: pos: self.pos size: self.size Button: id: Slap text: 'SLAP!' size_hint: 0.3, 0.3 pos_hint: {'x':0.7,'y':0.05} background_color: 1, 0, 0, 0 canvas.before: Color: rgba: 1, 0, 0, 1 Ellipse: pos: self.pos size: self.size Image: id: Back_of_card source: 'Back_Of_Card.png' allow_stretch: True pos_hint: {'x':0.425,'y': 0.05} size_hint: 0.15, 0.325 Label: text: 'number of cards: {}'.format(root.player_cards) font_size: 30 size_hint: 0.2, 0.1 pos_hint: {'x':0.1,'y':0.05} halign: 'left' Label: canvas.before: Color: rgba:1,1,1,1 Rectangle: pos: self.pos size: self.size text: "It is {}'s turn".format(root.current_player) font_size: 20 size_hint: 0.3, 0.05 pos_hint: {'x':0.35,'y':0.9} color: 0,0,0,1
问题分析及修复
1. Card类参数顺序错误
Deck类创建Card实例时传入的参数是Card(suit, face, image),但Card类的__init__方法定义为(face,suit,image),参数顺序不匹配,导致属性赋值混乱,初始化时触发错误。
修复:
调整Card类的__init__参数顺序,与调用处一致:
class Card: def __init__(self, suit, face, image): self.face = face self.suit = suit self.image = image
2. shuffle_deck方法逻辑错误
原方法中嵌套了for循环,会在每次while循环里重复执行多次pop操作,导致deck.cards被提前掏空引发索引错误。
修复:
移除多余的for循环,仅保留while循环内的随机pop:
def shuffle_deck(self, deck): shuffled_deck = [] while len(deck.cards) != 0: r = random.randint(0, len(deck.cards)-1) temp = deck.cards.pop(r) shuffled_deck.append(temp) return shuffled_deck
3. Players类引用未定义的方法
Players类中self.deck=GameController.deal,但GameController根本没有deal方法,会触发AttributeError。
修复:
暂时注释或移除该错误代码,后续实现deal方法后再添加:
class Players: def __init__(self, Username, number): # self.deck=GameController.deal # 先注释,待实现deal方法后修复 self.name = Username self.is_in = True self.number = number
4. start_game方法阻塞主线程及重复创建Screen
time.sleep(3)会阻塞Kivy的主线程,导致UI无响应甚至崩溃,应使用Kivy的Clock.schedule_once替代。- ScreenManager中已经添加过GameScreen,无需重新创建,直接获取已有实例即可。
修复:
from kivy.clock import Clock # 导入Clock class SingleMenu(Screen): def start_game(self): # 直接切换到已有游戏屏幕 self.manager.current = 'game_screen' # 用Clock延迟初始化GameController,避免阻塞主线程 Clock.schedule_once(lambda dt: self.init_game_controller(), 0) def init_game_controller(self): num_players = 3 game_controller = GameController(num_players) # 这里可以添加将game_controller关联到GameScreen的逻辑 game_screen = self.manager.get_screen('game_screen') # 例如:game_screen.game_controller = game_controller
5. 可选优化:提前初始化GameController
如果初始化逻辑不复杂,也可以先完成GameController初始化再切换屏幕,避免延迟:
class SingleMenu(Screen): def start_game(self): num_players = 3 game_controller = GameController(num_players) game_screen = self.manager.get_screen('game_screen') game_screen.game_controller = game_controller # 关联到游戏屏幕 self.manager.current = 'game_screen'
验证修复
完成以上修改后,重新运行程序,点击“开始游戏”按钮即可正常初始化GameController并切换到GameScreen,窗口不会崩溃。
内容的提问来源于stack exchange,提问作者programmer14234324
相关产品推荐
相关产品推荐

