做网站通常又什么开发完成,网站后台栏目管理,代码优化网站排名,展示型网站设计案例超级马里奥点我下载https://github.com/marblexu/PythonSuperMario 1.游戏介绍
小时候的经典游戏#xff0c;代码参考了github上的项目Mario-Level-1#xff0c;使用pygame来实现#xff0c;从中学习到了横版过关游戏实现中的一些处理方法。原项目实现了超级玛丽的第一个小…
超级马里奥点我下载https://github.com/marblexu/PythonSuperMario 1.游戏介绍
小时候的经典游戏代码参考了github上的项目Mario-Level-1使用pygame来实现从中学习到了横版过关游戏实现中的一些处理方法。原项目实现了超级玛丽的第一个小关。 在原项目的基础上游戏使用json文件来保存每一个关卡的数据将数据和代码解耦合目前已开发4个小关后续关卡的扩展也很方便只需要添加json文件和地图图片支持新的怪物就行。游戏还支持进入水管到新的子地图。 这篇文章是要介绍下游戏中的几个界面显示和界面之前如何转换所以特意写了一个demo程序完整的游戏代码在下面的github链接中下载。
2.状态介绍
游戏中的状态机一般都是有限状态机简写为FSMFinite State Machine简称状态机是表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型。 状态机的每一个状态至少需要有下面三个操作 Startup当从其他状态进入这个状态时需要进行的初始化操作 Update 在这个状态运行时进行的更新操作 Cleanup当从这个状态退出时需要进行的清除操作 状态需要的变量 next: 表示这个状态退出后要转到的下一个状态 persist在状态间转换时需要传递的数据 done表示这个状态是否结束状态机会根据这个值来决定转换状态 状态机代码实现
因为这篇文章的目的是游戏界面的状态机实现所以专门写了一个state_demo.py文件让大家可以更加方便的看代码。
游戏启动代码
开始是 pygame的初始化设置屏幕大小为c.SCREEN_SIZE(800, 600)。所有的常量都保存在单独的constants.py中。
import os
import pygame as pg
import constants as cpg.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()oad_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 {}for pic in os.listdir(directory):name, ext os.path.splitext(pic)if ext.lower() in accept:img pg.image.load(os.path.join(directory, pic))if img.get_alpha():img img.convert_alpha()else:img img.convert()img.set_colorkey(colorkey)graphics[name] imgreturn graphicsGFX 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 函数中定义了上面说的三个变量nextpersistdone还有start_time 和 current_time 用于记录时间。
class State():def __init__(self):self.start_time 0.0self.current_time 0.0self.done Falseself.next Noneself.persist {}abstractmethoddef startup(self, current_time, persist):abstract methoddef cleanup(self):self.done Falsereturn self.persistabstractmethoddef update(sefl, surface, keys, current_time):abstract methodinfo类
下面介绍Info类界面的显示大部分都是由它来完成init函数中create_info_labels函数创建通用的信息create_state_labels函数对于不同的状态会初始化不同的信息。
class Info():def __init__(self, game_info, state):self.coin_total game_info[c.COIN_TOTAL]self.total_lives game_info[c.LIVES]self.state stateself.game_info game_infoself.create_font_image_dict()self.create_info_labels()self.create_state_labels()self.flashing_coin FlashCoin(280, 53)Control类
Control 是状态机类main函数是游戏的主循环setup_states函数设置游戏启动时运行的状态
class 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 Nonedef setup_states(self, state_dict, start_state):self.state_dict state_dictself.state_name start_stateself.state self.state_dict[self.state_name]def main(self):while not self.done:self.event_loop()self.update()pg.display.update()self.clock.tick(self.fps)3.完整源码
constants.py
我设置的游戏时间是300秒也可以改。
SCREEN_HEIGHT 600
SCREEN_WIDTH 800
SCREEN_SIZE (SCREEN_WIDTH,SCREEN_HEIGHT)ORIGINAL_CAPTION Super Mario BrosGAME_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 luigiITEM_SHEET item_objectsstate_demo.py
import os
import pygame as pg
from abc import ABC, abstractmethod
import constants as cclass State():def __init__(self):self.start_time 0.0self.current_time 0.0self.done Falseself.next Noneself.persist {}abstractmethoddef startup(self, current_time, persist):abstract methoddef cleanup(self):self.done Falsereturn self.persistabstractmethoddef update(sefl, surface, keys, current_time):abstract methodclass Menu(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)def startup(self, current_time, persist):self.next c.LOAD_SCREENself.persist persistself.game_info persistself.overhead_info Info(self.game_info, c.MAIN_MENU)self.setup_background()self.setup_player()self.setup_cursor()def setup_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(bottomSCREEN_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)def setup_player(self):self.player_list []player_rect_info [(178, 32, 12, 16), (178, 128, 12, 16)]for rect in player_rect_info:image get_image(GFX[mario_bros],*rect, c.BLACK, 2.9)rect image.get_rect()rect.x, rect.bottom 110, c.GROUND_HEIGHTself.player_list.append((image, rect))self.player_index 0def setup_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 rectself.cursor.state c.PLAYER1def update(self, surface, keys, current_time):self.current_time current_timeself.game_info[c.CURRENT_TIME] self.current_timeself.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)def update_cursor(self, keys):if self.cursor.state c.PLAYER1:self.cursor.rect.y 358if keys[pg.K_DOWN]:self.cursor.state c.PLAYER2self.player_index 1self.game_info[c.PLAYER_NAME] c.PLAYER_LUIGIelif self.cursor.state c.PLAYER2:self.cursor.rect.y 403if keys[pg.K_UP]:self.cursor.state c.PLAYER1self.player_index 0self.game_info[c.PLAYER_NAME] c.PLAYER_MARIOif keys[pg.K_RETURN]:self.done Trueclass LoadScreen(State):def __init__(self):State.__init__(self)self.time_list [2400, 2600, 2635]def startup(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)def set_next_state(self):return c.LEVELdef set_info_state(self):return c.LOAD_SCREENdef update(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 Trueclass GameOver(LoadScreen):def __init__(self):LoadScreen.__init__(self)self.time_list [3000, 3200, 3235]def set_next_state(self):return c.MAIN_MENUdef set_info_state(self):return c.GAME_OVERclass TimeOut(LoadScreen):def __init__(self):LoadScreen.__init__(self)self.time_list [2400, 2600, 2635]def set_next_state(self):if self.persist[c.LIVES] 0:return c.GAME_OVERelse:return c.LOAD_SCREENdef set_info_state(self):return c.TIME_OUTclass Level(State):def __init__(self):State.__init__(self)def startup(self, current_time, persist):self.game_info persistself.persist self.game_infoself.player Noneself.overhead_info Info(self.game_info, c.LEVEL)self.setup_background()def setup_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(bottomself.bg_rect.bottom)def update(self, surface, keys, current_time):self.game_info[c.CURRENT_TIME] self.current_time current_timeself.overhead_info.update(self.game_info, self.player)if self.overhead_info.time 0:self.update_game_info()self.done Trueself.draw(surface)def update_game_info(self):self.persist[c.LIVES] - 1if self.persist[c.LIVES] 0:self.next c.GAME_OVERelif self.overhead_info.time 0:self.next c.TIME_OUTelse:self.next c.LOAD_SCREENdef draw(self, surface):self.level.blit(self.background, self.viewport, self.viewport)surface.blit(self.level, (0,0), self.viewport)self.overhead_info.draw(surface)class Character(pg.sprite.Sprite):def __init__(self, image):pg.sprite.Sprite.__init__(self)self.image imageself.rect self.image.get_rect()class Info():def __init__(self, game_info, state):self.coin_total game_info[c.COIN_TOTAL]self.total_lives game_info[c.LIVES]self.state stateself.game_info game_infoself.create_font_image_dict()self.create_info_labels()self.create_state_labels()self.flashing_coin FlashCoin(280, 53)def create_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 -*for character, image_rect in zip(character_string, image_rect_list):self.image_dict[character] get_image(GFX[text_images], *image_rect, (92, 148, 252), 2.9)def create_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]def create_state_labels(self):if self.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()def create_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)if self.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))def create_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]def create_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]def create_level_labels(self):self.time c.GAME_TIME_OUTself.current_time 0self.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]def create_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]def create_time_out_labels(self):timeout_label []self.create_label(timeout_label, TIME OUT, 290, 310)self.state_labels [timeout_label, *self.info_labels]def create_label(self, label_list, string, x, y):for letter in string:label_list.append(Character(self.image_dict[letter]))self.set_label_rects(label_list, x, y)def set_label_rects(self, label_list, x, y):for i, letter in enumerate(label_list):letter.rect.x x ((letter.rect.width 3) * i)letter.rect.y yif letter.image self.image_dict[-]:letter.rect.y 7letter.rect.x 2def update(self, level_info, levelNone):self.level levelself.handle_level_state(level_info)def handle_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])if self.state c.LOAD_SCREEN:self.update_text(self.stage_label2, level_info[c.LEVEL_NUM])if self.state c.LEVEL:if (level_info[c.CURRENT_TIME] - self.current_time) 1000:self.current_time level_info[c.CURRENT_TIME]self.time - 1self.update_text(self.clock_time_label, self.time, True)def update_text(self, text, score, resetFalse):if reset and len(text) len(str(score)):text.remove(text[0])index len(text) - 1for digit in reversed(str(score)):rect text[index].recttext[index] Character(self.image_dict[digit])text[index].rect rectindex - 1def draw(self, surface):self.draw_info(surface, self.state_labels)if self.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)def draw_info(self, surface, label_list):for label in label_list:for letter in label:surface.blit(letter.image, letter.rect)class FlashCoin(pg.sprite.Sprite):def __init__(self, x, y):pg.sprite.Sprite.__init__(self)self.frame_index 0self.frames []self.load_frames()self.image self.frames[self.frame_index]self.rect self.image.get_rect()self.rect.x xself.rect.y yself.animation_timer 0def load_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)]for frame_rect in frame_rect_list:self.frames.append(get_image(sheet, *frame_rect, c.BLACK, c.BRICK_SIZE_MULTIPLIER))def update(self, current_time):time_list [375, 125, 125, 125]if self.animation_timer 0:self.animation_timer current_timeelif (current_time - self.animation_timer) time_list[self.frame_index]:self.frame_index 1if self.frame_index 4:self.frame_index 0self.animation_timer current_timeself.image self.frames[self.frame_index]class 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 Nonedef setup_states(self, state_dict, start_state):self.state_dict state_dictself.state_name start_stateself.state self.state_dict[self.state_name]def update(self):self.current_time pg.time.get_ticks()if self.state.done:self.flip_state()self.state.update(self.screen, self.keys, self.current_time)def flip_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, persist)def event_loop(self):for event in pg.event.get():if event.type pg.QUIT:self.done Trueelif event.type pg.KEYDOWN:self.keys pg.key.get_pressed()elif event.type pg.KEYUP:self.keys pg.key.get_pressed()def main(self):while not self.done:self.event_loop()self.update()pg.display.update()self.clock.tick(self.fps)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)))return imagedef load_all_gfx(directory, colorkey(255,0,255), accept(.png, .jpg, .bmp, .gif)):graphics {}for pic in os.listdir(directory):name, ext os.path.splitext(pic)if ext.lower() in accept:img pg.image.load(os.path.join(directory, pic))if img.get_alpha():img img.convert_alpha()else:img img.convert()img.set_colorkey(colorkey)graphics[name] imgreturn graphics# 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()
需要的图片
1.
2.
3.
4.
5.
6. 7.
8.