最终效果

在这里插入图片描述

一、前言

前面其实已经做过使用CharacterController实现过第一人称的角色控制器:
【unity小技巧】unity最完美的CharacterController 3d角色控制器,实现移动、跳跃、下蹲、奔跑、上下坡、物理碰撞效果,复制粘贴即用

但是我发现很多人还是不理解,都跑来私信问我,所以决定重新做一个更加详细且简单控制器,主要是实现俯视角和第三人称角色控制。

二、Character Controller参数介绍

添加一个胶囊体,添加Character Controller组件,记得删除Capsule Collider组件,因为Character Controller自带了碰撞,所以不需要
在这里插入图片描述
Character Controller参数介绍
在这里插入图片描述

Slope Limit 可以爬坡的角度

Step Offset 能上的楼梯阶梯高度

skin width 相当于在CharacterController自己的碰撞体外再添加了一个与碰撞角度相关的碰撞体,skinwith可以消除抖动并且防止角色卡住。这里的建议是最好让你的skinwith的值至少大于0.01并且大于控制器半径的10%,但是调整了这个值可能会导致角色的脚无法触及地面,记得同时把碰撞体重心的位置向上调整一点点就可以了

Min Move Distance 最小的移动距离是指的是低于这个数值的移动会被系统忽略掉,我们一般设置为0

Center、Radius、Height其实就是定义碰撞体范围,中心位置、半径、高

三、添加虚拟相机

参考:【推荐100个unity插件之10】Unity最全的最详细的Cinemachine(虚拟相机系统)介绍,详细案例讲解,快速上手
在这里插入图片描述

四、2.5D俯视角人物操作

代码

using UnityEngine;

public class PlayerMovement : MonoBehaviour
{
    private CharacterController controller; // 角色控制器组件的引用
	public float Speed = 0.1f; // 玩家移动的速度
	float horizontal;
    float vertical;
    
	void Start()
    {
        controller = GetComponent<CharacterController>(); // 初始化角色控制器
    }
    
	void Update()
    {
        // 获取水平和垂直轴的输入
        horizontal = Input.GetAxis("Horizontal");
        vertical = Input.GetAxis("Vertical");
		
		MoveLikeTopDown();
    }

    private void MoveLikeTopDown()
    {
        // 获取水平和垂直轴的输入
        float horizontal = Input.GetAxis("Horizontal");
        float vertical = Input.GetAxis("Vertical");

        // 根据输入创建移动方向向量,并标准化
        Vector3 direction = new Vector3(horizontal, 0, vertical).normalized;

        // 计算移动向量并根据速度和时间更新位置
        Vector3 move = direction * Speed * Time.deltaTime;
        controller.Move(move);

        // 将玩家的位置从世界坐标转换为屏幕坐标
        Vector3 playerScreenPoint = Camera.main.WorldToScreenPoint(transform.position);

        // 计算鼠标相对于玩家的偏移量
        Vector3 point = Input.mousePosition - playerScreenPoint;

        // 计算与鼠标位置的角度
        float angle = Mathf.Atan2(point.x, point.y) * Mathf.Rad2Deg;

        // 更新玩家的朝向,使其面向鼠标
        transform.eulerAngles = new Vector3(transform.eulerAngles.x, angle, transform.eulerAngles.z);
    }
}

配置
在这里插入图片描述

相机参数改一下,改成俯视角,拉高相机
在这里插入图片描述
效果
在这里插入图片描述

五、自带重力的SimpleMove 移动

SimpleMove 方法是 CharacterController 组件提供的一个简化移动方式,它会自动处理重力,并且更适合不需要手动处理重力的场景。使用 SimpleMove 方法可以让代码更简洁,因为它自动处理了重力的应用。相比之下,Move 方法则要求你手动计算和应用重力。因此,如果你的角色只需要基本的移动而不需要额外的物理处理,SimpleMove 是一个更方便的选择。

但是SimpleMove 方法本身不支持跳跃。SimpleMove 自动处理重力,但不提供直接的方式来控制跳跃或其他复杂的运动行为。如果你需要实现跳跃功能,你应该使用 Move 方法,并手动管理重力和跳跃逻辑。

