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);
    }
}

射击

理清步骤

  1. 确定射击的方式,是从眼睛瞄准的方向开始发射子弹(也就是Camera),并不是从枪口里发射子弹。

  2. 将武器的属性封装成一个单独的类。

  3. 具体实现有Unity自带的API,Physics.Raycast()函数,第一个参数为射线的起点位置,第二个参数为射线的方向,第三个参数为第一个被击中的目标,第四个为射线的距离。

  4. 规范人物的命名,每个玩家身上都有一个NetworkObject组件,其中有一个属性GlobalObjectHash,其在每个客户端的同一个玩家身上都是相同的,我们可以将玩家命名为"Player " + hash

  5. 在调试阶段,我们可以在屏幕上将命中的物体名字打印出来。具体逻辑在GameManager.cs中。

  6. 将击中后的结果发送给服务器

客户端和服务端的逻辑关系

由于在这里开枪的动作,是各自执行各自的代码,因此也需要将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();
    }
}