2019-11-14 14:25:04 e295166319 阅读数 36

“游戏框架”作为整个游戏的框架,具有核心的地位,一个游戏的层次和后期维护性就取决于游戏框架。


1:3D引擎框架图


2:Unity3D游戏客户端基础框架

一些通用的基础系统的框架搭建,其中包括:

UI框架(UGUI+MVC)

消息管理(Message Manager)

网络层框架(Socket + Protobuf)

表格数据(Protobuf)

资源管理(Unity5.x的AssetBundle方案)

热更框架(tolua)


2.1:UI框架

 

编写UI框架的意义:

1:打开、关闭、层级、页面跳转等管理问题集中化,将外部切换等逻辑交给UIManager处理。

2:功能逻辑分散化,每个页面维护自身逻辑,依托于框架便于多人协同开发,不用关心跳转和显示关闭细节。


--通用性框架能够做到简单的代码复用和“项目经验”沉淀。

基于Unity3D和UGUI实现的简单的UI框架,实现内容:

  1. 加载、显示、隐藏、关闭页面,根据标示获得相应界面实例;
  2. 提供界面显示隐藏动画接口;
  3. 单独界面层级,Collider,背景管理;
  4. 根据存储的导航信息完成界面导航;
  5. 界面通用对话框管理;
  6. 便于进行需求和功能扩展;
     

2.2:消息管理(Message Manager)

  1. 一个消息系统的核心功能:
  2. 一个通用的事件监听器;
  3. 管理各个业务监听的事件类型(注册和解绑事件监听器);
  4. 全局广播事件;
  5. 广播事件所传参数数量和数据类型都是可变的(数量可以是0~3,数据类型是泛型)

消息管理设计思路:在消息系统初始化时将每个模块绑定的消息列表,根据消息类型分类(用一个string类型的数据类标识),即建立一个字典Dictionary<string, List<Model>>:每条消息触发时需要通知的模块列表:某条消息触发,遍历字典中绑定的模块列表。


2.3:网络层框架(NetworkManager)

  1. 除了单机游戏,限制绝大多数的网游都是以强联网的方式实现的,选用Socket通信可以实时地更新玩家状态。
  2. 选定了联网方式后,还需要考虑网络协议定制的问题,Protobuf无疑是个比较好的选择,一方面是跨平台特性好,另一方面是数据量小可以节省通信成本。
  3. Socket通信:联网方式、联网步骤,数据收发以及协议数据格式。(加入线程池管理已经用一个队列来管理同时发起的请求,让Socket请求和接收异步执行,基本的思路就是引入多线程和异步等技术。)
  4. Protobuf网络框架主要用途是:数据存储(序列化和反序列化),功能类似xml和json等;制作网络通信协议等。(Protobuf不仅可以进行excel表格数据的导出,还能直接用于网络通信协议的定制。)
  5. Protobuf是由Google公司发布的一个开源的项目,是一款方便而又通用的数据传输协议。(在Unity中可借助Protobuf来进行数据存储和网络协议两方面的开发。)

2.4:表格数据

  1. 在游戏开发中,有很多数据是不需要通过网络层从服务器拉取下来的,而是通过表格配置的格式存储在本地。
  2. 游戏中的一个道具,通常服务器只下发该道具的ID(唯一标识)和LV(等级),然后客户端从本地数据中检索到该道具的具体属性值。(通常使用Excel表格来配置数据,可以使用Protobuf、JSON、XML等序列化和反序列化特性对表格数据转化。)
     

2.5:资源管理(AssetBundle)

  1. AssetBundle是Unity引擎提供的一种资源压缩文件,文件扩展名通常为unity3d或assetbundle。
  2. 对于资源的管理,其实是为热更新提供可能,Unity制作游戏的资源管理方式就通过AssetBundle工具将资源打成多个ab包,通过网络下载新的ab包来替换本地旧的包,从而实现热更的目的。
  3. AssetBundle是Unity编辑器在编辑环境创建的一系列的文件,这些文件可以被用在项目的运行环境中。(包括的资源文件有:模型文件(models)、材质(materials)、纹理(textures)和场景(scenes)等。)

Editor打包AssetBundle:
 

//Editor打包AssetBundle
[MenuItem(“Assets/Build AssetBundles”)]
static void BuildAllAssetBundles(){
    BuildPipeline.BuildAssetBundles(Application.dataPath+“/AssetBundles”,
    BuildAssetBundleOptions.None, BuildTarget.StandaloneOSXIntel);
}

2.6、热更新框架(tolua)

  1. 使用C#编写底层框架,使用lua编写业务逻辑,这是业内最常见的设计方式,还有一个非常成熟的热更新框架tolua。
  2. 通常可热更新的有:图片资源、UI预制和lua脚本,而处于跨平台的考虑,C#脚本是不允许进行热更的。

 

 

 

 

 

 

 

 

 

 

2017-05-14 20:14:32 jxw167 阅读数 2732

笔者介绍:姜雪伟,IT公司技术合伙人,IT高级讲师,CSDN社区专家,特邀编辑,畅销书作者,国家专利发明人;已出版书籍:《手把手教你架构3D游戏引擎》电子工业出版社和《Unity3D实战核心技术详解》电子工业出版社等。

CSDN视频网址:http://edu.csdn.net/lecturer/144

在利用Unity3D引擎开发程序时,UI资源的加载,卸载,隐藏以及UI渐变动画等功能是UI架构设计必须考虑的。

做每一款游戏都需要将这些功能编写一遍非常耗时,在此给读者介绍一种快速的实现方式,因为我们这个是通用的

模块,所以必须要使用模板实现,而且我们的逻辑脚本是不挂接到对象上的。接下来首先设计一个管理类Manager,

代码如下所示:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
/// <summary>
/// 抽象管理类
/// </summary>
/// <typeparam name="K"></typeparam>
/// <typeparam name="V"></typeparam>
public class Manager<T,K, V> : Singleton<T>
    where V : class ,IDisposable
    where T : Singleton<T>, new()
{
    protected Dictionary<K, V> mMap = new Dictionary<K, V>();
 
    /// <summary>
    /// 获取 对应实体
    /// </summary>
    /// <param name="key"></param>
    /// <returns></returns>
    public V Get(K key)
    {
        if (key == null) return null;
        return mMap.ContainsKey(key) ? mMap[key] : null;
    }

    /// <summary>
    /// 获取类型T的 Value
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="key"></param>
    /// <returns></returns>
    public U Get<U>(K key) where U : class,V 
    {
        V v = Get(key);
        return v as U;
    }

    /// <summary>
    /// 获取类型T的Value
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    public T Get<T>() where T : class,V 
    {
        foreach(V value in mMap.Values)
        {
            if(value.GetType().Equals(typeof(T)))
            {
                return value as T;
            }
        }
        return null;
    }
    /// <summary>
    /// 添加对应实体
    /// </summary>
    /// <param name="key"></param>
    /// <param name="value"></param>
    public bool Put(K key, V value)
    {
        if (mMap.ContainsKey(key))
        {
            if (value == mMap[key])
            {
                return false;
            }
            V v = mMap[key];
            mMap[key] = value;
            v.Dispose();
        }
        else
        {
            mMap.Add(key, value);
        }
        return true;
    }

    /// <summary>
    /// 删除
    /// </summary>
    /// <param name="key"></param>
    /// <returns></returns>
    public bool Remove(K key)
    {
        if (mMap.ContainsKey(key))
        {
            V v = mMap[key];
            mMap.Remove(key);
            v.Dispose();
        }
        return true;
    }


    public Dictionary<K,V>.ValueCollection Values
    {
        get { return mMap.Values; }
    }
    /// <summary>
    /// 清除所有管理的对象
    /// </summary>
    public void Clear()
    {
        foreach (V value in mMap.Values)
        {
            value.Dispose();
        }
        mMap.Clear();
    }
}

public class ManagerT<K, V> : Manager<ManagerT<K,V>, K, V>
    where V : class ,IDisposable
{

}


在这个类属于抽象类,它利用Dictionary实现了对象的管理操作,接下来需要实现UI的管理类了,先把代码给读者展示如下:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;

/// <summary>
/// UI 服务类
/// </summary>
public class UIService : Manager<UIService, string, UIService.UI>,IDisposable
{
    public void Dispose()
    {
        DestroyAll();
    }

    /// <summary>
    /// 创建UI
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="name"></param>
    /// <returns></returns>
    private T _CreateUI<T>(string name) where T : UI
    {
        T ui = Activator.CreateInstance(typeof(T), name) as T;
        return ui;
    }

    public class UIHolder : MonoBehaviour
    {
        public UI ui { get; set; }
    }
    public class UI : IDisposable
    {
        public UI(string name)
        {
                _Init(name);  
        }

        public enum UIStyle
        {
            Normal,//默认类型
            HideByTapScene,//点击空白处隐藏类型,和黑底不冲突
        }

        UIStyle mStyle = UIStyle.Normal;

        public UIStyle Style
        {
            get { return mStyle; }
            set { mStyle = value; }
        }

        internal GameObject mPrefab = null;
        protected UIPanel mPanel = null;
        #region 面板动画处理
        protected UITweener[] mTweens = null;
        protected UITweener   mMainTween = null;
        protected UITweener.ToggleStyle mMainToggleStyle = UITweener.ToggleStyle.normal;
        private void _TweensInit()
        {
            mTweens = mPrefab.GetComponentsInChildren<UITweener>();
            if (mTweens != null)
            {
                UITweener tween;
                for (int i = 0; i < mTweens.Length; ++i)
                {
                    tween = mTweens[i];
                    if (tween.toggleStyle == UITweener.ToggleStyle.OnShow)
                    {
                        mMainTween = tween;
                        mMainToggleStyle = UITweener.ToggleStyle.OnShow;
                    }
                    if (tween.toggleStyle == UITweener.ToggleStyle.OnShowAndHide)
                    {
                        mMainTween = tween;
                        mMainToggleStyle = UITweener.ToggleStyle.OnShowAndHide;
                        break;
                    }
                }
            }
        }

