目录
前两天我突发奇想想做联机游戏,就去找教程,肝了一天终于做出来了。
做的这个实例是通过PUN2实现的,看一下效果:

先说一下搜寻资料过程中找到的实现游戏联机的方式:暂时就记录了这11个。
视频教程是小破站Up主:独立开发者C酱,个人感觉这套模型和这个教程泰裤辣,能跟着做完这个游戏Demo也是很开心的,下面依然以博客的形式记录实现这个游戏的过程。
首先新建一个U3D项目导入素材包。
https://download.csdn.net/download/qq_48512649/88858525
https://download.csdn.net/download/qq_48512649/88858525去Unity官方资源商店下载PUN2插件导入到项目中

要去PUN2官网申请PUN2账号获取AppID,获取AppID教程参考下面这篇文章:不同的是Photon Type要改为PUN

生成AppID后把它复制到插件中去


粘贴生成好的AppID:

玩家登录场景是login,战斗场景是game

双击登录场景,编写Game脚本挂载到Game上

- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
- using Photon.Pun;
-
- public class Game : MonoBehaviour
- {
- public static UIManager uiManager;
-
- public static bool isLoaded = false;
-
- private void Awake()
- {
- if (isLoaded == true)
- {
- Destroy(gameObject);
- }
- else
- {
- isLoaded = true;
- DontDestroyOnLoad(gameObject); //跳转场景当前游戏物体不删除
- uiManager = new UIManager();
- uiManager.Init();
-
- //设置发送 接收消息频率 降低延迟
- PhotonNetwork.SendRate = 50;
- PhotonNetwork.SerializationRate = 50;
- }
- }
-
- // Start is called before the first frame update
- void Start()
- {
- //显示登录界面
- uiManager.ShowUI
("LoginUI"); - }
-
- // Update is called once per frame
- void Update()
- {
-
- }
- }
LoginUI脚本,对开始游戏和退出游戏按键的处理
- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
- using UnityEngine.UI;
- using Photon.Pun;
- using Photon.Realtime;
-
- //登录界面
- public class LoginUI : MonoBehaviour,IConnectionCallbacks
- {
- // Start is called before the first frame update
- void Start()
- {
- transform.Find("startBtn").GetComponent
- transform.Find("quitBtn").GetComponent
- }
-
- private void OnEnable()
- {
- PhotonNetwork.AddCallbackTarget(this); //注册pun2事件
- }
-
- private void OnDisable()
- {
- PhotonNetwork.RemoveCallbackTarget(this); //注销pun2事件
- }
-
- public void onStartBtn()
- {
- Game.uiManager.ShowUI
("MaskUI").ShowMsg("正在连接服务器..."); -
- //连接pun2服务器
- PhotonNetwork.ConnectUsingSettings(); //成功后会执行OnConnectedToMaster函数
- }
-
- public void onQuitBtn()
- {
- Application.Quit();
- }
-
- public void OnConnected()
- {
-
- }
-
- //连接成功后执行的函数
- public void OnConnectedToMaster()
- {
- //关闭所有界面
- Game.uiManager.CloseAllUI();
- Debug.Log("连接成功");
- //显示大厅界面
- Game.uiManager.ShowUI
("LobbyUI"); - }
-
- //断开服务器执行的函数
- public void OnDisconnected(DisconnectCause cause)
- {
- Game.uiManager.CloseUI("MaskUI");
- }
-
- public void OnRegionListReceived(RegionHandler regionHandler)
- {
-
- }
-
- public void OnCustomAuthenticationResponse(Dictionary<string, object> data)
- {
-
- }
-
- public void OnCustomAuthenticationFailed(string debugMessage)
- {
-
- }
-
- }
MaskUI脚本提供遮罩界面文字显示的公共调用方法
- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
- using UnityEngine.UI;
-
- //遮罩界面
- public class MaskUI : MonoBehaviour
- {
- // Start is called before the first frame update
- void Start()
- {
-
- }
-
- public void ShowMsg(string msg)
- {
- transform.Find("msg/bg/Text").GetComponent
().text = msg; - }
- }
连接服务器成功可以看到控制台打印并输出
编写脚本 LobbyUI 处理游戏大厅界面

- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
- using Photon.Pun;
- using Photon.Realtime;
- using UnityEngine.UI;
-
- //大厅界面
- public class LobbyUI : MonoBehaviourPunCallbacks
- {
- private TypedLobby lobby; //大厅对象
-
- private Transform contentTf;
- private GameObject roomPrefab;
- void Start()
- {
- //关闭按钮
- transform.Find("content/title/closeBtn").GetComponent
- //创建房间按钮
- transform.Find("content/createBtn").GetComponent
- //刷新按钮
- transform.Find("content/updateBtn").GetComponent
-
- contentTf = transform.Find("content/Scroll View/Viewport/Content");
- roomPrefab = transform.Find("content/Scroll View/Viewport/item").gameObject;
-
- lobby = new TypedLobby("fpsLobby", LobbyType.SqlLobby); //1.大厅名字 2.大厅类型(可搜索)
- //进入大厅
- PhotonNetwork.JoinLobby(lobby);
- }
-
- //进入大厅回调
- public override void OnJoinedLobby()
- {
- Debug.Log("进入大厅...");
- }
-
- //创建房间
- public void onCreateRoomBtn()
- {
- Game.uiManager.ShowUI
("CreateRoomUI"); - }
-
- //关闭大厅界面
- public void onCloseBtn()
- {
- //断开连接
- PhotonNetwork.Disconnect();
- Game.uiManager.CloseUI(gameObject.name);
- //显示登录界面
- Game.uiManager.ShowUI
("LoginUI"); - }
-
- //刷新房间列表
- public void onUpdateRoomBtn()
- {
- Game.uiManager.ShowUI
("MaskUI").ShowMsg("刷新中..."); - PhotonNetwork.GetCustomRoomList(lobby, "1"); //执行该方法后会触发OnRoomListUpdate回调
- }
-
- //清除已经存在的房间物体
- private void ClearRoomList()
- {
- while (contentTf.childCount != 0)
- {
- DestroyImmediate(contentTf.GetChild(0).gameObject);
- }
- }
-
- //刷新房间后的回调
- public override void OnRoomListUpdate(List
roomList ) - {
- Game.uiManager.CloseUI("MaskUI");
- Debug.Log("房间刷新");
-
- ClearRoomList();
- for (int i = 0; i < roomList.Count; i++)
- {
- GameObject obj = Instantiate(roomPrefab, contentTf);
- obj.SetActive(true);
- string roomName = roomList[i].Name; //房间名称
- obj.transform.Find("roomName").GetComponent
().text = roomName; - obj.transform.Find("joinBtn").GetComponent
- {
- Debug.Log(roomName);
- //加入房间
- Game.uiManager.ShowUI
("MaskUI").ShowMsg("加入中..."); -
- PhotonNetwork.JoinRoom(roomName); //加入房间
- });
- }
- }
-
- public override void OnJoinedRoom()
- {
- //加入房间回调
- Game.uiManager.CloseAllUI();
- Game.uiManager.ShowUI
("RoomUI"); - }
-
- public override void OnJoinRoomFailed(short returnCode, string message)
- {
- //加入房间失败
- Game.uiManager.CloseUI("MaskUI");
- }
- }
创建房间脚本 CreateRoomUI

- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
- using Photon.Pun;
- using Photon.Realtime;
- using UnityEngine.UI;
-
- public class CreateRoomUI : MonoBehaviourPunCallbacks
- {
- private InputField roomNameInput; //房间名称
- void Start()
- {
- transform.Find("bg/title/closeBtn").GetComponent
- transform.Find("bg/okBtn").GetComponent
- roomNameInput = transform.Find("bg/InputField").GetComponent
(); -
- //随机一个房间名称
- roomNameInput.text = "room_" + Random.Range(1, 9999);
- }
-
- //创建房间
- public void onCreateBtn()
- {
- Game.uiManager.ShowUI
("MaskUI").ShowMsg("创建中..."); - RoomOptions room = new RoomOptions();
- room.MaxPlayers = 8; //房间最大玩家数
- PhotonNetwork.CreateRoom(roomNameInput.text, room); //1.房间名称 2.房间的对象参数
- }
-
- //关闭按钮
- public void onCloseBtn()
- {
- Game.uiManager.CloseUI(gameObject.name);
- }
-
- //创建成功后回调
- public override void OnCreatedRoom()
- {
- Debug.Log("创建成功");
- Game.uiManager.CloseAllUI();
- //显示房间UI
- Game.uiManager.ShowUI
("RoomUI"); - }
-
- //创建失败
- public override void OnCreateRoomFailed(short returnCode, string message)
- {
- Game.uiManager.CloseUI("MaskUI");
- }
- }
创建房间完成后会进入到房间里 编写RoomUI脚本

- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
- using Photon.Pun;
- using Photon.Realtime;
- using UnityEngine.UI;
-
- public class RoomUI : MonoBehaviour,IInRoomCallbacks
- {
- Transform startTf;
- Transform contentTf;
- GameObject roomPrefab;
- public List
roomList; - private void Awake()
- {
- roomList = new List
(); - contentTf = transform.Find("bg/Content");
- roomPrefab = transform.Find("bg/roomItem").gameObject;
- transform.Find("bg/title/closeBtn").GetComponent
- startTf = transform.Find("bg/startBtn");
- startTf.GetComponent
-
- PhotonNetwork.AutomaticallySyncScene = true; //执行PhotonNetwork.LoadLevel加载场景的时候 其他玩家也跳转相同的场景
- }
-
- void Start()
- {
- //生成房间里的玩家项
- for (int i = 0; i < PhotonNetwork.PlayerList.Length; i++)
- {
- Player p = PhotonNetwork.PlayerList[i];
- CreateRoomItem(p);
- }
- }
-
- private void OnEnable()
- {
- PhotonNetwork.AddCallbackTarget(this);
- }
-
- private void OnDisable()
- {
- PhotonNetwork.RemoveCallbackTarget(this);
- }
-
- //生成玩家
- public void CreateRoomItem(Player p)
- {
- GameObject obj = Instantiate(roomPrefab, contentTf);
- obj.SetActive(true);
- RoomItem item = obj.AddComponent
(); - item.owerId = p.ActorNumber; //玩家编号
- roomList.Add(item);
-
- object val;
- if (p.CustomProperties.TryGetValue("IsReady", out val))
- {
- item.IsReady = (bool)val;
- }
- }
-
- //删除离开房间的玩家
- public void DeleteRoomItem(Player p)
- {
- RoomItem item = roomList.Find((RoomItem _item) => { return p.ActorNumber == _item.owerId; });
- if (item != null)
- {
- Destroy(item.gameObject);
- roomList.Remove(item);
- }
- }
-
- //关闭
- void onCloseBtn()
- {
- //断开连接
- PhotonNetwork.Disconnect();
- Game.uiManager.CloseUI(gameObject.name);
- Game.uiManager.ShowUI
("LoginUI"); - }
-
- //开始游戏
- void onStartBtn()
- {
- //加载场景 让房间里的玩家也加载场景
- PhotonNetwork.LoadLevel("game");
- }
-
- //新玩家进入房间
- public void OnPlayerEnteredRoom(Player newPlayer)
- {
- CreateRoomItem(newPlayer);
- }
-
- //房间里的其他玩家离开房间
- public void OnPlayerLeftRoom(Player otherPlayer)
- {
- DeleteRoomItem(otherPlayer);
- }
-
- public void OnRoomPropertiesUpdate(ExitGames.Client.Photon.Hashtable propertiesThatChanged)
- {
-
- }
-
- //玩家自定义参数更新回调
- public void OnPlayerPropertiesUpdate(Player targetPlayer, ExitGames.Client.Photon.Hashtable changedProps)
- {
- RoomItem item = roomList.Find((_item) => { return _item.owerId == targetPlayer.ActorNumber; });
- if (item != null)
- {
- item.IsReady = (bool)changedProps["IsReady"];
- item.ChangeReady(item.IsReady);
- }
-
- //如果是主机玩家判断所有玩家的准备状态
- if (PhotonNetwork.IsMasterClient)
- {
- bool isAllReady = true;
- for (int i = 0; i < roomList.Count; i++)
- {
- if (roomList[i].IsReady == false)
- {
- isAllReady = false;
- break;
- }
- }
- startTf.gameObject.SetActive(isAllReady); //开始按钮是否显示
- }
- }
-
- public void OnMasterClientSwitched(Player newMasterClient)
- {
-
- }
- }
玩家进入房间后会显示信息和准备状况,编写RoomItem脚本实现。只有房间内所有玩家都处于准备状态房主才能开始游戏。

- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
- using Photon.Pun;
- using Photon.Realtime;
- using UnityEngine.UI;
-
- public class RoomItem : MonoBehaviour
- {
-
- public int owerId; //玩家编号
- public bool IsReady = false; //是否准备
- void Start()
- {
- if (owerId == PhotonNetwork.LocalPlayer.ActorNumber)
- {
- transform.Find("Button").GetComponent
- }
- else
- {
- transform.Find("Button").GetComponent
().color = Color.black; - }
-
- ChangeReady(IsReady);
- }
-
- public void OnReadyBtn()
- {
- IsReady = !IsReady;
-
- ExitGames.Client.Photon.Hashtable table = new ExitGames.Client.Photon.Hashtable();
-
- table.Add("IsReady", IsReady);
-
- PhotonNetwork.LocalPlayer.SetCustomProperties(table); //设置自定义参数
-
- ChangeReady(IsReady);
- }
-
- public void ChangeReady(bool isReady)
- {
- transform.Find("Button/Text").GetComponent
().text = isReady == true ? "已准备" : "未准备"; - }
- }
双击切换到战斗场景game中,编写FightManager脚本挂载到fight上

- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
- using Photon.Pun;
-
- public class FightManager : MonoBehaviour
- {
- private void Awake()
- {
- //隐藏鼠标
- Cursor.lockState = CursorLockMode.Locked;
- Cursor.visible = false;
-
- //关闭所有界面
- Game.uiManager.CloseAllUI();
- //显示战斗界面
- Game.uiManager.ShowUI
("FightUI"); -
- Transform pointTf = GameObject.Find("Point").transform;
-
- Vector3 pos = pointTf.GetChild(Random.Range(0, pointTf.childCount)).position;
-
- //实例化角色
- PhotonNetwork.Instantiate("Player", pos, Quaternion.identity); //实例化的资源要放在Resources文件夹
- }
- }
编写战斗场景UI界面处理脚本FightUI

- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
- using UnityEngine.UI;
-
- public class FightUI : MonoBehaviour
- {
- private Image bloodImg;
- void Start()
- {
- bloodImg = transform.Find("blood").GetComponent
(); - }
-
- //更新子弹个数显示
- public void UpdateBulletCount(int count)
- {
- transform.Find("bullet/Text").GetComponent
().text = count.ToString(); - }
-
- //更新血量
- public void UpdateHp(float cur, float max)
- {
- transform.Find("hp/fill").GetComponent
().fillAmount = cur / max; - transform.Find("hp/Text").GetComponent
().text = cur + "/" + max; - }
-
- public void UpdateBlood()
- {
- StopAllCoroutines();
- StartCoroutine(UpdateBloodCo());
- }
-
- public IEnumerator UpdateBloodCo()
- {
- bloodImg.color = Color.white;
- Color color = bloodImg.color;
- float t = 0.35f;
- while (t >= 0)
- {
- t -= Time.deltaTime;
- color.a = Mathf.Abs(Mathf.Sin(Time.realtimeSinceStartup));
- bloodImg.color = color;
-
- yield return null;
- }
-
- color.a = 0;
- bloodImg.color = color;
- }
- }
编写脚本 LossUI

- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
- using UnityEngine.UI;
-
- public class LossUI : MonoBehaviour
- {
- public System.Action onClickCallBack;
- // Start is called before the first frame update
- void Start()
- {
- transform.Find("resetBtn").GetComponent
- }
-
- public void OnClickBtn()
- {
- if (onClickCallBack != null)
- {
- onClickCallBack();
- }
-
- Game.uiManager.CloseUI(gameObject.name);
- }
- }
给角色Player挂载Photon View组件,实例化生成玩家的代码我们已经在FightManager脚本中实现了。

编写角色控制脚本PlayerController挂载到角色上,战斗系统的逻辑也在这个脚本里,其中的参数数值可以参考下图:

- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
- using Photon.Pun;
- using Photon.Realtime;
-
- //角色控制器
- public class PlayerController : MonoBehaviourPun,IPunObservable
- {
-
- //组件
- public Animator ani;
- public Rigidbody body;
- public Transform camTf; //跟随的相机
-
- //数值
- public int CurHp = 10;
- public int MaxHp = 10;
- public float MoveSpeed = 5f;
-
- public float H; //水平值
- public float V; //垂直值
- public Vector3 dir; //移动方向
-
- public Vector3 offset; //摄像机与角色之间的偏移值
-
- public float Mouse_X; //鼠标偏移值
- public float Mouse_Y;
- public float scroll; //鼠标滚轮值
- public float Angle_X; //x轴的旋转角度
- public float Angle_Y; //y轴的旋转角度
-
- public Quaternion camRotation; //摄像机旋转的四元数
-
- public Gun gun; //枪的脚本
-
- //声音
- public AudioClip reloadClip;
- public AudioClip shootClip;
-
- public bool isDie = false;
-
- public Vector3 currentPos;
- public Quaternion currentRotation;
-
- void Start()
- {
- Angle_X = transform.eulerAngles.x;
- Angle_Y = transform.eulerAngles.y;
-
- ani = GetComponent
(); - body = GetComponent
(); - gun = GetComponentInChildren
(); - camTf = Camera.main.transform;
- currentPos = transform.position;
- currentRotation = transform.rotation;
- if (photonView.IsMine)
- {
- Game.uiManager.GetUI
("FightUI").UpdateHp(CurHp, MaxHp); - }
- }
-
-
- void Update()
- {
- //判断是否是本机玩家 只能操作本机角色
- if (photonView.IsMine)
- {
- if (isDie == true)
- {
- return;
- }
- UpdatePosition();
- UpdateRotation();
- InputCtl();
- }
- else
- {
- UpdateLogic();
- }
- }
-
- //其他角色更新发送过来的数据(位置 旋转)
- public void UpdateLogic()
- {
- transform.position = Vector3.Lerp(transform.position, currentPos, Time.deltaTime * MoveSpeed * 10);
- transform.rotation = Quaternion.Slerp(transform.rotation, currentRotation, Time.deltaTime * 500);
- }
-
- private void LateUpdate()
- {
- ani.SetFloat("Horizontal", H);
- ani.SetFloat("Vertical", V);
- ani.SetBool("isDie", isDie);
- }
-
- //更新位置
- public void UpdatePosition()
- {
- H = Input.GetAxisRaw("Horizontal");
- V = Input.GetAxisRaw("Vertical");
- dir = camTf.forward * V + camTf.right * H;
- body.MovePosition(transform.position + dir * Time.deltaTime * MoveSpeed);
- }
-
- //更新旋转(同时设置摄像机的位置的旋转值)
- public void UpdateRotation()
- {
- Mouse_X = Input.GetAxisRaw("Mouse X");
- Mouse_Y = Input.GetAxisRaw("Mouse Y");
- scroll = Input.GetAxis("Mouse ScrollWheel");
-
- Angle_X = Angle_X - Mouse_Y;
- Angle_Y = Angle_Y + Mouse_X;
-
- Angle_X = ClampAngle(Angle_X, -60, 60);
- Angle_Y = ClampAngle(Angle_Y, -360, 360);
-
- camRotation = Quaternion.Euler(Angle_X, Angle_Y, 0);
-
- camTf.rotation = camRotation;
-
- offset.z += scroll;
-
- camTf.position = transform.position + camTf.rotation * offset;
-
- transform.eulerAngles = new Vector3(0, camTf.eulerAngles.y, 0);
- }
-
- //角色操作
- public void InputCtl()
- {
- if (Input.GetMouseButtonDown(0))
- {
- //判断子弹个数
- if (gun.BulletCount > 0)
- {
- //如果正在播放填充子弹的动作不能开枪
- if (ani.GetCurrentAnimatorStateInfo(1).IsName("Reload"))
- {
- return;
- }
-
- gun.BulletCount--;
- Game.uiManager.GetUI
("FightUI").UpdateBulletCount(gun.BulletCount); - //播放开火动画
- ani.Play("Fire", 1, 0);
-
- StopAllCoroutines();
- StartCoroutine(AttackCo());
- }
- }
-
- if (Input.GetKeyDown(KeyCode.R))
- {
- //填充子弹
- AudioSource.PlayClipAtPoint(reloadClip, transform.position); //播放填充子弹的声音
- ani.Play("Reload");
- gun.BulletCount = 10;
- Game.uiManager.GetUI
("FightUI").UpdateBulletCount(gun.BulletCount); - }
- }
-
- //攻击协同程序
- IEnumerator AttackCo()
- {
- //延迟0.1秒才发射子弹
- yield return new WaitForSeconds(0.1f);
- //播放射击音效
- AudioSource.PlayClipAtPoint(shootClip, transform.position);
-
- //射线检测 鼠标中心点发送射线
- Ray ray = Camera.main.ScreenPointToRay(new Vector3(Screen.width * 0.5f, Screen.height * 0.5f,Input.mousePosition.z));
- //射线可以改成在枪口位置为起始点 发送,避免射线射到自身
-
- RaycastHit hit;
- if (Physics.Raycast(ray, out hit, 10000, LayerMask.GetMask("Player")))
- {
- Debug.Log("射到角色");
- hit.transform.GetComponent
().GetHit(); - }
-
- photonView.RPC("AttackRpc", RpcTarget.All); //所有玩家执行 AttackRpc 函数
- }
-
- [PunRPC]
- public void AttackRpc()
- {
- gun.Attack();
- }
-
- //受伤
- public void GetHit()
- {
- if (isDie == true)
- {
- return;
- }
-
- //同步所有角色受伤
- photonView.RPC("GetHitRPC", RpcTarget.All);
- }
-
- [PunRPC]
- public void GetHitRPC()
- {
- CurHp -= 1; //扣一滴血
- if (CurHp <= 0)
- {
- CurHp = 0;
- isDie = true;
- }
-
- if (photonView.IsMine)
- {
- Game.uiManager.GetUI
("FightUI").UpdateHp(CurHp, MaxHp); - Game.uiManager.GetUI
("FightUI").UpdateBlood(); -
- if (CurHp == 0)
- {
- Invoke("gameOver", 3); //3秒后显示失败界面
- }
- }
- }
-
- private void gameOver()
- {
- //显示鼠标
- Cursor.visible = true;
- Cursor.lockState = CursorLockMode.None;
- //显示失败界面
- Game.uiManager.ShowUI
("LossUI").onClickCallBack = OnReset; - }
-
- //复活
- public void OnReset()
- {
- //隐藏鼠标
- Cursor.visible = false;
- Cursor.lockState = CursorLockMode.Locked;
- photonView.RPC("OnResetRPC", RpcTarget.All);
- }
-
- [PunRPC]
- public void OnResetRPC()
- {
- isDie = false;
- CurHp = MaxHp;
- if (photonView.IsMine)
- {
- Game.uiManager.GetUI
("FightUI").UpdateHp(CurHp, MaxHp); - }
- }
-
- //限制角度在-360 到 360之间
- public float ClampAngle(float val, float min, float max)
- {
- if (val > 360)
- {
- val -= 360;
- }
-
- if (val < -360)
- {
- val += 360;
- }
-
- return Mathf.Clamp(val, min, max);
- }
-
- private void OnAnimatorIK(int layerIndex)
- {
- if (ani != null)
- {
- Vector3 angle = ani.GetBoneTransform(HumanBodyBones.Chest).localEulerAngles;
- angle.x = Angle_X;
- ani.SetBoneLocalRotation(HumanBodyBones.Chest, Quaternion.Euler(angle));
- }
- }
-
- public void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
- {
- if (stream.IsWriting)
- {
- //发送数据
- stream.SendNext(H);
- stream.SendNext(V);
- stream.SendNext(Angle_X);
- stream.SendNext(transform.position);
- stream.SendNext(transform.rotation);
- }
- else
- {
- //接收数据
- H = (float)stream.ReceiveNext();
- V = (float)stream.ReceiveNext();
- Angle_X = (float)stream.ReceiveNext();
- currentPos = (Vector3)stream.ReceiveNext();
- currentRotation = (Quaternion)stream.ReceiveNext();
- }
- }
- }
编写枪的脚本Gun,挂载到Assault_Rifle_02上,并按下图把参数填充好

- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
-
- //枪的脚本
- public class Gun : MonoBehaviour
- {
-
- public int BulletCount = 10;
-
- public GameObject bulletPrefab;
- public GameObject casingPreafab;
-
- public Transform bulletTf;
- public Transform casingTf;
-
- void Start()
- {
-
- }
-
- public void Attack()
- {
- GameObject bulletObj = Instantiate(bulletPrefab);
- bulletObj.transform.position = bulletTf.transform.position;
- bulletObj.GetComponent
().AddForce(transform.forward * 500, ForceMode.Impulse); //子弹速度 让中心点跟枪口位置可自行调整摄像机的偏移值 -
- GameObject casingObj = Instantiate(casingPreafab);
- casingObj.transform.position = casingTf.transform.position;
- }
- }



