赞
踩
Unity实现自己的简易游戏窗口管理器
概述:
在一个手游项目中,进入游戏之后,我们会打开很多游戏窗口(界面),比如帮助窗口(界面){新手指引界面,玩法介绍界面,技能介绍界面},模块窗口(界面){任务界面,铸造界面,宠物界面,签到界面等等},这些界面我们都可以使用窗口管理器来实现,那么今天我将从一个很简单的小例子入手,和大家一起来完成一个简易的窗口管理器,最后我会把复杂的窗口管理器的实现思路告诉大家!
功能需求:
可以打开窗口,可以关闭打开的这个窗口,最后打开的窗口永远显示在前面,而且能够实现后面窗口的事件屏蔽。
实现过程:
1.游戏开始后,层次面板将出现一个我们自定义的UIRoot(身上需要挂载UIRoot脚本,Panel必须添加),这个游戏物体下面需要有个Camera,Camer身上需要的组件是Camera和UICamera,这个游戏物体下面还需要一个WindowRoot(窗口根节点)(本来应该还有DiagLogRoot的但是这个小例子为了测试,就不必写了),其中WindowRoot这个游戏物体是个空物体,他身上无需任何脚本,它的主要功能就是用来承放 我们打开的窗口的。
2.既然叫做窗口管理器,当然是希望有需要打开窗口的地方调用这个窗口管理器啊,所以,这个窗口管理器需要做成单例:(贴一个单例模式的写法)
- /// <summary>
- /// singleton test class.
- /// </summary>
- public class SingletonTest
- {
-
- private static SingletonTest mInstance;
-
- public static SingletonTest GetSingletonTestInstance()
- {
- //if minstance == null.
- if (mInstance == null)
- {
- mInstance=new SingletonTest();
- }
-
- return mInstance;
- }
-
- }
根据游戏过程中的第一条,我们可以分析写出如下代码:
- using UnityEngine;
- using System.Collections;
- using System.Collections.Generic;
-
- public class WindowManager
- {
-
- //UIRoot根节点
- private GameObject CustomRoot;
- //摄像机
- private GameObject Camera;
- //打开的窗口根节点
- private GameObject WindowRoot;
- //存储打开过的窗口
- private Dictionary<string, GameObject> mLoadWindow;
-
-
- /// <summary>
- /// 单例模式
- /// </summary>
- private static WindowManager mInstance;
-
- public static WindowManager GetWindowManagerSingleton()
- {
- if (mInstance == null)
- {
- mInstance = new WindowManager();
- }
-
- return mInstance;
- }
-
-
-
- private WindowManager()
- {
- CustomRoot = new GameObject("UI Root");
- CustomRoot.AddComponent<UIRoot>();
-
- Camera = new GameObject("Camera");
- Camera.transform.SetParent(CustomRoot.transform);
- Camera.AddComponent<Camera>();
- Camera.AddComponent<UICamera>();
-
- WindowRoot = new GameObject("WindowRoot");
- WindowRoot.transform.SetParent(CustomRoot.transform);
- }
- }
好了,到了这一步我们就算是把窗口管理器的前期工作做好了,接下来,我们之前说了,窗口管理器要实现打开窗口和关闭窗口,那么这个windowmanager中就需要一个openwindow和closewindow的函数,那我们继续完善工程,完善工程之前,我们需要建一个存储待打开窗口的文件夹,我的命名规范如下:
好了,接下来我们就要分别打开这两个窗口,test1,test2,完善我们的窗口管理器代码:
(不完善版本)
- public GameObject OpenWindow(string varWindowName)
- {
- GameObject tempWindow;
- if(string.IsNullOrEmpty(varWindowName))return null;
- //集合中已经在加载过的情况下
- if (mLoadWindow.TryGetValue(varWindowName, out tempWindow))
- {
- SetWindow(tempWindow);
- return tempWindow;
- }
- //没有加载过这个窗口
- //1.根据名字加载
- GameObject loadWindow = Resources.Load<GameObject>(varWindowName);
- //2.实例预制体中同名子级
- GameObject childUI = loadWindow.transform.Find(loadWindow.name).gameObject;
- tempWindow = GameObject.Instantiate(childUI);
- //3.添加进集合(防止以后重复实例)
- mLoadWindow.Add(varWindowName,tempWindow);
- //4.设置实例出来的物体
- SetWindow(tempWindow);
- //5.返回值
- return tempWindow;
- }
-
- /// <summary>
- /// set window
- /// </summary>
- /// <param name="varWindowGameObject"></param>
- public void SetWindow(GameObject varWindowGameObject)
- {
- varWindowGameObject.transform.SetParent(WindowRoot.transform);
- varWindowGameObject.transform.localScale=Vector3.one;
- }
所以我们需要回到构造函数那里,添加上这条代码:
- //添加panel组件,不添加会有不好的效果
- CustomRoot.AddComponent<UIPanel>();
添加完毕之后,我们再来运行,看下效果,我们会发现,虽然问题解决了,但是并没有窗口显示在游戏视图,但是场景视图是有的,这个时候,你需要做的事情就是:
1.检查窗口的大小
2.检查摄像机的相关参数
这个时候,我们该如何解决这个问题呢?我们之前不是自己实例出来的一个摄像机吗?我们可以给那个摄像机修改一下参数,参数代码:
- Camera = new GameObject("Camera");
- Camera.transform.SetParent(CustomRoot.transform);
- Camera camera=Camera.AddComponent<Camera>();
- Camera.AddComponent<UICamera>();
-
- camera.orthographic = true;//正交视野
- camera.orthographicSize = 1f;
- camera.nearClipPlane = -0.03f;
- camera.farClipPlane = 200f;
经过修改,我们最终发现窗口可以正常被打开了,效果图:
但是最后我们发现console控制台报出了警告,正规项目,所有黄色警告都是要尽量解除的,报了警告就是说明你的代码有问题,如图:
就是说panel父级物体和子级物体的层不一样,虽然不一样,但NGUI自动帮我们转了相同层,就把这个警告显示出来了。这个问题说白了就是父级和子级层的问题,层的问题相当好解决,之前有篇文章是通过位运算来修改layer层的,那么我们在这个小例子里面不需要使用位运算。
//varWindowGameObject.layer = LayerMask.NameToLayer("Default");
通过上面的两种方式之中的任选其一,我们会发现日志输出上面没有警告了!开心吧?好了,至此我们就实现了使用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时候,可以将这些隐藏的窗口显示在最上层
这种情况也是需要我们去思考的,所以我们写出的代码一定要有把握全局应对所有情况的功能)
- /// <summary>
- /// set window
- /// </summary>
- /// <param name="varWindowGameObject"></param>
- public void SetWindow(GameObject varWindowGameObject)
- {
- //varWindowGameObject.layer = LayerMask.NameToLayer("Default");
- varWindowGameObject.transform.SetParent(WindowRoot.transform);
- varWindowGameObject.transform.localScale=Vector3.one;
-
- //获取当前打开的窗口下面有多少panel
- UIPanel[] panels = varWindowGameObject.GetComponentsInChildren<UIPanel>();
- int currentWindowDepth = mWindowDepth;
- foreach (var itemPanel in panels)//假如当前打开的窗口下面有2个panel
- {
- itemPanel.depth += currentWindowDepth;
- //更新当前panel数组中要加的panel值
- if (itemPanel.depth > currentWindowDepth)
- {
- currentWindowDepth = itemPanel.depth;
- }
- }
-
- //所有panel都加完了,更新全局深度
- mWindowDepth = currentWindowDepth;
-
- }
- /// <summary>
- /// 关闭某个窗口,active=false,修改depth
- /// </summary>
- /// <param name="varName"></param>
- public void CloseWindow(string varName)
- {
- GameObject tempWindow;
- if (string.IsNullOrEmpty(varName)) return;
- if (mLoadWindow.TryGetValue(varName, out tempWindow))
- {
- //1.设置active
- tempWindow.SetActive(false);
- //2.修改depth值时,看看这个窗口下面有多少panel
- int tempDepth = mWindowDepth;
- UIPanel[] panels = tempWindow.GetComponentsInChildren<UIPanel>();//当然这里可以在上面封装,全局公用depth数量
- for (int i = panels.Length-1; i >=0; i--)
- {
- panels[i].depth -= tempDepth -1;
- if (panels[i].depth < tempDepth)
- {
- tempDepth = panels[i].depth;
- }
- }
- mWindowDepth = tempDepth;
- Debug.LogError(mWindowDepth);
- }
- }
链接:http://pan.baidu.com/s/1kVPTFfx 密码:wau4
最后再次展示一下工程结构图:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。