当前位置:   article > 正文

Unity3D教学 开发简单版第一人称射击游戏 可以多人联机(附源码)_unity 多人联机 源码

unity 多人联机 源码

简介:

这一篇文章主要是和大家分享如何制作一个属于自己的“第一人称射击游戏”,而且是要可以多人联机的。这个游戏属于比简单的,大神可以直接无视,如果有做错的地方请大家多多指点,我也是刚学如何做游戏。代码是用C#编写,主要实现的功能有三个:第一人称移动控制、角色控制(如射击)、TCP服务端和客户端。我将项目命名为FPS。


游戏运作流程:

服务端

1. 创建服务端

2. 从各个客户端接收数据,然后广播给所有客户


客户端

1. 连接至服务端

2. 从服务端接收到其他客户的数据(比如当前位置,是否有开枪等等),然后更新到游戏上

3. 发送自己当前在游戏的状态给服务端


联机效果图:

服务端



客户端



完整源代码与已编译好的游戏程序:

下载


C#脚本简介:

1. FirstPersonControl.cs

这个脚本是用来做第一人称控制的

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. public class FirstPersonControl : MonoBehaviour {
  5. // Use this for initialization
  6. void Start () {
  7. // 获取摄像头对象
  8. mCamera = transform.Find("Main Camera");
  9. // 获取右手对象
  10. mRightHand = transform.Find("RightHand");
  11. // 获取脚步声播放组建
  12. mAudio = transform.GetComponent<AudioSource>();
  13. }
  14. // Update is called once per frame
  15. void Update() {
  16. if (UpdateMovement()) {
  17. PlayStepSound();
  18. } else {
  19. StopPlayStepSound();
  20. }
  21. UpdateLookAt();
  22. }
  23. // 第一人称控制器的常量与变量
  24. private Transform mCamera;
  25. private Transform mRightHand;
  26. private AudioSource mAudio;
  27. public float mMoveSpeed = 8; // 物体移动速度
  28. public float mMouseSensitivity = 5; // 鼠标旋转的敏感度
  29. public float mMinimumX = 325; // 向下望的最大角度
  30. public float mMaximumX = 45; // 向上望的最大角度
  31. public float mMinimumY = -360; // 向左望的最大角度
  32. public float mMaximumY = 360; // 向右望的最大角度
  33. private Vector3 _curRotation = new Vector3(0,0,0); // 当前旋转角度
  34. // 更新移动位置
  35. private bool UpdateMovement() {
  36. float distance = mMoveSpeed * Time.deltaTime; // 移动距离
  37. // 前
  38. if (Input.GetKey(KeyCode.W)) {
  39. float x = Mathf.Sin(transform.eulerAngles.y * Mathf.Deg2Rad) * distance;
  40. float z = Mathf.Cos(transform.eulerAngles.y * Mathf.Deg2Rad) * distance;
  41. transform.Translate(new Vector3(x,0,z), Space.World);
  42. return true;
  43. }
  44. // 后
  45. if (Input.GetKey(KeyCode.S)) {
  46. float x = Mathf.Sin(transform.eulerAngles.y * Mathf.Deg2Rad) * distance;
  47. float z = Mathf.Cos(transform.eulerAngles.y * Mathf.Deg2Rad) * distance;
  48. transform.Translate(new Vector3(-x, 0, -z), Space.World);
  49. return true;
  50. }
  51. // 左
  52. if (Input.GetKey(KeyCode.A)) {
  53. transform.Translate(new Vector3(-distance, 0, 0));
  54. return true;
  55. }
  56. // 右
  57. if (Input.GetKey(KeyCode.D)) {
  58. transform.Translate(new Vector3(distance, 0, 0));
  59. return true;
  60. }
  61. return false;
  62. }
  63. // 更新摄像头指向位置
  64. private void UpdateLookAt() {
  65. // 左右旋转
  66. _curRotation.y = _curRotation.y + Input.GetAxis("Mouse X") * mMouseSensitivity;
  67. _curRotation.y = Mathf.Clamp(_curRotation.y, mMinimumY, mMaximumY);
  68. // 设置身体
  69. Vector3 rotation = transform.eulerAngles;
  70. rotation.y = _curRotation.y;
  71. transform.eulerAngles = rotation;
  72. // 上下旋转
  73. _curRotation.x = _curRotation.x - Input.GetAxis("Mouse Y") * mMouseSensitivity;
  74. _curRotation.x = Mathf.Clamp(_curRotation.x, mMinimumX, mMaximumX);
  75. // 设置摄像头
  76. mCamera.localEulerAngles = new Vector3(_curRotation.x, 0, 0);
  77. // 设置右手
  78. rotation = mRightHand.eulerAngles;
  79. rotation.x = _curRotation.x;
  80. mRightHand.eulerAngles = rotation;
  81. }
  82. // 播放脚步声
  83. private void PlayStepSound() {
  84. if (!mAudio.isPlaying) {
  85. mAudio.Play();
  86. }
  87. }
  88. // 停止播放声音
  89. private void StopPlayStepSound() {
  90. if (mAudio.isPlaying) {
  91. mAudio.Stop();
  92. }
  93. }
  94. }


