Unity learning record - physics system and collision

Unity learning record - physics system and collision

Preface

​ This article is assignment 6 of 2020 3D Game Programming and Design from the School of Software Engineering, Sun Yat-sen University

Programming question: Simple flying saucer - improved version of physics engine

1. Question requirements

Improved Hit UFO game:

  • Game content requirements:
  1. Pressadapter modeDesign drawing modification flying saucer game
  2. Allowing it to support both physical movement and kinematic (transformation) movement

2.Basic introduction

(1)Adapter model1
  • Definition: Convert the interface of a class into another interface desired by the customer, so that classes that cannot work together due to incompatible interfaces can work together.
  • advantage:
    1. The client can transparently call the target interface through the adapter.
    2. Existing classes are reused, and programmers do not need to modify the original code and reuse existing adapter classes.
    3. Decoupling the target class and the adapter class solves the problem of inconsistent interfaces between the target class and the adapter class.
    4. It complies with the opening and closing principle in many business scenarios.
  • Implementation: It can be implemented through multiple inheritance, by introducing components that have been implemented in the existing component library into the adapter class, which also implements the business interface of the current system.
(2)Unity physics engine
  • Physics engine

​ Physici Engine is a software organization that assigns game world objects to real-world physical attributes (weight, shape, etc.) and abstracts them into rigid body (Rigid) models (also including pulleys, ropes, etc.), so that game objects can Under the action of , simulate the movement of the real world and the collision process between objects. Based on Newton's classical mechanics model, it provides a simple API to calculate the movement, rotation and collision of objects, achieving near-realistic movement and collision effects.

  • unity physics pull2

    The unity physics engine mainly includes components such as character controllers, rigid bodies, forces and moments, collision bodies, and triggers.

    • Character controller: Mainly used for third-person player control or first-person player control that does not use rigid body physics components.
    • Rigidbody: Assign physical attributes to game objects, allowing the game objects to accept thrust and torsion under the control of the physical system, thereby achieving motion effects in the real world.
    • Forces and moments: the interaction between objects. Among them, the force acting on the center of gravity of the object causes the object to produce acceleration in that direction, and the torque causes the object to produce angular acceleration.
    • Collider: Defines the shape of the object for use in physical collisions.
    • Trigger: used to trigger events and can be used to detect collisions

3. Code interpretation

Insert image description here

​ This project is an improved version of the previous project. It mainly uses the physics engine to realize the movement of the flying saucer, and uses the Adapter mode to realize the code reuse of the physical movement part. The implementation of the code part is relatively simple and only requires partial additions and modifications.

Necessary operation

​ Open the previous prefab, add rigid body components, and lock the three-axis Rotation to prevent the flying saucer from rotating in all directions while flying.

Insert image description here

Code correction

While writing this assignment, I discovered a bug in the previous assignment.

Since the last assignment used a function to calculate motion transformation, the bug here was not displayed. This bug can be discovered after using the motion engine to simulate motion.

​ Specifically, in the flying saucer factory, when recycling the flying saucer, they forgot to set the Active of the free object to false, so that in the physics engine, the subsequent flying saucer retained some of its original values, causing it to drop extremely fast. Modification This issue has since been resolved.

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

​Added in order to adapt to the physics engineFixedUpdate()

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

​ Also added to adapt to the physics engineFixUpdate()

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

​ is also addedFixUpdate(), but it will not affect the previous code, it only functions to derive the parent class code

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

​ The new class realizes motion through the physics engine, which is consistent with the above FlyAction implementation.

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

​ The action management class of the flying saucer has added a new calling function for physical movement.

​ The idea is basically the same as the previous kinematic transformation function idea. Due to the characteristics of the physics engine, only the value of the force needs to be passed in as the parameter, and there is no need to pass in the angle.

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

​ Added a switch to switch exercise modes in 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.Demonstration

​The gif picture is as follows. The movement demonstrated here is the movement of the physics engine, which is basically the same as last time.

Insert image description here

code location

​ The code and documents have been uploaded tohw6 · XiaoChen04_3/3D_Computer_Game_Programming - gitee

Some information reference


  1. Detailed explanation of adapter mode (Adapter mode) - Tencent Cloud Developer Community-Tencent Cloud (tencent.com) ↩︎

  2. Introduction to Unity Physics Engine-CSDN Blog ↩︎

Guess you like

Origin blog.csdn.net/jmpei32534/article/details/127979904