本文主要是介绍unity小游戏-打飞碟,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
一、回顾
上一个作业实现了动作分离+MVC,也就是Model+View+Controller+Action。同时,由于游戏对象自创建开始到游戏结束一直存在,游戏对象的创建、回收仍然由conroller统一管理。当游戏对象在游戏过程中提前被回收,同时又有新的游戏对象产生,对象的管理工作就不得不从controller中剥离出来,否则不但管理困难,游戏对象还难以复用。
因此,这次在前面的框架的基础上,进一步细化:建立“游戏对象工厂”,统一管理游戏过程中游戏对象的产生、回收、复用等等。
二、游戏介绍
在打飞碟游戏中,每一轮都会有新的飞碟产生,鼠标点中飞碟则被视为“打中飞碟”,会使飞碟消失,飞碟飞出屏幕区域同样会消失,一轮中飞碟全部消失则进入下一轮。从这里可以看到,游戏对象的创建与销毁是频繁的,而且新对象与旧对象之间没有实质性的差异,是可以复用的。
视频链接:unity小游戏-打飞碟_单机游戏热门视频
三、实现
代码实现部分借鉴了unity 实现简易打飞碟游戏-CSDN博客,respect。
1、
首先总体框架不变,Model+View+Controller+Action,对象工厂放在controller中(或许也可以提出来)。
2、Actions
Actions中的SSActionManager、SSAction几乎不变。
对应的CCActionManager、CCAction逻辑如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class CCActionManager : SSActionManager, IActionCallback
{public RoundController sceneController;//RoundController相当于FirstControllerpublic CCAction action;public DiskFactory factory;// Start is called before the first frame updatenew void Start(){sceneController = Singleton<RoundController>.Instance;sceneController.actionManager = this;factory = Singleton<DiskFactory>.Instance;}//回调函数public void SSActionEvent(SSAction source,SSActionEventType events = SSActionEventType.Completed,int intParam = 0,string strParam = null,Object objectParam = null) {//factory.FreeDisk(source.transform.gameObject);}public void MoveDisk(GameObject disk) {action = CCAction.GetSSAction(disk.GetComponent<DiskAttributes>().speedX, disk.GetComponent<DiskAttributes>().speedY);RunAction(disk, action, this);}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//飞碟从界面左右两侧飞入,离开界面时运动结束
public class CCAction : SSAction
{public float speedX;public float speedY;public static CCAction GetSSAction(float x, float y) {CCAction action = ScriptableObject.CreateInstance<CCAction>();//CCFlyAction action = SSAction.Instance;action.speedX = x;action.speedY = y;return action;}// Start is called before the first frame updatepublic override void Start(){}// Update is called once per framepublic override void Update(){//检查当前脚本所附加的游戏对象是否处于活动状态if (this.transform.gameObject.activeSelf == false) {//飞碟已经被"销毁"Debug.Log("1");this.destroy = true;this.callback.SSActionEvent(this);return;}Vector3 vec3 = Camera.main.WorldToScreenPoint (this.transform.position);if (vec3.x < -200 || vec3.x > Camera.main.pixelWidth + 200 || vec3.y < -200 || vec3.y > Camera.main.pixelHeight + 200) {Debug.Log("2");this.destroy = true;this.callback.SSActionEvent(this);return;}//更新位置//Time.deltaTime表示从上一帧到当前帧的时间间隔// += 速度*时间transform.position += new Vector3(speedX, speedY, 0) * Time.deltaTime * 2;}
}
3、Controller
RoundController负责管理轮次、调用对象工厂生成对象、检测鼠标是否点中飞碟,同时实现FirstController(场记)的功能。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Threading;public class RoundController : MonoBehaviour, ISceneController, IUserAction
{int round = 0;int max_round = 5;float timer = 0.5f;GameObject disk;DiskFactory factory ;public CCActionManager actionManager;public ScoreController scoreController;public UserGUI userGUI;//before Startvoid Awake() {SSDirector director = SSDirector.getInstance();director.currentSceneController = this;director.currentSceneController.LoadSource();//Add同时赋值给RoundController的CCAcM、ScoreCgameObject.AddComponent<CCActionManager>();gameObject.AddComponent<ScoreController>();gameObject.AddComponent<DiskFactory>();factory = Singleton<DiskFactory>.Instance;gameObject.AddComponent<UserGUI>();userGUI = gameObject.GetComponent<UserGUI>();}// Start is called before the first frame updatevoid Start(){}// Update is called once per framevoid Update(){if (userGUI.mode == 0) return;GetHit();gameOver();if (round > max_round) {return;}timer -= Time.deltaTime;if (timer <= 0 && actionManager.RemainActionCount() == 0) {//从工厂中得到10个飞碟,为其加上动作for (int i = 0; i < 10; ++i) {disk = factory.GetDisk(round);actionManager.MoveDisk(disk);}round += 1;if (round <= max_round) {userGUI.round = round;}timer = 4.0f;}}public void LoadSource() {}public void gameOver() {if (round > max_round && actionManager.RemainActionCount() == 0)userGUI.gameMessage = "THE END";}public void GetHit() {if (Input.GetButtonDown("Fire1")) {Vector3 mp = Input.mousePosition; //get Screen Position//create ray, origin is camera, and direction to mousepointCamera ca = Camera.main;Ray ray = ca.ScreenPointToRay(Input.mousePosition);//Return the ray's hitRaycastHit hit;if (Physics.Raycast(ray, out hit)) {//点中飞碟则加分scoreController.Record(hit.transform.gameObject);//设为不可见hit.transform.gameObject.SetActive(false);}else{//没点中飞碟则惩罚UnityEngine.Debug.Log("nope");scoreController.Publish();}}}
}
ScoreController负责记分,包括增加飞碟对应的分数、惩罚减少分数。它在RoundController中被调用。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class ScoreController : MonoBehaviour
{int score;public RoundController roundController;public UserGUI userGUI;// Start is called before the first frame updatevoid Start(){roundController = (RoundController)SSDirector.getInstance().currentSceneController;//这行代码使得RoundController中的gameObject.AddComponent<ScoreController>();的同时,//将自身赋给了roundController.scoreController roundController.scoreController = this;userGUI = this.gameObject.GetComponent<UserGUI>();}//加分public void Record(GameObject disk) {score += disk.GetComponent<DiskAttributes>().score;userGUI.score = score;}//扣分public void Publish(){UnityEngine.Debug.Log("publish");if(score>=1)score-=1;userGUI.score = score;}}
DiskFactory:
功能:生产飞碟、设置飞碟属性值、管理飞碟。通过两个队列:free、used,分别管理空闲飞碟、正在被使用的飞碟。当需要新的飞碟的时候,就从空闲飞碟队列中取出一个返回,并且放到正在被使用飞碟中管理。当一个飞碟被销毁(实际上没有销毁),就从正在被使用队列移动到空闲飞碟,实现复用。
using System.IO;
using System.Diagnostics;
using System.Security.AccessControl;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MyException : System.Exception
{public MyException() { }public MyException(string message) : base(message) { }
}public class DiskFactory : MonoBehaviour
{List<GameObject> disk_used;List<GameObject> disk_free;System.Random rand;//public int miss=0;// Start is called before the first frame updatevoid Start(){disk_used = new List<GameObject>();disk_free = new List<GameObject>();rand = new System.Random();//Disk disk = GetDisk(1); }// Update is called once per framevoid Update(){}void setAttributes(GameObject disk,int round){//根据不同round设置diskAttributes的值//随意的旋转角度disk.transform.localEulerAngles = new Vector3(-rand.Next(20,40),0,0);DiskAttributes attri = disk.GetComponent<DiskAttributes>();attri.score = rand.Next(1,4);//由分数来决定速度、颜色、大小//速度(大小)attri.speedX = (rand.Next(1,5) + attri.score + round) * 0.2f;attri.speedY = (rand.Next(1,5) + attri.score + round) * 0.2f;//颜色Color[] color={Color.blue, Color.green, Color.red};disk.GetComponent<Renderer>().material.color = color[attri.score-1];//大小disk.transform.localScale += new Vector3(-0.2f * (attri.score-1), 0, -0.2f * (attri.score-1));//飞碟可从四个方向飞入(左上、左下、右上、右下)int[,] dir={{1,1},{1,-1},{-1,1},{-1,-1}};float[,] screenPos={{Screen.width*0.1f,0},{Screen.width*0.1f,Screen.height*1.1f},{Screen.width*1f,0},{Screen.width*0.8f,Screen.height*0.8f}};int direction = rand.Next(0,4);//速度方向attri.speedX *= dir[direction,0];attri.speedY *= dir[direction,1];//初始位置disk.transform.Translate(Camera.main.ScreenToWorldPoint(new Vector3(screenPos[direction,0], screenPos[direction,1], 8)));UnityEngine.Debug.Log("trans",disk.transform);}public GameObject GetDisk(int round) {GameObject disk;if (disk_free.Count != 0) {disk = disk_free[0];disk_free.Remove(disk);}else {disk=new Disk().disk;}setAttributes(disk,round);//setAttributes(disk,round);disk_used.Add(disk);disk.SetActive(true);UnityEngine.Debug.Log("generate disk");return disk;}public void FreeDisk(GameObject disk) {disk.SetActive(false);//将位置和大小恢复到预制,这点很重要!disk.transform.position = new Vector3(0, 0,0);disk.transform.localScale = new Vector3(2f,0.1f,2f);if (!disk_used.Contains(disk)) {throw new MyException("Try to remove a item from a list which doesn't contain it.");}UnityEngine.Debug.Log("free disk");disk_used.Remove(disk);disk_free.Add(disk);}
}
Singleton:这个类并非控制器,它实现了单示例接口,保证游戏中一些实例对象的单一(比如只有一个对象工厂、记分员ScoreController、场记RoundController等等)
使用:在前面Action、Controller都有使用,比如CCActionManager中的:
sceneController = Singleton<RoundController>.Instance;factory = Singleton<DiskFactory>.Instance;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Singleton<T> : 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; } }
}
4、Model、Views
飞碟Disk
using System.Drawing;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class DiskAttributes : MonoBehaviour
{//public GameObject gameobj;public int score;public float speedX;public float speedY;
}public class Disk
{public GameObject disk;public Disk(){disk = GameObject.Instantiate(Resources.Load("Prefabs/disk", typeof(GameObject))) as GameObject;disk.AddComponent<DiskAttributes>();}
}
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class UserGUI : MonoBehaviour
{public int mode;public int score;public int miss;public int round;public string gameMessage;private IUserAction action;public GUIStyle bigStyle, blackStyle, smallStyle;//自定义字体格式public Font pixelFont;private int menu_width = Screen.width / 5, menu_height = Screen.width / 10;//主菜单每一个按键的宽度和高度// Start is called before the first frame updatevoid Start(){mode = 0;score = 0;miss = 0;round = 0;gameMessage = "";action = SSDirector.getInstance().currentSceneController as IUserAction;//pixelStyle//pixelFont = Font.Instantiate(Resources.Load("Fonts/ThaleahFat", typeof(Font))) as Font;//if (pixelFont == null) Debug.Log("null");//pixelFont.fontSize = 50;//pixelFont = Arial;//大字体初始化bigStyle = new GUIStyle();bigStyle.normal.textColor = Color.white;bigStyle.normal.background = null;bigStyle.fontSize = 50;bigStyle.alignment=TextAnchor.MiddleCenter;//blackblackStyle = new GUIStyle();blackStyle.normal.textColor = Color.black;blackStyle.normal.background = null;blackStyle.fontSize = 50;blackStyle.alignment=TextAnchor.MiddleCenter;//小字体初始化smallStyle = new GUIStyle();smallStyle.normal.textColor = Color.white;smallStyle.normal.background = null;smallStyle.fontSize = 20;smallStyle.alignment=TextAnchor.MiddleCenter;}// Update is called once per framevoid Update(){}void OnGUI() {//GUI.skin.button.font = pixelFont;GUI.skin.button.fontSize = 35;switch(mode) {case 0:mainMenu();break;case 1:GameStart();break;} }void mainMenu() {GUI.Label(new Rect(Screen.width / 2 - menu_width * 0.5f, Screen.height * 0.1f, menu_width, menu_height), "HIT UFO", bigStyle);bool button = GUI.Button(new Rect(Screen.width / 2 - menu_width * 0.5f, Screen.height * 3 / 7, menu_width, menu_height), "START");if (button) {mode = 1;}}void GameStart() {UnityEngine.Debug.Log(Camera.main.pixelWidth);UnityEngine.Debug.Log(Screen.width*1f);GUI.Label(new Rect(Screen.width / 2 - menu_width * 0.5f, Screen.height * 0.1f, menu_width, menu_height), gameMessage,bigStyle);//, bigStyle300, 60, 50, 200GUI.Label(new Rect(Screen.width*0.8f, Screen.height * 0.8f-30, menu_width, menu_height), "Score: " + score, smallStyle);//0,0,100,50GUI.Label(new Rect(Screen.width*0.8f, Screen.height * 0.8f, menu_width, menu_height), "Round: " + round, smallStyle);}
}
这篇关于unity小游戏-打飞碟的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!