用Unity制作愤怒的小鸟_菜鸟篇

一、游戏概述

  这款游戏算是小时候的童年了,比较适合我这种新手练手,刚开始也是不知道用什么组件和逻辑去制作,后面也是看视频学的。

游戏结构:UI页面(加载画面和关卡选择)和游戏场景

二、加载页面(场景Loading)

加载页面很简单就两张图,而外摄像机或者Canvas里加个脚本,用来设置分辨率( Screen.SetResolution)和延迟调用Invoke()来实现跳转场景到地图选择。

三、地图选择和关卡选择(场景01_level)

地图选择

 地图和关卡选择核心代码(绑定到每个地图)

public class MapSelect : MonoBehaviour {

    public int starsNum = 0;       //星星数
    public bool isSelect = false;  //是否被选中

    public GameObject locks;  //锁   
    public GameObject stars;  //星星

    public GameObject panel;  //关卡
    public GameObject map;   //地图
    public Text starsText;     //星星数量

    public int startNum = 1;  
    public int endNum = 3;


    private void Start()
    {
        //PlayerPrefs.DeleteAll();

        if (PlayerPrefs.GetInt("totalNum", 0) >= starsNum)  //判断本地是否有存totalNum,有则返回对应数,无则返回0(以此来判断星星数)
        {    
            Debug.Log(PlayerPrefs.GetInt("totalNum"));
            isSelect = true;  //设置有就说明被选中

        }

        if (isSelect) 
        {
            locks.SetActive(false);  //关锁
            stars.SetActive(true);   //打开星星

            //TODo:text显示
            int counts = 0;      //目前得星星数
            for (int i = startNum; i <= endNum; i++) 
            {
                counts += PlayerPrefs.GetInt("level"+i.ToString(),0);   //星星数求和
            }
            starsText.text = counts.ToString() + "/60";
        }
    }
    /// <summary>
    /// 鼠标点击
    /// </summary>
    public void Selected()   //按钮事件
    {
        if (isSelect) {              
            panel.SetActive(true);  
            map.SetActive(false);
        }
    }

    public void panelSelect() {  //关卡按钮事件
        panel.SetActive(false);
        map.SetActive(true);
    }
}

主要是运用PlayerPrefs本地持久化类判断选择地图,本人主页有详细写。关卡的选择则是根据Button激活方法判断。

关卡选择

代码

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;

public class LevelSelect : MonoBehaviour {

    public bool isSelect = false;  
    public Sprite levelBG;      //每一关背景
    private Image image;

    public GameObject[] stars;

    private void Awake()
    {
        image = GetComponent<Image>();
    }


    private void Start()
    {
        if (transform.parent.GetChild(0).name == gameObject.name)
        {
            isSelect = true;
        }
        else {// 判断当前关卡是否可以选择
            int beforeNum = int.Parse(gameObject.name) - 1;
            if (PlayerPrefs.GetInt("level" + beforeNum.ToString()) > 0) {
                isSelect = true;
            }
        }
        

        if (isSelect) {
            image.overrideSprite = levelBG;       //已经被选则则加上背景
            transform.Find("num").gameObject.SetActive(true);

            int count = PlayerPrefs.GetInt("level" + gameObject.name);//获取现在关卡对应的名字,然后获得对应的星星个数
            if (count > 0) {
                for (int i = 0; i < count; i++) {
                    stars[i].SetActive(true);
                }
            }
        }
    }

    public void Selected()
    {
        if (isSelect) {
            //
            PlayerPrefs.SetString("nowLevel","level" +gameObject.name);
            SceneManager.LoadScene(2);
        }
    }

}

 脚本都挂载在每一关的对象上,也是通过PlayerPrefs.set/get获取本地的信息判断是否选择和激活对象。

 四、游戏场景(02_game)

核心组件

 Spring Joint 2D————用来挂载鸟身上,模拟弹簧效果

 

 Enable Collision—— 与这个关节连接的两个刚体是否应该相互碰撞 ?
