1. 程式人生 > >用角色控制器控制移動和碰撞

用角色控制器控制移動和碰撞

Unity3d中CharacterController的移動和碰撞分析

這個地方值得記錄一下.之前看教程的時候沒注意,後來才發現這裡涉及到很重要的角色移動方式和碰撞檢測問題.可惜今天沒時間了,等之後有時間了再來補上.

有必要仔細檢視一下官方文件,養成習慣.


Character Controller API

官方註釋:

A CharacterController allows you to easily do movement constrained by collisions without having to deal with a rigidbody.

也就是說,角色控制器自帶的移動函式包含了碰撞體,不用再讓我們自己設定剛體和碰撞體了.

這個元件有兩個移動函式可以使用.

SimpleMove()

Moves the character with speed.
Velocity along the y-axis is ignored. Speed is in meters/s. Gravity is automatically applied. Returns if the character is grounded. It is recommended that you make only one call to Move or SimpleMove per frame.

最重要的一點就是,這個函式的Y軸速度是被限制了的.這個方法自帶Gravity即重力屬性,需要接地,所以物體不能騰空運動.這個方法接收一個速度向量.

下面是官方示例:

using UnityEngine;
using System.Collections;

[RequireComponent(typeof(CharacterController))]
public class ExampleClass : MonoBehaviour {
    public float speed = 3.0F;
    public float rotateSpeed = 3.0F;
    void Update() {
        CharacterController controller = GetComponent<CharacterController>();
        transform.Rotate(0, Input.GetAxis("Horizontal") * rotateSpeed, 0);
        Vector3 forward = transform.TransformDirection(Vector3.forward);
        float curSpeed = speed * Input.GetAxis("Vertical");
        controller.SimpleMove(forward * curSpeed);
    }

Move()

官方註釋:

A more complex move function taking absolute movement deltas.
Attempts to move the controller by motion, the motion will only be constrained by collisions. It will slide along colliders. CollisionFlags is the summary of collisions that occurred during the Move. This function does not apply any gravity.

與上面的方法相對,這個方法需要我們控制很多屬性.首先它沒有Gravity屬性,如果不設定的話物體會飄起來;當然,這也意味著我們可以通過控制它來實現跳躍的方法(其實Gravity就是Y方向向量的加速度值).這個方法還帶CollisionFlags屬性,這個屬性可以迴應本方法的collision所接觸到的所有coliders.可以利用這個屬性做一些類似Mask的功能,無視某一類碰撞體.

官方示例:

using UnityEngine;
using System.Collections;

public class ExampleClass : MonoBehaviour {
    public float speed = 6.0F;
    public float jumpSpeed = 8.0F;
    public float gravity = 20.0F;
    private Vector3 moveDirection = Vector3.zero;
    void Update() {
        CharacterController controller = GetComponent<CharacterController>();
        if (controller.isGrounded) {
            moveDirection = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
            moveDirection = transform.TransformDirection(moveDirection);
            moveDirection *= speed;
            if (Input.GetButton("Jump"))
                moveDirection.y = jumpSpeed;
            
        }
        moveDirection.y -= gravity * Time.deltaTime;
        controller.Move(moveDirection * Time.deltaTime);
    }
}

Move() 的 CollisionFlags 屬性

CollisionFlags is a bitmask returned by CharacterController.Move.
It gives you a broad overview of where your character collided with any other objects.

四個屬性分別是:
None, Sides(碰撞體邊), Above(對應天花板), Below(對應地面)

示例:

using UnityEngine;
using System.Collections;

public class ExampleClass : MonoBehaviour
{
    void Update()
    {
        CharacterController controller = GetComponent<CharacterController>();
        if (controller.collisionFlags == CollisionFlags.None)
            print("Free floating!");

        if ((controller.collisionFlags & CollisionFlags.Sides) != 0)
            print("Touching sides!");

        if (controller.collisionFlags == CollisionFlags.Sides)
            print("Only touching sides, nothing else!");

        if ((controller.collisionFlags & CollisionFlags.Above) != 0)
            print("Touching sides!");

        if (controller.collisionFlags == CollisionFlags.Above)
            print("Only touching Ceiling, nothing else!");

        if ((controller.collisionFlags & CollisionFlags.Below) != 0)
            print("Touching ground!");

        if (controller.collisionFlags == CollisionFlags.Below)
            print("Only touching ground, nothing else!");
    }
}

OnControllerColliderHit(ControllerColliderHit)

OnControllerColliderHit is called when the controller hits a collider while performing a Move.
This can be used to push objects when they collide with the character.

官方將它歸類於Message中,我不知道是不是訊息函式的意思.由官方註釋可知它可以用來實現碰撞體之間的推動.

using UnityEngine;
using System.Collections;

public class ExampleClass : MonoBehaviour {
    public float pushPower = 2.0F;
    void OnControllerColliderHit(ControllerColliderHit hit) {
        Rigidbody body = hit.collider.attachedRigidbody;
        if (body == null || body.isKinematic)   // 不存在或者設定
            return;                             // 為運動學特徵
        
        if (hit.moveDirection.y < -0.3F)    // 可以理解為摩檫力過大吧
            return;
        
        Vector3 pushDir = new Vector3(hit.moveDirection.x, 0, hit.moveDirection.z);
        body.velocity = pushDir * pushPower;
    }
}