unity3d第三人称wasd控制的C#代码move函数的

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了unity3d第三人称wasd控制的C#代码move函数的相关的知识,希望对你有一定的参考价值。

参考技术A 附件上传不了,只有复制了,将就看

using UnityEngine;
using System.Collections;
public class FPSWalker_edit02 : MonoBehaviour

/// 类似于VRML的控制方式
/// ↑前进 ↓后退 →右转 ←左转
/// Ctrl + →右平移 Ctrl + ←左平移
/// 按住鼠标左键,可以通过鼠标上下左右转动视角
public float speed = 6.0f;
public float jumpSpeed = 8.0f;
public float gravity = 20.0f;
public bool MouseChange = true;
private Vector3 moveDirection = Vector3.zero;
private bool grounded = false;
void FixedUpdate()

if (grounded)

if (!(Input.GetKey(KeyCode.LeftControl)||Input.GetKey(KeyCode.RightControl))) //如果没有按下左Ctrl键

//只能前后平移
moveDirection = new Vector3(0, 0, Input.GetAxis("Vertical"));
moveDirection = transform.TransformDirection(moveDirection);
moveDirection *= speed;

else //

//可前后左右平移
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
CharacterController controller = GetComponent("CharacterController") as CharacterController;
CollisionFlags flags = controller.Move(moveDirection * Time.deltaTime);
grounded = (flags & CollisionFlags.CollidedBelow) != 0; //当controller处在空中间,grounded为false,即跳动和行走都无效
//鼠标控制视角
// if (Input.GetMouseButton(0) && (Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl)) && MouseChange) //如果按下鼠标左键并且鼠标MouseChange为真
if (Input.GetMouseButton(0) && MouseChange) //如果按下鼠标左键并且鼠标MouseChange为真

///鼠标旋转视角部分
///
if (axes == RotationAxes.MouseXAndY)

// Read the mouse input axis
//这里,rotationX和rotationY用来保存对象现有的角度,同时还将鼠标的移动中计算出增减的角度并合进来
rotationX += Input.GetAxis("Mouse X") * sensitivityX;
rotationY += Input.GetAxis("Mouse Y") * sensitivityY;
rotationX = ClampAngle(rotationX, minimumX, maximumX);
rotationY = ClampAngle(rotationY, minimumY, maximumY);
Quaternion xQuaternion = Quaternion.AngleAxis(rotationX, Vector3.up); //通过左右值和Vector3.up(作为以Y为旋转轴的向量值)求出左右旋转度的四元数值
Quaternion yQuaternion = Quaternion.AngleAxis(rotationY, Vector3.left); //通过上下值和Vector3.left(作为以X为旋转轴的向量值)求出上下旋转度的四元数值
//originalRotation = transform.localRotation;
transform.localRotation = originalRotation * xQuaternion * yQuaternion; //将上面求出来的左右和上下两个四元数值添加入角度中

else if (axes == RotationAxes.MouseX)

rotationX += Input.GetAxis("Mouse X") * sensitivityX;
rotationX = ClampAngle(rotationX, minimumX, maximumX);
Quaternion xQuaternion = Quaternion.AngleAxis(rotationX, Vector3.up);
transform.localRotation = originalRotation * xQuaternion;

else

rotationY += Input.GetAxis("Mouse Y") * sensitivityY;
rotationY = ClampAngle(rotationY, minimumY, maximumY);
Quaternion yQuaternion = Quaternion.AngleAxis(rotationY, Vector3.left);
transform.localRotation = originalRotation * yQuaternion;


else

///左右旋转
///并且没有按下左或右ctrl键时
if (!(Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl)))

Vector3 angle_temp = transform.eulerAngles;
angle_temp.y += Input.GetAxis("Horizontal") * sensitivityX * 0.3f;
rotationX = ClampAngle(angle_temp.y, minimumX, maximumX);
//rotationY = ClampAngle(rotationY, minimumY, maximumY);
transform.eulerAngles = angle_temp;

//键盘上下键控制俯仰角
//else
//
// Vector3 angle_temp = transform.eulerAngles;
// angle_temp.x += Input.GetAxis("Vertical") * -1 * sensitivityY * 0.3f;
// rotationY = ClampAngle(angle_temp.x , minimumY , maximumY);
// transform.eulerAngles = angle_temp;
//


