[Tutorial de desarrollo de juegos de Unity] Los conceptos básicos cero te llevarán de principiante a Super Dios 16: cuatro formas de controlar el movimiento de los personajes usando controladores de personajes

Cuatro formas de controlar el movimiento del personaje.

  • Modificar directamente la posición del componente
  • Vaya al centro comercial de recursos para descargar el sistema de control de personajes terminado y úselo directamente
  • Componente de controlador de personajes proporcionado por Unity.
  • Hágalo usted mismo a través del sistema de física.

Modificar directamente la posición del componente

La forma más básica es la forma más directa de cambiar la posición de un objeto y es
adecuada para situaciones
de demostración donde no existe un sistema físico ni requisitos especiales para el movimiento.

public float speed;

void Update()
{
    
    
    Move();
}

void Move()
{
    
    
    float MoveX = Input.GetAxisRaw("Horizontal");
    float MoveZ = Input.GetAxisRaw("Vertical");
    Vector3 dir = new Vector3(MoveX, 0, MoveZ).normalized;

    //1、Translate
    transform.Translate(dir * Time.deltaTime * speed);

    //2、修改positon
    //transform.position += dir * Time.deltaTime * speed;

    //3、TransformPoint
    //transform.TransformPoint(dir * Time.deltaTime * speed);
}

Vaya al centro comercial de recursos para descargar el sistema de control de personajes terminado.

Por ejemplo, ve al centro comercial para buscar una tercera persona.
Uno

Para un uso específico, consulte el artículo:
https://zhuanlan.zhihu.com/p/31662004?mode=light

Componente de controlador de personajes proporcionado por Unity.

1. Primero debemos agregar el componente Controlador de personajes al personaje.
Insertar descripción de la imagen aquí

parámetro describir
Límite de pendiente Pendiente máxima que se puede mover directamente hacia arriba
Desplazamiento de paso Altura máxima del obstáculo que se puede cruzar directamente
Ancho de la piel Grosor de la piel, un valor mayor puede reducir la inquietud, un valor menor puede hacer que el personaje se atasque, generalmente se establece en el 10% del radio ( : pspero esto hará que el personaje no toque el suelo, por lo que en este momento puedes ajustar el valor del eje Y de Centro para que el personaje toque el suelo.)
Distancia mínima de movimiento Cuando la distancia de movimiento único del personaje es menor que este valor, se ignorará y no tendrá efecto. Se puede utilizar para reducir la inquietud.
Radio y altura El radio y la altura del personaje controlado.

2. Cree un nuevo script MoveText, cuélguelo en el personaje y escriba el script de control.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class MoveTest : MonoBehaviour
{
    
    
    private CharacterController player;//创建角色控制器
    void Start()
    {
    
    
        player = GetComponent<CharacterController>();

    }

    void Update()
    {
    
    
        //获取水平轴
        float horizontal = Input.GetAxis("Horizontal");
        //获取垂直轴
        float vertical = Input.GetAxis("Vertical");
        //创建成一个方向向量
        Vector3 dir = new Vector3(horizontal,0,vertical);
        //让物体朝该方向移动
        //player.Move(dir);   	//更复杂的移动,不计算重力影响
        player.SimpleMove(dir);	//以一定速度来移动,移动时自动计算重力因素影响
    }
}

3. Ejecute la depuración
Insertar descripción de la imagen aquí

API de controlador de caracteres

Variables/Métodos describir
se fundamenta Si el controlador del personaje está tocando el suelo.
límite de pendiente Límite de grado de pendiente
paso de compensación Altura que se puede cruzar sobre escalones, en metros
detectar colisiones Si otros cuerpos rígidos y controladores de personajes pueden colisionar con este controlador de personajes. El valor predeterminado es verdadero.
Movimiento simple() Muévete a cierta velocidad y calcula automáticamente la influencia de los factores de gravedad al moverte.
Mover() Movimientos más complejos que no tienen en cuenta los efectos de la gravedad.

Determinar si el controlador del personaje está tocando el suelo.

void Update()
{
    
    
	if (controller.isGrounded)
	{
    
    
		print("正在地面上...");
	}
}

La diferencia entre SimpleMove y Move

  1. movimiento simple
  • No se ve afectado por la velocidad del eje Y y tiene su propio efecto de gravedad.无法实现跳跃功能
  • El valor de retorno es Bool, cuando el carácter toca el suelo, devuelve Verdadero, de lo contrario devuelve Falso.
  1. Mover
  • Sin efecto de gravedad, date cuenta de la gravedad tú mismo.可做跳跃功能
  • Valor de retorno (objeto CollisionFlags), devuelve información sobre la colisión entre el personaje y el objeto.

