繁体   English   中英

Unity - 使用新输入系统/Character Controller 让角色看着鼠标

[英]Unity - Make character look at mouse with New input system/Character Controller

一直按照本指南https://youtu.be/bXNFxQpp2qk?t=1280了解角色 Controller。这让我开始进行基本的运动,在 21:20 时,他创造了一种旋转玩家的方法。

https://youtu.be/b0AQg5ZTpac和面向鼠标的播放器的此视频 position。在 7:15 标记处,她解释了如何获取我将其存储在positionToLookAt中的 mousePosition

我希望我的角色的旋转是用鼠标完成的,并且有一个像这里这样的视野: https://youtu.be/rQG9aUWarwE

一直想弄到让玩家朝向鼠标position的方向,一直没有得到结果。 此处显示https://imgur.com/gallery/mPPWogi视频结果和我的 PlayerInputs。

一些同行提到替换Quaternion targetRotation = Quaternion.LookRotation(positionToLookAt); 通过Quaternion targetRotation = Quaternion.LookRotation(positionToLookAt - transform.position);

但它不起作用并在 imgur 中产生了相同的结果。

有人可以帮我从这里出去吗? 我被卡住了,我不知道如何让这个角色看着鼠标。

这是方法的片段

   void handleRotation()
    {
        Vector3 positionToLookAt;

        // get mouse position
        Vector2 mousePosition = playerInput.CharacterControls.MousePosition.ReadValue<Vector2>();
        mousePosition = Camera.main.ScreenToWorldPoint(mousePosition);

        // insert mouse position to looking position
        positionToLookAt.x = currentMovement.x;
        //positionToLookAt.x = mousePosition.x;
        positionToLookAt.y = 0.0f;
        positionToLookAt.z = currentMovement.z;
        //positionToLookAt.z = mousePosition.y;

        Quaternion currentRotation = transform.rotation;

        if (isMovementPressed)
        {
            Quaternion targetRotation = Quaternion.LookRotation(positionToLookAt);
            transform.rotation = Quaternion.Slerp(currentRotation, targetRotation, rotationFactorPerFrame * Time.deltaTime);
        }

    }

如果有人想要我的完整代码,请在这里。

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


public class AnimationAndMovementController : MonoBehaviour
{

    PlayerInput playerInput;
    UnityEngine.CharacterController characterController;

    Animator animator;

    int isWalkingHash;
    int isRunningHash;


    Vector2 currentMovementInput;
    Vector3 currentMovement;
    Vector3 currentRunMovement;
    bool isMovementPressed;
    bool isRunPressed;
    float rotationFactorPerFrame = 15.0f;
    float runMultiplier = 3.0f;

    //Debug.Log(context.ReadValue<Vector2>());



    void Start()
    {
        
    }

    void Awake()
    {
        playerInput = new PlayerInput();
        characterController = GetComponent<UnityEngine.CharacterController>();
        animator = GetComponent<Animator>();

        isWalkingHash = Animator.StringToHash("isWalking");
        isRunningHash = Animator.StringToHash("isRunning");


        playerInput.CharacterControls.Move.started += onMovementInput;
        playerInput.CharacterControls.Move.canceled += onMovementInput;
        playerInput.CharacterControls.Move.performed += onMovementInput;

        playerInput.CharacterControls.Run.started += onRun;
        playerInput.CharacterControls.Run.canceled += onRun;

    }

    void onRun(InputAction.CallbackContext context)
    {
        isRunPressed = context.ReadValueAsButton();
    }


    void onMovementInput (InputAction.CallbackContext context)
    {

        currentMovementInput = context.ReadValue<Vector2>();
        currentMovement.x = currentMovementInput.x;
        currentMovement.z = currentMovementInput.y;

        currentRunMovement.x = currentMovementInput.x * runMultiplier;
        currentRunMovement.z = currentMovementInput.y * runMultiplier;

        isMovementPressed = currentMovementInput.x != 0 || currentMovementInput.y != 0;

    }