2. Character.cs

角色控制脚本,主要是用来控制开枪等角色动作

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. public class Character : MonoBehaviour {
  5. // Use this for initialization
  6. void Start() {
  7. // 获取摄像头对象
  8. mCamera = transform.Find("Main Camera");
  9. // 获取右手对象
  10. mRightHand = transform.Find("RightHand");
  11. // 获取枪声播放组件
  12. mGunAudio = transform.Find("RightHand/Pistol").GetComponent<AudioSource>();
  13. // 获取火花效果
  14. mFireEffect = transform.Find("RightHand/Pistol/FireEffect").GetComponent<ParticleSystem>();
  15. // 获取网络组件
  16. mNetwork = transform.GetComponent<Network>();
  17. }
  18. // Update is called once per frame
  19. void Update() {
  20. UpdateFire();
  21. // 发送当前状态到服务端,然后服务端就会转发给其他客户
  22. mNetwork.SendStatus(transform.position, transform.eulerAngles,
  23. mCamera.eulerAngles, mRightHand.eulerAngles, _isShooted, _hp);
  24. // 处理服务器发过来的数据包,数据包里装着其他客户的信息
  25. ProcessPackage();
  26. }
  27. private Transform mCamera;
  28. private Transform mRightHand;
  29. private AudioSource mGunAudio;
  30. public GameObject mPiece; // 开枪后撞击产生的碎片
  31. private ParticleSystem mFireEffect; // 开枪后的火花
  32. private bool _isShooted; // 判断是否开了枪
  33. private Network mNetwork; // 网络组件
  34. public GameObject mEnemyCharacter; // 其他客户的实例
  35. private Hashtable _htEnemies = new Hashtable(); // 其他客户的控制脚本
  36. // 开枪
  37. private void UpdateFire() {
  38. if (Input.GetButtonUp("Fire1")) {
  39. // 射击音效与画面
  40. PlayShotSound();
  41. // 播放火花效果
  42. PlayFireEffect();
  43. // 判断射击位置
  44. RaycastHit hit;
  45. if (Physics.Raycast(mCamera.position, mCamera.forward, out hit, 100)) {
  46. // 被枪击中的地方会有碎片弹出
  47. DrawPieces(hit);
  48. }
  49. // 设置开枪判断
  50. _isShooted = true;
  51. } else {
  52. // 设置开枪判断
  53. _isShooted = false;
  54. }
  55. }
  56. // 播放枪声
  57. private void PlayShotSound() {
  58. mGunAudio.PlayOneShot(mGunAudio.clip);
  59. }
  60. // 画碎片
  61. private void DrawPieces(RaycastHit hit) {
  62. for (int i = 0; i < 5; ++i) {
  63. GameObject p = Transform.Instantiate(mPiece);
  64. // 碎片撞击到物体后的反弹位置
  65. Vector3 fwd = mCamera.forward * -1;
  66. p.transform.position = hit.point;
  67. p.GetComponent<Rigidbody>().AddForce(fwd * 100);
  68. // 0.3秒后删除
  69. Destroy(p, 0.3f);
  70. }
  71. }
  72. // 播放火花效果
  73. private void PlayFireEffect() {
  74. mFireEffect.Play();
  75. }
  76. // 人物变量
  77. private int _hp = 100;
  78. // 受到伤害
  79. public void GetHurt() {
  80. _hp -= 10;
  81. if (_hp <= 0) {
  82. // 复活
  83. Revive();
  84. }
  85. }
  86. // 复活
  87. private void Revive() {
  88. _hp = 100;
  89. transform.position = new Vector3(0,1,0);
  90. }
  91. // 处理数据包
  92. private void ProcessPackage() {
  93. Network.Package p;
  94. // 获取数据包直到完毕
  95. while (mNetwork.NextPackage(out p)) {
  96. // 确定不是本机,避免重复
  97. if (mNetwork._id == p.id) {
  98. return;
  99. }
  100. // 获取该客户相对应的人物模组
  101. if (!_htEnemies.Contains(p.id)) {
  102. AddEnemyCharacter(p.id);
  103. }
  104. // 更新客户的人物模型状态
  105. EnemyCharacter ec = (EnemyCharacter)_htEnemies[p.id];
  106. // 血量
  107. ec.SetHP(p.hp);
  108. // 移动动作
  109. ec.Move(p.pos.V3, p.rot.V3, p.cameraRot.V3, p.rightHandRot.V3);
  110. // 开枪
  111. if (p.isShooted) {
  112. ec.Fire();
  113. }
  114. }
  115. }
  116. // 增加客户的人物模组
  117. private EnemyCharacter AddEnemyCharacter(int id) {
  118. GameObject p = GameObject.Instantiate(mEnemyCharacter);
  119. EnemyCharacter ec = p.GetComponent<EnemyCharacter>();
  120. // 修改ID
  121. ec.SetID(id);
  122. // 加入到哈希表
  123. _htEnemies.Add(id, ec);
  124. return ec;
  125. }
  126. // 删除客户的人物模组
  127. private void RemoveEnemyCharacter(int id) {
  128. EnemyCharacter ec = (EnemyCharacter)_htEnemies[id];
  129. ec.Destroy();
  130. _htEnemies.Remove(id);
  131. }
  132. // 删除所有客户的人物模组
  133. public void RemoveAllEnemyCharacter() {
  134. foreach (int id in _htEnemies.Keys) {
  135. EnemyCharacter ec = (EnemyCharacter)_htEnemies[id];
  136. ec.Destroy();
  137. }
  138. _htEnemies.Clear();
  139. }
  140. }


