Unity3d——打飞碟小游戏

打飞碟小游戏

本次打飞碟小游戏,基本上是在上两周面向对象上编程实现的一个小游戏。在课程群里面,老师分享了一个优秀博客,此处引入,作为借鉴。
在本次作业中,我的脚本结构主要是参考上一次动作分离版牧师与魔鬼,保留SSDirector、SSAction与SSActionManager。在这次写代码的过程中,我发现我定义的接口太混乱,所以将所有的接口又再次提出放到了Interface中,这样比较方便debug。在本次代码中,新引入的就是Singleton,以及DiskData与DiskFactory。在此处的DiskFactory是用来管理飞碟出场的,并且是单例的;DiskData是用来对每一个飞碟加入自己的属性,即得分与大小比例,然后加入飞碟的预制中。
以下是上周动作分离设计的UML图:
这里写图片描述
在这个游戏中,稍稍改良,在CCActionManager改为FlyActionManager,下面的CCActionSequence改为了一个动作发生——UFOFlyAction。
在这位同学的博客中,我借鉴了他的爆炸的预设和skybox,好吧,是因为我实在懒得挑了,而且这个天空盒的画风真的太赞了~向这位同学学习学习~

游戏简介

在这个游戏中,一共有三个关卡,第一个关卡只有红色的飞碟,一个飞碟是1分。当得到15分后,即进入第二关,新加入黄色飞碟,击中一个得2分。当得到30分之后,即进入第三关,新加入蓝色飞碟,击中一个得3分。当错过了10个飞碟的话,游戏结束。

具体函数实现

  • 首先介绍FirstController:
    继承MonoBehavior,ISceneController,IUserAction。主要函数如下:
 void Start ()
{
//获取各个实例及各个类
    SSDirector director = SSDirector.getInstance();     
    director.currentScenceController = this;             
    diskFactory = Singleton<DiskFactory>.Instance;
    scoreRecorder = Singleton<ScoreRecorder>.Instance;
    actionManager = gameObject.AddComponent<FlyActionManager>() as FlyActionManager;
    userGUI = gameObject.AddComponent<UserGUI>() as UserGUI;

}

void Update ()
{
    if(isGameStart)
    {
        if (isGameOver)
        {
            CancelInvoke("LoadResources");
        }
        if (!isPlayGame)
        {
            InvokeRepeating("LoadResources", 1f, speed);
            isPlayGame = true;
        }
        SendDisk();
        if (scoreRecorder.score >= scoreRound2 && round == 1)
        {
            round = 2;
            speed = speed - 0.6f;
            CancelInvoke("LoadResources");
            isPlayGame = false;
        }
        else if (scoreRecorder.score >= scoreRound3 && round == 2)
        {
            round = 3;
            speed = speed - 0.5f;
            CancelInvoke("LoadResources");
            isPlayGame = false;
        }
    }
}
//加载资源
public void LoadResources()
{
    diskQueue.Enqueue(diskFactory.GetDisk(round)); 
}
//发送飞碟
private void SendDisk()
{
    float position_x = 16;                       
    if (diskQueue.Count != 0)
    {
        GameObject disk = diskQueue.Dequeue();
        diskNotHit.Add(disk);
        disk.SetActive(true);
        float ran_y = Random.Range(1f, 4f);
        float ran_x = Random.Range(-1f, 1f) < 0 ? -1 : 1;
        disk.GetComponent<DiskData>().direction = new Vector3(ran_x, ran_y, 0);
        Vector3 position = new Vector3(-disk.GetComponent<DiskData>().direction.x * position_x, ran_y, 0);
        disk.transform.position = position;
        float power = Random.Range(10f, 15f);
        float angle = Random.Range(15f, 28f);
        actionManager.UFOFly(disk,angle,power);
    }

    for (int i = 0; i < diskNotHit.Count; i++)
    {
        GameObject temp = diskNotHit[i];
        if (temp.transform.position.y < -10 && temp.gameObject.activeSelf == true)
        {
            diskFactory.FreeDisk(diskNotHit[i]);
            diskNotHit.Remove(diskNotHit[i]);
            userGUI.BloodReduce();
        }
    }
}
//击打飞碟
public void Hit(Vector3 pos)
{
    Ray ray = Camera.main.ScreenPointToRay(pos);
    RaycastHit[] hits;
    hits = Physics.RaycastAll(ray);
    bool not_hit = false;
    for (int i = 0; i < hits.Length; i++)
    {
        RaycastHit hit = hits[i];
        if (hit.collider.gameObject.GetComponent<DiskData>() != null)
        {
            for (int j = 0; j < diskNotHit.Count; j++)
            {
                if (hit.collider.gameObject.GetInstanceID() == diskNotHit[j].gameObject.GetInstanceID())
                {
                    not_hit = true;
                }
            }
            if(!not_hit)
            {
                return;
            }
            diskNotHit.Remove(hit.collider.gameObject);
            scoreRecorder.Record(hit.collider.gameObject);
            Transform explode = hit.collider.gameObject.transform.GetChild(0);
            explode.GetComponent<ParticleSystem>().Play();
            StartCoroutine(WaitingParticle(0.08f, hit, diskFactory, hit.collider.gameObject));
            break;
        }
    }
}
//统计得分
public int GetScore()
{
    return scoreRecorder.score;
}
//重新开始
public void Restart()
{
    isGameOver = false;
    isPlayGame = false;
    scoreRecorder.score = 0;
    round = 1;
    speed = 2f;
}
//游戏结束
public void GameOver()
{
    isGameOver = true;
}

