unity 第五期

原题1、什么是单例?举一个游戏中使用单列的例子。

答:单例,啊~那是一种内功心法~~
而且是行走江湖必备的一种最基本的内功心法。
说到单例,我们得要把后面两个字补全,也就是单例模式
单例模式是传说中的23种设计模式之一。

为什么DC老湿说其实内功心法呢,
我们来做横向比喻

游戏引擎是兵器、编程语言是外功、设计模式是内功
只会设计模式不会引擎不会编码的话,就像天龙八部里的虚竹,空有一身从无崖子那边继承过来的90年的内功,在成为逍遥派掌门之前,却无任何用武之地(用现在的话讲,叫做然并卵)
只会编程语言,其他两个都不会或者只会一点皮毛的话,那一辈子永远只是个小角色,永远只能写些小功能,处理不了大系统,或者说是一种高不成低不就的存在,如灌篮高手里木暮
只会引擎,其他两个都不会的话,可能比较的偏向美术、场景布局等,当然高级点的可以是建模师、动画师(3dsmax、maya)、特效师(ae、unity)


官方解释:
单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。这个类称为单例类。


如果希望在系统中某个类的对象只能存在一个,单例模式是最好的解决方案。
在游戏中使用单例的例子
比如音乐管理类,游戏主页的背景音乐、战斗界面的背景音乐、技能音效、UI控件触发音效等等,这些是不是把他们放在统一的一个类里面去管理,效率更高,然后别的系统模块需要什么音乐音效,直接调音乐管理类就好了。


和单身汪不一样,一个办公室里面单身汪可以有很多只   

但是一个游戏开发目里面,且只有一个音乐管理类   

名字随便起,可以叫也可叫MusicController,等等


显然归纳起来单例模式的要点有三个;
一是某各类只能有一个实例;
二是它必须自行创建这个事例;
三是它必须自行向整个系统提供这个实例。