public enum RotationAxes MouseXAndY = 0, MouseX = 1, MouseY = 2
public RotationAxes axes = RotationAxes.MouseXAndY;
public float sensitivityX = 15F;
public float sensitivityY = 15F;
public float minimumX = -360F;
public float maximumX = 360F;
public float minimumY = -60F;
public float maximumY = 60F;
public float rotationX = 0F;
public float rotationY = 0F;
Quaternion originalRotation;
void Start()

// Make the rigid body not change rotation
//使刚体不会改变角度
if (rigidbody)
rigidbody.freezeRotation = true;
//originalRotation = transform.localRotation; //BUG处
originalRotation = new Quaternion(0f, 0f, 0f, 1f); //修正代码

public static float ClampAngle(float angle, float min, float max)

if (angle < -360F)
angle += 360F;
if (angle > 360F)
angle -= 360F;
return Mathf.Clamp(angle, min, max);

本回答被提问者采纳

[Unity]尝试实现第三人称控制器(其四移动控制②)

[Unity]尝试实现第三人称控制器(其四、移动控制②)

【声明】此第三人称控制器是复现unity明星资产中第三人称控制器,如果有需要可以直接下载该资产并学习。我个人是不喜欢重复造轮子的行为的,但是官方的控制器我使用时有些不尽如人意的BUG。尽管如此,该控制器的效果让我觉得很不错,虽然无法实现无缝动画,不过个人项目应该先想办法让其动起来才行。
【版本】此项目基于Unity【2021.3lts】版本制作

方向修正

还记得我们在Move方法中的位移角度的控制吗?
我们既然获得了摄像机,现在我们只需要将摄像机的角度也计算进来,我们的方向和摄像机就达到了联动效果

_targetRotation = Mathf.Atan2(currentInput.x, currentInput.z) * Mathf.Rad2Deg 
		+ _mainCamera.transform.eulerAngles.y;

我自己的代码里加了一部分对于动画的控制,这部分以后再讲,仅仅是对一个值的控制罢了。

速度拓展

接下来我们可以制作跑步之类的速度了。我们可以使用如下两种方式制作:
Ⅰ.对不同的状态进行bool值的创建。当激活某个状态时,直接将该bool值激活。
Ⅱ.对一组相斥的状态创建enum,如idle,walk,run应该时互斥的,为其创建一组互斥状态。
老实讲,我认为两种方式没什么太大的差距,enum不熟悉,第一种就足够。第二种主要是看的会清晰一点点。还是那句话,没有优劣,仅仅是是否习惯。

public enum PlayerActionState

    Idle,
    Walk,
    Run


public PlayerActionState playerActionState = PlayerActionState.Idle;

我们现在考虑对这个状态的控制放到哪里。其实你可以放在之前的输入控制部分,毕竟我们角色的状态大都是由输入值来决定的,我们在那边改变后,只需要在Move中结合角色的坠落之类的信息判断角色的状态即可。当然,我们在输入控制中也做过对外的状态数据,所以我们也可以在Move中直接查看。我们首先在每次Move执行的一开始对角色目前的状态进行判断

private void PlayerStateJudge()

	playerActionState = PlayerActionState.Idle;
	if (_inputsMassage.move!=Vector2.zero)
	
		playerActionState = PlayerActionState.Walk;
		if (_inputsMassage.run)
			playerActionState = PlayerActionState.Run;
	

判断完成后,我们根据状态为_currentSpeed赋值

_currentSpeed = playerActionState switch

	PlayerActionState.Idle => 0f,
	PlayerActionState.Walk => walkSpeed,
	PlayerActionState.Run => _runSpeed,
	_ => 0f
;

这样就可以跑步了。

新的状态

我们会为角色添加下蹲的状态,当然,这个效果涉及到动画的更改
因为是较为独立的一个状态,我们通过bool控制。
我们在Move的开始去判断(请在所有状态判断时考虑依赖问题,将无需依赖的优先判断)
然后考虑run等状态与下蹲状态的优先度,越优先越靠后赋值
我们认为当你进入下蹲状态后,无法跑步,所以固定速度为下蹲行走速度。当退出下蹲状态不执行。

_currentSpeed = playerActionState switch

	PlayerActionState.Idle => 0f,
	PlayerActionState.Walk => walkSpeed,
	PlayerActionState.Run => _runSpeed,
	_ => 0f
;
if (_isCrouch) 
	_currentSpeed = _crouchSpeed;

速度的逐渐变化

目前来说,我们的速度是直接变化的。如果有朋友连接上动画进行测试会发现,我们的速度由于是直接变化的,动画与动画之间存在“跳变”。我们需要逐渐改变速度来润滑这个速度
如果学过数值分析,大家会发现这个就是插值。那么插值时,我们需要获取两个点,才能对其创建插值函数。那么这两个点是什么?就是当前的速度和要变化到的速度。
我们首先记录当前速度:

//玩家当前水平速度的参考
float currentHorizontalSpeed = new Vector3(_characterController.velocity.x, 
        0.0f, _characterController.velocity.z).magnitude;//偏离度,保证目标速度与目前速度相差大才可以插值,避免小幅度的抽搐
float speedOffset = 0.1f;

这里的magnitude是精确长度(我们之前说过速度和向量的问题,这里只需要大小)
接下来会有语义重复的情况,所以我们上方判断速度变成targetSpeed。
我们开始判断偏离度,如果说目标值和当前值相差很大,我们需要有一个插值变化过程使其顺滑;当小于偏离度后,我们认为直接变化也不会有人能够看出来,所以直接将其改变。

//判断偏离度
if (currentHorizontalSpeed < targetSpeed - speedOffset ||
	currentHorizontalSpeed > targetSpeed + speedOffset)

	_currentSpeed = Mathf.Lerp(currentHorizontalSpeed, targetSpeed ,Time.deltaTime * SpeedChangeRate);
	//四舍五入到小数点后3位
	_currentSpeed = Mathf.Round(_currentSpeed * 1000f) / 1000f;

else

	_currentSpeed = targetSpeed;

至此,我们的移动控制部分基本完成。我在与动画系统相连后,会出现动画抖动的问题,如果各位也有这种问题或者有解决这个问题的方式,请在下方留言

代码

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

