第二十七章 碰撞体Collision(下)

news2024/10/3 8:16:38

本章节我们继续研究碰撞体,并且探索一下碰撞体与刚体之间的联系。我们回到之前的工程,然后给我们的紫色球体Sphere1也添加一个刚体组件。如下所示

此时,两个球体都具备了碰撞体和刚体组件。接下来,我们Play运行查看效果

我们发现,黄球碰撞紫球之后,两者都向右移动了,并且黄色还发出的角度变化。之所以这样,肯定是刚体组件作用的原因。应该还是依据动能公式进行各种计算,其中影响的因素包括双方的质量,以及运动方的移动速度等等。如果指向让两个球体在X轴上移动的话,我们可以借助刚体的Constraints下的Freeze Position来实现,我们要做的就是勾选Y轴和Z轴,这样球体在受到刚体影响进行移动的时候,就只会在X轴上发生移动了。

大家可以将两个球体像上图那样设置,然后运行一下,查看效果,这里就不演示了。

有时候,我们需要在两个球体发生碰撞的时候做一些处理,比如播放一个碰撞火花效果。我们应该怎么办呢?这里要给大家介绍一个方法。脚本系统可以使用 OnCollisionEnter 方法检测何时发生碰撞。接下来,我们给紫球添加一个C#脚本(CollisionScript.cs),在这个脚本中我们只打印相互碰撞的两个游戏对象的名称即可。如下所示:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CollisionScript : MonoBehaviour
{
    void OnCollisionEnter(Collision collision)
    {
        // 打印碰撞游戏对象的名称
        Debug.Log("当前对象 " + gameObject.name + " 与 " + collision.gameObject.name + " 发生碰撞");
    }
}

方法OnCollisionEnter 被传入的参数为 Collision 类,该类中的gameObject变量可以获取到参与碰撞的游戏对象,进而可以获取到碰撞游戏对象的基本信息。同时,我们还可以通过Collision类的GetContact 或 GetContacts方法来获取碰撞点,该方法会返回ContactPoint对象,这个ContactPoint对象中point属性就是一个Vector3类型的位置点。接下来,我们将上面的脚本附加到紫球上面,然后Play运行当前工程。

由于我们的地面Plane也存在碰撞体组件,因此工程运行后就打印了紫球Sphere1与地面Plane的碰撞日志信息,然后就是当黄球Sphere2碰撞紫球Sphere1的时候,打印出紫球与黄球的碰撞日志信息。请注意,紫球是被碰撞的游戏物体哦。接下来,我们在写一个脚本(CollisionScript2.cs),并将该脚本附加到黄球上面,如下所示:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CollisionScript2 : MonoBehaviour
{
    void OnCollisionEnter(Collision collision)
    {
        // 打印碰撞游戏对象的名称
        Debug.Log("碰撞游戏对象的名称 " + collision.gameObject.name + " 发生碰撞");
    }
}

为了区分日志信息,我们上面的代码只打印了碰撞目标的名称,也就是紫球Sphere1 。然后,我们将这个脚本附加到黄球Sphere2上面,运行当前工程,如下所示:

首先两个球体Sphere1和Sphere2都会与地面Plane发生碰撞,这个不是我们关注的。我们主要关注的是当黄球Sphere2与紫球Sphere1 发生碰撞的时候,两个脚本中的OnCollisionEnter都被触发了。而方法的参数Collision对象都代表了碰撞的对方。上图中第一个红色框日志信息就是黄球Sphere2输出的,它的碰撞对方自然就是紫球Sphere1了。而下面的红色框日志就是紫球Sphere1输出的,它直接输出自己与Sphere2发生碰撞。其实,除了OnCollisionEnter方法之外,还有OnCollisionStay和OnCollisionExit两个方法,从名称上面来看,一个是碰撞中的调用方法(碰撞可能会持续发生),另一个是碰撞结束的调用方法(一次性)。而我们上面的OnCollisionEnter方法则是碰撞开始调用的方法(一次性)。我们可以根据我们要实现的效果来选择使用其中任意方法。

