您当前所在位置: > 爆料站 > 好好玩

如何用Python实现超级玛丽的界面和状态机?_pygame

时间:2019-12-22 12:46:05  来源:  作者:网络

原标题:如何用 Python 实现超级玛丽的界面和状态机?

作者 | marble_xu

责编 | 郭芮

出品 | CSDN博客

小时候的经典游戏,代码参考了github上的项目Mario-Level-1(https://github.com/justinmeister/Mario-Level-1),使用pygame来实现,从中学习到了横版过关游戏实现中的一些处理方法。原项目实现了超级玛丽的第一个小关。

在原项目的基础上,游戏使用json文件来保存每一个关卡的数据,将数据和代码解耦合,目前已开发4个小关,后续关卡的扩展也很方便,只需要添加json文件和地图图片,支持新的怪物就行。游戏还支持进入水管,到新的子地图。

这篇文章是要介绍下游戏中的几个界面显示和界面之前如何转换,所以特意写了一个demo程序,完整的游戏代码在下面的github链接(https://github.com/marblexu/PythonSuperMario)中下载。

状态机介绍

游戏中的状态机一般都是有限状态机,简写为FSM(Finite State Machine),简称状态机,是表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型。

状态机的每一个状态至少需要有下面三个操作:

  • Startup:当从其他状态进入这个状态时,需要进行的初始化操作;
  • Update :在这个状态运行时进行的更新操作;
  • Cleanup:当从这个状态退出时,需要进行的清除操作。

状态需要的变量:

  • next: 表示这个状态退出后要转到的下一个状态;
  • persist:在状态间转换时需要传递的数据;
  • done:表示这个状态是否结束,状态机会根据这个值来决定转换状态。

游戏界面状态机的状态转换图如下,箭头表示可能的状态转换方向:(注意有个转换不太好画出来:Time Out状态可以转换到Game Over状态。)

图1

这几个状态的意思比较简单,下面把游戏界面的截图发一下。

  • Main Menu: 主菜单,启动程序就进入这个状态,可以用UP和DOWN键选择player 1或player 2,按回车键开启游戏。

图2

  • Load Screen: 游戏开始前的加载界面。

图3

  • Game Run: 游戏运行时的状态,在代码实现中是Level类。

图4

  • Game Over: 人物死亡且生命数目为0时到这个状态。

图5

  • Time Out: 在游戏中时间超时会到这个状态,这个和Game Over类似,就不截图了。

状态机代码实现

因为这篇文章的目的是游戏界面的状态机实现,所以专门写了一个state_demo.py文件,让大家可以更加方便的看代码。

游戏启动代码

开始是 pygame的初始化,设置屏幕大小为c.SCREEN_SIZE(800, 600)。所有的常量都保存在单独的constants.py中。

importos

importpygame aspg

importconstants asc

pg.init

pg.event.set_allowed([pg.KEYDOWN, pg.KEYUP, pg.QUIT])

pg.display.set_caption(c.ORIGINAL_CAPTION)

SCREEN = pg.display.set_mode(c.SCREEN_SIZE)

SCREEN_RECT = SCREEN.get_rect

load_all_gfx函数查找指定目录下所有符合后缀名的图片,使用pg.image.load函数加载,保存在graphics set中。

GFX 保存在resources/graphics目录找到的所有图片,后面获取各种图形时会用到。

def load_all_gfx(directory, colorkey=( 255, 0, 255), accept=( '.png', '.jpg', '.bmp', '.gif')):

graphics = {}

forpic inos.listdir(directory):

name, ext = os. path.splitext(pic)

ifext. lowerinaccept:

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] = img

returngraphics

GFX = load_all_gfx( os. path.join( "resources", "graphics"))

下面是demo的入口函数,先创建了一个保存所有状态的state_dict set,调用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

状态类

先定义一个State 基类, 按照上面说的状态需要的三个操作分别定义函数(startup, update, cleanup)。在 init 函数中定义了上面说的三个变量(next,persist,done),还有start_time 和 current_time 用于记录时间。

classState:

def__init__(self):

self.start_time = 0.0

self.current_time = 0.0

self.done = False

self.next = None

self.persist = {}

@abstractmethod

defstartup(self, current_time, persist):

'''abstract method'''

defcleanup(self):

self.done = False

returnself.persist

@abstractmethod

defupdate(sefl, surface, keys, current_time):

'''abstract method'''

看一个状态类LoadScreen的具体实现,这个状态的显示效果如图3。

startup 函数保存了传入的persist,设置 next 为Level 状态类,start_time保存进入这个状态的开始时间。初始化一个Info类,这个就是专门用来显示界面信息的。

update 函数根据在这个状态已运行的时间(current_time - self.start_time),决定显示内容和是否结束状态(self.done = True)。

classLoadScreen( State):

def__init__( self) :

State.__init_ _( self)

self.time_list = [ 2400, 2600, 2635]

defstartup( self, current_time, persist) :

self.start_time = current_time

self.persist = persist

self.game_info = self.persist

self. 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.LEVEL

defset_info_state( self) :

returnc.LOAD_SCREEN

defupdate( self, surface, keys, current_time) :

if(current_time - self.start_time) < self.time_list[ 0]:

surface.fill(c.BLACK)

self.overhead_info.update( self.game_info)

self.overhead_info.draw(surface)

elif (current_time - self.start_time) < self.time_list[ 1]:

surface.fill(c.BLACK)

elif (current_time - self.start_time) < self.time_list[ 2]:

surface.fill(( 106, 150, 252))

else:

self.done = True

Info类

下面介绍Info类,界面的显示大部分都是由它来完成,init函数中create_info_labels函数创建通用的信息,create_state_labels函数对于不同的状态,会初始化不同的信息。

classInfo:

def__init__( self, game_info, state) :

self.coin_total = game_info[c.COIN_TOTAL]

self.total_lives = game_info[c.LIVES]

self.state = state

self.game_info = game_info

self.create_font_image_dict

self.create_info_labels

self.create_state_labels

self.flashing_coin = FlashCoin( 280, 53)

create_font_image_dict函数从之前加载的图片GFX[‘text_images’]中,截取字母和数字对应的图形,保存在一个set中,在后面创建文字时会用到。

defcreate_font_image_dict( self) :

self.image_dict = {}

image_list = []

image_rect_list = [ # 0 - 9

( 3, 230, 7, 7), ( 12, 230, 7, 7), ( 19, 230, 7, 7),

( 27, 230, 7, 7), ( 35, 230, 7, 7), ( 43, 230, 7, 7),

( 51, 230, 7, 7), ( 59, 230, 7, 7), ( 67, 230, 7, 7),

( 75, 230, 7, 7),

# A - Z

( 83, 230, 7, 7), ( 91, 230, 7, 7), ( 99, 230, 7, 7),

( 107, 230, 7, 7), ( 115, 230, 7, 7), ( 123, 230, 7, 7),

( 3, 238, 7, 7), ( 11, 238, 7, 7), ( 20, 238, 7, 7),

( 27, 238, 7, 7), ( 35, 238, 7, 7), ( 44, 238, 7, 7),

( 51, 238, 7, 7), ( 59, 238, 7, 7), ( 67, 238, 7, 7),

( 75, 238, 7, 7), ( 83, 238, 7, 7), ( 91, 238, 7, 7),

( 99, 238, 7, 7), ( 108, 238, 7, 7), ( 115, 238, 7, 7),

( 123, 238, 7, 7), ( 3, 246, 7, 7), ( 11, 246, 7, 7),

( 20, 246, 7, 7), ( 27, 246, 7, 7), ( 48, 246, 7, 7),

# -*

( 68, 249, 6, 2), ( 75, 247, 6, 6)]

character_string = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ -*'

forcharacter, image_rect inzip(character_string, image_rect_list):

self.image_dict[character] = get_image(GFX[ 'text_images'],

*image_rect, ( 92, 148, 252), 2.9)

get_image函数从一个大的Surface sheet 中按照 area(x, y, width, height)截取出部分图片 放入Surface image对应的起始位置(0,0),并按照scale参数调整大小。

pygame的 blit 函数介绍如下:

pg.Surface.blit( source, dest, area=None, special_flags=0) -> Rect

draw one image onto another

def get_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))