Optimización de código, control de caracteres más complejo.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class MoveTest : MonoBehaviour
{
    
    
    public float speed = 1; //移动速度 默认值1
    public float gravity = 1;//掉落速度(重力) 默认值1

    private CharacterController player;//创建角色控制器

    bool isGrounded = false;
    float fallSpeed = 0; //掉落速度 会随着滞空时间不断增加

    // Start is called before the first frame update
    void Start()
    {
    
    
        player = GetComponent<CharacterController>();
    }

    // Update is called once per frame
    void Update()
    {
    
    
        Movement();
    }

    void Movement()
    {
    
    
        //player提供了isGrounded接口用于检测是否落地
        isGrounded = player.isGrounded;

        //不在地面时,下坠速度不断增加
        if (!isGrounded)
        {
    
    
            fallSpeed -= gravity;
        }
        else
        {
    
    
            if (fallSpeed < 0)
            {
    
    
                fallSpeed = 0;
            }
        }

        //平移
        float MoveX = Input.GetAxisRaw("Horizontal");
        float MoveZ = Input.GetAxisRaw("Vertical");
        //Vector3 dir = new Vector3(MoveX, 0, MoveZ).normalized;

        //平移加上掉落 
        Vector3 v = (transform.forward * MoveZ + transform.right * MoveX).normalized * speed + transform.up * fallSpeed;
        // Debug.Log(v);
        // Debug.Log(Time.deltaTime);
        //1、simpleMove 带重力的移动
        // player.SimpleMove(dir * speed * Time.deltaTime);
        //2、Move 不带重力 需要自己设置重力
        player.Move(v * Time.deltaTime);
    }
}

Hágalo usted mismo a través del sistema de física.

El método físico 施加力hace que el objeto se mueva cambiando el estado de movimiento del objeto. El componente central es el Rigibody.

Una breve introducción a Rigibody

parámetro significado Función
Masa calidad La masa de un objeto (en unidades arbitrarias). Se recomienda que la masa de un objeto no difiera 100 veces de la de otros objetos.
Arrastrar resistencia La resistencia del aire que experimenta un objeto cuando es movido por una fuerza. 0 significa que no hay resistencia del aire. Cuando es extremadamente grande, el objeto dejará de moverse inmediatamente.
Arrastre angular resistencia angular La resistencia del aire que experimenta un objeto cuando gira mediante un par. 0 significa que no hay resistencia del aire. Cuando es extremadamente alta, el objeto dejará de girar inmediatamente.
Usa la gravedad Usa la gravedad Si el objeto se ve afectado por la gravedad. Si se activa, el objeto se ve afectado por la gravedad.
es cinemático ¿Es kinesiología? Si el objeto del juego obedece las leyes de la física cinemática. Si se activa, el objeto ya no es impulsado por el motor de física y solo puede manipularse mediante transformaciones. Adecuado para simular una plataforma móvil o simular cuerpos rígidos conectados por juntas de bisagra
Interpolar interpolación Modo de interpolación de movimiento de objetos. Cuando se encuentra fluctuación cuando se produce un movimiento de cuerpo rígido, puede probar las siguientes opciones: Ninguna, no se aplica ninguna interpolación; Interpolar, que suaviza la transformación de este cuadro basándose en la transformación del cuadro anterior; Extrapolar, que utiliza la transformación del siguiente fotograma para suavizar la transformación de este fotograma
Detección de colisiones Comprobación de impacto Modo de detección de colisiones. Se utiliza para evitar que objetos a alta velocidad atraviesen otros objetos sin provocar una colisión. Los modos de colisión incluyen Discreto (discontinuo), Continuo (continuo) y Continuo Dinámico (dinámico continuo). Entre ellos, el modo Discreto se usa para detectar colisiones con otros colisionadores u otros objetos en la escena; el modo Continuo se usa para detectar colisiones con colisionadores dinámicos. Colisión de objetos (cuerpos rígidos); el modo dinámico continuo se utiliza para detectar colisiones con objetos en modo continuo y modo dinámico continuo, y es adecuado para objetos de alta velocidad.
Restricciones restricción Restricciones al movimiento de cuerpos rígidos. Entre ellos, Congelar posición significa que el movimiento del cuerpo rígido a lo largo del eje seleccionado en el mundo no será válido, y Congelar rotación significa que la rotación del cuerpo rígido a lo largo de los ejes X, Y y Z seleccionados en el mundo será inválido.

manifestación

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[RequireComponent(typeof(Rigidbody))]
public class RigibodyMove : MonoBehaviour
{
    
    
    Rigidbody rb;
    public float speed;