IEnumerator WaitingParticle(float wait_time, RaycastHit hit, DiskFactory disk_factory, GameObject obj)
{
     yield return new WaitForSeconds(wait_time); 
     hit.collider.gameObject.transform.position = new Vector3(0, -9, 0);
     disk_factory.FreeDisk(obj);
}

public void BeginGame()
{
    isGameStart = true;
}
  • ScoreRecord:
    这个脚本主要是实现计分功能,其代码实现很简单:
public class ScoreRecorder : MonoBehaviour {
    public int score;
    // Use this for initialization
    void Start () {
        score = 0;
    }

    public void Record(GameObject disk)
    {
        int temp = disk.GetComponent<DiskData>().score;
        score += temp;
    }

    public void Reset()
    {
        score = 0;
    }
}
  • 飞碟自身的函数和飞碟工厂的脚本实现如下:
public class DiskData : MonoBehaviour {

    public int score = 1;
    public Vector3 direction;
    public Vector3 scale = new Vector3(1, 1, 1);
}
public class DiskFactory : MonoBehaviour {
    public GameObject diskPrefab = null;
    private List<DiskData> used = new List<DiskData>();
    private List<DiskData> free = new List<DiskData>();

    public GameObject GetDisk(int round)
    {
        int choice = 0;
        int scope = 1, scope1 = 4, scope2 = 7;
        float startY = -10f;
        string tag;
        diskPrefab = null;

        if(round == 1)
            choice = Random.Range(0, scope);
        else if(round == 2)
            choice = Random.Range(0, scope1);
        else if(round == 3)
            choice = Random.Range(0, scope2);

        if(choice <= scope)
            tag = "disk1";
        else if(choice <= scope && choice > scope1)
            tag = "disk2";
        else
            tag = "disk3";

        for(int i = 0; i < free.Count; i++)
        {
            if(free[i].tag == tag)
            {
                diskPrefab = free[i].gameObject;
                free.Remove(free[i]);
                break;
            }
        }
        if(diskPrefab == null)
        {
            if (tag == "disk1")
                diskPrefab = Instantiate(Resources.Load<GameObject>("Prefabs/disk1"), new Vector3(0, startY, 0), Quaternion.identity);
            else if(tag == "disk2")
                diskPrefab = Instantiate(Resources.Load<GameObject>("Prefabs/disk2"), new Vector3(0, startY, 0), Quaternion.identity);
            else if(tag == "disk3")
                diskPrefab = Instantiate(Resources.Load<GameObject>("Prefabs/disk3"), new Vector3(0, startY, 0), Quaternion.identity);

            float ranX = Random.Range(-1f, -1f) < 0 ? -1 : 1;
            diskPrefab.GetComponent<DiskData>().direction = new Vector3(ranX, startY, 0);
            diskPrefab.transform.localScale = diskPrefab.GetComponent<DiskData>().scale;
        }
        used.Add(diskPrefab.GetComponent<DiskData>());
        return diskPrefab;
    }