一般c#实现单例
[C#]  纯文本查看  复制代码
public class AudioManager
{
    private static AudioManager instance;
    public static AudioManager Instance
    {
        get
        {
            if (instance == null)
                instance = new AudioManager();

            return instance;
        }
    }
}

注意:unity3D的编程方式就是组件化编程,如果脚本继承自MonoBehaviour,对其new的话会报警告
 
那么有人问:老湿,我在Unity里的某个类是继承自Monobehaviour的,非要让其实现单例,该怎么弄得
DC老湿:你可以在Awake()方法里面,写上Instance = this;
注意,这个Instance是要在方法外部定义好 public static XXX Instance; 
XXX是类名





原题2、Unity实现昼夜交替的方法或思路?

答:最最简单的做法,就是两个相机挂天空盒子的做法,通过一个Toggle 开关来切换白昼和黑夜。
如图

由于材质球的关系,gif图里的黑夜不是特别的明显。

[C#]  纯文本查看  复制代码
using UnityEngine;
using UnityEngine.UI;
using System.Collections;

public class DayOrNight : MonoBehaviour {

    public Toggle mToggle;
    public Camera DayCamera;
    public Camera NightCamera;

    void Start () 
    {
        if (mToggle != null) 
        {
            //isTag(mToggle.isOn);
            mToggle.onValueChanged.AddListener(isTag);
        }
      }

    private void isTag(bool on)
    {
        DayCamera.enabled = on;
        NightCamera.enabled = !on;
    }
}
这种做法实在过于简单粗暴,可以说是简单的极致
我们来看一个复杂做法的极致

知乎上碰巧最近有一篇文章刚好讨论了这个问题
http://www.zhihu.com/question/39560196

我看到有人说用Time of Day插件
下面是两张gif的动态图,加载会比较慢,不过绝对值得你等待一看。
如图
这一张是月亮与太阳刚好对立位置,也就是正午12点太阳在正上方,午夜24点月亮在正上方


这一张,大家注意在其检视面板里的我鼠标示意的下方Position选项,选了一个Realistic,也就是逼真
也就是晚上19点到20点的某个时间点,月亮是处于正上方的。

这个Position的选项让我非常好奇,想一探究竟,看看其代码是如何实现的。
代码非常的彪悍啊,完全看不懂这些个参数代表什么,不过妙在注解是超链接,点过去一看


我去,看上去非常高深的样子,标题是如何计算行星的位置
Paul Schlyter Stockholm Sweden该网页作者的信息
保罗斯里特,斯德哥尔摩天文台,瑞典


哦,原来这些个N、i、w。。。原来是天体行星的轨道参数

这是太阳和月亮的轨道参数,
哦,插件的作者在Unity的Asset Store上叫Mod Monkeys,
一个来自德国某个工作室的独立游戏开发者,
参考了瑞典首都斯德哥尔摩天文观测台的保罗斯里特的天体行星轨道计算
硬是把这些高深的天文学原理,用c#脚本翻译,并借助Unity实现出想要的功能,
6666
我想这个位独立开发者一定是为天文爱好者


说了这么多,截了这么多图,也装了这么多腔(bi)。也无非就是和大伙介绍介绍人家老外做的插件。至少比我那粗暴的强行交换相机换天空盒要高大上多了。

一开始看到在Asset Store上卖60刀,以为就几个漂亮的天空盒,加点shader就卖这么贵,一定是疯了。细看才知道原来被后应用着天文学。

而且其文档清晰详细    ,从实现原理,到代码详解都有,还算是个比较良心的插件作者。
有时间研究研究,一定会获益颇多的。

我把这个插件分享给大家~
  Time of Day - Dynamic Sky Dome.zip (4.2 MB, 下载次数: 157) 

另外一些别的做法,知乎上面也有讲到什么通过旋转平型光,
或是smooth平滑改变环境光亮度
亦或是自己写shader,实现光影反射等绚丽效果
由于篇幅的原因这里也就不多啰嗦了。


及时反馈【1】:网名“贼子哪里走”同学问的兼容性的问题,是的,我本人使用u4.x版本导入的该插件,没问题。由于u5.x改了很多的API,而Time of Day这个插件我是在某牛上下的,还只是2.3.1版本,这个版本确实不支持u5.x
这样,我上网再找找,最新版的Time of Day应该是能支持u5.x的
 
及时反馈【1-1】
经过一番搜索,不负众望,我还是算比较容易的在网上找到了Time of Day 插件3.0版,
  ,这个版本u5.x就可以用了
下面分享给大家,这个插件的作者确实非常良心,文档说明、版本号都相当清晰明了~
  Time of Day Dynamic Sky Dome 3.0.rar (7.48 MB, 下载次数: 103) 




原题3、塔防游戏中(保卫萝卜),底图的路劲信息如何存储?让怪物沿着路劲走的方法是?

答:博客园的这篇文章http://www.cnblogs.com/hll2008/p/4235714.html
写的还是非常详细的,项目虽然是cocos2d - x3.x
但是思路都是一样。
如图   

底图路进店信息如何存储,一张图一个ID,然后把路径点信息保存到xml文件里加在图的,根据id去xml取路径点信息,就ok了。
至于让怪沿着路径点走的方法,就是坐标点减坐标点得到其位移向量。再给其一个速度,再用距离判断是否开始计算移动到下个点。

至于向量计算,我给个网址分享给大家http://blog.gamerisker.com/archives/347.html,这里描述的相当详细。





原题4、《英雄联盟》中,带位移的技能能否穿越墙壁如何来判定?如闪现过墙/撞墙(以程序角度)

答:真把我当无所不会吗?
先说如果这个在客户端来判断的话,可以用触发器,如图    ,这个是Unity官方例子Angrybots的编辑器界面,你能清楚的看到触发器的范围,然后你可以通过触发器来判断实现是否穿墙还是撞墙。详细链接地址。触发器实现的代码就不用我多说了吧,网上搜搜都有的。

但是LOL毕竟是个强联网的MOBA游戏,
首先申明我没有参与制作过moba类游戏,所以以下的观点,也只是我个人粗浅的看法而已。如有说的不对的地方,还望不吝赐教
强联网的MOBA游戏,其实你的鼠标每点一下都是在跟服务器交互信息,服务器判断你是否可以进行接下来的操作,然后把数据返回客户端,再呈现在我们玩家眼前的,由于过程可能也就几百毫秒,所以在网络情况好的前提下,是感觉不出来。
否则主要逻辑全在客户端的话,那就外挂满天飞了,什么一键五杀,1v9,出门装就无尽之刃什么的妥妥的有。
那如果在服务端做判断、做验证的话,服务端肯定应该有存储整张地图信息,并随着游戏的发展实时更新地图信息,包括英雄当前所在位置信息,
通过地图信息和英雄位置信息,这两点可以计算出英雄离墙的距离。
进而通过距离(Unity的c#封装的代码Vecter3.Distance(英雄位置,墙体位置),服务端的选择的语言以及封装的代码那就有各式各样了)、
当前鼠标位置(屏幕坐标到世界坐标的转换)、
墙体薄厚(tag标识)
这三条来判断是可以穿墙还是撞墙。





5、继承和接口的用法和区别?

答:这个问题的表述就是一个门外汉的表述,应该这么问:
类的继承与接口的继承,各自的用法和区别
(或者像网上说的,实现接口和类继承的区别)

这样问才明了。

为什么这么说呢,因为这两个从表面形式上看上去差不多,都是在类名后面冒号然后是基类或者接口
虽然看上去相似,但二者的区别可大不一样

接口定义了一份契约,实现了接口的类或结构就意味着遵守接口定义的契约,
接口只允许包含方法、属性、索引器和时间的签名,只允许包含签名;
不允许包含实现接口,不含常量、字段、运算符、实例构造函数、析构造函数、以及任何静态成员。

标志:接口,interface  IA { ...}(一般接口命名虽然没有强制要求,但是大家约定俗成一般都会在前面加个I) ,class B: IA(然后这个B要实现IA里面定义的方法)
接口的继承:如果你觉得契约还是印象不深刻,那你可以把其想象成卖身契。

标志:类, class A{ ...}
classB: A
类的继承:B继承了A之后呢,就可以直接调用A里的公共属性或者方法,还可以重写A(父类)里的虚方法等。

注意一点:c# 只能继承一个类,但可以继承多个接口





6、如何控制子弹发射频率,技能冷却时间(需示 代码)

答:那我就用DFGUI,做个栗子吧~
又是gif图,请耐心等待加载
如图

条件简陋,就用背包图代替子弹发射器,用蓝色箭头代替子弹
然后底下的技能图,我是直接拿的DFGUI的第一个官方的例子里的部分预设拷贝过来的。

EmissionManager发射器管理类,其实干的事情呢,就是先获取全部技能槽信息,然后技能槽绑定点击事件
点击事件里的判断什么时候可以去实例化子弹并发射,什么时候是做cd冷却时间的表现等。
[C#]  纯文本查看  复制代码
using UnityEngine;
using System.Collections;

public class EmissionManager : MonoBehaviour {

    public dfSprite ArrowTemp;
    public SpellSlot[] slots;

    //之所public出来是方便我在检视面板查看信息的,正常来说应该是private
    public int CrtClickIndex = 0;

        void Start ()
    {
        if (ArrowTemp != null)
        {
            ArrowTemp.IsVisible = false;
        }

        for (int i = 0; i < slots.Length; i++)
        {
            slots[i].GetComponent<dfControl>().Click += OnClickSpell;
        }
        }

    //技能槽点击事件
    void OnClickSpell(dfControl control, dfMouseEventArgs args)
    {
        if (!args.Used)
        {
            var slot = control.GetComponent<SpellSlot>();
            if (slot.Spell != string.Empty)
            {
                if (slot.IsSpellActive)
                    return;

                #region 有bug,当不停点击多个技能槽的时候,冷却时间就混乱了,~( TロT)σ 可能当时作者写该算法的时候没有考虑那么多
                //var spell = SpellDefinition.FindByName(slot.Spell);
                //slot.onSpellActivated(spell);
                #endregion

                #region 所以就只好自己实现咯 ╮(╯▽╰)╭
                var cdSprite = control.Find("CoolDown") as dfSprite;

                dfTweenFloat cdTween = slot.GetComponent<dfTweenFloat>();
                if (cdTween == null)
                {
                    //纯粹只是为了区别不同技能槽的cd时间,正规项目还是统一读配表的
                    var timeLenght = slot.SlotNumber * 0.2f;

                    cdTween = addCDTween(cdSprite.gameObject, timeLenght);
                    cdTween.TweenCompleted -= cdTweenCompleted;
                    cdTween.TweenCompleted += cdTweenCompleted;
                }

                //不同的技能槽,且不在技能冷却时间
                if (CrtClickIndex != slot.SlotNumber && !slot.IsSpellActive)
                {
                    //记录当前点中第几个技能槽的信息
                    CrtClickIndex = slot.SlotNumber;

                    slot.IsSpellActive = true;
                    cdSprite.IsVisible = true;

                    cdTween.Reset();    //重置补间动画
                    cdTween.Play();     //走你(播动画)

                    var ArrowObj = Instantiate(ArrowTemp.gameObject, ArrowTemp.transform.position, Quaternion.identity) as GameObject;
                    ArrowObj.GetComponent<dfControl>().IsVisible = true;
                    ArrowObj.transform.parent = this.transform;
                    var am = ArrowObj.GetComponent<ArrowMove>();
                    am.IsMove = true;
                }
                //相同的技能槽,且不在技能冷却时间
                else if (CrtClickIndex == slot.SlotNumber && !slot.IsSpellActive)
                {
                    slot.IsSpellActive = true;
                    cdSprite.IsVisible = true;

                    cdTween.Reset();    //重置补间动画
                    cdTween.Play();     //走你(播动画)

                    var ArrowObj = Instantiate(ArrowTemp.gameObject, ArrowTemp.transform.position, Quaternion.identity) as GameObject;
                    ArrowObj.GetComponent<dfControl>().IsVisible = true;
                    ArrowObj.transform.parent = this.transform;
                    var am = ArrowObj.GetComponent<ArrowMove>();
                    am.IsMove = true;
                }
             
                #endregion
            }
            args.Use();        
        }
    }

    /// <summary>
    /// 补间动画事件监听回调
    /// </summary>
    /// <param name="sender"></param>
    private void cdTweenCompleted(dfTweenPlayableBase sender) 
    {
        var slot = sender.transform.parent.GetComponent<SpellSlot>();
        slot.IsSpellActive = false;

        var cdSprite = sender.GetComponent<dfSprite>();
        cdSprite.IsVisible = false;
    }

    /// <summary>
    /// 代码加入补间动画组件,并人为设定特定值。
    /// </summary>
    /// <param name="go">被添加组件的GameObject</param>
    /// <param name="timeLenght">补间动画播放时间</param>
    /// <returns></returns>
    private dfTweenFloat addCDTween(GameObject go, float timeLenght)
    {
        dfTweenFloat cdTween = go.AddComponent<dfTweenFloat>();
        if (cdTween)
        {
            cdTween.TweenName = "Fill";
            cdTween.Target = new dfComponentMemberInfo(); ;
            cdTween.Target.Component = go.GetComponent<dfSprite>();
            cdTween.Target.MemberName = "FillAmount";
            cdTween.Function = dfEasingType.Linear;

            #region  纯粹秀一秀自定义曲线的写法
            //Keyframe keyframe;
            //AnimationCurve curve = new AnimationCurve();
            //keyframe = new Keyframe(0.0f, 0.0f, 0.0f, 5.0f); curve.AddKey(keyframe);
            //keyframe = new Keyframe(1.0f, 1.0f, -2.0f, 0f); curve.AddKey(keyframe);
            //cdTween.AnimationCurve = curve;
            #endregion
            
            cdTween.LoopType = dfTweenLoopType.Once;
            cdTween.Length = timeLenght;
            cdTween.StartValue = 1f;
            cdTween.EndValue = 0f;
        }
        return cdTween;
    }


    void OnDestroy()
    {
        for (int i = 0; i < slots.Length; i++)
        {
            slots[i].GetComponent<dfControl>().Click -= OnClickSpell;
        }
    }
}
还有子弹(也就是蓝色箭头的)的脚本
[C#]  纯文本查看  复制代码
using UnityEngine;
using System.Collections;

public class ArrowMove : MonoBehaviour {

    public float m_speed = 10f;

    public float m_liveTime = 3f;

    public bool IsMove = false;

    private Transform mTrans;
    private dfControl mControl;

        void Start () {
        mTrans = this.transform;
        mControl = mTrans.GetComponent<dfControl>();
        }
        
        void Update () {
        if (IsMove)
        {
            m_liveTime -= Time.deltaTime;
            if (m_liveTime <= 0)
                Destroy(this.gameObject);
            

            mControl.Position = new Vector3(mControl.Position.x + m_speed, mControl.Position.y, mControl.Position.z);

            //mTrans.Translate(new Vector3(m_speed * Time.deltaTime, 0, 0));
        }
        }
}

这个工程例子是用Unity版本4.6.8p1
DFGUI插件版本是1.0.15

我今天就只拿dfgui做例子了,至于用ngui或者ugui,我实在懒得做了,而且篇幅也有限。
其实思路都是差不多的,只是不同gui写法略有不同罢了。
看同学们是否有强烈的需求,
如果想让DC老湿出NGUI或者UGUI的例子的话,
就在点评里面,叫我一声,看我答不答应 

哈哈,开玩笑的,为人师表的我哪敢不答应。
这样吧
如果点评或者送鲜花的人数超过100个
那就表示大伙真的有强烈的需求,
那我就再勤奋一下,用NGUI或者是UGUI做一个再把代码贴出来,
如果人数超过200个,我就把两个都做了,以及把整个工程文件一起分享给各位。

要问如何点评呢?就是在文章下面 
用红圈圈圈出来的 和 红框框框起来的地方,点一下,然后把阁下的心声告诉在下。
让我能第一时间懂你。





原题7、根据你的经验,说说Unity项目使用SVN进行版本控制时的一些注意事项?

答:说实话,其实在项目中svn用的最多的就是提交和更新两个功能,
其次是showlog,看看谁上传了哪些脚本或者资源,
然后还有revert
还有上传文件时产生冲突和如何解决冲突
创建分支等等

注意事项说的再多也只是纸上谈兵,今天左耳朵进,第二天就统统忘光光了,相类似的问题,只有你亲身经历过整个项目的开发流程团队合作,你才会印象深刻
所以DC老湿觉得,还是给你两个ppt,当作工具书,平时就随便浏览浏览~
等学酥们以后参与团队制作开发项目的时候,再好好用心体会。

  SVN版本控制入门.pptx (792.25 KB, 下载次数: 87)
  SVN版本控制提高.pptx (506.91 KB, 下载次数: 80)





原题8、Unity5中,使用旧教程中的代码rigidbody.XXX会报错,如何处理?(新手问题)

答:没关系,不要怕,我是学霸,最爱学渣。
题目都告诉你了:。。。使用“旧”教程。。。
老的不是很严谨的写法,终究会被替代,就像unity3.x版本到unity4.x的时候
原来可以独立对transform.postion.x进行赋值,然而到了4.x版本,就只用transform.postion = new Vector3(xx,xx,xx)

最重要的一点是遇到报错,遇到问题,先别慌,网络如此发达的时候,可以上网搜呀,别担心老湿陪着你一起上网找找看~
http://zhidao.baidu.com/link?url=xtONi79V2gsgKOFCWdUuM8ATs6TJKoj7Qx9QJUgddRiqLSaclldTXV53ux_lI2URZzbQzvdT-uHem4Py-Z2RgyHGtZiP29AUpMN0XhYm7mG
百度知道上面已经回答的相当清楚了。

应该也是为了规范,得要先定义rigidbody,如果这个rigidbody指的是自己的话呢,就用this.GetComponent<Rigidbody>()
当然这个this可写可不写,看个人喜欢咯。





原题9、Unity的GameObject.Find()是深度优先还是广度优先?

答: 讲解什么是深度优先、什么是广度优先
http://demo.netfoucs.com/a396901990/article/details/45028741
我们了解到深度优先是一种类似递归的搜索算法(就是一条路走到底,还找不到就返回走第二条路)
广度优先刚好相反,是只要有岔路都走个遍,走到下一个拐点要是还有岔路也都先探一探酱紫。

如图,
代码是通过GameObject.Find() 的方法,找到名为GameObject的物体,然后隐藏其子物体
如果是广度优先的话,那么该被隐藏应该是和A 平级的节点GameObjec 的自物体,

但执行下来,隐藏的确是D下面的GameObject的子物体

可见GameObject.Find() 应该是深度优先

网上的这篇文章还是蛮不错的,讲到了GameObject.Find早就不推荐使用啦,分享给大家
csdn 博客《Unity3D 游戏开发GameObject.Find()Transform.Findc 查找隐藏对象》
http://blog.csdn.net/teng_ontheway/article/details/47188141





原题10、分享下你学习Unity的经验/学习路线。有没有什么推荐的教程和书籍?

答: 这题就是吹牛逼的题目
其实最好的老师就是兴趣,最深的印象就是实践。

但是毕竟已经到了最后一道题了,我还是要装模作样的把这个结尾收的漂亮一点。
首尾呼应一下吧,
要推荐的书有《 Unity3D 手机游戏开发
第一题我们讲道理继承自monobehaviour的单例如何实现
这个其实在《 Unity3D 手机游戏开发》里的第二章,太空射击里就有讲到
这本是金玺曾,金老师写的书,由于版权问题,我就不方便给出pdf 了。
我是一个支持正版的正经男人
(其实网上都有,学酥们动动小手就能下载到。
但是看归看,别乱传阅,毕竟有盗版之嫌。)


如果你看完后觉得那本书对你有用的话,还是希望多支持支持金老师。
反正DC 老湿两年前是确确实实的买了金老师的实体书的。(为自己如此正义的行为点个赞)
这本书写得也还是蛮良心的。当时也算是我的启蒙教材了。


但是毕竟这本书出了有两三年了,就像第八题一样, 技术在不断的更新进步,有些东西也许不适用了。
金老师那个时候还是Unity4.x时代,现在都已经Unity5.x了。

那么哪些人是紧跟时代步伐的呢,
我想,那就是泰课网以及泰课网的论坛泰斗社区了

这里的版主勤劳、管理员细心,老师给力,给人感觉是实实在在做事。
管理员有泡菜就不说了,绝对细心给力,
还有出精品视频课程的老师们有海洋、肖红、siki、dzq等等,还好多我认识的不认识的,在成长中或者已经是大牛的。
当然最重要还是广大的热爱学习大伙们。
正因为每个人有意无意的努力,造就了这个欣欣向荣蓬勃发展的社区~

在这里,你应该是能学到好多东西,结识好多伙伴~
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值