使用unity3d需要注意到细节

欢迎来到unity学习unity培训unity企业培训教育专区,这里有很多U3D资源U3D培训视频U3D教程U3D常见问题U3D项目源码,【狗刨学习网】unity极致学院,致力于打造业内unity3d培训、学习第一品牌。

操作transform.localPosition的时候请小心

  移动GameObject是非常平常的一件事情,一下代码看起来很简单:

  transform.localPosition += new Vector3 ( 10.0f * Time.deltaTime, 0.0f, 0.0f );

  但是小心了,假设上面这个GameObject有一个parent, 并且这个parent GameObject的localScale是(2.0f,2.0f,2.0f)。你的GameObject将会移动20.0个单位/秒。因为该 GameObject的world position等于:

  Vector3 offset = new Vector3( my.localPosition.x * parent.lossyScale.x,

  my.localPosition.y * parent.lossyScale.y,

  my.localPosition.z * parent.lossyScale.z );

  Vector3 worldPosition = parent.position + parent.rotation * offset;

  换句话说,上面这种直接操作localPosition的方式是在没有考虑scale计算的时候进行的,为了解决这个问题,Unity3D提供了Translate函数,所以正确的做法应该是:

  transform.Translate ( 10.0f * Time.deltaTime, 0.0f, 0.0f );

  曝出在Inspector的变量同样的也能被Animation View Editor所使用

  有时候我们会想用Unity3D自带的Animation View Editor来做一些简单的动画操作。而Animation Editor不仅可以操作Unity3D自身的component,还可以操作我们自定义的MonoBehavior中的各个Property。所以加入 你有个float值需要用曲线操作,你可以简单的将它曝出到成可以serialize的类型,如:

  public float foobar = 1.0f;

  这样,这个变量不仅会在Inspector中出现,还可以在animation view中进行操作,生成AnimationClip供我们通过AnimationComponent调用。

  范例:

  public class TestCurve : MonoBehaviour {

  public float foobar = 0.0f;

  IEnumerator Start () {

  yield return new WaitForSeconds (2.0f);

  animation.Play("foobar_op");

  InvokeRepeating ( "LogFoobar", 0.0f, 0.2f );

  yield return new WaitForSeconds (animation["foobar_op"].length);

  CancelInvoke ("LogFoobar");

  }

  void LogFoobar () {

  Debug.Log("foobar = " + foobar);

  }

  }

  

  GetComopnentT 可以取父类类型

  Unity3D 允许我们对MonoBehavior做派生,所以你可能有以下代码:

  public class foo : MonoBehaviour {

  ...

  }

  public class bar : foo {

  ...

  }

  

假设我们现在有A,B两个GameObject, A包含foo, B包含bar, 当我们写

  foo comp1 = A.GetComponentfoo();

  bar comp2 = B.GetComponentbar();

  可以看到comp1, comp2都得到了应得的Component。那如果我们对B的操作改成:

  foo comp2 = B.GetComponentfoo();

  答案是comp2还是会返回bar Component并且转换为foo类型。你同样可以用向下转换得到有效变量:

  bar comp2_bar = comp2 as bar;

  合理利用GetComponentbase_type()可以让我们设计Component的时候耦合性更低。

  Invoke, yield 等函数会受 Time.timeScale 影响

  Unity3D提供了一个十分方便的调节时间的函数Time.timeScale。对于初次使用Unity3D的使用者,会误导性的认为Time.timeScale同样可以适用于游戏中的暂停(Pause)和开始(Resume)。所以很多人有习惯写:

  Time.timeScale = 0.0f

  对于游戏的暂停/开始,是游戏系统设计的一部分,而Time.timeScale不不是用于这个部分的操作。正确的做法应该是搜集需要暂停的脚本或 GameObject,通过设置他们的enabled = false 来停止他们的脚本活动或者通过特定函数来设置这些物件暂停时需要关闭那些操作。

  Time.timeScale 更多的是用于游戏中慢镜头的播放等操作,在服务器端主导的游戏中更应该避免此类操作。值得一提的是,Unity3D的许多时间相关的函数都和 timeScale挂钩,而timeScale = 0.0f将使这些函数或动画处于完全停止的状态,这也是为什么它不适合做暂停操作的主要原因。

  这里列出受timeScale影响的一些主要函数和Component:

  MonoBehaviour.Invoke()

  MonoBehaviour.InvokeRepeating()

  yield WaitForSeconds()

  GameObject.Destroy()

  Animation Component

  Time.time, Time.deltaTime

  Coroutine 和 IEnumerator的关系

  初写Unity3D C#脚本的时候,我们经常会犯的错误是调用Coroutine函数忘记使用StartCoroutine的方式。如:

  TestCoroutine.cs

  IEnumerator CoLog () {

  yield return new WaitForSeconds (2.0f);

  Debug.Log("hello foobar");

  }

  当我们用以下代码去调用上述函数:

  TestCoroutine  testCo = GetComponentTestCoroutine();

  testCo.CoLog ();

  testCo.StartCoroutine ( "CoLog" );

  那么testCo.CoLog()的调用将不会起任何作用。

  StartCoroutine, InvokeRepeating 和其调用者关联

  通常我们只在一份GameObject中去调用StartCoroutine或者InvokeRepeating, 我们写:

  StartCoroutine ( Foobar() );

  InvokeRepeating ( "Foobar", 0.0f, 0.1f );

  所以如果这个GameObject被disable或者destroy了,这些coroutine和invokes将会被取消。就好比我们手动调用:

  StopAllCoroutines ();

  CancelInvoke ();

  

