Unity学习记录——物理系统与碰撞

Unity学习记录——物理系统与碰撞

前言

​ 本文是中山大学软件工程学院2020级3d游戏编程与设计的作业6

编程题:简单打飞碟——物理引擎改进版

1.题目要求

改进飞碟(Hit UFO)游戏:

  • 游戏内容要求:
  1. adapter模式设计图修改飞碟游戏
  2. 使他同时支持物理运动与运动学(变换)运动

2.基本介绍

(1)Adapter模式1
  • 定义:将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。
  • 优点:
    1. 客户端通过适配器可以透明地调用目标接口。
    2. 复用了现存的类,程序员不需要修改原有代码而重用现有的适配者类。
    3. 将目标类和适配者类解耦,解决了目标类和适配者类接口不一致的问题。
    4. 在很多业务场景中符合开闭原则。
  • 实现:可采取多重继承的方式实现,釆用将现有组件库中已经实现的组件引入适配器类中,该类同时实现当前系统的业务接口。
(2)unity物理引擎
  • 物理引擎

​ 物理引擎(Physici Engine)是一个软件组织,它将游戏世界对象赋予现实世界物理属性(重量、形状等)并抽象为刚体(Rigid)模型(也包括滑轮、绳索等),使得游戏物体在力的作用下,仿真现实世界的运动及物体之间的碰撞过程。记载牛顿经典力学模型基础之上,提供简单的API完成计算物体的运动、旋转和碰撞,实现近乎真实的运动与碰撞效果。

  • unity物理引擎2

    unity物理引擎主要包含角色控制器、刚体、力与力矩、碰撞体、触发器等组件

    • 角色控制器:主要用于第三人称玩家控制或者是不使用刚体物理组件的第一人称玩家控制。
    • 刚体(Rigidbody):为游戏对象赋予物理属性,使游戏对象在物理系统的控制下接受推力与扭力,从而实现现实世界中的运动效果。
    • 力与力矩:在物体之间的作用过程表现出来。其中,力作用于物体的重心使物体产生该方向上的加速度,力矩则使物体产生角加速度
    • 碰撞体(Collider):定义物体形状以便用于物理碰撞。
    • 触发器:用来触发事件,可用于检测碰撞发生

3.代码解读

在这里插入图片描述

​ 本次项目是上一次项目的改进版本,主要是使用了物理引擎实现了本次飞碟的运动,以及利用Adapter模式实现了物理运动部分的代码复用。代码部分实现较为简单,只需要进行部分增加与修改即可。

必要操作

​ 打开之前的预制件,添加刚体组件,同时锁住三轴的Rotation防止飞碟在飞行的时候发生各个方向上的旋转

在这里插入图片描述

代码改正

​ 在写这一次作业的时候发现了上一次作业存在一处bug。

​ 上一次作业由于使用函数计算运动变换,所以此处bug并未显示出来,在使用运动引擎模拟运动之后就可以发现此bug。

​ 具体为飞碟工厂之中,回收飞碟的时候忘记了给free的对象的Active置为false,使得在物理引擎之中,后续发出的飞碟保留了原来的部分数值,导致其下降速度极其快,修改之后此问题已经解决。

public void FreeDisk()
{
   	for (int i = 0; i < used.Count; i++)
	{
   		if (used[i].gameObject.transform.position.y <= -10f)
        {
         	used[i].gameObject.SetActive(false); // 此处修复了bug
            free.Add(used[i]);
            used.Remove(used[i]);
        }
    }
}

SSAction

​ 为了适配物理引擎添加了FixedUpdate()

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SSAction : ScriptableObject
{
    public bool lived = true;
    public bool deleted = false;
    public GameObject gameobject;
    public Transform transform;
    public ISSActionCallback callback;
    // 不允许代码中生成该类
    protected SSAction() { }
    public virtual void Start()
    {
        throw new System.NotImplementedException();
    }
    public virtual void Update()
    {
        throw new System.NotImplementedException();
    }
    // 添加部分
    public virtual void FixedUpdate() {
        throw new System.NotImplementedException();
    }
}
SSActionManager

​ 同样是为了适配物理引擎添加了FixUpdate()

protected void Update()
{
    foreach (SSAction ac in waitingAdd)
    {
        actions[ac.GetInstanceID()] = ac;
	}
	waitingAdd.Clear();
	foreach (KeyValuePair<int, SSAction> kv in actions)
	{
		SSAction ac = kv.Value;
		if (ac.deleted)
        {
            waitingDelete.Add(ac.GetInstanceID());
        }
        else if (ac.lived)
        {
            ac.Update();
            ac.FixedUpdate();	// 新增部分
        }
    }
    foreach (int key in waitingDelete)
    {
        SSAction ac = actions[key];
        actions.Remove(key);
        Object.Destroy(ac);
   	}
    waitingDelete.Clear();
}
FlyAction

​ 同样是新增了FixUpdate(),但是不会对之前代码起作用,仅仅是起到派生父类代码的作用

public class FlyAction : SSAction
{
    public float gravity = -5;                                 //向下的加速度
    private Vector3 start_vector;                              //初速度向量
    private Vector3 gravity_vector = Vector3.zero;             //加速度的向量,初始时为0
    private Vector3 current_angle = Vector3.zero;              //当前时间的欧拉角
    private float time;                                        //已经过去的时间

