利用pygame开发一款跳跃得分游戏
- 游戏规则:可以左右移动以及空格跳跃,如果获得了特殊道具,就可以跳跃比较远的距离,敌人会随机生成,如果碰到了飞行敌人,就会死亡,没有跳动平台上也会死亡。
主要代码解析
首先创建玩家类
class Player(pg.sprite.Sprite):
def __init__(self, game):
self._layer = PLAYER_LAYER
self.groups = game.all_sprites
pg.sprite.Sprite.__init__(self, self.groups)
# pg.sprite.Sprite.__init__(self)
self.game = game
# 不同的状态
self.walking = False
self.jumping = False
self.current_frame = 0
self.last_update = 0
self.load_images() # 加载图片
self.image = self.standing_frames[0]
self.rect = self.image.get_rect()
self.rect.center = (WIDTH / 2, HEIGHT / 2)
self.pos = vec(WIDTH / 2, HEIGHT / 2)
self.vel = vec(0, 0) # 速度
self.acc = vec(0, 0) # 加速度
平台
# settings.py
# 平台列表
PLATFORM_LIST = [(0, HEIGHT - 40, WIDTH, 40),
(WIDTH / 2 - 50, HEIGHT * 3 / 4, 100, 20),
(125, HEIGHT - 350, 100, 20),
(350, 200, 100, 20),
(175, 100, 50, 20)]
# main.py/Game
def new(self):
# start a new game
self.all_sprites = pg.sprite.Group()
self.platforms = pg.sprite.Group()
self.player = Player(self)
self.all_sprites.add(self.player)
# 实例化一系列平台,并添加到所有元素组与平台元素组中
for plat in PLATFORM_LIST:
p = Platform(*plat)
self.all_sprites.add(p)
self.platforms.add(p)
self.run()
实现玩家的跳跃
def jump(self):
# 跳跃到其他平台 - 玩家对象x加减1,为了做碰撞检测,只有站立在平台上,才能实现跳跃
self.rect.y += 2
hits = pg.sprite.spritecollide(self, self.game.platforms, False)
self.rect.y -= 2
if hits and not self.jumping:
self.game.jump_sound.play() # 播放跳跃的声音
self.jumping = True
self.vel.y = -20
让玩家真正移动
def update(self):
# 动画
self.animate()
self.acc = vec(0, PLAYER_GRAV)
keys = pg.key.get_pressed()
if keys[pg.K_LEFT]:
self.acc.x = -PLAYER_ACC
if keys[pg.K_RIGHT]:
self.acc.x = PLAYER_ACC
# 获得加速度
self.acc.x += self.vel.x * PLAYER_FRICTION
# 速度与加速度
self.vel += self.acc
# 如果速度小于0.1,则速度为0(比如这样设置,不然速度永远无法0)
if abs(self.vel.x) < 0.1:
self.vel.x = 0
self.pos += self.vel + 0.5 * self.acc
# wrap around the sides of the screen
if self.pos.x > WIDTH:
self.pos.x = 0
if self.pos.x < 0:
self.pos.x = WIDTH
self.rect.midbottom = self.pos
使背景移动并产生敌人
def update(self):
self.all_sprites.update()
# 产生敌人
now = pg.time.get_ticks()
# 通过时间间隔来判断是否要产生敌人
if now - self.mob_timer > 5000 + random.choice([-1000, -500, 0, 500, 1000]):
self.mob_timer = now
Mob(self)
# 碰撞检测 - 如果碰撞到了敌人,游戏结束
mob_hits = pg.sprite.spritecollide(self.player, self.mobs, False, pg.sprite.collide_mask)
if mob_hits:
self.playing = False
# 玩家在界面中时(y>0),进行碰撞检测,检测玩家是否碰撞到平台
if self.player.vel.y > 0:
hits = pg.sprite.spritecollide(self.player, self.platforms, False)
if hits:
lowest = hits[0]
for hit in hits:
if hit.rect.bottom > lowest.rect.bottom:
lowest = hit # 保存最小的值
# 避免平移效果 - 兔子最底部没有小于碰撞检测中的最小值,则不算跳跃到平台上
if self.player.pos.y < lowest.rect.centery:
self.player.pos.y = lowest.rect.top
self.player.vel.y = 0
self.player.jumping = False
# 会产生平移效果
# if hits:
# self.player.pos.y = hits[0].rect.top
# self.player.vel.y = 0
# 玩家到达游戏框 1/4 处时(注意,游戏框,头部为0,底部为游戏框长度,到到游戏框的1/4处,表示已经到达了顶部一部分了)
if self.player.rect.top <= HEIGHT / 4:
# 玩家位置移动(往下移动)
# self.player.pos.y += abs(self.player.vel.y)
self.player.pos.y += max(abs(self.player.vel.y), 2)
# 随机生成新云朵
if random.randrange(100) < 10:
Cloud(self)
# 云彩同步移动
for cloud in self.clouds:
cloud.rect.y += max(abs(self.player.vel.y / 2), 2)
# 敌人位置同步往下移动
for mob in self.mobs:
mob.rect.y += max(abs(self.player.vel.y), 2)
# 平台在游戏框外时,将其注销,避免资源浪费
for plat in self.platforms:
# 平台移动位置(往下移动,移动的距离与玩家相同,这样玩家才能依旧站立在原本的平台上)
# plat.rect.y += abs(self.player.vel.y)
plat.rect.y += max(abs(self.player.vel.y), 2)
if plat.rect.top >= HEIGHT:
plat.kill()
# 分数增加 - 平台销毁,分数相加
self.score += 10
# 碰撞检测 - 玩家碰撞到急速飞跃道具
pow_hits = pg.sprite.spritecollide(self.player, self.powerups, True)
for pow in pow_hits:
# 道具类型 - 不同道具可以实现不同的效果
if pow.type == 'boost':
self.boost_sound.play() # 播放相应的音效
self.player.vel.y = -BOOST_POWER # 快递移动的距离
self.player.jumping = False # 此时不为跳跃状态
# 死亡 - 玩家底部大于游戏框高度
if self.player.rect.bottom > HEIGHT:
for sprite in self.all_sprites:
sprite.rect.y -= max(self.player.vel.y, 10)
# 元素底部小于0 - 说明在游戏框外面,将其删除
if sprite.rect.bottom < 0:
sprite.kill()
# 平台个数为0,游戏结束
if len(self.platforms) == 0:
self.playing = False
# 判断平台数,产生新的平台
while len(self.platforms) < 6:
width = random.randrange(50, 100)
# 平台虽然是随机生成的,但会生成在某一个范围内
p = Platform(self, random.randrange(0, WIDTH - width),
random.randrange(-75, -30))
self.platforms.add(p)
self.all_sprites.add(p)
循环
def show_go_screen(self):
# game over/continue
if not self.running: # 是否在运行
return
pg.mixer.music.load(os.path.join(self.snd_dir, 'Yippee.ogg'))
pg.mixer.music.play(loops=-1)
self.screen.fill(BGCOLOR) # 游戏框背景颜色填充
# 绘制文字
self.draw_text("GAME OVER", 48, WHITE, WIDTH / 2, HEIGHT / 4)
self.draw_text("Score: " + str(self.score), 22, WHITE, WIDTH / 2, HEIGHT / 2)
self.draw_text("Press a key to play again", 22, WHITE, WIDTH / 2, HEIGHT * 3 / 4)
# 判断分数
if self.score > self.highscore:
self.highscore = self.score
self.draw_text("NEW HIGH SCORE!", 22, WHITE, WIDTH / 2, HEIGHT / 2 + 40)
# 记录新的最高分到文件中
with open(os.path.join(self.dir, HS_FILE), 'w') as f:
f.write(str(self.score))
else:
self.draw_text("High Score: " + str(self.highscore), 22, WHITE, WIDTH / 2, HEIGHT / 2 + 40)
# 翻转
pg.display.flip()
# 等待敲击任意键,
self.wait_for_key()
pg.mixer.music.fadeout(500)
加载游戏元素图片
def load_data(self): # 加载数据
self.dir = os.path.dirname(__file__)
filepath = os.path.join(self.dir, HS_FILE)
with open(filepath, 'r') as f:
try:
self.highscore = int(f.read())
except:
self.highscore = 0
img_dir = os.path.join(self.dir, 'img')
# 加载精灵图片
self.spritesheet = Spritesheet(os.path.join(img_dir, SPRITESHEET))
# 加载云彩图片
self.cloud_images = []
for i in range(1, 4):
self.cloud_images.append(pg.image.load(os.path.join(img_dir, 'cloud{}.png'.format(i))).convert())
# 加载音乐
self.snd_dir = os.path.join(self.dir, 'snd')
# 跳跃时音效
self.jump_sound = pg.mixer.Sound(os.path.join(self.snd_dir, 'Jump33.wav'))
# 使用道具时音效
self.boost_sound = pg.mixer.Sound(os.path.join(self.snd_dir, 'Boost16.wav'))
完整代码如下
1. main.py
import pygame as pg
import random
import os
from settings import *
from sprites import *
class Game:
def __init__(self):
pg.init()
pg.mixer.init()
self.screen = pg.display.set_mode((WIDTH, HEIGHT))
pg.display.set_caption(TITLE)
self.clock = pg.time.Clock()
self.running = True
# 设置绘制时使用的字体
self.font_name = pg.font.match_font(FONT_NAME)
self.load_data()
def load_data(self): # 加载数据
self.dir = os.path.dirname(__file__)
filepath = os.path.join(self.dir, HS_FILE)
with open(filepath, 'r') as f:
try:
self.highscore = int(f.read())
except:
self.highscore = 0
img_dir = os.path.join(self.dir, 'img')
# 加载精灵图片
self.spritesheet = Spritesheet(os.path.join(img_dir, SPRITESHEET))
# 加载云彩图片
self.cloud_images = []
for i in range(1, 4):
self.cloud_images.append(pg.image.load(os.path.join(img_dir, 'cloud{}.png'.format(i))).convert())
# 加载音乐
self.snd_dir = os.path.join(self.dir, 'snd')
# 跳跃时音效
self.jump_sound = pg.mixer.Sound(os.path.join(self.snd_dir, 'Jump33.wav'))
# 使用道具时音效
self.boost_sound = pg.mixer.Sound(os.path.join(self.snd_dir, 'Boost16.wav'))
def new(self):
self.score = 0
# self.all_sprites = pg.sprite.Group()
# 层次添加,避免元素重叠显示(如背景云遮挡住平台与玩家)
self.all_sprites = pg.sprite.LayeredUpdates()
self.platforms = pg.sprite.Group()
self.powerups = pg.sprite.Group() # 急速飞跃道具
self.mobs = pg.sprite.Group() # 敌人对象
self.clouds = pg.sprite.Group() # 云彩对象
self.player = Player(self)
self.all_sprites.add(self.player)
for plat in PLATFORM_LIST:
p = Platform(self, *plat)
# self.all_sprites.add(p)
# self.platforms.add(p)
self.mob_timer = 0
# 游戏的背景音乐
pg.mixer.music.load(os.path.join(self.snd_dir, 'Happy Tune.ogg'))
# 创建云彩
for i in range(8):
c = Cloud(self)
c.rect.y += 500
self.run()
def run(self):
# loops表示循环次数,-1表示音乐将无限播放下去
pg.mixer.music.play(loops=-1)
self.playing = True
while self.playing:
self.clock.tick(FPS)
self.events()
self.update()
self.draw()
# 退出后停止音乐,fadeout(time)设置音乐淡出的时间,该方法会阻塞到音乐消失
pg.mixer.music.fadeout(500)
def update(self):
self.all_sprites.update()
# 产生敌人
now = pg.time.get_ticks()
# 通过时间间隔来判断是否要产生敌人
if now - self.mob_timer > 5000 + random.choice([-1000, -500, 0, 500, 1000]):
self.mob_timer = now
Mob(self)
# 碰撞检测 - 如果碰撞到了敌人,游戏结束
mob_hits = pg.sprite.spritecollide(self.player, self.mobs, False, pg.sprite.collide_mask)
if mob_hits:
self.playing = False
# 玩家在界面中时(y>0),进行碰撞检测,检测玩家是否碰撞到平台
if self.player.vel.y > 0:
hits = pg.sprite.spritecollide(self.player, self.platforms, False)
if hits:
lowest = hits[0]
for hit in hits:
if hit.rect.bottom > lowest.rect.bottom:
lowest = hit # 保存最小的值
# 避免平移效果 - 兔子最底部没有小于碰撞检测中的最小值,则不算跳跃到平台上
if self.player.pos.y < lowest.rect.centery:
self.player.pos.y = lowest.rect.top
self.player.vel.y = 0
self.player.jumping = False
# 会产生平移效果
# if hits:
# self.player.pos.y = hits[0].rect.top
# self.player.vel.y = 0
# 玩家到达游戏框 1/4 处时(注意,游戏框,头部为0,底部为游戏框长度,到到游戏框的1/4处,表示已经到达了顶部一部分了)
if self.player.rect.top <= HEIGHT / 4:
# 玩家位置移动(往下移动)
# self.player.pos.y += abs(self.player.vel.y)
self.player.pos.y += max(abs(self.player.vel.y), 2)
# 随机生成新云朵
if random.randrange(100) < 10:
Cloud(self)
# 云彩同步移动
for cloud in self.clouds:
cloud.rect.y += max(abs(self.player.vel.y / 2), 2)
# 敌人位置同步往下移动
for mob in self.mobs:
mob.rect.y += max(abs(self.player.vel.y), 2)
# 平台在游戏框外时,将其注销,避免资源浪费
for plat in self.platforms:
# 平台移动位置(往下移动,移动的距离与玩家相同,这样玩家才能依旧站立在原本的平台上)
# plat.rect.y += abs(self.player.vel.y)
plat.rect.y += max(abs(self.player.vel.y), 2)
if plat.rect.top >= HEIGHT:
plat.kill()
# 分数增加 - 平台销毁,分数相加
self.score += 10
# 碰撞检测 - 玩家碰撞到急速飞跃道具
pow_hits = pg.sprite.spritecollide(self.player, self.powerups, True)
for pow in pow_hits:
# 道具类型 - 不同道具可以实现不同的效果
if pow.type == 'boost':
self.boost_sound.play() # 播放相应的音效
self.player.vel.y = -BOOST_POWER # 快递移动的距离
self.player.jumping = False # 此时不为跳跃状态
# 死亡 - 玩家底部大于游戏框高度
if self.player.rect.bottom > HEIGHT:
for sprite in self.all_sprites:
sprite.rect.y -= max(self.player.vel.y, 10)
# 元素底部小于0 - 说明在游戏框外面,将其删除
if sprite.rect.bottom < 0:
sprite.kill()
# 平台个数为0,游戏结束
if len(self.platforms) == 0:
self.playing = False
# 判断平台数,产生新的平台
while len(self.platforms) < 6:
width = random.randrange(50, 100)
# 平台虽然是随机生成的,但会生成在某一个范围内
p = Platform(self, random.randrange(0, WIDTH - width),
random.randrange(-75, -30))
self.platforms.add(p)
self.all_sprites.add(p)
# 事件处理
def events(self):
for event in pg.event.get():
# 关闭
if event.type == pg.QUIT:
if self.playing:
self.playing = False
self.running = False
# 跳跃
if event.type == pg.KEYDOWN:
if event.key == pg.K_SPACE:
self.player.jump()
# 按钮抬起,减小跳跃速度,从而实现,快速点击,短跳,长按,长跳的效果
if event.type == pg.KEYUP:
if event.key == pg.K_SPACE:
self.player.jump_cut()
def draw(self):
# 绘制
self.screen.fill(BGCOLOR)
self.all_sprites.draw(self.screen)
# 绘制文字 - 具体的分数
self.draw_text(str(self.score), 22, WHITE, WIDTH / 2, 15)
# 翻转
pg.display.flip()
# 开始游戏的钩子函数
def show_start_screen(self):
pg.mixer.music.load(os.path.join(self.snd_dir, 'Yippee.ogg'))
pg.mixer.music.play(loops=-1)
self.screen.fill(BGCOLOR) # 填充颜色
# 绘制文字
self.draw_text(TITLE, 48, WHITE, WIDTH / 2, HEIGHT / 4)
self.draw_text("Left and right button move, space bar jump", 22, WHITE, WIDTH / 2, HEIGHT / 2)
self.draw_text("Press any key to start the game", 22, WHITE, WIDTH / 2, HEIGHT * 3 / 4)
# 画布翻转
pg.display.flip()
self.wait_for_key() # 等待用户敲击键盘中的仍以位置
pg.mixer.music.fadeout(500)
def wait_for_key(self):
waiting = True
while waiting:
self.clock.tick(FPS)
for event in pg.event.get():
if event.type == pg.QUIT: # 点击退出,结束等待循环
waiting = False
self.running = False
if event.type == pg.KEYUP: # 按下键盘,结束等待循环
waiting = False
def show_go_screen(self):
# game over/continue
if not self.running: # 是否在运行
return
pg.mixer.music.load(os.path.join(self.snd_dir, 'Yippee.ogg'))
pg.mixer.music.play(loops=-1)
self.screen.fill(BGCOLOR) # 游戏框背景颜色填充
# 绘制文字
self.draw_text("GAME OVER", 48, WHITE, WIDTH / 2, HEIGHT / 4)
self.draw_text("Score: " + str(self.score), 22, WHITE, WIDTH / 2, HEIGHT / 2)
self.draw_text("Press a key to play again", 22, WHITE, WIDTH / 2, HEIGHT * 3 / 4)
# 判断分数
if self.score > self.highscore:
self.highscore = self.score
self.draw_text("NEW HIGH SCORE!", 22, WHITE, WIDTH / 2, HEIGHT / 2 + 40)
# 记录新的最高分到文件中
with open(os.path.join(self.dir, HS_FILE), 'w') as f:
f.write(str(self.score))
else:
self.draw_text("High Score: " + str(self.highscore), 22, WHITE, WIDTH / 2, HEIGHT / 2 + 40)
# 翻转
pg.display.flip()
# 等待敲击任意键,
self.wait_for_key()
pg.mixer.music.fadeout(500)
# 绘制文字
def draw_text(self, text, size, color, x, y):
font = pg.font.Font(self.font_name, size) # 设置字体与大小
text_surface = font.render(text, True, color) # 设置颜色
text_rect = text_surface.get_rect() # 获得字体对象
text_rect.midtop = (x, y) # 定义位置
self.screen.blit(text_surface, text_rect) # 在屏幕中绘制字体
g = Game()
g.show_start_screen()
while g.running:
g.new()
g.show_go_screen()
pg.quit()
2. sprites.py
import pygame as pg
import random
from settings import *
vec = pg.math.Vector2 # 二维变量
# 加载与解析精灵图片
class Spritesheet:
def __init__(self, filename):
# 主要要使用convert()进行优化, convert()方法会 改变图片的像素格式
# 这里加载了整张图片
self.spritesheet = pg.image.load(filename).convert()
# 从比较大的精灵表中,通过相应的xml位置,抓取中出需要的元素
def get_image(self, x, y, width, height):
# 创建Surface对象(画板对象)
image = pg.Surface((width, height))
# blit — 画一个图像到另一个
# 将整张图片中,对应位置(x,y)对应大小(width,height)中的图片画到画板中
image.blit(self.spritesheet, (0, 0), (x, y, width, height))
# pygame.transform.scale 缩放的大小
# 这里将图片缩放为原来的一半
image = pg.transform.scale(image, (width // 2, height // 2))
return image
class Player(pg.sprite.Sprite):
def __init__(self, game):
self._layer = PLAYER_LAYER
self.groups = game.all_sprites
pg.sprite.Sprite.__init__(self, self.groups)
# pg.sprite.Sprite.__init__(self)
self.game = game
# 不同的状态
self.walking = False
self.jumping = False
# 当前帧(用于判断当前要执行哪个动画)
self.current_frame = 0
self.last_update = 0
self.load_images() # 加载图片
self.image = self.standing_frames[0]
self.rect = self.image.get_rect()
self.rect.center = (WIDTH / 2, HEIGHT / 2)
self.pos = vec(WIDTH / 2, HEIGHT / 2)
self.vel = vec(0, 0) # 速度
self.acc = vec(0, 0) # 加速度
def load_images(self):
# 站立状态
self.standing_frames = [self.game.spritesheet.get_image(614, 1063, 120, 191),
self.game.spritesheet.get_image(690, 406, 120, 201)]
for frame in self.standing_frames:
frame.set_colorkey(BLACK) # 将图像矩阵中除图像外周围的元素都设置为透明的
# 走动状态
self.walk_frames_r = [self.game.spritesheet.get_image(678, 860, 120, 201),
self.game.spritesheet.get_image(692, 1458, 120, 207)]
self.walk_frames_l = []
for frame in self.walk_frames_r:
frame.set_colorkey(BLACK)
# 水平翻转
self.walk_frames_l.append(pg.transform.flip(frame, True, False))
# 跳跃状态
self.jump_frame = self.game.spritesheet.get_image(382, 763, 150, 181)
self.jump_frame.set_colorkey(BLACK)
# 短跳
def jump_cut(self):
if self.jumping:
if self.vel.y < -3:
self.vel.y = -3
def jump(self):
# 跳跃到其他平台 - 玩家对象x加减1,为了做碰撞检测,只有站立在平台上,才能实现跳跃
self.rect.y += 2
hits = pg.sprite.spritecollide(self, self.game.platforms, False)
self.rect.y -= 2
if hits and not self.jumping:
self.game.jump_sound.play() # 播放跳跃的声音
self.jumping = True
self.vel.y = -20
def update(self):
# 动画
self.animate()
self.acc = vec(0, PLAYER_GRAV)
keys = pg.key.get_pressed()
if keys[pg.K_LEFT]:
self.acc.x = -PLAYER_ACC
if keys[pg.K_RIGHT]:
self.acc.x = PLAYER_ACC
# 获得加速度
self.acc.x += self.vel.x * PLAYER_FRICTION
# 速度与加速度
self.vel += self.acc
# 如果速度小于0.1,则速度为0(比如这样设置,不然速度永远无法0)
if abs(self.vel.x) < 0.1:
self.vel.x = 0
self.pos += self.vel + 0.5 * self.acc
# wrap around the sides of the screen
if self.pos.x > WIDTH:
self.pos.x = 0
if self.pos.x < 0:
self.pos.x = WIDTH
self.rect.midbottom = self.pos
def animate(self):
'''
pygame 中的时间是以毫秒(千分之一秒)表示的。
通过 pygame.time.get_ticks 函数可以获得 pygame.init 后经过的时间的毫秒数。
'''
now = pg.time.get_ticks()
if self.vel.x != 0: # 判断速度在x轴方向是否为0,从而判断玩家对象是否移动
self.walking = True
else:
self.walking = False
# 走动状态下的动画
if self.walking:
# 当前时间 - 上次时间 大于 180,即间隔时间大于180时
if now - self.last_update > 180:
self.last_update = now
# 当前帧 加一 与 walk_frames_l 长度取余,从而得到当前要做哪个东西
self.current_frame = (self.current_frame + 1) % len(self.walk_frames_l)
bottom = self.rect.bottom
# 向左走还是向右走
if self.vel.x > 0:
# 当前帧要做的动作
self.image = self.walk_frames_r[self.current_frame]
else:
self.image = self.walk_frames_l[self.current_frame]
self.rect = self.image.get_rect()
self.rect.bottom = bottom
# 站立状态下的动画
if not self.jumping and not self.walking:
if now - self.last_update > 350:
self.last_update = now
self.current_frame = (self.current_frame + 1) % len(self.standing_frames)
bottom = self.rect.bottom
self.image = self.standing_frames[self.current_frame]
self.rect = self.image.get_rect()
self.rect.bottom = bottom
self.mask = pg.mask.from_surface(self.image) # 创建蒙版
class Platform(pg.sprite.Sprite):
def __init__(self, game, x, y):
self._layer = PLATFORM_LAYER # 对应的图层
self.groups = game.all_sprites, game.platforms # 所在的组
pg.sprite.Sprite.__init__(self, self.groups) # 添加、实例化
# pg.sprite.Sprite.__init__(self)
self.game = game
# 载入图片
images = [self.game.spritesheet.get_image(0, 288, 380, 94),
self.game.spritesheet.get_image(213, 1662, 201, 100)]
# 随机选择一种
self.image = random.choice(images)
self.image.set_colorkey(BLACK)
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
# 随机在平台上初始化道具
if random.randrange(100) < POW_SPAWN_PCT:
Pow(self.game, self)
# 道具对象
class Pow(pg.sprite.Sprite):
def __init__(self, game, plat):
self._layer = POW_LAYER
self.groups = game.all_sprites, game.powerups
pg.sprite.Sprite.__init__(self, self.groups)
self.game = game # 整个游戏对象
self.plat = plat # 平台对象
self.type = random.choice(['boost'])
# 加载道具图片
self.image = self.game.spritesheet.get_image(820, 1805, 71, 70)
self.image.set_colorkey(BLACK)
self.rect = self.image.get_rect()
# 道具出现在平台中间的上方
self.rect.centerx = self.plat.rect.centerx
self.rect.bottom = self.plat.rect.top - 5
def update(self):
# 更新时,避免道具一同移动
self.rect.bottom = self.plat.rect.top - 5
# 如果道具对应的平台已经被消除,那么道具也要被消除
if not self.game.platforms.has(self.plat):
self.kill() # 消除道具
class Mob(pg.sprite.Sprite):
def __init__(self, game):
self._layer = MOB_LAYER
self.groups = game.all_sprites, game.mobs
pg.sprite.Sprite.__init__(self, self.groups)
self.game = game
self.image_up = self.game.spritesheet.get_image(566, 510, 122, 139)
self.image_up.set_colorkey(BLACK)
self.image_down = self.game.spritesheet.get_image(568, 1534, 122, 135)
self.image_down.set_colorkey(BLACK)
self.image = self.image_up # 默认为向上的图片
self.rect = self.image.get_rect()
# x轴中心位置随机选择
self.rect.centerx = random.choice([-100, WIDTH + 100])
# 随机x轴速度
self.vx = random.randrange(1, 4)
if self.rect.centerx > WIDTH:
self.vx *= -1
# 随机敌人y轴位置
self.rect.y = random.randrange(HEIGHT / 2)
self.vy = 0 # y轴速度默认为0
self.dy = 0.5
def update(self):
self.rect.x += self.vx # x轴位置移动
self.vy += self.dy
# 实现上下抖动移动的效果
if self.vy > 3 or self.vy < -3:
self.dy *= -1
center = self.rect.center
# 上下移动方向不同,使用不同的图片
if self.dy < 0:
self.image = self.image_up
else:
self.image = self.image_down
self.rect = self.image.get_rect()
self.mask = pg.mask.from_surface(self.image) # 创建蒙版
self.rect.center = center
# y轴具体的移动
self.rect.y += self.vy
# 超过屏幕范围,删除敌人
if self.rect.left > WIDTH + 100 or self.rect.right < -100:
self.kill()
class Cloud(pg.sprite.Sprite):
def __init__(self, game):
self._layer = CLOUD_LAYER
self.groups = game.all_sprites, game.clouds
pg.sprite.Sprite.__init__(self, self.groups)
self.game = game
self.image = random.choice(self.game.cloud_images)
self.image.set_colorkey(BLACK)
self.rect = self.image.get_rect()
# 随机出现位置
scale = random.randrange(50, 101) / 100
self.image = pg.transform.scale(self.image, (int(self.rect.width * scale),
int(self.rect.height * scale)))
self.rect.x = random.randrange(WIDTH - self.rect.width)
self.rect.y = random.randrange(-500, -50)
def update(self):
# 云朵大于2倍高度,就被消除
if self.rect.top > HEIGHT * 2:
self.kill()
3.settings.py
TITLE = "jumpy!"
WIDTH = 480
HEIGHT = 600
FPS = 60
FONT_NAME = 'arial' # 绘制时使用的字体
HS_FILE = "highscore.txt" # 记录最高分
SPRITESHEET = "spritesheet_jumper.png" # 精灵图片
# 玩家参数
PLAYER_ACC = 0.5
PLAYER_FRICTION = -0.12
PLAYER_GRAV = 0.8
PLAYER_JUMP = 20
# 道具属性
BOOST_POWER = 60
POW_SPAWN_PCT = 7
# 不同元素在不同层
PLAYER_LAYER = 2 # 玩家
PLATFORM_LAYER = 1 # 平台
POW_LAYER = 1 # 道具
MOB_LAYER = 2 # 敌人
CLOUD_LAYER = 0 # 云
# 起始平台
PLATFORM_LIST = [(0, HEIGHT - 60),
(WIDTH / 2 - 50, HEIGHT * 3 / 4 - 50),
(125, HEIGHT - 350),
(350, 200),
(175, 100)]
# 默认的颜色
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
YELLOW = (255, 255, 0)
LIGHTBLUE = (0, 155, 155)
BGCOLOR = LIGHTBLUE
游戏运行截图
来源:oschina
链接:https://my.oschina.net/u/4460029/blog/3223207