Unity2D研究院之精灵Sprite之间的碰撞事件
碰撞我个人觉得可以分为两大类: 比较物理的碰撞,和不太物理的碰撞。
比较物理的碰撞:比如奋斗的小鸟这种游戏,小鸟飞出去后会根据自己的速度还有重力碰到障碍物后可能会被弹回来。
不太物理的碰撞:比如超级玛丽这种游戏,无论玛丽以多块的加速度向前跑碰到了障碍物,玛丽并不会被弹回来。
那么这篇文章我主要想写写 不太物理的碰撞,想一想以前的2D游戏是怎么做的?
场景层:场景编辑器来生成一个庞大的二维数组,每一个数字代表一个含义,比如0-9表示不同的tile(小图)。整个游戏场景都是根据二维数组用双for循环绘制出来的。
物理层:再来一个和上面一样的二位数组,比如0表示可以通过,1表示不可以通过,也就是碰撞层。 策划在编辑器里面来编辑所有的物理层。
有可能还会在有两个相同的二维数组, 前景层 后景层。。。就不赘述了。。 最后用代码去检测人物是否能继续前进,或者被碰撞。
结合到unity3d里面,现在还能这么搞吗?答案:当然能这么搞了,只是unity给我们提供了新的方案,检测碰撞的这一部分可以不用我们自己写代码了。。
二维数组似乎有点麻烦,我们把问题简化一下。如下图所示,红色区域表示小鱼不可行走区域。我相信刚开始学unity2d的人首先想到的就是transfom.postion 来给小鱼赋值, 在Update里面来检查是否碰撞,因为我也是这么想的。
算法说难也不难,有轮子为什么我们不用呢?在处理位置的时候我们使用rigidbody2D.MovePosition()这个方法。另外记得要给精灵添加一个Rigidbody2D的组件。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
using
UnityEngine;
using
System.Collections;
public
class
FishCommon : MonoBehaviour
{
void
Update () {
//手指
或者 鼠标的坐标传进去
Move(Input.mousePosition);
}
public
void
Move(Vector3 postions){
//在这里进行坐标的换算
rigidbody2D.MovePosition(ScreenToWorld(postions));
}
private
Vector2 ScreenToWorld(Vector3 postion){
Vector3
pos = Camera.main.ScreenToWorldPoint(postion);
return
new
Vector2(pos.x,pos.y);
}
}
|
如下图所示,小鱼在移动的时候,如果碰到的障碍物它就会停住。
物理碰撞
如果需要精灵响应物理碰撞,那么就必须添加Rigidbody2D的组件。如下图所示,我不需要 位置与旋转运动阻力,所以linear Drag 和 angular Drag 设置0。我也不需要重力所以GravityScale 也就是0.我需要固定精灵的角度所以勾选Fixed Angle 。因为我需要感应物理碰撞,所以不够选Is Kinematic 。下面依次是物理差值、休眠模式、碰撞检测。调一调参数就能看出效果。
这里我想详细的在说说 Is Kinematic 属性。既然已经需要用到物理碰撞,那么在勾选 Is Kinematic 是不是吃饱了撑了?我在做个假设?比如我现在控制小鱼在吃金币,当碰撞到金币的时候我并不希望小鱼停住。但是我程序中还想得到碰撞的这个事件,那么此时该如何? 解决这个命题之前我们要看看物理碰撞的事件。
物理碰撞的事件
这是要把碰撞的信息回调到代码中,以前物理老师讲过“力的作用是相互的”。“小鱼”碰撞“石头”也就是“石头”碰撞“小鱼”,那么Rigidbody2D组件到底是给“小鱼”加 还是给“石头”加?请记住一点,Rigidbody2D永远给需要感应物理(有位移)的对象加。
例子1:“小鱼”碰撞“石头” ,石头是要挡住小鱼的前进,那么石头永远不可能发生位移,Rigidbody2D加给小鱼。
例子2:“小鱼”要吃一个金币,但是金币被一个特殊的石头挡住了。我需要控制小鱼把这个石头挪开,因为这个石头需要发生位移,Rigidbody2D加给小鱼,并且加给石头。
例子3:“小鱼”不加Rigidbody2D 而“石头”加Rigidbody2D 这种情况应该是非常非常少见的。
回到物理的碰撞事件上,要得到精灵的碰撞事件,必须保证这两个精灵至少有一个含有Rigidbody2D组件。还是“小鱼”碰撞“石头”的例子。如下代码所示,“小鱼”和“石头”都挂上,当发生碰撞的时候都可以收到对方的信息。
1
2
3
4
5
6
7
8
9
|
using
UnityEngine;
using
System.Collections;
public
class
ColliderEvent : MonoBehaviour {
void
OnCollisionEnter2D(Collision2D coll) {
Debug.Log(coll.gameObject.name);
}
}
|
OnCollisionEnter2D 表示进入碰撞的事件,对应还有碰撞中、和碰撞结束的事件。 详细的就在API里面找找吧。http://docs.unity3d.com/ScriptReference/MonoBehaviour.html显然这个脚本只挂在”小鱼”身上比较好,原因是可能有很多石头,只监听“小鱼”身上的就够了。
在举个例子,现在变成“小鱼”改吃 “金币”了。“小鱼”需要穿越过“金币”的碰撞区域,在回到上面我说的Rigidbody2D组件上。此时请把“小鱼”身上的 Is Kinematic 勾选上。并且把Box Collider2D上的 Is Trigger勾选。
把如下脚本挂在“小鱼”身上。
1
2
3
4
5
6
7
8
9
|
using
UnityEngine;
using
System.Collections;
public
class
ColliderEvent : MonoBehaviour {
void
OnTriggerEnter2D(Collider2D other) {
Debug.Log(other.name);
}
}
|
OnTriggerEnter2D 表示进入碰撞的事件,对应还有碰撞中、和碰撞结束的事件。如下图所示,“小鱼”已经可以穿过碰撞区域了。
例子4: 如果“小鱼”既要响应碰撞也要响应触发该如何?也就是说“小鱼”碰到石头会停止,但是同时碰到金币则会穿越。此时“小鱼”身上的Box Collider2D不要勾选Is Trigger,而是“金币”勾选Is Trigger即可。
总结:
1、精灵之间Game视图显示上没有碰撞表现,但代码要监听碰撞事件,那么 Is Kinematic 勾选,Is Trigger勾选、用transform.postion给坐标赋值即可。
2、精灵之间Game视图显示上要有碰撞表现,并且代码要监听碰撞事件。那么Is Kinematic 不勾选,Is Trigger不要勾选,用Rigidbody2D来给坐标赋值即可。
如果想做插值动画, 新项目的话强烈建议使用DoTween http://dotween.demigiant.com/ 虽然目前还是Alph版本,但是功能已经很强大的,我相信很快就有正式版本了。