Unity实现打飞碟小游戏

        使用圆柱体作为飞碟的模型,用不同的颜色区分不同分值和大小的飞碟,从屏幕边缘(相机选择正交投影)抛出飞碟,用鼠标点击进行射击。

        实现上,通过为飞碟添加刚体组件,并勾选重力效果,就可以使运动轨迹为抛物线。抛出飞碟时,在下半屏幕的边缘(左边缘下半,右边缘下边,下边缘)选取随机点作为抛出点,以上半屏幕的中央区域的随机点为目标,并按照设定的速度抛出,就可以使得飞碟具有随机的运动轨迹,并且轨迹大体上位于屏幕内。

        程序的UML图如下

        为了方便游戏的配置,使用ScriptableObject对象,记录游戏规则的参数,定义如下。

using System;
using UnityEngine;

[CreateAssetMenu(fileName = "RuleAttributes", menuName = "(ScriptableObject)RuleAttributes")]
public class GameruleAttribute : ScriptableObject
{
    [Tooltip("回合数")]
    public int trailNum;
    [Tooltip("每n回合增加一次飞碟数量")]
    public int increaseRound;
    [Tooltip("每次增加的飞碟数量")]
    public int increaseNum;
    [Tooltip("初始飞碟数量")]
    public int initNum;
    [Tooltip("3分飞碟阈值")]
    public int Rate3;
    [Tooltip("2分飞碟阈值")]
    public int Rate2;
    [Tooltip("1分飞碟阈值")]
    public int Rate1;
    [Tooltip("3分飞碟速度")]
    public int Speed3;
    [Tooltip("2分飞碟速度")]
    public int Speed2;
    [Tooltip("1分飞碟速度")]
    public int Speed1;
}

        因为需要重复10轮,每轮抛出数个飞盘,如果使用对象池管理飞盘对象,则可以减少创建和销毁游戏对象的开销。对象池实现如下。使用三个List存储三类飞碟的对象,每当需要激活并使用飞盘时,就从中寻找休眠的飞盘。如果没有空闲的飞盘,则创建一个并加入List。当飞盘被击中或者离开游戏区域时,将其设置为休眠状态,而不是销毁。飞碟的分值设计为1、2、3三种,分别制作为预制件:蓝色扁平圆柱、黄色扁平圆柱和红色扁平圆柱,半径依次减小。

using UnityEngine;
using System.Collections.Generic;
using UnityEngine.Pool;

public class ObjectPoolManager
{
    public static ObjectPoolManager Instance { get; private set; }
    public int initialPoolSize1 = 10; // 初始对象池大小
    public int initialPoolSize2 = 10; // 初始对象池大小
    public int initialPoolSize3 = 10; // 初始对象池大小

    private List<GameObject> objectPool1; // 对象池
    private List<GameObject> objectPool2; // 对象池
    private List<GameObject> objectPool3; // 对象池

    public static ObjectPoolManager getInstance()//提供单例的访问
    {
        if (Instance == null)
        {
            Instance = new ObjectPoolManager();
        }
        return Instance;
    }

    public ObjectPoolManager()
    {
        objectPool1 = new List<GameObject>();
        objectPool2 = new List<GameObject>();
        objectPool3 = new List<GameObject>();
        for (int i = 0; i < initialPoolSize1; i++)
        {
            GameObject obj = UnityEngine.Object.Instantiate<GameObject>(Resources.Load<GameObject>("Prefabs/dish1"));
            obj.SetActive(false);
            obj.name = "score1";
            obj.GetComponent<Rigidbody>().position =new Vector3(-10000, 0, 0);
            objectPool1.Add(obj);
        }
        for (int i = 0; i < initialPoolSize2; i++)
        {
            GameObject obj = UnityEngine.Object.Instantiate<GameObject>(Resources.Load<GameObject>("Prefabs/dish2"));
            obj.SetActive(false);
            obj.name = "score2";
            obj.GetComponent<Rigidbody>().position = new Vector3(-10000, 0, 0);
            objectPool2.Add(obj);
        }
        for (int i = 0; i < initialPoolSize3; i++)
        {
            GameObject obj = UnityEngine.Object.Instantiate<GameObject>(Resources.Load<GameObject>("Prefabs/dish3"));
            obj.SetActive(false);
            obj.name = "score3";
            obj.GetComponent<Rigidbody>().position = new Vector3(-10000, 0, 0);
            objectPool3.Add(obj);
        }
    }

    public GameObject GetObjectFromPool1()
    {
        foreach (GameObject obj in objectPool1)
        {
            if (!obj.activeInHierarchy)
            {
                obj.SetActive(true);
                return obj;
            }
        }
        // 如果没有可用对象,则动态创建一个新对象
        GameObject newObj = UnityEngine.Object.Instantiate<GameObject>(Resources.Load<GameObject>("Prefabs/dish1"));
        newObj.name = "score1";
        newObj.GetComponent<Rigidbody>().position = new Vector3(-10000, 0, 0);
        objectPool1.Add(newObj);
        return newObj;
    }

    public GameObject GetObjectFromPool2()
    {
        foreach (GameObject obj in objectPool2)
        {
            if (!obj.activeInHierarchy)
            {
                obj.SetActive(true);
                return obj;
            }
        }
        // 如果没有可用对象,则动态创建一个新对象
        GameObject newObj = UnityEngine.Object.Instantiate<GameObject>(Resources.Load<GameObject>("Prefabs/dish2"));
        newObj.name = "score2";
        newObj.GetComponent<Rigidbody>().position = new Vector3(-10000, 0, 0);
        objectPool2.Add(newObj);
        return newObj;
    }