void MoveLikeWow()
{
    float horizontal = Input.GetAxis("Horizontal");
    float vertical = Input.GetAxis("Vertical");
    Vector3 move = transform.forward * Speed * vertical;

    // 使用 SimpleMove 方法,它会自动处理重力
    controller.SimpleMove(move);

    // 旋转角色
    transform.Rotate(Vector3.up, horizontal * RotateSpeed);
}

效果,可以看到角色已经有了重力
在这里插入图片描述

六、第三人称角色控制

如前面所说,如果你的游戏不需要角色进行跳跃且对重力定制性不高功能,则使用前面的SimpleMove确实是一种很好的方法。
如果我们角色需要跳跃等复杂的功能时,

1、移动

ws控制人物前后移动,用A和D来操作人物的旋转

using UnityEngine;

public class PlayerMovement : MonoBehaviour
{
    private CharacterController controller; // 角色控制器组件的引用
    public float Speed = 0.1f; // 玩家移动的速度
    public float RotateSpeed = 1f; // 玩家旋转的速度
    float horizontal;
    float vertical;

    void Start()
    {
        controller = GetComponent<CharacterController>(); // 初始化角色控制器
    }

    void Update()
    {
        // 获取水平和垂直轴的输入
        horizontal = Input.GetAxis("Horizontal");
        vertical = Input.GetAxis("Vertical");

		MoveLikeWow();
    }

    private void MoveLikeWow()
    {
        // 根据输入和速度计算移动方向
        Vector3 move = transform.forward * Speed * vertical;
        
        // 使用角色控制器移动角色
        controller.Move(move);

        // 根据水平输入旋转角色
        transform.Rotate(Vector3.up, horizontal * RotateSpeed);
    }
}

绑定相机跟随看向主角,并修改相机视角看向角色的头部
在这里插入图片描述

效果
在这里插入图片描述

相机拐弯有点抖动问题,把这个水平方向的这个震荡值调成零
在这里插入图片描述

效果
在这里插入图片描述

2、添加重力

注意controller.move调用多次的话,这里面的位移是可以互相叠加的,所以就是最终就是水平位移加上重力,不用担心下面move的会覆盖前面的move

public float Gravity = 19.8f;//重力
private Vector3 Velocity = Vector3.zero; // 角色的当前速度

// 更新重力影响的速度
Velocity.y += Gravity * Time.deltaTime;
// 使用角色控制器应用重力的移动
controller.Move(Velocity * Time.deltaTime);

效果
在这里插入图片描述

3、 加地面检测,限制在地面重力不要累加

可以看到,目前角色下落很快,那是因为目前游戏,角色重力就是一直累加,我们先加地面检测,地面检测其实有两种办法

3.1、自定义球形区域检测

你可以选择自己在角色脚下放置一个球形检测区域做地面检测,就像下面这样

[Header("地面检测")]
public bool IsGround;
public Transform GroundCheck;
public float CheckRadius = 0.2f;
public LayerMask layerMask;

//Physics.CheckSphere 用于检测一个球形区域是否与指定的碰撞层发生接触
IsGround = Physics.CheckSphere(GroundCheck.position, CheckRadius, layerMask);

但是这样通常会有一个问题,就是当角色处于悬崖边缘时,检测很容易不准确,导致跳不起来。
当然你可以选择不使用球形检测,改用其他的检测进行优化,但是这无疑会增加很多的工作量。

3.2、使用isGrounded判断是否接触地面

CharactorController自带有isGrounded函数可以判断是否接触地面
在这里插入图片描述
但是它可不像官方文档说的这么简单,如果不懂如何正常使用的话,它存在很多坑,比如明明角色在地面上但是"CharacterController.isGrounded的值总是为false",或者"CharacterController.isGrounded的值在true和false反复横跳"。

如果你查看CharacterController.isGrounded会发现有一个段描述如下
在这里插入图片描述
其实际意思是上一次调用CharacterController.Move时CharacterController是否接触到了地面?

