最近在准备《虚拟现实》这门课的期末作业,用Unity 3D实现一个课设,因为我和组员做的是基于Unity.Netcode的联网射击第三人称游戏,所以遇到了这个问题,小编也是忙活了2个星期才得以解决,特此分享哦
---------------------------------------------------------------------------------------------------------------------------------
参考了B站大佬的思路,【Unity联网游戏教程】创建服务器端子弹_哔哩哔哩_bilibili
不过他做的是2D游戏,我的是3D,整体思路就是
1.做两个子弹的变体
2.将这两个子弹变体运用起来
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Unity.Netcode;
using System;
public class BulletLaunch : NetworkBehaviour
{
[SerializeField] private GameObject serverBullet;
[SerializeField] private GameObject clientBullet;
[SerializeField] private Transform BulletSpawn;
[SerializeField] private float BulletSpeed=20;
// Start is called before the first frame update
public AudioClip clip;
//关联换子弹音效
public AudioClip check;
// 子弹个数
private int bulletCount = 10;
//声音
private AudioSource Gunplayer;
void Start()
{
Gunplayer = GetComponent<AudioSource>();
}
// Update is called once per frame
void Update()
{
if(Input.GetKeyDown(KeyCode.Mouse0) && bulletCount > 0)
{
if(IsLocalPlayer)
{
clientFire();
bulletCount --;
Gunplayer.PlayOneShot(clip);
if (bulletCount == 0)
{
Gunplayer.PlayOneShot(check);
Invoke("Reload", 1.5f);
}
if(Input.GetKeyDown(KeyCode.R))
{
Gunplayer.PlayOneShot(check);
Invoke("Reload",1.5f);
}
}
}
}
[ClientRpc]
private void clientFireClientRpc(Vector3 Bposition, Quaternion Brotation)
{
GameObject ctemp = Instantiate(clientBullet,Bposition, Brotation);
ctemp.GetComponent<Rigidbody>().velocity = ctemp.transform.forward *BulletSpeed; // 速度
}
[ServerRpc]
private void FireServerRpc(Vector3 Bposition, Quaternion Brotation)
{
GameObject temp = Instantiate(serverBullet,Bposition, Brotation);
temp.GetComponent<Rigidbody>().velocity = temp.transform.forward *BulletSpeed; // 速度
clientFireClientRpc(Bposition,Brotation); // 通知
}
private void clientFire()
{
if(IsServer){
//客户端生成假子弹
GameObject stemp = Instantiate(serverBullet,BulletSpawn.transform.position, BulletSpawn.transform.rotation);
stemp.GetComponent<Rigidbody>().velocity = stemp.transform.forward * BulletSpeed; // 速度
clientFireClientRpc(BulletSpawn.transform.position, BulletSpawn.transform.rotation); // 通知
}
else{
GameObject ctemp = Instantiate(clientBullet,BulletSpawn.transform.position, BulletSpawn.transform.rotation);
ctemp.GetComponent<Rigidbody>().velocity = ctemp.transform.forward * BulletSpeed; // 速度
//请求服务端生成子弹
FireServerRpc(BulletSpawn.transform.position, BulletSpawn.transform.rotation);
}
}
void Reload()
{
bulletCount=10;
}
}
3.然后再给玩家添加一个受到伤害的脚本
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DamageToPlayer : MonoBehaviour
{
[SerializeField] private int damage = 20;
private void OnTriggerEnter(Collider coll)
{
if (coll.tag == "Player")
{
//如果子弹碰撞到的是玩家
//那就获取被碰撞玩家的health组件
if(coll.attachedRigidbody.TryGetComponent<playerHealth>(out playerHealth health))
{
health.TakeDamage(damage);
Destroy(this);
}
}
}
}
4.给玩家添加的生命值脚本需要网络变量来实现,他会自动更新在服务器的,这样当玩家生命值为
0 的时候,大家都知道,便于后面的逻辑
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Unity.Netcode;
using TMPro;
public class playerHealth : NetworkBehaviour
{
[field: SerializeField] public int MaxHealth { get; private set; } = 100;
public NetworkVariable<int> CurrentHealth = new NetworkVariable<int>();
public NetworkVariable<bool> IsDead = new NetworkVariable<bool>(); // 同步玩家是否死亡状态
[SerializeField] private TextMeshProUGUI textField;
Animator _selfAnim; // 获取动画
public override void OnNetworkSpawn()
{
if (!IsServer) return;
CurrentHealth.Value = MaxHealth;
IsDead.Value = false; // 初始化时玩家不死亡
_selfAnim = GetComponent<Animator>(); // 获取动画
}
public void TakeDamage(int damage)
{
ModifyHealth(-damage);
}
public void RestoreHealth(int damage)
{
ModifyHealth(damage);
}
public void ModifyHealth(int value)
{
if (IsDead.Value) {
_selfAnim.SetTrigger("isdead"); // 死亡动画
return;
}
var newHealth = CurrentHealth.Value + value;
CurrentHealth.Value = Mathf.Clamp(newHealth, 0, MaxHealth);
if (CurrentHealth.Value == 0)
{
IsDead.Value = true; // 更新玩家死亡状态
}
}
void Update()
{
// 根据玩家死亡状态更新 TextMeshPro 组件的显示
textField.enabled = IsDead.Value;
}
}
结尾:只要大家不放弃,有问题都能解决的,加油!