    public GameObject GetObjectFromPool3()
    {
        foreach (GameObject obj in objectPool3)
        {
            if (!obj.activeInHierarchy)
            {
                obj.SetActive(true);
                return obj;
            }
        }
        // 如果没有可用对象,则动态创建一个新对象
        GameObject newObj = UnityEngine.Object.Instantiate<GameObject>(Resources.Load<GameObject>("Prefabs/dish3"));
        newObj.name = "score3";
        newObj.GetComponent<Rigidbody>().position = new Vector3(-10000, 0, 0);
        objectPool3.Add(newObj);
        return newObj;
    }

    public void ReturnObjectToPool(GameObject obj)
    {
        obj.SetActive(false);
    }
}

        抛出飞碟的代码如下,其中rule是上面的GameruleAttribute的实例。在需要抛出飞盘时,通过生成随机数,按照阈值参数选取要抛出的实体类型,从对象池中拿取并激活,然后选取随机初始位置和目标位置进行发射。

public void throwDish()
{
    int r = UnityEngine.Random.Range(1, 101);
    GameObject dish;
    int speed;
    if (r >= rule.Rate3)
    {
        dish = pool.GetObjectFromPool3();
        speed = rule.Speed3;
    }else if(r>= rule.Rate2)
    {
        dish = pool.GetObjectFromPool2();
        speed = rule.Speed2;
    }
    else
    {
        dish = pool.GetObjectFromPool1();
        speed = rule.Speed1;
    }
    r = UnityEngine.Random.Range(1,4);
    float weight= UnityEngine.Random.Range(0f, 1f);
    Vector3 p1 = new Vector3(0, 0, -680), p2 = new Vector3(0, -300, -680), p3 = new Vector3(0, -300, 680), p4 = new Vector3(0, 0, 680);
    switch (r)
    {
        case 1:
            dish.GetComponent<Rigidbody>().position = weight * p1 + (1 - weight) * p2;
            break;
        case 2:
            dish.GetComponent<Rigidbody>().position = weight * p2 + (1 - weight) * p3;
            break;
        case 3:
            dish.GetComponent<Rigidbody>().position = weight * p3 + (1 - weight) * p4;
            break;
    }
    float targetz= UnityEngine.Random.Range(-150,150);
    float targety = UnityEngine.Random.Range(100, 300);
    Vector3 target = new Vector3(0,targety,targetz);
    Vector3 direction = (target - dish.GetComponent<Rigidbody>().position).normalized;
    dish.GetComponent<Rigidbody>().velocity = direction * speed;
}

        为了能销毁离开游戏空间的飞碟,使用plane对象将游戏空间包围,当飞碟发生碰撞时移入休眠状态。为了避免飞碟相互碰撞导致的意外休眠,将飞碟设置为独立图层,并在项目设置中将取消勾选该图层对象之间的相互碰撞。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class DishCllision : MonoBehaviour
{
    // Start is called before the first frame update
    private void OnCollisionEnter(Collision collision)
    {
        this.gameObject.GetComponent<Rigidbody>().position = new Vector3(100000, 0, 0);

        SSDirector.getInstance().currentSceneController.RemoveCollision(this.gameObject);
    }
}

        游戏逻辑部分,通过使用场记对象管理其他对象,提供初始化场景、抛飞碟、读取用户点击对象等功能,管理对象池,并保存和维护记录游戏进度和得分的变量。通过调用裁判类提供的方法,维护得分,判断回合以及游戏是否结束。

裁判类的实现如下,该类不继承MonoBehaviour,实现为单例模式。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;


public class Referee : IReferee
{
    public FirstController controller;
    public static Referee Instance;
    public int Score{get; set;}
    public static Referee getInstance()//提供单例的访问
    {
        Debug.Log("getReferee");
        if (Instance == null)
        {
            Instance = new Referee();
        }
        return Instance;
    }

    // Update is called once per frame
    public void check()
    {
        Debug.Log(controller.activeDish);
        bool b1 = CheckRoundOver();
        bool b2 = CheckGameOver();
        if (b1 && !b2)
        {
            controller.startNextTurn();
        }
    }
    public bool CheckRoundOver()
    {
        if (controller.activeDish == 0)
        {
            controller.curtrail++;
            return true;
        }
        return false;
    }
    public bool CheckGameOver()
    {
        if (controller.rule.trailNum == controller.curtrail)
        {
            controller.GameOver();
            return true;
        }
        return false;
    }
    public void ShootDish(GameObject dish)
    {
        switch (dish.name)
        {
            case "score1":
                Score += 1;
                break;
            case "score2":
                Score += 2;
                break;
            case "score3":
                Score += 3;
                break;
        }
        dish.GetComponent<Rigidbody>().position = new Vector3(-10000, 0, 0);
        controller.pool.ReturnObjectToPool(dish);
        controller.activeDish--;
    }
    public void SetController(ISceneController c)
    {
        controller = c as FirstController;
    }
    public int getScore()
    {
        return Score;
    }
    public void Restart()
    {
        Score = 0;
    }
}

 游戏视屏:

完整代码:GitHub - xu-yongjia/3DgameDesign: Homework

猜你喜欢

转载自blog.csdn.net/weixin_66458735/article/details/134353932