使用刚体组件能够帮助我们完成游戏对象的运动效果,然后配合碰撞体组件可以帮助我们完成碰撞后的运动效果。这两个组件基本上可以帮助我们模拟现实世界中物体的物理碰撞和移动。但是,有时候,我们还是希望通过代码来完成移动或者碰撞后的移动。比如上面中两个球体碰撞后,黄球弹了起来,这个可能不是我们想要的效果。因此我们不得不做轴向的限制。但是,如果使用代码控制的话,我们就可以简简单单的让球体在X轴上移动即可。如何使用代码控制呢?我们之前也讲过,我们可以启动刚体的“Is Kinematic”属性,使用代码来控制游戏对象的移动或旋转。因此,我们就先启用紫球Sphere1的“Is Kinematic”属性。这样的话,紫球应该就不能受物理引擎而发生运动了,运行工程如下:

接下来,我们开始Play运行工程

正如我们预想的那样,紫球不在向右运动,而且黄球由于紫球碰撞体的阻挡,也停止了移动。接下来,我们继续启动黄球Sphere2的“Is Kinematic”属性。

这样的话,我们之前施加的力Constant Force组件就不能其作用了,我们应该使用脚本控制黄球向右移动,我们需要向“CollisionScript2.cs”脚本中添加如下代码:

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.D))
        transform.Translate(new Vector3(0.5f, 0, 0));
    }

我们使用按键“D”来控制黄球向右移动去碰撞紫球,运行工程如下所示:

我们发现,黄球可以穿透紫球,并且紫球也没有任何的反应。并且我们的控制台也没有任何的日志输出。这说明,当我们启用刚体的“Is Kinematic”属性,而由代码去控制游戏对象进行移动或旋转的时候,现实世界中的碰撞效果全部消失了,并且OnCollisionEnter方法也没有被触发。那我们应该怎么办呢?首先,我们应该明白一点的是,我们启用刚体的“Is Kinematic”属性之后,就不能够让Unity物理引擎来控制游戏对象的移动或旋转了。正如我们使用按键“D”来控制黄球移动一个道理。同样的,紫球在受到碰撞的时候,也需要我们使用代码来控制它的移动和旋转。但是,我们怎么知道两球碰撞的时间点呢,OnCollisionEnter方法也失效了啊。我们需要通过另一个方法来执行碰撞后的运动效果,就是碰撞体里面的“Is Trigger”属性。接下来,我们就开启紫球Sphere1碰撞体里面的“Is Trigger”属性,截图如下:

然后,我们需要在紫球的“CollisionScript.cs”脚本中添加“OnTriggerEnter”方法。该方法的参数为Collider 对象(与OnCollisionEnter方法参数Collision不一样哦)。这里的Collider 对象同样可以通过gameObject来获取碰撞对象。具体代码如下: 

    void OnTriggerEnter(Collider other)
    {
        Debug.Log("当前对象被" + other.gameObject.name + "碰撞到了!");
    }

接下来,我们Play运行当前工程。

我们终于看到OnTriggerEnter方法被触发了,打印出了黄球Sphere2了。那么,我们继续给黄球添加OnTriggerEnter方法也试一试,如下所示:

    void OnTriggerEnter(Collider other)
    {
        Debug.Log("碰撞到了 " + other.gameObject.name + " 对象!");
    }

接下来,我们就Play运行当前工程