image.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函数会设置字符串中每一个Surface 对象 rect 的(x, y)值。

pygame.Rect 对象中常用的成员变量(x,y),表示这个Surface的左上角的位置。

top, bottom: 表示Surface 在y轴上最上边和最下边的值, 所以top和y 值是一样的

left, right: 表示Surface 在x轴上最左边和最右边的值,所以 left和x 值是一样的

下面的坐标图可以看到,在左上角是整个屏幕的原点(0,0), 图中标识了长方形rect的四个顶点的坐标。

defcreate_info_labels( self) :

...

self.mario_label = []

...

self.create_label( self.mario_label, 'MARIO', 75, 30)

defcreate_label( self, label_list, string, x, y) :

forletter instring:

label_list.append(Character( self.image_dict[letter]))

self.set_label_rects(label_list, x, y)

defset_label_rects( self, label_list, x, y) :

fori, letter inenumerate(label_list):

letter.rect.x = x + ((letter.rect.width + 3) * i)

letter.rect.y = y

ifletter.image == self.image_dict[ '-']:

letter.rect.y += 7

letter.rect.x += 2

Control类

Control 是状态机类,main函数是游戏的主循环,setup_states函数设置游戏启动时运行的状态。

classControl:

def__init__( self) :

self.screen = pg.display.get_surface

self.done = False

self.clock = pg.time.Clock

self.fps = 60

self.current_time = 0. 0

self.keys = pg.key.get_pressed

self.state_dict = {}

self.state_name = None

self.state = None

defsetup_states( self, state_dict, start_state) :

self.state_dict = state_dict

self.state_name = start_state

self.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 保存键盘输入。

update函数会检测状态的done值,调用状态的更新函数。如果检测到当前状态结束,就调用flip_state函数进行旧状态的清理操作,并转换到下一个状态。

defupdate( self) :

self.current_time = pg.time.get_ticks

ifself.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. next

persist = self.state.cleanup

self.state = self.state_dict[ self.state_name]

self.state.startup( self.current_time, persist)

defevent_loop( self) :

forevent inpg.event.get:

ifevent.type == pg. QUIT:

self.done = True

elif event.type == pg. KEYDOWN:

self.keys = pg.key.get_pressed

elif event.type == pg. KEYUP:

self.keys = pg.key.get_pressed

完整代码

有两个文件constants.py 和 state_demo.py,constants.py 保存了所有的字符串定义和常量。

constants.py

GAME_TIME_OUT 表示游戏的超时时间,这边为了demo演示,设成了5秒,实际是300秒。

