开源移动小游戏项目《FlappyBird》学习心得

Posted rickcode

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了开源移动小游戏项目《FlappyBird》学习心得相关的知识,希望对你有一定的参考价值。

技术图片

游戏设计思路:

场景关卡:

  • 地图中会随机生成一个又一个的管道,每一个管道由上下管道组成,中间的空隙是固定的,每条管道的生成时间是随机的,所以它们之间的间距不一样
    技术图片

玩法:

  • 点击屏幕,小鸟便会被施加一个向上的速度,该速度随重力会逐渐减少。
  • 每当小鸟越过一个管道时,得分加1,当小鸟碰到地图上的管道或者掉入地面时,游戏结束。
  • 游戏中的得分没有上限
    技术图片

技术实现:

GameState:

用脚本写一个枚举类GmaeState,表明游戏的三个场景状态

public enum GameState
{
    Intro,
    Playing,
    Dead
}

public static class GameStateManager
{
    public static GameState GameState { get; set; }

    static GameStateManager ()
    {
        GameState = GameState.Intro;
    }
}

摄像机和场景:

摄像机作为父物体,他的子物体如下:
技术图片
分别是:background背景,Spawnerobject管道物体,Floor地板,Pipedestroy管道销毁模板,ceiling天花板,IntroGUI开始界面UI,Score得分计数,DeathGUI死亡时UI图标

摄像机:

非常简单,只需要每帧向右移动0.5即可,

void Start () {
        cameraZ = transform.position.z;
	}

    float cameraZ;

	void Update () {
        transform.position = new Vector3(Player.position.x + 0.5f, 0, cameraZ);
       
	}

    public Transform Player;
}

Spawnerobject管道物体:

  • 定义了一个数组:里面有两个管道prefab,当游戏开始或结束重新开始时,会随机选择一个管道:
    技术图片

  • 管道物件是固定好的,每次生成只需调整它的y轴即可,y轴数值随机生成

  • 每隔一段随机的时间,在小鸟前面生成管道

void Start()
    {
        SpawnObject = SpawnObjects[Random.Range(0, SpawnObjects.Length)];
        Spawn();
    }

    void Spawn()
    {
        if (GameStateManager.GameState == GameState.Playing)
        {
            //random y position
            float y = Random.Range(-0.5f, 1f);
            GameObject go = Instantiate(SpawnObject, this.transform.position + new Vector3(0, y, 0), Quaternion.identity) as GameObject;
        }
        Invoke("Spawn", Random.Range(timeMin, timeMax));
    }

    private GameObject SpawnObject;
    public GameObject[] SpawnObjects;

    public float timeMin = 0.7f;
    public float timeMax = 2f;
}

Floor地板:

游戏开始时,小鸟和相机绑定在一起,不断先前移动,为了实现一个小鸟一直往前飞的效果,此时地板只需不动就好
但地板的长度有限,所以要让它不断的向前刷新位置。

    void Update()
    {
        if (transform.localPosition.x < -3.9f)
        {
            transform.localPosition = new Vector3(0, transform.localPosition.y, transform.localPosition.z);
        }
        transform.Translate(-Time.deltaTime, 0, 0);
    }
}

Pipedestroy管道销毁模板:

每当管道触碰到它时,就销毁

ceiling天花板:

小鸟的飞行高度上限,防止飞出屏幕外

IntroGUI开始界面UI:

几张spite的简单UI

DeathGUI死亡时UI图标:

同上;

Score得分计数:

使用Sprite[] numberSprites数组来根据得分情况调用相应的spite图片;

	void Start () {
        (Tens.gameObject as GameObject).SetActive(false);
        (Hundreds.gameObject as GameObject).SetActive(false);
	}
	
	// Update is called once per frame
	void Update () {

        if (previousScore != Score) //save perf from non needed calculations
        { 
            if(Score < 10)
            {
                //just draw units
                Units.sprite = numberSprites[Score];
            }
            else if(Score >= 10 && Score < 100)
            {
                (Tens.gameObject as GameObject).SetActive(true);
                Tens.sprite = numberSprites[Score / 10];
                Units.sprite = numberSprites[Score % 10];
            }
            else if(Score >= 100)
            {
                (Hundreds.gameObject as GameObject).SetActive(true);
                Hundreds.sprite = numberSprites[Score / 100];
                int rest = Score % 100;
                Tens.sprite = numberSprites[rest / 10];
                Units.sprite = numberSprites[rest % 10];
            }
        }

	}


    int previousScore = -1;
    public Sprite[] numberSprites;
    public SpriteRenderer Units, Tens, Hundreds;
}

主角小鸟:

  • 屏幕点击,开始游戏。游戏中点击屏幕,小鸟向上飞跃,并触发音效
  • 小鸟越过管道,加分并触发加分音效
  • 小鸟碰到地板和管道,触发死亡和死亡音效
  • 小鸟恒定移动速度、向上飞跃速度、坠落速度的设置,飞跃时的角度修正
 public class FlappyScript : MonoBehaviour
{

    public AudioClip FlyAudioClip, DeathAudioClip, ScoredAudioClip;
    public Sprite GetReadySprite;
    public float RotateUpSpeed = 1, RotateDownSpeed = 1;
    public GameObject IntroGUI, DeathGUI;
    public Collider2D restartButtonGameCollider;
    public float VelocityPerJump = 3;
    public float XSpeed = 1;

    // Use this for initialization
    void Start()
    {

    }

    FlappyYAxisTravelState flappyYAxisTravelState;

    enum FlappyYAxisTravelState
    {
        GoingUp, GoingDown
    }

