鼠标点中飞碟后,飞碟爆炸并计分,达到500分后难度提升,1000分则胜利
注意设置脚本的执行顺序,否则可能出现创建对象为空的情况!
脚本挂载如下:
预制包括一个飞碟和一个爆炸的粒子效果
注意:要把Diskdata脚本挂到预制的Disk上
下面是爆炸粒子的设置:
在上一次牧师与魔鬼游戏的基础上,新添加了工厂模式生产飞碟。
工厂类的UML图如下:
全部类的UML图如下:
各类说明
各类具体代码
这次除了引入工厂模式,还用了单实例的模板Singleton
Singleton模板代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Singleton where T : MonoBehaviour
{
private static T instance;
public static T Instance
{
get
{
if (instance == null)
{
instance = (T)Object.FindObjectOfType(typeof(T));
if (instance == null)
{
Debug.LogError("Can't find instance of " + typeof(T));
}
}
return instance;
}
}
}
使用起来很简单,场景单实例要求场景中至少有一个 T 类型的 Mono 子类,然后在需要的地方,如在场记中,调用如下即可获得对应类的单实例对象:
diskFactory = Singleton<DiskFactory>.Instance;
scoreRecorder = Singleton<ScoreRecorder>.Instance;
actionManager = Singleton<RoundActionManager>.Instance;
除了飞碟工厂和记分员的代码是新写的之外,其他类如场记、动作管理者、界面都是在上一篇牧师与魔鬼游戏的代码基础上修改的,工作量非常少
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DiskData : MonoBehaviour {
public float size;
public Color color;
public float speed;
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DiskFactory : MonoBehaviour {
private List used = new List();//存储正在使用的飞碟
private List free = new List();//存储使用完了被回收的飞碟
//颜色数组用于随机分配颜色
private Color[] color = { Color.red, Color.green, Color.blue, Color.yellow };
//生产飞碟,先从回收部分取,若回收的部分为空,才从资源加载新的飞碟
public GameObject GetDisk(int ruler)
{
GameObject a_disk;
if (free.Count > 0)
{
a_disk = free[0];
free.Remove(free[0]);
}
else
{
a_disk = GameObject.Instantiate(Resources.Load("Prefabs/Disk")) as GameObject;
Debug.Log(a_disk);
}
switch (ruler)
{
case 1:
a_disk.GetComponent().size = UnityEngine.Random.Range(0, 6);//随机大小
a_disk.GetComponent().color = color[UnityEngine.Random.Range(0, 4)];//随机颜色
a_disk.GetComponent().speed = UnityEngine.Random.Range(10, 15);//不同关卡速度不同,同一关卡速度在一定范围内
a_disk.transform.localScale = new Vector3(a_disk.GetComponent().size * 2, a_disk.GetComponent().size * 0.1f, a_disk.GetComponent().size * 2);
a_disk.GetComponent().material.color = a_disk.GetComponent().color;
break;
case 2:
a_disk.GetComponent().size = UnityEngine.Random.Range(0, 4);
a_disk.GetComponent().color = color[UnityEngine.Random.Range(0, 4)];
a_disk.GetComponent().speed = UnityEngine.Random.Range(15, 20);
a_disk.transform.localScale = new Vector3(a_disk.GetComponent().size * 2, a_disk.GetComponent().size * 0.1f, a_disk.GetComponent().size * 2);
a_disk.GetComponent().material.color = a_disk.GetComponent().color;
break;
}
a_disk.SetActive(true);
used.Add(a_disk);
return a_disk;
}
//回收飞碟
public void FreeDisk(GameObject disk)
{
for(int i = 0; i < used.Count; i++)
{
if(used[i] == disk)
{
disk.SetActive(false);
used.Remove(used[i]);
free.Add(disk);
}
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ScoreRecorder : MonoBehaviour {
private float score;
public float getScore()
{
return score;
}
public void Record(GameObject disk)
{
//size越小、速度越快,分越高
score += (100 - disk.GetComponent().size *(20 - disk.GetComponent().speed));
//根据颜色加分
Color c = disk.GetComponent().color;
switch (c.ToString())
{
case "red":
score += 50;
break;
case "green":
score += 40;
break;
case "blue":
score += 30;
break;
case "yellow":
score += 10;
break;
}
}
public void Reset()
{
score = 0;
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public enum State { WIN, LOSE, PAUSE, CONTINUE, START };
public interface ISceneController
{
State state { get; set; }
void LoadResources();
void Pause();
void Resume();
void Restart();
}
public class RoundController : MonoBehaviour, IUserAction, ISceneController {
public DiskFactory diskFactory;
public RoundActionManager actionManager;
public ScoreRecorder scoreRecorder;
private List disks;
private int round;//第几个回合
private GameObject shootAtSth;
GameObject explosion;
//游戏状态
public State state { get; set; }
//计时器, 每关60秒倒计时
public int leaveSeconds;
//用来计数,每秒自动发射一次飞碟
public int count;
IEnumerator DoCountDown()
{
while (leaveSeconds >= 0)
{
yield return new WaitForSeconds(1);
leaveSeconds--;
}
}
void Awake()
{
SSDirector director = SSDirector.getInstance();
director.setFPS(60);
director.currentScenceController = this;
LoadResources();
diskFactory = Singleton.Instance;
scoreRecorder = Singleton.Instance;
actionManager = Singleton.Instance;
leaveSeconds = 60;
count = leaveSeconds;
state = State.PAUSE;
disks = new List();
}
void Start () {
round = 1;//从第一关开始
LoadResources();
}
void Update()
{
LaunchDisk();
Judge();
RecycleDisk();
}
public void LoadResources()
{
Camera.main.transform.position = new Vector3(0, 0, -15);
explosion = Instantiate(Resources.Load("Prefabs/ParticleSys"), new Vector3(-40, 0, 0), Quaternion.identity) as GameObject;
}
public void shoot()//用户在游戏状态为开始或者继续时,才能左键射击
{
if (Input.GetMouseButtonDown(0) && (state == State.START || state == State.CONTINUE))
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit))
{
if ((SSDirector.getInstance().currentScenceController.state == State.START || SSDirector.getInstance().currentScenceController.state == State.CONTINUE))
{
shootAtSth = hit.transform.gameObject;
explosion.transform.position = hit.collider.gameObject.transform.position;
explosion.GetComponent().material = hit.collider.gameObject.GetComponent().material;
explosion.GetComponent().Play();
}
}
}
}
public void LaunchDisk()//每秒自动发射飞碟
{
if(count - leaveSeconds == 1)
{
count = leaveSeconds;
GameObject disk = diskFactory.GetDisk(round);//从飞碟工厂得到飞碟
Debug.Log(disk);
disks.Add(disk);//飞碟进入场景
actionManager.addRandomAction(disk);//让动作管理者设计轨迹
}
}
public void RecycleDisk()//检查需不需要回收飞碟
{
for(int i = 0; i < disks.Count; i++)
{
if( disks[i].transform.position.z < -18)
{
diskFactory.FreeDisk(disks[i]);//让飞碟工厂回收
disks.Remove(disks[i]);
}
}
}
public void Judge()//判断游戏状态,是否射中以及够不够分数进入下一回合
{
if(shootAtSth != null && shootAtSth.transform.tag == "Disk" && shootAtSth.activeInHierarchy)//射中飞碟
{
scoreRecorder.Record(shootAtSth);//计分
diskFactory.FreeDisk(shootAtSth);//回收飞碟
shootAtSth = null;//点击的物体重置为空,避免计分出错
}
if(scoreRecorder.getScore() > 500 * round)//每关500分才能进入下一关,重新倒数60秒
{
round++;
leaveSeconds = count = 60;
}
if (round == 3) //只设计了两关, 所以赢了
{
StopAllCoroutines();
state = State.WIN;
}
else if (leaveSeconds == 0 && scoreRecorder.getScore() < 500 * round) //时间到,分数不够,输了
{
StopAllCoroutines();
state = State.LOSE;
}
else
state = State.CONTINUE;
}
public void Pause()
{
state = State.PAUSE;
StopAllCoroutines();
for (int i = 0; i < disks.Count; i++)
{
disks[i].SetActive(false);//暂停后飞碟不可见
}
}
public void Resume()
{
StartCoroutine(DoCountDown()); //开启协程计时
state = State.CONTINUE;
for (int i = 0; i < disks.Count; i++)
{
disks[i].SetActive(true);//恢复后飞碟可见
}
}
public void Restart()
{
scoreRecorder.Reset();
Application.LoadLevel(Application.loadedLevelName);
SSDirector.getInstance().currentScenceController.state = State.START;
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public interface ISSActionCallback
{
void actionDone(SSAction source);
}
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; }
public virtual void Start()
{
throw new System.NotImplementedException();
}
public virtual void Update()
{
throw new System.NotImplementedException();
}
}
public class MoveToAction : SSAction
{
public Vector3 target;
public float speed;
private MoveToAction() { }
public static MoveToAction getAction(Vector3 target, float speed)
{
MoveToAction action = ScriptableObject.CreateInstance();
action.target = target;
action.speed = speed;
return action;
}
public override void Update()
{
this.transform.position = Vector3.MoveTowards(this.transform.position, target, speed * Time.deltaTime);
if (this.transform.position == target)
{
this.destroy = true;
this.callback.actionDone(this);
}
}
public override void Start() {}
}
public class SequenceAction : SSAction, ISSActionCallback
{
public List sequence;
public int repeat = -1; //-1表示无限循环,0表示只执行一遍,repeat> 0 表示重复repeat遍
public int currentAction = 0;//当前动作列表里,执行到的动作序号
public static SequenceAction getAction(int repeat, int currentActionIndex, List sequence)
{
SequenceAction action = ScriptableObject.CreateInstance();
action.sequence = sequence;
action.repeat = repeat;
action.currentAction = currentActionIndex;
return action;
}
public override void Update()
{
if (sequence.Count == 0) return;
if (currentAction < sequence.Count)
{
sequence[currentAction].Update();
}
}
public void actionDone(SSAction source)
{
source.destroy = false;
this.currentAction++;
if (this.currentAction >= sequence.Count)
{
this.currentAction = 0;
if (repeat > 0) repeat--;
if (repeat == 0)
{
this.destroy = true;
this.callback.actionDone(this);
}
}
}
public override void Start()
{
foreach (SSAction action in sequence)
{
action.gameObject = this.gameObject;
action.transform = this.transform;
action.callback = this;
action.Start();
}
}
void OnDestroy()
{
foreach (SSAction action in sequence)
{
DestroyObject(action);
}
}
}
public class SSActionManager : MonoBehaviour
{
private Dictionary<int, SSAction> actions = new Dictionary<int, SSAction>();
private List waitingToAdd = new List();
private List<int> watingToDelete = new List<int>();
protected void Update()
{
foreach (SSAction ac in waitingToAdd)
{
actions[ac.GetInstanceID()] = ac;
}
waitingToAdd.Clear();
foreach (KeyValuePair<int, SSAction> kv in actions)
{
SSAction ac = kv.Value;
if (ac.destroy)
{
watingToDelete.Add(ac.GetInstanceID());
}
else if (ac.enable)
{
ac.Update();
}
}
foreach (int key in watingToDelete)
{
SSAction ac = actions[key];
actions.Remove(key);
DestroyObject(ac);
}
watingToDelete.Clear();
}
public void RunAction(GameObject gameObject, SSAction action, ISSActionCallback whoToNotify)
{
action.gameObject = gameObject;
action.transform = gameObject.transform;
action.callback = whoToNotify;
waitingToAdd.Add(action);
action.Start();
}
}
public class RoundActionManager : SSActionManager, ISSActionCallback
{
public RoundController scene;
public MoveToAction action1, action2;
public SequenceAction saction;
float speed;
public void addRandomAction(GameObject gameObj)
{
int[] X = { -20, 20 };
int[] Y = { -5, 5 };
int[] Z = { -20, -20 };
// 随机生成起始点和终点
Vector3 starttPos = new Vector3(
UnityEngine.Random.Range(-20, 20),
UnityEngine.Random.Range(-5, 5),
UnityEngine.Random.Range(50, 10)
);
gameObj.transform.position = starttPos;
Vector3 randomTarget = new Vector3(
X[UnityEngine.Random.Range(0, 2)],
Y[UnityEngine.Random.Range(0, 2)],
Z[UnityEngine.Random.Range(0, 2)]
);
MoveToAction action = MoveToAction.getAction(randomTarget, gameObj.GetComponent().speed);
RunAction(gameObj, action, this);
}
protected void Start()
{
scene = (RoundController)SSDirector.getInstance().currentScenceController;
scene.actionManager = this;
}
protected new void Update()
{
base.Update();
}
public void actionDone(SSAction source)
{
Debug.Log("Done");
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public interface IUserAction
{
void shoot();//射击动作
}
public class UserGUI : MonoBehaviour
{
private IUserAction action;
private float width, height;
private string countDownTitle;
void Start()
{
countDownTitle = "Start";
action = SSDirector.getInstance().currentScenceController as IUserAction;
}
float castw(float scale)
{
return (Screen.width - width) / scale;
}
float casth(float scale)
{
return (Screen.height - height) / scale;
}
void OnGUI()
{
width = Screen.width / 12;
height = Screen.height / 12;
//倒计时
GUI.Label(new Rect(castw(2f)+20, casth(6f) - 20, 50, 50), ((RoundController)SSDirector.getInstance().currentScenceController).leaveSeconds.ToString());
//分数
GUI.Button(new Rect(580, 10, 80, 30), ((RoundController)SSDirector.getInstance().currentScenceController).scoreRecorder.getScore().ToString());
if (SSDirector.getInstance().currentScenceController.state != State.WIN && SSDirector.getInstance().currentScenceController.state != State.LOSE
&& GUI.Button(new Rect(10, 10, 80, 30), countDownTitle))
{
if (countDownTitle == "Start")
{
//恢复场景
countDownTitle = "Pause";
SSDirector.getInstance().currentScenceController.Resume();
}
else
{
//暂停场景
countDownTitle = "Start";
SSDirector.getInstance().currentScenceController.Pause();
}
}
if (SSDirector.getInstance().currentScenceController.state == State.WIN)//胜利
{
if (GUI.Button(new Rect(castw(2f), casth(6f), width, height), "Win!"))
{
//选择重来
SSDirector.getInstance().currentScenceController.Restart();
}
}
else if (SSDirector.getInstance().currentScenceController.state == State.LOSE)//失败
{
if (GUI.Button(new Rect(castw(2f), casth(6f), width, height), "Lose!"))
{
SSDirector.getInstance().currentScenceController.Restart();
}
}
}
void Update()
{
//监测用户射击
action.shoot();
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SSDirector : System.Object
{
public 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;
}
public void NextScene()
{
Debug.Log("抱歉,没下一个场景了");
}
}
菜鸟一个,不好的地方欢迎大家吐槽TAT。