        private bool _HasTween()
        {
            return mMainTween != null && mMainTween.toggleStyle !=  UITweener.ToggleStyle.normal;
        }
        private void _TweenOnShow(EventDelegate.Callback call)
        {
            mMainTween.SetOnFinished(call);
            UITweener tween;
            for (int i = 0; i < mTweens.Length; ++i)
            {
                tween = mTweens[i];
                if(tween.toggleStyle != UITweener.ToggleStyle.normal)
                {
                    tween.ResetToStart(true);
                    tween.PlayForward();
                }
            }
        }

        private void _TweenOnHide(EventDelegate.Callback call)
        {
            if (mMainToggleStyle == UITweener.ToggleStyle.OnShow)
            {
                call();
                return;
            }
            mMainTween.SetOnFinished(call);
            UITweener tween;
            for (int i = 0; i < mTweens.Length; ++i)
            {
                tween = mTweens[i];
                if (tween.toggleStyle == UITweener.ToggleStyle.OnShowAndHide)
                {
                    tween.ResetToStart(!true);
                    tween.PlayReverse();
                }
            }
        }
        private void _Dummy()
        {

        }
        private void _Hide()
        {
            mPrefab.SetActive(false);
            mHiding = false;
        }
        #endregion

        internal void _Init(string name)
        {
            try
            {
                GameObject prefab = Resource.LoadUI(name);
                mPrefab = GameObject.Instantiate(prefab) as GameObject;
                UIHolder uiholder = UtilGameObject.GetOrAddComponent<UIHolder>(mPrefab);
                uiholder.ui = this;
                mPrefab.name = name;
                mPanel = mPrefab.GetComponent<UIPanel>(); //允许Panel为空
                _TweensInit();
            }
            catch (Exception e)
            {
                Looper.LogException(e);
            }
            UIService.Instance.InitUI(mPrefab);
            UIService.Instance._OnShowUI(this, true);
        }
        #region <默认属性>
 
        
        /// <summary>
        /// 深度信息
        /// </summary>
        public int depth
        {
            get
            {
                if (mPanel==null)
                {
                    return -1;
                }
                return mPanel.depth;
            }
            set
            {
                if (mPanel == null) return;
                mPanel.depth = value;
            }
        }
        /// <summary>
        /// 是否正在显示
        /// </summary>
        /// <returns></returns>
        public bool IsShowing()
        {
            if (mPrefab == null)
                return false;
            return mPrefab.activeSelf;
        }
        float mLastShowTime = Time.time;
        /// <summary>
        /// 最后一次显示的时间
        /// </summary>
        public float LastShowTime
        {
            get { return mLastShowTime; }
            set { mLastShowTime = value; }
        }
        #endregion

        /// <summary>
        /// 名字(和预制件名称一样)
        /// </summary>
        public  string Name
        {
            get { return mPrefab != null ? mPrefab.name : ""; }
        }
        /// <summary>
        /// 销毁对象
        /// </summary>
        public void Dispose()
        {
            UIService.Instance._OnHidUI(this);

            try
            {
                OnClose();
                if (mPrefab != null)
                {
                    GameObject.DestroyImmediate(mPrefab);
                    mPrefab = null;
                }

            }
            catch (Exception e)
            {
                Looper.LogException(e);
            }
            
        }

        /// <summary>
        /// 刷新界面
        /// 1 Grid 重排问题
        /// </summary>
        public void Refresh()
        {
           
                UIGrid[] grids = mPrefab.GetComponentsInChildren<UIGrid>();
                foreach (UIGrid grid in grids)
                {
                    if (grid != null && !grid.animateSmoothly)
                    {
                        grid.Reposition();
                    }
                }
           
        }


        public IEnumerator _Refresh()
        {
            {
                UIGrid[] grids = mPrefab.GetComponentsInChildren<UIGrid>();
                foreach (UIGrid grid in grids)
                {
                    if (grid != null && !grid.enabled)
                    {
                        grid.repositionNow = true;
                        grid.Reposition();
                    }
                }
            }

            yield return null;
        }


        //解决同一个UI动画隐藏还没结束,动画显示就开始了 状态不对的问题
        bool mHiding = false;
        /// <summary>
        /// 是否显示
        /// </summary>
        public void Show(bool v)
        {

            if (v)
            {
                if (mHiding)
                {
                    _Hide();
                }
                UIService.Instance._OnShowUI(this);
            }
            else
            {
                UIService.Instance._OnHidUI(this);
            }

            try
            {

                if (mPrefab != null)
                {
                    
                    if (_HasTween())
                    {
                        if (v)
                        {
                            mPrefab.SetActive(true);
                            _TweenOnShow(_Dummy);
                        }
                        else
                        {
                            mHiding = true;
                            _TweenOnHide(_Hide);
                        }
                    }
                    else
                    {
                        mPrefab.SetActive(v);
                    }
                    OnShow(v);
                    if (v)
                    {
                        LastShowTime = Time.time;
                        Refresh();
                        //();
                    }
                }
                
            }
            catch (Exception e)
            {
                Looper.LogException(e);
            }


        }


        /// <summary>
        /// 跟随场景中物体
        /// </summary>
        /// <param name="target"></param>
        public void ApplyHub(Transform target)
        {
            NGUIExt guiExt = PluginManager.Instance.Get<NGUIExt>();
            if (guiExt != null)
            {
                guiExt.ApplyHud(mPrefab, target);
            }
        }

        public virtual void OnCreate() { }
        public virtual void OnShow(bool v) { }
        public virtual void OnClose() { }

        /// <summary>
        /// 点击事件
        /// </summary>
        /// <param name="obj">被点击的控件</param>
        public virtual void OnClick(GameObject obj) { }

        /// <summary>
        /// 双击事件
        /// </summary>
        /// <param name="obj">被双击的控件</param>
        public virtual void OnDoubleClick(GameObject obj) { }
        /// <summary>
        /// 按住事件
        /// </summary>
        /// <param name="obj">被Pressed的控件</param>
        /// <param name="pressed"></param>
        public virtual void OnPress(GameObject obj, bool isPressed) { }

        /// <summary>
        /// 拖动事件
        /// </summary>
        /// <param name="obj">拖动的控件</param>
        /// <param name="delta"></param>
        public virtual void OnDrag(GameObject obj, Vector2 delta) { }

        /// <summary>
        /// 拖放事件
        /// </summary>
        /// <param name="obj">拖放的当前控件</param>
        /// <param name="objSelected">一直被拖住的控件</param>
        public virtual void OnDrap(GameObject obj, GameObject objSelected) { }

        /// <summary>
        /// 显示Tooltip事件
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="isShow">显示或隐藏Tooltip</param>
        public virtual void OnTooltip(GameObject obj, bool isShow) { }

        /// <summary>
        /// 被选中事件
        /// </summary>
        /// <param name="obj">被选中的控件</param>
        /// <param name="isSelected">选中或取消被选中</param>
        public virtual void OnSelect(GameObject obj, bool isSelected) { }

        /// <summary>
        /// 光标划过事件
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="isHover">光标进入或光标离开</param>
        public virtual void OnHover(GameObject obj, bool isHover) { }

        /// <summary>
        /// 根据类型获取对应名称控件;
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="name"></param>
        /// <returns></returns>
        protected T GetChild<T>(string name,GameObject obj = null, int index = 0) where T : MonoBehaviour
        {
            if(obj == null)
            {
                obj = mPrefab;
            }

            Transform child = obj.transform.Find(name);
            if (child == null)
            {
                T[] childs = obj.GetComponentsInChildren<T>();
                foreach (T t in childs)
                {
                    if (t.gameObject.name == name)
                    {
                        return t;
                    }
                }
            }
            else
            {
                if (child.childCount == 0)
                {
                    return child.GetComponent<T>();
                }

                {
                    int count = 0;
                    T[] comps = child.GetComponents<T>();
                    foreach (T t in comps)
                    {

                        if (t.gameObject.name == name && count == index)
                        {
                            return t;
                        }
                        count++;
                    }
                }
                {
                    int count = 0;
                    T[] childs = child.GetComponentsInChildren<T>();
                    foreach (T t in childs)
                    {

                        if (t.gameObject.name == name && count == index)
                        {
                            return t;
                        }
                        count++;
                    }
                }

            }

            Debug.LogError(obj.name + " hasn't Components :" + typeof(T).Name + " in children named:" + name);
            return null;
        }

        protected GameObject FindChild(string name)
        {
            var child = mPrefab.transform.FindChild(name);
            return child.gameObject;
        }

        protected T FindChild<T>(string name) where T : Component
        {
            var child = mPrefab.transform.FindChild(name);
            T cmp = child.GetComponent<T>();
        
            return cmp;
        }

