《学Unity的猫》——第十章:Unity的物理碰撞,流浪喵星计划

简介:我是一名Unity游戏开发工程师,皮皮是我养的猫,会讲人话,它接到了喵星的特殊任务:学习编程,学习Unity游戏开发。
于是,发生了一系列有趣的故事。
在这里插入图片描述

10.1 流浪喵星

2020年,喵星所在的拉姆达星系中,巨大的鲁特恒星即将毁灭,已经不适合喵星人生存,面对绝境,喵星人开启了 “流浪喵星” 计划,试图带着喵星一起逃离拉姆达星系,寻找喵星人新的家园。
然而,喵星在靠近纳美星的时候被强大的引力吸住,马上就要撞上去了,情况十分危急。
皮皮:“呼叫铲屎官,呼叫铲屎官,只剩最后1分钟了。”
我:“请点燃纳美星!”
皮皮:“来不及了。”
我:“以下是命令 :活下去!”
在这里插入图片描述
皮皮:“奇迹出现了,喵星直接穿透了纳美星。”

10.2 碰撞的必要条件

皮皮:“铲屎官,你刚刚是怎么做到的?”
我:“最后关头,我将纳美星的碰撞器禁用了,避免了这次物理碰撞。”

Unity3D内置物理引擎,可以模拟物理效果。典型的一个物理效果就是碰撞。
两个物体发生物理碰撞的必要条件是:两个物体都带有Collider(碰撞器)组件,其中一个物体带有Rigidbody(刚体)组件,并且是运动的物体带有Rigidbody组件。

10.2.1 Collider,碰撞器组件

Collider,即碰撞器。Unity中提供了各种形状的碰撞器组件。点击AddComponent,可以看到如下的各种碰撞器组件,这些碰撞器组件名字带2D的都是继承自Collider2D类,名字没带2D的都继承自Collider类,一般3D物体的使用Collider碰撞器,2D物体使用Collider2D碰撞器。
在这里插入图片描述
我们通过3D Object菜单创建的几何体都默认带了相应的碰撞器组件。
在这里插入图片描述
比如创建球体Sphere,它会自带球形碰撞器组件Sphere Collider
在这里插入图片描述

10.2.2 Rigidbody,刚体组件

Rigidbody,刚体组件,刚体能让你的游戏对象被物理引擎所控制,它能通过受到推力和扭力来实现真实的物理表现效果。

我们可以通过给刚体施加一个力来改变一个它的运动状态:

// 获取刚体组件
Rigidbody rigidBody = gameObject.GetComponent<Rigidbody>();
// 给刚体一个向量为(1, 0, 0)的力
rigidBody.AddForce(new Vector3(1, 0, 0));

在这里插入图片描述

刚体受到力的作用,会改变运动状态。假设初始状态是禁止的,那么施加一个力之后,刚体会动起来。动起来的速度与刚体受到的力成正比,与刚体的质量成反比。
RigidbodyMass属性就是刚体的质量。
在这里插入图片描述
使用代码设置刚体的质量:

// 设置刚体的质量为1
rigidBody.mass = 1;

我们要可以直接给刚体设置速度:

// 给刚体设置速度
rigidBody.velocity = new Vector3(1, 0, 0);

另外,还可以设置角速度:

// 给刚体设置角速度
rigidBody.angularVelocity = new Vector3(0, 1, 0);

有了角速度,物体就会开始旋转。
在这里插入图片描述
刚体的运动状态还会受到空气阻力的影响,RigidbodyDrag属性就是刚体的空气阻力,默认为0,即空气阻力为0。
在这里插入图片描述
通过代码设置刚体的空气阻力:

// 设置刚体的空气阻力为0
rigidBody.drag = 0;

物体的旋转受到角阻力Angular Drag的影响。
在这里插入图片描述
通过代码设置刚体的角阻力:

// 设置刚体的角阻力为0.05f
rigidBody.angularDrag = 0.05f;

皮皮拿过鼠标,点击AddComponent,添加了刚体组件。
在这里插入图片描述
刚体的属性如下:
在这里插入图片描述
点击运行Unity
在这里插入图片描述
皮皮:“怎么回事呀?球体直接垂直往下掉了。”
我:“因为Rigidbody有个Use Gravity属性,意思就是是否使用重力,默认是勾选着的,所以球体就受到重力作用直接垂直往下掉了。”
在这里插入图片描述
去掉Use Gravity的勾选,移动球体,可以产生碰撞了,不过如果移动速度快一点,还是可以穿过去。
在这里插入图片描述
皮皮:“这又是怎么回事呀?”
我:“因为我们是强行拖拉来移动刚体物体的,并不是由物理引擎来驱动移动,刚体的碰撞检测不是连续的。另外,被撞的物体是一个静态碰撞器,我们这样强行拉着刚体移动,只要速度稍快一些就会出现穿透现象。”
皮皮:“你解释一个问题,中间又引入了新的概念,什么是静态碰撞器?”

10.3 碰撞器类型
10.3.1 Static Collider,静态碰撞器

静态碰撞器是指只挂了Collider而没挂Rigidbody的游戏对象。这类对象会保持静止或者很轻微的移动。这类碰撞器被刚体碰撞后不会移动。
皮皮:“难怪刚刚被撞的另一个球不会动,因为它是一个静态碰撞器。”

