Unity小记二

前言:

​ 本篇文章是记录我在使用Unity中的过程中遇到并解决的问题和发现的知识点。所以文章比较杂碎。但我本来就是想这样才把这种文章归成随笔的。我使用的Unity版本为2019.4.6f1。



关于碰撞器

  1. 纵使是触发器也要有刚体才可以检测。所以碰撞检测必须要有一方有刚体。但是如果你将isKinematic设置为true。则脚本中的OnCollision型的函数都不再其作用,而OnTrigger类型的都可以。
  2. Unity模拟碰撞器,如果你不在指定旋转那么其旋转会参考世界坐标。等于说你放了一个欧姆角为Vector.Zero的碰撞器。
  3. RaycastHit中的normal属性可以得到射线接触面的法线。

关于Unity脚本中的this.enable

  1. 当脚本的this.enable == false时,这个脚本就不在运行了。所以FixUpdate也会受到this.enable的影响。

  2. unity中物体的transform.forward可以用transform.rotation*Vector.forward。其他的方向也是同理。

不同屏幕大小的摄像机适配问题

​ 最近我在做项目时就遇到了在特殊屏幕大小的手机中呈现我的项目时会看不到游戏角色。为了解决这个问题,我使用了两种方法

    1. 通过设置Camera.aspect可以保证手机屏幕比的适配,但是所得到的图像会有问题。
    2. 使用两个参照摄像机进行适配。首先我们要知道此游戏最低的适配屏幕比(设为small)和最大的适配屏幕比(设为big)。然后用两个摄像机去分别适配这两种屏幕。这样我们得到了两个不同的摄像机的位置。即最小适配屏幕比位置(设为near)和最大适配屏幕比位置(设为far)。那么在此之中的屏幕比的摄像机位置也一定在这中间。那么他们一定遵循比例递增。那么我们可以用他们自身的屏幕比(设为self)作为标准。则其比例可以为(self - small)/(big - small)。然后使用Vector.lerp(near,far,(self - small)/(big - small))得到这个位置。当然这个方法的问题也很明显,一些实在特殊的屏幕比仍然会出现这个问题。

​ 其实如果在不同的屏幕大小下,同一游戏的呈现效果本来就有些区别。如果这并不会对玩家的游戏体验造成太大的影响,那么就不需要去进行特殊处理。

如何得到一个物体相对于另一个物体的本地坐标

​ 我这里提供三种思路。我们先设定两个变量Transform parent和Transform child。我们要得到child在parent为父下的本地坐标。

  1. 直接将child设置为parent的孩子就可以直接得到我们要的结果代码入下:

    1
    2
    child.SetParent(parent);
    print(child.localPosition);

    这个方法很简单的得到了,我们想要的结果。如果你不想让child改变自己的父亲。那么你可以先存储下child的父亲得到结果后再变回来。代码入下:

    1
    2
    3
    4
    Transform tmp = child.parent;
    child.SetParent(parent);
    print(child.localPosition);
    child.SetParent(tmp);

    当然这个方法可能会在调用的时候出现问题。毕竟你打乱了原本的child的父子关系。

  2. 在parent的物体下设置子物体target(名字你可以任意取)并且得到此子物体的 Transform 设为 t。然后每次要得到我们结果时,我们只需让target世界坐标和child的世界坐标一致后返回此时target的本地坐标。代码入下:

    1
    2
    t.position = child.position;
    print(t.localPosition)

    这个方法十分直观,而代价也就是项目中多了一个空物体罢了。

  3. 数学。

    ​ 我们要得到的child在parent为父下的本地坐标。其转化为数学问题就是求在某一坐标系下的坐标在另一个坐标系的坐标。我们知道transform.position会返回其在世界坐标的位置而transform.forward、transform.right、transform.up可以得到这个物体的当前的正前、正左和正上。所以这个问题又变成了如何将世界坐标系下的点,转化为以parent作为原点并用其正前、正上和正左作为x、y、z的坐标系下的点。

    ​ 如果你考虑一步将其转换完全是十分困难的。我们可以将其分成两步去做:第一步 旋转、第二步 平移。

    ​ 旋转:我们使用向量点乘得到parent和child在旋转后坐标系下的映射,则我们就得到了parent和child在旋转后坐标系下的坐标pos,par。

    ​ 平移:在得到坐标系下的点位后,我们只需将pos - par就可以得到结果。

    ​ 具体代码入下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    Vector3 pos = new Vector3();
    pos.z = Vector3.Dot(childObj.position, parentObj.forward);
    pos.y = Vector3.Dot(childObj.position, parentObj.up);
    pos.x = Vector3.Dot(childObj.position, parentObj.right);

    Vector3 par = new Vector3();
    par.z = Vector3.Dot(parentObj.position, parentObj.forward);
    par.y = Vector3.Dot(parentObj.position, parentObj.up);
    par.x = Vector3.Dot(parentObj.position, parentObj.right);

    print(pos - par);

杂项

  1. Unity中无论哪里,两个权重一样的物体排在后面的物体会遮住前面的物体。(我想可以这么认为,前面的物体先被渲染了一次,后面的物体又再进行了一次渲染。那么他们之间重叠的部分自然显示后面渲染的物体。当然前提是他们权值相同,否则权重小的会被大的遮住。)
  2. LineRenderer中点位置是按照自身位置算。
  3. 当物体不在场景中显示时其协程也不会运作。
  4. 物体的标记进行要打开Gizmos才可以显示。
  5. Unity中刚体是可以在运行时增加的。
  6. LayerMask.NameToLayer返回的是Mask所在位a。如果你要启用这个mask那么就要使用1<<a才算是启用这个Mask。
  7. Vector.Angle只能支持到180度。如果两个向量之间的夹角超过180.比如220度。那么Vector.Angle会返回140度。

关于C#的一些知识

  1. C#中Action是无返回值的委托,Func是有返回值的委托其中Func<>中的<>号里的第一个类型记返回值类型。

  2. C#中List转Array有三种方法

    ① new List<>(Array)

    ② 使用System.linq提供的API——Array.ToList()

    ③ 使用List自带的AddRange()。


今日句读:人有时候真是奇怪,选择了的会后悔,放弃了的会遗憾,但完美只能是一种理想,而不可能是一种存在。山本文绪《蓝,另一种蓝》