SCREEN_HEIGHT= 600

SCREEN_WIDTH= 800

SCREEN_SIZE= (SCREEN_WIDTH,SCREEN_HEIGHT)

ORIGINAL_CAPTION= "Super Mario Bros"

GAME_TIME_OUT= 5

## COLORS ##

# R G B

BLACK= ( 0, 0, 0)

SIZE_MULTIPLIER= 2.5

BRICK_SIZE_MULTIPLIER= 2.69

BACKGROUND_MULTIPLER= 2.679

GROUND_HEIGHT= SCREEN_HEIGHT - 62

#STATES FOR ENTIRE GAME

MAIN_MENU= 'main menu'

LOAD_SCREEN= 'load screen'

TIME_OUT= 'time out'

GAME_OVER= 'game over'

LEVEL= 'level'

#MAIN MENU CURSOR STATES

PLAYER1= '1 PLAYER GAME'

PLAYER2= '2 PLAYER GAME'

#GAME INFO DICTIONARY KEYS

COIN_TOTAL= 'coin total'

SCORE= 'score'

TOP_SCORE= 'top score'

LIVES= 'lives'

CURRENT_TIME= 'current time'

LEVEL_NUM= 'level num'

PLAYER_NAME= 'player name'

PLAYER_MARIO= 'mario'

PLAYER_LUIGI= 'luigi'

ITEM_SHEET= 'item_objects'

state_demo.py

上面讲的状态类,状态机类都放在这里。

import os

import pygame as pg

from abc import ABC, abstractmethod

import constants as c

classState:

def__init__( self) :

self.start_time = 0. 0

self.current_time = 0. 0

self.done = False

self. next= None

self.persist = {}

@abstractmethod

defstartup( self, current_time, persist) :

'''abstract method'''

defcleanup( self) :

self.done = False

returnself.persist

@abstractmethod

defupdate(sefl, surface, keys, current_time):

'''abstract method'''

classMenu( State):

def__init__( self) :

State.__init_ _( self)

persist = {c. COIN_TOTAL:0,

c. SCORE:0,

c. LIVES:3,

c. TOP_SCORE:0,

c. CURRENT_TIME:0. 0,

c. LEVEL_NUM:1,

c. PLAYER_NAME:c.PLAYER_MARIO}

self.startup( 0. 0, persist)

defstartup( self, current_time, persist) :

self. next= c.LOAD_SCREEN

self.persist = persist

self.game_info = persist

self.overhead_info = Info( self.game_info, c.MAIN_MENU)

self.setup_background

self.setup_player

self.setup_cursor

defsetup_background( self) :

self.background = GFX[ 'level_1']

self.background_rect = self.background.get_rect

self.background = pg.transform.scale( self.background,

(int( self.background_rect.width*c.BACKGROUND_MULTIPLER),

int( self.background_rect.height*c.BACKGROUND_MULTIPLER)))

self.viewport = SCREEN.get_rect(bottom=SCREEN_RECT.bottom)

self.image_dict = {}

image = get_image(GFX[ 'title_screen'], 1, 60, 176, 88,

( 255, 0, 220), c.SIZE_MULTIPLIER)

rect = image.get_rect

rect.x, rect.y = ( 170, 100)

self.image_dict[ 'GAME_NAME_BOX'] = (image, rect)

defsetup_player( self) :

self.player_list = []

player_rect_info = [( 178, 32, 12, 16), ( 178, 128, 12, 16)]

forrect inplayer_rect_info:

image = get_image(GFX[ 'mario_bros'],

*rect, c.BLACK, 2.9)

rect = image.get_rect

rect.x, rect.bottom = 110, c.GROUND_HEIGHT

self.player_list.append((image, rect))

self.player_index = 0

defsetup_cursor( self) :

self.cursor = pg.sprite.Sprite

self.cursor.image = get_image(GFX[c.ITEM_SHEET], 24, 160, 8, 8, c.BLACK, 3)

rect = self.cursor.image.get_rect

rect.x, rect.y = ( 220, 358)

self.cursor.rect = rect

self.cursor.state = c.PLAYER1

defupdate( self, surface, keys, current_time) :

self.current_time = current_time

self.game_info[c.CURRENT_TIME] = self.current_time

self.player_image = self.player_list[ self.player_index][ 0]

self.player_rect = self.player_list[ self.player_index][ 1]

self.update_cursor(keys)

self.overhead_info.update( self.game_info)

surface.blit( self.background, self.viewport, self.viewport)

surface.blit( self.image_dict[ 'GAME_NAME_BOX'][ 0],

self.image_dict[ 'GAME_NAME_BOX'][ 1])

surface.blit( self.player_image, self.player_rect)

surface.blit( self.cursor.image, self.cursor.rect)

self.overhead_info.draw(surface)

defupdate_cursor( self, keys) :

ifself.cursor.state == c. PLAYER1:

self.cursor.rect.y = 358

ifkeys[pg.K_DOWN]:

self.cursor.state = c.PLAYER2

self.player_index = 1

self.game_info[c.PLAYER_NAME] = c.PLAYER_LUIGI

elif self.cursor.state == c. PLAYER2:

self.cursor.rect.y = 403

ifkeys[pg.K_UP]:

self.cursor.state = c.PLAYER1

self.player_index = 0

self.game_info[c.PLAYER_NAME] = c.PLAYER_MARIO

ifkeys[pg.K_RETURN]:

self.done = True

classLoadScreen( State):