    void handleRotation()
    {
        Vector3 positionToLookAt;

        Vector2 mousePosition = playerInput.CharacterControls.MousePosition.ReadValue<Vector2>();
        mousePosition = Camera.main.ScreenToWorldPoint(mousePosition);

        positionToLookAt.x = currentMovement.x;
        //positionToLookAt.x = mousePosition.x;
        positionToLookAt.y = 0.0f;
        positionToLookAt.z = currentMovement.z;
        //positionToLookAt.z = mousePosition.y;

        Quaternion currentRotation = transform.rotation;

        if (isMovementPressed)
        {
            Quaternion targetRotation = Quaternion.LookRotation(positionToLookAt);
            transform.rotation = Quaternion.Slerp(currentRotation, targetRotation, rotationFactorPerFrame * Time.deltaTime);
        }

    }

    void handleAnimation()
    {
        bool isWalking = animator.GetBool(isWalkingHash);
        bool isRunning = animator.GetBool(isRunningHash);

        if (isMovementPressed && !isWalking) {
            animator.SetBool(isWalkingHash, true);
        }
        else if (!isMovementPressed && isWalking){
            animator.SetBool(isWalkingHash, false);
        }

        if ((isMovementPressed && isRunPressed) && !isRunning)
        {
            animator.SetBool(isRunningHash, true);
        }
        else if ((!isMovementPressed && !isRunPressed) && isRunning)
        {
            animator.SetBool(isRunningHash, false);
        }
        else if ((isMovementPressed && !isRunPressed) && isRunning)
        {
            animator.SetBool(isRunningHash, false);
        }

    }


    void handleGravity()
    {

       

        if (characterController.isGrounded) {
            float groundedGravity = -0.05f;
            currentMovement.y = groundedGravity;
            currentRunMovement.y = groundedGravity;
        } else {
            float gravity = -9.8f;
            currentMovement.y = gravity;
            currentRunMovement.y = gravity;
        }
    }




    // Update is called once per frame
    void Update()
    {
        handleRotation();
        handleAnimation();
        handleGravity();

        if (isRunPressed) {
            characterController.Move(currentRunMovement * Time.deltaTime);
        }
        else {
            characterController.Move(currentMovement * Time.deltaTime);
        }

        
    }


    void OnEnable()
    {
        playerInput.CharacterControls.Enable();
    }

    void OnDisable()
    {
        playerInput.CharacterControls.Disable();
    }
}

编辑

更新了完整代码, handleRotation() 是处理旋转的地方:

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

public class AnimationAndMovementController : MonoBehaviour
{

    PlayerInput playerInput;
    CharacterController characterController;


    Animator animator;

    int isWalkingHash;
    int isRunningHash;

    Camera _camera;

    Vector2 currentMovementInput;
    Vector3 currentMovement;
    Vector3 currentRunMovement;

    bool isMovementPressed;
    bool isRunPressed;

    //float rotationFactorPerFrame = 15.0f;
    float runMultiplier = 3.0f;



    // Start is called before the first frame update
    void Start()
    {
        _camera = Camera.main;
    }

    void Awake()
    {
        playerInput = new PlayerInput();
        characterController = GetComponent<CharacterController>();
        animator = GetComponent<Animator>();

        isWalkingHash = Animator.StringToHash("isWalking");
        isRunningHash = Animator.StringToHash("isRunning");


        playerInput.CharacterControls.Move.started += onMovementInput;
        playerInput.CharacterControls.Move.canceled += onMovementInput;
        playerInput.CharacterControls.Move.performed += onMovementInput;

        playerInput.CharacterControls.Run.started += onRun;
        playerInput.CharacterControls.Run.canceled += onRun;

    }


    void onRun(InputAction.CallbackContext context)
    {
        isRunPressed = context.ReadValueAsButton();
    }