        /// <summary>
        /// 获取对应名称子控件;
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        protected GameObject GetChild(string name, GameObject obj = null)
        {
            if(obj == null)
            {
                obj = mPrefab;
            }
            Transform child = obj.transform.Find(name);
            if (child == null)
            {
                Debug.LogError(obj.name + "is not find child of:" + name);
                return null;
            }
            return child.gameObject;
        }
        /// <summary>
        /// 用指定的对象替换子对象
        /// </summary>
        /// <param name="name">子对象名字</param>
        /// <param name="obj">指定的对象</param>
        /// <returns></returns>
        protected bool ReplaceChild(string name, GameObject obj)
        {
            GameObject orginal = GetChild(name);

            if (orginal == null) {
				GameObject.DestroyImmediate (obj);
				return false;
			}
            obj.transform.parent = orginal.transform.parent;
            obj.transform.localPosition = Vector3.zero;
            obj.transform.localRotation = Quaternion.identity;
            obj.transform.localScale = Vector3.one;
			obj.name = orginal.name;

			orginal.transform.parent = null;
			GameObject.DestroyImmediate (orginal);

            return true;
        }

        /// <summary>
        /// 用指定的对象替换子对象
        /// </summary>
        /// <param name="orginal">原件</param>
        /// <param name="obj">指定的对象</param>
        /// <returns></returns>
        protected bool ReplaceChild(GameObject orginal, GameObject obj)
        {
            if (orginal == null) {
				GameObject.DestroyImmediate (obj);
				return false;
			}
            obj.transform.parent = orginal.transform.parent;
            obj.transform.localPosition = Vector3.zero;
            obj.transform.localRotation = Quaternion.identity;
            obj.transform.localScale = Vector3.one;
			obj.name = orginal.name;

			orginal.transform.parent = null;
			GameObject.DestroyImmediate (orginal);
            return true;
        }

    }


    NGUIExt mPlugin;
    GameObject mBG;
    UIPanel mBGPanel;
    int mBGCount = 0;

    private void _OnShowUI(UI t,bool init = false)
    {
        if (t.IsShowing())
        {
            if(!init)
                return;
        }
        if (t.IsShowBlackBG())
        {
            mBGCount++;
            if (mBGCount > 0 && mBG.activeSelf == false)
            {
                mBG.SetActive(true);
            }
            if (mBG.activeSelf)
            {
                mBGPanel.depth = t.depth-1;
            }
        }

    }
    private void _OnHidUI(UI t)
    {
        if (!t.IsShowing()) return;

        if (t.IsShowing() && t.IsShowBlackBG())
        {
            mBGCount--;
            if(mBGCount <= 0)
            {
                mBGCount = 0;
                if (mBG.activeSelf == true)
                {
                    mBG.SetActive(false);
                }
            }


            if (mBG.activeSelf)
            {
                UI ui = _GetLastShowUI(true, t);
                if (ui != null) mBGPanel.depth = ui.depth-1;
            }
        }

    }

    public bool IsFingerHoverGUI()
    {
        if(mPlugin == null)
        {
            return false;
        }
        return mPlugin.IsFingerHoverGUI;
    }

    public bool IsFingerHoverGUI3D()
    {
        if (mPlugin == null)
        {
            return false;
        }
        return mPlugin.IsFingerHoverGUI3D();
    }

    public bool IsFingerHoverGUIWithout3D()
    {
        if (mPlugin == null)
        {
            return false;
        }
        return mPlugin.IsFingerHoverGUIWithout3D();
    }

    public bool IsPrefab(GameObject ui_prefab)
    {
        return mPlugin.IsPrefab(ui_prefab);
    }
    
    /// <summary>
    /// 初始化 继承与 Singleton 对象构建的时候被调用
    /// </summary>
    protected override void OnCreate()
    {
        
        mPlugin = PluginManager.Instance.Get<NGUIExt>();
        if (mPlugin != null)
        {
            mPlugin.SetEventHandler(this._HandlerUIEvent);
        }
        GameObject prefab = Resource.LoadUICommon("black_background");
        Looper.Assert(prefab != null , "默认黑底 black_background Prefab不存在 !!");
        if(prefab != null)
        {
            mBG = mPlugin.AddChild(prefab);
            mBGPanel = mBG.GetComponent<UIPanel>();
            mBG.transform.localScale = new Vector3(1, 1, 1);
            mBG.transform.localPosition = new Vector3(mBG.transform.localPosition.x, mBG.transform.localPosition.y, Mathf.Clamp(mBG.transform.localPosition.z, -2f, 2f));
            if(mBG != null)
            {
                UISprite sprite = mBG.GetComponent<UISprite>();
                BoxCollider collider = mBG.GetComponent<BoxCollider>();
                if(sprite!= null)
                {
                    Vector2 size = mPlugin.GetSize();
                    sprite.SetDimensions((int)size.x, (int)size.y);
                    collider.size = new Vector3(size.x, size.y, 0);
                }
            }
            
            mBG.SetActive(false);
        }
        //EasyTouch.On_SimpleTap += On_SimpleTap;
        
    }

    public void InitUI(GameObject ui)
    {
        try
        {
            LayerUtils.SetLayer(ui.transform, (int)LayerUtils.ELayerIndex.ui);
            ui.transform.parent = mPlugin.GetRoot().transform;
            
            switch(ui.name)
            {
                default:
                    ui.transform.localScale = new Vector3(1, 1, 1);
                    ui.transform.localPosition = Vector3.zero;
                    break;
            }
        }
        catch (Exception e)
        {
            Looper.LogException(e);
        }
      
    }
    /// <summary>
    /// 创建UI
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="name"></param>
    /// <returns></returns>
    public T CreateUI<T>(string name) where T: UI
    {
        UI  t = this.Get(name) as T;
        if(t == null)
        {
            t = _CreateUI<T>(name) as UI;
           try
           {
               t.OnCreate();
           }
           catch (Exception e)
           {
               Looper.LogException(e);
           }
           
           Put(name, t);
        }
        return t as T;
    }
    List<UI> mUIScene = new List<UI>();
    /// <summary>
    /// 创建一个不加入管理队列的UI,主要用于世界地图上关卡界面的创建。
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="name"></param>
    /// <returns></returns>
    public T CreateUIWithoutManager<T>(string name) where T : UI
    {
        T ui = _CreateUI<T>(name);
        try
        {
            ui.OnCreate();
        }
        catch (Exception e)
        {
            Looper.LogException(e);
        }
        mUIScene.Add(ui);
        return ui;
    }
	
    /// <summary>
    /// 销毁不加入队列的UI
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="ui"></param>
    public void CloseUIWithoutManager<T>(T ui) where T : UI
    {
        try
        {
            ui.OnClose();
            ui.Dispose();
        }
        catch (Exception e)
        {
            Looper.LogException(e);
        }
        mUIScene.Remove(ui);
    }
    /// <summary>
    /// 显示UI
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="name"></param>
    public T ShowUI<T>(string name) where T : UI
    {
        T t = CreateUI<T>(name);
        if(t != null)
        {
            t.Show(true);
        }
        return t;
    }
    /// <summary>
    /// 显示UI
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="name"></param>
    public T HideUI<T>(string name) where T : UI
    {
        T t = Get<T>(name);
        if (t != null)
        {
            t.Show(!true);

        }
        return t;
    }

    Stack<UI> mStack = new Stack<UI>();

    /// <summary>
    /// 清除栈
    /// </summary>
    public void StackClean()
    {
        mStack.Clear();
    }
    /// <summary>
    /// UI隐藏压栈
    /// </summary>
    /// <param name="t"></param>
    /// <returns></returns>
    public UI Push(UI t)
    {
        if (t != null)
        {
            t.Show(false);
            mStack.Push(t);
        }
        return t;
    }
    /// <summary>
    /// UI隐藏压栈
    /// </summary>
    /// <param name="t"></param>
    /// <returns></returns>
    public T Push<T>(string name) where T: UI
    {
        T t = Get<T>(name);
        if (t != null)
        {
            t.Show(false);
            mStack.Push(t);
        }
        return t;
    }
    /// <summary>
    /// UI显示出栈
    /// </summary>
    /// <param name="t"></param>
    /// <returns></returns>
    public UI Pop()
    {
        UI t = mStack.Pop();
        if (t != null)
        {
            t.Show(true);
        }
        return t;
    }


    /// <summary>
    /// 销毁UI
    /// </summary>
    /// <param name="name"></param>
    public void Distroy(string name)
    {
        UI ui = Get(name);
        if(ui != null)
        {
            ui.Dispose();
            Remove(name);
        }
    }

    public void Distroy<T>(string name) where T : UI
    {
        UI ui = Get<T>(name);
        if (ui != null)
        {
            ui.Dispose();
            Remove(name);
        }
    }
    public void DestroyAll(bool includeScene= false)
    {
        StackClean();
        Clear();
        if(includeScene)
        {
            foreach (UI ui in mUIScene)
            {
                ui.Dispose();
            }
            mUIScene.Clear();
        }

    }
    /// <summary>
    /// 隐藏其他UI
    /// </summary>
    /// <param name="name"></param>
    public void HideOtherUI(string name)
    {
        foreach (UI ui in Values)
        {
            if (ui.Name.Equals(name))
                continue;
            ui.Show(false);
        }
    }

    UI _GetLastShowUI(bool showBG = false,UI except=null)
    {
        UI lastShowUI = null;
        float lastShowTime = 0;
        foreach (UI ui in Values)
        {
            if (ui == except) continue;
            if (ui.IsShowing() && ui.LastShowTime > lastShowTime)
            {
                if (showBG)
                {
                    if (ui.IsShowBlackBG())
                    {
                        lastShowUI = ui;
                        lastShowTime = ui.LastShowTime;
                    }
                }else
                {
                    lastShowUI = ui;
                    lastShowTime = ui.LastShowTime;
                }
            }
        }
        return lastShowUI;
    }