def__init__( self) :

State.__init_ _( self)

self.time_list = [ 2400, 2600, 2635]

defstartup( self, current_time, persist) :

self.start_time = current_time

self.persist = persist

self.game_info = self.persist

self. 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.LEVEL

defset_info_state( self) :

returnc.LOAD_SCREEN

defupdate( self, surface, keys, current_time) :

if(current_time - self.start_time) < self.time_list[ 0]:

surface.fill(c.BLACK)

self.overhead_info.update( self.game_info)

self.overhead_info.draw(surface)

elif (current_time - self.start_time) < self.time_list[ 1]:

surface.fill(c.BLACK)

elif (current_time - self.start_time) < self.time_list[ 2]:

surface.fill(( 106, 150, 252))

else:

self.done = True

classGameOver( LoadScreen):

def__init__( self) :

LoadScreen.__init_ _( self)

self.time_list = [ 3000, 3200, 3235]

defset_next_state( self) :

returnc.MAIN_MENU

defset_info_state( self) :

returnc.GAME_OVER

classTimeOut( LoadScreen):

def__init__( self) :

LoadScreen.__init_ _( self)

self.time_list = [ 2400, 2600, 2635]

defset_next_state( self) :

ifself.persist[c.LIVES] == 0:

returnc.GAME_OVER

else:

returnc.LOAD_SCREEN

defset_info_state( self) :

returnc.TIME_OUT

classLevel( State):

def__init__( self) :

State.__init_ _( self)

defstartup( self, current_time, persist) :

self.game_info = persist

self.persist = self.game_info

self.player = None

self.overhead_info = Info( self.game_info, c.LEVEL)

self.setup_background

defsetup_background( self) :

self.background = GFX[ 'level_1']

self.bg_rect = self.background.get_rect

self.background = pg.transform.scale( self.background,

(int( self.bg_rect.width*c.BACKGROUND_MULTIPLER),

int( self.bg_rect.height*c.BACKGROUND_MULTIPLER)))

self.bg_rect = self.background.get_rect

self.level = pg.Surface(( self.bg_rect.w, self.bg_rect.h)).convert

self.viewport = SCREEN.get_rect(bottom= self.bg_rect.bottom)

defupdate( self, surface, keys, current_time) :

self.game_info[c.CURRENT_TIME] = self.current_time = current_time

self.overhead_info.update( self.game_info, self.player)

ifself.overhead_info.time <= 0:

self.update_game_info

self.done = True

self.draw(surface)

defupdate_game_info( self) :

self.persist[c.LIVES] -= 1

ifself.persist[c.LIVES] == 0:

self. next= c.GAME_OVER

elif self.overhead_info.time == 0:

self. next= c.TIME_OUT

else:

self. next= c.LOAD_SCREEN

defdraw( self, surface) :

self.level.blit( self.background, self.viewport, self.viewport)

surface.blit( self.level, ( 0, 0), self.viewport)

self.overhead_info.draw(surface)

classCharacter( pg. sprite. Sprite):

def__init__( self, image) :

pg.sprite.Sprite.__init_ _( self)

self.image = image

self.rect = self.image.get_rect

classInfo:

def__init__( self, game_info, state) :

self.coin_total = game_info[c.COIN_TOTAL]

self.total_lives = game_info[c.LIVES]

self.state = state

self.game_info = game_info

self.create_font_image_dict

self.create_info_labels

self.create_state_labels

self.flashing_coin = FlashCoin( 280, 53)

defcreate_font_image_dict( self) :

self.image_dict = {}

image_list = []

image_rect_list = [ # 0 - 9

( 3, 230, 7, 7), ( 12, 230, 7, 7), ( 19, 230, 7, 7),

( 27, 230, 7, 7), ( 35, 230, 7, 7), ( 43, 230, 7, 7),

( 51, 230, 7, 7), ( 59, 230, 7, 7), ( 67, 230, 7, 7),

( 75, 230, 7, 7),

# A - Z

( 83, 230, 7, 7), ( 91, 230, 7, 7), ( 99, 230, 7, 7),

( 107, 230, 7, 7), ( 115, 230, 7, 7), ( 123, 230, 7, 7),

( 3, 238, 7, 7), ( 11, 238, 7, 7), ( 20, 238, 7, 7),

( 27, 238, 7, 7), ( 35, 238, 7, 7), ( 44, 238, 7, 7),

( 51, 238, 7, 7), ( 59, 238, 7, 7), ( 67, 238, 7, 7),

( 75, 238, 7, 7), ( 83, 238, 7, 7), ( 91, 238, 7, 7),

( 99, 238, 7, 7), ( 108, 238, 7, 7), ( 115, 238, 7, 7),

( 123, 238, 7, 7), ( 3, 246, 7, 7), ( 11, 246, 7, 7),

( 20, 246, 7, 7), ( 27, 246, 7, 7), ( 48, 246, 7, 7),

# -*

( 68, 249, 6, 2), ( 75, 247, 6, 6)]

character_string = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ -*'

forcharacter, image_rect inzip(character_string, image_rect_list):

self.image_dict[character] = get_image(GFX[ 'text_images'],

*image_rect, ( 92, 148, 252), 2.9)

defcreate_info_labels( self) :

self.score_text = []

self.coin_count_text = []

self.mario_label = []

self.world_label = []

self.time_label = []

self.stage_label = []

self.create_label( self.score_text, '000000', 75, 55)

