mirror--tankWar
一、创建离线场景
- 1、创建新项目,导入mirror,创建场景重命名为OfflineScenes
- 2、从Prefabs文件夹中,将预制体LevelArt拖拽到场景中,LevelArt有光源,删除场景中自带的光源
- 4、从models文件夹中,将Tank拖拽到场景中,调试好合适的位置,也可以拖拽其他的模型布置场景
- 5、创建canvas,修改UI Scale Mode选项为:scale with screen size,下面的尺寸根据自己的需求更改,我打包出来的是4:3的界面,创建输入框--输入姓名,3个滑杆--调整颜色,一个按钮,其余的自做调整,
- 6、创建一个空对象,重命名为OfflineManager,创建的脚本OffLineConfig.cs,编写代码,实现功能:tank旋转、拖动滑杆更新坦克颜色、保存输入框输入的姓名、坦克颜色,切换场景
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using UnityEngine.SceneManagement; public class OffLineConfig : MonoBehaviour { // 坦克的Transform组件 public Transform tank; // 坦克旋转速度 public float speedR; // 坦克材质 public Material materialTank; // 滑杆数组 public Slider[] color; // 名字 public Text playerNameText; // 提示信息 public Text markedText; // 坦克颜色 private Color tankColor; // Update is called once per frame void Update() { // 加负号是为了绕y轴逆时针旋转 tank.Rotate(-Vector3.up * Time.deltaTime * speedR); // 修改颜色 tankColor.r = color[0].value /255f; tankColor.g = color[1].value /255f; tankColor.b = color[2].value /255f; materialTank.color = tankColor; } public void clickConfigBtn() { // 如果姓名为空 if(playerNameText.text == "") { // 提示 markedText.color = Color.red; markedText.text = "名字不能为空!!!"; } else { // 记录姓名、颜色 PlayerPrefs.SetString("playerName", playerNameText.text); PlayerPrefs.SetFloat("tankR", tankColor.r); PlayerPrefs.SetFloat("tankG", tankColor.g); PlayerPrefs.SetFloat("tankB", tankColor.b); // 切换场景 SceneManager.LoadScene(1); } } }
- 7、一个离线场景,一个主场景,都放到build中,这样才能切换场景,将脚本OffLineConfig.cs绑定到OfflineManager物体上,将需要拖拽的物体拖拽到
二、创建主场景
- 1、创建新场景,重命名为main,创建一个空对象,重命名为NetworkManager,添加脚本NetworkManagerHUD,会自动再添加两个组件,将可以修改Server Tick Rate,OfflineScenes场景拖拽到Offline Scenes中
-
2、tank初始化
-
将Tank拖拽到场景中,调整与摄像机的位置
- 坦克添加刚体组件,坦克添加碰撞盒子,设置位置为(0,0.95,0),大小为(1.51,1.71,1.62),注意:碰撞盒子不能紧挨地面,容易检测坦克与地面发生碰撞导致坦克无法移动。
-
在坦克里创建一个3D Text,重命名为playerName,调整位置,添加NetworkIdentity、NetworkTransform组件,将坦克做成预制体,将其拖拽到NetworkManager中的Player Prefab中
-
-
3、 创建脚本TankControl.cs,拖拽到Tank上,编写脚本,先完成的功能是能够同步名字、材质
-
using System.Collections; using System.Collections.Generic; using UnityEngine; // 引用mirror using Mirror; // 继承NetworkBehaviour public class TankControl : NetworkBehaviour { // 3DText,用于显示玩家姓名 public TextMesh player3DText; // 坦克材质 public Material materialTank; // 坦克颜色 private Color tankColor; // OnStartLocalPlayer 类似star(),只在本地玩家上被调用 public override void OnStartLocalPlayer() { player3DText.text = PlayerPrefs.GetString("playerName"); tankColor.r = PlayerPrefs.GetFloat("tankR"); tankColor.g = PlayerPrefs.GetFloat("tankG"); tankColor.b = PlayerPrefs.GetFloat("tankB"); } }
- 将该拖拽的拖拽上去,保存场景,打包,发现名字和材质并没有同步,只是在自己的客户端上更改了
- 为了同步,我们使用SynVar:用于同步服务器和所有客户端的变量,变量只能在服务器上更改
- 变量只能在服务其被修改,所以在客户端调用的方法,上面要加上【command],修改代码
-
using System.Collections; using System.Collections.Generic; using UnityEngine; // 引用mirror using Mirror; // 继承NetworkBehaviour public class TankControl : NetworkBehaviour { // 3DText,用于显示玩家姓名 public TextMesh player3DText; // 坦克盖材质 public MeshRenderer RendererTank; // 坦克头材质 public MeshRenderer RendererTankHead; // 坦克左轮 public MeshRenderer RendererTankLeft; // 坦克右轮 public MeshRenderer RendererTankRight; // 存放 private Material playerMaterialClone; // SynVar用于同步服务器和所有客户端的变量,变量只能在服务器上更改 // hook允许你创建一个在客户端的方法,当客户端上接受到更新的信息后,执行这个方法 [SyncVar(hook = nameof(OnNameChanged))] public string playerName; // 名字发生改变 void OnNameChanged(string _old, string _new) { player3DText.text = playerName; } [SyncVar(hook = nameof(OnColorChanged))] public Color playerColor; // 颜色发生变化TODO void OnColorChanged(Color _old, Color _new) { // 为playerMaterialClone赋值,任何都可以,只要是material类型 playerMaterialClone = new Material(RendererTank.material); playerMaterialClone.color = playerColor; RendererTank.material = playerMaterialClone; RendererTankHead.material = playerMaterialClone; RendererTankLeft.material = playerMaterialClone; RendererTankRight.material = playerMaterialClone; } // OnStartLocalPlayer 类似star(),只在本地玩家上被调用 public override void OnStartLocalPlayer() { // 获取玩家名字 string name = PlayerPrefs.GetString("playerName"); // 获取颜色一个颜色 Color tankColor = new Color(PlayerPrefs.GetFloat("tankR"), PlayerPrefs.GetFloat("tankG"), PlayerPrefs.GetFloat("tankB")); // 调用方法,修改变量要在服务器端 cmdSetupPlayer(name, tankColor); } //Command,从客户端调用,但是在服务器上运行 [Command] void cmdSetupPlayer(string _name, Color _color) { //玩家信息发送到服务器,然后服务器更新所有客户端上SyncVar变量 playerName = _name; playerColor = _color; } }
- 保存场景,打包,发现名字同步了,但是所有客户端的材质颜色会改成最后一个客户端的颜色
- 这是因为我一直改的都是TankColour材质的颜色,而所有的客户端坦克上都用的这个材质,只要一个发生变化,都发生变化,所以应该是每一个客户端创建新材质替换TankColour材质,而不是修改TankColour材质。
-
坦克的部分是分开的,所以需要用创建的材质,替换所有部分的材质,修改代码-------成功
-
using System.Collections; using System.Collections.Generic; using UnityEngine; // 引用mirror using Mirror; // 继承NetworkBehaviour public class TankControl : NetworkBehaviour { // 3DText,用于显示玩家姓名 public TextMesh player3DText; // 坦克盖材质 public MeshRenderer RendererTank; // 坦克头材质 public MeshRenderer RendererTankHead; // 坦克左轮 public MeshRenderer RendererTankLeft; // 坦克右轮 public MeshRenderer RendererTankRight; // 存放 private Material playerMaterialClone; // SynVar用于同步服务器和所有客户端的变量,变量只能在服务器上更改 // hook允许你创建一个在客户端的方法,当客户端上接受到更新的信息后,执行这个方法 [SyncVar(hook = nameof(OnNameChanged))] public string playerName; // 名字发生改变 void OnNameChanged(string _old, string _new) { player3DText.text = playerName; } [SyncVar(hook = nameof(OnColorChanged))] public Color playerColor; // 颜色发生变化TODO void OnColorChanged(Color _old, Color _new) { // 为playerMaterialClone赋值,任何都可以,只要是material类型 playerMaterialClone = new Material(RendererTank.material); playerMaterialClone.color = playerColor; RendererTank.material = playerMaterialClone; RendererTankHead.material = playerMaterialClone; RendererTankLeft.material = playerMaterialClone; RendererTankRight.material = playerMaterialClone; } // OnStartLocalPlayer 类似star(),只在本地玩家上被调用 public override void OnStartLocalPlayer() { // 获取玩家名字 string name = PlayerPrefs.GetString("playerName"); // 获取颜色一个颜色 Color tankColor = new Color(PlayerPrefs.GetFloat("tankR"), PlayerPrefs.GetFloat("tankG"), PlayerPrefs.GetFloat("tankB")); // 调用方法,修改变量要在服务器端 cmdSetupPlayer(name, tankColor); } //Command,从客户端调用,但是在服务器上运行 [Command] void cmdSetupPlayer(string _name, Color _color) { //玩家信息发送到服务器,然后服务器更新所有客户端上SyncVar变量 playerName = _name; playerColor = _color; } }
-
- 4、移动+相机跟随
- 看一看下面有没有勾选上
-
时间有限不详细叙述代码,创建相关变量,
- 此时代码TankControl.cs的代码
-
using System.Collections; using System.Collections.Generic; using UnityEngine; // 引用mirror using Mirror; // 继承NetworkBehaviour public class TankControl : NetworkBehaviour { // 同步姓名、材质 // 3DText,用于显示玩家姓名 public TextMesh player3DText; // 坦克盖材质 public MeshRenderer RendererTank; // 坦克头材质 public MeshRenderer RendererTankHead; // 坦克左轮 public MeshRenderer RendererTankLeft; // 坦克右轮 public MeshRenderer RendererTankRight; // 存放 private Material playerMaterialClone; // 相机跟随+移动 private float moveSpeed = 5; // 坦克移动的速度 private float turnSpped = 5; // 坦克转向的速度 private Rigidbody rb; // 刚体组件 private Vector3 offset;//和相机的相对位置 Quaternion camRotation; //记录相机初始角度 // SynVar用于同步服务器和所有客户端的变量,变量只能在服务器上更改 // hook允许你创建一个在客户端的方法,当客户端上接受到更新的信息后,执行这个方法 [SyncVar(hook = nameof(OnNameChanged))] public string playerName; // 名字发生改变 void OnNameChanged(string _old, string _new) { player3DText.text = playerName; } [SyncVar(hook = nameof(OnColorChanged))] public Color playerColor; // 颜色发生变化TODO void OnColorChanged(Color _old, Color _new) { // 为playerMaterialClone赋值,任何都可以,只要是material类型 playerMaterialClone = new Material(RendererTank.material); playerMaterialClone.color = playerColor; RendererTank.material = playerMaterialClone; RendererTankHead.material = playerMaterialClone; RendererTankLeft.material = playerMaterialClone; RendererTankRight.material = playerMaterialClone; } // OnStartLocalPlayer 类似star(),只在本地玩家上被调用 public override void OnStartLocalPlayer() { // 获取玩家名字 string name = PlayerPrefs.GetString("playerName"); // 获取颜色一个颜色 Color tankColor = new Color(PlayerPrefs.GetFloat("tankR"), PlayerPrefs.GetFloat("tankG"), PlayerPrefs.GetFloat("tankB")); // 调用方法,修改变量要在服务器端 cmdSetupPlayer(name, tankColor); rb = GetComponent<Rigidbody>(); offset = Camera.main.transform.position - transform.position; camRotation = Camera.main.transform.rotation; } // 物理动作尽量放到FixedUpdate中 void FixedUpdate() { // 如果不是本地玩家,不执行下面的代码 if (!isLocalPlayer) return; // 相机跟随 Camera.main.transform.position = transform.position + offset; // 控制转向--转向是y轴方向转向,水平变量控制 float h = Input.GetAxis("Horizontal"); // 控制前进 float v = Input.GetAxis("Vertical"); // 前进 rb.velocity = transform.forward * v * moveSpeed; // 转向 rb.angularVelocity = transform.up * h * turnSpped; } //Command,从客户端调用,但是在服务器上运行 [Command] void cmdSetupPlayer(string _name, Color _color) { //玩家信息发送到服务器,然后服务器更新所有客户端上SyncVar变量 playerName = _name; playerColor = _color; } }
- 5、发射子弹
- 在tank预制体中创建一个空对象,重命名FirePos,用来做发射子弹的地点,设置位置和角度
- 从models中添加子弹,为子弹添加碰撞器、刚体组件、将子弹设置成预制体
- 添加变量,编写代码,拖拽该有的变量
- 打包、运行,只能在自身的客户生成子弹,无法在所有客户端同步,应该在所有的客户端生成子弹,这里要用到Spawn
- 修改代码
-
用Spawn()生成的物体要加上Networkidentity组件,所以给子弹加上Networkidentity组件,并且添加到NetworkManage组件中
-
-
打包、运行,只能在自身的客户生成子弹,无法在所有客户端同步,是因为Spawn()需要服务器调用,所以修改代码
-
- 此时代码TankControl.cs的代码
-
using System.Collections; using System.Collections.Generic; using UnityEngine; // 引用mirror using Mirror; // 继承NetworkBehaviour public class TankControl : NetworkBehaviour { // 同步姓名、材质 // 3DText,用于显示玩家姓名 public TextMesh player3DText; // 坦克盖材质 public MeshRenderer RendererTank; // 坦克头材质 public MeshRenderer RendererTankHead; // 坦克左轮 public MeshRenderer RendererTankLeft; // 坦克右轮 public MeshRenderer RendererTankRight; // 存放 private Material playerMaterialClone; // 相机跟随+移动 private float moveSpeed = 5; // 坦克移动的速度 private float turnSpped = 5; // 坦克转向的速度 private Rigidbody rb; // 刚体组件 private Vector3 offset;//和相机的相对位置 Quaternion camRotation; //记录相机初始角度 // 发射子弹 public GameObject shellPrefab; // 子弹预制体 public Transform firePoint; // 发射点的Transform组件 // SynVar用于同步服务器和所有客户端的变量,变量只能在服务器上更改 // hook允许你创建一个在客户端的方法,当客户端上接受到更新的信息后,执行这个方法 [SyncVar(hook = nameof(OnNameChanged))] public string playerName; // 名字发生改变 void OnNameChanged(string _old, string _new) { player3DText.text = playerName; } [SyncVar(hook = nameof(OnColorChanged))] public Color playerColor; // 颜色发生变化TODO void OnColorChanged(Color _old, Color _new) { // 为playerMaterialClone赋值,任何都可以,只要是material类型 playerMaterialClone = new Material(RendererTank.material); playerMaterialClone.color = playerColor; RendererTank.material = playerMaterialClone; RendererTankHead.material = playerMaterialClone; RendererTankLeft.material = playerMaterialClone; RendererTankRight.material = playerMaterialClone; } // OnStartLocalPlayer 类似star(),只在本地玩家上被调用 public override void OnStartLocalPlayer() { // 获取玩家名字 string name = PlayerPrefs.GetString("playerName"); // 获取颜色一个颜色 Color tankColor = new Color(PlayerPrefs.GetFloat("tankR"), PlayerPrefs.GetFloat("tankG"), PlayerPrefs.GetFloat("tankB")); // 调用方法,修改变量要在服务器端 cmdSetupPlayer(name, tankColor); // 与相机的偏移量,相机 的初始角度 rb = GetComponent<Rigidbody>(); offset = Camera.main.transform.position - transform.position; camRotation = Camera.main.transform.rotation; } // 物理动作尽量放到FixedUpdate中 void FixedUpdate() { // 如果不是本地玩家,不执行下面的代码 if (!isLocalPlayer) return; // 相机跟随 Camera.main.transform.position = transform.position + offset; // 控制转向--转向是y轴方向转向,水平变量控制 float h = Input.GetAxis("Horizontal"); // 控制前进 float v = Input.GetAxis("Vertical"); // 前进 rb.velocity = transform.forward * v * moveSpeed; // 转向 rb.angularVelocity = transform.up * h * turnSpped; // 如果按下空格键,射击 if (Input.GetKeyDown(KeyCode.Space)) { // 射击 shoot(); } } //加上[Command]的方法,为客户端向服务器端发消息,让服务器执行此方法 [Command] void shoot() { // 实例化子弹 GameObject go = Instantiate(shellPrefab, firePoint.position, firePoint.rotation); // Spawn:在所有的客户端生成括号里面的物体 NetworkServer.Spawn(go); } //Command,从客户端调用,但是在服务器上运行 [Command] void cmdSetupPlayer(string _name, Color _color) { //玩家信息发送到服务器,然后服务器更新所有客户端上SyncVar变量 playerName = _name; playerColor = _color; } }
- 现在能够同步出现子弹了,但是子弹没有速度,创建脚本ShellControl.cs脚本,拖拽给shell子弹,将爆炸效果拖拽上去
-
using System.Collections; using System.Collections.Generic; using UnityEngine; public class ShellControl : MonoBehaviour { public float shellSpeed = 10; // 子弹速度 private Rigidbody shellRb; // 子弹刚体组件 public GameObject shellExplosionPrefab; // 子弹爆炸效果 // Start is called before the first frame update void Start() { shellRb = GetComponent<Rigidbody>(); // 设置初速度,不能写到update中,速度只设置一次就好 shellRb.velocity = transform.forward * shellSpeed; } // 子弹碰撞到东西是爆炸 private void OnTriggerEnter(Collider other) { // 实例化爆炸效果 GameObject.Instantiate(shellExplosionPrefab, transform.position, transform.rotation); // 删除子弹 Destroy(gameObject); } }
-
由于每个客户端都生成了子弹,所有爆炸效果也都可以生成,所以不需要考虑爆炸同步,子弹能够删除,但是爆炸效果没有删除,所以为爆炸效果创建组件DestoryExplore.cs
-
using System.Collections; using System.Collections.Generic; using UnityEngine; public class DestoryExplore : MonoBehaviour { // Start is called before the first frame update void Start() { // 删除爆炸效果 GameObject.Destroy(gameObject, 1.5f); } }
- 发射子弹功能完毕(爆炸效果还是有点问题)
- 6、坦克伤害
- 创建变量hp表示坦克血量,判断坦克的碰撞体,碰到的是不是子弹,谁碰到,谁减血,用到TargetRpc。
-
打包,运行,会发现只有客户端的 删掉了自己的坦克,因为我们只让受伤的目标客户端删掉了(看天蓝色的坦克)。修改代码
-
-
现在就可以了
-
7、坦克重生
-
坦克消失,身上的脚本就不起作用了,所以需要在场景中创建一个空物体,重命名GameManager,在上面创建脚本GameManager.cs,编写代码
-
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.SceneManagement; using Mirror; public class GameManager : NetworkBehaviour { // 为了在其他脚本中,更好的调用,制作成单例 public static GameManager instance; // 判断玩家是否死亡 public bool isPlayerDead = false; private void Awake() { instance = this; } private void Update() { // 如果按下p键并且isPlayerDead为True,才能重生 if (Input.GetKeyDown(KeyCode.P) && isPlayerDead ) { // 重新设置摄像机的位置 Camera.main.transform.position = new Vector3(0f, 3.5f, -9.5f); // 重新生成玩家 NetworkClient.AddPlayer(); } } }
- 在TankControl脚本中,添加代码
- 坦克重生完成,运行会发现,重生的坦克有些问题(会和最后一个进入客户端的坦克同名同材质,)稍后测试是不是同一台电脑的原因
- 目前为止,此时代码TankControl.cs的代码
-
using System.Collections; using System.Collections.Generic; using UnityEngine; // 引用mirror using Mirror; // 继承NetworkBehaviour public class TankControl : NetworkBehaviour { // 同步姓名、材质 // 3DText,用于显示玩家姓名 public TextMesh player3DText; // 坦克盖材质 public MeshRenderer RendererTank; // 坦克头材质 public MeshRenderer RendererTankHead; // 坦克左轮 public MeshRenderer RendererTankLeft; // 坦克右轮 public MeshRenderer RendererTankRight; // 存放 private Material playerMaterialClone; // 相机跟随+移动 private float moveSpeed = 5; // 坦克移动的速度 private float turnSpped = 5; // 坦克转向的速度 private Rigidbody rb; // 刚体组件 private Vector3 offset;//和相机的相对位置 Quaternion camRotation; //记录相机初始角度 // 发射子弹 public GameObject shellPrefab; // 子弹预制体 public Transform firePoint; // 发射点的Transform组件 // 坦克血量 int hp = 20; // SynVar用于同步服务器和所有客户端的变量,变量只能在服务器上更改 // hook允许你创建一个在客户端的方法,当客户端上接受到更新的信息后,执行这个方法 [SyncVar(hook = nameof(OnNameChanged))] public string playerName; // 名字发生改变 void OnNameChanged(string _old, string _new) { player3DText.text = playerName; } [SyncVar(hook = nameof(OnColorChanged))] public Color playerColor; // 颜色发生变化TODO void OnColorChanged(Color _old, Color _new) { // 为playerMaterialClone赋值,任何都可以,只要是material类型 playerMaterialClone = new Material(RendererTank.material); playerMaterialClone.color = playerColor; RendererTank.material = playerMaterialClone; RendererTankHead.material = playerMaterialClone; RendererTankLeft.material = playerMaterialClone; RendererTankRight.material = playerMaterialClone; } // OnStartLocalPlayer 类似star(),只在本地玩家上被调用 public override void OnStartLocalPlayer() { // 获取玩家名字 string name = PlayerPrefs.GetString("playerName"); // 获取颜色一个颜色 Color tankColor = new Color(PlayerPrefs.GetFloat("tankR"), PlayerPrefs.GetFloat("tankG"), PlayerPrefs.GetFloat("tankB")); // 调用方法,修改变量要在服务器端 cmdSetupPlayer(name, tankColor); // 与相机的偏移量,相机 的初始角度 rb = GetComponent<Rigidbody>(); offset = Camera.main.transform.position - transform.position; camRotation = Camera.main.transform.rotation; } // 物理动作尽量放到FixedUpdate中 void FixedUpdate() { // 如果不是本地玩家,不执行下面的代码 if (!isLocalPlayer) return; // 相机跟随 Camera.main.transform.position = transform.position + offset; // 控制转向--转向是y轴方向转向,水平变量控制 float h = Input.GetAxis("Horizontal"); // 控制前进 float v = Input.GetAxis("Vertical"); // 前进 rb.velocity = transform.forward * v * moveSpeed; // 转向 rb.angularVelocity = transform.up * h * turnSpped; // 如果按下空格键,射击 if (Input.GetKeyDown(KeyCode.Space)) { // 射击 shoot(); } } //加上[Command]的方法,为客户端向服务器端发消息,让服务器执行此方法 [Command] void shoot() { // 实例化子弹 GameObject go = Instantiate(shellPrefab, firePoint.position, firePoint.rotation); // Spawn:在所有的客户端生成括号里面的物体 NetworkServer.Spawn(go); } //Command,从客户端调用,但是在服务器上运行 [Command] void cmdSetupPlayer(string _name, Color _color) { //玩家信息发送到服务器,然后服务器更新所有客户端上SyncVar变量 playerName = _name; playerColor = _color; } // 进入的是子弹 private void OnCollisionEnter(Collision collision) { // 这个判断需要在服务器端调用,不能是客户端,如果发现卡顿,就会出现问题 if(collision.gameObject.tag == "Shell" && isServer) { // 谁碰到谁减血 NetworkIdentity networkIdentity = GetComponent<NetworkIdentity>(); TakeDamage(networkIdentity.connectionToClient); } } [TargetRpc] void TakeDamage(NetworkConnection connection) { if (hp <= 0) return; // 如果血量小于0,直接返回 hp -= 10; // 伤害 //第一次 血量变成0 if (hp <= 0) { // 将目标客户端上的isPlayerDead,设置为True GameManager.instance.isPlayerDead = true; // 删除这个坦克,在服务器上删除,其他的客户端都会删 DestroyTank(); } } [Command] void DestroyTank() { NetworkServer.Destroy(this.gameObject); } }
-
- 8、答题重生
- 创建一个脚本QuestionData.cs,里面写一个题类
-
// 题类 public class QuestionData { // 问题 public string question; // 答案 public string answer; }
- 创建一个文件夹Resources,在创建一个文本,里面题和答案用逗号分割开
- 修改GameManager.cs中的代码
- 创建UI,自己做选择,创建一个Text用来显示题目,一个输入框输入答案,一个按钮用来确定,我还创建了一个Text用来提示(按p键重生)
-
编写代码
-
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.SceneManagement; using Mirror; using UnityEngine.UI; public class GameManager : NetworkBehaviour { // 为了在其他脚本中,更好的调用,制作成单例 public static GameManager instance; // 判断玩家是否死亡 public bool isPlayerDead = false; // 创建列表,存储每一道题 List<QuestionData> quests = new List<QuestionData>(); // 显示题目的面板 public GameObject panel; // 答案 public Text answer; // 问题 public Text question; // 题目序号 int n = 0; private void Awake() { instance = this; // 加载问题文件 TextAsset questiondata = Resources.Load<TextAsset>("question1"); string a = questiondata.text; foreach (var item in a.Split('\n')) { QuestionData q = new QuestionData(); string[] b = item.Split(','); q.question = b[0]; // 大坑:1、编码格式不同 2、没有清除前后的空格 q.answer = b[1].Trim(); quests.Add(q); } // 设置初始问题 question.text = quests[n].question; } private void Update() { // 如果按下p键并且isPlayerDead为True,才能重生 if (Input.GetKeyDown(KeyCode.P) && isPlayerDead ) { // 显示问题面板 panel.SetActive(true); } } public void Check() { // 如果回答对 if (answer.text == quests[n].answer) { // 随机一个序号 n = Random.Range(0, quests.Count - 1); question.text = quests[n].question; // 隐藏问题面板 panel.SetActive(false); // 设置isPlayerDead为false isPlayerDead = false; // 重新设置摄像机的位置 Camera.main.transform.position = new Vector3(0f, 3.5f, -9.5f); // 重新生成玩家 NetworkClient.AddPlayer(); } } }
- 拖拽需要的变量
-
结束
-
之后是微调,比如添加点错误提示,加点音效,扩展题库。