Unity学习记录——与游戏世界交互

Unity学习记录——与游戏世界交互 前言
? 本文是中山大学软件工程学院2020级3d游戏编程与设计的作业5
编程题:简单打飞碟 1. 题目要求2. 基本介绍
? 本次简单打飞碟小游戏使用了工厂模式管理不同飞碟的生产与回收 。
? **工厂模式( )**是 游戏开发中最常用的设计模式之一 。该模式将实例化对象的代码提取出来,放到一个类中统一管理和维护 , 达到和主项目的依赖关系的解耦 。从而提高项目的扩展和维护性 。也因此 , 在工厂模式中,我们在创建对象时不会对用户暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象 。
3.代码解读
? 本次代码仍旧使用了MVC模式,同时进行了动作分离 。
? 由于动作分离中动作基类等代码基本一致,本次编程题中,一部分代码使用了上次作业(牧师与魔鬼动作分离版本)中的代码,复用的代码分别为:(),(),()三个类 。
? 同时 , ()与()两个类的设计在这三次游戏编程中也是基本一致 , 此处不进行介绍 。
? 以下为代码解读:
? 飞碟的动作类 。控制飞碟的飞行 。实现的逻辑为根据飞碟的位置决定飞碟的运动,当飞碟位置在一定范围内时进行我们所设计的模拟位移,超出一定范围之后就停止位移,等待工厂的回收 。
public class FlyAction : SSAction{public float gravity = -1;//向下的加速度private Vector3 start_vector;//初速度向量private Vector3 gravity_vector = Vector3.zero;//加速度的向量,初始时为0private Vector3 current_angle = Vector3.zero;//当前时间的欧拉角private float time;//已经过去的时间private FlyAction() { }public static FlyAction GetSSAction(int lor, float angle, float power){//初始化物体将要运动的初速度向量FlyAction action = CreateInstance();if (lor == -1){action.start_vector = Quaternion.Euler(new Vector3(0, 0, -angle)) * Vector3.left * power;}else{action.start_vector = Quaternion.Euler(new Vector3(0, 0, angle)) * Vector3.right * power;}return action;}public override void Update(){//计算物体的向下的速度,v=attime += Time.fixedDeltaTime;gravity_vector.y = gravity * time;//位移模拟transform.position += (start_vector + gravity_vector) * Time.fixedDeltaTime;current_angle.z = Mathf.Atan((start_vector.y + gravity_vector.y) / start_vector.x) * Mathf.Rad2Deg;transform.eulerAngles = current_angle;//如果物体y坐标小于-10,动作就做完了if (this.transform.position.y < -10){this.deleted = true;this.callback.SSActionEvent(this);}}public override void Start() { }}
? 飞碟的动作管理类 。通过调用控制飞碟的运动 。
public class FlyActionManager : SSActionManager{public FlyAction fly;public FirstController scene_controller;protected void Start(){scene_controller = (FirstController)SSDirector.GetInstance().CurrentSceneController;scene_controller.action_manager = this;}public void DiskFly(GameObject disk, float angle, float power){int loc = disk.transform.position.x < 0 ? 1 : -1;fly = FlyAction.GetSSAction(loc, angle, power);this.RunAction(disk, fly, this);}}
Judge
? 裁判类 。由于游戏到达第三回合之后就直接停止,裁判类无需再判断游戏的终止条件,只是进行游戏分数的记录 。
public class Judge : MonoBehaviour{private float score;void Start(){score = 0;}public void Record(GameObject disk){score += disk.GetComponent().score;}public float GetScore(){return score;}public void Reset(){score = 0;}}
? 场景单实例类 。当所需的实例第一次被需要时,在场景内搜索该实例,下一次使用时不需要搜索直接返回 。
public class Singleton : MonoBehaviour where T : MonoBehaviour {protected static T instance;public static T Instance {get {if (instance == null) {instance = (T)FindObjectOfType(typeof(T));if (instance == null) {Debug.LogError("An instance of " + typeof(T)+ " is needed in the scene, but there is none.");}}return instance;}}}
? 负责游戏第一个场景的搭载 。
? 在之中还编写了获取相应数值的(),重新开始游戏的实现游戏的(),游戏结束的()等小部件函数 。
? 而更为重要的函数为()、Hit()、()三个函数 。
? ()检测每一帧的鼠标点击是否命中目标,同时根据此时的回合数设置不同的游戏难度 。此处变量count用来控制飞碟的发射速度以及游戏回合切换的暂停时间:每经过一帧count加1,当count到达相应数值就发射飞碟,当回合结束时 , 使count在一定数值的帧内不再增加(此处引入变量hold来进行此操作),即可暂停发射飞碟并在此时插入切换回合的UI 。
? Hit()检测射线与飞碟是否碰撞,如碰撞则计分并回收飞碟 。
? ()从工厂中拿飞碟并根据种类设置一定范围内的随机发射参数,然后调用动作管理器执行发射飞碟的动作 。
public class FirstController : MonoBehaviour, ISceneController, IUserAction{public FlyActionManager action_manager;public DiskFactory disk_factory;public UserGUI user_gui;public Judge judge;private int round = 1;private int trial = 0;private bool running = false;private int count = 0;public bool stop = false;public int hold = 0;void Start(){SSDirector director = SSDirector.GetInstance();director.CurrentSceneController = this;disk_factory = Singleton.Instance;judge = Singleton.Instance;action_manager = gameObject.AddComponent() as FlyActionManager;user_gui = gameObject.AddComponent() as UserGUI;}void Update(){if (running){count++;if (Input.GetButtonDown("Fire1")){Vector3 pos = Input.mousePosition;Hit(pos);}switch (round){case 1:{if (stop){count = 0;hold++;if (hold == 300){stop = false;hold = 0;}}if (count >= 180){count = 0;SendDisk(1);trial += 1;if (trial == 10){stop = true;hold = 0;round += 1;trial = 0;}}break;}case 2:{if (stop){count = 0;hold++;if (hold == 600){stop = false;hold = 0;}}if (count >= 120){count = 0;if (trial % 2 == 0) SendDisk(1);else SendDisk(2);trial += 1;if (trial == 10){stop = true;hold = 0;round += 1;trial = 0;}}break;}case 3:{if (stop){count = 0;hold++;if (hold == 600){stop = false;hold = 0;}}if (count >= 60){count = 0;if (trial % 3 == 0) SendDisk(1);else if (trial % 3 == 1) SendDisk(2);else SendDisk(3);trial += 1;if (trial == 10){stop = true;hold = 0;}}break;}default:{if (stop){hold++;if (hold == 300){running = false;stop = false;hold = 0;}}break;}}disk_factory.FreeDisk();}}public void LoadResources(){disk_factory.GetDisk(round);disk_factory.FreeDisk();}private void SendDisk(int type){GameObject disk = disk_factory.GetDisk(type);float ran_y = 0;float ran_x = Random.Range(-1f, 1f) < 0 ? -1 : 1;float power = 0;float angle = 0;if (type == 1){ran_y = Random.Range(2f, 5f);power = Random.Range(5f, 8f);angle = Random.Range(15f, 20f);}else if (type == 2){ran_y = Random.Range(3f, 6f);power = Random.Range(10f, 13f);angle = Random.Range(10f, 15f);}else{ran_y = Random.Range(1f, 3f);power = Random.Range(15f, 18f);angle = Random.Range(5f, 10f);}disk.transform.position = new Vector3(ran_x * 16f, ran_y, 0);action_manager.DiskFly(disk, angle, power);}public void Hit(Vector3 pos){Ray ray = Camera.main.ScreenPointToRay(pos);RaycastHit[] hits;hits = Physics.RaycastAll(ray);for (int i = 0; i < hits.Length; i++){RaycastHit hit = hits[i];if (hit.collider.gameObject.GetComponent() != null){judge.Record(hit.collider.gameObject);hit.collider.gameObject.transform.position = new Vector3(0, -10, 0);}}}public float GetScore(){return judge.GetScore();}public int GetRound(){return round;}public int GetTrial(){return trial;}public bool GetStop(){return stop;}public int GetHold(){return hold;}public void ReStart(){running = true;judge.Reset();disk_factory.Reset();round = 1;trial = 0;stop = true;}public void GameOver(){running = false;}}
Disk
? 包括了飞碟类型type,分数score,颜色color三个属性 , 可以通过这些属性设置飞碟预制件的属性

Unity学习记录——与游戏世界交互

文章插图
public class Disk : MonoBehaviour{public int type = 1;public int score = 1;public Color color = Color.white;}
? Disk的工厂 。主要进行飞碟的生产与回收 。
? 在本函数之中,维护两个列表,一个是使用中的飞碟,一个是空闲飞碟 。当场景控制器需要获取一个飞碟时,先在空闲列表中寻找可用的空闲飞碟,如果找不到就根据预制重新实例化一个飞碟 。回收飞碟的逻辑为遍历使用列表,当有飞碟已经完成了所有动作,即位置在摄像机之下 , 则回收 。
public class DiskFactory : MonoBehaviour{private List used = new List();private List free = new List();public GameObject GetDisk(int type){GameObject disk_prefab = null;//寻找空闲飞碟,如果无空闲飞碟则重新实例化飞碟if (free.Count > 0){for (int i = 0; i < free.Count; i++){if (free[i].type == type){disk_prefab = free[i].gameObject;free.Remove(free[i]);break;}}}if (disk_prefab == null){if (type == 1){disk_prefab = Instantiate(Resources.Load("Prefabs/disk1"),new Vector3(0, -10f, 0), Quaternion.identity);}else if (type == 2){disk_prefab = Instantiate(Resources.Load("Prefabs/disk2"),new Vector3(0, -10f, 0), Quaternion.identity);}else{disk_prefab = Instantiate(Resources.Load("Prefabs/disk3"),new Vector3(0, -10f, 0), Quaternion.identity);}disk_prefab.GetComponent().material.color = disk_prefab.GetComponent().color;}used.Add(disk_prefab.GetComponent());disk_prefab.SetActive(true);return disk_prefab;}public void FreeDisk(){for (int i = 0; i < used.Count; i++){if (used[i].gameObject.transform.position.y <= -10f){free.Add(used[i]);used.Remove(used[i]);}}}public void Reset(){FreeDisk();}}
? UI代码 , 显示回合切换以及游戏开始结束、游戏进行时的UI 。
public class UserGUI : MonoBehaviour{private IUserAction action;//每个GUI的styleGUIStyle bold_style = new GUIStyle();GUIStyle text_style = new GUIStyle();GUIStyle over_style = new GUIStyle();GUIStyle round_style = new GUIStyle();private bool game_start = false;private int wid = Screen.width;private int heig = Screen.height;void Start(){action = SSDirector.GetInstance().CurrentSceneController as IUserAction;}void OnGUI(){bold_style.normal.textColor = new Color(1, 0, 0);bold_style.fontSize = 16;text_style.normal.textColor = new Color(0, 0, 0, 1);text_style.fontSize = 16;over_style.normal.textColor = new Color(1, 0, 0);over_style.fontSize = 25;round_style.normal.textColor = new Color(1, 0, 0);round_style.fontSize = 70;if (game_start && !action.GetStop()){GUI.Label(new Rect(wid - 150, 5, 200, 50), "分数:" + action.GetScore().ToString(), text_style);GUI.Label(new Rect(100, 5, 50, 50), "Round:" + action.GetRound().ToString(), text_style);GUI.Label(new Rect(180, 5, 50, 50), "Trial:" + action.GetTrial().ToString(), text_style);if (action.GetRound() == 3 && action.GetTrial() == 10){GUI.Label(new Rect(wid / 2 - 20, heig / 2 - 100, 100, 100), "游戏结束", over_style);GUI.Label(new Rect(wid / 2 - 10, heig / 2 - 50, 50, 50), "你的分数:" + action.GetScore().ToString(), text_style);if (GUI.Button(new Rect(wid / 2 - 20, heig / 2, 100, 50), "重新开始")){action.ReStart();return;}action.GameOver();}}else if (game_start && action.GetStop()){if (action.GetHold() > 300 && action.GetRound() <= 3 && action.GetTrial() == 0){GUI.Label(new Rect(wid / 2 - 110, heig / 2 - 60, 100, 100), "Round" + (action.GetRound()).ToString(), round_style);}if (action.GetRound() == 1){GUI.Label(new Rect(wid / 2 - 110, heig / 2 - 60, 100, 100), "Round" + (action.GetRound()).ToString(), round_style);}}else{GUI.Label(new Rect(wid / 2 - 60, heig / 2 - 100, 100, 100), "简单打飞碟", over_style);GUI.Label(new Rect(wid / 2 - 50, heig / 2 - 50, 100, 100), "鼠标点击飞碟", text_style);if (GUI.Button(new Rect(wid / 2 - 50, heig / 2, 100, 50), "游戏开始")){game_start = true;action.ReStart();}}}}
4.演示
? 演示gif图入下:
? 提醒:本次游戏项目在运行时是默认打开垂直同步的 , 开关在如下图所示位置:
? 如果关闭了垂直同步,游戏帧率会变高,但是也会体验到地狱绘卷难度的打飞碟,如下所示:
代码位置
【Unity学习记录——与游戏世界交互】? 代码以及文档均已经上传至hw5 · / - gitee