self.create_label( self.coin_count_text, '*00', 300, 55)

self.create_label( self.mario_label, 'MARIO', 75, 30)

self.create_label( self.world_label, 'WORLD', 450, 30)

self.create_label( self.time_label, 'TIME', 625, 30)

self.create_label( self.stage_label, '1-1', 472, 55)

self.info_labels = [ self.score_text, self.coin_count_text, self.mario_label,

self.world_label, self.time_label, self.stage_label]

defcreate_state_labels( self) :

ifself.state == c. MAIN_MENU:

self.create_main_menu_labels

elif self.state == c. LOAD_SCREEN:

self.create_player_image

self.create_load_screen_labels

elif self.state == c. LEVEL:

self.create_level_labels

elif self.state == c. GAME_OVER:

self.create_game_over_labels

elif self.state == c. TIME_OUT:

self.create_time_out_labels

defcreate_player_image( self) :

self.life_times_image = get_image(GFX[ 'text_images'],

75, 247, 6, 6, ( 92, 148, 252), 2.9)

self.life_times_rect = self.life_times_image.get_rect(center=( 378, 295))

self.life_total_label = []

self.create_label( self.life_total_label, str( self.total_lives), 450, 285)

ifself.game_info[c.PLAYER_NAME] == c. PLAYER_MARIO:

rect = ( 178, 32, 12, 16)

else:

rect = ( 178, 128, 12, 16)

self.player_image = get_image(GFX[ 'mario_bros'],

*rect, ( 92, 148, 252), 2.9)

self.player_rect = self.player_image.get_rect(center=( 320, 290))

defcreate_main_menu_labels( self) :

mario_game = []

luigi_game = []

top = []

top_score = []

self.create_label(mario_game, c.PLAYER1, 272, 360)

self.create_label(luigi_game, c.PLAYER2, 272, 405)

self.create_label(top, 'TOP - ', 290, 465)

self.create_label(top_score, '000000', 400, 465)

self.state_labels = [mario_game, luigi_game, top, top_score,

* self.info_labels]

defcreate_load_screen_labels( self) :

world_label = []

self.stage_label2 = []

self.create_label(world_label, 'WORLD', 280, 200)

self.create_label( self.stage_label2, '1-1', 430, 200)

self.state_labels = [world_label, self.stage_label2,

* self.info_labels, self.life_total_label]

defcreate_level_labels( self) :

self.time = c.GAME_TIME_OUT

self.current_time = 0

self.clock_time_label = []

self.create_label( self.clock_time_label, str( self.time), 645, 55)

self.state_labels = [* self.info_labels, self.clock_time_label]

defcreate_game_over_labels( self) :

game_label = []

over_label = []

self.create_label(game_label, 'GAME', 280, 300)

self.create_label(over_label, 'OVER', 400, 300)

self.state_labels = [game_label, over_label, * self.info_labels]

defcreate_time_out_labels( self) :

timeout_label = []

self.create_label(timeout_label, 'TIME OUT', 290, 310)

self.state_labels = [timeout_label, * self.info_labels]

defcreate_label( self, label_list, string, x, y) :

forletter instring:

label_list.append(Character( self.image_dict[letter]))

self.set_label_rects(label_list, x, y)

defset_label_rects( self, label_list, x, y) :

fori, letter inenumerate(label_list):

letter.rect.x = x + ((letter.rect.width + 3) * i)

letter.rect.y = y

ifletter.image == self.image_dict[ '-']:

letter.rect.y += 7

letter.rect.x += 2

defupdate( self, level_info, level=None) :

self.level = level

self.handle_level_state(level_info)

defhandle_level_state( self, level_info) :

self.score = level_info[c.SCORE]

self.update_text( self.score_text, self.score)

self.update_text( self.coin_count_text, level_info[c.COIN_TOTAL])

self.update_text( self.stage_label, level_info[c.LEVEL_NUM])

self.flashing_coin.update(level_info[c.CURRENT_TIME])

ifself.state == c. LOAD_SCREEN:

self.update_text( self.stage_label2, level_info[c.LEVEL_NUM])

ifself.state == c. LEVEL:

if(level_info[c.CURRENT_TIME] - self.current_time) > 1000:

self.current_time = level_info[c.CURRENT_TIME]

self.time -= 1

self.update_text( self.clock_time_label, self.time, True)

defupdate_text( self, text, score, reset=False) :

ifreset andlen(text) > len(str(score)):

text.remove(text[ 0])

index = len(text) - 1

fordigit inreversed(str(score)):

rect = text[index].rect

text[index] = Character( self.image_dict[digit])

text[index].rect = rect

index -= 1

defdraw( self, surface) :

self.draw_info(surface, self.state_labels)

ifself.state == c. LOAD_SCREEN:

surface.blit( self.player_image, self.player_rect)

surface.blit( self.life_times_image, self.life_times_rect)

surface.blit( self.flashing_coin.image, self.flashing_coin.rect)

defdraw_info( self, surface, label_list) :

forlabel inlabel_list:

forletter inlabel:

surface.blit(letter.image, letter.rect)

classFlashCoin( pg. sprite. Sprite):

def__init__( self, x, y) :

pg.sprite.Sprite.__init_ _( self)

self.frame_index = 0

self.frames = []

self.load_frames

self.image = self.frames[ self.frame_index]

self.rect = self.image.get_rect

self.rect.x = x

self.rect.y = y

self.animation_timer = 0

defload_frames( self) :

