Unity 可编程渲染管线 实现内幕 -- Unite 2017 柳振东分享《可编程渲染管线》

关于UniteUnite大会是由Unity举办的全球开发者大会,至今已有10年的历史。Unite现已成为游戏行业,VR/AR行业中最具有权威性和影响力的活动。...
  • 2017-05-27

重建 是UGUI优化的关键 -- Unite2017嘉宾杨怀忠分享《UGUI深度优化》

 关于UniteUnite大会是由Unity举办的全球开发者大会,至今已有10年的历史。Unite现已成为游戏行业,VR/AR行业中最具有权威性和影响力的活动...
  • 2017-05-27

Unity UI框架开发(一)实现UIBaseWindow

UI框架实现了UI的的显示、隐藏、按钮点击、UI数值更新,也是大多数游戏UI的功能。本篇文章介绍的是用代码实现UI框架,为了实现这个框架用到的三个基类 UIBaseWindow、UIBaseView和UIWindowCtrl,下面要介绍的是第一个基类 UIBaseWindow。首先是窗口类UIBaseWindow: ?123456789101112131415161718192021222324252627282930public class UIBaseWindow {     public GameObject winParent = null;     public virtual void OnCreate(string winName)    {        if (winParent == null){        // GameSettings.GetInstance().UIWin是一个对象,可自定义,也可以直接使用CanVas,反正是用来存放UI的            winParent = ResMgr.CreateEmptyGameObject(GameSettings.GetInstance().UIWin, winName);        }    }     // 在当前窗口切换其他窗口    public void StartWindow(System.Type windowName)    {        UIWindowCtrl.GetInstance().ShowWindow(windowName);    }     // 重现当前窗口    public virtual void OnResume()    {        winParent.SetActive(true);    }     // 隐藏当前窗口    public virtual void OnStop()    {        winParent.SetActive(false);    }}该基类主要封装了几个方法,都很简单,主要说下几点。 (1)OnCreate(string winName)方法创建一个空对象,是用来存放该窗口的后续所有子对象,其中用到调用的方法是我封装的一个方法。?123456789public static GameObject CreateEmptyGameObject(GameObject parent, string name){    GameObject obj = new GameObject(name);    obj.transform.SetParent(parent.transform);    obj.transform.localPosition = Vector3.zero;    obj.transform.localEulerAngles = Vector3.zero;    obj.transform.localScale = Vector3.one;    return obj;}(2)StartWindow(System.Type windowName)方法是启动一个窗口,参数是一个窗口类名的类型,如:typeof(Win_Shop);至于调用的UIWindowCtrl类方法将在后面说明。下面我们实现一个继承于基类的具体窗口类Win_Main。?123456789101112131415public class Win_Main : UIBaseWindow {     private MainView mMainView;     public override void OnCreate(string winName)    {        base.OnCreate(winName);        mMainView = new MainView(this, "Prefas/UI/UIMain/MainView");    }     public override void OnResume()    {        base.OnResume();    }}好了,现在我们再来梳理下这个UI框架,以上面代码为例;比如我要显示一个主界面Main,这个界面主要包含一些背景图和、商城、设置、帮助、等按钮,这些都做成一个预设MainView,然后从上面代码看到一个同名类MainView,该类实现了预设的各种功能。所以当我要OnCreate()一个窗口时就会调用这个MainVeiw预设界面了。
  • 2017-05-25

Unity3D打斗游戏开发(三)简单实现对象池

对象池经常用在频繁创建、销毁对象(并且对象创建、销毁开销很大)的场景,网上也有很多资料对其做了介绍,今天要和大家介绍的是在打斗类游戏开发中如何去实现对象池,一起来看看吧。对象池原理在游戏中我们都知道像怪物、子弹和特效等是需要频繁使用的(创建和销毁)。那么,毋庸置疑这一过程是会消耗性能的,所以引用对象池技术的意思就是在游戏开始前预先初始化一个含一定数量对象的池子,当游戏中需要时就从池子中取,不需要时就放回池中。这样就能避免游戏中频繁的创建和销毁对象。下面代码将简单实现一个对象池?123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354using UnityEngine;using System.Collections; public class MyObjectPool {    // 方法的委托    public delegate T CreateDelegate();    private CreateDelegate dCreate;     private T[] objList; // 使用数组存储存取速度快,减少内存碎片    private int size = 0;    private int poolSize = 1;     ///     /// 对象池初始化对象,size为对象池初始大小    ///     ///     public MyObjectPool(CreateDelegate create,int size)    {        dCreate = create;        objList = new T[size];        for (int i = 0; i < size; i++) {            Push(dCreate());        }    }     ///     /// 对象池取对象,如果取完了,扩展对象池大小    ///     ///     public T Pop()    {        T newObj = default(T);        if (objList != null && size != 0){            newObj = objList[--size];            objList[size] = default(T);            return newObj;        }        else {            Debug.Log("对象池对象不够,扩展池大小 "+size);            newObj = dCreate();            objList = new T[++poolSize];        }        return newObj;    }     ///     /// 对象池回收对象    ///     ///     public void Push(T t)    {        objList[size++] = t;    }}在另一脚本创建对象池?12345678910111213141516171819// 游戏场景中用来调用所创建的对象池public MyObjectPool monsterPool; void Start () {            // 初始化对象池    monsterPool = new MyObjectPool(InitObjPool,10);       } // 通过委托调用的方法private GameObjcet InitObjPool(){    // 初始化怪物对象池,obj可以为怪物、子弹和特效等预设    GameObjcet go = MonoBehaviour.Instantiate(obj);    // 先隐藏起来,取出时再显示    // 因为对象池类型设为T,所以不能在对象池内部作隐藏和显示操作    go.SetActive(false);    return go;}小结:这里简单的实现了对象池的用就取,不用放回功能。对象池的创建放在和场景初始化一起就行了(搭配上一篇的异步加载使用,即异步加载场景完之后对象池也建好了)。关于对象使用后的数据重置问题,如果对象是使用挂脚本方式,在使用过程中数据可能会改变,那么这些对象就是独立的个体(数据不唯一),所以我们应该尽量避免在对象身上挂脚本。比如对象为怪物,那么我们就是创建一个Monster类(不继承MonoBehaviour),关于怪物的属性、初始化方法都放在这个类里面,对象实例只是其中的一个GameObject,游戏中使用的是Monster类,对象实例只是一个模型,没有任何数据。那么将其放回对象池就不需要作数据重置了。我们在新建一个怪物时只需new一个Monster实例,加上从对象池取一个怪物对象就可以完成一个怪物的创建了。技术扩展由于没有真正的销毁对象,而且随着数量的增多还不断扩展对象池大小,是很占用内存的,特别是手机的。Unity3D打斗游戏开发系列:Unity3D打斗游戏开发(一)普通攻击敌人判断Unity3D打斗游戏开发(二)异步加载场景
  • 2017-05-25

Unity UI框架开发(二)实现UIBaseView

接上篇实现UIBaseWindow,本篇文章要介绍的是实现UI框架三个基类中的第二个基类UIBaseView。下面继续实现第二个基类UIBaseView。 ?1234567891011121314151617181920212223242526272829303132333435abstract public class UIBaseView {     public GameObject view;    private UIBaseWindow _window;    public UIBaseView(UIBaseWindow parent,string path)    {        _window = parent;        view = ResMgr.InitLoadPrefabsInParent(parent.winParent, path);        Start();    }     public abstract void Start();     // 获取当前View所属Window    public virtual UIBaseWindow GetWindow()    {        return _window;    }     // 设置当前View所属Window    public virtual void SetWindow(UIBaseWindow window)    {        _window = window;    }     public virtual void Hide()    {        view.SetActive(false);    }     public virtual void Show()    {        view.SetActive(true);    }}该类的一个实例MainView?12345678910111213141516171819202122232425262728293031public class MainView : UIBaseView {     public MainView(UIBaseWindow window,string path)        :base(window,path)    {    }     public override void Start()    {        view.SetOnClickListener(OnButtonClick);    }     private void OnButtonClick(GameObject btn)    {        switch(btn.name)        {            case BUTTON.Btn_Set:                // 调用当前View所属Window的StartWindow方法切换来窗口                GetWindow().StartWindow(typeof(Win_Set));                break;            case BUTTON.Btn_Shop:                GetWindow().StartWindow(typeof(Win_Shop));                break;        }    }     private struct BUTTON{        public const string Btn_Set = "Btn_Set";        public const string Btn_Shop = "Btn_Shop";    }}上面有段按钮点击回调方法绑定的代码view.SetOnClickListener(OnButtonClick);方法SetOnClickListener其实是在其他脚本封装的一个点击事件绑定方法,还是个扩展方法。扩展方法 (1)给某个类扩展一个方法出来; (2)该方法必须为static,参数一为类本身,必须带this关键字(this GameObject obj)?1234567public static void SetOnClickListener(this GameObject obj,EventTriggerListener.methodDelegate method)    {        foreach (Button btn in obj.GetComponentsInChildren()) // 获取obj所有子物体的Button组件        {            EventTriggerListener.getListener(btn.gameObject).onClick = method;        }    }上面的MainView实现了点击按钮显示Set、Shop窗口。
  • 2017-05-25

Unity中使用柏林噪声生成地图

       使用柏林噪声生成地图主要是利用Unity的 Mathf.PerlinNoise   函数(柏林噪声)的不同寻常的功能。https://docs.unity3d.com/ScriptReference/Mathf.PerlinNoise.html   其中一个实例代码是生成  柏林噪声图片。     第二个实例是动画效果(看似y轴的随机运动,对随机的动画来讲很好啊!)。这样设置 得到: 1、Mathf.PerlinNoise  函数的返回值是 0~12、一样的输入,输出也必将是一样的:  比如下面的测试,输出一直是 0.4652731    void Update()    {        float height = heightScale *Mathf.PerlinNoise(5f/*Time.time * xScale*/, 0.0F);3、他的代码思路就是  100 *100个方块,然后根据 x, z 设置y坐标值和颜色(跟y有关),float xSample = (cube.transform.localPosition.x + _seedX) / _relief;float zSample = (cube.transform.localPosition.z + _seedZ) / _relief;float noise = Mathf.PerlinNoise(xSample, zSample);y = _maxHeight * noise;为了增加随机性, 对x,z 的系数做随机:_seedX = Random.value * 100f;_seedZ = Random.value * 100f; 比如使用噪声生成 类似我的世界的地图等代码相对简单,直接在Awake 函数中执行生成了 地图!改变   尺寸 (宽度和深度)改变   最大高度             如果不启用柏林噪声,而是使用随机的效果: 平整度: 还有是否添加碰撞体: _relief 跌宕起伏,决定了 采样的间隔大小。值越大跨度越小。 地图大小,就是控制父对象的scale ?123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103// RandomMapMaker.cs  using UnityEngine;  using System.Collections;  using System.Collections.Generic;     public class RandomMapMaker : MonoBehaviour {     private float _seedX, _seedZ;     [SerializeField]  private float _width = 50;  [SerializeField]  private float _depth = 50;     [SerializeField]  private bool _needToCollider = false;     [SerializeField]  private float _maxHeight = 10;     [SerializeField]  private bool _isPerlinNoiseMap = true;     [SerializeField]  private float _relief = 15f;     [SerializeField]  private bool _isSmoothness = false;     [SerializeField]  private float _mapSize = 1f;  //=================================================================================  //初期化  //=================================================================================  private void Awake () {     transform.localScale = new Vector3(_mapSize, _mapSize, _mapSize);     _seedX = Random.value * 100f;  _seedZ = Random.value * 100f;     for (int x = 0; x < _width; x++) {  for (int z = 0; z < _depth; z++) {     GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);  cube.transform.localPosition = new Vector3 (x, 0, z);  cube.transform.SetParent (transform);  if(!_needToCollider){  Destroy(cube.GetComponent ());  }     SetY (cube);  }  }  }     private void OnValidate (){     if(!Application.isPlaying){  return;  }     transform.localScale = new Vector3(_mapSize, _mapSize, _mapSize);     foreach (Transform child in transform) {  SetY (child.gameObject);  }  }     private void SetY(GameObject cube){  float y = 0;     if(_isPerlinNoiseMap){  float xSample = (cube.transform.localPosition.x + _seedX) / _relief;  float zSample = (cube.transform.localPosition.z + _seedZ) / _relief;  float noise = Mathf.PerlinNoise(xSample, zSample);  y = _maxHeight * noise;  }     else{  y = Random.Range (0, _maxHeight);  }     if(!_isSmoothness){  y = Mathf.Round (y);  }     cube.transform.localPosition = new Vector3 (cube.transform.localPosition.x, y, cube.transform.localPosition.z);     Color color = Color.black;  if(y > _maxHeight * 0.3f){  ColorUtility.TryParseHtmlString("#019540FF", out color);  }  else if(y > _maxHeight * 0.2f){  ColorUtility.TryParseHtmlString("#2432ADFF", out color);  }  else if(y > _maxHeight * 0.1f){  ColorUtility.TryParseHtmlString("#D4500EFF", out color);  }  cube.GetComponent ().material.color = color;  }  } 
  • 2017-05-25

Unity CSV文件加载

游戏开发中经常涉及到文本文件的加载处理,游戏常用文件格式非常多,json,xml,csv,二进制等等,本篇文章要给大家介绍的是csv文件的加载,其实,在游戏中使用csv文件格式就是将excel另存为csv格式即可,这样非常方便策划对配置文件的修改。配置文件的处理有很多种方式,在这里给大家推荐一种比较简单的csv文件加载脚本,代码如下所示: ?123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146using System;  using System.Collections;  using System.Collections.Generic;     namespace Utils  {      public class CsvReader      {          //          // Properties          //          public int ColCount          {              get             {                  return m_Col;              }          }             protected List Datas          {              get             {                  if (m_Datas == null)                  {                      m_Datas = new List();                  }                  return m_Datas;              }          }             public int RowCount          {              get             {                  if (m_Datas == null)                  {                      return 0;                  }                  return m_Datas.Count;              }          }             //          // Methods          //          public void Clear()          {              if (m_Datas != null)              {                  m_Datas.Clear();              }              m_Col = 0;          }             public bool GetBoolData(int r, int c)          {              return GetIntData(r, c) != 0;          }             public string GetData(int r, int c)          {              if (m_Datas == null)              {                  return string.Empty;              }              if (r < 0 || r >= m_Datas.Count)              {                  return string.Empty;              }              if (c < 0 || c >= m_Datas[r].Length)              {                  return string.Empty;              }              return m_Datas[r][c].Trim();          }             public float GetFloatData(int r, int c)          {              string data = GetData(r, c);              if (string.IsNullOrEmpty(data))              {                  return 0f;              }              float result;              if (!float.TryParse(data, out result))              {                  result = 0f;              }              return result;          }             public int GetIntData(int r, int c)          {              string data = GetData(r, c);              if (string.IsNullOrEmpty(data))              {                  return 0;              }              int result;              if (!int.TryParse(data, out result))              {                  result = 0;              }              return result;          }             public bool LoadFromFile(string fileName)          {              string text = Singleton.Instance.LoadText(fileName, ResourceCacheType.rctNone);              return text != null && LoadFromString(text);          }             public bool LoadFromString(string text)          {              Clear();              if (string.IsNullOrEmpty(text))              {                  return false;              }              string[] array = text.Split(CsvReader._cLineDims);              if (array != null && array.Length > 0)              {                  for (int i = 0; i < array.Length; i++)                  {                      string text2 = array[i];                      if (!string.IsNullOrEmpty(text2))                      {                          string[] array2 = text2.Split(new char[]                          {                              ','                         });                          Datas.Add(array2);                          m_Col = Math.Max(m_Col, array2.Length);                      }                  }              }              return true;          }             public static readonly char _cLineDims = ';';          private List m_Datas = null;          private int m_Col = 0;       //   private int m_Row = 0;      }  }  以上代码可以直接在工程中使用,直接掉用接口:?1"white-space:pre">  public bool LoadFromFile(string fileName)  脚本中也提供了数据清理,获取数据等接口供用户使用。
  • 2017-05-25

最新问答

更多

粽享童年,迎零码壹,程序员用自己的方式吟诗做对,颂享端午、儿童节。。。

游戏开发社区_Gad-腾讯游戏开发者平台
++阿联酋长 2017-05-25 回答了该问题
今天我什么事情都不做,也不敲代码只在家好好吃妈妈做的饭,和她包的粽子,陪他们唠唠嗑。足矣。
  • 2017-05-25

现在unity3d做的mmo是如何在服务器端做碰撞检测或nav mesh寻路的?

游戏开发社区_Gad-腾讯游戏开发者平台
追梦人 2017-05-04 回答了该问题
这个可以参考kbengine的一个demo
  • 2017-05-04

Unity3D如何有效地组织代码?

游戏开发社区_Gad-腾讯游戏开发者平台
Soulら小兮 2017-05-13 回答了该问题
并没有啥本质区别,以前写cocos的时候也可以写成component based。所以主要的矛盾并不是unity vs cocos,而是component vs inheritance。非常推荐game programming patterns书中的component章节(Component · Decoupling Patterns · Game Programming Patterns)unity自己有一些蹩脚的地方比如全局的dt,没有全局统一入口,update顺序不确定等等,都还是比较容易克服的。
unity
  • 2017-05-13

新手求助!.unitypackage文件里的model能再加代码实现移动吗

游戏开发社区_Gad-腾讯游戏开发者平台
滴血红豆 2017-05-19 回答了该问题
GameObject-&gt;Create Empty  是这样子吗
unity
  • 2017-05-19
微信扫码关注GAD官方公众号 关注GAD二维码
qq

Gad游戏开发核心用户群

484290331

合作伙伴

  • 游戏开发社区_Gad-腾讯游戏开发者平台 Microsoft
  • 游戏开发社区_Gad-腾讯游戏开发者平台 超维星球孵化器
  • 游戏开发社区_Gad-腾讯游戏开发者平台 白鹭引擎
  • 游戏开发社区_Gad-腾讯游戏开发者平台 开源引擎LayaAir
  • 游戏开发社区_Gad-腾讯游戏开发者平台 腾讯大学
  • 游戏开发社区_Gad-腾讯游戏开发者平台 WeTest腾讯质量开放平台