    public void FreeDisk(GameObject disk)
    {
        for (int i = 0; i < used.Count; i++)
        {
            if (disk.GetInstanceID() == used[i].gameObject.GetInstanceID())
            {
                used[i].gameObject.SetActive(false);
                free.Add(used[i]);
                used.Remove(used[i]);
                break;
            }
        }
    }

}
  • Singleton:
    这个脚本我是按照老师课件上来写的,用来实现飞碟工厂的单例:
public class Singleton<T> : MonoBehaviour where T: MonoBehaviour {

    protected static T instance;

    public static T Instance
    {
        get
        {
            if(instance == null)
            {
                instance = (T)FindObjectOfType(typeof(T));
                if(instance == null)
                {
                    Debug.LogError("An instance of " + typeof(T) + " is needed in the scene, but there is none.");
                }
            }
            return instance;
        }
    }
}
  • 接下来还有一个实现界面的UserGUI:
void OnGUI ()
    {
        boldStyle.normal.textColor = new Color(1, 0, 0);
        boldStyle.fontSize = 20;
        textStyle.normal.textColor = new Color(0,0,0, 1);
        textStyle.fontSize = 20;
        scoreStyle.normal.textColor = new Color(1,0,1,1);
        scoreStyle.fontSize = 20;
        overStyle.normal.textColor = new Color(1, 0, 0);
        overStyle.fontSize = 25;

        if (gameStart)
        {
            //用户射击
            if (Input.GetButtonDown("Fire1"))
            {
                Vector3 pos = Input.mousePosition;
                action.Hit(pos);
            }

            GUI.Label(new Rect(10, 5, 200, 50), "Score:", textStyle);
            GUI.Label(new Rect(55, 5, 200, 50), action.GetScore().ToString(), scoreStyle);

            GUI.Label(new Rect(Screen.width - 120, 5, 50, 50), "Life:", textStyle);
            //显示当前血量
            for (int i = 0; i < life; i++)
            {
                GUI.Label(new Rect(Screen.width - 75 + 10 * i, 5, 50, 50), "X", boldStyle);
            }
            //游戏结束
            if (life == 0)
            {
                score = action.GetScore();
                GUI.Label(new Rect(Screen.width / 2 - 20, Screen.width / 2 - 250, 100, 100), "Game Over", overStyle);
                GUI.Label(new Rect(Screen.width / 2 - 10, Screen.width / 2 - 200, 50, 50), "Score:", textStyle);
                GUI.Label(new Rect(Screen.width / 2 + 50, Screen.width / 2 - 200, 50, 50), score.ToString(), textStyle);
                if (GUI.Button(new Rect(Screen.width / 2 - 20, Screen.width / 2 - 150, 100, 50), "Restart"))
                {
                    life = 10;
                    action.Restart();
                    return;
                }
                action.GameOver();
            }
        }
        else
        {
            GUI.Label(new Rect(Screen.width / 2 - 30, Screen.width / 2 - 350, 100, 100), "Hit UFO!", overStyle);
            if (GUI.Button(new Rect(Screen.width / 2 - 25, Screen.width / 2 - 250, 100, 50), "Game Start"))
            {
                gameStart = true;
                action.BeginGame();
            }
        }
    }
    public void BloodReduce()
    {
        if(life > 0)
            life--;
    }

再放出游戏界面的截图:
这里写图片描述
这里写图片描述
这里写图片描述

以上就是主要函数了,总体代码请看我的github,这是传送门

猜你喜欢

转载自blog.csdn.net/emilybluse/article/details/79981252