类刺客信条跑酷系统开发日记

Parkour Climbing System开发日记

------类刺客信条跑酷系统

Day1 摄像头脚本

在unity中,xyz轴是右手坐标系,即x水平向右,y垂直向上,z水平向前

csharp 复制代码
public class CameraController : MonoBehaviour
{
    //摄像机跟随的目标
    [SerializeField] Transform followTarget;

    // Update is called once per frame
    void Update()
    {
        //摄像机放在目标后面5个单位的位置
        transform.position = followTarget.position - new Vector3(0, 0, 5);
    }
}

怎么旋转这个相机呢?

摄像机向后移动的参量乘一个水平旋转角度

所以,引入四元数点欧拉Quaternion.Euler

这个水平视角旋转角度需要绕y轴的旋转角度,还需要鼠标控制这个角度

并且,当摄像头旋转的时候,摄像头始终对着player

csharp 复制代码
public class CameraController : MonoBehaviour
{
    //摄像机跟随的目标
    [SerializeField] Transform followTarget;
    //距离
    [SerializeField] float distance;

    //绕y轴的旋转角度
    float rotationY;

    private void Update()
    {
        //鼠标x轴控制rotationY
        rotationY += Input.GetAxis("Mouse X");
        //水平视角旋转参量
        //想要水平旋转视角,所以需要的参量为绕y轴旋转角度
        var horizontalRotation = Quaternion.Euler(0, rotationY, 0);

        //摄像机放在目标后面5个单位的位置
        transform.position = followTarget.position - horizontalRotation * new Vector3(0, 0, distance);
        //摄像机始终朝向目标
        transform.rotation = horizontalRotation;
    }
}

完成了水平视角的旋转

让相机垂直旋转

还需要在垂直视角旋转的时候合理的限幅:让视角最高不超过45°,最低到人物的胸部位置

csharp 复制代码
public class CameraController : MonoBehaviour
{
    //摄像机跟随的目标
    [SerializeField] Transform followTarget;
    [SerializeField] float rotationSpeed = 1.5f;
    //距离
    [SerializeField] float distance;

    //绕y轴的旋转角度------水平视角旋转
    float rotationY;
    //绕x轴的旋转角度------垂直视角旋转
    float rotationX;
    //限制rotationX幅度
    [SerializeField] float minVerticalAngle = -20;
    [SerializeField] float maxVerticalAngle = 45;
    //框架偏移向量------摄像机位置视差偏移
    [SerializeField] Vector2 frameOffset;

    private void Update()
    {
        //鼠标x轴控制rotationY
        rotationY += Input.GetAxis("Mouse X") * rotationSpeed;
        //鼠标y轴控制rotationX
        rotationX += Input.GetAxis("Mouse Y") * rotationSpeed;
        //限制rotationX幅度
        rotationX = Mathf.Clamp(rotationX, minVerticalAngle, maxVerticalAngle);

        //视角旋转参量
        //想要水平旋转视角,所以需要的参量为绕y轴旋转角度
        var targetRotation = Quaternion.Euler(rotationX, rotationY, 0);

        //摄像机的焦点位置
        var focusPosition = followTarget.position + new Vector3(frameOffset.x, frameOffset.y, 0);
        //摄像机放在目标后面5个单位的位置
        transform.position = focusPosition - targetRotation * new Vector3(0, 0, distance);
        //摄像机始终朝向目标
        transform.rotation = targetRotation;
    }
}

大致实现了摄像机跟随人物进行旋转

还需要一些细节调整:

csharp 复制代码
    private void Start()
    {
        //隐藏光标
        Cursor.lockState = CursorLockMode.Locked;
        Cursor.visible = false;

    }

考虑到存在大多数角色控制器都有控制反转的选项

