【Unity入门教程】 第七章 物理引擎【中国大学MOOC游戏引擎原理及应用】

    科技2025-07-28  37

    以下均为来自中国大学mooc 游戏引擎原理及应用时的学习笔记,不含商用,仅供学习交流使用,如果侵权请联系作者删除。

    文章目录

    7.1 物理引擎基础7.2 典型效果模拟7.3 链接结构7.4 碰撞检测7.5 浮力效果

    7.1 物理引擎基础

    为小球添加刚体组件即可使得小球具有刚体属性 这时候把小球放到空中会掉下来

    但是并不会反弹 是因为地面不具有物理材质

    为了让地面具有物理材质,我们可以这样:

    创建一个物理材质并使得其具有弹性 然后将其拖拽给plane中mesh collider 这个方法可以给小球添加一个恒力 relative代表的是相对小球的局部空间

    7.2 典型效果模拟

    模拟子弹:

    var关键字 var 是3.5新出的一个定义变量的类型 其实也就是弱化类型的定义 VAR可代替任何类型 编译器会根据上下文来判断你到底是想用什么类型的 至于什么情况下用到VAR 我想就是你无法确定自己将用的是什么类型 就可以使用VAR 类似 OBJECT 但是效率比OBJECT高点。

    首先我们将小球变成预制件,为其添加刚体和恒力的组件 然后编写以下代码

    using System.Collections; using System.Collections.Generic; using UnityEngine; public class bulltetshoot : MonoBehaviour { public GameObject bullet; static float presstime = 0f; // Start is called before the first frame update void Start() { } // Update is called once per frame void Update() { if (Input.GetButton("Fire1")) { presstime += Time.deltaTime; } if (Input.GetButtonUp("Fire1")) { Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); GameObject b = Instantiate(bullet, ray.origin, Quaternion.LookRotation(ray.direction)) as GameObject;//这个函数可以把预制件实例化,参数分别是预制件,位置,方向 var cf = b.GetComponent<ConstantForce>().relativeForce = new Vector3(0, 0, presstime * 10);//var为任意类型的变量 presstime = 0; } } }qi

    之后将这个组件拖拽给摄像机 (其实不拖拽给摄像机也没有关系 因为脚本中自带着去获取了以主相机视角发出来的射线的)然后在把预制件托给这个物体

    之后便可以模拟出发射子弹的效果

    7.3 链接结构

    球窝类型 门扇类型

    固定类型

    unity中实现:

    我们创建一个空物体,然后为其添加hinge joint组件,此时会自动添加rigid body组件

    可以通过调整这个anchor来调整锚定点的位置,这个箭头表示旋转轴的方向和正方向

    我们创建一个新物体,并且把那个锚点定位方块的旋转轴

    为了让物体不发生移动,我们把空物体的is kinematic给点上 接下来我们给这个物体添加上刚体的属性

    然后把它拖拽给空物体的hinge组件上面,意思就是让它们链接起来了

    此时运行游戏可以看到这个物体动起来了

    我们在创建一个空物体,然后给他挂接一个Spring joint组件,是弹簧连接 然后会发现这个物体像弹簧一样在动

    这个连接是可以被破坏的

    这个limits代表的是当力或者力矩达到这个值的时候就会被破坏 此时我们调整这个 然后发射子弹 当蓄力到一定程度,便可把这个力矩给打烂

    有时候当我们破坏了物体 我们需要知道这个信息,因此我们写一个函数,将这个函数拖拽给两个空物体 ,

    private void OnJointBreak(float breakForce) { Debug.Log("you destroied " + name + " with a force of" + breakForce + "N"); }

    当我们破坏地时候 就会在右下角出现信息

    7.4 碰撞检测

    对于这种静态碰撞体可以利用预计算来计算它们跟其他物体的碰撞结果 常常运用于房屋 树木这种不可移动的物体

    触发器碰撞比如说计算浮力

    刚体和刚体 刚体和静态物体 刚体和运动学物体是可以进行碰撞的检测的

    这些是可以当作触发器的

    在unity中 物体都有碰撞体积 在右边的这里可以调整碰撞体积

    当我们给它点上这个的时候 它的行为就会变成一个触发器类型

    碰撞函数: 上面那个函数处理普通的碰撞类型事件 下面那个函数处理触发器的碰撞类型事件

    发生碰撞的 至少需要有一个是刚体

    我们建立这样的一个场景 其中 粉色墙壁是触发器 而其他墙壁是普通刚体 我们给粉色墙壁和其他墙壁都附上下面这段脚本

    private void OnCollisionEnter(UnityEngine.Collision collision) { if (collision.gameObject.tag == "Bullet") { Destroy(collision.gameObject);//销毁碰撞的物体 } } private void OnTriggerEnter(Collider other)//触发器碰撞 { var rb = other.GetComponent<Rigidbody>(); if (rb != null) { rb.useGravity = false;//消除重力 } }

    当我们射出子弹的时候 如果碰到粉色墙壁则会失去重力 碰到其他墙壁则这个物体会消失

    添加tag的方法在这里

    7.5 浮力效果

    首先创建一个如图所示的地形 水用cube+材质来替代(standard assest中有水的材质

    我们写出以下代码:首先是挂接在水上的脚本: 对水来说思路很简单,只需要写出当物体与水发生触发器类型的碰撞以及离开的时候 给含有float脚本的组件设置其状态:处于水的状态和离开水的状态

    using System.Collections; using System.Collections.Generic; using UnityEngine; public class water : MonoBehaviour { // Start is called before the first frame update void Start() { } // Update is called once per frame void Update() { } private void OnTriggerEnter(Collider other) { if (other.gameObject.GetComponent<floater>()) { other.gameObject.GetComponent<floater>().SetIsInWater(true); } } private void OnTriggerExit(Collider other) { if (other.gameObject.GetComponent<floater>()) { other.gameObject.GetComponent<floater>().SetIsInWater(false); } } }

    挂接在漂浮的物体上的脚本:

    using System.Collections; using System.Collections.Generic; using UnityEngine; public class floater : MonoBehaviour { private bool isInwater; private GameObject water; private float waterY; private const float floatageForce= 0; private const float density = 1;//密度 private const float g = 9.8f; private const float waterDrag = 5;//这类似阻尼,我们给物体在运动时添加一个阻力 // Start is called before the first frame update void Start() { isInwater = false; water = GameObject.FindWithTag("Water");//我们搜寻场景中含有Water这个tag的物体 } // Update is called once per frame void Update() { } private void FixedUpdate()//由于是一个物理现象,最好使用fixupdate来实现物理现象的函数 { if (isInwater) { calFloatage(); GetComponent<Rigidbody>().drag = waterDrag;// } } void calFloatage() { waterY = water.transform.position.y+water.transform.localScale.y/2;//获得水面最高处的坐标 //if (waterY > (transform.position.y - transform.localScale.y))//transform.position.y - transform.localScale.y这个是 if(isInwater) { float h = waterY - (transform.position.y - transform.localScale.y / 2) > transform.localScale.y ? transform.localScale.y : waterY - (transform.position.y - transform.localScale.y / 2);//如果物体全部沉到水中和只沉了一部分的情况 float floatageForce = density * g * transform.localScale.x * transform.localScale.z * h;//计算浮力 GetComponent<Rigidbody>().AddForce(0, floatageForce, 0);//给这个物体添加浮力 } } public void SetIsInWater(bool inWater) { isInwater = inWater; } /*public bool GetIsInWater() { return isInwater; }*/ }

    然后给物块添加刚体属性 给水添加触发器属性

    最终成果如图

    Processed: 0.009, SQL: 8