Connected Rigid B ——连接另一个物体(这里我连得是我的右目标点)
Auchor Configure Connect——是否自动配置连接的锚以匹配世界空间中的锚?
Anchor——与本物体相连的接点的位置坐标(本物体的锚点)
Connected Anchor——连接除本物体外的另一个物体的连接点位置坐标(锚点)
Auto Configure Distance——锚点之间的距离是否自动计算?
Distance——关节点的位置, 弹簧试图保持两个物体之间的距离
Damping Ratio—— 弹簧力与移动速度成比例减少的量
Frequency—— 弹簧围绕物体之间的距离摆动的频率
Break Force—— 需要施加此关节断裂的力

Line Renderer组件————用来模拟皮带效果(一般用于3D)


Materials:线的材质。
Loop:是否要形成环。
Width:线的宽度。
Color:渐变颜色。

五、核心代码

public class Bird : MonoBehaviour {

    private bool isClick = false;
    public float maxDis = 3;
    [HideInInspector]
    public SpringJoint2D sp;
    protected Rigidbody2D rg;

    public LineRenderer right;
    public Transform rightPos;
    public LineRenderer left;
    public Transform leftPos;

    public GameObject boom;

    protected TestMyTrail myTrail;
    [HideInInspector]
    public  bool canMove = false;
    public float smooth = 3;

    public AudioClip select;
    public AudioClip fly;

    private bool isFly = false;
    [HideInInspector]
    public bool isReleased = false;//是否释放了小鸟

    public Sprite hurt;
    protected SpriteRenderer render;

    private void Awake()
    {
        sp = GetComponent<SpringJoint2D>();
        rg = GetComponent<Rigidbody2D>();
        myTrail = GetComponent<TestMyTrail>();
        render = GetComponent<SpriteRenderer>();
    }

    private void OnMouseDown()//鼠标按下
    {
        if (canMove)
        {
            AudioPlay(select);
            isClick = true;
            rg.isKinematic = true;
        }
    }


    private void OnMouseUp() //鼠标抬起
    {
        if (canMove)
        {
            isClick = false;
            rg.isKinematic = false;
            Invoke("Fly", 0.1f);
            //禁用划线组件
            right.enabled = false;
            left.enabled = false;
            canMove = false;

        }

    }


    private void Update()
    {
        if (EventSystem.current.IsPointerOverGameObject())//判断是否点击到了UI
            return;

        if (isClick) {//鼠标一直按下,进行位置的跟随
            transform.position = Camera.main.ScreenToWorldPoint(Input.mousePosition);
            //transform.position += new Vector3(0, 0, 10);
            transform.position += new Vector3(0,0,-Camera.main.transform.position.z);


            if (Vector3.Distance(transform.position, rightPos.position) > maxDis) { //进行位置限定
                Vector3 pos = (transform.position - rightPos.position).normalized;//单位化向量
                pos *= maxDis;//最大长度的向量
                transform.position = pos + rightPos.position;

            }
            Line();
        }


        //相机跟随
        float posX = transform.position.x;
        Camera.main.transform.position = Vector3.Lerp(Camera.main.transform.position,new Vector3(Mathf.Clamp(posX,0,15),Camera.main.transform.position.y, 
            Camera.main.transform.position.z),smooth*Time.deltaTime);


        if (isFly) {
            if (Input.GetMouseButtonDown(0)) {
                ShowSkill();
            }
        }
    }

    void Fly()
    {
        isReleased = true;
        isFly = true;
        AudioPlay(fly);
        myTrail.StartTrails();
        sp.enabled = false;
        Invoke("Next", 5);
    }

    /// <summary>
    /// 划线
    /// </summary>
    void Line()
    {
        right.enabled = true;
        left.enabled = true;

        right.SetPosition(0, rightPos.position);
        right.SetPosition(1, transform.position);

        left.SetPosition(0, leftPos.position);
        left.SetPosition(1, transform.position);
    }