csharp 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CameraController : MonoBehaviour
{
    //摄像机跟随的目标
    [SerializeField] Transform followTarget;
    [SerializeField] float rotationSpeed = 1.5f;
    //距离
    [SerializeField] float distance;

    //绕y轴的旋转角度------水平视角旋转
    float rotationY;
    //绕x轴的旋转角度------垂直视角旋转
    float rotationX;
    //限制rotationX幅度
    [SerializeField] float minVerticalAngle = -20;
    [SerializeField] float maxVerticalAngle = 45;
    //框架偏移向量------摄像机位置视差偏移
    [SerializeField] Vector2 frameOffset;

    //视角控制反转
    [Header("视角控制反转:invertX是否反转垂直视角,invertY是否反转水平视角")]
    [SerializeField] bool invertX;
    [SerializeField] bool invertY;

    float invertXValue;
    float invertYValue;

    private void Start()
    {
        //隐藏光标
        Cursor.lockState = CursorLockMode.Locked;
        Cursor.visible = false;

    }

    private void Update()
    {
        //视角控制反转参数
        invertXValue = (invertX)? -1 : 1;
        invertYValue = (invertY)? -1 : 1;

        //水平视角控制------鼠标x轴控制rotationY
        rotationY += Input.GetAxis("Mouse X") * rotationSpeed * invertYValue;
        //垂直视角控制------鼠标y轴控制rotationX
        rotationX += Input.GetAxis("Mouse Y") * rotationSpeed * invertXValue;
        //限制rotationX幅度
        rotationX = Mathf.Clamp(rotationX, minVerticalAngle, maxVerticalAngle);

        //视角旋转参量
        //想要水平旋转视角,所以需要的参量为绕y轴旋转角度
        var targetRotation = Quaternion.Euler(rotationX, rotationY, 0);

        //摄像机的焦点位置
        var focusPosition = followTarget.position + new Vector3(frameOffset.x, frameOffset.y, 0);
        //摄像机放在目标后面5个单位的位置
        transform.position = focusPosition - targetRotation * new Vector3(0, 0, distance);
        //摄像机始终朝向目标
        transform.rotation = targetRotation;
    }
}

我通常喜欢这样选择,垂直反转(鼠标向上就看上面),水平不反转(鼠标向左就看左边)

就是让摄像机视角和鼠标移动方向对我来说是同步的,相当于第一人称视角控制的习惯

Day2 第三人称人物控制脚本

前序准备

先创建个人物模型( 从Mixamo下载的)

导入unity中,选择模型后点开inspector-Materials-Textures,选一个文件夹存放纹理

OK,下面就开始为这个角色写控制脚本吧!

最简化的第三人称角色控制

csharp 复制代码
public class PlayerController : MonoBehaviour
{
    [SerializeField]float moveSpeed = 5f;
    private void Update()
    {
        float h = Input.GetAxis("Horizontal");
        float v = Input.GetAxis("Vertical");

        //标准化 moveInput 向量
        var moveInput = new Vector3(h, 0, v).normalized;

        transform.position += moveInput * moveSpeed * Time.deltaTime;

    }

}

需要注意的:

  1. .normalized

    如果不进行标准化,moveInput 向量的长度会变得大于1(具体来说,比如h,v长度都为1,\(\sqrt{h^2 + 0^2 + v^2} = \sqrt{1^2 + 0^2 + 1^2} = \sqrt{2} \approx 1.414\))。这意味着在对角线方向上移动时,玩家的移动速度会比只在一个方向上移动时快。为了确保玩家在所有方向上移动时速度一致,需要对向量进行标准化。

  2. Time.deltaTime

    Time.deltaTime 是Unity引擎提供的一个浮点数,表示从上一帧到当前帧所用的时间(以秒为单位)。使用 Time.deltaTime 可以确保玩家的移动速度在不同帧率下保持一致。如果不使用 Time.deltaTime,在高帧率下玩家会移动得更快,在低帧率下玩家会移动得更慢。

改进

上面这样显然不能满足角色控制,因为当我们按下前进方向键的时候,人物并没有根据当前摄像机显示的方向移动

还需要进行如下改进:

在CameraController.cs里面加入

csharp 复制代码
    //水平方向的旋转,返回摄像机的水平旋转四元数。
    public Quaternion PlanarRotation => Quaternion.Euler(0, rotationY, 0);

这里提一句C#中的特性:

大多数语言中想要获取一个返回值,需要定义一个函数,然后返回

csharp 复制代码
    public Quaternion GetPlanarRotation()
    {
        return Quaternion.Euler(0, rotationY, 0);
    }

但是C#可以优雅的利用表达式主体定义的属性,直接获取这个属性

然后在PlayerController.cs里调用这个返回值

csharp 复制代码
public class PlayerController : MonoBehaviour
{
    [SerializeField]float moveSpeed = 5f;

    CameraController cameraController;

