3D游戏小总结(第一人称、第三人称实现)

本文详细介绍了Unity3D中如何实现第一人称视角的相机控制和角色移动,以及第三人称角色的移动和相机控制。包括鼠标输入处理、角色碰撞检测、重力影响下的跳跃和移动,以及相机的旋转和平滑跟随。提供了关键代码示例,涵盖了游戏开发中的基础角色控制机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一:简易第一人称视角控制

    private Transform cameraTransform;//相机
    [SerializeField] 
    private Transform characterTransform;//人物
    private Vector3 cameraRotation;
    public float MouseSensitivity;//5
    public Vector2 MaxminAngle;//(-65,65)

    private void Start()
    {
        cameraTransform = transform;
    }

    void Update()
    {
        var tmp_MouseX = Input.GetAxis("Mouse X");
        var tmp_MouseY = Input.GetAxis("Mouse Y");

        cameraRotation.x -= tmp_MouseY * MouseSensitivity;
        cameraRotation.y += tmp_MouseX * MouseSensitivity;

        cameraRotation.x = Mathf.Clamp(cameraRotation.x, MaxminAngle.x, MaxminAngle.y);//限制人物的俯仰角度
        
        characterTransform.rotation = Quaternion.Euler(0, cameraRotation.y, 0);//人物旋转跟随鼠标
        cameraTransform.rotation = Quaternion.Euler(cameraRotation.x, cameraRotation.y, 0);   //控制相机的旋转和人物的旋转保持一致

二:第一人称移动控制

(1):常规控制移动方法:
在这里插入图片描述
在这里插入图片描述

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

public class FPSMoveMovement : MonoBehaviour
{
    public float Speed;
    public float JumpHeight;
    public float gravity;

    private Transform characterTransform;
    private Rigidbody characterRigidbody;
    private bool isGrounded;
   
    // Start is called before the first frame update
    void Start()
    {
        characterTransform = transform;
        characterRigidbody = GetComponent<Rigidbody>();
    }

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

    private void FixedUpdate()
    {
        if (isGrounded)
        {
            var tmp_Horizontal = Input.GetAxis("Horizontal");
            var tmp_Vertical = Input.GetAxis("Vertical");

            var tmp_CurrentDirection = new Vector3(tmp_Horizontal, 0, tmp_Vertical);
            tmp_CurrentDirection = characterTransform.TransformDirection(tmp_CurrentDirection);//将角色的局部坐标转化为世界坐标
            tmp_CurrentDirection *= Speed;

            var tmp_CurrentVelocity = characterRigidbody.velocity;
            var tmp_VelocityChange = tmp_CurrentDirection - tmp_CurrentVelocity;
            tmp_VelocityChange.y = 0;
            //控制角色的移动
            characterRigidbody.AddForce(tmp_VelocityChange, ForceMode.VelocityChange);
            //控制角色的跳跃
            if (Input.GetButtonDown("Jump"))
            {
                characterRigidbody.velocity=new Vector3(tmp_CurrentVelocity.x,CalculateJumpHeightSpeed(),
                    tmp_CurrentVelocity.z);
            }

        }
        characterRigidbody.AddForce(new Vector3(0, -gravity*characterRigidbody.mass, 0)); //控制角色的下落速度
    }
    //控制角色的跳跃高度
    private float CalculateJumpHeightSpeed()
    {
        return Mathf.Sqrt(2 * gravity * JumpHeight);
    }

    private void OnCollisionStay(Collision collision)
    {
        isGrounded = true;
    }
    private void OnCollisionExit(Collision collision)
    {
        isGrounded = false;
    }
}

(2):通过CharaCterController控制人物的移动方法
在这里插入图片描述

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

public class FPCharacterControllerMovement : MonoBehaviour
{
    private CharacterController characterController;
    private Animator characterAnimator;
    private Vector3 movementDirection;
    private Transform characterTransform;
    private float velocity;


    private bool isCrouched;
    private float originHeight;

    public float SprintingSpeed;
    public float WalkSpeed;

    public float SprintingSpeedWhenCrouched;
    public float WalkSpeedWhenCrouched;

    public float Gravity = 9.8f;
    public float JumpHeight;
    public float CrouchHeight = 1f;

    private void Start()
    {
        characterController = GetComponent<CharacterController>();
        characterAnimator = GetComponentInChildren<Animator>();
        characterTransform = transform;
        originHeight = characterController.height;
    }


    private void Update()
    {
        float tmp_CurrentSpeed = WalkSpeed;
        if (characterController.isGrounded)//CharacterController是否在最后一次移动接触地面
        {
            var tmp_Horizontal = Input.GetAxis("Horizontal");
            var tmp_Vertical = Input.GetAxis("Vertical");
            movementDirection =
                characterTransform.TransformDirection(new Vector3(tmp_Horizontal, 0, tmp_Vertical));//将局部坐标转换为世界坐标
            //控制人物的跳跃
            if (Input.GetButtonDown("Jump"))
            {
                movementDirection.y = JumpHeight;
            }
            //控制人物的下蹲
            if (Input.GetKeyDown(KeyCode.C))
            {
                var tmp_CurrentHeight = isCrouched ? originHeight : CrouchHeight;
                StartCoroutine(DoCrouch(tmp_CurrentHeight));
                isCrouched = !isCrouched;
            }
            //控制人物下蹲、站立的移动速度
            if (isCrouched)
            {
                tmp_CurrentSpeed = Input.GetKey(KeyCode.LeftShift) ? SprintingSpeedWhenCrouched : WalkSpeedWhenCrouched;
            }
            else
            {
                tmp_CurrentSpeed = Input.GetKey(KeyCode.LeftShift) ? SprintingSpeed : WalkSpeed;
            }

            var tmp_Velocity = characterController.velocity;
            tmp_Velocity.y = 0;
            velocity = tmp_Velocity.magnitude;//返回角色的移动速度
            characterAnimator.SetFloat("Velocity", velocity, 0.25f, Time.deltaTime);//控制人物动画的播放
        }

        movementDirection.y -= Gravity * Time.deltaTime;//角色受重力下降的效果
        characterController.Move(tmp_CurrentSpeed * Time.deltaTime * movementDirection);//控制角色的移动
    }

    /// <summary>
    /// 下蹲方法
    /// </summary>
    /// <param name="_target"></param>
    /// <returns></returns>
    private IEnumerator DoCrouch(float _target)
    {
        float tmp_CurrentHeight = 0;
        while (Mathf.Abs(characterController.height - _target) > 0.1f)
        {
            yield return null;
            characterController.height =
                Mathf.SmoothDamp(characterController.height, _target,
                    ref tmp_CurrentHeight, Time.deltaTime * 5);
        }
    }
}

人物行走状态的动画BlendTree
在这里插入图片描述

三:第三人称角色移动控制

在这里插入图片描述

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

public class M_PlayerController : MonoBehaviour
{
    private bool m_IsGrounded;//是否在地面上
    private float m_GroundCheckDistance = 1.33f;//射线检测地面的距离 从射线发射位置到地面的距离再加0.1f(具体数值自行测试)
    

    public float MoveSpeed = 10.0f;       //移动速度
    public float RotateSpeed = 10.0f;     //旋转速度
    private Rigidbody rig;               //角色的刚体组件
    private float h, v;
    Vector3 c;//指向玩家单位向量
    public Camera Camera_self;//相机

    void Start()
    {
        rig = transform.GetComponent<Rigidbody>();
        c = (transform.position - Camera_self.transform.position).normalized;

    }
    private void Update()
    {
       
        CheckGroundStatus();
        Move();
        if (Input.GetKeyDown(KeyCode.Space) && m_IsGrounded)//跳跃
        {
            rig.velocity = new Vector3(0, 5, 0);
        }
    }
    /// <summary>
    /// 人物移动
    /// </summary>
    public void Move()
    {
        h = Input.GetAxis("Horizontal");
        v = Input.GetAxis("Vertical");
        float inputMotionValue = Mathf.Max(Mathf.Abs(h), Mathf.Abs(v));
        c = (transform.position - Camera_self.transform.position).normalized;//将角色的方向和相机的方向关联起来 从而达到旋转相机 人物也跟随旋转
        string s = transform.localEulerAngles.ToString();//欧拉角
        string[] sarr = s.Split(',');
        sarr = sarr[0].Split('(');
        float anglex = float.Parse(sarr[1]);//rotation的x值
        c.y = Mathf.Tan((360 - anglex) * Mathf.Deg2Rad);//正切(弧度)
        Vector3 cc = Quaternion.AngleAxis(90, Vector3.up) * c;//c绕y轴旋转90度
        Vector3 forward = c * v + cc * h;
        if (inputMotionValue > 0)
        {
            Quaternion lookRot = Quaternion.LookRotation(forward);
            transform.rotation = Quaternion.Slerp(transform.rotation, lookRot, RotateSpeed * inputMotionValue * Time.deltaTime);
            transform.Translate(Vector3.forward * inputMotionValue * MoveSpeed * Time.deltaTime);
        }
    }

    /// <summary>
    /// 检测地面
    /// </summary>
    void CheckGroundStatus()
    {
        RaycastHit hitInfo;
        Debug.DrawLine(transform.position + (Vector3.up * 0.1f), transform.position + (Vector3.up * 0.1f) + (Vector3.down * m_GroundCheckDistance));

        if (Physics.Raycast(transform.position + (Vector3.up * 0.1f), Vector3.down, out hitInfo, m_GroundCheckDistance))
        {         
            m_IsGrounded = true;         
        }
        else
        {
            m_IsGrounded = false;                
        }
     
    }
}

四:第三人称相机控制

在这里插入图片描述

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

public class M_CameraController : MonoBehaviour
{
    public enum RotateType
    {
        UableRotate,
        HorizontalOnly,
        VerticalOnly,
        Both
    }

    public enum UpdateType
    {
        LateUpdate,
        FixedUpdate,
        Update
    }

    public Transform target;
    public Vector3 headOffset = new Vector3(0, 1.7f, 0);
    public LayerMask ignoreLayer;
    public UpdateType updateType;
    public bool smooth;
    [Range(0.1f, 1f)] public float smoothLevel = 0.5f;
    public RotateType rotateType;
    public float rotateSpeed = 10.0f;//旋转速度
    public float zoomSpeed = 5f;//缩放速度
    public float minDistance = 3.0f;
    public float maxDistance = 15.0f;
    public float maxLookUpAngle = 70.0f;
    public float maxLookDownAngle = 30.0f;

    float OriginDistance;
    float currentDistance;
    Vector3 direction;
    Vector3 disOffset;
    Vector3 visibleEuler;

    // Use this for initialization
    void Start()
    {
        ResetView();
    }

    // Update is called once per frame
    void Update()
    {
        if (updateType == UpdateType.Update)
        {
            visibleEuler = ConvertEulerAngle(transform.rotation.eulerAngles);
            float h = Input.GetAxis("Mouse X");
            float v = Input.GetAxis("Mouse Y");
            float zoom = Input.GetAxis("Mouse ScrollWheel");
            Translate(zoom);
            Rotate(h, v);
        }
    }

    private void LateUpdate()
    {
        if (updateType == UpdateType.LateUpdate)
        {
            visibleEuler = ConvertEulerAngle(transform.rotation.eulerAngles);
            float h = Input.GetAxis("Mouse X");
            float v = Input.GetAxis("Mouse Y");
            float zoom = Input.GetAxis("Mouse ScrollWheel");
            Translate(zoom);
            Rotate(h, v);
        }
    }

    private void FixedUpdate()
    {
        if (updateType == UpdateType.FixedUpdate)
        {
            visibleEuler = ConvertEulerAngle(transform.rotation.eulerAngles);
            float h = Input.GetAxis("Mouse X");
            float v = Input.GetAxis("Mouse Y");
            float zoom = Input.GetAxis("Mouse ScrollWheel");
            Translate(zoom);
            Rotate(h, v);
        }
    }
    /// <summary>
    /// 控制相机的移动
    /// </summary>
    /// <param name="zoom"></param>
    void Translate(float zoom)
    {
        OriginDistance -= zoom * zoomSpeed;
        OriginDistance = Mathf.Clamp(OriginDistance, minDistance, maxDistance);//限制缩放的范围
        RaycastHit hit;
        if (Physics.Raycast(target.position + headOffset, transform.position - target.position - headOffset, out hit, OriginDistance,
            ~ignoreLayer, QueryTriggerInteraction.Ignore))//检测层依据需求更改
        {
            currentDistance = hit.distance;
        }
        else
        {
            currentDistance = OriginDistance;
        }
        disOffset = direction.normalized * currentDistance;
        if (smooth && rotateType == RotateType.Both)//平滑移动,但累计差会导致相机逐渐回到某个特定视角,且伴随有轻微抖动
            transform.position = Vector3.Lerp(transform.position, target.position + headOffset + disOffset, smoothLevel);
        else
            transform.position = target.position + headOffset + disOffset;//旋转时伴随轻微卡顿
        transform.LookAt(target.position + headOffset);
        disOffset = Vector3.zero;
    }
    /// <summary>
    /// 控制相机绕角色旋转
    /// </summary>
    /// <param name="h">鼠标左右方向移动数值</param>
    /// <param name="v">鼠标前后方向移动数值</param>
    void Rotate(float h, float v)
    {
        if (rotateType != RotateType.UableRotate)
        {
            float finallyRotateV = v * rotateSpeed;
            if (finallyRotateV + visibleEuler.x >= maxLookUpAngle)
            {
                finallyRotateV = visibleEuler.x + finallyRotateV - maxLookUpAngle;
            }
            else if (finallyRotateV + visibleEuler.x <= -maxLookDownAngle)
            {
                finallyRotateV = visibleEuler.x + finallyRotateV + maxLookDownAngle;
            }
            if (rotateType == RotateType.Both)
            {
                //左右旋转
                transform.RotateAround(target.position + headOffset, transform.up, h * rotateSpeed);
                //上下旋转
                transform.RotateAround(target.position + headOffset, transform.right, -finallyRotateV);
            }
            else if (rotateType == RotateType.HorizontalOnly)
            {
                transform.RotateAround(target.position + headOffset, Vector3.up, h * rotateSpeed);
            }
            else if (rotateType == RotateType.VerticalOnly)
            {
                transform.RotateAround(target.position + headOffset, transform.right, -finallyRotateV);
            }
            transform.LookAt(target.position + headOffset);//相机一直朝着主角望去
            direction = (transform.position - target.position - headOffset).normalized;
        }
    }

    public float ConvertAngle(float value)
    {
        float angle = value - 180;

        if (angle > 0)
            return angle - 180;

        return angle + 180;
    }

    public Vector3 ConvertEulerAngle(Vector3 euler)
    {
        return new Vector3(ConvertAngle(euler.x), ConvertAngle(euler.y), ConvertAngle(euler.z));
    }

    /// <summary>
    /// 初始化相机位置
    /// </summary>
    public void ResetView()
    {
        if (minDistance > maxDistance)//如果最小距离大于最大距离  则将最小最大距离互换
        {
            maxDistance += minDistance;
            minDistance = maxDistance - minDistance;
            maxDistance -= minDistance;
        }
        if (rotateType == RotateType.VerticalOnly || rotateType == RotateType.Both) direction = -target.forward;
        else direction = new Vector3(1, 1, -1).normalized;
        disOffset = direction * (minDistance + maxDistance) / 2;
        transform.position = target.position + headOffset + disOffset;
        OriginDistance = disOffset.magnitude;
        disOffset = Vector3.zero;
    }

    public void SetTarget(Transform new_target)
    {
        target = new_target;
    }

    private void OnDrawGizmos()
    {
        Gizmos.color = Color.red;
        Gizmos.DrawLine(target.position + headOffset, transform.position);
        Gizmos.color = Color.yellow;
        Gizmos.DrawWireSphere(target.position + headOffset, minDistance);
        Gizmos.DrawLine(target.position + headOffset + target.forward * minDistance, target.position + headOffset + target.forward * maxDistance);
        Gizmos.DrawLine(target.position + headOffset - target.forward * minDistance, target.position + headOffset - target.forward * maxDistance);
        Gizmos.DrawLine(target.position + headOffset + target.up * minDistance, target.position + headOffset + target.up * maxDistance);
        Gizmos.DrawLine(target.position + headOffset - target.up * minDistance, target.position + headOffset - target.up * maxDistance);
        Gizmos.DrawLine(target.position + headOffset + target.right * minDistance, target.position + headOffset + target.right * maxDistance);
        Gizmos.DrawLine(target.position + headOffset - target.right * minDistance, target.position + headOffset - target.right * maxDistance);
        Gizmos.DrawWireSphere(target.position + headOffset, maxDistance);
    }

}

五: 第三人称相机跟随方法二

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

public class MyFollowCamera : MonoBehaviour
{
    public float distanceAway=10;          // z轴上的距离
    public float distanceUp=5;            // 高度距离
    public float smooth=5;                // 移动平滑值

    private GameObject hovercraft;      // to store the hovercraft
    private Vector3 targetPosition;     // 相机目标位置

    Transform follow;//跟随物体

    void Start()
    {
        //需要跟随的物体
        follow = GameObject.FindWithTag("Player").transform;
    }

    void LateUpdate()
    {
        //目标位置,由跟随物体的坐标+设置的高度+z轴的距离
        //减去follow.forward * distanceAway  为距离物体后背的距离  关于follow.forward的用法可以查看Transform.forward文章
        targetPosition = follow.position + Vector3.up * distanceUp - follow.forward * distanceAway;

        //从当前位置到目标位置 平滑过渡
        transform.position = Vector3.Lerp(transform.position, targetPosition, Time.deltaTime * smooth);

        //相机朝向
        transform.LookAt(follow);
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值