这看上去很美妙,对于AI来说,这就像告诉一个NPC你已经死了,你自己的那些小动作就都听下来吧。

  

但是注意了,假如这样的代码用在了一个Manager类型的控制AI上,他有可能去控制其他的AI, 也有可能通过Invoke, Coroutine去做一些微线程的操作,这个时候就要明确StartCoroutine或者InvokeRepeating的调用者的设计。讨论之前我 们先要理解,StartCoroutine或InvokeRepeating的调用会在该MonoBehavior中开启一份Thread State, 并将需要操作的函数,变量以及计时器放入这份Stack中通过并在引擎每帧Update的最后,Renderer渲染之前统一做处理。所以如果这个
MonoBehavior被Destroy了,那么这份Thread State也就随之消失,那么所有他存储的调用也就失效了。

  

如果有两份GameObject A和B, 他们互相知道对方,假如A中通过StartCoroutine或InvokeRepeating去调用B的函数从而控制B,这个时候Thread State是存放在A里,当A被disable或者destroy了,这些可能需要一段时间的控制函数也就失效了,这个时候B明明还没死,也不会动了。更 好的做法是让在A的函数中通过B.StartCoroutine (  ) 让这份Thread State存放于B中。

  

// class TestCortouine

  public class TestCoroutine : MonoBehaviour {

  public IEnumerator CoLog ( string _name ) {

  Debug.Log(_name + " hello foobar 01");

  yield return new WaitForSeconds (2.0f);

  Debug.Log(_name + " hello foobar 02");

  }

  }

  // component attached on GameObject A

  public class A: MonoBehaviour {

  public GameObject B;

  void Start () {

  TestCoroutine  compB = B.GetComponentTestCoroutine();

  // GOOD, thread state in B

  // same as: compB.StartCoroutine ( "CoLog", "B" );

  compB.StartCoroutine ( compB.CoLog("B") );

  // BAD, thread state in A

  StartCoroutine ( compB.CoLog("A") );

  Debug.Log("Bye bye A, we‘ll miss you");

  Destroy(gameObject); // T_T I don‘t want to die...

  }

  }

  

以上代码,得到的结果将会是:

  B hello foobar 01

  A hello foobar 01

  Bye bye A, we‘ll miss you

  B hello foobar 02

  

如不需要Start, Update, LateUpdate函数,请去掉他们

  当你的脚本里没有任何Start, Update, LateUpdate函数的时候,Unity3D将不会将它们加入到他的Update List中,有利于脚本整体效率的提升。

  我们可以从这两个脚本中看到区别:

  Update_01.cs

  public class Update_01 : MonoBehaviour {

  void Start () {}

  void Update () {}

  }

  Update_02.cs

  public class Update_02 : MonoBehaviour {

  }

更多内容,请访问【狗刨学习网】unity极致学院
http://edu.gopedu.com

     声明:此篇文档时来自于【狗刨学习网】社区-unity极致学院,是网友自行发布的Unity3D学习文章,如果有什么内容侵犯了你的相关权益,请与官方沟通,我们会即时处理。

  

时间: 2024-10-25 06:41:53

使用unity3d需要注意到细节的相关文章

unity3D总结的一些细节,不注意有些要折腾非常多天!

1. 注意!!ps保存图片时,若保存为ps格式,若关闭最大兼容将会导致unity导入失败!(n天) 2.switch 推断NGUI popuplist传来的value字符串时一定要trim一下去掉空格! 3. 获取子物体时,方法不同得到的结果不同! foreach(Transform tr in transform) 返回的是下一级子物体,通俗点就是"儿子". Transform []  allModel = GetComponentsInChildren<Transform&g

Unity3d程序方面的细节及优化

