当前位置:   article > 正文

Unity实现自己的简易游戏窗口管理器_unity addcomponent()

unity addcomponent()

Unity实现自己的简易游戏窗口管理器

概述:

在一个手游项目中,进入游戏之后,我们会打开很多游戏窗口(界面),比如帮助窗口(界面){新手指引界面,玩法介绍界面,技能介绍界面},模块窗口(界面){任务界面,铸造界面,宠物界面,签到界面等等},这些界面我们都可以使用窗口管理器来实现,那么今天我将从一个很简单的小例子入手,和大家一起来完成一个简易的窗口管理器,最后我会把复杂的窗口管理器的实现思路告诉大家!


功能需求:

可以打开窗口,可以关闭打开的这个窗口,最后打开的窗口永远显示在前面,而且能够实现后面窗口的事件屏蔽。


实现过程:

1.游戏开始后,层次面板将出现一个我们自定义的UIRoot(身上需要挂载UIRoot脚本,Panel必须添加),这个游戏物体下面需要有个Camera,Camer身上需要的组件是Camera和UICamera,这个游戏物体下面还需要一个WindowRoot(窗口根节点)(本来应该还有DiagLogRoot的但是这个小例子为了测试,就不必写了),其中WindowRoot这个游戏物体是个空物体,他身上无需任何脚本,它的主要功能就是用来承放 我们打开的窗口的。

2.既然叫做窗口管理器,当然是希望有需要打开窗口的地方调用这个窗口管理器啊,所以,这个窗口管理器需要做成单例:(贴一个单例模式的写法)

  1. /// <summary>
  2. /// singleton test class.
  3. /// </summary>
  4. public class SingletonTest
  5. {
  6. private static SingletonTest mInstance;
  7. public static SingletonTest GetSingletonTestInstance()
  8. {
  9. //if minstance == null.
  10. if (mInstance == null)
  11. {
  12. mInstance=new SingletonTest();
  13. }
  14. return mInstance;
  15. }
  16. }


根据游戏过程中的第一条,我们可以分析写出如下代码:

  1. using UnityEngine;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. public class WindowManager
  5. {
  6. //UIRoot根节点
  7. private GameObject CustomRoot;
  8. //摄像机
  9. private GameObject Camera;
  10. //打开的窗口根节点
  11. private GameObject WindowRoot;
  12. //存储打开过的窗口
  13. private Dictionary<string, GameObject> mLoadWindow;
  14. /// <summary>
  15. /// 单例模式
  16. /// </summary>
  17. private static WindowManager mInstance;
  18. public static WindowManager GetWindowManagerSingleton()
  19. {
  20. if (mInstance == null)
  21. {
  22. mInstance = new WindowManager();
  23. }
  24. return mInstance;
  25. }
  26. private WindowManager()
  27. {
  28. CustomRoot = new GameObject("UI Root");
  29. CustomRoot.AddComponent<UIRoot>();
  30. Camera = new GameObject("Camera");
  31. Camera.transform.SetParent(CustomRoot.transform);
  32. Camera.AddComponent<Camera>();
  33. Camera.AddComponent<UICamera>();
  34. WindowRoot = new GameObject("WindowRoot");
  35. WindowRoot.transform.SetParent(CustomRoot.transform);
  36. }
  37. }

好了,上面我们就是完成了一个很简单的窗口管理器的变量声明部分,单例模式部分,接着我们在工程中来实践一下,看看调用窗口管理器之后会出现什么样的效果



好了,到了这一步我们就算是把窗口管理器的前期工作做好了,接下来,我们之前说了,窗口管理器要实现打开窗口和关闭窗口,那么这个windowmanager中就需要一个openwindow和closewindow的函数,那我们继续完善工程,完善工程之前,我们需要建一个存储待打开窗口的文件夹,我的命名规范如下:


好了,接下来我们就要分别打开这两个窗口,test1,test2,完善我们的窗口管理器代码:

(不完善版本)

  1. public GameObject OpenWindow(string varWindowName)
  2. {
  3. GameObject tempWindow;
  4. if(string.IsNullOrEmpty(varWindowName))return null;
  5. //集合中已经在加载过的情况下
  6. if (mLoadWindow.TryGetValue(varWindowName, out tempWindow))
  7. {
  8. SetWindow(tempWindow);
  9. return tempWindow;
  10. }
  11. //没有加载过这个窗口
  12. //1.根据名字加载
  13. GameObject loadWindow = Resources.Load<GameObject>(varWindowName);
  14. //2.实例预制体中同名子级
  15. GameObject childUI = loadWindow.transform.Find(loadWindow.name).gameObject;
  16. tempWindow = GameObject.Instantiate(childUI);
  17. //3.添加进集合(防止以后重复实例)
  18. mLoadWindow.Add(varWindowName,tempWindow);
  19. //4.设置实例出来的物体
  20. SetWindow(tempWindow);
  21. //5.返回值
  22. return tempWindow;
  23. }
  24. /// <summary>
  25. /// set window
  26. /// </summary>
  27. /// <param name="varWindowGameObject"></param>
  28. public void SetWindow(GameObject varWindowGameObject)
  29. {
  30. varWindowGameObject.transform.SetParent(WindowRoot.transform);
  31. varWindowGameObject.transform.localScale=Vector3.one;
  32. }

如果之前构造函数里面没有给我们实例出来的UI Root添加Panel的话,它的效果会是这个样子( 记得自己去掉克隆后缀):


所以我们需要回到构造函数那里,添加上这条代码:

  1. //添加panel组件,不添加会有不好的效果
  2. CustomRoot.AddComponent<UIPanel>();
添加完毕之后,我们再来运行,看下效果,我们会发现,虽然问题解决了,但是并没有窗口显示在游戏视图,但是场景视图是有的,这个时候,你需要做的事情就是:

1.检查窗口的大小

2.检查摄像机的相关参数




这个时候,我们该如何解决这个问题呢?我们之前不是自己实例出来的一个摄像机吗?我们可以给那个摄像机修改一下参数,参数代码:

  1. Camera = new GameObject("Camera");
  2. Camera.transform.SetParent(CustomRoot.transform);
  3. Camera camera=Camera.AddComponent<Camera>();
  4. Camera.AddComponent<UICamera>();
  5. camera.orthographic = true;//正交视野
  6. camera.orthographicSize = 1f;
  7. camera.nearClipPlane = -0.03f;
  8. camera.farClipPlane = 200f;

图解摄像机的修改参数:


经过修改,我们最终发现窗口可以正常被打开了,效果图:


但是最后我们发现console控制台报出了警告,正规项目,所有黄色警告都是要尽量解除的,报了警告就是说明你的代码有问题,如图:


就是说panel父级物体和子级物体的层不一样,虽然不一样,但NGUI自动帮我们转了相同层,就把这个警告显示出来了。这个问题说白了就是父级和子级层的问题,层的问题相当好解决,之前有篇文章是通过位运算来修改layer层的,那么我们在这个小例子里面不需要使用位运算。

        //varWindowGameObject.layer = LayerMask.NameToLayer("Default");

还有一种方式就是我们项目中常用的方式,在我们创建窗口预制时候,就直接修改UIRoot(父级),父级一修改,所有子级都会改变!

通过上面的两种方式之中的任选其一,我们会发现日志输出上面没有警告了!开心吧?好了,至此我们就实现了使用WindowManager打开一个预制窗口,但是打开一个是明显不行的,在游戏项目中,我们通常是打开通过button打开很多个窗口。


二 多窗口打开的窗口管理器

要做这个功能,我们就需要对上面的预制体进行修改了,怎么修改呢?我们之前不是说了,我们在实例窗口的时候,其实是实例的同名子级,所以我们需要给那个子级添加父亲,在root下面新建一个gameobject,和子级同名,然后把子级拖到这个新建的gameobject下面去,同时:记住给这个gameobject添加panel脚本,同时修改depth深度为1,如下图:



关于为什么要给新建的父级添加panel,后面会详细说到。我们说了,打开窗口的时候,要把打开的这个窗口显示在最前面,那么通过修改什么?当然是panel中的depth了,关闭这个窗口的时候,我们要把这个窗口的depth变成原来的1!所以这个功能需要在打开窗口和关闭窗口中去实现!修改代码:

(具体:可以定义一个int类型的变量,来存存windowroot下面所打开的窗口之后的最大深度值,为什么要这么做?


①.首先,我打开一个窗口的时候,本身panel值为1,那么我们把这个1赋值给全局深度值,这个时候全局深度值就是1,

②.当我们打开第二个窗口的时候,(默认深度值也是1)第二个窗口的深度必须为2,才能显示在第一个的上面,一次类推,

③.可能有人会问了,我打开一个窗口之后再打开第二个窗口的时候,我把第一个关闭不就行了,那么第二个不用去改深度值照样可以显示在最上面啊,当然这样也是可以的,

④.但我们这个例子要实现的就是:打开一个窗口,通过该窗口的关闭按钮关闭窗口,同时关闭窗口之时,需要将panel深度值变为初始值,

⑤.还有一种情况需要我们处理,我们打开一个窗口,但是这个窗口下面其实还有n个隐藏窗口,当我们操作打开窗口上面的某个button时候,可以将这些隐藏的窗口显示在最上层


这种情况也是需要我们去思考的,所以我们写出的代码一定要有把握全局应对所有情况的功能

  1. /// <summary>
  2. /// set window
  3. /// </summary>
  4. /// <param name="varWindowGameObject"></param>
  5. public void SetWindow(GameObject varWindowGameObject)
  6. {
  7. //varWindowGameObject.layer = LayerMask.NameToLayer("Default");
  8. varWindowGameObject.transform.SetParent(WindowRoot.transform);
  9. varWindowGameObject.transform.localScale=Vector3.one;
  10. //获取当前打开的窗口下面有多少panel
  11. UIPanel[] panels = varWindowGameObject.GetComponentsInChildren<UIPanel>();
  12. int currentWindowDepth = mWindowDepth;
  13. foreach (var itemPanel in panels)//假如当前打开的窗口下面有2个panel
  14. {
  15. itemPanel.depth += currentWindowDepth;
  16. //更新当前panel数组中要加的panel值
  17. if (itemPanel.depth > currentWindowDepth)
  18. {
  19. currentWindowDepth = itemPanel.depth;
  20. }
  21. }
  22. //所有panel都加完了,更新全局深度
  23. mWindowDepth = currentWindowDepth;
  24. }

这样修好之后,我们可以保证每个后打开的窗口都在最前面显示了,那么如何实现点击关闭窗口之后,修改回原来的depth值?

  1. /// <summary>
  2. /// 关闭某个窗口,active=false,修改depth
  3. /// </summary>
  4. /// <param name="varName"></param>
  5. public void CloseWindow(string varName)
  6. {
  7. GameObject tempWindow;
  8. if (string.IsNullOrEmpty(varName)) return;
  9. if (mLoadWindow.TryGetValue(varName, out tempWindow))
  10. {
  11. //1.设置active
  12. tempWindow.SetActive(false);
  13. //2.修改depth值时,看看这个窗口下面有多少panel
  14. int tempDepth = mWindowDepth;
  15. UIPanel[] panels = tempWindow.GetComponentsInChildren<UIPanel>();//当然这里可以在上面封装,全局公用depth数量
  16. for (int i = panels.Length-1; i >=0; i--)
  17. {
  18. panels[i].depth -= tempDepth -1;
  19. if (panels[i].depth < tempDepth)
  20. {
  21. tempDepth = panels[i].depth;
  22. }
  23. }
  24. mWindowDepth = tempDepth;
  25. Debug.LogError(mWindowDepth);
  26. }
  27. }

经过调用,我们很好的实现了我们想要的多窗口打开的简易窗口管理器,当然这我这也说了只是一个简单版本,真正项目中用到的比这个稍微复杂,但是也不是没有规律可循,只不过多了一下封装而已,本案例我会提供工程下载的地址:

链接:http://pan.baidu.com/s/1kVPTFfx 密码:wau4


最后再次展示一下工程结构图:



声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/103407
推荐阅读
相关标签
  

闽ICP备14008679号