利用pygame开发一款跳跃得分游戏

我是研究僧i 提交于 2020-04-07 15:48:08
利用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
游戏运行截图

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!