    Vector3 birdRotation = Vector3.zero;
    // Update is called once per frame
    void Update()                                              //每帧侦测屏幕的点击状态
    {
        //handle back key in Windows Phone
        if (Input.GetKeyDown(KeyCode.Escape))
            Application.Quit();

        if (GameStateManager.GameState == GameState.Intro)
        {
            MoveBirdOnXAxis();
            if (WasTouchedOrClicked())
            {
                BoostOnYAxis();
                GameStateManager.GameState = GameState.Playing;
                IntroGUI.SetActive(false);
                ScoreManagerScript.Score = 0;
            }
        }

        else if (GameStateManager.GameState == GameState.Playing)
        {
            MoveBirdOnXAxis();
            if (WasTouchedOrClicked())
            {
                BoostOnYAxis();
            }

        }

        else if (GameStateManager.GameState == GameState.Dead)     
        {
            Vector2 contactPoint = Vector2.zero;

            if (Input.touchCount > 0)
                contactPoint = Input.touches[0].position;
            if (Input.GetMouseButtonDown(0))
                contactPoint = Input.mousePosition;

            //check if user wants to restart the game
            if (restartButtonGameCollider == Physics2D.OverlapPoint
                (Camera.main.ScreenToWorldPoint(contactPoint)))
            {
                GameStateManager.GameState = GameState.Intro;
                Application.LoadLevel(Application.loadedLevelName);
            }
        }

    }


    void FixedUpdate()
    {
        //just jump up and down on intro screen
        if (GameStateManager.GameState == GameState.Intro)
        {
            if (GetComponent<Rigidbody2D>().velocity.y < -1) //when the speed drops, give a boost
                GetComponent<Rigidbody2D>().AddForce(new Vector2(0, GetComponent<Rigidbody2D>().mass * 5500 * Time.deltaTime));  //调整开始界面小鸟的飞行状态
                                                        
        }
        else if (GameStateManager.GameState == GameState.Playing || GameStateManager.GameState == GameState.Dead) 
        {
            FixFlappyRotation();     // 角度修正
        }
    }

    bool WasTouchedOrClicked()
    {
        if (Input.GetButtonUp("Jump") || Input.GetMouseButtonDown(0) || 
            (Input.touchCount > 0 && Input.touches[0].phase == TouchPhase.Ended))
            return true;
        else
            return false;
    }

    void MoveBirdOnXAxis()
    {
        transform.position += new Vector3(Time.deltaTime * XSpeed, 0, 0);
    }

    void BoostOnYAxis()
    {
        GetComponent<Rigidbody2D>().velocity = new Vector2(0, VelocityPerJump);
        GetComponent<Audiosource>().PlayOneShot(FlyAudioClip);
    }



    /// <summary>
    /// when the flappy goes up, it‘ll rotate up to 45 degrees. when it falls, rotation will be -90 degrees min
    /// </summary>
    private void FixFlappyRotation()
    {
        if (GetComponent<Rigidbody2D>().velocity.y > 0) flappyYAxisTravelState = FlappyYAxisTravelState.GoingUp;
        else flappyYAxisTravelState = FlappyYAxisTravelState.GoingDown;

        float degreesToAdd = 0;

        switch (flappyYAxisTravelState)                                 //根据小鸟的状态,来选择相应的角度计算数值
        {
            case FlappyYAxisTravelState.GoingUp:                         
                degreesToAdd = 6 * RotateUpSpeed;
                break;
            case FlappyYAxisTravelState.GoingDown:
                degreesToAdd = -3 * RotateDownSpeed;
                break;
            default:
                break;
        }
        //solution with negative eulerAngles found here: http://answers.unity3d.com/questions/445191/negative-eular-angles.html

        //clamp the values so that -90<rotation<45 *always*
        birdRotation = new Vector3(0, 0, Mathf.Clamp(birdRotation.z + degreesToAdd, -90, 45));         //生成新的方向
        transform.eulerAngles = birdRotation;                                                          //根据方向生成新的角度
    }

    /// <summary>
    /// check for collision with pipes
    /// </summary>
    /// <param name="col"></param>
    void OnTriggerEnter2D(Collider2D col)                                           
    {
        if (GameStateManager.GameState == GameState.Playing)                 //如果游戏开始
        {
            if (col.gameObject.tag == "Pipeblank") //pipeblank is an empty gameobject with a collider between the two pipes  如果
            {
                GetComponent<AudioSource>().PlayOneShot(ScoredAudioClip);
                ScoreManagerScript.Score++;
            }
            else if (col.gameObject.tag == "Pipe")   //col的
            {
                FlappyDies();
            }
        }
    }

    void OnCollisionEnter2D(Collision2D col)          
    {
        if (GameStateManager.GameState == GameState.Playing)
        {
            if (col.gameObject.tag == "Floor")      //小鸟触发到了地板
            {
                FlappyDies();
            }
        }
    }

    void FlappyDies()                  //小鸟死亡
    {
        GameStateManager.GameState = GameState.Dead;
        DeathGUI.SetActive(true);                       //使用死亡UI
        GetComponent<AudioSource>().PlayOneShot(DeathAudioClip);   //使用死亡音效
    }

}






以上是关于开源移动小游戏项目《FlappyBird》学习心得的主要内容,如果未能解决你的问题,请参考以下文章

[游戏开发-学习笔记]菜鸟慢慢飞-官方教程学习小心得

[Canvas前端游戏开发]——FlappyBird详解

#跟着小白一起学鸿蒙# [番外]一起学做FlappyBird

强化学习DQN:Flappy Bird实例分析

原生JS实现FlappyBird游戏 超详细解析 快来做一个自己玩吧

一个简单用原生js实现的小游戏----FlappyBird