    /// <summary>
    /// 隐藏最新显示的UI
    /// </summary>
    public void HideLastShow()
    {
        UI lastShowUI = _GetLastShowUI();
        if (lastShowUI != null)
        {
            lastShowUI.Show(false);
        }
    }
    /// <summary>
    /// 隐藏所有UI
    /// </summary>
    public void HideAllUI()
    {
        foreach(UI ui in Values)
        {
            if(ui != null)ui.Show(false);
        }
    }
    /// <summary>
    /// 获取UI跟面板
    /// </summary>
    /// <param name="obj"></param>
    /// <returns></returns>
     GameObject _GetRootPanelExt(GameObject obj)
    {
        Transform parent = obj.transform.parent;
        UIHolder uiHolder = null;// = root.GetComponent<UIService.UIHolder>();

        while (parent != null)
        {
            if (parent.gameObject.GetComponent<UICamera>() != null)
                break;

            UIHolder tempPanel = parent.GetComponent<UIHolder>();
            if (tempPanel != null)
            {
                uiHolder = tempPanel;
            }
            parent = parent.parent;
        }
        if (uiHolder == null)
        {
            return null;
        }
        return uiHolder.gameObject;
    }
    /// <summary>
    /// NGUI事件处理器
    /// </summary>
    /// <param name="eventNane">事件名称</param>
    /// <param name="sender">发送事件的控件</param>
    /// <param name="arg">事件参数</param>
    private void _HandlerUIEvent(string eventName, GameObject sender, object arg)
    {
        try
        {
            if(sender == mBG)
            {
                //MainLooper.LogError("mBG, eventName:" + eventName + "!!");
                if (eventName.Equals("OnClick"))
                {
                   
                }
                return;
            }
            GameObject root = _GetRootPanelExt(sender);//mPlugin.GetRootPanel(sender);
            if (root == null) return;
            UI ui = Get(root.name);
            if(ui == null)
            {
                UIHolder uiHolder = root.GetComponent<UIHolder>();
                if( uiHolder!= null) ui = uiHolder.ui;
            }
            if (ui == null)
            {
                Debug.LogWarning("UI:" + root.name + " not match prefab's name " + sender.name);
                return;
            }

            if (eventName.Equals("OnClick"))
            {
                ui.OnClick(sender);
            }
            else if (eventName.Equals("OnPress"))
            {
                ui.OnPress(sender, (bool)arg);
            }
            else if (eventName.Equals("OnDrag"))
            {
                ui.OnDrag(sender, (Vector2)arg);
            }
            else if (eventName.Equals("OnDrop"))
            {
                ui.OnDrap(sender, (GameObject)arg);
            }
            else if (eventName.Equals("OnSelect"))
            {
                ui.OnSelect(sender, (bool)arg);
            }
            else if (eventName.Equals("OnHover"))
            {
                ui.OnHover(sender, (bool)arg);
            }
            else if (eventName.Equals("OnTooltip"))
            {
                ui.OnTooltip(sender, (bool)arg);
            }
            else if (eventName.Equals("OnDoubleClick"))
            {
                ui.OnDoubleClick(sender);
            }
        }
        catch(Exception e)
        {
            Looper.LogException(e);
        }

    }
}

该类实现了UI的创建,也就是我们说的实例化操作,以及UI的显示,隐藏,动画等效果。在代码的最后使用了点击的回调函数避免

将脚本挂接到对象上。以上类的实现基本上把UI的大部分功能都实现出来了,可以直接拿过来使用。


2017-03-01 09:19:25 chepy 阅读数 920

KSFramework是一个Unity 5 Asset Bundle开发框架和工具集,专注于运行时热重载,使用了SLua作为脚本引擎。
https://github.com/mr-kelly/KSFramework

KSFramework是一个整合KEngine、SLua和一些开发组件组成的全功能Unity 5开发框架,适合有一定规模的团队使用。

热重载是KSFramework的开发重点——在不重启游戏的前提下,重载代码、配置表可立刻看到修改效果,最大限度的提升开发、调试的速度,并且在运营阶段方便的进行产品热更新。

看看Demo!

双击打开Assets/Game.unity场景,点击播放。

开始Game.unity后的日志输出

这时候KSFramework的默认Demo开始,做了这些事情:

  • 基础模块的启动
  • Lua模块的启动
  • 尝试读取并打印Excel表格GameConfig.xlsx内容
  • 加载UI界面Login
  • 执行UI界面Login的Lua脚本
  • Lua脚本绑定UI控件、设置UI控件
  • Lua脚本读取并打印Excel表格GameConfig.xlsx内容

总而言之,这个Demo囊括了KSFramework中的几个核心模块的使用:

  • KEngine中的Setting模块的使用
  • KEngine中的UI的资源加载
  • SLua脚本引擎与UI的联合

接下来,将模仿这个Demo,创建/加载一个新的UI界面、创建/读取一个新的配置表格。

尝试做一个公告界面Billboard

接下来,我们将创建一个UI公告牌(Billboard),使用Lua脚本,并从配置表读取公告内容。

创建UI资源

创建New Scene

KEngine-UI-Create UI创建UI布局

点击Create UI后,默认随机名字,把UI名字修改为Billboard

修改UI名字为Billboard,UI界面右边带有黄色UI标识

编辑一下UI场景,一个背景Image,两个Label

保存一下场景,保存到Assets/BundleEditing/UI/Billboard.unity

导出——打包AssetBundle,快捷键Ctrl+Alt+E

加载UI界面

好了,Billboard界面创建好了,也导出成了AssetBundle。
接下来,我们通过代码打开界面。

编辑Assets/Code/Game.cs

在OnFinishInitModules函数的末端,加上这样的一句:

// 开始加载我们的公告界面!
UIModule.Instance.OpenWindow("Billboard");

完成。 打开场景Assets/Game.unity,点击播放按钮:

我们的UI通过AssetBundle打开了,弹出提示找不到UI Lua脚本,接下来我们创建Lua脚本吧