sheet = GFX[c.ITEM_SHEET]

frame_rect_list = [( 1, 160, 5, 8), ( 9, 160, 5, 8),

( 17, 160, 5, 8), ( 9, 160, 5, 8)]

forframe_rect inframe_rect_list:

self.frames.append(get_image(sheet, *frame_rect,

c.BLACK, c.BRICK_SIZE_MULTIPLIER))

defupdate( self, current_time) :

time_list = [ 375, 125, 125, 125]

ifself.animation_timer == 0:

self.animation_timer = current_time

elif (current_time - self.animation_timer) > time_list[ self.frame_index]:

self.frame_index += 1

ifself.frame_index == 4:

self.frame_index = 0

self.animation_timer = current_time

self.image = self.frames[ self.frame_index]

classControl:

def__init__( self) :

self.screen = pg.display.get_surface

self.done = False

self.clock = pg.time.Clock

self.fps = 60

self.current_time = 0. 0

self.keys = pg.key.get_pressed

self.state_dict = {}

self.state_name = None

self.state = None

defsetup_states( self, state_dict, start_state) :

self.state_dict = state_dict

self.state_name = start_state

self.state = self.state_dict[ self.state_name]

defupdate( self) :

self.current_time = pg.time.get_ticks

ifself.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. next

persist = self.state.cleanup

self.state = self.state_dict[ self.state_name]

self.state.startup( self.current_time, persist)

defevent_loop( self) :

forevent inpg.event.get:

ifevent.type == pg. QUIT:

self.done = True

elif event.type == pg. KEYDOWN:

self.keys = pg.key.get_pressed

elif event.type == pg. KEYUP:

self.keys = pg.key.get_pressed

defmain( self) :

whilenotself. done:

self.event_loop

self.update

pg.display.update

self.clock.tick( self.fps)

defget_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))

image.set_colorkey(colorkey)

image = pg.transform.scale(image,

(int(rect.width*scale),

int(rect.height*scale)))

returnimage

defload_all_gfx(directory, colorkey=( 255, 0, 255) , accept=( '.png', '.jpg', '.bmp', '.gif')):

graphics = {}

forpic inos.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] = img

returngraphics

# pygame related initial code

pg.init

pg.event.set_allowed([pg.KEYDOWN, pg.KEYUP, pg.QUIT])

pg.display.set_caption(c.ORIGINAL_CAPTION)

SCREEN = pg.display.set_mode(c.SCREEN_SIZE)

SCREEN_RECT = SCREEN.get_rect

GFX = load_all_gfx(os.path.join( "resources", "graphics"))

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

用到的图片

图片文件名要保存为对应的,不然代码中会找不到,并且保存到state_demo.py所在目录下的resourcesgraphics子目录中。如果能上github,可以直接下载resourcesgraphics目录中的图片。

1、item_objects.png

2、level_1.png

3、mario_bros.png

4、text_images.png

5、tile_set.png

6、title_screen.png

编译环境:python3.7 + pygame1.9。

声明:本文为CSDN博主「marble_xu」的原创文章,原文链接:https://blog.csdn.net/marble_xu/article/details/96427946游戏网

责任编辑:

相关下载

玩家评论

今年哪部电影下了大血本?必须是《唐顿庄园》啊!_玛丽

原标题:今年哪部电影下了大血本?必须是《唐顿庄园》啊! 文|Dedee 周六,我们不谈车只装腔 翘首企盼了整整四年,《唐顿庄园》终于赶在今年12月于国内上映详情>>

阅读: 1
日期: 2019-12-21
简直是来自韩国的玛丽苏高干文啊,可玄彬还是让我沉迷_孙艺珍

原标题:简直是来自韩国的玛丽苏高干文啊,可玄彬还是让我沉迷 姐关注了很久的韩剧《爱的迫降》终于开播啦! 总体来说,扯是真的扯,甜也是真的甜。剧情很详情>>

阅读: 5
日期: 2019-12-18
这首歌今天登上全球第一,用了整整25年_玛丽亚·凯莉

原标题:这首歌今天登上全球第一,用了整整25年 原创: Lens WeLens 在1994年的圣诞前夕,哥伦比亚公司发行了女歌手玛丽亚·凯莉(Mariah Carey)《圣诞快详情>>

阅读: 6
日期: 2019-12-18
英国老爸化身"Tony老师":给女儿设计"圣诞树"发型吸睛_玛丽亚

原标题:英国老爸化身"Tony老师":给女儿设计"圣诞树"发型吸睛 海外网12月14日电 近日,一群外国老爸为了学习如何给女儿扎头发,自发开办了一所发型学校详情>>

阅读: 8
日期: 2019-12-14
《启源女神》轮椅妹玛丽属性介绍 轮椅妹玛丽作用分析及心得

导 读 其实启源女神游戏中,有玩家小伙伴认为轮椅妹玛丽这张卡有问题,回血量少的可怜,小编就来给大家解释一下,它是高倍率2火165的无条件AOE,可以进AOE一波流套路。这是非常重要详情>>

阅读: 1
日期: 2019-12-14
《启源女神》轮椅妹玛丽怎么玩 轮椅妹玛丽玩法攻略

导 读 最近在启源女神这款游戏里,大家都在问游戏中的玛丽这个角色怎么玩,有玩家小伙伴认为轮椅妹玛丽这张卡有问题,回血量少的可怜,小编就来给大家解释一下,想看细节的话就可以详情>>

