所需资源
课程来源(请支持正版课程)
安装Unity Hub
安装Visual Studio
角色模型
环境准备
①Unity设置
不设置的话编写有些代码没有自动补全
点开 Preferences
选择 visual studio
②角色导入
点击 windows—>Package Manager
左上角 My Assets
找到该角色模型导入
角色预制件配置
1、动画控制器配置
①创建一个 Animation 文件夹,以便存放动画相关文件,在 Animation 中创建一个 Animatior Controller
②双击打开Animator Controller
将 SciFiWarriorPBRHPPolyart(下载的人物模型)中的 Animation 中的 Idle_guard_ar(角色的站立动作)拖入状态图,将该状态更名为idle。
将 Run_guard_AR(角色的跑步动作)拖入状态图,连接两状态,即:
③在 Parameters 中添加 Bool 参数 isRun ,用来控制从站立到跑步的状态转换
④选中 Idle—>Run_guard_AR
取消勾选 has exit time (有退出时间),取消勾选即达到 Transition 过度条件,那么会立马过度,不会等待动画结束,Conditions 选择刚刚创建的参数,当 isRun 为 true 时,从站立变为跑步
⑤同理选中 Run_guard_AR—>idle ,取消 has exit time , Conditions 中 isRun 为 false
2、角色预制件配置
将 SciFiWarriorPBRHPPolyart—>Mesh->Polyart_Mesh 拖入场景
①添加 Animatior 组件,将刚才配置好的 Animator Controller(动画控制器)拖入相应位置
②添加 Capsule Conllider(胶囊碰撞体)组件,用于碰撞检测
③添加 Character Controller(角色控制器)组件,用于控制角色,并将 center 中的 y 改为1
角色控制
关于单件模式:请看这篇博客
注: 原视频实现的是第一人称,由于本例加入了动画动作,摄像机放入头部中,会穿模,暂改为第三人称
①角色移动:
getAxisRaw 用于接收接受键盘输入,例如 float xMov = Input.GetAxisRaw(“Horizontal”);
其中 Horizontal 为虚拟轴中的水平轴,其已经规定好了按键输入
如何查看虚拟轴?
Edit —> project settings —> Input Manager
下拉 Axis
input.GetAxis()与input.GetAxisRaw()的区别:
参考博客
input.GetAxis() 的返回值是[-1, 1],拥有平滑过渡功能
input.GetAxisRaw() 的返回值是{-1,0,1}
对于Horizontal:
按键 A 返回-1,不按键返回0,按键 D 返回1
对于Vertical:
按键 S 返回-1,不按键返回0,按键 W 返回1
对于transform.right: 世界变换的红色轴
对于transform.forward: 世界变换的蓝色轴
对于transform.right * xMov: 通过返回的{-1,0,1}决定是朝红色轴的反向,不变,或者正向移动,对于 transform.forward * yMov 同理
两者相加即向量的合成,通过normalized将向量归一,将该方向向量×一个速度值,变为带有方向的速度
Time.deltaTime: 按秒为单位,完成上一帧所用的时间。
位移 = 速度 × 时间
PlayerController:
②角色旋转
鼠标横向移动即角色旋转,鼠标纵向移动即摄像机旋转
关于角度限制:
Mathf.Clamp(value,min, max);
限制 value 的值在 min 和 max 之间, 如果 value 小于 min,返回 min。 如果 value 大于 max ,返回 max ,否则返回 value。
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
public float Speed = 5f;
public float LookSensitivity = 20f;
public Camera cam;
private float cameraRotationTotal = 0f; // 累计转了多少度
[SerializeField]
private float cameraRotationLimit = 85f;
static PlayerController instance;
public static PlayerController Instance()
{
return instance;
}
private void Awake()
{
instance = this;
}
private Animator animator;
private CharacterController cc;
// Start is called before the first frame update
void Start()
{
Cursor.lockState = CursorLockMode.Locked;//将鼠标隐藏
animator = GetComponent<Animator>(); //获取动画组件
cc = GetComponent<CharacterController>();//获取角色控制器组件
}
//实现移动功能
void move()
{
float xMov = Input.GetAxisRaw("Horizontal");//获取水平输入
float yMov = Input.GetAxisRaw("Vertical");//获取垂直输入
Vector3 velocity = (transform.right* xMov +transform.forward * yMov).normalized* Speed;//速度向量
if(velocity != Vector3.zero )//如果速度不为0,则移动
{
cc.Move(velocity * Time.deltaTime);
animator.SetBool("isRun", true);//动画组件参数isRun设为true,控制角色移动动作
}
else
{
animator.SetBool("isRun", false);
}
}
//实现旋转功能
void rotate()
{
float xMse = Input.GetAxisRaw("Mouse X");
float yMse = Input.GetAxisRaw("Mouse Y");
Vector3 yRotation = new Vector3(0f, xMse, 0f) * LookSensitivity;//鼠标横向移动即绕y轴旋转
Vector3 xRotation = new Vector3(yMse,0f,0f) * LookSensitivity;//鼠标纵向移动即绕x轴旋转
if (yRotation != Vector3.zero)//对y轴旋转即角色旋转
{
transform.Rotate(yRotation);
}
if (xRotation != Vector3.zero)//对x轴旋转即相机旋转(第一人称)
{
cameraRotationTotal += xRotation.x;
cameraRotationTotal = Mathf.Clamp(cameraRotationTotal, -cameraRotationLimit, cameraRotationLimit);
cam.transform.localEulerAngles = new Vector3(cameraRotationTotal, 0f, 0f);
}
}
// Update is called once per frame
void Update()
{
move();
rotate();
}
}
联机功能
①下载包
Netcode for GameObjects
Multiplayer Tools
②创建NetworkManager
创建空对象(更名为 NetworkManager),选中,添加一个 transport
给同步物体加唯一编号,选中 Player,添加组件 Network Object
利用 NetworkManager 管理 Player:
③给Player添加同步信息
添加组件 Network Transform
同步信息勾选:
- 位置:全部更新
- 角度:只围绕y轴旋转
- 大小:不更新
④给Player的跟随相机添加同步信息
只同步 x 轴
⑤实现开局创建角色
在 NetworkManager 中
⑥创建简单UI界面以便调试
创建 UI—>Canvas ,更名为 MenuUI ,添加 Button
给 NetworkManager 添加UI脚本 NetworkManagerUI ,将 MenuUI 中的 HostBtn、ServerBtn、ClientBtn 分别拖入
NetworkManagerUI:
using System.Collections;
using System.Collections.Generic;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.UI;
public class NetworkManagerUI : MonoBehaviour
{
[SerializeField]
private Button HostBtn;
[SerializeField]
private Button ServerBtn;
[SerializeField]
private Button ClientBtn;
// Start is called before the first frame update
void Start()
{
//对按键进行监听
HostBtn.onClick.AddListener(()=>
{
NetworkManager.Singleton.StartHost();
});
ServerBtn.onClick.AddListener(() =>
{
NetworkManager.Singleton.StartServer();
});
ClientBtn.onClick.AddListener(() =>
{
NetworkManager.Singleton.StartClient();
});
}
}
⑦ 处理冲突
如果这时运行,我们打开两个窗口,运行一个窗口,则两个窗口中的角色都会动,由于共同接收输入,所以这时我们需要禁用不是本地玩家的一些功能。
给 Player 添加脚本 PlayerSetup
添加不是本地玩家需要被禁用的行为
注:该类继承自NetworkBehaviour
PlayerSetup:
using System.Collections;
using System.Collections.Generic;
using Unity.Netcode;
using UnityEngine;
//该类继承自NetworkBehaviour
public class PlayerSetup : NetworkBehaviour
{
[SerializeField]
private Behaviour[] componentsToDisable;
private Camera senceCam;
// Start is called before the first frame update
void Start()
{
if (!IsLocalPlayer)//若不是本地玩家
{
for(int i = 0; i < componentsToDisable.Length; i++)
{
componentsToDisable[i].enabled = false;
}
}
else//若是本地玩家
{
senceCam = Camera.main;//调用主摄像机
if(senceCam != null)
{
senceCam.gameObject.SetActive(false);//将主摄像机关闭
}
}
}
//当该脚本挂载的物体不被激活执行代码
private void OnDisable()
{
senceCam.gameObject.SetActive(true);//主摄像机打开
}
}
⑧解决客户端Client权限问题
原视频是不带动画效果,对于不带动画效果出现的问题是:
客户端 Client 无法操作
1)无动画效果解决方法:
创建 ClientNetworkTransform 脚本,添加如下代码,该 ClientNetworkTransform 类继承自 NetworkTransform 类,重写函数 bool OnIsServerAuthoritative(),将该函数返回 false,然后将 Player 中和 Player 中的摄像机的 NetworkTransform 替换为该脚本,就解决了不带动画的,Client 端无法操作的问题
2)带动画效果解决方法:
当我用带动画的人物做出联机功能是,出现的问题是:
首先解决了上述问题后出现的情况:角色在各自的世界都可以移动,但是在别的世界却无法移动,且都没有动画动作
①解决动画动作:
在 Player 中添加 Network Animator 脚本,将 Player 中的Animator 组件拖入相应位置,即可解决
- 这时会发现 client 端可以看到 host 端的角色动作,但 host 端无法看到 client 端的角色动作,且两个窗口皆无法看到别窗口的角色移动
②解决角色移动
将 Player 中的 Animator 组件中的 Apply Root Motion 取消勾选
- 这时 client 端可以看到 host 端的角色动作和移动,而 host 端只能看到 client 端的移动,却看不到角色的动作。
对于该问题我上网搜索好久没找到答案,于是便自己思考,考虑到是 host 和 client 端的关系,且对于无动画效果时,由于 client 没有权限, client 端物体不能移动,创建一个新类去继承NetworkTransform 类,而现在是添加了动画之后, Host 端看不到 Client 端的动画,于是能不能创建一个新类去继承 NetworkAnimator ,将该类的函数 OnIsServerAuthoritative() 重写,返回 false ,答案是可行的!
③解决动画效果client权限问题:
创建 ClientNetworkAnimator 脚本,将 Player 中的 Animator 拖到相应位置
ClientNetworkTransform:
using Unity.Netcode.Components;
using UnityEngine;
namespace Unity.Multiplayer.Samples.Utilities.ClientAuthority
{
[DisallowMultipleComponent]
public class ClientNetworkTransform : NetworkTransform
{
protected override bool OnIsServerAuthoritative()
{
return false;
}
}
}
ClientNetworkAnimator:
using Unity.Netcode.Components;
using UnityEngine;
namespace Unity.Multiplayer.Samples.Utilities.ClientAuthority
{
[DisallowMultipleComponent]
public class ClientNetworkAnimator : NetworkAnimator
{
protected override bool OnIsServerAuthoritative()
{
return false;
}
}
}