[我们的UI通过AssetBundle打开了,弹出提示找不到UI Lua脚本,接下来我们创建Lua脚本吧。

创建Lua脚本

在目录Product/Lua/UI中新建一个lua文件

写一段Lua代码:UIBillboard的执行逻辑

local UIBase = import("KSFramework/UIBase")

local UIBillboard = {}
extends(UIBillboard, UIBase)

function UIBillboard:OnInit(controller)
    self.Controller = controller
    self.TitleLabel = self:GetUIText('Title')
    self.ContentLabel = self:GetUIText('Content')
end

function UIBillboard:OnOpen()
    self.TitleLabel.text = "This is a title"
    self.ContentLabel.text = "Here is content!"
end

return UIBillboard

这段lua中,创建了一个Table叫UIBillboard,这个table必须有OnInit(controller)函数。它通过代码设置了UI中的文字。

好了,接下来,我们要为策划准备配置表了。

创建配置表

打开Product/SettingSource目录,复制一份StringsTable.xlsx,并改名叫Billboard.xlsx吧

复制一份StringsTable.xlsx

用Excel打开我们新的Billboard.xlsx,编辑我们的公告。我们大概定一下需求,我们假设写入3条公告,每次打开公告随机显示其中一条。每个公告,我们给它一个英文ID,一列中文标题,一列中文内容。

Excel表修改如下:

增加公告内容

回到Unity,监测到Excel变动。点击OK。

上一步监测到变动,只编译Excel表,手动执行一些重新编译,并生成配置表代码

这时候,打开AppSettings.cs代码文件,我们可以发现,已经生成名叫BillboardSettings的类了

因为我们要在Lua使用BillboardSettings读取配置表,这里需要重新生成一下SLua的静态代码

接下来修改Lua代码,随机读取一条公告,并设置Content、Title

运行播放Game.unity,我们的公告界面完成了

公告界面完成了。我们创建了一个UI、写了C#和Lua代码加载它、然后创建了一张配置表,再从Lua读取配置表,设置UI的显示。

玩玩热重载

热重载Lua

接着我们刚才运行的Game.unity。 我们尝试一下对Lua热重载:在不重启游戏的情况,快速重载Lua代码,方便程序调试。

菜单KSFramework->UI->Reload+ReOpen:热重载Lua脚本,并重新打开目前处在打开状态的UI界面

我们可以从菜单执行热重载并重新打开UI界面,或者使用快捷键Ctrl+Alt+Shift+R。
由于我们的Lua脚本中,每次执行随机获取一条公告。因此可以看到公告内容在不停的变动着。

热重载的实现,主要依赖于每个lua脚本的最后一行——return Table;C#层在执行完Lua脚本后,会把这个返回的Table缓存起来,每次需要使用table时,直接从缓存拿;而热重载,实际就是把这个缓存table删掉,这样当需要用到这个table时,就会重新执行Lua文件,来获取Table,这样就达到了热重载得目的。

热重载Excel表格

我们保持运行刚刚的Game.unity,不要停止掉。这时候我们去修改Excel表格。

修改Excel表格

保存后,回到Unity,提示表格有改动。

发现表格有变动,点击OK编译表

从菜单中心一下重载配置表格吧

Ctrl+Alt+Shift+R刷新Lua

重载Lua,我们的新修改的配置表内容生效了。

至此,我们的Lua和配置表的改动,都可以在不重启、不重新转菊花的情况下快速修改。


KEngine策划指南:配置表格的编辑与编译

KEngine资源的打包、加载、调试监控

2017-01-25 12:43:16 jxw167 阅读数 2956

笔者介绍:姜雪伟,IT公司技术合伙人,IT高级讲师,CSDN社区专家,特邀编辑,畅销书作者,国家专利发明人;已出版书籍:《手把手教你架构3D游戏引擎》电子工业出版社和《Unity3D实战核心技术详解》电子工业出版社等。

CSDN视频网址:http://edu.csdn.net/lecturer/144

网上很多开发者跟我咨询关于GPU编程,其实GPU编程没有大家想象的那么难,它也是一种针对GPU显卡的编程脚本,在学习Shader编程之前,作为常识,我们要了解Shader编程开发语言有哪几种?

HLSL(High Level Shader Language)只能供微软的Direct3D以及XNA使用,微软提供的GPU编程不能跨平台使用,只能在Window平台上使用。

         CG(C for Graphics)由NVIDIA公司开发,CG程序可以根据运行时的需要或者事先编译成GPU汇编代码,CG语言也是最基本的语言,它的结构跟C语言类似,容易学习。读者可以学习《CG教程-可编程实时图形权威指南》。

        GLSL(OpenGL Shading Language)也称作Glslang语言,OpenGL和OpenGLES都是使用这种GPU编程语言实现跨平台的。

        ShaderLab语言是Unity配备了一个强大的着色器语言(ShaderLab),它的顶点和片段是CG/HLSL高级语言编写的。

介绍完语言,写过Shader的读者都知道,Shader不好调试,通常的做法都是在程序中通过注释代码的方式逐步排除Bug,在这里给读者推荐几个调试Shader的工具:FX Composer工具,Render Monkey工具,我调试Shader使用的是Render Monkey,当然也会使用排除法。下面给读者介绍ShaderLab的内容,先看一下事例代码:

Shader "SeparateSpecular" {
Properties {
	_Color ("Main Color", Color) = (1,1,1,1)
	_SpecColor ("Specular Color", Color) = (0.5, 0.5, 0.5, 1)
	_Shininess ("Shininess", Range (0.01, 1)) = 0.078125
	_ReflectColor ("Reflection Color", Color) = (1,1,1,0.5)
	_MainTex ("Base (RGB) Gloss (A)", 2D) = "white" {}
	_Cube ("Reflection Cubemap", Cube) = "_Skybox" { TexGen CubeReflect }
	_GrayTex("Gray(RGB)", 2D) = "white"{}


	_Cutoff ("Alpha cutoff", Range(0,1)) = 0.5
	
	_RimFaceColor ("Rim Face Color", Color) = (0.26,0.19,0.16,0.0)
	_RimHairColor ("Rim Hair Color", Color) = (0.26,0.19,0.16,0.0)


	_RimFacePower ("Rim Face Power", Range(0.5,8.0)) = 3.0
	_RimHairPower ("Rim Hair Power", Range(0.5,8.0)) = 3.0


	_GrayFaceColor("Face Color", Color) = (0.5, 0.5, 0.5, 1)
	_GrayHairColor("Hair Color", Color) = (0.5, 0.5, 0.5, 1)
}
SubShader {
	LOD 300
	Tags { "RenderType"="Opaque" }


CGPROGRAM
#pragma surface surf BlinnPhong
sampler2D _MainTex;
samplerCUBE _Cube;
sampler2D _GrayTex;


fixed4 _Color;
fixed4 _ReflectColor;
half _Shininess;
float _RimFacePower;
float _RimHairPower;
float4 _RimFaceColor;
float4 _RimHairColor;


float4 _GrayFaceColor;
float4 _GrayHairColor;


struct Input {
	float2 uv_MainTex;
	float3 worldRefl;
	float3 viewDir;
	float2 uv_GrayTex;
};


void surf (Input IN, inout SurfaceOutput o) {
	fixed4 tex = tex2D(_MainTex, IN.uv_MainTex)* _Color;
	fixed4 graytex = tex2D(_GrayTex, IN.uv_GrayTex);


	fixed4 c = tex;
	
	fixed4 grayface = graytex * _GrayFaceColor;
	
	fixed4 grayhair = (1.0 - graytex.rgba) * _GrayHairColor;


	o.Albedo =c.rgb * grayhair.rgb;
	o.Albedo += c.rgb * grayface.rgb;


	o.Gloss = tex.a;
	o.Specular = _Shininess;
	
	fixed4 reflcol = texCUBE (_Cube, IN.worldRefl);
	reflcol *= tex.a;
	half rim = 1.0 - saturate(dot (normalize(IN.viewDir), o.Normal));


	o.Emission = reflcol.rgb * _ReflectColor.rgb + _RimHairColor.rgb * pow (rim, _RimHairPower) * grayhair.rgb;
	o.Emission += reflcol.rgb * _ReflectColor.rgb + _RimFaceColor.rgb * pow (rim, _RimFacePower) * grayface.rgb;


	o.Alpha = reflcol.a * _ReflectColor.a;
}
ENDCG
}

看上面的脚本中,Properties有很多项,以属性中的
_Color ("Main Color", Color) = (1,1,1,1)

_Color 定义的是Shader内部的名字表示颜色,Main Color是指示器中显示的名字,Color表示的是属性类型,(1,1,1,1)表示的是颜色的默认值。再给读者看下图中对应的项:


下面把Properties中对应的定义给读者一一展现如下:

Properties定义的属性块,其中可以包含多个属性,其定义如下:
name(“display name”,Range(min,max))=number
定义浮点数属性,在检视器中可通过一个标注最大最小的滑条来修改。
name(“display name”,Color)=(number,number,number,number)
定义颜色属性
name("display name",2D) = "name"{options}
定义2D纹理属性
name("display name",Rect)="name"{options}
定义长方形(非2次方)纹理属性
name("display name", Cube)="name"{options}
定义立方贴图纹理属性
name("display name",Float)=number
定义浮点数属性
name("display name",Vector)=(number, number,number,number)
定义一个四元数的容器(相当于Vector4)属性
注意:对于Range和Float类型的属性只能是单精度值,对于Color和Vector类型的属性将包含4个由括号围住的的数描述。对于纹理(2D,Rect,Cube)缺省既可以是一个空字符串也可以是某个内置的缺省纹理:“white”,"black","gray","bump"。

纹理后买年的大括号内为纹理属性选项,为可选项。可用选项是:TexGen texgenmode纹理生成模式为对应贴图的自动纹理坐标生成模式,为ObjectLinear,EyeLiner,SphereMap,CubeReflect,CubeNormal之一,这些模式和OpenGL纹理生成模式相对应。注意如果使用自定义顶点程序,那么纹理生成将被忽略。

比如:_MainTex("Base", 2D) = "white"{TexGen EyeLiner}

LightmapMode 光照贴图模式,如果给出这个选项,纹理将能被渲染器的光线贴图属性所影响。纹理不能被使用在材质中,而是取自渲染器的设定。

以上是关于Properties中的说明,后面会给出subShader的说明。











2017-02-03 22:35:58 jxw167 阅读数 2588

笔者介绍:姜雪伟,IT公司技术合伙人,IT高级讲师,CSDN社区专家,特邀编辑,畅销书作者,国家专利发明人;已出版书籍:《手把手教你架构3D游戏引擎》电子工业出版社和《Unity3D实战核心技术详解》电子工业出版社等。

CSDN课程视频网址:http://edu.csdn.net/lecturer/144

网上有很多关于Shader的教程,我在这里就不给读者讲解基础知识了,我们直接讲重点,我会结合着C++底层代码一起讲解,帮助读者理解Unity3D引擎内部对于Shader加载的实现原理,下面就结合着Unity3D中的Shader的编写给读者解释,在Unity3D中的每个Shader中都有SubShader代码段。以下面的代码为例:

SubShader {

		Pass{
		// Dont write to the depth buffer
		ZWrite off

		// Set up alpha blending
		Blend SrcAlpha OneMinusSrcAlpha

		CGPROGRAM
		#pragma vertex vert
		#pragma fragment frag
		#include "UnityCG.cginc"

		sampler2D _MainTex;
		float4 _Color;

		struct v2f{
			float4 pos:SV_POSITION;
			float4 texcoord : TEXCOORD0;
		};

		v2f vert(appdata_base v)
		{
			v2f o;
			o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
			o.texcoord = v.texcoord;
			return o;
		}

		half4 frag(v2f i):COLOR0
		{
			half4 col = _Color * tex2D(_MainTex, i.texcoord.xy);
			return col;
		}

		ENDCG
		}
		
		pass
		{
		
		}
	} 
	
	SubShader
	{
		Pass
		{
		
		}
	}

SubShader中包含的代码段是核心程序,它中间还有Pass通道,Pass通道包含了定义的输出结构体和处理顶点函数和处理片段函数,在一个Shader中还可以包含多个SubShader,这些SubShader可以根据硬件自行适配,下面结合着C++代码给读者分析一下Unity3D中的Shader。先把DirectX中的Shader代码展示如下:

//--------------------------------------------------------------
// 全局变量
//--------------------------------------------------------------
float4x4 matWorldViewProj;	
float4x4 matWorld;	
float4   vecLightDir;
float4   vecEye;

float4   materialAmbient;
float4   materialDiffuse;
float4   materialSpecular;

//-------------------------------------------------------------
// 顶点渲染器输出结构
//-------------------------------------------------------------
struct VS_OUTPUT
{
   float4 Pos   : POSITION;
   float4 Color : COLOR;
};

//-------------------------------------------------------------
// 顶点渲染器
//-------------------------------------------------------------
VS_OUTPUT VS( float4 Pos: POSITION, float3 Normal: NORMAL, 
              uniform bool bEnableSelfShadow )
{
   VS_OUTPUT Out = (VS_OUTPUT) 0; 
   
   //顶点坐标变换
   Out.Pos = mul(Pos, matWorldViewProj);
   
   //单位化光照方向向量 
   float3 LightDir = normalize( vecLightDir );
   
   //计算观察方向
   float3 PosWorld = normalize( mul(Pos, matWorld) );
   float3 ViewDir  = normalize( vecEye - PosWorld );
   
   //计算法向量方向和漫反射强度
   float3 NormalWorld = normalize( mul(Normal, matWorld) );
   float4 diff        = saturate( dot(NormalWorld, LightDir) );
   
   //计算反射光方向( R = 2 * (N.L) * N - L)和镜面反射强度
   float3 Reflect = normalize( 2 * diff * NormalWorld - LightDir );
   float4 specu = pow( saturate(dot(Reflect, ViewDir)), 0.5 );
  
   //各种光的颜色
   float4 ambientColor  = { 0.1f, 0.0f, 0.0f, 1.0f};
   float4 diffuseColor  = { 1.0f, 0.0f, 0.0f, 1.0f};    
   float4 specularColor = { 1.0f, 0.0f, 0.0f, 1.0f};  
   
   //计算顶点颜色
   if(bEnableSelfShadow)  //启用自阴影
   {
       float shadow = saturate(4* diff);  
   
       //计算顶点颜色 = Ambient + Shadow * ( Diffuse + Specular )
       Out.Color = ambientColor * materialAmbient + shadow * 
                  (diffuseColor  * materialDiffuse * diff + 
                   specularColor * specu * materialSpecular); 
    }
    else //禁用自阴影
    {
        Out.Color = ambientColor  * materialAmbient + 
                    diffuseColor  * materialDiffuse * diff + 
                    specularColor * specu * materialSpecular; 
    }
   
   return Out;
}


//--------------------------------------------------------------
// 技术
//--------------------------------------------------------------
technique TShaderSelfShadow
{
    pass P0
    {
        VertexShader = compile vs_2_0 VS(true);
    }
}

technique TShaderNoSelfShadow
{
    pass P0
    {
        VertexShader = compile vs_2_0 VS(false);
    }
}

上述Shader定义了输出结构体这个跟Unity3D中的定义的结构体很类似,DirectX中的Shader定义了顶点渲染器VS函数:

VS_OUTPUT VS( float4 Pos: POSITION, float3 Normal: NORMAL, 
              uniform bool bEnableSelfShadow )
其中,VS函数是结合了顶点着色器和片段着色器一起实现的,另外,它还定义了technique,在technique中也有pass通道,这个technique跟SubShader类似,pass通道跟SubPass中的pass通道类似。因为Unity引擎内部我们是看不到的,那就以DirectX中的C++代码为例给读者解密一下引擎内部是如何实现的,C++完整代码如下所示:

/-----------------------------------------------------------------------------
// 全局变量
//-----------------------------------------------------------------------------
ID3DXFont*                 g_pFont = NULL;          //ID3DXFont字体对象
ID3DXSprite*               g_pTextSprite = NULL;    //ID3DXSprite文本精灵对象
bool                       g_bShowHelp = true;      //标识是否显示简单说明文本

CDXUTDialogResourceManager g_DialogResourceManager; //对话框资源管理器
CD3DSettingsDlg            g_SettingsDlg;           //Direct3D设备设置对话框
CDXUTDialog                g_HUD;                   //对话框
CDXUTDialog                g_SampleUI;              //对话框


LPDIRECT3DVERTEXBUFFER9    g_pVB     = NULL;       //顶点缓冲区
LPD3DXEFFECT               g_pEffect = NULL;       //效果
bool                       g_bEnableSelfShadow;    //标志是否启用预编译渲染器


//顶点结构和灵活顶点格式
struct CUSTOMVERTEX
{
    D3DXVECTOR3 position;   //位置
    D3DXVECTOR3 normal;     //法线
};
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_NORMAL)


//-----------------------------------------------------------------------------
// 控件ID
//-----------------------------------------------------------------------------
#define IDC_TOGGLEFULLSCREEN      1
#define IDC_TOGGLEREF             2
#define IDC_CHANGEDEVICE          3
#define IDC_ENABLE_SELFSHADOW     4


//-----------------------------------------------------------------------------
// Desc: 函数声明
//------------------------------------------------------------------------------
bool    CALLBACK IsDeviceAcceptable( D3DCAPS9* pCaps, D3DFORMAT AdapterFormat, D3DFORMAT BackBufferFormat, bool bWindowed, void* pUserContext );
bool    CALLBACK ModifyDeviceSettings( DXUTDeviceSettings* pDeviceSettings, const D3DCAPS9* pCaps, void* pUserContext );
HRESULT CALLBACK OnCreateDevice( IDirect3DDevice9* pd3dDevice, const D3DSURFACE_DESC* pBackBufferSurfaceDesc, void* pUserContext );
HRESULT CALLBACK OnResetDevice( IDirect3DDevice9* pd3dDevice, const D3DSURFACE_DESC* pBackBufferSurfaceDesc, void* pUserContext );
void    CALLBACK OnFrameMove( IDirect3DDevice9* pd3dDevice, double fTime, float fElapsedTime, void* pUserContext );
void    CALLBACK OnFrameRender( IDirect3DDevice9* pd3dDevice, double fTime, float fElapsedTime, void* pUserContext );
LRESULT CALLBACK MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, bool* pbNoFurtherProcessing, void* pUserContext );
void    CALLBACK KeyboardProc( UINT nChar, bool bKeyDown, bool bAltDown, void* pUserContext );
void    CALLBACK OnGUIEvent( UINT nEvent, int nControlID, CDXUTControl* pControl, void* pUserContext );
void    CALLBACK OnLostDevice( void* pUserContext );
void    CALLBACK OnDestroyDevice( void* pUserContext );