    void onMovementInput(InputAction.CallbackContext context)
    {

        currentMovementInput = context.ReadValue<Vector2>();
        currentMovement.x = currentMovementInput.x;
        currentMovement.z = currentMovementInput.y;

        currentRunMovement.x = currentMovementInput.x * runMultiplier;
        currentRunMovement.z = currentMovementInput.y * runMultiplier;

        isMovementPressed = currentMovementInput.x != 0 || currentMovementInput.y != 0;

    }



    void handleAnimation()
    {
        bool isWalking = animator.GetBool(isWalkingHash);
        bool isRunning = animator.GetBool(isRunningHash);

        if (isMovementPressed && !isWalking)
        {
            animator.SetBool(isWalkingHash, true);
        }
        else if (!isMovementPressed && isWalking)
        {
            animator.SetBool(isWalkingHash, false);
        }

        if ((isMovementPressed && isRunPressed) && !isRunning)
        {
            animator.SetBool(isRunningHash, true);
        }
        else if ((!isMovementPressed && !isRunPressed) && isRunning)
        {
            animator.SetBool(isRunningHash, false);
        }
        else if ((isMovementPressed && !isRunPressed) && isRunning)
        {
            animator.SetBool(isRunningHash, false);
        }

    }


    void handleGravity()
    {
        if (characterController.isGrounded)
        {
            float groundedGravity = -0.05f;
            currentMovement.y = groundedGravity;
            currentRunMovement.y = groundedGravity;
        }
        else
        {
            float gravity = -9.8f;
            currentMovement.y = gravity;
            currentRunMovement.y = gravity;
        }
    }



    void handleRotation()
    {

        // We're getting a Vector2, whereas we will need a Vector3
        // Get a z value based on camera, and include it in a Vector3
        var mousePosition = playerInput.CharacterControls.MousePosition.ReadValue<Vector2>();
        var mousePositionZ = _camera.farClipPlane * .5f;
        var mouseViewportPosition = _camera.ScreenToWorldPoint(new Vector3(mousePosition.x, mousePosition.y, mousePositionZ));


        // Do the same with the object's position
        var positionOnViewport = Camera.main.WorldToViewportPoint(transform.position);

        // Get the angle between the points
        var angle = AngleBetweenTwoPoints(positionOnViewport, mouseViewportPosition);

        // Apply the angle as the rotation of the object
        transform.rotation = Quaternion.Euler(new Vector3(0f, -angle, 0f));

    }


    float AngleBetweenTwoPoints(Vector3 a, Vector3 b)
    {
        return Mathf.Atan2(b.y - a.y, b.x - a.x) * Mathf.Rad2Deg;
    }



    // Update is called once per frame
    void Update()
    {

        handleRotation();
        handleAnimation();
        handleGravity();


        if (isRunPressed)
        {
            characterController.Move(currentRunMovement * Time.deltaTime);
        }
        else
        {
            characterController.Move(currentMovement * Time.deltaTime);
        }

    }


    void OnEnable()
    {
        playerInput.CharacterControls.Enable();
    }

    void OnDisable()
    {
        playerInput.CharacterControls.Disable();
    }


}

输入

编辑 2:当前迭代

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

public class AnimationAndMovementController : MonoBehaviour
{

    PlayerInput playerInput;
    CharacterController characterController;


    Animator animator;

    int isWalkingHash;
    int isRunningHash;

    Camera _camera;

    Vector2 currentMovementInput;
    Vector3 currentMovement;
    Vector3 currentRunMovement;

    bool isMovementPressed;
    bool isRunPressed;

    float rotationFactorPerFrame = 15.0f;
    float runMultiplier = 3.0f;



    // Start is called before the first frame update
    void Start()
    {
        _camera = Camera.main;
    }

    void Awake()
    {
        playerInput = new PlayerInput();
        characterController = GetComponent<CharacterController>();
        animator = GetComponent<Animator>();

        isWalkingHash = Animator.StringToHash("isWalking");
        isRunningHash = Animator.StringToHash("isRunning");


        playerInput.CharacterControls.Move.started += onMovementInput;
        playerInput.CharacterControls.Move.canceled += onMovementInput;
        playerInput.CharacterControls.Move.performed += onMovementInput;

        playerInput.CharacterControls.Run.started += onRun;
        playerInput.CharacterControls.Run.canceled += onRun;

    }


