恐龙酷跑小游戏

  • 摘要
  • 一,引言
  • 二,系统结构
  • 三,实现代码
  • 四,运行结果
  • 五,总结和展望


摘要

论述了Python语言中Pygame库的框架结构和一些常用的该库API,使用Python库进行2D游戏开发时需要注意的事项,以及进行2D游戏开发的常用技巧。
Python制作chrome断网游戏“小恐龙”,跟chrome断网时的游戏一样,用两根手指就能玩的小游戏,主要效果是实现地图滚动,事件随机生成,恐龙跳跃,下蹲,和记录成绩功能。
关键字:Pygame,游戏开发

一,引言

电子游戏自上个世纪诞生以来,以不可思议的速度发展着,从最初的按钮丢硬币简单的游戏到如今的模拟的视频游戏,电子游戏从最初的纯娱乐产品到现在的游戏行业,他的生命力越发的旺盛和持久。Python语言的出现,也大大的增强了程序的创新和发展,各种方便的接口API和别人些好的库,更好的方便了开发人员的技术选择,从而加快了开发人员的开发进度。Pygame是python的一个开源游戏框架,继承了python开源,快速的优点,使得2D游戏开发进度大大的加快。

二,系统结构

2.1游戏框架图

把恐龙类,鸟类,云类,仙人掌类实现并运用在游戏场景类中,实现这个恐龙酷跑的游戏。

Python编程跑酷 python小恐龙跑酷_游戏


图1 游戏框架图

2.2创建主场景

Python编程跑酷 python小恐龙跑酷_Python_02


图2 主场景图

场景尺寸:800*350

标题:恐龙酷跑

运行帧数:60

对象:恐龙,云,仙人掌,鸟

2.3事件处理流程

Python编程跑酷 python小恐龙跑酷_Python_03


图3 事件处理流程图

三,实现代码

3.1 游戏背景类,生成一个地图

# 地图
class GameBackground:
    image1 = None
    image2 = None
    main_scene = None
    speed = 20 # 滚动速度
    x1 = None
    x2 = None

    # 初始化地图
    def __init__(self, scene):
        # 加载相同张图片资源,做交替实现地图滚动
        self.image1 = pygame.image.load("images/dragon/map.png")  #加载第一张图的路径
        self.image2 = self.image1
        # 保存场景对象
        self.main_scene = scene
        # 辅助移动地图
        self.x1 = 0
        self.x2 = self.main_scene.size[0]

    # 计算地图图片绘制坐标
    def action(self):
        self.x1 = self.x1 - self.speed
        self.x2 = self.x2 - self.speed
        if self.x1 <= -self.main_scene.size[0]:
            self.x1 = 0
        if self.x2 <= 0:
            self.x2 = self.main_scene.size[0]

    # 绘制地图的两张图片
    def draw(self):
        map_y = self.main_scene.size[1] - self.image1.get_height()
        self.main_scene.scene.blit(self.image1, (self.x1, map_y))
        self.main_scene.scene.blit(self.image2, (self.x2, map_y))

3.2 鸟类,生成一只鸟

# 鸟
class Bird:
    speed = 10
    image = None
    x = None
    y = None
    width = None
    height = None
    index = 0
    main_scene = None
    ret = 1

    def __init__(self, x, y, image, main_scene):
        self.x = x
        self.y = y
        self.image = image
        self.main_scene = main_scene
        image = self.image[self.index]
        self.width = image.get_width()
        self.height = image.get_height()

    def move(self):
        self.x -= self.speed

    def draw(self):
        if self.ret <= 8:
            self.index = 0
        else:
            self.index = 1

        if self.ret == 16:
            self.ret = 0

        image = self.image[self.index]
        self.main_scene.scene.blit(image, (self.x, self.y))
        self.ret += 1

3.3 云类,生成云

# 云
class Cloud:
    speed = 1
    image = None
    x = None
    y = None

    def __init__(self, x, y, image):
        self.x = x
        self.y = y
        self.image = image

    def move(self):
        self.x -= self.speed

3.4 仙人掌类,生成仙人掌

# 仙人掌
class Cactus:
    speed = 8
    image = None
    x = None
    y = None
    width = None
    height = None

    def __init__(self, x, y, image):
        self.x = x
        self.y = y
        self.image = image
        self.width = image.get_width()
        self.height = image.get_height()

    def move(self):
        self.x -= self.speed

3.5 恐龙类生成恐龙

# 恐龙
class Dragon:
    speed = 10
    image = None
    x = None
    y = None
    width = None
    height = None
    index = 0
    main_scene = None
    ret = 1
    style = 0 # 0:站立,1:蹲下
    jump = 0 # 0: 未起跳,1:开始上升,2:开始下降
    jump_y_add = 0
    is_hit = 0

    def __init__(self, x, y, image, main_scene):
        self.x = x
        self.y = y
        self.image = image
        self.main_scene = main_scene

    def set_jump(self):
        if self.style == 0 and self.jump == 0:
            self.jump = 1
            self.main_scene.jump_sound.play()

    def move(self):
        if self.jump == 1:
            self.y -= 10
            self.jump_y_add += 10
            if self.jump_y_add == 200:
                self.jump = 2

        if self.jump == 2:
            self.y += 10
            self.jump_y_add -= 10
            if self.jump_y_add == 0:
                self.jump = 0

    def draw(self):
        if self.ret <= 5:
            if self.style == 0:
                self.index = 0
            else:
                self.index = 2
        else:
            if self.style == 0:
                self.index = 1
            else:
                self.index = 3

        if self.ret == 10:
            self.ret = 0

        image = self.image[self.index]
        self.width = image.get_width()
        self.height = image.get_height()
        self.main_scene.scene.blit(image, (self.x, self.y))
        self.ret += 1

3.6游戏主场景

# 主场景
class MainScene:
    running = True
    size = None
    scene = None
    bg = None

    clouds = []
    cloud_image = None

    items = []
    item_images = []
    item_ret= 1
    item_ret_num = 100

    bird_images = []
    birds = []
    bird_ret= 1
    bird_ret_num = 150

    dragon = None
    dragon_images = []

    gameover_image = None
    restart_image = None
    restart_x = None
    restart_y = None
    score = 0.0

    jump_sound = None
    gameover_sound = None

    # 初始化主场景
    def __init__(self):
        # 初始化 pygame,使用自定义字体必须用到
        pygame.init()
        # 场景尺寸
        self.size = (800, 350)
        # 场景对象
        self.scene = pygame.display.set_mode([self.size[0], self.size[1]])
        # 设置标题
        pygame.display.set_caption("恐龙跑酷")
        # 创建clock对象控制帧数
        self.timer = pygame.time.Clock()

        # 创建地图对象
        self.bg = GameBackground(self)

        # 创建云
        self.cloud_image = pygame.image.load("images/dragon/cloud.png")
        self.create_cloud()

        # 创建仙人掌
        for n in range(7):
            self.item_images.append(pygame.image.load("images/dragon/item_" + str(n+1) + ".png"))

        # 创建鸟
        self.bird_images.append(pygame.image.load("images/dragon/bird_1.png"))
        self.bird_images.append(pygame.image.load("images/dragon/bird_2.png"))

        # 创建恐龙
        for n in range(4):
            self.dragon_images.append(pygame.image.load("images/dragon/dragon_" + str(n+1) + ".png"))

        d_x = 50
        d_y = self.size[1] - self.dragon_images[0].get_height()
        self.dragon = Dragon(d_x, d_y, self.dragon_images, self)

        # gameover
        self.gameover_image = pygame.image.load("images/dragon/gameover.png")
        self.restart_image = pygame.image.load("images/dragon/restart.png")

        # 音效加载
        self.jump_sound = pygame.mixer.Sound("sounds/dragon/jump.wav")
        self.gameover_sound = pygame.mixer.Sound("sounds/dragon/gameover.wav")

    #  生成两朵云
    def create_cloud(self):
        self.clouds.append(Cloud(350, 30, self.cloud_image))
        self.clouds.append(Cloud(650, 100, self.cloud_image))

    # 绘制
    def draw_elements(self):
        if self.dragon.is_hit == 1:
            g_x = self.size[0] // 2 - self.gameover_image.get_width() // 2
            self.scene.blit(self.gameover_image, (g_x, 120))

            self.restart_x = self.size[0] // 2 - self.restart_image.get_width() // 2
            self.restart_y = 170
            self.scene.blit(self.restart_image, (self.restart_x, self.restart_y))
            return

        self.scene.fill((255, 255, 255)) # 填充背景色为白色
        self.bg.draw()                   # 绘制背景

        # 绘制云
        for c in self.clouds[:]:
            self.scene.blit(c.image, (c.x, c.y))

        # 绘制仙人掌
        for i in self.items[:]:
            self.scene.blit(i.image, (i.x, i.y))

        # 绘制鸟
        for b in self.birds[:]:
            b.draw()

        # 绘制恐龙
        self.dragon.draw()

        # 绘制跑动距离
        self.score += 0.1
        font = pygame.font.Font("freesansbold.ttf", 20)
        text = font.render(str(int(self.score)) + "m", True, (83, 83, 83))
        text_rect = text.get_rect()
        text_rect.right = self.size[0] - 10
        text_rect.top = 10
        self.scene.blit(text, text_rect)

    # 计算元素坐标及生成元素
    def action_elements(self):
        if self.dragon.is_hit == 1:
            return

        # 地图
        self.bg.action()

        # 云
        for c in self.clouds[:]:
            c.move()

            if c.x < - self.cloud_image.get_width():
                self.clouds.remove(c)

        if len(self.clouds) == 0:
            self.create_cloud()

        # 仙人掌
        if self.item_ret % self.item_ret_num == 0:
            image = self.item_images[random.randint(0, len(self.item_images) - 1)]
            x = self.size[0]
            y = self.size[1] - image.get_height()
            self.items.append(Cactus(x, y, image))
        self.item_ret += 1
        if self.item_ret == self.item_ret_num:
            self.item_ret = 0
            self.item_ret_num = random.randint(60, 110)

        for i in self.items[:]:
            i.move()

            if i.x < -i.width:
                self.items.remove(i)

        # 鸟
        if int(self.score) > 100:
            if self.bird_ret % self.bird_ret_num == 0:
                x = self.size[0]
                y = 210
                self.birds.append(Bird(x, y, self.bird_images, self))
            self.bird_ret += 1
            if self.bird_ret == self.bird_ret_num:
                self.bird_ret = 0
                self.bird_ret_num = random.randint(150, 300)

            for b in self.birds[:]:
                b.move()

                if b.x < -b.width:
                    self.birds.remove(b)

        # 恐龙
        self.dragon.move()

    # 处理事件
    def handle_event(self):
        for event in pygame.event.get():
            # 检测松开键盘事件
            if event.type == pygame.KEYUP:
                if event.key == K_DOWN:
                    if self.dragon.style == 1:
                        self.dragon.style = 0
                        self.dragon.y -= 34

            # 检测按下鼠标事件
            if event.type == pygame.MOUSEBUTTONDOWN:
                if pygame.mouse.get_pressed()[0]:
                    if self.dragon.is_hit == 1:
                        pos = event.pos # 点击位置坐标

                        # 判断点击范围是否是重启图片上
                        x1 = self.restart_x
                        x2 = self.restart_x + self.restart_image.get_width()

                        y1 = self.restart_y
                        y2 = self.restart_y + self.restart_image.get_height()

                        if pos[0] >= x1 and pos[0] <= x2 and pos[1] >= y1 and pos[1] <= y2:
                            self.dragon.is_hit = 0
                            self.score = 0
                            self.items.clear()
                            self.birds.clear()


            # 检测到事件为退出时
            if event.type == pygame.QUIT:
                self.running = False

    # 碰撞检测
    def detect_collision(self):
        if self.dragon.is_hit == 0:
            for i in self.items[:]:
                if self.collision(self.dragon, i) or self.collision(i, self.dragon):
                    self.dragon.is_hit = 1
                    break

            for b in self.birds[:]:
                if self.collision(self.dragon, b) or self.collision(b, self.dragon):
                    self.dragon.is_hit = 1
                    break

            if self.dragon.is_hit == 1:
                self.gameover_sound.play()

    # 验证是否碰撞
    def collision(self, a, b):
        offset = 30 # 增加20误差,降低难度
        temp1 = (b.x + offset <= a.x + a.width <= b.x + offset + b.width)
        temp2 = (b.y + offset <= a.y + a.height <= b.y + offset + b.height)
        return temp1 and temp2

    # 处理按键
    def key_pressed(self):
        # 获取按下按键信息
        key_pressed = pygame.key.get_pressed()

        if key_pressed[K_DOWN]:
            if self.dragon.jump == 0:
                if self.dragon.style == 0:
                    self.dragon.style = 1
                    self.dragon.y += 34

        if key_pressed[K_SPACE]:
            self.dragon.set_jump()

    # 处理帧数
    def set_fps(self):
        # 刷新显示
        pygame.display.update()
        # 设置帧率为60fps
        self.timer.tick(60)

    # 主循环,主要处理各种事件
    def run_scene(self):

        while self.running:
            # 计算元素坐标及生成元素
            self.action_elements()
            # 绘制元素图片
            self.draw_elements()
            # 处理事件
            self.handle_event()
            # 碰撞检测
            self.detect_collision()
            # 按键处理
            self.key_pressed()
            # 更新画布设置fps
            self.set_fps()

四,运行结果

Python编程跑酷 python小恐龙跑酷_游戏_04


Python编程跑酷 python小恐龙跑酷_python_05

五,总结和展望

pygame是刚开始学习,不是很熟悉,游戏实现是参照别人的代码改的,自己的能力还不是很足。对于游戏的坐标计算还是存在问题,有时候鸟和仙人掌会一起出现,这会导致游戏可玩性很差,因为容易死,而且游戏退出有问题,经常游戏会崩溃,这是我还没有解决的一个大问题。而且chome上面的游戏会随着行走的距离的增加而增加行进速度,从而增加游戏的整体难度,而我这个小游戏并不可以做到这一点,他只能在刚开始的时候设定一个初始速度,然后会一直这个速度运行下去。后面我会继续花费时间去研究pygame争取写出完整属于自己的2D小游戏。