    void Start()
    {
    
    
        rb = GetComponent<Rigidbody>();
    }

    // Update is called once per frame
    void Update()
    {
    
    
        Move();
    }

    void Move()
    {
    
    
        float MoveX = Input.GetAxisRaw("Horizontal");
        float MoveZ = Input.GetAxisRaw("Vertical");
        Vector3 dir = new Vector3(MoveX, 0, MoveZ).normalized;

        //1、AddForce
        //rb.AddForce(dir * speed * Time.deltaTime);
        //2、修改velocity
        //rb.velocity += dir * speed * Time.deltaTime;
        //3、MovePosition
        rb.MovePosition(transform.position + dir * speed * Time.deltaTime);
    }
}

expandir

De hecho, hay muchas cuestiones que deben tenerse en cuenta para un controlador de personaje completo. Como la gravedad, los saltos, la resistencia, el movimiento del aire, el manejo de pendientes, el manejo de pasos, el sprint, el agacharse, etc., debemos considerar el tipo de juego que estamos realizando para elegir la solución adecuada.

Dos controles de uso común son Character y RIgidBody. Sus funciones integradas se enumeran a continuación.

1.Controlador de caracteres

  • manejo de pendientes
  • pasos de manejo
  • No se quedará atrapado en la pared.

2.Cuerpo rígido

  • Viene con gravedad
  • proporcionar resistencia
  • Puede interactuar con objetos físicos.

El siguiente es un código de controlador de personajes relativamente completo proporcionado por el jefe.

Primero un script móvil

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class MoveTest:MonoBehaviour
{
    
    
    private CharacterController cc;//创建角色控制器
    public float speed = 12f;
    public float gravity = -9.81f;
    public float jumpHeight = 3f; //跳跃高度

    public Transform groundCheck;//用来获取生成球体的位置
    public float groundDistance = 0.4f;
    public LayerMask groundMask;//值判断为地面的层

    Vector3 velocity;
    bool isGrounded; //检测是否在地面


    
    void Start()
    {
    
    
        cc = GetComponent<CharacterController>();
    }

    // Update is called once per frame
    void Update()
    {
    
    
        MoveMent();
    }

    void MoveMent()
    {
    
    
        //CheckSphere 检验球,这段代码看起来很懵,其实这只是创建一个看不见的球用来检测,碰到就是true没有碰到就是false
        //第一个值是生产的位置,第二个值是圆的半径,第三个值是可筛选的层
        isGrounded = Physics.CheckSphere(groundCheck.position, groundDistance, groundMask);//检测是否在地面

        if(isGrounded && velocity.y < 0)
        {
    
    
            //防止在真正掉落到地面前 停止掉落
            velocity.y = -2;
        }

        float MoveX = Input.GetAxisRaw("Horizontal");
        float MoveZ = Input.GetAxisRaw("Vertical");

        Vector3 dir = (transform.right * MoveX + transform.forward * MoveZ).normalized;

        //平移
        cc.Move(dir * speed * Time.deltaTime);

        if (Input.GetButtonDown("Jump") && isGrounded)
        {
    
    
            //物理公式 v=sqrt(v*-2*g)
            velocity.y = Mathf.Sqrt(jumpHeight * -2f * gravity);
        }

        velocity.y += gravity * Time.deltaTime;
        //再乘一个Time.deltaTime是由物理决定的 1/2gt^2
        cc.Move(velocity * Time.deltaTime);
    }
}

Luego un script de controlador de perspectiva

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class MouseLook : MonoBehaviour
{
    
    
    public float mouseSensitivity = 100f;

    public Transform playerBody;

    float xRotation = 0f;
    // Start is called before the first frame update
    void Start()
    {
    
    
        Cursor.lockState = CursorLockMode.Locked;
    }

    // Update is called once per frame
    void Update()
    {
    
    
        FreeLook();
    }

    void FreeLook()
    {
    
    
        float mouseX = Input.GetAxis("Mouse X") * mouseSensitivity * Time.deltaTime;
        float mouseY = Input.GetAxis("Mouse Y") * mouseSensitivity * Time.deltaTime;

        xRotation -= mouseY;
        xRotation = Mathf.Clamp(xRotation, -90f, 90f);

        //摄像头旋转x轴
        transform.localRotation = Quaternion.Euler(xRotation, 0f, 0f);

        //角色旋转y轴
        playerBody.Rotate(Vector3.up * mouseX);
    }
}

La configuración de los parámetros es la siguiente:
Insertar descripción de la imagen aquí

