Unity游戏Hit UFO实现

Unity游戏Hit UFO 实现

一、游戏内容

  1. 游戏有n个round,每个round都包括10次trial
  2. 每个trail的飞碟颜色、大小、发射位置、速度、角度、同时出现的个数不同。
  3. 每个飞碟出现有随机性,总体难度随着round上升
  4. 鼠标点中飞碟得分,得分规则按照色彩、大小、速度不同进行计算

二、设计思路

  1. 将颜色、速度、得分属性赋予飞碟,不同round中的飞碟有不同颜色、速度和得分。其中需要注意的是,每个飞碟的得分根据round值、颜色、速度来计算。
  2. 参考Singleton实现UFO_factory,实现不同飞碟的产生与回收。
  3. 鼠标点击正在运动中的飞碟,该飞碟对应的分数就加到游戏的整体得分中。

三、代码实现

(1) 飞碟的实现
  1. 飞碟类记录每个飞碟的属性
public class UFO_info : MonoBehaviour
{
    public float speed;         //水平速度
    public int points;          //得分
    public Vector3 direction;   //初始方向
}
  1. UFO_factory类来实现不同飞碟的长生和回收
public class UFO_factory : MonoBehaviour
{
    public GameObject UFO;              //飞碟预制

    private List<UFO_info> used;                //正被使用的飞碟
    private List<UFO_info> free;                //空闲的飞碟

    public void Start()
    {
        used = new List<UFO_info>();
        free = new List<UFO_info>();
        UFO = GameObject.Instantiate<GameObject>(Resources.Load<GameObject>("Prefabs/UFO"), Vector3.zero, Quaternion.identity);
        UFO.SetActive(false);
    }

    public GameObject GetDisk(int round)
    {
        GameObject disk;
        //如果有空闲的飞碟,则直接使用,否则生成一个新的
        if (free.Count > 0)
        {
            disk = free[0].gameObject;
            free.Remove(free[0]);
        }
        else
        {
            disk = GameObject.Instantiate<GameObject>(UFO, Vector3.zero, Quaternion.identity);
            disk.AddComponent<UFO_info>();
        }

        //按照round来设置飞碟属性
        //飞碟的等级 = 0~2之间的随机数 * 轮次数
        //0~4:  红色飞碟  
        //4~7:  绿色飞碟  
        //7~10: 蓝色飞碟
        float level = UnityEngine.Random.Range(0, 2f) * (round + 1);
        if (level < 4)
        {
            disk.GetComponent<UFO_info>().points = 1;
            disk.GetComponent<UFO_info>().speed = 4.0f;
            disk.GetComponent<UFO_info>().direction = new Vector3(UnityEngine.Random.Range(-1f, 1f) > 0 ? 2 : -2, 1, 0);
            disk.GetComponent<Renderer>().material.color = Color.red;
        }
        else if (level > 7)
        {
            disk.GetComponent<UFO_info>().points = 3;
            disk.GetComponent<UFO_info>().speed = 8.0f;
            disk.GetComponent<UFO_info>().direction = new Vector3(UnityEngine.Random.Range(-1f, 1f) > 0 ? 2 : -2, 1, 0);
            disk.GetComponent<Renderer>().material.color = Color.blue;
        }
        else
        {
            disk.GetComponent<UFO_info>().points = 2;
            disk.GetComponent<UFO_info>().speed = 6.0f;
            disk.GetComponent<UFO_info>().direction = new Vector3(UnityEngine.Random.Range(-1f, 1f) > 0 ? 2 : -2, 1, 0);
            disk.GetComponent<Renderer>().material.color = Color.green;
        }

        used.Add(disk.GetComponent<UFO_info>());

        return disk;
    }

    public void FreeDisk(GameObject disk)
    {
        //找到使用中的飞碟,将其踢出并加入到空闲队列
        foreach (UFO_info UFO_info in used)
        {
            if (UFO_info.gameObject.GetInstanceID() == disk.GetInstanceID())
            {
                disk.SetActive(false);
                free.Add(UFO_info);
                used.Remove(UFO_info);
                break;
            }

        }
    }
}
(2)游戏动作类
  1. 基础动作
public class MyBaseAction : ScriptableObject
{
    public bool enable = true;
    public bool destroy = false;

    public GameObject gameObject { get; set; }
    public Transform transform { get; set; }
    public MyActionCallback callback { get; set; }

    protected MyBaseAction()
    {

    }

    // Start is called before the first frame update
    public virtual void Start()
    {
        throw new System.NotImplementedException();
    }

    // Update is called once per frame
    public virtual void Update()
    {
        throw new System.NotImplementedException();
    }
}
  1. 飞碟的飞行动作
