解决Host与Server的矛盾
在WeaponManager里
public class WeaponManager : NetworkBehaviour
{
...
[ServerRpc]
private void ToggleWeaponServerRpc()
{
//用IsHost优化逻辑
if (!IsHost)
{
ToggleWeapon();
}
ToggleWeaponClientRpc();
}
}
在Player中
public class Player : NetworkBehaviour
{
...
public void TakeDamage(int damage) {
...
if (currentHealth.Value <= 0) {
currentHealth.Value = 0;
//优化
if (!IsHost)
{
DieOnServer();
}
DieClientRpc();
}
}
...
}
给武器添加特效
先将需要的特效拖进文件夹Prefabs里
在M16里添加新物体FirePoint,把MuzzleFlash拖进去
调整枪火的位置,角度,大小
叉掉两个选项
沙鹰同理
创建一个WeaponGraphics脚本来管理特效
public class WeaponGraphics : MonoBehaviour
{
public ParticleSystem muzzleFlash;
//可能需要现时生成出来,所以用的GameObject
public GameObject goopHitEffectPrefab;
public GameObject stoneEffectPrefab;
}
把这个类分别给各种枪装上,并将对应的特效被WeaponGraphics引用
渲染特效
在WeaponManager里
public class WeaponManager : NetworkBehaviour
{
...
//当前武器的特效
private WeaponGraphics currentGraphics;
...
//切换武器
public void EquipWeapon(PlayerWeapon weapon) {
...
//切换武器时要把当前的特效也换成武器相应的特效
currentGraphics = weaponObject.GetComponent<WeaponGraphics>();
}
//动态返回当前特效
public WeaponGraphics GetCurrentGraphics()
{
return currentGraphics;
}
...
}
为了让火花渲染出来需要将PlayerShooting解禁,如果PlayerShooting被禁,ClientRpc内的函数就不能让客户端调用PlayerShooting类里面的OnShoot();
在PlayerShooting里
public class PlayerShooting : NetworkBehaviour
{
...
//编写每次射击前的逻辑
private void OnShoot()
{
//播放特效
weaponManager.GetCurrentGraphics().muzzleFlash.Play();
}
[ClientRpc]
private void OnShootClientRpc()
{
OnShoot();
}
[ServerRpc]
private void OnShootServerRpc()
{
if (!IsHost)
{
OnShoot();
}
OnShootClientRpc();
}
private void Shoot() {
OnShootServerRpc();
...
}
void Update()
{
if (!IsLocalPlayer) return;
...
}
}
修复切枪连射bug
在PlayerShooting里
public class PlayerShooting : NetworkBehaviour
{
...
void Update()
{
...
else //连发
{
...
//修复bug
else if (Input.GetButtonUp("Fire1") || Input.GetKeyDown(KeyCode.Q))
{
//对Shoot()关闭周期函数
CancelInvoke("Shoot");
}
}
}
...
}
添加受击特效
修改特效参数,改成一个点
修改渲染时间
在PlayerShooting里
public class PlayerShooting : NetworkBehaviour
{
//enum好像定义一组用于枚举的常量
enum HitEffectMaterial
{
Goop,
Stone
}
//传三个参数,位置,角度, 材质
private void OnHit(Vector3 pos, Vector3 normal, HitEffectMaterial material)
{
GameObject hitEffectPrefab;
if(material == HitEffectMaterial.Goop)
{
hitEffectPrefab = weaponManager.GetCurrentGraphics().goopHitEffectPrefab;
}
else
{
hitEffectPrefab = weaponManager.GetCurrentGraphics().stoneEffectPrefab;
}
//Quaternion.LookRotation(normal)可以返回normal的反方向
GameObject hitEffectObject = Instantiate(hitEffectPrefab, pos, Quaternion.LookRotation(normal));
//将特效物体转成粒子
ParticleSystem particleSystem = hitEffectObject.GetComponent<ParticleSystem>();
//瞬发
particleSystem.Emit(1);
particleSystem.Play();
Destroy(hitEffectObject, 1f);
}
[ServerRpc]
private void OnHitServerRpc(Vector3 pos, Vector3 normal, HitEffectMaterial material)
{
if (!IsHost)
{
OnHit(pos, normal, material);
}
OnHitClientRpc(pos, normal, material);
}
[ClientRpc]
private void OnHitClientRpc(Vector3 pos, Vector3 normal, HitEffectMaterial material)
{
OnHit(pos, normal, material);
}
private void Shoot() {
OnShootServerRpc();
if (!IsLocalPlayer) return;
//此为Raycast射中的物体,因为Physics.Raycast的返回值为bool类型
RaycastHit hit;
//Physics.Raycast对带有collider的物体进行信息反馈
//射出位置, 射出方向, 射中物体,射程,需要穿过的物体
//射击时改成当前武器
if (Physics.Raycast(cam.transform.position, cam.transform.forward, out hit, currentWeapon.range, mask)) {
//GameObject.ocllider.tag就能访问物体的标签
if (hit.collider.tag == "Player") {
//射线碰撞在客户端,而让受伤的逻辑在服务器上实现
ShootServerRpc(hit.collider.name, currentWeapon.damage);
OnHitServerRpc(hit.point, hit.normal, HitEffectMaterial.Goop);
}
else
{
OnHitServerRpc(hit.point, hit.normal, HitEffectMaterial.Stone);
}
}
}