实现武器单发和连发
在PlayerWeapon类中定义武器的shootRate,用于表示射速。
如果shootRate这个值为0的话,说明是单发,否则的话通过shootRate来实现每秒打多少发。连发实际上就是快速的单发。通过InvokeRepeating这个函数可以实现这个功能(周期性地调动这个函数,第一个参数:多少秒后第一次调用; 第二个参数:平均每两次的执行间隔)
当我们松开的时候,需要将这个事件终止。同样的也有一个api——CancelInvoke(“函数名称”)
void Update()
{
if (!IsLocalPlayer) return;
currentWeapon = weaponManager.GetCurrentWeapon();
if (currentWeapon.shootRate <= 0)
{
if (Input.GetButtonDown("Fire1"))
{
Shoot();
}
}
else
{
if (Input.GetButtonDown("Fire1"))
{
InvokeRepeating("Shoot", 0f, 1f / 10);
}
else if (Input.GetButtonUp("Fire1") || Input.GetKey(KeyCode.Q))
{
CancelInvoke("Shoot");
}
}
}
实现人物切换武器
为了方便武器的更换,在这里我们采取一种新的方式来动态更换武器
首先,我们要知道,人物拿着武器,实质上就是将武器放在一个合适的位置,然后再将这个武器给渲染出来。因此,在这里我们可以首先在武器的位置创建一个空的GameObject来记录当前武器的位置,在Player预制体中的Camera下,创建一个GameObject,命名为WeaponHolder,我们只需要动态的将武器挂载到WeaponHolder中即可。
其次,我们来创建一个脚本用来管理玩家的武器——WeaponManager.cs。由于我们的武器之前是挂载在PlayerWeapon上的,这个脚本上包含着武器的信息,我们还需要添加一些变量shootRate(一秒可以打多少发)。
第三步,我们需要写几个辅助函数,例如获取当前的武器,装备武器、更改武器等。
在Update()中监听切换武器的快捷键Q。
处理网络同步问题,和之前讲到的一样,写一个ServerRpc函数,用于在服务器端执行,写一个ClientRpc函数,用于在客户端执行。从而实现客户端和服务端同步,其他玩家也可以看到切枪。
最后更改
PlayerShoot.cs
文件,将其中weapon属性改为currentWeapon,且在Update()中动态地从WeaponManager类中获取当前武器。
具体代码
weaponManager.cs
using Unity.Netcode;
using UnityEngine;
public class WeaponManager : NetworkBehaviour
{
[SerializeField]
private GameObject weaponHolder;
[SerializeField]
private PlayerWeapon primaryWeapon;
[SerializeField]
private PlayerWeapon secondaryWeapon;
private PlayerWeapon currentWeapon;
private WeaponGraphics currentGraphics;
void Start()
{
EquipWeapon(primaryWeapon);
}
public PlayerWeapon GetCurrentWeapon()
{
return currentWeapon;
}
public WeaponGraphics GetCurrentGraphics()
{
return currentGraphics;
}
private void EquipWeapon(PlayerWeapon weapon)
{
currentWeapon = weapon;
for (int i = 0; i < weaponHolder.transform.childCount; i++)
{
Destroy(weaponHolder.transform.GetChild(i).gameObject);
}
GameObject weaponObject = Instantiate(currentWeapon.graphics, weaponHolder.transform.position, weaponHolder.transform.rotation);
weaponObject.transform.SetParent(weaponHolder.transform);
currentGraphics = weaponObject.GetComponent<WeaponGraphics>();
}
public void ToggleWeapon()
{
if (currentWeapon == primaryWeapon)
{
EquipWeapon(secondaryWeapon);
}
else
{
EquipWeapon(primaryWeapon);
}
}
[ClientRpc]
private void ToggleWeaponClientRpc()
{
ToggleWeapon();
}
[ServerRpc]
private void ToggleWeaponServerRpc()
{
if (!IsHost)
{
ToggleWeapon();
}
ToggleWeaponClientRpc();
}
void Update()
{
if (IsLocalPlayer)
{
if (Input.GetKeyDown(KeyCode.Q))
{
ToggleWeaponServerRpc();
}
}
}
}
PlayerShoot.cs
using Unity.Netcode;
using UnityEngine;
public class PlayerShoot : NetworkBehaviour
{
private const string PLAYER_TAG = "Player";
private PlayerWeapon currentWeapon;
private Camera eyesCamera;
private WeaponManager weaponManager;
private PlayerController playerController;
void Start()
{
eyesCamera = GetComponentInChildren<Camera>();
weaponManager = GetComponent<WeaponManager>();
playerController = GetComponent<PlayerController>();
}
[ServerRpc]
private void OnShootServerRpc()
{
if (!IsHost)
{
OnShoot();
}
OnShootClientRpc();
}
[ClientRpc]
private void OnShootClientRpc()
{
OnShoot();
}
private void Shoot()
{
RaycastHit hit;
if (Physics.Raycast(eyesCamera.transform.position, eyesCamera.transform.forward, out hit, currentWeapon.range))
{
if (hit.collider.tag == PLAYER_TAG)
{
ShootServerRpc();
}
}
}
[ServerRpc]
private void ShootServerRpc(string name, int damage)
{
PlayerObject player = GameManager.singleton.GetPlayer(name);
player.TakeDamage(damage);
}
void Update()
{
if (!IsLocalPlayer) return;
currentWeapon = weaponManager.GetCurrentWeapon();
}
}
评论区