public class RunAction :MyBaseAction
{
    float gravity;          //重力加速度
    float speed;            //水平速度
    Vector3 direction;      //飞行方向
    float time;             //时间

    //生产函数(工厂模式)
    public static RunAction GetSSAction(Vector3 direction, float speed)
    {
        RunAction action = ScriptableObject.CreateInstance<RunAction>();
        action.gravity = 9.8f;
        action.time = 0;
        action.speed = speed;
        action.direction = direction;
        return action;
    }

    public override void Start()
    {

    }

    public override void Update()
    {
        time += Time.deltaTime;
        transform.Translate(Vector3.down * gravity * time * Time.deltaTime);
        transform.Translate(direction * speed * Time.deltaTime);
        //如果飞碟到达底部,则动作结束,进行回调
        if (this.transform.position.y < -6)
        {
            this.destroy = true;
            this.enable = false;
            this.callback.BaseActionEvent(this);
        }

    }
}
(3) 动作管理类
  1. 基础动作管理
public class BaseActionManager : MonoBehaviour
{
    //动作集,以字典形式存在
    private Dictionary<int, MyBaseAction> actions = new Dictionary<int, MyBaseAction>();
    //等待被加入的动作队列(动作即将开始)
    private List<MyBaseAction> waitingAdd = new List<MyBaseAction>();
    //等待被删除的动作队列(动作已完成)
    private List<int> waitingDelete = new List<int>();

    protected void Update()
    {
        //将waitingAdd中的动作保存
        foreach (MyBaseAction ac in waitingAdd)
            actions[ac.GetInstanceID()] = ac;
        waitingAdd.Clear();

        //运行被保存的事件
        foreach (KeyValuePair<int, MyBaseAction> kv in actions)
        {
            MyBaseAction ac = kv.Value;
            if (ac.destroy)
            {
                waitingDelete.Add(ac.GetInstanceID());
            }
            else if (ac.enable)
            {
                ac.Update();
            }
        }

        //销毁waitingDelete中的动作
        foreach (int key in waitingDelete)
        {
            MyBaseAction ac = actions[key];
            actions.Remove(key);
            Destroy(ac);
        }
        waitingDelete.Clear();
    }

    //准备运行一个动作,将动作初始化,并加入到waitingAdd
    public void Run(GameObject gameObject, MyBaseAction action,MyActionCallback manager)
    {
        action.gameObject = gameObject;
        action.transform = gameObject.transform;
        action.callback = manager;
        waitingAdd.Add(action);
        action.Start();
    }

    // Start is called before the first frame update
    protected void Start()
    {

    }

}
  1. 飞行动作管理
public class RunActionManager : BaseActionManager, MyActionCallback
{
    //飞行动作
    public RunAction flyAction;
    //控制器
    public FirstController controller;

    protected new void Start()
    {
        controller = (FirstController)MyDirector.GetInstance().CurrentScenceController;
        controller.actionManager = this;
    }

    public void Fly(GameObject ufo, float speed, Vector3 direction)
    {
        flyAction = RunAction.GetSSAction(direction, speed);
        Run(ufo, flyAction, this);
    }

    //回调函数
    public void BaseActionEvent(MyBaseAction source,
    BaseActionEventType events = BaseActionEventType.Competed,
    int intParam = 0,
    string strParam = null,
    Object objectParam = null)
    {
        //飞碟结束飞行后进行回收
        controller.ufoFactory.FreeDisk(source.gameObject);
    }
}
(4) 接口类
  1. 用户动作接口
public interface IUserAction
{
    void Hit(Vector3 position);
    void Restart();
    void SetMode(bool isInfinite);
}
  1. 场景控制类接口
public interface ISceneController
{
    void LoadResources();
}
(5)FirstController
public class FirstController : MonoBehaviour, ISceneController, IUserAction
{
    public RunActionManager actionManager;                   //动作管理者
    public UFO_factory ufoFactory;                         //飞碟工厂
    int[] roundDisks;           //对应轮次的飞碟数量
    bool isInfinite;            //游戏当前模式
    int points;                 //游戏当前分数
    int round;                  //游戏当前轮次
    int sendCnt;                //当前已发送的飞碟数量
    float sendTime;             //发送时间

    void Start()
    {
        LoadResources();
    }

    public void LoadResources()
    {
        MyDirector.GetInstance().CurrentScenceController = this;
        gameObject.AddComponent<UFO_factory>();
        gameObject.AddComponent<RunActionManager>();
        gameObject.AddComponent<UserGUI>();
        ufoFactory = once<UFO_factory>.Instance;
        sendCnt = 0;
        round = 0;
        sendTime = 0;
        points = 0;
        isInfinite = false;
        roundDisks = new int[] { 3, 5, 8, 13, 21 };
    }