void    InitApp();
void    RenderText();


//-----------------------------------------------------------------------------
// Desc: 入口函数
//-----------------------------------------------------------------------------
INT WINAPI WinMain( HINSTANCE, HINSTANCE, LPSTR, int )
{
    //为Debug配置启用运行时内存检查功能
#if defined(DEBUG) | defined(_DEBUG)
    _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
#endif

    //设置回调函数
    DXUTSetCallbackDeviceCreated( OnCreateDevice );
    DXUTSetCallbackDeviceReset( OnResetDevice );
    DXUTSetCallbackDeviceLost( OnLostDevice );
    DXUTSetCallbackDeviceDestroyed( OnDestroyDevice );
    DXUTSetCallbackMsgProc( MsgProc );
    DXUTSetCallbackKeyboard( KeyboardProc );
    DXUTSetCallbackFrameRender( OnFrameRender );
    DXUTSetCallbackFrameMove( OnFrameMove );

	//应用程序相关的初始化
	InitApp();

	//初始化DXUT, 创建窗口, 创建Direct3D设备对象
	DXUTInit( true, true, true );
	DXUTSetCursorSettings( true, true );
	DXUTCreateWindow( L"HLSLSelfShadowing" );
	DXUTCreateDevice( D3DADAPTER_DEFAULT, true, 640, 480, 
		IsDeviceAcceptable, ModifyDeviceSettings );

	//进入消息循环和场景渲染
	DXUTMainLoop();

	//在此进行应用程序相关的清除工作

	return DXUTGetExitCode();
}


//-----------------------------------------------------------------------------
// Desc: 应用程序相关初始化
//-----------------------------------------------------------------------------
void InitApp()
{
    //初始化对话框
    g_SettingsDlg.Init( &g_DialogResourceManager );
    g_HUD.Init( &g_DialogResourceManager );
    g_SampleUI.Init( &g_DialogResourceManager );

	//为g_HUD对话框设置消息处理函数,添加控件
    g_HUD.SetCallback( OnGUIEvent ); int iY = 10; 
    g_HUD.AddButton( IDC_TOGGLEFULLSCREEN, L"Toggle full screen", 35, iY, 125, 22 );
    g_HUD.AddButton( IDC_TOGGLEREF, L"Toggle REF (F3)", 35, iY += 24, 125, 22 );
    g_HUD.AddButton( IDC_CHANGEDEVICE, L"Change device (F2)", 35, iY += 24, 125, 22, VK_F2 );

	//为g_SampleUI对话框设置消息处理函数,添加控件
    g_SampleUI.SetCallback( OnGUIEvent ); iY = 0; 
	g_bEnableSelfShadow = true;
    g_SampleUI.AddCheckBox( IDC_ENABLE_SELFSHADOW, L"Enable preshaders", 35, iY, 125, 22, true );
}


//-----------------------------------------------------------------------------
// Desc: 设备能力检查
//-----------------------------------------------------------------------------
bool CALLBACK IsDeviceAcceptable( D3DCAPS9* pCaps, D3DFORMAT AdapterFormat, 
                                  D3DFORMAT BackBufferFormat, bool bWindowed, 
								  void* pUserContext )
{
	//检查后台缓冲区格式是否支持Alpha混合等操作(post pixel blending operations)
    IDirect3D9* pD3D = DXUTGetD3DObject(); 
    if( FAILED( pD3D->CheckDeviceFormat( pCaps->AdapterOrdinal, pCaps->DeviceType,
                    AdapterFormat, D3DUSAGE_QUERY_POSTPIXELSHADER_BLENDING, 
                    D3DRTYPE_TEXTURE, BackBufferFormat ) ) )
        return false;

	//检查当前设备支持的顶点渲染器版本
	if( pCaps->VertexShaderVersion < D3DVS_VERSION(2,0) )
		return FALSE;

    return true;
}


