手把手带大家重写个接金币小游戏呀~
共 8361字,需浏览 17分钟
·
2022-02-18 17:56
↑ 关注 + 星标 ,每天学Python新技能
后台回复【大礼包】送你Python自学大礼包
开
发
工
具
Python版本:3.6.4
相关模块:
pygame模块;
以及一些python自带的模块。
环
境
搭
建
安装Python并添加到环境变量,pip安装需要的相关模块即可。
原
理
简
介
首先先声明一下,这个小游戏主要是依赖pygame写的,不会pygame的小伙伴请先阅读相关的开发文档了解一下这是啥:
官方文档:
https://www.pygame.org/docs/
中文版本的小教程:
https://pygame-zero.readthedocs.io/zh_CN/latest/
ok,言归正传,首先,我们来进行一下游戏的初始化操作(比如初始化游戏窗口,加载一些必要的游戏素材等等),代码实现如下:
'''游戏初始化'''
def initGame():
# 初始化pygame, 设置展示窗口
pygame.init()
screen = pygame.display.set_mode(cfg.SCREENSIZE)
pygame.display.set_caption('catch coins —— 微信公众号: Charles的皮卡丘')
# 加载必要的游戏素材
game_images = {}
for key, value in cfg.IMAGE_PATHS.items():
if isinstance(value, list):
images = []
for item in value: images.append(pygame.image.load(item))
game_images[key] = images
else:
game_images[key] = pygame.image.load(value)
game_sounds = {}
for key, value in cfg.AUDIO_PATHS.items():
if key == 'bgm': continue
game_sounds[key] = pygame.mixer.Sound(value)
# 返回初始化数据
return screen, game_images, game_sounds
接着,为了让我们在写小游戏的过程中保持一个良好的心情,就先让自己喜欢的游戏背景音乐响起来吧:
# 播放背景音乐
pygame.mixer.music.load(cfg.AUDIO_PATHS['bgm'])
pygame.mixer.music.play(-1, 0.0)
然后选一张好看的背景图片,这样才可以在它的基础上逐渐加入接金币这个小游戏涉及到的游戏元素嘛:
源代码如下:
clock = pygame.time.Clock()
while True:
screen.fill(0)
screen.blit(game_images['background'], (0, 0))
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
pygame.display.flip()
clock.tick(cfg.FPS)
接着,我们把用来接金币的小人放进去呗,就像这样:
代码实现如下:
hero = Hero(game_images['hero'], position=(375, 520))
while True:
...
key_pressed = pygame.key.get_pressed()
if key_pressed[pygame.K_a] or key_pressed[pygame.K_LEFT]:
hero.move(cfg.SCREENSIZE, 'left')
if key_pressed[pygame.K_d] or key_pressed[pygame.K_RIGHT]:
hero.move(cfg.SCREENSIZE, 'right')
...
小人的移动主要是通过我们自己定义的Hero类里的move函数实现的,具体而言代码实现如下:
'''定义hero类'''
class Hero(pygame.sprite.Sprite):
def __init__(self, images, position=(375, 520), **kwargs):
pygame.sprite.Sprite.__init__(self)
self.images_right = images[:5]
self.images_left = images[5:]
self.images = self.images_right.copy()
self.image = self.images[0]
self.mask = pygame.mask.from_surface(self.image)
self.rect = self.image.get_rect()
self.rect.left, self.rect.top = position
self.diretion = 'right'
self.speed = 8
self.switch_frame_count = 0
self.switch_frame_freq = 1
self.frame_index = 0
'''左右移动hero'''
def move(self, screensize, direction):
assert direction in ['left', 'right']
if direction != self.diretion:
self.images = self.images_left.copy() if direction == 'left' else self.images_right.copy()
self.image = self.images[0]
self.diretion = direction
self.switch_frame_count = 0
self.switch_frame_count += 1
if self.switch_frame_count % self.switch_frame_freq == 0:
self.switch_frame_count = 0
self.frame_index = (self.frame_index + 1) % len(self.images)
self.image = self.images[self.frame_index]
if direction == 'left':
self.rect.left = max(self.rect.left-self.speed, 0)
else:
self.rect.left = min(self.rect.left+self.speed, screensize[0])
'''画到屏幕上'''
def draw(self, screen):
screen.blit(self.image, self.rect)
实现小人左右走动这个效果的核心思路其实就是根据函数输入的方向来改变小人可以使用的图片组合(images_right/images_left)。然后对于正在往某个方向行动中的小人,每隔switch_frame_freq帧游戏画面就切换一次小人的图片,游戏中的小人图片一共十张:
前5张是向右走的图片组,后5张是向左走的图片组。
现在,我们开始实现掉金币的效果啦,就像这样:
核心代码实现如下:
food_sprites_group = pygame.sprite.Group()
generate_food_freq = random.randint(10, 20)
generate_food_count = 0
while True:
...
generate_food_count += 1
if generate_food_count > generate_food_freq:
generate_food_freq = random.randint(10, 20)
generate_food_count = 0
food = Food(game_images, random.choice(['gold',] * 10 + ['apple']), cfg.SCREENSIZE)
food_sprites_group.add(food)
for food in food_sprites_group:
if food.update(): food_sprites_group.remove(food)
...
其中食物类的定义如下:
'''定义食物类'''
class Food(pygame.sprite.Sprite):
def __init__(self, images_dict, selected_key, screensize, **kwargs):
pygame.sprite.Sprite.__init__(self)
self.screensize = screensize
self.image = images_dict[selected_key]
self.mask = pygame.mask.from_surface(self.image)
self.rect = self.image.get_rect()
self.rect.left, self.rect.bottom = random.randint(20, screensize[0]-20), -10
self.speed = random.randrange(5, 10)
self.score = 1 if selected_key == 'gold' else 5
'''更新食物位置'''
def update(self):
self.rect.bottom += self.speed
if self.rect.top > self.screensize[1]:
return True
return False
整个定义其实很简单,苹果5分,金币1分,update函数用于实时更新食物的位置以实现食物下降的效果。
问题来了,我们该怎么实现接金币的效果呢,这金币一直往下掉接不住可咋办啊。别担心,只需要调用到pygame库里的碰撞检测功能就可以啦,具体而言代码实现如下:
while True:
...
# --碰撞检测
for food in food_sprites_group:
if pygame.sprite.collide_mask(food, hero):
game_sounds['get'].play()
food_sprites_group.remove(food)
score += food.score
...
代码原理其实很简单,就是实时地检测小人有没有接触到金币,接触到的话就发出接到金币的声音,并移除对应的金币和获得一定的奖励得分。效果是这样子的:
最后,我们来完善一下我们的小游戏,让它看起来更加"规范"一些:
(1) 加入倒计时(在规定的时间内获得尽量高的得分)
while True:
...
# --倒计时信息
countdown_text = 'Count down: ' + str((90000 - pygame.time.get_ticks()) // 60000) + ":" + str((90000 - pygame.time.get_ticks()) // 1000 % 60).zfill(2)
countdown_text = font.render(countdown_text, True, (0, 0, 0))
countdown_rect = countdown_text.get_rect()
countdown_rect.topright = [cfg.SCREENSIZE[0]-30, 5]
screen.blit(countdown_text, countdown_rect)
...
(2) 显示得分(显示当前的得分和历史最高分)
while True:
...
# --显示得分
score_text = f'Score: {score}, Highest: {highest_score}'
score_text = font.render(score_text, True, (0, 0, 0))
score_rect = score_text.get_rect()
score_rect.topleft = [5, 5]
screen.blit(score_text, score_rect)
...
(3) 添加游戏结束界面(玩家想结束游戏还是想重新开始游戏呢?)
'''游戏结束画面'''
def showEndGameInterface(screen, cfg, score, highest_score):
# 显示的文本信息设置
font_big = pygame.font.Font(cfg.FONT_PATH, 60)
font_small = pygame.font.Font(cfg.FONT_PATH, 40)
text_title = font_big.render(f"Time is up!", True, (255, 0, 0))
text_title_rect = text_title.get_rect()
text_title_rect.centerx = screen.get_rect().centerx
text_title_rect.centery = screen.get_rect().centery - 100
text_score = font_small.render(f"Score: {score}, Highest Score: {highest_score}", True, (255, 0, 0))
text_score_rect = text_score.get_rect()
text_score_rect.centerx = screen.get_rect().centerx
text_score_rect.centery = screen.get_rect().centery - 10
text_tip = font_small.render(f"Enter Q to quit game or Enter R to restart game", True, (255, 0, 0))
text_tip_rect = text_tip.get_rect()
text_tip_rect.centerx = screen.get_rect().centerx
text_tip_rect.centery = screen.get_rect().centery + 60
text_tip_count = 0
text_tip_freq = 10
text_tip_show_flag = True
# 界面主循环
clock = pygame.time.Clock()
while True:
screen.fill(0)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_q:
return False
elif event.key == pygame.K_r:
return True
screen.blit(text_title, text_title_rect)
screen.blit(text_score, text_score_rect)
if text_tip_show_flag:
screen.blit(text_tip, text_tip_rect)
text_tip_count += 1
if text_tip_count % text_tip_freq == 0:
text_tip_count = 0
text_tip_show_flag = not text_tip_show_flag
pygame.display.flip()
clock.tick(cfg.FPS)
大功告成啦,完整源代码详见相关文件~
效
果
展
示
在终端运行如下命令即可:
python Game9.py
效果如下:
更
多
代码截止2020-11-08测试无误。
随手推下自己B站账号吧(粉丝再多点再开始做视频,大家有问题也可以在B站上私信我):
https://space.bilibili.com/406756145
顺便立个flag,希望在2020年结束之前抽出空来用python复现一下“魔塔”这个经典的小游戏~欢迎大家期待/督促一下我。
推荐阅读
您看此文用 分 秒,转发只需1秒哦~