所以我们可以总结出isGrounded正确进行地面检测的两个条件:

  • CharacterController.isGrounded的地面检测判断之前一定要先执行CharacterController.Move方法
  • 你必须一直为它施加一个向下的速度,且速度不能为0

代码实现如下,注意当isGrounded为true时,将Y轴方向的速度设为了一个较小的负值(这里我设置的-2),这是为了确保能稳定触碰地面,避免isGrounded误判为false

// 应用重力
controller.Move(velocity * Time.fixedDeltaTime);
//地面检测
isGround = controller.isGrounded;
if (isGround)
{
    velocity.y = -2f;
}else{
    // 重力累加
    velocity.y += Gravity * Time.fixedDeltaTime;
}

4、跳跃

其实也就是在垂直方向施加一个力,这里有一个通用的近似物理效果的一个运动公式
在这里插入图片描述

// 跳跃处理
if (isGround && Input.GetButtonDown("Jump"))
{
    velocity.y = Mathf.Sqrt(JumpHeight * -2 * Gravity);
}

效果
在这里插入图片描述

5、物理碰撞效果

Character Controller本身不会对力作出反应,也不会自动推开刚体。

如果要通过角色控制器来推动刚体或对象,可以编写脚本通过 OnControllerColliderHit() 函数对与控制器碰撞的任何对象施力。

另一方面,如果希望玩家角色受到物理组件的影响,那么可能更适合使用rigidbody,而不是Character Controller

 //物理碰撞
private void OnControllerColliderHit(ControllerColliderHit hit)
{
    //获取碰撞体上的 Rigidbody 组件。
    Rigidbody body = hit.rigidbody;
    
    //检查获取到的 Rigidbody 是否为空或者是否为静态物体(isKinematic)
    if (body == null || body.isKinematic) return;

    // 我们不想把物体推到我们下面 
    if (hit.moveDirection.y < -0.3) return;

    //根据移动方向计算推动方向,
    //我们只将物体推向两侧,从不上下 
    Vector3 pushDir = new Vector3(hit.moveDirection.x, 0, hit.moveDirection.z);

    // 在碰撞点施加冲量 ForceMode.Impulse:添加一个瞬间的冲击力到刚体,且自动应用它的质量
    body.AddForceAtPosition(pushDir * 0.1f, hit.point, ForceMode.Impulse);
}

效果
在这里插入图片描述

七、下蹲

下蹲的逻辑就是让CharacterController 的高度减半,还有中心点的位置也跟着减半,当然还有摄像机的高度,还需要注意的是人物如果头顶有东西的时候我们是不允许他起立的,不然会穿模,所以还需要一个头顶检测,头顶我们使用盒子检测最好,可以覆盖整个头部

参考:https://blog.csdn.net/qq_36303853/article/details/134984516

源码

很遗憾源码我并不想免费分享,我也建议大家能自己手动去敲代码,逐步实现和理解每一块功能。项目实现所涉及的主要功能思路和代码我也已经毫无保留的分享在文章中了,当然,如果你真的需要的话,源码我也放出来了,收个辛苦费,就当作你对我不断创作的支持。力量随微,心暖人。您的每一次支持都是我创作的最大动力!!!

https://gf.bilibili.com/item/detail/1106434120

完结

赠人玫瑰,手有余香!如果文章内容对你有所帮助,请不要吝啬你的点赞评论和关注,你的每一次支持都是我不断创作的最大动力。当然如果你发现了文章中存在错误或者有更好的解决方法,也欢迎评论私信告诉我哦!

好了,我是向宇https://xiangyu.blog.csdn.net

一位在小公司默默奋斗的开发者,闲暇之余,边学习边记录分享,站在巨人的肩膀上,通过学习前辈们的经验总是会给我很多帮助和启发!如果你遇到任何问题,也欢迎你评论私信或者加群找我, 虽然有些问题我也不一定会,但是我会查阅各方资料,争取给出最好的建议,希望可以帮助更多想学编程的人,共勉~
在这里插入图片描述

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