物理系统与碰撞
课程内容
1.1物理引擎
1、游戏世界运动分类
运动学(Kinematics),从几何的角度(指不涉及物体本身的物理性质和加在物体上的力) 描述和研究物体位置随时间的变化规律的力学分支。点的运动学研究点的运动方程、轨迹、位移、速度、加速度等运动特征,以及在不同空间中的变换。运动学是理论力学的一个分支学科,它是运用几何学的方法来研究物体的运动。
- 不考虑外部力作用下的运动
- 将一个物体作为几何部件,抽象为质点运动模型
- 仅考虑物体位置、速度、角度 …
- 研究方法:代数,如:线性代数
动力学(Dynamics),它主要研究作用于物体的力与物体运动的关系。动力学的研究对象是运动速度远小于光速的宏观物体。动力学的研究以牛顿运动定律为基础;牛顿运动定律的建立则以实验为依据。动力学是牛顿力学或经典力学的一部分,但自20世纪以来,动力学又常被人们理解为侧重于工程技术应用方面的一个力学分支。
在游戏物理引擎中,主要是刚体动力学。主要包括质点系动力学的基本定理,由动量定理、动量矩定理、动能定理以及由这三个基本定理推导出来的其他一些定理。动量、动量矩和动能(见能)是描述质点、质点系和刚体运动的基本物理量。
- 考虑外部力对物体运动的影响
- 包括重力、阻力、摩擦力等,以及物体的重量和形状,甚至弹性物体
- 通常将一个物体当作刚体
- 模拟物体在现实世界中的运动
2、物理引擎职责与作用
**物理引擎(Physics Engine) **是一个软件组件,它将游戏世界对象赋予现实世界物理属性(重量、形状等),并抽象为刚体(Rigid)模型(也包括滑轮、绳索等),使得游戏物体在力的作用下,仿真现实世界的运动及其之间的碰撞过程。即在牛顿经典力学模型基础之上,通过简单的 API 计算游戏物体的运动、旋转和碰撞,现实的运动与碰撞的效果。
使用物理引擎,游戏开发者仅需考虑给游戏物体赋予形状(假设为均匀分布)和力,在游戏引擎驱动下自动完成运动与碰撞的计算。
物理引擎基础知识
1、物理引擎学习与使用
物理引擎的使用可能是最简单的。你要做的事情可能就是将力作用在游戏物体上。
尽管你不必学习物理引擎原理与算法,如
- 物理引擎涉及复杂的运动学知识
- 碰撞计算与优化
为了有效使用物理引擎,你需要:
- 了解物理运动的基本知识
- 理解游戏离散仿真产生的特殊现象,避免游戏失真
- 了解可能导致性能问题的方面,使得游戏更加流畅
2、概念与建模
刚体(Rigid body)
刚体是指在运动中和受力作用后,形状和大小不变,而且内部各点的相对位置不变的物体。
- 绝对刚体实际上是不存在的,只是一种理想模型,因为任何物体在受力作用后,都或多或少地变形,如果变形的程度相对于物体本身几何尺寸来说极为微小,在研究物体运动时变形就可以忽略不计。
- 齿轮、绳索、滑轮不属于刚体,但属于该引擎。因此:物理引擎不只是刚体
unity物理引擎实现
要产生令人信服的物理行为,游戏中的物体必须正确加速,并受到碰撞、重力和其他力的影响。Unity的内置物理引擎为您提供了处理物理模拟的组件。只需几个参数设置,您就可以创建以现实方式被动行为的对象(例如,它们将被碰撞和跌落移动,但不会自己开始移动)。通过从脚本中控制物理,您可以为对象提供车辆、机器甚至一块织物的动态。
Unity 内置物理引擎建立在 PhysX 基础之上。
2.1 常用物理组件
- Rigidbody 刚体组件
点运动模型相关属性
响应力和力矩 - Collider 碰撞器
物体碰撞与碰撞检测属性
碰撞现状一般不是物体形状 - Mesh 网格
物体形状属性 - Joint 连接器(自学)
- 其他
- 衣服、常力、用户定义的力场等等
1、角色控制器
第一人称或第三人称游戏中的角色通常需要一些基于碰撞的物理效果,这样角色就不会跌穿地板或穿过墙壁。但是,通常情况下,角色的加速度和移动在物理上并不真实,因此角色可以不受动量影响而几乎瞬间加速、制动和改变方向。
角色控制器 (Character Controller) 主要用于第三人称玩家控制或者是不使用刚体物理组件的第一人称玩家控制。
改进飞碟(Hit UFO)游戏
游戏内容要求:
- 按 adapter模式 设计图修改飞碟游戏
- 使它同时支持物理运动与运动学(变换)运动
adpter模式介绍
适配器(Adapter)模式:将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。
代码更改
本次代码主要更改部分在interface、UFCFlyAction、FirstController中
FirstController.cs
public FlyActionManager actionManager;
public DiskFactory diskFactory;
public UserGUI userGUI;
public ScoreRecorder scoreRecorder;
//加载资源
private Queue<GameObject> diskQueue = new Queue<GameObject>();
private List<GameObject> diskNotHit = new List<GameObject>();
private int round = 1;
private float speed = 2f;
private bool isPlayGame = false;
private bool isGameOver = false;
private bool isGameStart = false;
private int scoreRound2 = 15;
private int scoreRound3 = 30;
//记录游戏状态
void Start ()
{
SSDirector director = SSDirector.getInstance();
director.currentScenceController = this;
diskFactory = Singleton<DiskFactory>.Instance;
scoreRecorder = Singleton<ScoreRecorder>.Instance;
actionManager = gameObject.AddComponent<FlyActionManager>() as FlyActionManager;
userGUI = gameObject.AddComponent<UserGUI>() as UserGUI;
}
//控制游戏资源
void Update ()
{
if(isGameStart)
{
if (isGameOver)
{
CancelInvoke("LoadResources");
}
if (!isPlayGame)
{
InvokeRepeating("LoadResources", 1f, speed);
isPlayGame = true;
}
SendDisk();
if (scoreRecorder.score >= scoreRound2 && round == 1)
{
round = 2;
speed = speed - 0.6f;
CancelInvoke("LoadResources");
isPlayGame = false;
}
else if (scoreRecorder.score >= scoreRound3 && round == 2)
{
round = 3;
speed = speed - 0.5f;
CancelInvoke("LoadResources");
isPlayGame = false;
}
}
}
//加载资源
public void LoadResources()
{
diskQueue.Enqueue(diskFactory.GetDisk(round));
}
private void SendDisk()
{
float position_x = 16;
if (diskQueue.Count != 0)
{
GameObject disk = diskQueue.Dequeue();
diskNotHit.Add(disk);
disk.SetActive(true);
float ran_y = Random.Range(1f, 4f);
float ran_x = Random.Range(-1f, 1f) < 0 ? -1 : 1;
disk.GetComponent<DiskData>().direction = new Vector3(ran_x, ran_y, 0);
Vector3 position = new Vector3(-disk.GetComponent<DiskData>().direction.x * position_x, ran_y, 0);
disk.transform.position = position;
float power = Random.Range(10f, 15f);
float angle = Random.Range(15f, 28f);
actionManager.UFOFly(disk,angle,power);
}
for (int i = 0; i < diskNotHit.Count; i++)
{
GameObject temp = diskNotHit[i];
if (temp.transform.position.y < -10 && temp.gameObject.activeSelf == true)
{
diskFactory.FreeDisk(diskNotHit[i]);
diskNotHit.Remove(diskNotHit[i]);
userGUI.BloodReduce();
}
}
}
//击中飞碟事件
public void Hit(Vector3 pos)
{
Ray ray = Camera.main.ScreenPointToRay(pos);
RaycastHit[] hits;
hits = Physics.RaycastAll(ray);
bool not_hit = false;
for (int i = 0; i < hits.Length; i++)
{
RaycastHit hit = hits[i];
if (hit.collider.gameObject.GetComponent<DiskData>() != null)
{
for (int j = 0; j < diskNotHit.Count; j++)
{
if (hit.collider.gameObject.GetInstanceID() == diskNotHit[j].gameObject.GetInstanceID())
{
not_hit = true;
}
}
if(!not_hit)
{
return;
}
diskNotHit.Remove(hit.collider.gameObject);
scoreRecorder.Record(hit.collider.gameObject);
Transform explode = hit.collider.gameObject.transform.GetChild(0);
explode.GetComponent<ParticleSystem>().Play();
StartCoroutine(WaitingParticle(0.08f, hit, diskFactory, hit.collider.gameObject));
break;
}
}
}
//等待击中时特效发生
IEnumerator WaitingParticle(float wait_time, RaycastHit hit, DiskFactory disk_factory, GameObject obj)
{
yield return new WaitForSeconds(wait_time);
hit.collider.gameObject.transform.position = new Vector3(0, -9, 0);
disk_factory.FreeDisk(obj);
}
interface.cs
public interface ISSActionCallback
{
void SSActionEvent(SSAction source, SSActionEventType events = SSActionEventType.Competeted,
int intParam = 0, string strParam = null, Object objectParam = null);
}
//场景控制类
public interface ISceneController
{
void LoadResources();
}
//在已有的接口类中添加击中方法,配置为Disk的Adapter接口
public interface IUserAction
{
void Restart();
void Hit(Vector3 pos);
void GameOver();
int GetScore();
void BeginGame();
}
UFCFlyAction.cs
public class UFOFlyAction : SSAction {
public float gravity = -5;
//设置物体重力
private Vector3 startVector;//物体随机出现
private Vector3 gravityVector = Vector3.zero;//物体做平抛运动时改变的位置
private float time;//记录物体运动时间
private Vector3 currentAngle = Vector3.zero;//记录抛出角度
private UFOFlyAction()
{
}
public static UFOFlyAction GetSSAction(Vector3 direction, float angle, float power)
{
UFOFlyAction action = CreateInstance<UFOFlyAction>();
if (direction.x == -1)
{
action.startVector = Quaternion.Euler(new Vector3(0, 0, -angle)) * Vector3.left * power;
}
else
{
action.startVector = Quaternion.Euler(new Vector3(0, 0, angle)) * Vector3.right * power;
}
return action;
}
public override void Update()
{
time += Time.fixedDeltaTime;
gravityVector.y = gravity * time;
transform.position += (startVector + gravityVector) * Time.fixedDeltaTime;
currentAngle.z = Mathf.Atan((startVector.y + gravityVector.y) / startVector.x);
transform.eulerAngles = currentAngle;
if(this.transform.position.y < -10)
{
this.destroy = true;
this.callback.SSActionEvent(this);
}
}
public override void Start()
{
}
视频演示
3D游戏编程与设计第五次作业演示视频