首先,先上设计图:
效果图:
预设设置:
简单说一下设计思路。对于飞碟的运动:运动学运动,有点类似牧师恶魔里面的过河;物理运动,则类似射箭小游戏的设计。所以,对于这两种动作分别实现即可。
运动学实现:
public void playDisk(GameObject disk)
{
temp = disk;
//随机选取起始坐标
float x = Random.Range(-1.5f, 1.5f);
float z = Random.Range(-2f, 2f);
Vector3 initialPos = new Vector3(x, -3.7f, z);
disk.transform.position = initialPos;
//随机选取发射方向
float xEnd = Random.Range(-2f, 2f);
float yEnd = Random.Range(6f, 8f);
float zEnd = Random.Range(0f, 5f);
Vector3 dir = new Vector3(xEnd, yEnd, zEnd);
//随机设置速度
float speed = Random.Range(10f,50f);
action = MoveToAction.getAction(dir, speed);
RunAction(disk, action, this);
}
//飞碟运动到指定地点后,添加重力,方便其落回地面
public void actionDone(SSAction source)
{
if(source == action)
{
temp.GetComponent<Rigidbody>().useGravity = true;
}
}
物理学实现:
public void playDisk(GameObject disk)
{
//随机选取起始坐标
float x = Random.Range(-1.5f, 1.5f);
float z = Random.Range(0f, 0.5f);
Vector3 initialPos = new Vector3(x, -3.7f, z);
disk.transform.position = initialPos;
//随机选取发射方向
float xEnd = Random.Range(-2f, 2f);
float yEnd = Random.Range(6f, 8f);
float zEnd = Random.Range(0f, 5f);
Vector3 dir = new Vector3(xEnd, yEnd, zEnd);
disk.transform.up = dir;
disk.GetComponent<Rigidbody>().velocity = dir * speed; //设置速度
disk.GetComponent<Rigidbody>().useGravity = true;
//Force = UnityEngine.Random.Range(-20, 20); //获取随机的风力
//disk.GetComponent<Rigidbody>().AddForce(new Vector3(Force, 0, 0), ForceMode.Force);
}
接下来,实现飞碟两种运动的组合:
public void EmitDisk()
{
if (count - leftTime == 2)
{
count = leftTime;
for(int i = 0; i < _diskNum; i++)//第一关,发射一个飞碟每次;第二关发射两个飞碟每次
{
GameObject disk = factory.getDisk();//从飞碟工厂得到飞碟
Debug.Log(disk);
usedDisk.Add(disk);//飞碟进入场景
disk.SetActive(true);
disk.GetComponent<Renderer>().material.color = _color;
disk.transform.localScale = _scale;
if (mode == 1)
iactionmanager.Normalplay(disk);//让普通动作管理者设计轨迹
else
iactionmanager.Physicplay(disk);//物理运动管理者设计
}
mode = 3 - mode;//更换下一次运动模式
}
}
public class IActionManager : MonoBehaviour {
public PhysicActionManager physicaction;
public CCActionManager ccaction;
void Awake()
{
physicaction = Singleton<PhysicActionManager>.Instance;
ccaction = Singleton<CCActionManager>.Instance;
}
public void Normalplay(GameObject disk)
{
ccaction.playDisk(disk);
}
public void Physicplay(GameObject disk)
{
physicaction.playDisk(disk);
}
}
这样,就完成了两种运动的组合。接下来,就是计分。因为两种运动,在其完成之前,我都添加了重力。所以,飞碟都会下落。因此,飞碟落地则会扣分;若未落地被击中,则会得分:
public void RecycleDisk()//检查需不需要回收飞碟
{
for (int i = 0; i < usedDisk.Count; i++)
{
if (usedDisk[i].activeInHierarchy == false )
{
if (usedDisk[i].transform.position.y <= -2)
_judge.failAdisk();//失分
usedDisk[i].GetComponent<Rigidbody>().useGravity = false;
factory.free(usedDisk[i]);//让飞碟工厂回收
usedDisk.Remove(usedDisk[i]);//移除
}
}
}
public void shoot()
{
if (Input.GetMouseButtonDown(0) && (state == State.START || state == State.CONTINUE))
{
Debug.Log("shoot");
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit))
{
hit.transform.gameObject.SetActive(false);
_judge.scoreADisk();//得分
}
}
}
//*************************
void Update()
{
RecycleDisk();
}
接着,完善细节就可以完成整个游戏了。
附上完整代码
Singleton.cs
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 need in the scene, but there is none.");
}
}
return instance;
}
}
}
UserInterface.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class UserInterface : MonoBehaviour {
private IUserAction action;
float width, height;
// Use this for initialization
void Awake()
{
Debug.Log("UserInterface");
}
void Start()
{
Debug.Log("UserInterface");
action = Singleton<SceneController>.Instance 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), Singleton<SceneController>.Instance.leftTime.ToString());
//分数
GUI.Button(new Rect(castw(2f) +400, casth(6f) -20, 80, 30),"分数"+ Singleton<Judge>.Instance.getPoint().ToString());
GUI.Button(new Rect(castw(2f) + 400, casth(6f) -60, 80, 30), "关卡" + Singleton<RoundController>.Instance.getRound().ToString());
if (Singleton<SceneController>.Instance.state != State.WIN && Singleton<SceneController>.Instance.state != State.LOSE
&& GUI.Button(new Rect(10, 10, 80, 30), Singleton<SceneController>.Instance.countDownTitle))
{
if (Singleton<SceneController>.Instance.countDownTitle == "Start")
{
Debug.Log(Singleton<SceneController>.Instance.state);
action.Resume();//用户继续
Debug.Log(Singleton<SceneController>.Instance.state);
Singleton<SceneController>.Instance.countDownTitle = "Pause";
Singleton<SceneController>.Instance.onCountDown = true;
StartCoroutine(Singleton<SceneController>.Instance.WaitFourSecond());
}
else
{
Debug.Log(Singleton<SceneController>.Instance.state);
action.Pause();//用户暂停
Debug.Log(Singleton<SceneController>.Instance.state);
Singleton<SceneController>.Instance.countDownTitle = "Start";
Singleton<SceneController>.Instance.onCountDown = false;
StopAllCoroutines();
}
}
if (Singleton<SceneController>.Instance.state == State.WIN)//胜利
{
StopAllCoroutines();
if (GUI.Button(new Rect(castw(2f), casth(6f), width+20, height), "Win!"))
{
action.Restart();
}
}
else if (Singleton<SceneController>.Instance.state == State.LOSE)//失败
{
StopAllCoroutines();
if (GUI.Button(new Rect(castw(2f), casth(6f), width+20, height), "Lose!"))
{
action.Restart();
}
}
}
}
SceneController.cs
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/**
* 所有的分数通过调用Judge实现
* 所有的关卡控制通过RoudController实现
* 所有的动作通过IActionManger实现
* SceneControler负责调度,实现加载场景,暂停,重新开始等
* */
public enum State { WIN, LOSE, PAUSE, CONTINUE, START };
public interface IUserAction
{
void EmitDisk();//射击
void Pause();//用户选择暂停游戏
void Resume();//继续
void Restart();//重来
}
public class SceneController : MonoBehaviour, IUserAction
{
public Camera camera;
public Color _color;
public Vector3 _scale;
public int _diskNum;
public GameObject bullet;
public static Judge _judge;
public DiskFactory factory;
public IActionManager iactionmanager;
public RoundController roundcontroller;
public Director director;
public State state { get; set; }//游戏状态
//计时器, 每关60秒倒计时
public int totalTime;
public int leftTime;
public bool onCountDown = false;
public string countDownTitle = "Start";
public int count; //用来计数,每4秒自动发射一次飞碟
public int mode;//用于轮流选择使用普通动作还是物理运动,1是普通,2是物理
public static List<GameObject> usedDisk;
void Awake()
{
director = Director.getInstance();
director.setFPS(60);
state = State.PAUSE;
factory = DiskFactory.getInstance();
roundcontroller = Singleton<RoundController>.Instance;
_judge = Singleton<Judge>.Instance;
iactionmanager = Singleton<IActionManager>.Instance;
usedDisk = new List<GameObject>();
totalTime = 60;
countDownTitle = "Start";
leftTime = totalTime;
count = leftTime;
}
void Start()
{
Debug.Log("SceneController");
loadScene();
mode = 1;
}
void Update()
{
if(roundcontroller.getRound() == 3)
{
state = State.WIN;
}
if(leftTime == 0 && _judge.getPoint() < 100)
{
state = State.LOSE;
}
EmitDisk();
shoot();
RecycleDisk();
}
//********************飞碟的调度*****************
//协程 倒计时
public IEnumerator WaitFourSecond()
{
while (leftTime > 0)
{
yield return new WaitForSeconds(1f);
leftTime--;
}
}
//设置飞碟属性
public void setting(Color color, Vector3 scale, int diskNum)
{
_color = color;
_scale = scale;
_diskNum = diskNum;
}
//飞碟发射
public void EmitDisk()
{
if (count - leftTime == 2)
{
count = leftTime;
for(int i = 0; i < _diskNum; i++)//第一关,发射一个飞碟每次;第二关发射两个飞碟每次
{
GameObject disk = factory.getDisk();//从飞碟工厂得到飞碟
Debug.Log(disk);
usedDisk.Add(disk);//飞碟进入场景
disk.SetActive(true);
disk.GetComponent<Renderer>().material.color = _color;
disk.transform.localScale = _scale;
if (mode == 1)
iactionmanager.Normalplay(disk);//让普通动作管理者设计轨迹
else
iactionmanager.Physicplay(disk);//物理运动管理者设计
}
mode = 3 - mode;//更换下一次运动模式
}
}
//飞碟回收
//只回收落地飞碟
//利用事件触发解决
//在飞碟的脚本里实现具体回收
public void RecycleDisk()//检查需不需要回收飞碟
{
for (int i = 0; i < usedDisk.Count; i++)
{
if (usedDisk[i].activeInHierarchy == false )
{
if (usedDisk[i].transform.position.y <= -2)
_judge.failAdisk();
usedDisk[i].GetComponent<Rigidbody>().useGravity = false;
factory.free(usedDisk[i]);//让飞碟工厂回收
usedDisk.Remove(usedDisk[i]);//移除
}
}
}
//用户在游戏状态为开始或者继续时,才能左键射击,返回射击的对象
public void shoot()
{
if (Input.GetMouseButtonDown(0) && (state == State.START || state == State.CONTINUE))
{
Debug.Log("shoot");
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit))
{
hit.transform.gameObject.SetActive(false);
_judge.scoreADisk();
}
}
}
//********************加载场景*********************
//加载场景
public void loadScene()
{
Instantiate(Resources.Load("Plane"), new Vector3(0, -4, 0), Quaternion.identity);
}
//********************游戏控制****************
//暂停
public void Pause()
{
state = State.PAUSE;
for (int i = 0; i < usedDisk.Count; i++)
{
usedDisk[i].SetActive(false);//暂停后飞碟不可见;通过DiskFactory实现
}
}
//继续
public void Resume()
{
state = State.CONTINUE;
for (int i = 0; i < usedDisk.Count; i++)
{
usedDisk[i].SetActive(true);//恢复后飞碟可见
}
}
//重新开始
public void Restart()
{
_judge.ResetPoint();//Judge实现,分数清零
roundcontroller.setRound(0);
roundcontroller.nextRound();
state = State.START;
}
public void setTime()
{
totalTime = 60;
countDownTitle = "Start";
leftTime = totalTime;
count = leftTime;
}
}
RoundController.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RoundController : MonoBehaviour
{
public SceneController scene;
int round;
// Use this for initialization
void Start()
{
scene = Singleton<SceneController>.Instance;
round = 0;
}
//下一个关卡
public void nextRound()
{
scene.setTime();
round++;
loadRound(round);
}
//设置关卡
public void setRound(int round)
{
this.round = round;
}
//获取关卡
public int getRound()
{
return round;
}
//转换关卡
public void loadRound(int round)
{
float r, g, b;
Color color;
Vector3 scale;
float d;//飞碟的直径
r = Random.Range(0f, 0.1f);
g = Random.Range(0f, 0.1f);
b = Random.Range(0f, 0.1f);
color = new Color(r, g, b);
d = Random.Range(2.1f, 3.0f);
scale = new Vector3(d, 0.01f, d);
switch (round)
{
case 1:
scene.setting(color, scale, 1);
break;
case 2:
scene.setting(color, scale, 2);
break;
}
}
}
Judge.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Judge : MonoBehaviour {
public int point;
public int oneDiskPoint;
private RoundController roundcontroller;
private SceneController scene;
void Awake()
{
point = 0;
oneDiskPoint = 10;
roundcontroller = Singleton<RoundController>.Instance;
}
//默认开始第一关
void Start()
{
roundcontroller.nextRound();
}
void Update()
{
if(point == 100)
{
roundcontroller.nextRound();
point = 0;
}
}
//*************************游戏分数***************
//击中飞碟得分
public void scoreADisk()
{
point += oneDiskPoint;
}
//掉落飞碟失去分数
public void failAdisk()
{
point -= oneDiskPoint;
}
//获取分数
public int getPoint()
{
return point;
}
//***********************游戏控制辅助函数***************
public void ResetPoint()
{
point = 0;
}
}
CCActionManager.cs
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<MoveToAction>();
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<SSAction> sequence;
public int repeat = -1; //-1表示无限循环,0表示只执行一遍,repeat> 0 表示重复repeat遍
public int currentAction = 0;//当前动作列表里,执行到的动作序号
public static SequenceAction getAction(int repeat, int currentActionIndex, List<SSAction> sequence)
{
SequenceAction action = ScriptableObject.CreateInstance<SequenceAction>();
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<SSAction> waitingToAdd = new List<SSAction>();
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 CCActionManager : SSActionManager, ISSActionCallback
{
public RoundController scene;
public MoveToAction action;
public SequenceAction saction;
float speed;
private GameObject temp;
public void playDisk(GameObject disk)
{
temp = disk;
//随机选取起始坐标
float x = Random.Range(-1.5f, 1.5f);
float z = Random.Range(-2f, 2f);
Vector3 initialPos = new Vector3(x, -3.7f, z);
disk.transform.position = initialPos;
//随机选取发射方向
float xEnd = Random.Range(-2f, 2f);
float yEnd = Random.Range(6f, 8f);
float zEnd = Random.Range(0f, 5f);
Vector3 dir = new Vector3(xEnd, yEnd, zEnd);
//随机设置速度
float speed = Random.Range(10f,50f);
action = MoveToAction.getAction(dir, speed);
RunAction(disk, action, this);
}
protected void Start()
{
Debug.Log("CCactionmanger");
}
protected new void Update()
{
base.Update();
}
public void actionDone(SSAction source)
{
if(source == action)
{
temp.GetComponent<Rigidbody>().useGravity = true;
}
}
}
PhysicActionManager.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PhysicActionManager : MonoBehaviour
{
public RoundController scene;
//public MoveToAction action1, action2;
//public SequenceAction saction;
private float speed = 5.0f;//初速
//private float Force = 0f; //风力
public void playDisk(GameObject disk)
{
//随机选取起始坐标
float x = Random.Range(-1.5f, 1.5f);
float z = Random.Range(0f, 0.5f);
Vector3 initialPos = new Vector3(x, -3.7f, z);
disk.transform.position = initialPos;
//随机选取发射方向
float xEnd = Random.Range(-2f, 2f);
float yEnd = Random.Range(6f, 8f);
float zEnd = Random.Range(0f, 5f);
Vector3 dir = new Vector3(xEnd, yEnd, zEnd);
disk.transform.up = dir;
disk.GetComponent<Rigidbody>().velocity = dir * speed; //设置速度
disk.GetComponent<Rigidbody>().useGravity = true;
}
}
IActionManager.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class IActionManager : MonoBehaviour {
public PhysicActionManager physicaction;
public CCActionManager ccaction;
void Awake()
{
physicaction = Singleton<PhysicActionManager>.Instance;
ccaction = Singleton<CCActionManager>.Instance;
}
public void Normalplay(GameObject disk)
{
ccaction.playDisk(disk);
}
public void Physicplay(GameObject disk)
{
physicaction.playDisk(disk);
}
}
DiskFactory.cs
using System;
using System.Collections.Generic;
using UnityEngine;
/**
* 储存和free飞碟
* */
public class DiskFactory : System.Object {
private static DiskFactory _instance;
private static List<GameObject> unused;//飞碟队列
private static GameObject gameobject;
public static DiskFactory getInstance()
{
if (_instance == null)
{
_instance = new DiskFactory();
unused = new List<GameObject>();
}
return _instance;
}
//获取飞碟
public GameObject getDisk()
{
if (unused.Count == 0)
{
gameobject = GameObject.Instantiate(Resources.Load("Cylinder"), new Vector3(0, 0, 0), Quaternion.identity) as GameObject;
gameobject.transform.Rotate(80f, 0, 0);
return gameobject;
}
else
return unused[0];
}
//free飞碟
public void free(GameObject obj)
{
unused.Add(obj);
}
}
Director.cs
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Director : System.Object
{
public static Director _instance;
public IUserAction currentScenceController { get; set; }
public bool running { get; set; }
public static Director getInstance()
{
if (_instance == null)
{
_instance = new Director();
}
return _instance;
}
public int getFPS()
{
return Application.targetFrameRate;
}
public void setFPS(int fps)
{
Application.targetFrameRate = fps;
}
以上就是本次游戏的所有内容,希望对大家有所帮助。