public class ThirdPlayerMoveController : MonoBehaviour

    CharacterController _characterController;
    PlayerInput _playerInput;
    PlayerInputsMassage _inputsMassage;
    GameObject _mainCamera;
    Animator _anim;

    public PlayerActionState playerActionState = PlayerActionState.Idle;

    [Header("相机设置")]
    public GameObject _cinemachineFollowTarget;
    float _cinemachineTagertX;
    float _cinemachineTagertY;
    [Tooltip("相机仰角")]
    public float TopClamp = 70.0f;
    [Tooltip("相机俯角")]
    public float BottomClamp = -30.0f;
    [Tooltip("额外的度数覆盖摄像头。有用的微调相机位置时,锁定")]
    public float CameraAngleOverride = 0.0f;

    [Header("玩家设置")]
    [Tooltip("这将决定普通行走时的速度")]
    public float walkSpeed = 1.5f;
    [Tooltip("这将决定跑步时的速度")]
    public float _runSpeed = 5.0f;
    private bool _isCrouch = false;
    [Tooltip("这将决定蹲下行走的速度")]
    public float _crouchSpeed = 1.0f;
    [Tooltip("这将决定不同状态速度间切换的速度")]
    public float SpeedChangeRate = 10.0f;

    private float _currentSpeed;
    private float _targetRotation = 0.0f;
    [Tooltip("角色光滑旋转时间")]
    private float RotationSmoothTime = 0.12f;
    [Tooltip("在角色光滑旋转过程中的速度")]
    private float _rotationVelocity;

    private float _threshold = 0.01f;

    private bool IsCurrentDeviceMouse
    
        get  return _playerInput.currentControlScheme == "KeyboardMouse"; 
    

    private void Awake()
    
        // get a reference to our main camera
        if (_mainCamera == null)
        
            _mainCamera = GameObject.FindGameObjectWithTag("MainCamera");
        
    

    // Start is called before the first frame update
    void Start()
    
        _characterController = GetComponent<CharacterController>();
        _inputsMassage = GetComponent<PlayerInputsMassage>();
        _playerInput = GetComponent<PlayerInput>();
        _anim = GetComponentInChildren<Animator>();
    

    private void FixedUpdate()
    
        Move();
    

    private void LateUpdate()
    
        CameraRotation();
    

    /// <summary>
    /// 相机追踪点的控制
    /// </summary>
    private void CameraRotation()
    
        if(_inputsMassage.look.sqrMagnitude>_threshold)//look值大于误差代表有输入
        
            float deltaTimeMultiplier = IsCurrentDeviceMouse ? 1f : Time.deltaTime;

            _cinemachineTagertX += _inputsMassage.look.x * deltaTimeMultiplier;
            _cinemachineTagertY += _inputsMassage.look.y * deltaTimeMultiplier;
        
        _cinemachineTagertX = ClampAngle(_cinemachineTagertX, float.MinValue, float.MaxValue);
        _cinemachineTagertY = ClampAngle(_cinemachineTagertY, BottomClamp, TopClamp);

        _cinemachineFollowTarget.transform.rotation = Quaternion.Euler((-_cinemachineTagertY - CameraAngleOverride) * Settings.mouseYmoveTimes,
                _cinemachineTagertX * Settings.mouseXmoveTimes, 0.0f);
    

    private void Move()
    
        _isCrouch = _inputsMassage.crouch;
        //在这里进行状态的判断
        PlayerStateJudge();

        //首先将移动速度赋予临时变量,考虑到有可能在其他地方使用,我们将其存储起来
        //_currentSpeed = walkSpeed;(转换为更加完善的速度控制)
        float targetSpeed = playerActionState switch
        
            PlayerActionState.Idle => 0f,
            PlayerActionState.Walk => walkSpeed,
            PlayerActionState.Run => _runSpeed,
            _ => 0f
        ;
        if (_isCrouch) targetSpeed = _crouchSpeed;

        //玩家当前水平速度的参考
        float currentHorizontalSpeed = new Vector3(_characterController.velocity.x, 0.0f, _characterController.velocity.z).magnitude;
        //偏离度,保证目标速度与目前速度相差大才可以插值,避免小幅度的抽搐
        float speedOffset = 0.1f;
        //判断偏离度
        if (currentHorizontalSpeed < targetSpeed - speedOffset ||
                currentHorizontalSpeed > targetSpeed + speedOffset)
        
            _currentSpeed = Mathf.Lerp(currentHorizontalSpeed, targetSpeed ,Time.deltaTime * SpeedChangeRate);
            //四舍五入到小数点后3位
            _currentSpeed = Mathf.Round(_currentSpeed * 1000f) / 1000f;
        
        else
        
            _currentSpeed = targetSpeed;
        

        //判断是否进行移动输入
        if (_inputsMassage.move == Vector2.zero) _currentSpeed = 0;

        var currentInput = new Vector3(_inputsMassage.move.x, 0, _inputsMassage.move.y).normalized;

        //单位向量的方向,或者说位移方向
        if (_inputsMassage.move!=Vector2.zero)
        
            _targetRotation = Mathf.Atan2(currentInput.x, currentInput.z) * Mathf.Rad2Deg + _mainCamera.transform.eulerAngles.y;


            #region 在位移过程中的转向
            float rotation = Mathf.SmoothDampAngle(transform.eulerAngles.y, _targetRotation, ref _rotationVelocity,
                RotationSmoothTime);
            transform.rotation = Quaternion.Euler(0.0f, rotation, 0.0f);
            #endregion

        

        Vector3 targetDir = Quaternion.Euler(0.0f, _targetRotation, 0.0f) * Vector3.forward;

        _characterController.Move(targetDir.normalized * _currentSpeed * Time.deltaTime);
        //TODO:这里的Move可以执行垂直方向的速度,直接加上垂直的Vector就可以

        _anim.SetFloat("Speed", _currentSpeed);
        _anim.SetBool("Crouch", _isCrouch);
    

    /// <summary>
    /// 限制角度
    /// </summary>
    /// <param name="lfAngle"></param>
    /// <param name="lfMin"></param>
    /// <param name="lfMax"></param>
    /// <returns></returns>
    private static float ClampAngle(float lfAngle, float lfMin, float lfMax)
    
        if (lfAngle < -360f) lfAngle += 360f;
        if (lfAngle > 360f) lfAngle -= 360f;
        return Mathf.Clamp(lfAngle, lfMin, lfMax);
    

    /// <summary>
    /// 对玩家状态进行判断
    /// </summary>
    private void PlayerStateJudge()
    
        playerActionState = PlayerActionState.Idle;
        if (_inputsMassage.move!=Vector2.zero)
        
            playerActionState = PlayerActionState.Walk;
            if (_inputsMassage.run)
                playerActionState = PlayerActionState.Run;
        

    



以上是关于unity3d第三人称wasd控制的C#代码move函数的的主要内容,如果未能解决你的问题,请参考以下文章

Unity3D鼠标控制摄像机“左右移动控制视角+WASD键盘控制前后左右+空格键抬升高度”脚本

Unity3D鼠标控制摄像机“左右移动控制视角+WASD键盘控制前后左右+空格键抬升高度”脚本

Unity3D鼠标控制摄像机“左右移动控制视角+WASD键盘控制前后左右+空格键抬升高度”脚本

Unity3d怎么控制第三人称的朝向

求unity3d 用wasd与空格键控制人物移动的代码。

unity第一人称控制器脚步声在哪