    private FlyAction() { }
    public static FlyAction GetSSAction(int lor, float angle, float power)
    {
        //初始化物体将要运动的初速度向量
        FlyAction action = CreateInstance<FlyAction>();
        if (lor == -1)
        {
            action.start_vector = Quaternion.Euler(new Vector3(0, 0, -angle)) * Vector3.left * power;
        }
        else
        {
            action.start_vector = Quaternion.Euler(new Vector3(0, 0, angle)) * Vector3.right * power;
        }
        return action;
    }

    public override void Update()
    {
        //计算物体的向下的速度,v=at
        time += Time.fixedDeltaTime;
        gravity_vector.y = gravity * time;

        //位移模拟
        transform.position += (start_vector + gravity_vector) * Time.fixedDeltaTime;
        current_angle.z = Mathf.Atan((start_vector.y + gravity_vector.y) / start_vector.x) * Mathf.Rad2Deg;
        transform.eulerAngles = current_angle;

        //如果物体y坐标小于-10,动作就做完了
        if (this.transform.position.y < -10)
        {
            this.deleted = true;
            this.callback.SSActionEvent(this);
        }
    }
    public override void FixedUpdate() { }	// 新增部分
    public override void Start() { }
}
PhysisFlyAction

​ 新增的类,通过物理引擎实现运动,与上面的FlyAction实现思路一致

public class PhysisFlyAction : SSAction {
    private Vector3 start_vector;                              
    public float power;
    private PhysisFlyAction() { }
    public static PhysisFlyAction GetSSAction(int loc, float power) {
        PhysisFlyAction action = CreateInstance<PhysisFlyAction>();
        if (loc == -1) {
            action.start_vector = Vector3.left * power;
        }
        else {
            action.start_vector = Vector3.right * power;
        }
        action.power = power;
        return action;
    }

    public override void Update() { }

    public override void FixedUpdate() {
        if (transform.position.y <= -10f) {
            gameobject.GetComponent<Rigidbody>().velocity = new Vector3(0, 0, 0);
            this.deleted = true;
            this.callback.SSActionEvent(this);
        }
    }

    public override void Start() {
        gameobject.GetComponent<Rigidbody>().AddForce(start_vector*3, ForceMode.Impulse);
    }
}
FlyActionManager

​ 飞碟的动作管理类,新增了物理运动的调用函数。

​ 思路与之前运动学变换的函数思路基本一致,由于物理引擎特性,仅需要传入参数仅需要力的数值,无需传入角度

public class FlyActionManager : SSActionManager
{
    public FlyAction fly;
    public PhysisFlyAction ph_fly;
    public FirstController scene_controller;
    protected void Start()
    {
        scene_controller = (FirstController)SSDirector.GetInstance().CurrentSceneController;
        scene_controller.action_manager = this;
    }
    public void DiskFly(GameObject disk, float angle, float power)
    {
        disk.GetComponent<Rigidbody>().isKinematic = true;
        int loc = disk.transform.position.x < 0 ? 1 : -1;
        fly = FlyAction.GetSSAction(loc, angle, power);
        this.RunAction(disk, fly, this);
    }
    // 新增部分,与上面的函数实现思路一致
    public void DiskFly(GameObject disk, float power)
    {
        disk.GetComponent<Rigidbody>().isKinematic = false;
        int loc = disk.transform.position.x < 0 ? 1 : -1;
        ph_fly = PhysisFlyAction.GetSSAction(loc, power);
        this.RunAction(disk, ph_fly, this);
    }
}
FirstController

​ 新增了SendDisk()中切换运动方式的开关

private void SendDisk(int type)
{
    GameObject disk = disk_factory.GetDisk(type);

    float ran_y = 0;
    float ran_x = Random.Range(-1f, 1f) < 0 ? -1 : 1;

    float power = 0;
    float angle = 0;
    if (type == 1)
    {
        ran_y = Random.Range(5f, 8f);
        power = Random.Range(5f, 8f);
        angle = Random.Range(15f, 20f);
    }
    else if (type == 2)
    {
        ran_y = Random.Range(3f, 6f);
        power = Random.Range(10f, 13f);
        angle = Random.Range(10f, 15f);
    }
    else
    {
        ran_y = Random.Range(1f, 3f);
        power = Random.Range(15f, 18f);
        angle = Random.Range(5f, 10f);
    }
    disk.transform.position = new Vector3(ran_x * 16f, ran_y, 0);
    // action_manager.DiskFly(disk, angle, power);
    action_manager.DiskFly(disk, power);
}

4.演示

​ gif图如下,此处演示为物理引擎的运动,与上次基本一致

在这里插入图片描述

代码位置

​ 代码以及文档均已经上传至hw6 · XiaoChen04_3/3D_Computer_Game_Programming - gitee

部分资料参考


  1. 适配器模式(Adapter模式)详解 - 腾讯云开发者社区-腾讯云 (tencent.com) ↩︎

  2. unity物理引擎介绍-CSDN博客 ↩︎

猜你喜欢

转载自blog.csdn.net/jmpei32534/article/details/127979904