关于Unity3d程序方面的细节及优化 (基于移动开发) 1.每次创建的脚本对于用不到的Start(),.Update()函数都可以删除掉,尤其后者,即使什么都不做也会在更新. 2.不要做复杂的数学运算,比如开方运算Mathf.Sqrt()等,当我们求两个对象的距离的时候,可以直接自己计算求开方根上一级的运算.求三角函数也算复杂运算. 3.如果使用的Mono编辑器,在注释的时候,尽量采用英文注释(防止中文乱码) 4.Unity的每个component都在更新,脚本也算组件,所以一个对象不同的脚本

Unity3D的RaycastHit小细节

昨天在做一个3D模型(碰撞盒是MeshCollider)的射线碰撞,需要获得碰撞位置的三角形索引值,我想到了一个方案: 1.用碰撞点(RaycastHit.point)和每个三角形的3个顶点做共面检测 . 2.如果检测共面,再用斜坐标系分解的方法判断碰撞点是否在该三角形内. 3.如果在三角形内,则记录下该三角形的索引值. 结果虽然勉强得到想要的结果,但是由于每次需要遍历的射线点都在30个以上,需要检测的模型三角形常常是4000+个,效率非常捉急. 于是翻了一下Unity Scripting AP

Unity3D学习笔记之八为场景添加细节(一)

这一系列教程以及素材均参考自人人素材翻译组出品的翻译教程<Unity游戏引擎的基础入门视频教程>,下载链接附在第二篇学习笔记中. 我花了30分钟做了一个中等大小的迷宫场景,不知道大家自己发挥,做的场景大小如何. 在完成场景之后,我们看到Hierarchy视图里面的东西已经满了,所以我们先来整理一下Hierarchy视图.创建一个空的游戏物体命名为Environment. 然后来到Hierarchy视图,先讲First Person Controller找到,挪到最上方,然后选中第一个物体,按住

Unity3D学习笔记九为场景添加细节(二)

上节为场景中添加了第一块带有碰撞器的石头,本节我们来利用Prefab,将场景细节都添加进去,并且做的更完善. 这一系列教程以及素材均参考自人人素材翻译组出品的翻译教程<Unity游戏引擎的基础入门视频教程>,下载链接附在第二篇学习笔记中. 继续来到那块已经成形的石头--Boulder旁边,我们可以制作很多石头的Prefab,这样对于丰富我们巨大的场景很有利,我们先来创建第一个石头的Prefab按下Control+D复制一块石头,按住坐标轴拖动出来,按下R调整到缩放工具,按住中心的白色立方体缩小

Unity3D细节整理:AssetBundle对应的各种格式文件的类型

我们打包AssetBundle后,Unity3D会根据文件的后缀名将文件转换为特定的类型对象存储起来,我们后期获取时需要根据这些类型取出打包的数据,这里记录下不同后缀文件打包后的类型. 文本格式 支持后缀:txt.xml: 打包后的类型:TextAsset,数据保存在TextAsset的text属性中. 二进制格式 支持后缀:bytes: 打包后的类型:TextAsset,数据保存在TextAsset的bytes属性中. 预制件格式 支持后缀:prefab: 打包后的类型:GameObject,

Unity3D学习笔记之七创建自己的游戏场景

到现在为止我们已经拥有了比较完备的Prefab,已经可以创建宏大的游戏场景,并以第一人称视角在场景中漫游了.这里给大家做个小的示范,建一个小场景大家在创建场景的时候需要自由发挥,做个尽量大的场景出来. 这一系列教程以及素材均参考自人人素材翻译组出品的翻译教程<Unity游戏引擎的基础入门视频教程>,下载链接附在第二篇学习笔记中. 我们以最初的添加了First Person Controller的PFB_Straight为整个场景的中心点来展开.我们先从Project中Prefabs文件夹拖出来

使用Unity3D的50个技巧:Unity3D最佳实践

刚开始学习Unity3D时间不长,在看各种资料.除了官方的手册以外,其他人的经验也是非常有益的.偶尔看到老外这篇文章,觉得还不错,于是翻译过来和大家共享.原文地址:http://devmag.org.za/2012/07/12/50-tips-for-working-with-unity-best-practices/,下面是译文. 欢迎转载,请注明出处:燕良@游戏开发.另外,欢迎各路高手加入我的QQ群:264656505,切磋交流技术. 关于这些技巧 这些技巧不可能适用于每个项目. 这些是基于

[Unity3D]引擎崩溃、异常、警告、BUG与提示总结及解决方法

1.U3D经常莫名奇妙崩溃.   一般是由于空异常造成的,多多检查自己的引用是否空指针. 2.编码切换警告提示.   警告提示:Some are Mac OS X (UNIX) and some are Windows. This might lead to incorrect line numbers in stacktraces and compiler errors. Many text editors can fix this using Convert Line Endings men