想当年刚学Unity的时候,这个问题困扰了我好几天,因此来分享一下当初解决问题的思路.
我们通过Unity构建场景的过程中,经常发现一个现象,就是物体在拖进场景中后,我们会发现物体是反的,通过改变物体的rotation属性后,得到了正确的方向,可物体的坐标系又变得和默认坐标系(右上角系统自带的坐标系)不一样了,这样就给后续的脚本工作(通常是控制(Transform)脚本和生成(Intantiate)脚本)带来了困扰,因为脚本写作的过程中是按照世界坐标系来的,物体自己的坐标系和系统坐标系不一样了就会出现诸如按W向下 按S向上 按A向右 按D向左的现象.
其实,在游戏场景的创建过程中,不仅仅是物体的方向,一个游戏物体的坐标 方向 大小,都可能和我的的预期,或者是我们希望的样子有很大的区别,为了得到我们想要的样子,势必要改变该物体,那么这时为了不出现上一段出现的问题,我们就需要借助于Unity中的空物体,我们通过实例试验来得出结论(很多时候,为了记忆深刻,自己在Unity里试验一遍是个非常好的方法).
如上图,我通过互联网找到了一个飞机和一个炮弹的模型,我要实现一个飞机发射炮弹的demo, 要求是通过WSAD控制飞机的上下左右,按下空格键发射炮弹, 炮弹发射的位置是飞机前面的卡槽.
通过上图可以看出: 飞机和炮弹的比例很不协调 而且炮弹的方向也是反的 这里我们暂时不管这些问题 下面我们来一步一步地达到我们的要求
首先给飞机起名字叫player 炮弹起名字叫zidan
先给player写控制脚本
1 void float m_speed=3.0f; 2 3 void Update () { 4 //移动量 5 float movex = 0, movez = 0; 6 //前 7 if (Input .GetKey(KeyCode.W)) 8 { 9 movez += m_speed * Time.deltaTime; 10 } 11 //后 12 if (Input.GetKey(KeyCode.S)) 13 { 14 movez -= m_speed * Time.deltaTime; 15 } 16 //左 17 if (Input.GetKey(KeyCode.A)) 18 { 19 movex -= m_speed * Time.deltaTime; 20 } 21 //右 22 if (Input.GetKey(KeyCode.D)) 23 { 24 movex += m_speed * Time.deltaTime; 25 } 26 //变换 27 this.transform.Translate(new Vector3(movex, 0, movez)); 28 29 }
此时 把脚本挂在player上面 可以实现飞机的前后左右移动了.
然后 给炮弹写移动脚本
1 //子弹移动速度 2 public float m_speed = 3.0f; 3 //子弹生存时间 4 public float livetime = 3.0f; 5 6 7 void Update () { 8 //生存时间递减 9 livetime -= Time.deltaTime; 10 //生存时间结束后销毁物体 11 if (livetime <=0) 12 { 13 Destroy(this.gameObject); 14 } 15 //实现移动 16 this.transform.Translate(Vector3.forward * m_speed * Time.deltaTime); 17 18 }
挂在zidan上面 此时可以实现子弹基于自身坐标系向前(forward)的移动了
要把飞机和炮弹结合在一起 这里需要在player的脚本里加上Instantiate(生成/实例化)命令 并且把zidan拖到project面板里的prefabs文件夹里形成预制体
1 //移动速度 2 public float m_speed = 3.0f; 3 //生成的子弹 4 public Transform playerRocket; 5 //子弹生成速率 6 public float m_rate = 1.0f; 7 8 //子弹生成函数 9 void Fire() 10 { 11 Instantiate(playerRocket,this.transform.position,Quaternion.identity); 12 } 13 // Use this for initialization 14 void Start () 15 16 { 17 18 //按空格键发射炮弹 19 20 if (Input.GetKey(KeyCode.Space)) 21 { 22 InvokeRepeating("Fire", 1, m_rate); 23 } 24 25 } 26 27 // Update is called once per frame 28 void Update () { 29 //移动量 30 float movex = 0, movez = 0; 31 //前 32 if (Input .GetKey(KeyCode.W)) 33 { 34 movez += m_speed * Time.deltaTime; 35 } 36 //后 37 if (Input.GetKey(KeyCode.S)) 38 { 39 movez -= m_speed * Time.deltaTime; 40 } 41 //左 42 if (Input.GetKey(KeyCode.A)) 43 { 44 movex -= m_speed * Time.deltaTime; 45 } 46 //右 47 if (Input.GetKey(KeyCode.D)) 48 { 49 movex += m_speed * Time.deltaTime; 50 } 51 //变换 52 this.transform.Translate(new Vector3(movex, 0, movez)); 53 54 }
我们发现这时是这个样子的 炮弹偏大而且方向是反的
这个问题很好解决 我们把zidan的预制体调小到了合适的大小 方向也通过改rotation调整到了合适的方向 再次运行 发现是这个样子的
炮弹从飞机的后方发射出来了 为什么呢 因为此时炮弹自身的坐标的向前(forward)就是飞机的后方 脚本是没有错了 错就错在子弹的朝向和它自身坐标的forward的朝向不一致
怎么解决这个问题呢 这就需要空物体了 给zidan创建一个空物体 把子弹拖进去 给空物体而不是子弹挂脚本 这时你子弹怎么变换已经与空物体无关了 因为子弹是空物体的子类 影响不到空物体 于是我们把外面包裹着空物体的子弹做成预制体并且调好大小方向和飞机关联 再次运行
实现目标
总结,创建场景过程中,如果遇到模型和自己预期的不一致的时候,通过为模型创建父级空物体并把脚本挂到空物体上,可以实现脚本内容和模型transform的分离 从而更加灵活的调节我们需要的效果