阅读: 11
日期: 2019-12-12
《启源女神》玛丽怎么样 玛丽技能属性分享

导 读 在启源女神里,玛丽的名字来自于大名鼎鼎的血腥玛丽。启源女神玛丽怎么样?玛丽这张卡片好用吗?玛丽厉害吗?或许有些小伙伴还不是很了解,感兴趣的小伙伴们可千万不要错详情>>

阅读: 5
日期: 2019-12-05
启源女神轮椅妹玛丽玩法分享启源女神玛丽扩展阵容推荐

今天小编为大家介绍一下启源女神游戏中的轮椅妹玛丽的玩法攻略和她的扩展阵容推荐吧,有需要的朋友一定要仔细看哦。轮椅妹玛丽的作用:厉害在这是高倍率2火165的无条件AOE,可以详情>>

阅读: 0
日期: 2019-12-05
《弹射世界》玛丽娜船长怎么样 玛丽娜船长属性资料图鉴

导 读 在弹射世界中,有很多的五星角色,也是游戏中强度最高的角色了,接下来小编就给大家带来其中的船长玛丽娜角色介绍,希望能够帮助到大家起到作用。接下来感兴趣的小伙伴们就详情>>

阅读: 5
日期: 2019-12-03
一个时代的回忆,《石器时代》玛丽娜斯村

就算是从来没有接触过石器时代的人们肯定也曾经听过说“渔村”这两个字,它实在是太有名气了。 《石器时代》当中有四大新手村,其详情>>

阅读: 6
日期: 2019-11-22
战斗天赋解析系统玛丽亚攻略大全玛丽亚玩法指南及对局思路分享

战斗天赋解析系统中的玛丽亚是个输出型职业,很多玩家不会使用玛丽亚,下面小编就给各位带来了战斗天赋解析系统玛丽亚攻略大全,一起来看看吧。 特点与基本玩法:普通攻击为多段伤详情>>

阅读: 4
日期: 2019-11-20
超级玛丽主题餐厅看游戏和美食之间的磨合

超级玛丽主题餐厅其实也就是游戏和美食的结合,但是有的时候结合不好的话,那么就是一个不好的结合,慢慢的,大家也只是体验到了一个装修环境或者一个新鲜详情>>

阅读: 8
日期: 2019-11-20
大叔带萝莉,新番治愈动画《索玛丽与森林之神》公布最新PV_作品

原标题:大叔带萝莉,新番治愈动画《索玛丽与森林之神》公布最新PV 将于2020年1月开播的TV动画《索玛丽与森林之神》讲述了森林守护者哥雷姆与人类少详情>>

阅读: 2
日期: 2019-11-19
TV动画《索玛丽与森林之神》最新PV、主视觉图公开柴田理惠、速水奖等声优追加_罗莎

原标题:TV动画《索玛丽与森林之神》最新PV、主视觉图公开 柴田理惠、速水奖等声优追加 11月17日,将于2020年1月开播的TV动画《索玛丽与森林之神》详情>>

阅读: 8
日期: 2019-11-18
Sideshow漫威漫画《神奇蝙蝠侠:再续誓言》蜘蛛侠和玛丽简12.5寸雕像_封面

原标题:Sideshow 漫威漫画《神奇蝙蝠侠:再续誓言》蜘蛛侠和玛丽简12.5寸雕像 Sideshow将漫威漫画《神奇蝙蝠侠:再续誓言》其中一版由艺术家J. Scott 详情>>

阅读: 4
日期: 2019-11-18
超级玛丽也开始做餐厅了吗?主题餐厅带来新乐趣

当然并不是游戏去做餐饮了,也算不上是一种跨界,因为游戏主题餐厅在这两年来还是非常多的,超级玛丽也开始做餐厅了吗?并不是真正意义的做餐厅了,是主题餐详情>>

阅读: 8
日期: 2019-11-14
《狐王殿下恋爱了》今日上线!人狐相恋打造不一样的玛丽苏_颜雨

原标题:《狐王殿下恋爱了》今日上线!人狐相恋打造不一样的玛丽苏 11月11日,由人气女频导演陈未衾执导,潘子剑、吴艺敏、何之舟、伍程主演的玛丽苏爱情详情>>

阅读: 4
日期: 2019-11-12
神隐200多年,生不逢时的法国时尚女皇玛丽珠宝拍卖_安托瓦内特

原标题:神隐200多年,生不逢时的法国时尚女皇玛丽珠宝拍卖 拍卖行苏富比(Sotheby)在美国纽约展出一批精选珠宝,其中包括11月将在日内瓦拍卖,此前属于法国皇后详情>>

阅读: 6
日期: 2019-11-11
加州山火“金卡德”渐弱“玛丽亚”又起

原标题:加州山火“金卡德”渐弱“玛丽亚”又起 新华社北京11月3日电(记者陈立希)美国加利福尼亚州南部山火“玛丽亚”截至2日燃烧3天。大风助推火势,详情>>

阅读: 10
日期: 2019-11-03
启源女神玛丽值得培养吗启源女神玛丽角色评测及培养攻略

启源女神是近期十分热门的手游,其中玛丽受到很多玩家关注,玛丽值得培养吗,今天小编就带来一篇启源女神玛丽值得培养吗 启源女神玛丽角色评测及培养攻略,希望对各位小伙伴们有所详情>>