3.Network.cs

网络组件,用来创建服务端或客户端。服务端负责广播从各个客户端接收的数据给所有客户。客户端则是接收从服务端接收其他客户的数据。

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using System;
  5. using System.IO;
  6. using System.Runtime.Serialization.Formatters.Binary;
  7. using System.Net;
  8. using System.Net.Sockets;
  9. public class Network : MonoBehaviour {
  10. // Use this for initialization
  11. void Start () {
  12. // 获取人物组件
  13. mCharacter = transform.GetComponent<Character>();
  14. // 数据包大小
  15. _packageSize = PackageSize();
  16. }
  17. // Update is called once per frame
  18. void Update () {
  19. }
  20. // 网络设置UI
  21. void OnGUI() {
  22. GUI.Box (new Rect(0,0,800,60),"网络设置");
  23. if (_connected) {
  24. GUI.Label (new Rect(10,25,100,25), _isServer ? "已建立服务端":"已连接服务端");
  25. } else if (!_connected) {
  26. GUI.Label (new Rect(10,25,100,25), "未连接");
  27. }
  28. GUI.Label (new Rect(130,25,20,25), "IP:");
  29. GUI.Label (new Rect(270,25,40,25), "端口:");
  30. GUI.Label (new Rect(380,25,40,25), "ID:");
  31. if (!_connected && !_isServer) {
  32. _ip = GUI.TextField (new Rect (150, 25, 100, 25), _ip, 100);
  33. _port = System.Convert.ToInt32 (GUI.TextField (new Rect (310, 25, 50, 25), _port.ToString (), 100));
  34. _id = System.Convert.ToInt32 (GUI.TextField (new Rect (420, 25, 100, 25), _id.ToString(), 100));
  35. } else {
  36. GUI.TextField (new Rect (150, 25, 100, 25), _ip, 100);
  37. GUI.TextField (new Rect (310, 25, 50, 25), _port.ToString (), 100);
  38. GUI.TextField (new Rect (420, 25, 100, 25), _id.ToString(), 100);
  39. }
  40. if (!_connected && !_isServer) {
  41. if (GUI.Button(new Rect(540,25,100,25), "开启服务端")) {
  42. StartServer();
  43. ConnectServer();
  44. }
  45. if (GUI.Button(new Rect(660,25,100,25), "连接至服务端")) {
  46. ConnectServer();
  47. }
  48. } else {
  49. if (_isServer) {
  50. if (GUI.Button(new Rect(540,25,100,25), "关闭服务端")) {
  51. StopServer();
  52. }
  53. } else if (_connected) {
  54. if (GUI.Button(new Rect(540,25,100,25), "取消连接")) {
  55. DisconnectServer();
  56. }
  57. }
  58. }
  59. }
  60. // 服务端和客户端的共有变量
  61. private Character mCharacter; // 人物组件
  62. private bool _connected=false; // 判断是否已经建立连接或开启服务端
  63. private bool _isServer=false; // 判断本程序建立的是服务端还是客户端
  64. private string _ip = "127.0.0.1"; // 主机IP
  65. private int _port = 18000; // 端口
  66. public int _id = 1 ; // 人物id
  67. List<Package> _packages=new List<Package>(); // 数据包
  68. private int _packageSize; // 数据包大小
  69. // 数据包
  70. [Serializable]
  71. public struct Package {
  72. public int id;
  73. public Vector3Serializer pos; // 人物位置
  74. public Vector3Serializer rot; // 人物旋转角度
  75. public Vector3Serializer cameraRot; // 摄像头旋转角度
  76. public Vector3Serializer rightHandRot; // 右手旋转角度
  77. public bool isShooted; // 判断是否有开枪
  78. public int hp; // 血量
  79. }
  80. // 获取包大小
  81. private int PackageSize() {
  82. Package p = new Package();
  83. byte[] b;
  84. Serialize(p, out b);
  85. return b.Length;
  86. }
  87. // 可序列化的Vector3
  88. [Serializable]
  89. public struct Vector3Serializer {
  90. public float x;
  91. public float y;
  92. public float z;
  93. public void Fill(Vector3 v3) {
  94. x = v3.x;
  95. y = v3.y;
  96. z = v3.z;
  97. }
  98. public Vector3 V3
  99. { get { return new Vector3(x, y, z); } }
  100. }
  101. // 序列化数据包
  102. public bool Serialize(object obj, out byte[] result) {
  103. bool ret = false;
  104. result = null;
  105. try {
  106. MemoryStream ms = new MemoryStream();
  107. BinaryFormatter bf = new BinaryFormatter();
  108. bf.Serialize(ms, obj);
  109. result = ms.ToArray();
  110. ret = true;
  111. } catch (Exception e) {
  112. ret = false;
  113. Debug.Log(e.Message);
  114. }
  115. return ret;
  116. }
  117. // 反序列化数据包
  118. public bool Deserialize(byte[] data,out object result) {
  119. bool ret = false;
  120. result = new object();
  121. try {
  122. MemoryStream ms = new MemoryStream(data);
  123. BinaryFormatter bf = new BinaryFormatter();
  124. result = bf.Deserialize(ms);
  125. ret = true;
  126. } catch (Exception e) {
  127. ret = false;
  128. Debug.Log(e.Message);
  129. }
  130. return ret;
  131. }
  132. // 服务端变量
  133. TcpListener _listener;
  134. // 储存已连接客户的结构体
  135. private struct Client {
  136. public TcpClient client;
  137. public byte[] buffer; // 接收缓冲区
  138. public List<byte> pendingData; // 还未处理的数据
  139. }
  140. // 客户列表
  141. List<Client> _clients = new List<Client>();
  142. // 开启服务端
  143. private void StartServer() {
  144. try {
  145. _listener = new TcpListener(IPAddress.Any, _port);
  146. _listener.Start();
  147. _listener.BeginAcceptSocket(HandleAccepted, _listener);
  148. _isServer = true;
  149. } catch (Exception e) {
  150. Debug.Log(e.Message);
  151. }
  152. }
  153. // 停止停止服务端
  154. private void StopServer() {
  155. try {
  156. _listener.Stop();
  157. // 清空客户列表
  158. lock (_clients) {
  159. foreach (Client c in _clients) {
  160. RemoveClient(c);
  161. }
  162. _clients.Clear();
  163. }
  164. // 清空数据包
  165. lock (_packages) {
  166. _packages.Clear();
  167. }
  168. _isServer = false;
  169. } catch (Exception e) {
  170. Debug.Log(e.Message);
  171. }
  172. }
  173. // 处理客户端连接的回调函数
  174. private void HandleAccepted(IAsyncResult iar) {
  175. if (_isServer) {
  176. TcpClient tcpClient = _listener.EndAcceptTcpClient(iar);
  177. Client client = new Client();
  178. client.client = tcpClient;
  179. client.buffer = new byte[tcpClient.ReceiveBufferSize];
  180. client.pendingData = new List<byte>();
  181. // 把客户加入到客户列表
  182. lock (_clients) {
  183. AddClient(client);
  184. }
  185. // 开始异步接收从客户端收到的数据
  186. tcpClient.GetStream().BeginRead(
  187. client.buffer,
  188. 0,
  189. client.buffer.Length,
  190. HandleClientDataReceived,
  191. client);
  192. // 开始异步接收连接
  193. _listener.BeginAcceptSocket(HandleAccepted, _listener);
  194. }
  195. }
  196. // 从客户端接收数据的回调函数
  197. private void HandleClientDataReceived(IAsyncResult iar) {
  198. try {
  199. if (_isServer) {
  200. Client client = (Client)iar.AsyncState;
  201. NetworkStream ns = client.client.GetStream();
  202. int bytesRead = ns.EndRead(iar);
  203. // 连接中断
  204. if (bytesRead == 0) {
  205. lock (_clients) {
  206. _clients.Remove(client);
  207. }
  208. return;
  209. }
  210. // 保存数据
  211. for (int i=0; i<bytesRead; ++i) {
  212. client.pendingData.Add(client.buffer[i]);
  213. }
  214. // 把数据解析成包
  215. while (client.pendingData.Count >= _packageSize) {
  216. byte[] bp = client.pendingData.GetRange(0, _packageSize).ToArray();
  217. client.pendingData.RemoveRange(0, _packageSize);
  218. // 把数据包分发给所有客户
  219. lock(_clients) {
  220. foreach (Client c in _clients) {
  221. c.client.GetStream().Write(bp, 0, _packageSize);
  222. c.client.GetStream().Flush();
  223. }
  224. }
  225. }
  226. // 开始异步接收从客户端收到的数据
  227. client.client.GetStream().BeginRead(
  228. client.buffer,
  229. 0,
  230. client.buffer.Length,
  231. HandleClientDataReceived,
  232. client);
  233. }
  234. } catch (Exception e) {
  235. Debug.Log(e.Message);
  236. }
  237. }
  238. // 加入客户
  239. private void AddClient(Client c) {
  240. _clients.Add(c);
  241. }
  242. // 删除客户
  243. private void RemoveClient(Client c) {
  244. c.client.Client.Disconnect(false);
  245. }
  246. // 客户端变量
  247. Client _server;
  248. // 连接至服务端
  249. private void ConnectServer() {
  250. try {
  251. TcpClient tcpServer = new TcpClient();
  252. tcpServer.Connect(_ip, _port);
  253. _server = new Client();
  254. _server.client = tcpServer;
  255. _server.buffer = new byte[tcpServer.ReceiveBufferSize];
  256. _server.pendingData = new List<byte>();
  257. // 异步接收服务端数据
  258. tcpServer.GetStream().BeginRead(
  259. _server.buffer,
  260. 0,
  261. tcpServer.ReceiveBufferSize,
  262. HandleServerDataReceived,
  263. _server);
  264. _connected = true;
  265. } catch (Exception e) {
  266. Debug.Log(e.Message);
  267. }
  268. }
  269. // 从服务端断开
  270. private void DisconnectServer() {
  271. try {
  272. lock (_server.client) {
  273. _server.client.Client.Close();
  274. }
  275. // 清空数据包
  276. lock (_packages) {
  277. _packages.Clear();
  278. }
  279. // 删除所有客户人物模型
  280. mCharacter.RemoveAllEnemyCharacter();
  281. _connected = false;
  282. } catch (Exception e) {
  283. Debug.Log(e.Message);
  284. }
  285. }
  286. // 从服务端读取数据的回调函数
  287. private void HandleServerDataReceived(IAsyncResult iar) {
  288. if (_connected) {
  289. Client server = (Client)iar.AsyncState;
  290. NetworkStream ns = server.client.GetStream();
  291. int bytesRead = ns.EndRead(iar);
  292. // 连接中断
  293. if (bytesRead == 0) {
  294. DisconnectServer();
  295. return;
  296. }
  297. // 保存数据
  298. for (int i=0; i<bytesRead; ++i) {
  299. server.pendingData.Add(server.buffer[i]);
  300. }
  301. // 把数据解析成包
  302. while (server.pendingData.Count >= _packageSize) {
  303. byte[] bp = server.pendingData.GetRange(0, _packageSize).ToArray();
  304. server.pendingData.RemoveRange(0,_packageSize);
  305. // 把数据转换成包然后再储存包列表
  306. object obj;
  307. Deserialize(bp, out obj);
  308. lock (_packages) {
  309. _packages.Add((Package)obj);
  310. }
  311. }
  312. // 异步接收服务端数据
  313. server.client.GetStream().BeginRead(
  314. server.buffer,
  315. 0,
  316. server.client.ReceiveBufferSize,
  317. HandleServerDataReceived,
  318. server);
  319. }
  320. }
  321. // 发送自己的当前的状态包给服务端
  322. public void SendStatus(Vector3 pos, Vector3 rot,Vector3 cameraRot,
  323. Vector3 rightHandRot, bool isShooted, int hp) {
  324. try {
  325. if (_connected) {
  326. Package p = new Package();
  327. p.id = _id;
  328. p.pos.Fill(pos);
  329. p.rot.Fill(rot);
  330. p.cameraRot.Fill(cameraRot);
  331. p.rightHandRot.Fill(rightHandRot);
  332. p.isShooted = isShooted;
  333. p.hp = hp;
  334. // 发送包到服务端
  335. byte[] bp;
  336. Serialize(p, out bp);
  337. lock (_server.client) {
  338. _server.client.GetStream().Write(bp, 0, _packageSize);
  339. _server.client.GetStream().Flush();
  340. }
  341. }
  342. } catch (Exception e) {
  343. Debug.Log(e.Message);
  344. // 断开服务端
  345. DisconnectServer();
  346. }
  347. }
  348. // 获取包
  349. public bool NextPackage(out Package p) {
  350. lock (_packages) {
  351. if (_packages.Count == 0) {
  352. p = new Package();
  353. return false;
  354. }
  355. p = _packages[0];
  356. _packages.RemoveAt(0);
  357. }
  358. return true;
  359. }
  360. }

