Unity游戏Hit UFO 实现
一、游戏内容
游戏有n个round,每个round都包括10次trial 每个trail的飞碟颜色、大小、发射位置、速度、角度、同时出现的个数不同。 每个飞碟出现有随机性,总体难度随着round上升 鼠标点中飞碟得分,得分规则按照色彩、大小、速度不同进行计算
二、设计思路
将颜色、速度、得分属性赋予飞碟,不同round中的飞碟有不同颜色、速度和得分。其中需要注意的是,每个飞碟的得分根据round值、颜色、速度来计算。 参考Singleton实现UFO_factory,实现不同飞碟的产生与回收。 鼠标点击正在运动中的飞碟,该飞碟对应的分数就加到游戏的整体得分中。
三、代码实现
(1) 飞碟的实现
飞碟类记录每个飞碟的属性
public class UFO_info : MonoBehaviour
{
public float speed; //水平速度
public int points; //得分
public Vector3 direction; //初始方向
}
UFO_factory类来实现不同飞碟的长生和回收
public class UFO_factory : MonoBehaviour
{
public GameObject UFO; //飞碟预制
private List<UFO_info> used; //正被使用的飞碟
private List<UFO_info> free; //空闲的飞碟
public void Start()
{
used = new List<UFO_info>();
free = new List<UFO_info>();
UFO = GameObject.Instantiate<GameObject>(Resources.Load<GameObject>("Prefabs/UFO"), Vector3.zero, Quaternion.identity);
UFO.SetActive(false);
}
public GameObject GetDisk(int round)
{
GameObject disk;
//如果有空闲的飞碟,则直接使用,否则生成一个新的
if (free.Count > 0)
{
disk = free[0].gameObject;
free.Remove(free[0]);
}
else
{
disk = GameObject.Instantiate<GameObject>(UFO, Vector3.zero, Quaternion.identity);
disk.AddComponent<UFO_info>();
}
//按照round来设置飞碟属性
//飞碟的等级 = 0~2之间的随机数 * 轮次数
//0~4: 红色飞碟
//4~7: 绿色飞碟
//7~10: 蓝色飞碟
float level = UnityEngine.Random.Range(0, 2f) * (round + 1);
if (level < 4)
{
disk.GetComponent<UFO_info>().points = 1;
disk.GetComponent<UFO_info>().speed = 4.0f;
disk.GetComponent<UFO_info>().direction = new Vector3(UnityEngine.Random.Range(-1f, 1f) > 0 ? 2 : -2, 1, 0);
disk.GetComponent<Renderer>().material.color = Color.red;
}
else if (level > 7)
{
disk.GetComponent<UFO_info>().points = 3;
disk.GetComponent<UFO_info>().speed = 8.0f;
disk.GetComponent<UFO_info>().direction = new Vector3(UnityEngine.Random.Range(-1f, 1f) > 0 ? 2 : -2, 1, 0);
disk.GetComponent<Renderer>().material.color = Color.blue;
}
else
{
disk.GetComponent<UFO_info>().points = 2;
disk.GetComponent<UFO_info>().speed = 6.0f;
disk.GetComponent<UFO_info>().direction = new Vector3(UnityEngine.Random.Range(-1f, 1f) > 0 ? 2 : -2, 1, 0);
disk.GetComponent<Renderer>().material.color = Color.green;
}
used.Add(disk.GetComponent<UFO_info>());
return disk;
}
public void FreeDisk(GameObject disk)
{
//找到使用中的飞碟,将其踢出并加入到空闲队列
foreach (UFO_info UFO_info in used)
{
if (UFO_info.gameObject.GetInstanceID() == disk.GetInstanceID())
{
disk.SetActive(false);
free.Add(UFO_info);
used.Remove(UFO_info);
break;
}
}
}
}
(2)游戏动作类
基础动作
public class MyBaseAction : ScriptableObject
{
public bool enable = true;
public bool destroy = false;
public GameObject gameObject { get; set; }
public Transform transform { get; set; }
public MyActionCallback callback { get; set; }
protected MyBaseAction()
{
}
// Start is called before the first frame update
public virtual void Start()
{
throw new System.NotImplementedException();
}
// Update is called once per frame
public virtual void Update()
{
throw new System.NotImplementedException();
}
}
飞碟的飞行动作
public class RunAction :MyBaseAction
{
float gravity; //重力加速度
float speed; //水平速度
Vector3 direction; //飞行方向
float time; //时间
//生产函数(工厂模式)
public static RunAction GetSSAction(Vector3 direction, float speed)
{
RunAction action = ScriptableObject.CreateInstance<RunAction>();
action.gravity = 9.8f;
action.time = 0;
action.speed = speed;
action.direction = direction;
return action;
}
public override void Start()
{
}
public override void Update()
{
time += Time.deltaTime;
transform.Translate(Vector3.down * gravity * time * Time.deltaTime);
transform.Translate(direction * speed * Time.deltaTime);
//如果飞碟到达底部,则动作结束,进行回调
if (this.transform.position.y < -6)
{
this.destroy = true;
this.enable = false;
this.callback.BaseActionEvent(this);
}
}
}
(3) 动作管理类
基础动作管理
public class BaseActionManager : MonoBehaviour
{
//动作集,以字典形式存在
private Dictionary<int, MyBaseAction> actions = new Dictionary<int, MyBaseAction>();
//等待被加入的动作队列(动作即将开始)
private List<MyBaseAction> waitingAdd = new List<MyBaseAction>();
//等待被删除的动作队列(动作已完成)
private List<int> waitingDelete = new List<int>();
protected void Update()
{
//将waitingAdd中的动作保存
foreach (MyBaseAction ac in waitingAdd)
actions[ac.GetInstanceID()] = ac;
waitingAdd.Clear();
//运行被保存的事件
foreach (KeyValuePair<int, MyBaseAction> kv in actions)
{
MyBaseAction ac = kv.Value;
if (ac.destroy)
{
waitingDelete.Add(ac.GetInstanceID());
}
else if (ac.enable)
{
ac.Update();
}
}
//销毁waitingDelete中的动作
foreach (int key in waitingDelete)
{
MyBaseAction ac = actions[key];
actions.Remove(key);
Destroy(ac);
}
waitingDelete.Clear();
}
//准备运行一个动作,将动作初始化,并加入到waitingAdd
public void Run(GameObject gameObject, MyBaseAction action,MyActionCallback manager)
{
action.gameObject = gameObject;
action.transform = gameObject.transform;
action.callback = manager;
waitingAdd.Add(action);
action.Start();
}
// Start is called before the first frame update
protected void Start()
{
}
}
飞行动作管理
public class RunActionManager : BaseActionManager, MyActionCallback
{
//飞行动作
public RunAction flyAction;
//控制器
public FirstController controller;
protected new void Start()
{
controller = (FirstController)MyDirector.GetInstance().CurrentScenceController;
controller.actionManager = this;
}
public void Fly(GameObject ufo, float speed, Vector3 direction)
{
flyAction = RunAction.GetSSAction(direction, speed);
Run(ufo, flyAction, this);
}
//回调函数
public void BaseActionEvent(MyBaseAction source,
BaseActionEventType events = BaseActionEventType.Competed,
int intParam = 0,
string strParam = null,
Object objectParam = null)
{
//飞碟结束飞行后进行回收
controller.ufoFactory.FreeDisk(source.gameObject);
}
}
(4) 接口类
用户动作接口
public interface IUserAction
{
void Hit(Vector3 position);
void Restart();
void SetMode(bool isInfinite);
}
场景控制类接口
public interface ISceneController
{
void LoadResources();
}
(5)FirstController
public class FirstController : MonoBehaviour, ISceneController, IUserAction
{
public RunActionManager actionManager; //动作管理者
public UFO_factory ufoFactory; //飞碟工厂
int[] roundDisks; //对应轮次的飞碟数量
bool isInfinite; //游戏当前模式
int points; //游戏当前分数
int round; //游戏当前轮次
int sendCnt; //当前已发送的飞碟数量
float sendTime; //发送时间
void Start()
{
LoadResources();
}
public void LoadResources()
{
MyDirector.GetInstance().CurrentScenceController = this;
gameObject.AddComponent<UFO_factory>();
gameObject.AddComponent<RunActionManager>();
gameObject.AddComponent<UserGUI>();
ufoFactory = once<UFO_factory>.Instance;
sendCnt = 0;
round = 0;
sendTime = 0;
points = 0;
isInfinite = false;
roundDisks = new int[] { 3, 5, 8, 13, 21 };
}
public void SendDisk()
{
//从工厂生成一个飞碟
GameObject disk = ufoFactory.GetDisk(round);
//设置飞碟的随机位置
disk.transform.position = new Vector3(-disk.GetComponent<UFO_info>().direction.x * 7, UnityEngine.Random.Range(0f, 8f), 0);
disk.SetActive(true);
//设置飞碟的飞行动作
actionManager.Fly(disk, disk.GetComponent<UFO_info>().speed, disk.GetComponent<UFO_info>().direction);
}
public void Hit(Vector3 position)
{
Camera ca = Camera.main;
Ray ray = ca.ScreenPointToRay(position);
RaycastHit[] hits;
hits = Physics.RaycastAll(ray);
for (int i = 0; i < hits.Length; i++)
{
RaycastHit hit = hits[i];
if (hit.collider.gameObject.GetComponent<UFO_info>() != null)
{
//将飞碟移至底端,触发飞行动作的回调
hit.collider.gameObject.transform.position = new Vector3(0, -7, 0);
//积分
points += hit.collider.gameObject.GetComponent<UFO_info>().points;
//更新GUI数据
gameObject.GetComponent<UserGUI>().points = points;
}
}
}
public void Restart()
{
gameObject.GetComponent<UserGUI>().gameMessage = "";
round = 0;
sendCnt = 0;
points = 0;
gameObject.GetComponent<UserGUI>().points = points;
}
public void SetMode(bool isInfinite)
{
this.isInfinite = isInfinite;
}
void Update()
{
sendTime += Time.deltaTime;
//每隔1s发送一次飞碟
if (sendTime > 1)
{
sendTime = 0;
//每次发送至多5个飞碟
for (int i = 0; i < 5 && sendCnt < roundDisks[round]; i++)
{
sendCnt++;
SendDisk();
}
//判断是否需要重置轮次,不需要则输出游戏结束
if (sendCnt == roundDisks[round] && round == roundDisks.Length - 1)
{
if (isInfinite)
{
round = 0;
sendCnt = 0;
gameObject.GetComponent<UserGUI>().gameMessage = "";
}
else
{
gameObject.GetComponent<UserGUI>().gameMessage = "Game Over!";
}
}
//更新轮次
if (sendCnt == roundDisks[round] && round < roundDisks.Length - 1)
{
sendCnt = 0;
round++;
UserGUI.myrounds = round+1;
}
}
}
}
(6) 导演类
public class MyDirector : System.Object
{
private static MyDirector _instance;
public ISceneController CurrentScenceController { get; set; }
public static MyDirector GetInstance()
{
if (_instance == null)
{
_instance = new MyDirector();
}
return _instance;
}
}
(7) 用户GUI
public class UserGUI : MonoBehaviour
{
private IUserAction userAction;
public string gameMessage;
public int points;
static public int myrounds;
void Start()
{
points = 0;
gameMessage = "";
userAction = MyDirector.GetInstance().CurrentScenceController as IUserAction;
}
void OnGUI()
{
//小字体初始化
GUIStyle style = new GUIStyle();
style.normal.textColor = Color.white;
style.fontSize = 30;
//大字体初始化
GUIStyle bigStyle = new GUIStyle();
bigStyle.normal.textColor = Color.red;
bigStyle.fontSize = 50;
GUI.Label(new Rect(400, 30, 50, 200), "Hit UFO", bigStyle);
GUI.Label(new Rect(20, 0, 100, 50), "Points: " + points, style);
GUI.Label(new Rect(200, 0, 300, 50), "Rounds: " + myrounds, style);
GUI.Label(new Rect(400, 100, 50, 200), gameMessage, style);
if (GUI.Button(new Rect(20, 50, 100, 40), "Restart"))
{
userAction.Restart();
}
if (GUI.Button(new Rect(20, 100, 100, 40), "Normal Mode"))
{
userAction.SetMode(false);
}
if (GUI.Button(new Rect(20, 150, 100, 40), "Infinite Mode"))
{
userAction.SetMode(true);
}
if (Input.GetButtonDown("Fire1"))
{
userAction.Hit(Input.mousePosition);
}
}
}
四、效果展示
bilibil链接
github仓库地址