游戏涉及的类:
DiskData();飞碟数据
DiskFactory();飞碟的生产和销毁的工厂类;
RoundActionManager();动作管理类;
RoundController();场景控制类;
ScoreRecorder();分数记录;
SSDirector();导演类;
UserGUI();用户交互类;
类的解析:
关于导演类和用户交互类我就不做过多解释了,毕竟都是老师上课的代码和之前的一些模板。
动作管理类
我们就从动作管理类中的管理实例RoundActionManager()开始解析:
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<DiskData>().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");
}
}
在这个类中唯一值得注意的函数是addRandomAction(),该函数设置了起点和终点的坐标,然后就像是牧师与魔鬼动作分离版 中的船的动作从起点到终点一样设置飞碟的运动轨迹,没有抛物线,只因本人太菜不懂怎么设计。
飞碟数据类:
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<GameObject> used = new List<GameObject>();//存储正在使用的飞碟
private List<GameObject> free = new List<GameObject>();//存储使用完了被回收的飞碟
//颜色数组用于随机分配颜色
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);
}
a_disk.GetComponent<DiskData>().size = UnityEngine.Random.Range(0, 7-ruler);
a_disk.GetComponent<DiskData>().color = color[UnityEngine.Random.Range(0, 4)];
a_disk.GetComponent<DiskData>().speed = UnityEngine.Random.Range(10+ruler, 18+ruler);
a_disk.transform.localScale = new Vector3(a_disk.GetComponent<DiskData>().size * 2, a_disk.GetComponent<DiskData>().size * 0.1f, a_disk.GetComponent<DiskData>().size * 2);
a_disk.GetComponent<Renderer>().material.color = a_disk.GetComponent<DiskData>().color;
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<DiskData>().size *(20 - disk.GetComponent<DiskData>().speed));
//根据颜色加分
Color c = disk.GetComponent<DiskData>().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.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Singleton<T> 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;
}
}
}
为飞碟工厂,分数以及动作提供实例。
diskFactory = Singleton<DiskFactory>.Instance;
scoreRecorder = Singleton<ScoreRecorder>.Instance;
actionManager = Singleton<RoundActionManager>.Instance;
场景控制类:
重头戏来了!!!!!
在这个类中,我先实现了在ISceneController接口中定义的函数和枚举类型:
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 Pause()
{
state = State.PAUSE;
CoolTimes = 3;
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()
{
CoolTimes = 3;
scoreRecorder.Reset();
Application.LoadLevel(Application.loadedLevelName);
SSDirector.getInstance().currentScenceController.state = State.START;
}
在Resume和Pause两个函数中,我是用了协程的知识,简单来说协程就类似于Python中的挂起,关键字是yield。指的是在一个线程内,一个程序中断去执行另一个程序,有点类似于CPU中断。这样减少了切换线程带来的负担,同时不需要多线程中的锁机制,因为不存在同时写的问题。当程序执行到StopAllCoroutines()函数时,程序执行中断并挂起,这时执行的yield return语句,停止计时,等到StartCoroutines()时再从当前中断的地方继续执行。这就要在StartCoroutines()中加入另一个返回IEnumerator(协程)的函数作为参数:
IEnumerator DoCountDown()
{
while (leaveSeconds >= 0)
{
if (leaveSeconds >= 60) {
GameText.text = (leaveSeconds - 60).ToString ();
} else {
GameText.text = "";
}
yield return new WaitForSeconds(1);
leaveSeconds--;
}
}
该函数采用秒来跳帧便于计时,在最开始的3秒我用于游戏开始之前的倒数,因此每一回合的游戏时间实际为60秒。每一秒生产一个飞碟。
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<Renderer>().material = hit.collider.gameObject.GetComponent<Renderer>().material;
explosion.GetComponent<ParticleSystem>().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 == 4)
{
StopAllCoroutines();
state = State.WIN;
}
else if (leaveSeconds == 0 && scoreRecorder.getScore() < 500 * round) //时间到,分数不够,输了
{
StopAllCoroutines();
state = State.LOSE;
}
else
state = State.CONTINUE;
}
判断游戏进程。
在以上每个函数中都有枚举类型:
public enum State { WIN, LOSE, PAUSE, CONTINUE, START };
用来记录游戏状态。