我们发现两球的OnTriggerEnter方法都被触发了,正确打印出彼此对方的名称。除了OnTriggerEnter方法之前,还有OnTriggerExit和OnTriggerStay两个方法。看名称就知道,一个是碰撞结束方法,另一个是碰撞中的方法(碰撞也是可持续的哦)。接下来,我们就借助这三个方法来控制黄球的运行。例如,当发生碰撞后,黄球就停止移动。我们可以在“CollisionScript2.cs”脚本中设置一个碰撞进行时的变量isTriggerFlag=false,在OnTriggerEnter方法中设置为true,在OnTriggerExit方法中设置为false,当我们使用按键D进行移动的时候,需要参考isTriggerFlag来进行移动,具体完整代码如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CollisionScript2 : MonoBehaviour
{

    // 碰撞进行时变量
    private bool isTriggerFlag = false;

    void Update()
    {
        // 移动加入isTriggerFlag条件
        if (Input.GetKeyDown(KeyCode.D) && !isTriggerFlag)
        transform.Translate(new Vector3(0.5f, 0, 0));
    }

    void OnCollisionEnter(Collision collision)
    {
        // 打印碰撞游戏对象的名称
        Debug.Log("碰撞游戏对象的名称 " + collision.gameObject.name + " 发生碰撞");
    }

    void OnTriggerEnter(Collider other)
    {
        isTriggerFlag = true;
        Debug.Log("碰撞到了 " + other.gameObject.name + " 对象!");
    }

    void OnTriggerExit(Collider other)
    {
        isTriggerFlag = false;
    }
}

其实在上面的代码中,OnCollisionEnter方法已经失去了意义,可以删除掉了。

我们运行当前工程,当按下按键D移动黄球的时候,碰到紫球就不在移动了。

那么紫球该如何移动呢,我们可以在紫球的OnTriggerStay方法中进行移动,代码如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CollisionScript : MonoBehaviour
{
    void OnCollisionEnter(Collision collision)
    {
        // 打印碰撞游戏对象的名称
        Debug.Log("当前对象 " + gameObject.name + " 与 " + collision.gameObject.name + " 发生碰撞");
    }

    void OnTriggerEnter(Collider other)
    {
        Debug.Log("当前对象被" + other.gameObject.name + "碰撞到了!");
    }

    void OnTriggerStay(Collider other)
    {
        // 去掉地面碰撞的情况
        if (other.gameObject.name == "Sphere2")
        transform.Translate(0.1f, 0, 0);
    }
}

其实在上面的代码中,OnCollisionEnter方法已经失去了意义,可以删除掉了。

我们运行当前工程,当按下按键D移动黄球的时候,碰到紫球后,黄球停止移动且紫球向右缓慢移动,两者碰撞结束,黄球又可以移动……

静态碰撞体和动态碰撞体:

我们可以将碰撞体添加到没有刚体组件的游戏对象,从而创建场景的地板、墙壁和其他静止物体。这些被称为静态碰撞体(Static Collider)。相反,具有刚体的游戏对象上的碰撞体称为动态碰撞体(Rigidbody Collider)。如果我们在动态碰撞体上开启了IsKinematic 属性,那么就是运动碰撞体 (Kinematic Rigidbody Collider)。在游戏场景中,以上三种碰撞体都会出现,如何判断三者之间的碰撞关系呢?重点在于OnTriggerEnter和OnCollisionEnter方法的选择。我们可以这样的理解,刚体是基于碰撞体之上的运动组件。刚体可以帮助我们完成游戏物体的运动以及碰撞后的运动效果,同时提供OnCollisionEnter方法让我们可以处理碰撞后的游戏代码逻辑。如果我们不想让刚体来完成游戏的运动,也就是启用刚体的“Is Kinematic”属性,那么我们则需要使用代码来控制游戏物体的运动。为了能够获取碰撞发生,我们需要启用碰撞体的“Is Trigger”属性,Unity会在碰撞后调用OnTriggerEnter方法。因此,我们对游戏对象碰撞后的移动和旋转操作应该放置到OnTriggerEnter方法中。

但是OnTriggerEnter方法执行是有条件的,如下所示:

1、两个物体都必须有碰撞体组件;

2、其中一个物体的碰撞体的IsTrigger属性必须勾上;

3、最重要的一点,其中一个物体必须有刚体组件。如果是一个运动的物体去碰撞一个静止的物体,则刚体组件必须加在运动的物体上,否则无法触发OnOnTriggerEnter方法。