阅读: 6
日期: 2019-10-31
《沉睡2》三个女人一台戏!黑暗世界中,不同种族能否和谐共处?_玛丽菲森

原标题:《沉睡2》三个女人一台戏!黑暗世界中,不同种族能否和谐共处? 引子:在《沉睡魔咒1》中,剧情一改“王子用真爱之吻唤醒公主”这样的烂俗戏码,而是详情>>

阅读: 5
日期: 2019-10-25
欧美性感美女玛丽亚凯莉娇艳写真图片(图片)

详情>>

阅读: 2
日期: 2019-10-22
扎哈拉-玛丽-朱莉-皮特写真图片(图片)

详情>>

阅读: 1
日期: 2019-10-22
牧场物语重聚矿石镇玛丽结婚对象的信息详情-牧场物语重聚矿石镇玛丽结婚对象个人资料详情介绍

在《牧场物语:重聚矿石镇》游戏中,男性玩家可以与女角色结婚,这些女角色的个人资料大家一定有所好奇,这里就是关于《牧场物语:重聚矿石镇》玛丽结婚对象介绍的攻略,希望能够帮到详情>>

阅读: 42
日期: 2019-10-21
光环无限工作室称:制作人离职后不会影响后续开发_玛丽奥尔森

原标题:光环无限工作室称:制作人离职后不会影响后续开发 在8月份的时候,微软证实了《光环:无限》的创意总监蒂姆•隆戈已经离开343工作室。因此,执详情>>

阅读: 11
日期: 2019-10-16
打造你的血之少女!《神狱塔:断罪玛丽2》新预告

【导读】《神狱塔:断罪玛丽2》公布了新截图和游戏新预告片!感兴趣的小伙伴一起来看美少女携手作战吧!   《神狱塔:断罪玛丽2》是一款详情>>

阅读: 6
日期: 2019-10-16
美少女携手作战!《神狱塔断罪玛丽2》新预告

近日,Idea Factory International公布了NS版《神狱塔:断罪玛丽2》的一系列新截图和游戏新预告片,以下为具体内容。 新预告欣赏: 《神狱塔:断罪玛丽2详情>>

阅读: 13
日期: 2019-10-16
几个月前全网黑lisa黑出翔,如今一张玛丽苏图美出圈?

原标题:几个月前全网黑lisa黑出翔,如今一张玛丽苏图美出圈? 不知道大家发现没有,粉上一个新爱豆,往往是从一张出圈图开始。当时的情况可能是这样的,一个详情>>

阅读: 8
日期: 2019-10-14
还记得玛丽苏小说的鼻祖可爱淘吗?她现在长这样了

原标题:还记得玛丽苏小说的鼻祖可爱淘吗?她现在长这样了 今天要说的,是一位作家。 也是我的玛丽苏文学的启蒙老师——可爱淘! 试问,哪个90后上学的时详情>>

阅读: 15
日期: 2019-10-14
还在穿恨天高吗?低跟的猫跟鞋玛丽珍鞋绑带芭蕾鞋也能让你美上天

原标题:还在穿恨天高吗?低跟的猫跟鞋玛丽珍鞋绑带芭蕾鞋也能让你美上天 古有高跟鞋“在秀不在穿”,今有高跟鞋“在美不在高”。很多女性追求“详情>>

阅读: 7
日期: 2019-10-14
PinkMary粉红玛琍(粉红玛丽)引领今秋复古风,尽显“现代王妃”风范

原标题:Pink Mary 粉红玛琍(粉红玛丽)引领今秋复古风,尽显“现代王妃”风范 作为轻熟女性最成功的穿衣楷模,无论英国公爵夫人Kate Middleton、摩纳哥王详情>>

阅读: 13
日期: 2019-09-01
49岁的玛丽亚·凯莉,身材发福到120斤,风韵依然不减!

原标题:49岁的玛丽亚·凯莉,身材发福到120斤,风韵依然不减! 说起玛丽亚·凯莉,人们第一时间想到的是她甜美的歌喉,其实她的迷人之处还有很多,比如说她详情>>

阅读: 21
日期: 2019-09-01
《神狱塔断罪玛丽2》开场动画公布更有海量新截图

原标题:《神狱塔断罪玛丽2》开场动画公布 更有海量新截图 今日(8月28日),Idea Factory International公布了NS版《神狱塔:断罪玛丽2》的一系列新截图,以详情>>

阅读: 7
日期: 2019-08-28
《索玛丽与森林之神》发布预告来势汹汹_森山直

原标题:《索玛丽与森林之神》发布预告来势汹汹 TV动画《索玛丽与森林之神》是一部还未开播就引起日本二次元人士广泛关注的动画,声优水濑祈、小林详情>>

阅读: 6
日期: 2019-08-28
TV动画《索玛丽与森林之神》公开PV与最新海报森山直太朗、吉俣良、山冈晃等金牌制作人坐镇_作品

原标题:TV动画《索玛丽与森林之神》公开PV与最新海报 森山直太朗、吉俣良、山冈晃等金牌制作人坐镇 2020年1月新番《索玛丽与森林之神》正式官方网详情>>

阅读: 9
日期: 2019-08-27
TV动画《索玛丽与森林之神》公开PV与最新海报_作品

原标题:TV动画《索玛丽与森林之神》公开PV与最新海报 2020年1月新番《索玛丽与森林之神》正式官方网站于今日上线,一举公开了首支PV、最新海报以及详情>>

阅读: 12
日期: 2019-08-27
精彩推荐