Un controlador de personajes más completo que tiene en cuenta deslizamientos de tierra, lesiones causadas por caídas desde alturas, juicio sobre el paradero, carreras, saltos de conejos, etc. (el control de perspectiva no está incluido)

// From: https://wiki.unity3d.com/index.php/FPSWalkerEnhanced
// Modified:
// 1. Namespace to prevent conflicts.
// 2. Only checks Horizontal and Vertical inputs.

namespace PixelCrushers.SceneStreamer.Example
{
    
    
    using UnityEngine;

    [RequireComponent(typeof(CharacterController))]
    public class FPSWalkerEnhanced : MonoBehaviour
    {
    
    
        [Tooltip("How fast the player moves when walking (default move speed).")]
        [SerializeField]
        private float m_WalkSpeed = 6.0f;

        [Tooltip("How fast the player moves when running.")]
        [SerializeField]
        private float m_RunSpeed = 11.0f;

        [Tooltip("If true, diagonal speed (when strafing + moving forward or back) can't exceed normal move speed; otherwise it's about 1.4 times faster.")]
        [SerializeField]
        public bool m_LimitDiagonalSpeed = true;

        [Tooltip("If checked, the run key toggles between running and walking. Otherwise player runs if the key is held down.")]
        [SerializeField]
        private bool m_ToggleRun = false;

        //[Tooltip("How high the player jumps when hitting the jump button.")]
        //[SerializeField]
        //private float m_JumpSpeed = 8.0f;

        [Tooltip("How fast the player falls when not standing on anything.")]
        [SerializeField]
        private float m_Gravity = 20.0f;

        [Tooltip("Units that player can fall before a falling function is run. To disable, type \"infinity\" in the inspector.")]
        [SerializeField]
        private float m_FallingThreshold = 10.0f;

        [Tooltip("If the player ends up on a slope which is at least the Slope Limit as set on the character controller, then he will slide down.")]
        [SerializeField]
        private bool m_SlideWhenOverSlopeLimit = false;

        [Tooltip("If checked and the player is on an object tagged \"Slide\", he will slide down it regardless of the slope limit.")]
        [SerializeField]
        private bool m_SlideOnTaggedObjects = false;

        [Tooltip("How fast the player slides when on slopes as defined above.")]
        [SerializeField]
        private float m_SlideSpeed = 12.0f;

        [Tooltip("If checked, then the player can change direction while in the air.")]
        [SerializeField]
        private bool m_AirControl = false;

        [Tooltip("Small amounts of this results in bumping when walking down slopes, but large amounts results in falling too fast.")]
        [SerializeField]
        private float m_AntiBumpFactor = .75f;

        //[Tooltip("Player must be grounded for at least this many physics frames before being able to jump again; set to 0 to allow bunny hopping.")]
        //[SerializeField]
        //private int m_AntiBunnyHopFactor = 1;

        private Vector3 m_MoveDirection = Vector3.zero;
        private bool m_Grounded = false;
        private CharacterController m_Controller;
        private Transform m_Transform;
        private float m_Speed;
        private RaycastHit m_Hit;
        private float m_FallStartLevel;
        private bool m_Falling;
        private float m_SlideLimit;
        private float m_RayDistance;
        private Vector3 m_ContactPoint;
        private bool m_PlayerControl = false;
        //private int m_JumpTimer;


        private void Start()
        {
    
    
            // Saving component references to improve performance.
            m_Transform = GetComponent<Transform>();
            m_Controller = GetComponent<CharacterController>();

            // Setting initial values.
            m_Speed = m_WalkSpeed;
            m_RayDistance = m_Controller.height * .5f + m_Controller.radius;
            m_SlideLimit = m_Controller.slopeLimit - .1f;
            //m_JumpTimer = m_AntiBunnyHopFactor;
        }


        //private void Update()
        //{
    
    
        //    // If the run button is set to toggle, then switch between walk/run speed. (We use Update for this...
        //    // FixedUpdate is a poor place to use GetButtonDown, since it doesn't necessarily run every frame and can miss the event)
        //    if (m_ToggleRun && m_Grounded && Input.GetButtonDown("Run"))
        //    {
    
    
        //        m_Speed = (m_Speed == m_WalkSpeed ? m_RunSpeed : m_WalkSpeed);
        //    }
        //}