//-----------------------------------------------------------------------------
// Desc: 修改Direct3D渲染设备设置
//-----------------------------------------------------------------------------
bool CALLBACK ModifyDeviceSettings( DXUTDeviceSettings* pDeviceSettings, 
								    const D3DCAPS9* pCaps, void* pUserContext )
{
    //如果不支持硬件顶点处理则使用软件顶点处理
    if( (pCaps->DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) == 0)
    {
        pDeviceSettings->BehaviorFlags = D3DCREATE_SOFTWARE_VERTEXPROCESSING;
    }

	//调试顶点渲染器需要参考设备或软件顶点处理
#ifdef DEBUG_VS
	if( pDeviceSettings->DeviceType != D3DDEVTYPE_REF )
	{
		pDeviceSettings->BehaviorFlags &= ~D3DCREATE_HARDWARE_VERTEXPROCESSING;
		pDeviceSettings->BehaviorFlags &= ~D3DCREATE_PUREDEVICE;                            
		pDeviceSettings->BehaviorFlags |= D3DCREATE_SOFTWARE_VERTEXPROCESSING;
	}
#endif

	//调试像素渲染器需要参考设备
#ifdef DEBUG_PS
	pDeviceSettings->DeviceType = D3DDEVTYPE_REF;
#endif

	//如果使用参考设备,则弹出警告对话框
    static bool s_bFirstTime = true;
    if( s_bFirstTime )
    {
        s_bFirstTime = false;
        if( pDeviceSettings->DeviceType == D3DDEVTYPE_REF )
            DXUTDisplaySwitchingToREFWarning();
    }

    return true;
}


//-----------------------------------------------------------------------------
// Desc: 在此创建管理内存资源对象
//-----------------------------------------------------------------------------
HRESULT CALLBACK OnCreateDevice( IDirect3DDevice9* pd3dDevice, 
								const D3DSURFACE_DESC* pBackBufferSurfaceDesc, 
								void* pUserContext )
{
    HRESULT hr;

    V_RETURN( g_DialogResourceManager.OnCreateDevice( pd3dDevice ) );
    V_RETURN( g_SettingsDlg.OnCreateDevice( pd3dDevice ) );
    
    //创建字体
    V_RETURN( D3DXCreateFont( pd3dDevice, 15, 0, FW_BOLD, 1, FALSE, DEFAULT_CHARSET, 
                         OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, 
                         L"Arial", &g_pFont ) );

	//创建顶点缓冲区
    V_RETURN( pd3dDevice->CreateVertexBuffer( 50*2*sizeof(CUSTOMVERTEX),
                                              0, D3DFVF_CUSTOMVERTEX,
                                              D3DPOOL_MANAGED, &g_pVB, NULL ));
    //填充顶点缓冲区
    CUSTOMVERTEX* pVertices;
    V_RETURN( g_pVB->Lock( 0, 0, (void**)&pVertices, 0 ) );
    for( DWORD i=0; i<50; i++ )
    {
        FLOAT theta = (2*D3DX_PI*i)/(50-1);
        pVertices[2*i+0].position = D3DXVECTOR3( sinf(theta),-1.0f, cosf(theta) );
        pVertices[2*i+0].normal   = D3DXVECTOR3( sinf(theta), 0.0f, cosf(theta) );
        pVertices[2*i+1].position = D3DXVECTOR3( sinf(theta), 1.0f, cosf(theta) );
        pVertices[2*i+1].normal   = D3DXVECTOR3( sinf(theta), 0.0f, cosf(theta) );
    }
    g_pVB->Unlock();

	//创建效果
    V_RETURN(D3DXCreateEffectFromFile( pd3dDevice, L"HLSLSelfShadowing.fx", NULL, NULL, 
		D3DXSHADER_DEBUG, NULL, &g_pEffect, NULL ));

    return S_OK;
}


//-----------------------------------------------------------------------------
// Desc: 在此创建默认内存类型资源对象
//-----------------------------------------------------------------------------
HRESULT CALLBACK OnResetDevice( IDirect3DDevice9* pd3dDevice, 
                                const D3DSURFACE_DESC* pBackBufferSurfaceDesc, 
								void* pUserContext )
{
    HRESULT hr;

    V_RETURN( g_DialogResourceManager.OnResetDevice() );
    V_RETURN( g_SettingsDlg.OnResetDevice() );

	//设置对话框位置和尺寸
    g_HUD.SetLocation( pBackBufferSurfaceDesc->Width-170, 0 );
    g_HUD.SetSize( 170, 170 );
    g_SampleUI.SetLocation( pBackBufferSurfaceDesc->Width-170, 
		                    pBackBufferSurfaceDesc->Height-50 );
    g_SampleUI.SetSize( 170, 50 );

	//恢复字体
    if( g_pFont )
        V_RETURN( g_pFont->OnResetDevice() );
   
	//创建ID3DXSprite接口对象
    V_RETURN( D3DXCreateSprite( pd3dDevice, &g_pTextSprite ) );

	//恢复效果对象
	if( g_pEffect )
        V_RETURN( g_pEffect->OnResetDevice() );

	//构造世界矩阵
	D3DXMATRIX matWorld;
	D3DXMatrixIdentity( &matWorld );

	//构造观察矩阵
	D3DXMATRIXA16 matView;
	D3DXVECTOR3 vEyePt( 0.0f, 3.0f,-5 );
    D3DXVECTOR3 vLookatPt( 0.0f, 0.0f, 0.0f );
    D3DXVECTOR3 vUpVec( 0.0f, 1.0f, 0.0f );
    D3DXMatrixLookAtLH( &matView, &vEyePt, &vLookatPt, &vUpVec );

	//构造投影矩阵
	D3DXMATRIXA16 matProj;
	float fAspectRatio = (float)pBackBufferSurfaceDesc->Width / pBackBufferSurfaceDesc->Height;
    D3DXMatrixPerspectiveFovLH( &matProj, D3DX_PI/4, fAspectRatio, 1.0f, 100.0f );

	//为效果设置组合变换矩阵和世界矩阵
	D3DXMATRIX mWorldViewProj = matWorld * matView * matProj;
	g_pEffect->SetMatrix( "matWorldViewProj", &mWorldViewProj );
	g_pEffect->SetMatrix( "matWorld", &matWorld );

	//为效果设置观察点位置
	D3DXVECTOR4 vecEyeInEffect = D3DXVECTOR4(vEyePt.x, vEyePt.y, vEyePt.z, 0);
	g_pEffect->SetVector( "vecEye", &vecEyeInEffect );


	//为效果设置材质反射系数
	D3DXVECTOR4 mtrlAmbientInEffect  = D3DXVECTOR4( 0.5f, 0.5f, 0.0f, 1.0f );
	D3DXVECTOR4 mtrlDiffuseInEffect  = D3DXVECTOR4( 1.0f, 1.0f, 0.0f, 1.0f );
	D3DXVECTOR4 mtrlSpecularInEffect = D3DXVECTOR4( 1.0f, 1.0f, 1.0f, 1.0f );
	g_pEffect->SetVector( "materialAmbient",  &mtrlAmbientInEffect );
	g_pEffect->SetVector( "materialDiffuse",  &mtrlDiffuseInEffect );
	g_pEffect->SetVector( "materialSpecular", &mtrlSpecularInEffect);

	//设置剔出模式,为不剔出任何面
    hr = pd3dDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE );

    return S_OK;
}


//-----------------------------------------------------------------------------
// Desc: 更新场景
//-----------------------------------------------------------------------------
void CALLBACK OnFrameMove( IDirect3DDevice9* pd3dDevice, double fTime, 
						   float fElapsedTime, void* pUserContext )
{
	//为效果设置光的方向 
	D3DXVECTOR4 lightDir = D3DXVECTOR4( cosf(timeGetTime()/350.0f), 1.0f,
                                        sinf(timeGetTime()/350.0f), 1.0f );
	g_pEffect->SetVector( "vecLightDir", &lightDir );
}


//-----------------------------------------------------------------------------
// Desc: 渲染场景
//-----------------------------------------------------------------------------
void CALLBACK OnFrameRender( IDirect3DDevice9* pd3dDevice, double fTime, 
							float fElapsedTime, void* pUserContext )
{
    HRESULT hr;
  
	//如果正在利用Direct3D设备设置对话框进行设置, 则不渲染场景
    if( g_SettingsDlg.IsActive() )
    {
        g_SettingsDlg.OnRender( fElapsedTime );
        return;
    }

    //清除后台颜色缓冲区和深度缓冲区
    V( pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 
		                 D3DCOLOR_ARGB(0, 45, 50, 170), 1.0f, 0) );

    //渲染场景
    if( SUCCEEDED( pd3dDevice->BeginScene() ) )
    {
		//使用效果渲染图形
		if(g_bEnableSelfShadow)
		{
			V(g_pEffect->SetTechnique( "TShaderSelfShadow" ));
		}
		else
		{
			V(g_pEffect->SetTechnique( "TShaderNoSelfShadow" ));
		}

		UINT nPasses;
		g_pEffect->Begin( &nPasses, 0 );
		for( UINT iPass = 0; iPass < nPasses; iPass ++ )
		{
			V(g_pEffect->BeginPass( iPass ));
			V(pd3dDevice->SetStreamSource( 0, g_pVB, 0, sizeof(CUSTOMVERTEX) ));
			V(pd3dDevice->SetFVF( D3DFVF_CUSTOMVERTEX ));
			V(pd3dDevice->DrawPrimitive( D3DPT_TRIANGLESTRIP, 0, 2*50-2 ));
			V(g_pEffect->EndPass());
		}
		g_pEffect->End(); 

		//渲染文本和控件
        DXUT_BeginPerfEvent( DXUT_PERFEVENTCOLOR, L"HUD / Stats" ); 
        RenderText();
        V( g_HUD.OnRender( fElapsedTime ) );
        V( g_SampleUI.OnRender( fElapsedTime ) );
        DXUT_EndPerfEvent();

        V( pd3dDevice->EndScene() );
    }
}


