当前位置: 首页 > 后端技术 > Python

如何用Python实现超级马里奥的界面和状态机?

时间:2023-03-26 15:07:12 Python

01状态机介绍游戏中的状态机一般是有限状态机,简称FSM(finitestatemachine),简称状态机,表示有限数量的状态和行为,如转移和动作在这些状态之间。数学模型。状态机的每个状态至少需要以下三个操作:Startup:从其他状态进入该状态时,需要进行的初始化操作;update:该状态运行时执行的更新操作;清理:退出该状态时,需要进行的清理操作。状态需要的变量:Next:表示状态退出后要转移到的下一个状态;Persist:状态切换时需要传递的数据;Complete:表示状态是否结束,状态机根据这个值来判断过渡状态。游戏界面状态机的状态转换图如下,箭头表示可能的状态转换方向:(注意有一个转换不好画:超时状态可以转换为GameOver状态。)这些状态的含义比较简单,如下贴游戏界面截图。主菜单:主菜单,启动程序进入该状态,可以使用上下键选择玩家1或玩家2,按回车键开始游戏。载入画面:游戏开始前的载入画面。Gamerunning:游戏运行状态,代码实现中为Level类。Gameover:当角色死亡且生命数为0时,进入该状态。超时:在游戏中,超时会达到这个状态。这类似于GameOver,因此不会截取屏幕截图。02状态机代码实现因为本文的目的是游戏界面的状态机实现,所以特地写了一个state_demo.py文件,方便大家更方便的查看代码。游戏启动代码从pygame的初始化开始,设置屏幕大小为c.SCREEN_SIZE(800,600)。所有常量都保存在单独的constants.py中。将osimportpygame作为pgimport常量导入为cpg.init()c.SCREEN_SIZE)SCREEN_RECT=SCREEN.get_rect()load_all_gfx函数在指定目录下搜索所有匹配后缀的图片,用pg.image.load函数加载,保存到图形集中。GFX将所有找到的图片保存在resources/graphics目录下,后面获取各种图形时会用到。defload_all_gfx(directory,colorkey=(255,0,255),accept=('.png','.jpg','.bmp','.gif')):graphics={}forpicinos.listdir(directory)):name,ext=os.path.splitext(pic)ifext.lower()inaccept:img=pg.image.load(os.path.join(directory,pic))ifimg.get_alpha():img=img.convert_alpha()else:img=img.convert()img.set_colorkey(colorkey)graphics[name]=imgreturngraphicsGFX=load_all_gfx(os.path.join("resources","graphics"))这里是demo入口函数首先创建一个保存所有状态的state_dict设置,调用setup_states函数设置启动状态为MAIN_MENU。if__name__=='__main__':game=Control()state_dict={c.MAIN_MENU:Menu(),c.LOAD_SCREEN:LoadScreen(),c.LEVEL:Level(),c.GAME_OVER:GameOver(),c.TIME_OUT:TimeOut()}game.setup_states(state_dict,c.MAIN_MENU)game.main()状态类首先定义了一个状态基类,根据上面提到的状态所需的三个操作(启动,更新,清理)。以上三个变量(next、persist、```jsdone)定义在init函数中,start_time和current_time用于记录时间。类State():definit(self):self.start_time=0.0self.current_time=0.0self.done=Falseself.next=Noneself.persist={}@abstractmethoddefstartup(self,current_time,persist):'''抽象方法'''defcleanup(self):self.done=Falsereturnself.persist@abstractmethoddefupdate(sefl,surface,keys,current_time):'''抽象方法'''看一个状态的具体情况classLoadScreen实现了,该状态的显示效果如图3所示,startup函数保存了预期的persist,设置next为Level状态类,start_time保存了进入该状态的开始时间。初始化一个Infoclass,专门用来显示界面信息。update函数根据该状态下的运行时间(current_time-self.start_time)来决定显示什么以及是否结束该状态(self.done=True)。类LoadScreen(State):def__init__(self):State.__init__(self)self.time_list=[2400,2600,2635]defstartup(self,current_time,persist):self.start_time=current_timeself.persist=persistself.game_info=self.persistself.next=self.set_next_state()info_state=self.set_info_state()self.overhead_info=Info(self.game_info,info_state)defset_next_state(self):returnc.LEVELdefset_info_state(self):返回c.LOAD_SCREENdefupdate(self,surface,keys,current_time):if(current_time-self.start_time)Rectdrawoneimageontoanotherdefget_image(sheet,x,y,width,height,colorkey,scale):image=pg.Surface([width,height])rect=image.get_rect()image.blit(sheet,(0,0),(x,y,width,height))图像.set_colorkey(colorkey)image=pg.transform.scale(image,(int(rect.width*scale),int(rect.height*scale)))returnimage看看create_info_labels函数中的其中一个字符串'MARIO'在界面上是如何显示的.create_label函数参数(x,y)表示字符串在界面上的起始位置,根据self.image_dict中的字符获取对应的surface对象。set_label_rects函数将设置字符串中每个表面对象rect的(x,y)值。pygame.Rect对象中常用的成员变量(x,y)表示Surface左上角的位置。top,bottom:表示Surface在y轴上的最高值和最低值,所以top和y值相同。left,right:表示Surface在x轴上最左边和最右边的值,所以left和x的值是从下面的坐标图可以看出,左上角是原点(0,0)的整个屏幕,并在图中标识了附件矩形的四个顶点的坐标。defcreate_info_labels(self):...自我。mario_label=[]...自我。create_label(self.mario_label,'MARIO',75,30)defcreate_label(self,label_list,string,x,y):对于字符串中的字母:label_list.append(Character(self.image_dict[letter]))self.set_label_rects(label_list,x,y)defset_label_rects(self,label_list,x,y):对于i,枚举中的字母(label_list):letter.rect.x=x+((letter.rect.width+3)*i)letter.rect.y=yifletter.image==self.image_dict['-']:letter.rect.y+=7letter.rect.x+=2控制类Control是一个状态机类,主要功能是游戏的主循环,setup_states函数设置游戏启动时的运行状态。类Control():def__init__(self):self.screen=pg.display.get_surface()self.done=Falseself.clock=pg.time.Clock()self.fps=60self.current_time=0.0self.keys=pg.key.get_pressed()self.state_dict={}self.state_name=Noneself.state=Nonedefsetup_states(self,state_dict,start_state):self.state_dict=state_dictself.state_name=start_stateself.state=self.state_dict[self.state_name]defmain(self):whilenotself.done:self.event_loop()self.update()pg.display.update()self.clock.tick(self.fps)event_loop函数负责为了监控输入(键盘输入和退出按钮),slef.keys保存键盘输入。如果检测到当前状态结束,则调用flip_state函数来清理旧状态并转换到下一个状态。更新函数会检测状态的完成值并调用状态的更新函数。defupdate(self):self.current_time=pg.time.get_ticks()如果self.state.done:self.flip_state()self.state.update(self.screen,self.keys,self.current_time)defflip_state(self):previous,self.state_name=self.state_name,self.state.nextpersist=self.state.cleanup()self.state=self.state_dict[self.state_name]self.state.startup(self.current_time,坚持)defevent_loop(self):foreventinpg.event.get():ifevent.type==pg.QUIT:self.done=Trueelifevent.type==pg.KEYDOWN:self.keys=pg.key.get_pressed()elifevent.type==pg.KEYUP:self.keys=pg.key.get_pressed()03完整代码有两个文件constants.py和state_demo.py,constants.py保存了所有的字符串定义和常量。constants.pyGAME_TIME_OUT表示游戏超时时间。这里demo演示设置为5秒,实际为300秒。阅读原文获取完整代码:https://developer.aliyun.com/ask/267501?utm_content=g_1000095974