    private void Awake()
    {
        //相机控制器设置为main camera
        cameraController = Camera.main.GetComponent<CameraController>();
    }
    private void Update()
    {
        float h = Input.GetAxis("Horizontal");
        float v = Input.GetAxis("Vertical");

        float moveAmount = Mathf.Abs(h) + Mathf.Abs(v);

        //标准化 moveInput 向量
        var moveInput = new Vector3(h, 0, v).normalized;

        //让人物移动方向关联相机的朝向
        var moveDir = cameraController.PlanarRotation * moveInput;

        //每次判断moveAmount的时候,确保只有在玩家实际移动时才会更新移动+转向
	//没有输入就不更新转向,也就不会回到初始朝向
        if (moveAmount > 0)
        {
            //帧同步移动
            transform.position += moveDir * moveSpeed * Time.deltaTime;
            //人物模型转起来:让人物朝向与移动方向一致
            transform.rotation = Quaternion.LookRotation(moveDir);
        }

    }

}

这里解决了一个问题:

当方向键输入结束,人物模型朝向又回到了初始状态朝向

所以需要实时响应输入

  • if (moveAmount > 0)只有输入的时候才会更新人物朝向
  • 确保模型始终朝向移动方向。

但是还有一个问题:

人物朝向切换太快了,需要设置一个转向速度,让人物从当前朝向到目标朝向慢慢转向

csharp 复制代码
    [SerializeField]float rotationSpeed = 10f;

    Quaternion targetRotation;
csharp 复制代码
        //每次判断moveAmount的时候,确保只有在玩家实际移动时才会更新移动+转向
        //没有输入就不更新转向,也就不会回到初始朝向
        if (moveAmount > 0)
        {
            //帧同步移动
            transform.position += moveDir * moveSpeed * Time.deltaTime;
            //人物模型转起来:让人物朝向与移动方向一致
            targetRotation = Quaternion.LookRotation(moveDir);
        }
        //更新transform.rotation:让人物从当前朝向到目标朝向慢慢转向
        transform.rotation = Quaternion.RotateTowards(transform.rotation, targetRotation,
                         rotationSpeed * Time.deltaTime);

实现效果如下:

该部分完整代码:

csharp 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerController : MonoBehaviour
{
    [Header("玩家属性")]
    [SerializeField]float moveSpeed = 5f;
    [SerializeField]float rotationSpeed = 10f;

    Quaternion targetRotation;

    CameraController cameraController;

    private void Awake()
    {
        //相机控制器设置为main camera
        cameraController = Camera.main.GetComponent<CameraController>();
    }
    private void Update()
    {
        float h = Input.GetAxis("Horizontal");
        float v = Input.GetAxis("Vertical");

        float moveAmount = Mathf.Abs(h) + Mathf.Abs(v);

        //标准化 moveInput 向量
        var moveInput = new Vector3(h, 0, v).normalized;

        //让人物移动方向关联相机的朝向
        var moveDir = cameraController.PlanarRotation * moveInput;

        //每次判断moveAmount的时候,确保只有在玩家实际移动时才会更新移动+转向
        //没有输入就不更新转向,也就不会回到初始朝向
        if (moveAmount > 0)
        {
            //帧同步移动
            transform.position += moveDir * moveSpeed * Time.deltaTime;
            //人物模型转起来:让人物朝向与移动方向一致
            targetRotation = Quaternion.LookRotation(moveDir);
        }
        //更新transform.rotation:让人物从当前朝向到目标朝向慢慢转向
        transform.rotation = Quaternion.RotateTowards(transform.rotation, targetRotation,
                         rotationSpeed * Time.deltaTime);

    }

}

Day3 Animation动画

相关推荐
IP管家1 小时前
多级路由器如何避免IP冲突
网络·网络协议·tcp/ip·游戏·智能路由器·ip
Sui_Network1 小时前
Sui Basecamp 2025 全栈出击
大数据·游戏·web3·去中心化·区块链
染指11102 小时前
21.第二阶段x64游戏实战-分析采集物偏移
汇编·游戏·反游戏外挂·游戏逆向·x64dbg·x64游戏
wgc2k3 小时前
Java游戏服务器开发流水账(3)游戏数据的缓存简介
服务器·游戏
惊鸿醉15 小时前
Unity_JK框架【4】MonoSystem 和 协程工具类 的剖析与实践
游戏·unity·单例模式
wgc2k1 天前
Java游戏服务器开发流水账(2)开发中Maven的管理
java·服务器·游戏
shc521a21 天前
《盟重冰雪传奇》:战士玩法攻略!
游戏·kafka·hibernate·盟重冰雪传奇·盟重冰雪传奇手游·盟重冰雪城官网
豆约翰1 天前
pygame联网飞机大战游戏实现
python·游戏·pygame
梦想平凡2 天前
开元类双端互动组件部署实战全流程教程(第1部分:环境与搭建)
运维·服务器·前端·游戏·node.js