    void onRun(InputAction.CallbackContext context)
    {
        isRunPressed = context.ReadValueAsButton();
    }


    void onMovementInput(InputAction.CallbackContext context)
    {

        currentMovementInput = context.ReadValue<Vector2>();
        currentMovement.x = currentMovementInput.x;
        currentMovement.z = currentMovementInput.y;

        currentRunMovement.x = currentMovementInput.x * runMultiplier;
        currentRunMovement.z = currentMovementInput.y * runMultiplier;

        isMovementPressed = currentMovementInput.x != 0 || currentMovementInput.y != 0;

    }



    void handleAnimation()
    {
        bool isWalking = animator.GetBool(isWalkingHash);
        bool isRunning = animator.GetBool(isRunningHash);

        if (isMovementPressed && !isWalking)
        {
            animator.SetBool(isWalkingHash, true);
        }
        else if (!isMovementPressed && isWalking)
        {
            animator.SetBool(isWalkingHash, false);
        }

        if ((isMovementPressed && isRunPressed) && !isRunning)
        {
            animator.SetBool(isRunningHash, true);
        }
        else if ((!isMovementPressed && !isRunPressed) && isRunning)
        {
            animator.SetBool(isRunningHash, false);
        }
        else if ((isMovementPressed && !isRunPressed) && isRunning)
        {
            animator.SetBool(isRunningHash, false);
        }

    }


    void handleGravity()
    {
        if (characterController.isGrounded)
        {
            float groundedGravity = -0.05f;
            currentMovement.y = groundedGravity;
            currentRunMovement.y = groundedGravity;
        }
        else
        {
            float gravity = -9.8f;
            currentMovement.y = gravity;
            currentRunMovement.y = gravity;
        }
    }

    void handle_isRunPressed()
    {
        if (isRunPressed)
        {
            characterController.Move(currentRunMovement * Time.deltaTime);
        }
        else
        {
            characterController.Move(currentMovement * Time.deltaTime);
        }
    }

    void handleRotation()
    {
        var mousePosition = playerInput.CharacterControls.MousePosition.ReadValue<Vector2>();
        var mousePositionZ = _camera.farClipPlane * .5f;

        var mouseWorldPosition = _camera.ScreenToWorldPoint(new Vector3(mousePosition.x, mousePosition.y, mousePositionZ)); // _camera.ScreenToViewportPoint(mousePosition);

        // Get the angle between the points
        // Use the x and z from the object/mouse, since we're looking along the y axis
        var angle = AngleBetweenTwoPoints(new Vector2(transform.position.x, transform.position.z), new Vector2(mouseWorldPosition.x, mouseWorldPosition.z));

        transform.rotation = Quaternion.Euler(new Vector3(0f, -angle, 0f));
    }


    float AngleBetweenTwoPoints(Vector3 a, Vector3 b)
    {
        return Mathf.Atan2(b.y - a.y, b.x - a.x) * Mathf.Rad2Deg;
    }



    // Update is called once per frame
    void Update()
    {
        handleRotation();
        handleAnimation();
        handleGravity();

        handle_isRunPressed();
    }


    void OnEnable()
    {
        playerInput.CharacterControls.Enable();
    }

    void OnDisable()
    {
        playerInput.CharacterControls.Disable();
    }


}

不幸的是,在 handleRotation() 中,旋转仍然无法按预期使用我的代码

编辑 3:

void handleRotation()
    {
        // We're getting a Vector2, whereas we will need a Vector3
        // Get a z value based on camera, and include it in a Vector3
        Vector2 mousePosition = playerInput.CharacterControls.MousePosition.ReadValue<Vector2>();

        var mousePositionZ = _camera.farClipPlane * .5f;

        Vector3 mouseViewportPosition = _camera.ViewportToWorldPoint(new Vector3(mousePosition.x, mousePosition.y, _camera.transform.position.y));


        Debug.Log("MousePos: " + mouseViewportPosition);

        Vector3 positionToLookAt;

        positionToLookAt.x = mouseViewportPosition.x;
        positionToLookAt.y = 0.0f;
        //positionToLookAt.z = currentMovement.z;
        positionToLookAt.z = mouseViewportPosition.z;

        Quaternion currentRotation = transform.rotation;
        
        Quaternion targetRotation = Quaternion.LookRotation(positionToLookAt - transform.position);
        transform.rotation = Quaternion.Slerp(currentRotation, targetRotation, rotationFactorPerFrame * Time.deltaTime);
        
    }

它在旋转。 但不正确。

更新采取 2

这是我在Update()的完整代码:

var mousePosition = _playerInput.Player.MousePosition.ReadValue<Vector2>();
var mousePositionZ = _camera.farClipPlane * .5f;

var mouseWorldPosition = _camera.ScreenToWorldPoint(new Vector3(mousePosition.x, mousePosition.y, mousePositionZ)); // _camera.ScreenToViewportPoint(mousePosition);

// Get the angle between the points
// Use the x and z from the object/mouse, since we're looking along the y axis
var angle = AngleBetweenTwoPoints(new Vector2(transform.position.x, transform.position.z), new Vector2(mouseWorldPosition.x, mouseWorldPosition.z));

transform.rotation = Quaternion.Euler(new Vector3(0f, -angle, 0f));

辅助函数是一样的:

float AngleBetweenTwoPoints(Vector2 a, Vector2 b)
{
    return Mathf.Atan2(b.y - a.y, b.x - a.x) * Mathf.Rad2Deg;
}

更新

我想出了如何以约瑟夫最初尝试的方式做到这一点。 ScreenToWorldPoint确实可以在新的输入系统中正常工作。 问题在于我们尝试使用它的方式。 它需要一个Vector3

不能这样做:

// Don't do this, it wont work!
var mousePosition = _playerInput.Player.MousePosition.ReadValue<Vector2>();
var mouseViewportPosition = _camera.ScreenToWorldPoint(mousePosition);

你需要这样做:

// We're getting a Vector2, whereas we will need a Vector3
// Get a z value based on camera, and include it in a Vector3
var mousePosition = _playerInput.Player.MousePosition.ReadValue<Vector2>();
var mousePositionZ = _camera.farClipPlane * .5f;
var mouseViewportPosition = _camera.ScreenToWorldPoint(new Vector3(mousePosition.x, mousePosition.y, mousePositionZ));

原答案:

你的例子有一些问题......我认为你正在编辑它以根据移动方向进行旋转? 现在引擎中的鼠标位置可能存在问题......我注意到其他一些线程抱怨输入值不相同,但我不确定到什么程度。 也许转换为世界坐标不起作用?

出于某种原因,这对我不起作用,它总是吐出(0.0, 10.0, 0.0)

var mouseWorldPosition = Camera.main.ScreenToWorldPoint(mousePosition);

这是我能够开始工作的:

// Get the mouse position from the NEW input system
var mousePosition = _playerInput.Player.MousePosition.ReadValue<Vector2>();

// Convert the mousePosition to the VIEWPORT
var mouseViewportPosition = Camera.main.ScreenToViewportPoint(mousePosition);

// Do the same with the object's position
var positionOnViewport = Camera.main.WorldToViewportPoint(transform.position);

// Get the angle between the points
var angle = AngleBetweenTwoPoints(positionOnViewport, mouseViewportPosition);

// Apply the angle as the rotation of the object
transform.rotation = Quaternion.Euler(new Vector3(0f, -angle, 0f));

