美文网首页Golang游戏设计&游戏开发Go语言
用Go和Korok写一个Flappybird游戏-4

用Go和Korok写一个Flappybird游戏-4

作者: cb878579a389 | 来源:发表于2018-06-11 16:38 被阅读13次

    前一节 我们实现了游戏的核心逻辑,比如鸟的飞行效果,管道和地面的滚动,碰撞检测等等。本节会给各种场景和状态的转换添加过渡效果,让整个游戏进程更加自然。本节你将会学会:

    1. 如何添加场景转换
    2. 如何使用补间动画

    注:如果你没有完成上一节的教程,也可以直接从 这里 下载到上节结束时的代码,以便开始本节的内容。

    添加死亡菜单

    上节结束的时候,我们游戏已经可以正常游戏了,但是死亡的时候仅仅是暂停了动效。此时需要弹出一个“死亡”菜单以更好的提示用户。我们依然使用 GUI 系统来实现这个菜单,有了之前的基础,实现这个菜单应该不是太困难的事情,按部就班:

        // 添加新的菜单参数
        type GameScene struct {
            ...
            gameover struct{
                gfx.Tex2D
                gui.Rect
            }
            score struct{
                gfx.Tex2D
                gui.Rect
            }
            restart struct{
                gfx.Tex2D
                gui.Rect
            }
            ...
        }
    
        // 初始化菜单参数
        sn.gameover.Tex2D, _ = at.GetByName("gameover.png")
        sn.gameover.Rect = gui.Rect{
            X: (320-233)/2,
            Y: 70,
            W: 233,
            H: 70,
        }
        sn.score.Tex2D, _ = at.GetByName("result_board.png")
        sn.score.Rect = gui.Rect{
            X: (320 - 240)/2,
            Y: 200,
            W: 240,
            H: 120,
        }
        sn.restart.Tex2D, _ = at.GetByName("start.png")
        sn.restart.Rect = gui.Rect{
            X: (320 - 120)/2,
            Y: 360,
            W: 120,
            H: 60,
        }
    
        // 实现 showOver 方法,绘制死亡菜单
        func (sn *GameScene) showOver(dt float32) {
    
            // show game over
            gui.Image(1, sn.gameover.Rect, sn.gameover.Tex2D, nil)
    
            // show score
            gui.Image(2, sn.score.Rect, sn.score.Tex2D, nil)
    
            // show restart button
            e := gui.ImageButton(3, sn.restart.Rect, sn.restart.Tex2D, sn.restart.Tex2D, nil)
            if e.JustPressed() {
                // do something...
            }
        }
    

    这部分代码我们就不做过多的解释了,它和实现 Ready 状态的菜单,并没有任何区别。此时点击 "重新开始" 按钮,并没有任何反应,这是因为我们还没有处理点击事件, 运行这部分代码大概会得到这样的“死亡”菜单:

    game over
    重置游戏状态非常简单,在 GameScene 中添加新的方法 reStart (),在这里重置了鸟的当前状态,管道管理系统的状态:
    func (sn *GameScene) reStart() {
        sn.state = Ready
    
        // bird
        sn.bird.state = Flying
        sn.bird.Vec2 = f32.Vec2{80, 240}
        sn.bird.vy = 0
        sn.bird.rotate = 0
        korok.Transform.Comp(sn.bird.Entity).SetRotation(0)
        korok.Flipbook.Comp(sn.bird.Entity).Play("flying")
        // pipes
        sn.PipeSystem.Reset()
        sn.PipeSystem.StartScroll()
    }
    

    然后在之前的事情处理方法中调用之,现在运行:


    reset game state

    游戏状态已经正确的重置了。但是两种状态之间的转换略显僵硬。界面直接切换没有任何的过渡效果,如果能让场景渐变过去那就再好不过了。

    场景转化动画

    在我们当前的实现中,有好几个地方的场景过渡都是有问题的,最早的是开始游戏的场景过渡,其次是重新开始的场景过渡。实现场景过渡有很多种办法,此处的算法是:在场景上蒙上一层白色的遮罩,遮罩逐渐从透明过渡到白色,然后再从白色过渡到透明,在过渡的中间时刻切换场景。

    从新回到 StartScene 这个场景,这次我们将会在它上面添加一层遮罩。这层遮罩是用 GUI 系统添加的一个白色图片,然后用补间动画系统来对遮罩的颜色做插值动画。在 StartScene 中添加一个属性:

    type StartScene struct {
        ...
        mask gfx.Color
    }
    

    它的默认值为透明色,这样默认情况下它是不会遮住场景的,然后在 Update 方法中添加方法绘制遮罩:

        // fade color
        if sn.mask.A > 0 {
            gui.ColorRect(gui.Rect{W:320,H:480}, sn.mask,0)
        }
    

    这段代码绘制了一个白色的矩形框,它的大小完全遮住整个屏幕。接下来是最重要的逻辑了 —— 启动动画:

    func (sn *StartScene) fadeOut() {
        anim.OfColor(&sn.mask, gfx.Transparent, gfx.White).SetFunction(ease.InOutSine).SetDuration(1).OnComplete(func(reverse bool) {
            sn.LoadGame()
        }).Forward()
    }
    if e.JustPressed() {
        sn.fadeOut()
    }
    

    在之前的代码中,点击按钮之后就直接开始 LoadGame 了。现在点击之后,先执行一段动画,动画把遮罩颜色从透明色渐变到白色,在动画结束之后再加载游戏。anim.OfColor 的方法签名如下:

    func OfColor(target *gfx.Color, from, to gfx.Color) *proxyAnimator
    

    它的可以把一个 gfx.Color 在指定的时间内从初始值过渡到目标值。在 Korok 的当前实现中还绘制纯色的图形还有些缺陷,需要设置字体系统之后才可以绘制图形,所以还需要在设置一下字体:

    
    func (sn *StartScene) Load() {
        asset.Texture.LoadAtlas("images/bird.png", "images/bird.json")
        asset.Font.LoadTrueType("font1", "fonts/Marker Felt.ttf")
    }
    
    func (sn *StartScene) OnEnter(g *game.Game) {
        font, _ := asset.Font.Get("font1")
        gui.SetFont(font)
        ...
    }
    

    运行这段代码,下面就是渐变效果:


    fade out

    现在当前场景已经可以渐出了,但是结束的时候好像闪烁了一下。这是因为我么只给当前场景加入了渐出的动画,却没有给下一个场景添加渐入的动画。接下来会 GameScene 加入渐入的动画,但是在实现上会略有不同。在 StartScene 中,我们使用 anim.OfColor 来执行动画,这个方法适合快速实现的场景但是效率不高,在 GameScene 中,我们尝试一种更快的动画实现方式——Tween系统。

    Tween 系统

    这是 Korok 的底层动画系统实现,核心是一个归一化的 Animator 动画驱动器。此处会使用 ColorTween 来执行颜色的补间动画,首先在 GameScene 中添加属性:

    type GameScene struct {
        ...
        
        alphaTween ween.ColorTween
    }
    

    OnEnter 方法中,给它设置一个动画驱动器 Animator:

    sn.alphaTween.Range(gfx.White, gfx.Transparent).Animate(g.TweenEngine.NewAnimator())
    sn.alphaTween.Animator().SetFunction(ease.InOutSine).SetDuration(.5).Forward()
    

    这段代码给 ColorTween 设置了起始颜色 [White, Transparent],并配置了 Animator,然后配置 Animatorease 方程为 InOutSine,设置动画时长为 0.5秒,然后直接启动动画(因为我们希望场景加载之后立刻执行一个渐入的动画)。同时,和前一个场景类似,在 Update 方法中,需要绘制一个遮罩:

        if sn.alphaTween.Value().A > 0 {
            z := gui.SetZOrder(gui.DefaultZOrder+1)
            gui.ColorRect(gui.Rect{0,0, 320, 480}, sn.alphaTween.Value(), 0)
            gui.SetZOrder(z)
        }
    

    此处绘制遮罩的时候,调整了遮罩的z-order,这样可以使它绘制在其它GUI元素的上层,之所以要改变 z-order 是因为GUI的绘制使用的是画家算法,先绘制的UI元素实际会显示在下层,而我们在 Update 方法的入口绘制的遮罩,如果不改变z-order他会显示在其它UI的下方。现在运行一下:

    perfect!!
    现在我们已经实现了一次完美的过渡动画!!

    接下来还有一个地方,在点击重新开始的时候也需要过渡动画。前面已经写了很多代码了,实现这里的动画,只要调用 ColorTween 直接执行新的即可:

        if e.JustPressed() {
            sn.alphaTween.Range(gfx.Transparent, gfx.White)
            sn.alphaTween.Animator().SetFunction(ease.InOutSine).SetDuration(.5).OnComplete(func (r bool) {
                sn.reStart()
            }).Forward()
        }
    
        func (sn *GameScene) reStart() {
            ....
            // reverse
            sn.alphaTween.Animator().OnComplete(nil).Reverse()
        }
    

    检测到点击事件后,不再直接调用 reStart() 方法,而是先执行 ColorTween 启动动画,在动画结束后在调用 reStart() 方法,同时在 reStart() 结束后执行 ColorTweenReverse 方法,这是一个便捷的方法可以把刚刚的动画过程反向执行。这样我们就实现了一个: 透明 -> 白色 (restart) -> 透明 的过程。

    final tween

    总结

    以上我们实现了场景转化动画,学会使用了补间动画系统。"死亡" 菜单在原游戏中是有一个跳出的动画的,此处我们并没有实现,这部分交给你来实现了。下一节,将会给游戏添加音效,没有音效怎么好意思叫视频游戏呢!

    代码我已经传到 GitHub - ntop001/flappybird,请关注 ch4 分支。

    相关文章

      网友评论

        本文标题:用Go和Korok写一个Flappybird游戏-4

        本文链接:https://www.haomeiwen.com/subject/ckgqeftx.html