        private void FixedUpdate()
        {
    
    
            float inputX = Input.GetAxis("Horizontal");
            float inputY = Input.GetAxis("Vertical");

            // If both horizontal and vertical are used simultaneously, limit speed (if allowed), so the total doesn't exceed normal move speed
            float inputModifyFactor = (inputX != 0.0f && inputY != 0.0f && m_LimitDiagonalSpeed) ? .7071f : 1.0f;

            if (m_Grounded)
            {
    
    
                bool sliding = false;
                // See if surface immediately below should be slid down. We use this normally rather than a ControllerColliderHit point,
                // because that interferes with step climbing amongst other annoyances
                if (Physics.Raycast(m_Transform.position, -Vector3.up, out m_Hit, m_RayDistance))
                {
    
    
                    if (Vector3.Angle(m_Hit.normal, Vector3.up) > m_SlideLimit)
                    {
    
    
                        sliding = true;
                    }
                }
                // However, just raycasting straight down from the center can fail when on steep slopes
                // So if the above raycast didn't catch anything, raycast down from the stored ControllerColliderHit point instead
                else
                {
    
    
                    Physics.Raycast(m_ContactPoint + Vector3.up, -Vector3.up, out m_Hit);
                    if (Vector3.Angle(m_Hit.normal, Vector3.up) > m_SlideLimit)
                    {
    
    
                        sliding = true;
                    }
                }

                // If we were falling, and we fell a vertical distance greater than the threshold, run a falling damage routine
                if (m_Falling)
                {
    
    
                    m_Falling = false;
                    if (m_Transform.position.y < m_FallStartLevel - m_FallingThreshold)
                    {
    
    
                        OnFell(m_FallStartLevel - m_Transform.position.y);
                    }
                }

                // If running isn't on a toggle, then use the appropriate speed depending on whether the run button is down
                if (!m_ToggleRun)
                {
    
    
                    m_Speed = Input.GetKey(KeyCode.LeftShift) ? m_RunSpeed : m_WalkSpeed;
                }

                // If sliding (and it's allowed), or if we're on an object tagged "Slide", get a vector pointing down the slope we're on
                if ((sliding && m_SlideWhenOverSlopeLimit) || (m_SlideOnTaggedObjects && m_Hit.collider.tag == "Slide"))
                {
    
    
                    Vector3 hitNormal = m_Hit.normal;
                    m_MoveDirection = new Vector3(hitNormal.x, -hitNormal.y, hitNormal.z);
                    Vector3.OrthoNormalize(ref hitNormal, ref m_MoveDirection);
                    m_MoveDirection *= m_SlideSpeed;
                    m_PlayerControl = false;
                }
                // Otherwise recalculate moveDirection directly from axes, adding a bit of -y to avoid bumping down inclines
                else
                {
    
    
                    m_MoveDirection = new Vector3(inputX * inputModifyFactor, -m_AntiBumpFactor, inputY * inputModifyFactor);
                    m_MoveDirection = m_Transform.TransformDirection(m_MoveDirection) * m_Speed;
                    m_PlayerControl = true;
                }

                // Jump! But only if the jump button has been released and player has been grounded for a given number of frames
                //if (!Input.GetButton("Jump"))
                //{
    
    
                //    m_JumpTimer++;
                //}
                //else if (m_JumpTimer >= m_AntiBunnyHopFactor)
                //{
    
    
                //    m_MoveDirection.y = m_JumpSpeed;
                //    m_JumpTimer = 0;
                //}
            }
            else
            {
    
    
                // If we stepped over a cliff or something, set the height at which we started falling
                if (!m_Falling)
                {
    
    
                    m_Falling = true;
                    m_FallStartLevel = m_Transform.position.y;
                }

                // If air control is allowed, check movement but don't touch the y component
                if (m_AirControl && m_PlayerControl)
                {
    
    
                    m_MoveDirection.x = inputX * m_Speed * inputModifyFactor;
                    m_MoveDirection.z = inputY * m_Speed * inputModifyFactor;
                    m_MoveDirection = m_Transform.TransformDirection(m_MoveDirection);
                }
            }

            // Apply gravity
            m_MoveDirection.y -= m_Gravity * Time.deltaTime;

            // Move the controller, and set grounded true or false depending on whether we're standing on something
            m_Grounded = (m_Controller.Move(m_MoveDirection * Time.deltaTime) & CollisionFlags.Below) != 0;
        }


        // Store point that we're in contact with for use in FixedUpdate if needed
        private void OnControllerColliderHit(ControllerColliderHit hit)
        {
    
    
            m_ContactPoint = hit.point;
        }


        // This is the place to apply things like fall damage. You can give the player hitpoints and remove some
        // of them based on the distance fallen, play sound effects, etc.
        private void OnFell(float fallDistance)
        {
    
    
            print("Ouch! Fell " + fallDistance + " units!");
        }
    }
}

Supongo que te gusta

Origin blog.csdn.net/qq_36303853/article/details/129721957
Recomendado
Clasificación