最后介绍一下Layer层和碰撞体的关系,层与层之间是否发生碰撞。我们点击菜单栏Edit->Project Setting->Physics,然后找到Layer Collision Matrix(层碰撞矩阵)选项。在Layer Collision Matrix下就会显示所有层,层与层之间是否可以发生碰撞检测。默认是都发生碰撞检测的,也就是勾选状态。如果不想让两个层进行碰撞检测,取消两层交叉的勾选框即可。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/484320.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

从零开始带你开发橙光游戏AVG框架(仿 葬花 )

来源 从零开始带你开发橙光游戏AVG框架【55课数 收费】 从零开始带你开发橙光游戏AVG框架 unity教程【16课数 免费】 。。。。。。 挺大的,因为很多音频,.git就有 2.6G AVG_20230413_2020.2.23f1c1 介绍 QuickSheet使用 bug 包报错 可能是我换了un…

LeetCode138. 复制带随机指针的链表

138. 复制带随机指针的链表 描述示例解题思路以及代码解法1解法2 描述 给你一个长度为 n 的链表,每个节点包含一个额外增加的随机指针 random ,该指针可以指向链表中的任何节点或空节点。 构造这个链表的 深拷贝。 深拷贝应该正好由 n 个 全新 节点组成…

电脑文件加密软件哪个最好用:试试文件加密软件排行榜第一的EaseUS LockMyFile吧 | 军事级加密你值得拥有!!!

EaseUS LockMyFile是一款出色且安全可靠的军事级电脑文件加密管理软件,也叫易我文件加密软件,拥有文件隐藏、文件加锁、文件保护、读写监控、安全删除等诸多实用功能,能帮助大家锁定和隐藏闪存驱动器、外部USB 驱动器、内部硬盘驱动器以及局域…

51单片机(六)矩阵键盘和矩阵键盘密码锁

❤️ 专栏简介:本专栏记录了从零学习单片机的过程,其中包括51单片机和STM32单片机两部分;建议先学习51单片机,其是STM32等高级单片机的基础;这样再学习STM32时才能融会贯通。 ☀️ 专栏适用人群 :适用于想要…

几种常见时间复杂度实例分析

多项式量级 常量阶 O(1) 对数阶 O(logn) 线性阶 O(n) 线性对数阶 O(nlogn) 平方阶O(n2 ),立方阶O(n3 )...k次方阶O(nk) 非多项式量级(NP(Non-Deterministic Polynomial,非确定多项式)问题) 指数阶O(2n) 阶乘阶…

离线数据同步Sqoop与DataX

文章目录 一、Sqoop安装与使用1、简介2、Sqoop安装3、Sqoop实例3.1 Mysql导入Hadoop3.2 Hadoop导出到Mysql 二、DataX概述与入门1、DataX概述1.1 简介1.2 框架设计1.3 运行原理 2、DataX与 Sqoop 的对比3、快速入门 三、DataX常用入门案例1、从stream 流读取数据并打印到控制台…

前端web3入门脚本六:套利夹子机器人,羊毛党必备

一、前言 DEX上有很多零风险套利的机会,包括三角套利,夹子机器人… 今天主要介绍一下架子机器人的思路和简易实现。 二、实现思路 套利原理: 夹子机器人的核心:在韭菜买入前以更低价格买入,并再韭菜买入后卖出&#…

Curator中的分布式锁解读

目录 基本介绍 基本配置 可重入锁InterProcessMutex 不可重入锁InterProcessSemaphoreMutex 可重入读写锁InterProcessReadWriteLock 联锁InterProcessMultiLock 信号量InterProcessSemaphoreV2 栅栏barrier 倒计数器 基本介绍 Curator是netflix公司开源的一套zookeeper…

C语言力扣简单题-无重复字符的最长子串

(创作不易,感谢有你,你的支持,就是我前行的最大动力,如果看完对你有帮助,请留下您的足迹) 目录 无重复字符的最长子串 题目: 代码思路: 代码表示: 无重复字符的最长子…

【C++】lambda表达式