//-----------------------------------------------------------------------------
// Desc: 渲染文本
//-----------------------------------------------------------------------------
void RenderText()
{
    CDXUTTextHelper txtHelper( g_pFont, g_pTextSprite, 15 );

    //显示当前Direct3D设备状态和渲染帧速率
    txtHelper.Begin();
    txtHelper.SetInsertionPos( 5, 5 );
    txtHelper.SetForegroundColor( D3DXCOLOR( 1.0f, 1.0f, 0.0f, 1.0f ) );
    txtHelper.DrawTextLine( DXUTGetFrameStats(true) );
    txtHelper.DrawTextLine( DXUTGetDeviceStats() );

	//显示其他简要信息
    txtHelper.SetForegroundColor( D3DXCOLOR( 1.0f, 1.0f, 1.0f, 1.0f ) );
    txtHelper.DrawTextLine( L"通过HLSL编程实现自遮蔽阴影" );
    
    //显示简单帮助文本
    const D3DSURFACE_DESC* pd3dsdBackBuffer = DXUTGetBackBufferSurfaceDesc();
    if( g_bShowHelp )
    {
        txtHelper.SetInsertionPos( 10, pd3dsdBackBuffer->Height-15*6 );
        txtHelper.SetForegroundColor( D3DXCOLOR( 1.0f, 0.75f, 0.0f, 1.0f ) );
        txtHelper.DrawTextLine( L"Controls (F1 to hide):" );

        txtHelper.SetInsertionPos( 40, pd3dsdBackBuffer->Height-15*5 );
        txtHelper.DrawTextLine( L"Quit: ESC" );
    }
    else
    {
        txtHelper.SetInsertionPos( 10, pd3dsdBackBuffer->Height-15*2 );
        txtHelper.SetForegroundColor( D3DXCOLOR( 1.0f, 1.0f, 1.0f, 1.0f ) );
        txtHelper.DrawTextLine( L"Press F1 for help" );
    }
    txtHelper.End();
}


//-----------------------------------------------------------------------------
// Desc: 消息处理
//-----------------------------------------------------------------------------
LRESULT CALLBACK MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, 
						 bool* pbNoFurtherProcessing, void* pUserContext )
{
    *pbNoFurtherProcessing = g_DialogResourceManager.MsgProc( hWnd, uMsg, wParam, lParam );
    if( *pbNoFurtherProcessing )
        return 0;

    if( g_SettingsDlg.IsActive() )
    {
        g_SettingsDlg.MsgProc( hWnd, uMsg, wParam, lParam );
        return 0;
    }

    *pbNoFurtherProcessing = g_HUD.MsgProc( hWnd, uMsg, wParam, lParam );
    if( *pbNoFurtherProcessing )
        return 0;
   
	*pbNoFurtherProcessing = g_SampleUI.MsgProc( hWnd, uMsg, wParam, lParam );
    if( *pbNoFurtherProcessing )
        return 0;

    return 0;
}


//-----------------------------------------------------------------------------
// Desc: 键盘消息处理
//-----------------------------------------------------------------------------
void CALLBACK KeyboardProc( UINT nChar, bool bKeyDown, bool bAltDown, void* pUserContext )
{
    if( bKeyDown )
    {
        switch( nChar )
        {
            case VK_F1: g_bShowHelp = !g_bShowHelp; break;
        }
    }
}


//-----------------------------------------------------------------------------
// Desc: 处理各种控件消息
//-----------------------------------------------------------------------------
void CALLBACK OnGUIEvent( UINT nEvent, int nControlID, CDXUTControl* pControl, 
						 void* pUserContext )
{
    switch( nControlID )
    {
        case IDC_TOGGLEFULLSCREEN:
			DXUTToggleFullScreen(); 
			break;

        case IDC_TOGGLEREF:
			DXUTToggleREF(); 
			break;

        case IDC_CHANGEDEVICE:
			g_SettingsDlg.SetActive( !g_SettingsDlg.IsActive() ); 
			break;

		case IDC_ENABLE_SELFSHADOW: 
            g_bEnableSelfShadow = g_SampleUI.GetCheckBox( IDC_ENABLE_SELFSHADOW )->GetChecked(); 
            break;
    }
}


//-----------------------------------------------------------------------------
// Desc: 释放在OnResetDevice()中创建的资源
//-----------------------------------------------------------------------------
void CALLBACK OnLostDevice( void* pUserContext )
{
    g_DialogResourceManager.OnLostDevice();
    g_SettingsDlg.OnLostDevice();
    if( g_pFont )
        g_pFont->OnLostDevice();
    SAFE_RELEASE( g_pTextSprite );

	if( g_pEffect )
		g_pEffect->OnLostDevice();
}


//------------------------------------------------------------------------------
// Desc: 释放在OnCreateDevice()中创建的资源
//------------------------------------------------------------------------------
void CALLBACK OnDestroyDevice( void* pUserContext )
{
    g_DialogResourceManager.OnDestroyDevice();
    g_SettingsDlg.OnDestroyDevice();
    SAFE_RELEASE( g_pFont );

	SAFE_RELEASE(g_pVB);
	SAFE_RELEASE(g_pEffect);
}

C++代码可以直接在程序中运行,但是需要安装Direct SDK,代码中都有注释,这里就不一一讲解了,如果对此还不清楚,自己可以查看一下Direct SDK文档。在这里只把重点代码解释一下,首先是加载Shader脚本文件到内存中,代码函数如下所示:

	//创建效果
    V_RETURN(D3DXCreateEffectFromFile( pd3dDevice, L"HLSLSelfShadowing.fx", NULL, NULL, 
		D3DXSHADER_DEBUG, NULL, &g_pEffect, NULL ));

接下来开始对Shader文件中的矩阵和声明的变量进行赋值操作:

	//构造投影矩阵
	D3DXMATRIXA16 matProj;
	float fAspectRatio = (float)pBackBufferSurfaceDesc->Width / pBackBufferSurfaceDesc->Height;
    D3DXMatrixPerspectiveFovLH( &matProj, D3DX_PI/4, fAspectRatio, 1.0f, 100.0f );

	//为效果设置组合变换矩阵和世界矩阵
	D3DXMATRIX mWorldViewProj = matWorld * matView * matProj;
	g_pEffect->SetMatrix( "matWorldViewProj", &mWorldViewProj );
	g_pEffect->SetMatrix( "matWorld", &matWorld );

	//为效果设置观察点位置
	D3DXVECTOR4 vecEyeInEffect = D3DXVECTOR4(vEyePt.x, vEyePt.y, vEyePt.z, 0);
	g_pEffect->SetVector( "vecEye", &vecEyeInEffect );


	//为效果设置材质反射系数
	D3DXVECTOR4 mtrlAmbientInEffect  = D3DXVECTOR4( 0.5f, 0.5f, 0.0f, 1.0f );
	D3DXVECTOR4 mtrlDiffuseInEffect  = D3DXVECTOR4( 1.0f, 1.0f, 0.0f, 1.0f );
	D3DXVECTOR4 mtrlSpecularInEffect = D3DXVECTOR4( 1.0f, 1.0f, 1.0f, 1.0f );
	g_pEffect->SetVector( "materialAmbient",  &mtrlAmbientInEffect );
	g_pEffect->SetVector( "materialDiffuse",  &mtrlDiffuseInEffect );
	g_pEffect->SetVector( "materialSpecular", &mtrlSpecularInEffect);

这些参数是需要传到shader脚本文件中的,由于Unity3D它有自己的UnityCG库,它是已经实现了直接调用接口即可。

在Direct3D中通过调用函数SetTechnique执行Technique技术,也就是执行GPU渲染,代码段如下所示:

  //渲染场景
    if( SUCCEEDED( pd3dDevice->BeginScene() ) )
    {
		//使用效果渲染图形
		if(g_bEnableSelfShadow)
		{
			V(g_pEffect->SetTechnique( "TShaderSelfShadow" ));
		}
		else
		{
			V(g_pEffect->SetTechnique( "TShaderNoSelfShadow" ));
		}

		UINT nPasses;
		g_pEffect->Begin( &nPasses, 0 );
		for( UINT iPass = 0; iPass < nPasses; iPass ++ )
		{
			V(g_pEffect->BeginPass( iPass ));
			V(pd3dDevice->SetStreamSource( 0, g_pVB, 0, sizeof(CUSTOMVERTEX) ));
			V(pd3dDevice->SetFVF( D3DFVF_CUSTOMVERTEX ));
			V(pd3dDevice->DrawPrimitive( D3DPT_TRIANGLESTRIP, 0, 2*50-2 ));
			V(g_pEffect->EndPass());
		}
		g_pEffect->End(); 

		//渲染文本和控件
        DXUT_BeginPerfEvent( DXUT_PERFEVENTCOLOR, L"HUD / Stats" ); 
        RenderText();
        V( g_HUD.OnRender( fElapsedTime ) );
        V( g_SampleUI.OnRender( fElapsedTime ) );
        DXUT_EndPerfEvent();

        V( pd3dDevice->EndScene() );
    }

其实,以上利用Direct3D的原理帮助读者理解Unity3D引擎内部实现原理,它们都是相通的,我在博客中也做了关于3D游戏引擎系列文章讲解,在这里也是为了帮助读者理解。文字写的比较少,但是代码都有注释希望读者能够理解。



Unity3D入门

阅读数 615

没有更多推荐了,返回首页