unity生命周期
飞行
前置条件:需要满足player与周围的环境都具有碰撞检测(含有Collider即可),不然的话会出现一些奇奇怪怪的错误如果小球在碰撞过程中,发生翻转等问题,说明并未将小球的角度锁死,这样做的话,会导致侧翻等一系列问题。
注意:单独更改Configurable Joint的一些属性时,可能无法直接更改,需要我们重新给那个结构体赋值
Configurable Joint
概念
可配置关节 (Configurable Joint) 包含其他关节类型的所有功能,并提供更强大的角色移动控制。当您想要自定义布娃娃的运动并对角色强制实施某些姿势时,这种关节特别有用。使用可配置关节还可以将关节修改为您自行设计的高度专业化关节。
在这里我们实现的是,长按空格,人物会飞行,所以在这里我们更改的是YDrive(从地图上看,y轴是我们习惯的z轴)
Position Spring
:由 Unity 用于将关节从当前位置向目标位置旋转的弹簧扭矩(大概是弹力)。
Position Damper
:根据关节当前速度与目标速度之间的差值按比例减小弹簧扭矩量。此做法可减小关节移动速度。设置为大于零的值可让关节“抑制”振荡(否则将无限期进行振荡)。
Maximum Force
:限制可以施加的驱动力大小。要施加计算出的驱动力,请将此属性设置为不太可能计算的驱动高值。
在这里我们更改 Position Spring = 20,Maximum Force = 40
刚开始的时候Maximum Force的值过大,导致人物如果从过高的地方跳下来的话,会导致人物与地面穿模,哪怕都具有碰撞检测,速度过快的时候会发生这种情况。
于此同时我们还需要给刚体组件加上一个摩擦力,不然的话会导致弹簧组件一直往返弹跳(哪怕幅度越来越小,这样做给玩家的体验感不好,因此在这里我们加上一个摩擦力即可,就可以解决这个问题)
调整摄像机的旋转角度使其更加合理(固定在一个范围里面)
核心代码
PlayerInput.cs文件中
Vector3 force = Vector3.zero;
if (Input.GetButton("Jump"))
{
force = Vector3.up * trusterForce;
joint.yDrive = new JointDrive { positionSpring = 0f, maximumForce = 0f, positionDamper = 0f };
}
else
{
joint.yDrive = new JointDrive { positionSpring = 20f, maximumForce = 40f, positionDamper = 0f };
}
playerController.Truster(force);
PlayerController.cs文件中
private Vector3 thrusterForce = Vector3.zero;
public void Truster(Vector3 _thrusterForce)
{
thrusterForce = _thrusterForce;
}
private void PerformMovement()
{
if (velocity != Vector3.zero)
{
rb.MovePosition(rb.position + velocity * Time.fixedDeltaTime);
}
if (thrusterForce != Vector3.zero)
{
rb.AddForce(thrusterForce);
}
}
射击
理清步骤
确定射击的方式,是从眼睛瞄准的方向开始发射子弹(也就是Camera),并不是从枪口里发射子弹。
将武器的属性封装成一个单独的类。
具体实现有Unity自带的API,Physics.Raycast()函数,第一个参数为射线的起点位置,第二个参数为射线的方向,第三个参数为第一个被击中的目标,第四个为射线的距离。
规范人物的命名,每个玩家身上都有一个NetworkObject组件,其中有一个属性GlobalObjectHash,其在每个客户端的同一个玩家身上都是相同的,我们可以将玩家命名为
"Player " + hash
。在调试阶段,我们可以在屏幕上将命中的物体名字打印出来。具体逻辑在GameManager.cs中。
将击中后的结果发送给服务器
客户端和服务端的逻辑关系
由于在这里开枪的动作,是各自执行各自的代码,因此也需要将
player shooting
代码加入到禁用的组件里面。
如果射线碰撞后,将shootServerRpc
函数在server端执行
需要注意的是在server端(也有所有的代码)没有本地用户这一概念,也就是说shootServerRpc等都被禁用掉了,也就无法执行了
这就产生了矛盾,此时我们可以换一种理解方式,就是相当于用户将这个代码发送给服务端,让服务端来帮忙,运行那些代码。
具体代码
using System.Collections;
using System.Collections.Generic;
using Unity.Netcode;
using UnityEngine;
public class PlayerShoot : NetworkBehaviour
{
[SerializeField]
private PlayerWeapon weapon;
private Camera eyesCamera;
void Start()
{
eyesCamera = GetComponentInChildren<Camera>();
}
private void Shoot()
{
RaycastHit hit;
if (Physics.Raycast(eyesCamera.transform.position, eyesCamera.transform.forward, out hit, weapon.range))
{
ShootServerRpc(hit.transform.name);
}
}
[ServerRpc]
private void ShootServerRpc(string hittedName)
{
GameManager.UpdateInfo(transform.name + " 击中了 " + hittedName);
}
// Update is called once per frame
void Update()
{
if (Input.GetButtonDown("Fire1"))
{
Shoot();
}
}
}
using System;
[Serializable]
public class PlayerWeapon
{
public string name = "M16A1";
public int damage = 10;
public float range = 100f;
}
using UnityEngine;
public class GameManager : MonoBehaviour
{
private static string info;
public static void UpdateInfo(string _info)
{
info = _info;
}
private void OnGUI()
{
GUILayout.BeginArea(new Rect(200f, 200f, 200f, 400f));
GUILayout.BeginVertical();
GUILayout.Label(info);
Debug.Log(info);
GUILayout.EndVertical();
GUILayout.EndArea();
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
public class playerInput : MonoBehaviour
{
[SerializeField]
private float speed = 5f;
[SerializeField]
private float lookSensitivity = 3f;
[SerializeField]
private PlayerController playerController;
[SerializeField]
private float trusterForce = 20f;
private ConfigurableJoint joint;
void Start()
{
Cursor.lockState = CursorLockMode.Locked;
joint = GetComponent<ConfigurableJoint>();
}
void Update()
{
//水平方向的移动
float xMov = Input.GetAxisRaw("Horizontal");
float yMov = Input.GetAxisRaw("Vertical");
//normalized是单位化,保证速度不会因为斜向移动而变快
Vector3 velocity = (transform.right * xMov + transform.forward * yMov).normalized * speed;
playerController.Move(velocity);
float xMouse = Input.GetAxisRaw("Mouse X");
float yMouse = Input.GetAxisRaw("Mouse Y");
Vector3 xRot = new Vector3(yMouse, 0f, 0f) * lookSensitivity;
Vector3 yRot = new Vector3(0f, xMouse, 0f) * lookSensitivity;
playerController.Rotate(xRot, yRot);
Vector3 force = Vector3.zero;
if (Input.GetButton("Jump"))
{
force = Vector3.up * trusterForce;
joint.yDrive = new JointDrive { positionSpring = 0f, maximumForce = 0f, positionDamper = 0f };
}
else
{
joint.yDrive = new JointDrive { positionSpring = 20f, maximumForce = 40f, positionDamper = 0f };
}
playerController.Truster(force);
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
[SerializeField]
private Rigidbody rb;
private Vector3 velocity = Vector3.zero;
private Vector3 xRot = Vector3.zero;
private Vector3 yRot = Vector3.zero;
private Vector3 thrusterForce = Vector3.zero;
[SerializeField]
private Camera cam;
void Start()
{
}
public void Move(Vector3 _velocity)
{
velocity = _velocity;
}
public void Rotate(Vector3 _xRot, Vector3 _yRot)
{
xRot = _xRot;
yRot = _yRot;
}
public void Truster(Vector3 _thrusterForce)
{
thrusterForce = _thrusterForce;
}
private void PerformMovement()
{
if (velocity != Vector3.zero)
{
rb.MovePosition(rb.position + velocity * Time.fixedDeltaTime);
}
if (thrusterForce != Vector3.zero)
{
rb.AddForce(thrusterForce);
}
}
private void PerformRotation()
{
if (xRot != Vector3.zero)
{
cam.transform.Rotate(-xRot);
}
if (yRot != Vector3.zero)
{
rb.transform.Rotate(yRot);
}
}
private void FixedUpdate()
{
PerformMovement();
PerformRotation();
}
}
评论区