    public void SendDisk()
    {
        //从工厂生成一个飞碟
        GameObject disk = ufoFactory.GetDisk(round);
        //设置飞碟的随机位置
        disk.transform.position = new Vector3(-disk.GetComponent<UFO_info>().direction.x * 7, UnityEngine.Random.Range(0f, 8f), 0);
        disk.SetActive(true);
        //设置飞碟的飞行动作
        actionManager.Fly(disk, disk.GetComponent<UFO_info>().speed, disk.GetComponent<UFO_info>().direction);
    }

    public void Hit(Vector3 position)
    {
        Camera ca = Camera.main;
        Ray ray = ca.ScreenPointToRay(position);

        RaycastHit[] hits;
        hits = Physics.RaycastAll(ray);

        for (int i = 0; i < hits.Length; i++)
        {
            RaycastHit hit = hits[i];
            if (hit.collider.gameObject.GetComponent<UFO_info>() != null)
            {
                //将飞碟移至底端,触发飞行动作的回调
                hit.collider.gameObject.transform.position = new Vector3(0, -7, 0);
                //积分
                points += hit.collider.gameObject.GetComponent<UFO_info>().points;
                //更新GUI数据
                gameObject.GetComponent<UserGUI>().points = points;
            }
        }
    }

    public void Restart()
    {
        gameObject.GetComponent<UserGUI>().gameMessage = "";
        round = 0;
        sendCnt = 0;
        points = 0;
        gameObject.GetComponent<UserGUI>().points = points;
    }

    public void SetMode(bool isInfinite)
    {
        this.isInfinite = isInfinite;
    }

    void Update()
    {
        sendTime += Time.deltaTime;
        //每隔1s发送一次飞碟
        if (sendTime > 1)
        {
            sendTime = 0;
            //每次发送至多5个飞碟
            for (int i = 0; i < 5 && sendCnt < roundDisks[round]; i++)
            {
                sendCnt++;
                SendDisk();
            }
            //判断是否需要重置轮次,不需要则输出游戏结束
            if (sendCnt == roundDisks[round] && round == roundDisks.Length - 1)
            {
                if (isInfinite)
                {
                    round = 0;
                    sendCnt = 0;
                    gameObject.GetComponent<UserGUI>().gameMessage = "";
                }
                else
                {
                    gameObject.GetComponent<UserGUI>().gameMessage = "Game Over!";
                }
            }
            //更新轮次
            if (sendCnt == roundDisks[round] && round < roundDisks.Length - 1)
            {
                
                sendCnt = 0;
                round++;
                UserGUI.myrounds = round+1;
            }
        }
    }
}
(6) 导演类
public class MyDirector : System.Object
{
    private static MyDirector _instance;
    public ISceneController CurrentScenceController { get; set; }
    public static MyDirector GetInstance()
    {
        if (_instance == null)
        {
            _instance = new MyDirector();
        }
        return _instance;
    }
}

(7) 用户GUI
public class UserGUI : MonoBehaviour
{
    private IUserAction userAction;
    public string gameMessage;
    public int points;
    static public int myrounds;
    void Start()
    {
        points = 0;
        gameMessage = "";
        userAction = MyDirector.GetInstance().CurrentScenceController as IUserAction;
    }

    void OnGUI()
    {
        //小字体初始化
        GUIStyle style = new GUIStyle();
        style.normal.textColor = Color.white;
        style.fontSize = 30;

        //大字体初始化
        GUIStyle bigStyle = new GUIStyle();
        bigStyle.normal.textColor = Color.red;
        bigStyle.fontSize = 50;

        GUI.Label(new Rect(400, 30, 50, 200), "Hit UFO", bigStyle);
        GUI.Label(new Rect(20, 0, 100, 50), "Points: " + points, style);
        GUI.Label(new Rect(200, 0, 300, 50), "Rounds: " + myrounds, style);
        GUI.Label(new Rect(400, 100, 50, 200), gameMessage, style);
        if (GUI.Button(new Rect(20, 50, 100, 40), "Restart"))
        {
            userAction.Restart();
        }
        if (GUI.Button(new Rect(20, 100, 100, 40), "Normal Mode"))
        {
            userAction.SetMode(false);
        }
        if (GUI.Button(new Rect(20, 150, 100, 40), "Infinite Mode"))
        {
            userAction.SetMode(true);
        }
        if (Input.GetButtonDown("Fire1"))
        {
            userAction.Hit(Input.mousePosition);
        }
    }
}

四、效果展示

在这里插入图片描述
在这里插入图片描述
bilibil链接

github仓库地址

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值