总体思路来自此处,包括此功能( https://answers.unity.com/questions/855976/make-a-player-model-rotate-towards-mouse-location.html ):

float AngleBetweenTwoPoints(Vector3 a, Vector3 b)
{
    return Mathf.Atan2(b.y - a.y, b.x - a.x) * Mathf.Rad2Deg;
}

请注意,这并不完美……角度有点偏……不知道为什么会这样。 你可以在这里看到它有点夸张:

在此处输入图片说明

我认为这里故事的寓意是检查每个阶段的输入,看看它是什么样子以及它是如何得到/得到转变的。 现在我们知道一些新的输入值可能会关闭,您可以使用旧系统的鼠标位置,或者将我的示例与您提到的其他一些方法结合起来。

添加一个新答案,因为这只会像您尝试做的那样使用四元数。

第一步是将鼠标从屏幕坐标转换为世界坐标:

// Read the mouse position from the new input system
var mousePosition = _playerInput.Player.MousePosition.ReadValue<Vector2>();
// Ensure that there is a "valid" z value so that the conversion works properly
var mousePositionZ = _camera.farClipPlane * .5f;

// Convert!
var mouseWorldPosition = _camera.ScreenToWorldPoint(new Vector3(mousePosition.x, mousePosition.y, mousePositionZ));

现在我们在世界坐标中有了鼠标 position,我们可以将它与对象(玩家)的 position 进行比较,并根据该差异进行旋转:

// Calculate the difference between the positions
var positionVector = mouseWorldPosition - transform.position;

// Match the new y value to the object's Y value.
// This ensures that the rotation is calculated only with the X and Z
// I would love to know why this is happening... but I didn't find anything in my initial research
positionVector.y = transform.position.y;

// Now we calculate the rotation
var targetRotation = Quaternion.LookRotation(positionVector);

// FYI, if your object's final rotation is off by 90 degrees, you can do the following
// I think it has to do with what the system thinks "forward" is, and which way your model is facing by default.
// So you can either fix it in your model, or add/subtract 90 degrees
// Note that **multiplying** Quaternions together effectively **combines** them
// var targetRotation = Quaternion.LookRotation(positionVector) * Quaternion.Euler(0, -90, 0);

// And smoothly transition to the new angle using Slerp
transform.rotation = Quaternion.Lerp(transform.rotation, targetRotation, rotationFactorPerFrame * Time.deltaTime);

我遇到了同样的问题,最后我为 3D Top-Down 游戏想出了更流畅的鼠标解决方案。 这就是我的整个代码的样子。 我希望它有所帮助。

[SerializeField] float moveSpeed = 5f;
[SerializeField] float turnSpeed = 2f;

Vector2 _moveInput;
Vector2 _mousePos;

void Update()
{
    HandleMovement();
    HandleRotation();
}

void OnMove(InputValue moveInput)
{
    _moveInput = moveInput.Get<Vector2>();
}

void HandleMovement()
{
    Vector3 deltaPos = new Vector3(_moveInput.x, 0f, _moveInput.y) * moveSpeed * Time.deltaTime;
    transform.position += deltaPos;
}

void OnAim(InputValue currentMousePos)
{
    _mousePos = currentMousePos.Get<Vector2>();
}

void HandleRotation()
{
    Ray ray = Camera.main.ScreenPointToRay(_mousePos);
    Plane groundPlane = new Plane(Vector3.up, Vector3.zero);
    float rayDistance;

    if (groundPlane.Raycast(ray, out rayDistance))
    {
        Vector3 point = ray.GetPoint(rayDistance);
        LookAt(point);
    }
}

void LookAt(Vector3 lookPoint)
{
    Vector3 direction = (lookPoint - transform.position).normalized;
    Quaternion lookRotation = Quaternion.LookRotation(new Vector3(direction.x, 0f, direction.z));

    transform.rotation = Quaternion.Slerp(transform.rotation, lookRotation, Time.deltaTime * turnSpeed);
}

这是 PlayerInput 设置

您也可以查看此教程视频以获取更多详细信息

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM