声明
本作业借鉴自 https://blog.csdn.net/zzj051319/article/details/66475328 。
本人学习重点为设计模式,尽力做到高内聚低耦合。
描述
- 分多个round,每个 round 都是 n 个 trail;
- 不同round的飞碟的色彩、大小、速度不一。
- 鼠标击中得分,得分与round相关。
设计
-
场景的管理
导演类SSDirector、用户交互类UserGUI、场记类SceneController
(具体作用参见牧师与魔鬼(P&D) 第一版(基础 MVC 实现))动作的管理
动作基类SSAction、动作管理基类SSActionManager、实战动作管理CCActionManager
(具体作用参见牧师与魔鬼(P&D) 第二版(添加动作管理))
场记SceneController请了记分员Scorekeeper、飞碟管理员DiskFactory
- 记分员按飞碟的数据计分
- 场记管理出飞碟规则与管理碰撞
- 场记负责维护游戏状态
https://gitee.com/Ernie1/unity3d-learning/tree/hw4/hw4
飞碟数据DiskData
这个方法通过一个变量给游戏对象附加数据属性(分数),将作为Component挂载到一个 disk 游戏对象。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DiskData : MonoBehaviour {
public int score;
}
飞碟管理员DiskFactory
这个方法负责接收场记的请求(直接被场记调用),对 disk 游戏对象产生和回收。
这里运用了工厂模式,减少了游戏对象创建与销毁成本,减少创建和销毁次数,使程序易于扩展。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DiskFactory : System.Object {
private static DiskFactory _instance;
public SceneController sceneControler { get; set; }
public List<GameObject> used;
public List<GameObject> free;
public static DiskFactory getInstance(){
if (_instance == null) {
_instance = new DiskFactory();
_instance.used = new List<GameObject>();
_instance.free = new List<GameObject>();
}
return _instance;
}
public GameObject getDisk() {
GameObject newDisk;
if (free.Count == 0)
newDisk = GameObject.Instantiate(Resources.Load("prefabs/Disk")) as GameObject;
else {
newDisk = free[0];
free.Remove(free[0]);
}
newDisk.SetActive(true);
used.Add(newDisk);
return newDisk;
}
public void freeDisk(GameObject g) {
for (int i = 0; i < used.Count; i++) {
if (used[i] == g) {
used.Remove(g);
g.SetActive(false);
free.Add(g);
}
}
}
public void hideAll() {
for (int i = 0; i < used.Count; i++)
used [i].SetActive (false);
for (int i = 0; i < free.Count; i++)
free [i].SetActive (false);
}
}
记分员Scorekeeper
负责根据游戏对象的DiskData组件中的变量score进行分数的累加,分数被场景直接访问,提供清零方法。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Scorekeeper {
private static Scorekeeper _instance;
public static Scorekeeper getInstance(){
if (_instance == null)
_instance = new Scorekeeper();
return _instance;
}
public int score;
public void reset(){
score = 0;
}
public void record(GameObject hit) {
score += hit.GetComponent<DiskData> ().score;
}
}
抛出动作FlyDisk
继承了动作基类SSAction,接收速度级别参数,实现抛出Disk动作。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FlyDisk : SSAction {
Vector3 start; //起点
Vector3 target; //要到达的目标
Vector3 speed; //分解速度
float countTime;
Vector3 Gravity;
private int level;
public override void Start() {
start = new Vector3(7 - Random.value * 14, 0, 0);
target = new Vector3(Random.value * 80 - 40, Random.value * 29 - 4, 30);
this.transform.position = start;
float mainSpeed = 5 + level * 6;
float time = Vector3.Distance(target, start) / mainSpeed;
speed = new Vector3 ((target.x - start.x) / time, (target.y - start.y) / time + 5 * time, (target.z - start.z) / time);
Gravity = Vector3.zero;
countTime = 0;
}
public static FlyDisk GetSSAction(int level) {
FlyDisk action = ScriptableObject.CreateInstance<FlyDisk>();
action.level = level;
return action;
}
public override void Update() {
float g = -10;
Gravity.y = g * (countTime += Time.fixedDeltaTime);// v=gt
this.transform.position += (speed + Gravity) * Time.fixedDeltaTime;//模拟位移
if (this.transform.position.z >= target.z) {
DiskFactory.getInstance().freeDisk(gameobject);
this.destroy = true;
this.callback.SSActionEvent(this);
}
}
}
主体
导演类SSDirector
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public interface ISceneController{
void LoadResources();
}
public class SSDirector : System.Object {
private static SSDirector _instance;
public ISceneController currentScenceController { get; set; }
public bool running { get; set; }
public static SSDirector getInstance(){
if (_instance == null)
_instance = new SSDirector();
return _instance;
}
public int getFPS() {
return Application.targetFrameRate;
}
public void setFPS(int fps) {
Application.targetFrameRate = fps;
}
}
用户交互类UserGUI
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public interface IUserAction{
void StartGame();
void ReStart();
}
public class UserGUI : MonoBehaviour{
private IUserAction action;
void Start() {
action = SSDirector.getInstance ().currentScenceController as IUserAction;
}
void OnGUI() {
if (GUI.Button(new Rect(Screen.width/2-38, 38, 76, 20), "(RE)PLAY"))
action.ReStart();
}
}
场记类SceneController
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
public class SceneController : MonoBehaviour, ISceneController, IUserAction
{
public CCActionManager actionManager { get; set; }
public Scorekeeper scorekeeper;
public DiskFactory DF;
private int round = 0;
public int totalRound = 3;
public int trial = 10;
public Text ScoreText;
public Text RoundText;
public Text GameText;
private bool play = false;
private int num = 0;
private float heartbeat;
GameObject disk;
GameObject explosion;
void Awake() {
SSDirector director = SSDirector.getInstance();
DF = DiskFactory.getInstance();
DF.sceneControler = this;
director.setFPS(60);
director.currentScenceController = this;
director.currentScenceController.LoadResources();
scorekeeper = Scorekeeper.getInstance ();
}
void Start() {
round = 1;
heartbeat = 0;
}
public void LoadResources() {
explosion = Instantiate(Resources.Load("prefabs/Explosion"), new Vector3(-40, 0, 0), Quaternion.identity) as GameObject;
Instantiate(Resources.Load("prefabs/Light"));
}
void Update() {
if (play) {
if (heartbeat >= 1) {
launchDisk ();
heartbeat = 0;
}
heartbeat += Time.deltaTime;
}
if (Input.GetButtonDown("Fire1") && play) {
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit)) {
GameText.text = "";
if (hit.transform.tag == "Disk")
{
explosion.transform.position = hit.collider.gameObject.transform.position;
explosion.GetComponent<ParticleSystem>().Play();
hit.collider.gameObject.SetActive(false);
scorekeeper.record (hit.collider.gameObject);
}
}
}
updateStatus ();
}
public void StartGame() {
num = 0;
play = true;
scorekeeper.reset ();
}
public void ReStart() {
round = 1;
heartbeat = 0;
num = 0;
play = true;
GameText.text = "";
DF.hideAll ();
scorekeeper.reset ();
}
private void launchDisk() {
GameObject newDisk = DF.getDisk ();
if (!newDisk.GetComponent<DiskData> ())
newDisk.AddComponent<DiskData> ();
newDisk.GetComponent<DiskData> ().score = round;
newDisk.GetComponent<Renderer> ().material.color = new Color ((round % 10) * 0.1F, 0.4F, 0.8F);
float size = (float)(1.0f - 0.2 * round);
newDisk.transform.localScale = new Vector3(4*size, size/5, 4*size);
num++;
actionManager.singleRunAction (newDisk, round);
}
private void updateStatus() {
ScoreText.text = "Score:" + scorekeeper.score.ToString();
RoundText.text = "Round:" + round.ToString();
if (scorekeeper.score >= 6) {
++round;
GameText.text = "Round " + round.ToString();
scorekeeper.reset ();
num = 0;
}
if (round > totalRound) {
play = false;
GameText.text = "Win";
}
if (num >= trial) {
play = false;
GameText.text = "Game Over";
}
}
}
动作基类SSAction
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public enum SSActionEventType : int { Started, Competeted }
public interface ISSActionCallback {
void SSActionEvent(SSAction source, SSActionEventType events = SSActionEventType.Competeted,
int intParam = 0, string strParam = null, Object objectParam = null);
}
public class SSAction : ScriptableObject {
public bool enable = true;
public bool destroy = false;
public GameObject gameobject { get; set; }
public Transform transform { get; set; }
public ISSActionCallback callback{ get; set; }
protected SSAction () {}
//Use this for initialization
public virtual void Start () {
throw new System.NotImplementedException ();
}
// Update is called once per frame
public virtual void Update () {
throw new System.NotImplementedException ();
}
}
动作管理基类SSActionManager
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SSActionManager : MonoBehaviour
{
private Dictionary<int, SSAction> actions = new Dictionary<int, SSAction>();
private List<SSAction> waitingAdd = new List<SSAction>();
private List<int> waitingDelete = new List<int>();
// Use this for initialization
void Start() {
}
// Update is called once per frame
protected void Update() {
foreach (SSAction ac in waitingAdd) actions[ac.GetInstanceID()] = ac;
waitingAdd.Clear();
foreach (KeyValuePair<int, SSAction> kv in actions) {
SSAction ac = kv.Value;
if (ac.destroy)
waitingDelete.Add(ac.GetInstanceID());
else if (ac.enable)
ac.Update();
}
foreach (int key in waitingDelete) {
SSAction ac = actions [key];
actions.Remove (key);
DestroyObject (ac);
}
waitingDelete.Clear();
}
public void RunAction(GameObject gameobject, SSAction action, ISSActionCallback manager) {
action.gameobject = gameobject;
action.transform = gameobject.transform;
action.callback = manager;
waitingAdd.Add(action);
action.Start();
}
}
实战动作管理CCActionManager
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CCActionManager : SSActionManager, ISSActionCallback {
public SceneController sceneController;
public DiskFactory diskFactory;
void Start() {
sceneController = (SceneController)SSDirector.getInstance ().currentScenceController;
sceneController.actionManager=this;
diskFactory = DiskFactory.getInstance();
}
public new void Update () {
base.Update ();
}
public void singleRunAction (GameObject gameObject, int speedLevel) {
this.RunAction (gameObject, FlyDisk.GetSSAction (speedLevel), this);
}
#region ISSActionCallback implementation
public void SSActionEvent(SSAction source, SSActionEventType events = SSActionEventType.Competeted,
int intParam = 0, string strParam = null, Object objectParam = null){
}
#endregion
}