文章目录 lambda表达式lambda概念lambda表达式的格式关于捕获列表常见问题: 使用lambda表达式交换两个数lambda表达式底层原理 lambda表达式 lambda概念 lambda表达式本质是一个匿名函数(因为它没有名字),恰当使用lambda表达式可以让代码变得简洁.并且可以提高代码的可读性 例…

ChatGPT实现HTML网页文本提取

网页自动化工具 既然ChatGPT对于编程语言有非常强大的理解能力,那么它是否可以用来自动化地处理网页呢?答案是肯定的。ChatGPT可以使用机器学习算法来识别网页元素中的文本,并抽取出有用的信息。 例如我们提供一段层数比较多的相对来说较为…

【五一创作】|【C++】AVL树的实现

文章目录 1.AVL树概念2. AVL树性质3.AVL树的实现insert插入情况分析更新平衡因子旋转处理左单旋右单旋在insert中判断左右单旋的条件双旋转左右双旋 整体代码 1.AVL树概念 二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树&#…

PCIe数据链路层图文详细总结-PCIe专题知识(二)

目录 前言一、简介1.1 接收部件组成1.2 发送部件组成 二、数据链路层功能详细介绍2.1 DLLP介绍2.2 ACK/NAK协议2.3 发送端逻辑2.3.1 发送端TLP包处理总流程2.3.2 使用ACK/NAK协议详解 2.4 接收端逻辑2.4.1 接收端TLP包处理流程2.4.2 如何使用ACK/NAK协议 2.5 数据链路层发送报文…

Java每日一练(20230503)

1. 外观数列 给定一个正整数 n ,输出外观数列的第 n 项。 「外观数列」是一个整数序列,从数字 1 开始,序列中的每一项都是对前一项的描述。 你可以将其视作是由递归公式定义的数字字符串序列: countAndSay(1) "1"c…

[AION]我眼中的《永恒之塔私服》

当我第一次看到《永恒之塔私服》我被它那华丽的画面吸引了。      三维做的很逼真,他的三维技术,华丽的三维景象,从maya设计者专业的角度上说,他是一部做工完美的游戏,不管是他的背景还是他的人物。都比以前很多游…

Python每日一练(20230503)

目录 1. 外观数列 🌟🌟 2. 找出素数对 ※ 3. 子集 🌟🌟 🌟 每日一练刷题专栏 🌟 Golang每日一练 专栏 Python每日一练 专栏 C/C每日一练 专栏 Java每日一练 专栏 1. 外观数列 给定一个正整数 n…

Dockercompose编排

目录 一、Dockercompose简介 1、compose概述 2、YAML简介 1、概述 2、YAML支持的数据结构 二、compose部署 1、Docker compose环境安装 Docker compose常用字段 Docker compose常用命令 Docker Compose文件结构 2、准备依赖文件 3、编写配置文件docker-compose.yml…

fps枪械案例

文章目录 一、 介绍二、 知识点三、 鼠标移动控制视角四、 人物行走、奔跑、跳跃、下蹲、音效五、 射击、射速、瞄准、弹痕、枪火、弹壳、文本六、 手臂摇摆七、 步枪切换到手枪八、 切枪效果九、 添加各种动画 一、 介绍 经典fps案例 行走、奔跑、跳跃、切枪、换弹、武器展示…

gitlab搭建以及自动化部署

一、安装gitlab 首先下载gitlab的安装包,地址如下: https://mirrors.tuna.tsinghua.edu.cn/gitlab-ce/ubuntu/pool/bionic/main/g/gitlab-ce/ 然后安装下载的包即可,一般还需要安装openssh-server等依赖包,在安装gitlab包之前可以…

正则表达式-基本元字符和语法规则

© Ptw-cwl 文章目录 字符匹配元字符.元字符[]元字符[^]元字符*元字符元字符?元字符{}元字符|元字符()元字符^元字符$元字符\元字符\d元字符\w元字符\s元字符\b元字符\B元字符*?、?、??、{n,m}?元字符(?)、(?!)元字符(?:)元字符\1、\2等元字符^、$元字符&#x…