首页 >> 大全

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

2024-01-06 大全 33 作者:考证青年

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三个属性,可以通过这些属性设置飞碟预制件的属性

游戏交互体验是什么__游戏交互

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图入下:

​ 提醒:本次游戏项目在运行时是默认打开垂直同步的,开关在如下图所示位置:

​ 如果关闭了垂直同步,游戏帧率会变高,但是也会体验到地狱绘卷难度的打飞碟,如下所示:

代码位置

​ 代码以及文档均已经上传至hw5 · / - gitee

关于我们

最火推荐

小编推荐

联系我们


版权声明:本站内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 88@qq.com 举报,一经查实,本站将立刻删除。备案号:桂ICP备2021009421号
Powered By Z-BlogPHP.
复制成功
微信号:
我知道了