Unity3D教程:运用物理方法实现无限摆钟
发表于2016-11-07
无限摆钟有时候会在一些游戏场景中使用到,为了帮助大家项目的开发,下面就给大家介绍下在Unity3D中是运用物理方法实现无限摆钟的方法,想知道可以看一看。
一、概述
某一天,我想在游戏中实现一个钟摆式的陷阱,大致如下图所示
虽然Unity内置了Physx物理引擎,提供了铰链关节、Distance Joint2D之类的物理组件,但是我试过之后发现,虽然能实现钟摆,但会有能量的损失,无法做到无限摆钟。我又试着在物理材质中去掉摩擦力,也还是行不通。在全局变量中也没有找到空气阻力之类的参数。所以,只好自己来实现了,幸好用到的物理公式都很简单。我们来看看吧。
二、原理
如上图所示,O点是圆点,P点是摆钟的某一时刻的位置。则摆钟受到一个地心引力,重力加速度为g。
首先,摆钟是属于物理中的转动,与我们平时所常用的平动有联系,但也有区别。相关知识在大学物理中会学到,但不幸的是我们专业课并没有开设大学物理。那么我们现在来学习一下吧。
对于平动而言,有如下等式成立:
上面两个式子很简单直白,那么我们接着来看在转动中相关的等式是怎样的:
那么,对于我们上述的问题来说,我们已知钟摆受到重力加速度g,这是属于平动的物理量,我们怎么把它转换为转动中的角加速度呢?运用下面的式子:
现在,知道了a(通过重力加速度g求解),我们就可以求出钟摆的角加速度,然后依次推出某一时刻的角速度,以及角位移,这样钟摆的问题就得到求解了,我们接下来来看具体推导过程。
回到上图,角A是点P处切线PQ与垂线的夹角,已知钟摆在P点受到重力加速度g,那么可以求得其在PQ方向上的加速度a:
通过公式3,求得PQ方向上的角加速度:
联立公式2,求出角速度:
如图,对于cos(A)来说,有:
已知O点和P点的坐标,则L可求解。下面来看具体的实现。
三、实现
通过上述公式可以求出角速度以及角位移,然后我们可以调用Unity3D的transform.RotateAround(Vector3 point, Vector3 axis, float angle)来实现钟摆。此函数的作用是让物体绕着经过point点的轴axis旋转angle角度。注意angle的单位是角度,不是弧度。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | using UnityEngine; using System.Collections; public class Pendulum : MonoBehaviour { public Transform m_anchor; //圆点 public float g = 9.8f; //重力加速度 private Vector3 m_rotateAxis; //旋转轴 private float w=0; //角速度 // Use this for initialization void Start () { //求出旋转轴 m_rotateAxis = Vector3.Cross (transform.position-m_anchor.position, Vector3.down); } void DoPhysics() { float r = Vector3.Distance(m_anchor.position, transform.position); float l = Vector3.Distance ( new Vector3 (m_anchor.position.x, transform.position.y, m_anchor.position.z), transform.position); //当钟摆摆动到另外一侧时,l为负,则角加速度alpha为负。 Vector3 axis = Vector3.Cross (transform.position-m_anchor.position, Vector3.down); if (Vector3.Dot (axis, m_rotateAxis) < 0) { l = -l; } float cosalpha = l/r; //求角加速度 float alpha = (cosalpha*g)/r; //累计角速度 w += alpha * Time.deltaTime; //求角位移(乘以180/PI 是为了将弧度转换为角度) float thelta = w * Time.deltaTime*180.0f/Mathf.PI; //绕圆点m_ahchor的旋转轴m_rotateAxis旋转thelta角度 transform.RotateAround (m_anchor.position, m_rotateAxis, thelta); } // Update is called once per frame void Update () { DoPhysics (); } } |
至此,我们的实现就结束了。
下面是知乎上的一个关于转动的帖子,对我很有帮助:
https://www.zhihu.com/question/24218339