4.EnemyCharacter.cs

这个脚本负责控制其他客户的行为,比如从服务端接收到其他客户移动或开枪的数据,就要用这个脚本来更新其他客户的当前行为

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. public class EnemyCharacter : MonoBehaviour {
  5. // Use this for initialization
  6. void Start () {
  7. // 获取本机玩家的对象
  8. mCharacter = GameObject.Find("Character").transform;
  9. mCharacterComponent = mCharacter.GetComponent<Character>();
  10. // 获取摄像头对象
  11. mCamera = transform.Find("Camera");
  12. // 获取右手对象
  13. mRightHand = transform.Find("RightHand");
  14. // 获取枪声播放组件
  15. mGunAudio = transform.Find("RightHand/Pistol").GetComponent<AudioSource>();
  16. // 获取火花效果
  17. mFireEffect = transform.Find("RightHand/Pistol/FireEffect").GetComponent<ParticleSystem>();
  18. // 获取脚步声播放组件
  19. mAudio = transform.GetComponent<AudioSource>();
  20. // 显示血量和ID的组件
  21. txID = transform.Find ("ID");
  22. txIDText = transform.Find ("ID").GetComponent<TextMesh> ();
  23. txHP = transform.Find ("HP");
  24. txHPText = transform.Find ("HP").GetComponent<TextMesh> ();
  25. }
  26. // Update is called once per frame
  27. void Update () {
  28. // 摧毁对象
  29. if (isDestroy) {
  30. Destroy(gameObject);
  31. }
  32. // 更新对象属性
  33. UpdataProperties();
  34. }
  35. private Transform mCharacter;
  36. private Character mCharacterComponent;
  37. private Transform mCamera;
  38. private Transform mRightHand;
  39. private AudioSource mGunAudio;
  40. private AudioSource mAudio;
  41. private ParticleSystem mFireEffect; // 开枪后的火花
  42. public GameObject mPiece; // 开枪后撞击产生的碎片
  43. private bool isDestroy = false;
  44. // 销毁角色
  45. public void Destroy() {
  46. isDestroy = true;
  47. }
  48. // 角色移动动作
  49. public void Move(Vector3 pos, Vector3 rot, Vector3 cameraRot, Vector3 rightHandRot) {
  50. if (pos != transform.position) {
  51. transform.position = pos;
  52. PlayStepSound();
  53. } else {
  54. StopPlayStepSound();
  55. }
  56. mCamera.eulerAngles = cameraRot;
  57. transform.eulerAngles = rot;
  58. mRightHand.eulerAngles = rightHandRot;
  59. }
  60. // 播放脚步声
  61. private void PlayStepSound() {
  62. if (!mAudio.isPlaying) {
  63. mAudio.Play();
  64. }
  65. }
  66. // 停止播放声音
  67. private void StopPlayStepSound() {
  68. if (mAudio.isPlaying) {
  69. mAudio.Stop();
  70. }
  71. }
  72. // 开枪
  73. public void Fire() {
  74. // 射击音效与画面
  75. PlayShotSound();
  76. // 播放火花效果
  77. PlayFireEffect();
  78. // 判断射击位置
  79. RaycastHit hit;
  80. if (Physics.Raycast(mCamera.position, mCamera.forward, out hit, 100)) {
  81. // 被枪击中的地方会有碎片弹出
  82. DrawPieces(hit);
  83. // 判断本机玩家是否中枪如果是就减
  84. print(hit.collider.name);
  85. if (hit.collider.name == mCharacter.name) {
  86. mCharacterComponent.GetHurt();
  87. }
  88. }
  89. }
  90. // 播放枪声
  91. private void PlayShotSound() {
  92. mGunAudio.PlayOneShot(mGunAudio.clip);
  93. }
  94. // 画碎片
  95. private void DrawPieces(RaycastHit hit) {
  96. for (int i = 0; i < 5; ++i) {
  97. GameObject p = Transform.Instantiate(mPiece);
  98. // 碎片撞击到物体后的反弹位置
  99. Vector3 fwd = mCamera.forward * -1;
  100. p.transform.position = hit.point;
  101. p.GetComponent<Rigidbody>().AddForce(fwd * 100);
  102. // 0.3秒后删除
  103. Destroy(p, 0.3f);
  104. }
  105. }
  106. // 播放火花效果
  107. private void PlayFireEffect() {
  108. mFireEffect.Play();
  109. }
  110. // 人物变量
  111. private int _id = 1;
  112. private int _hp = 100;
  113. private Transform txID;
  114. private TextMesh txIDText;
  115. private Transform txHP;
  116. private TextMesh txHPText;
  117. // 角色id
  118. public void SetID(int id) {
  119. _id = id;
  120. }
  121. // 角色血量
  122. public void SetHP(int hp) {
  123. _hp = hp;
  124. }
  125. // 更新角色变量/属性
  126. private void UpdataProperties() {
  127. // 显示血量和ID
  128. txIDText.text = "ID:"+_id.ToString();
  129. txHPText.text = "HP:"+_hp.ToString();
  130. // 血量和ID的方向,面向着本机玩家
  131. txID.rotation = mCharacter.rotation;
  132. txHP.rotation = mCharacter.rotation;
  133. }
  134. }


声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/104073
推荐阅读
相关标签
  

闽ICP备14008679号