10.3.2 Rigidbody Collider,刚体碰撞器

刚体碰撞器是指既挂了Collider又挂了Rigidbody的游戏对象。运动的刚体碰撞器可以与静态碰撞器发生碰撞,两个刚体碰撞器之间也可以发生碰撞。如果刚体碰撞器禁止不动,强行移动静态碰撞器去碰刚体碰撞器,此时并不会发生碰撞,因为禁止的刚体碰撞器会进入休眠状态。

10.3.3 Kinematic Rigidbody Collider,运动学刚体碰撞器

运动学刚体碰撞器是指在刚体碰撞器的基础上,激活了Kinematic的游戏对象。在这里插入图片描述
要移动这类游戏对象,要修改它的Transform组件(坐标或角度),而不能通过力。

我:“运动学刚体碰撞器是个非常懒惰和霸道的家伙,它不受力、重力或扭矩的影响,它去碰别人,它自己不会受到反作用力。所以你可以把运动学刚体碰撞器看成一个所向披靡的碰撞器,谁遇到它都不是对手。”
皮皮:“那如果两个运动学刚体碰撞器相互碰撞会怎样?”
我:“谁也不让谁,直接穿透过去。”

10.4 碰撞事件

MonoBehavior脚本中,我们可以通过OnCollisionEnterOnCollisionStayOnCollisionExit来处理收碰撞事件。其中collision参数是对方的碰撞器对象,脚本挂在两个碰撞体上都可以触发OnCollisionXXX

private void OnCollisionEnter(Collision collision)
{
    Debug.Log("碰撞进入");
}

private void OnCollisionStay(Collision collision)
{
    Debug.Log("碰撞中");
}

private void OnCollisionExit(Collision collision)
{
    Debug.Log("碰撞结束");
}
10.5 触发器事件

有时候我们并不想产生物理碰撞的效果,但是又想检测碰撞器之间的碰撞事件,这个时候就可以使用触发器。
触发器其实是碰撞器,只是勾选了Is Trigger
MonoBehavior脚本中,我们可以通过OnTriggerEnterOnTriggerStayOnTriggerExit来处理收碰撞事件。

private void OnTriggerEnter(Collider other)
{
    Debug.Log("触发进入");
}

private void OnTriggerStay(Collider other)
{
    Debug.Log("触发中");
}

private void OnTriggerExit(Collider other)
{
    Debug.Log("触发结束");
}

想要产生触发器事件,两个碰撞器中至少要有一个勾选了Is Trigger,并且其中一个要带Rigidbody组件。
皮皮:“触发器有什么应用吗?看起来不知道用在哪里。”
我:“举个简单的例子,你走近一个门的时候,要触发播放开门的动作,那么门就可以弄成触发器。门的碰撞体可以稍微调大一点,这样人就可以提前碰到触发器,触发开门动作。”
在这里插入图片描述

10.6 射线碰撞检测

皮皮:“可不可以实现一个鼠标拖动物体的功能,这样就可以直接用鼠标拖动喵星逃离兰姆达星系啦。”
我:“可以呀,原理很简单,点击鼠标左键,从摄像机的位置往里发射射线,检测到碰撞,则把碰撞体缓存到一个对象中,移动鼠标的时候,把鼠标坐标转成世界坐标赋值给刚刚的物体,实现鼠标抓取物体移动的效果,鼠标松开时,释放缓存对象。”

Demo工程如下,把下文的TouchAndMoveObj .cs脚本挂在某个GameObject即可,比如挂在Main Camera上。

运行效果:
在这里插入图片描述

TouchAndMoveObj .cs脚本代码如下:

using UnityEngine;

public class TouchAndMoveObj : MonoBehaviour
{
    private Transform m_targetTrans;
    private Camera cam;
    private float m_posZ;

    private void Start()
    {
        cam = Camera.main;
    }

    private void Update()
    {
        // 鼠标左键按下
        if (Input.GetMouseButtonDown(0))
        {
            Ray ray = cam.ScreenPointToRay(Input.mousePosition);
            RaycastHit hit;
            if (Physics.Raycast(ray, out hit, 100))
            {
                // 缓存射线碰撞到的物体
                m_targetTrans = hit.transform;
                // 缓存物体与摄像机的距离
                m_posZ = m_targetTrans.position.z - cam.transform.position.z;
            }
        }
        // 鼠标左键抬起
        if (Input.GetMouseButtonUp(0))
        {
            // 释放碰撞体缓存
            m_targetTrans = null;
        }
        // 鼠标按住中
        if (null != m_targetTrans && Input.GetMouseButton(0))
        {
            // 鼠标的屏幕坐标转成世界坐标
            // 由于鼠标的屏幕坐标的z轴是0,所以需要使用物体距离摄像机的距离为z周的值
            var targetPos = cam.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, m_posZ)); ;
            // 让物体的坐标跟着鼠标走
            m_targetTrans.position = targetPos;
        }
    }
}

《学Unity的猫》——第十一章:Unity猫咪救济管理系统,山岗的星光

  • 9
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

林新发

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值