    /// <summary>
    /// 下一只小鸟的飞出
    /// </summary>
    /// 
   protected virtual void Next()
    {
        GameManager._instance.birds.Remove(this);
        Destroy(gameObject);
        Instantiate(boom, transform.position, Quaternion.identity);
        GameManager._instance.NextBird();
    }

    private void OnCollisionEnter2D(Collision2D collision)
    {
        isFly = false;
        myTrail.ClearTrails();
    }

    public void AudioPlay(AudioClip clip) {
        AudioSource.PlayClipAtPoint(clip,transform.position);
    }


    /// <summary>
    /// 炫技操作
    /// </summary>
    public virtual void ShowSkill() {
        isFly = false;
    }

    public void Hurt() {
        
        render.sprite = hurt;
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;

public class GameManager : MonoBehaviour {

    public List<Bird> birds;
    public List<Pig> pig;
    public static GameManager _instance;
    private Vector3 originPos; //初始化的位置

    public GameObject win;
    public GameObject lose;

    public GameObject[] stars;

    private int starsNum = 0;

    private int totalNum = 9;

    private void Awake()
    {
        _instance = this;
        if(birds.Count > 0) {
            originPos = birds[0].transform.position;
        }
        
    }

    private void Start()
    {
        Initialized();
    }

    /// <summary>
    /// 初始化小鸟
    /// </summary>
    private void Initialized()
    {
        for(int i = 0; i < birds.Count; i++)
        {
            if (i == 0) //第一只小鸟
            {
                birds[i].transform.position = originPos;
                birds[i].enabled = true;
                birds[i].sp.enabled = true;
                birds[i].canMove = true;
            }
            else
            {
                birds[i].enabled = false;
                birds[i].sp.enabled = false;
                birds[i].canMove = false;
            }
        }
    }

    /// <summary>
    /// 判定游戏逻辑
    /// </summary>
   public  void NextBird()
    {
        if(pig.Count > 0)
        {
            if(birds.Count > 0)
            {
                //下一只飞吧
                Initialized();
            }
            else
            {
                //输了
                lose.SetActive(true);
            }
        }
        else
        {
            //赢了
            win.SetActive(true);
        }

    }

    public void ShowStars() {
        StartCoroutine("show");
    }

    IEnumerator show() {
        for (; starsNum < birds.Count + 1; starsNum++)
        {
            if (starsNum >= stars.Length) {
                break;
            }
            yield return new WaitForSeconds(0.2f);
            stars[starsNum].SetActive(true);
        }
        print(starsNum);
    }

    public void Replay() {
        SaveData();
        SceneManager.LoadScene(2);
    }

    public void Home() {
        SaveData();
        SceneManager.LoadScene(1);
    }

    public void SaveData() {
        if (starsNum > PlayerPrefs.GetInt(PlayerPrefs.GetString("nowLevel"))){
            PlayerPrefs.SetInt(PlayerPrefs.GetString("nowLevel"), starsNum);
        }
        //存储所有的星星个数
        int sum = 0;        
        for (int i = 1; i <= totalNum; i++) {
            sum += PlayerPrefs.GetInt("level" + i.ToString());
        }
       

        PlayerPrefs.SetInt("totalNum",sum);
        print(PlayerPrefs.GetInt("totalNum"));
    }
}


主要由GameManager控制小鸟“排队"的问题,Bird再从中获取单例的数据,决定什么时候可以移动什么时候激活刚体,什么时候可以发射等操作。

猪和建筑物的脚本主要是受伤和死亡功能,通过刚体触发完成效果。

六、总结

1.愤怒的小鸟对逻辑结构还是有考验的,通过这个案例也认识到许多组件,比如弹簧、划线等之前了解甚少的组件,也加深了我的认知。

2.在脚本方面认识 到了PlayerPrefs和EventSystem下子类的功能和用法,脚本方面也认识到需要加强的很多,不会的很多,参考了许多视频(Siki),继续努力吧。

猜你喜欢

转载自blog.csdn.net/qq_57250692/article/details/132204146
今日推荐