项目结构:
Control/Action/FollowAction
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FollowAction : SSAction
{
private float speed = 2f; //跟随玩家的速度
private GameObject player; //玩家
private PatrolData data; //侦查兵数据
private FollowAction() { }
public static FollowAction GetSSAction(GameObject player)
{
FollowAction action = CreateInstance();
action.player = player;
return action;
}
public override void Update()
{
//防止碰撞发生后的旋转
if (transform.localEulerAngles.x != 0 || transform.localEulerAngles.z != 0)
{
transform.localEulerAngles = new Vector3(0, transform.localEulerAngles.y, 0);
}
if (transform.position.y != 0)
{
transform.position = new Vector3(transform.position.x, 0, transform.position.z);
}
Follow();
//如果侦察兵没有跟随对象,或者需要跟随的玩家不在侦查兵的区域内
if (data.wallSign != data.sign)
{
this.destroy = true;
this.enable = false;
this.callback.SSActionEvent(this, 1, this.gameobject);
}
}
public override void Start()
{
data = this.gameobject.GetComponent();
}
void Follow()
{
this.transform.position = Vector3.MoveTowards(this.transform.position, player.transform.position, speed * Time.deltaTime);
this.transform.LookAt(player.transform.position);
}
}
Control/Action/PatrolAction
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PatrolAction : SSAction
{
private enum Dirction { EAST, NORTH, WEST, SOUTH };
private float posX, posZ; //移动前的初始x和z方向坐标
private float moveLength; //移动的长度
private float moveSpeed = 1.2f; //移动速度
private bool moveSign = true; //是否到达目的地
private Dirction dirction = Dirction.WEST; //移动的方向
private PatrolData data; //侦察兵的数据
private PatrolAction() { }
public static PatrolAction GetSSAction(Vector3 location)
{
PatrolAction action = CreateInstance();
action.posX = location.x;
action.posZ = location.z;
//设定移动矩形的边长
action.moveLength = Random.Range(3, 3);
return action;
}
public override void Start()
{
this.gameobject.GetComponent().enabled = true;
data = this.gameobject.GetComponent();
}
public override void Update()
{
//防止碰撞发生后的旋转
if (transform.localEulerAngles.x != 0 || transform.localEulerAngles.z != 0)
{
transform.localEulerAngles = new Vector3(0, transform.localEulerAngles.y, 0);
}
if (transform.position.y != 0)
{
transform.position = new Vector3(transform.position.x, 0, transform.position.z);
}
//如果侦察兵需要跟随玩家并且玩家就在侦察兵所在的区域,侦查动作结束
if (data.wallSign == data.sign) //由事件触发
{
this.destroy = true;
this.enable = false;
this.callback.SSActionEvent(this, 0, this.gameobject);
}
else
{
Patrol();
}
}
private void Patrol()
{
if (moveSign)
{
//不需要转向则设定一个目的地,按照矩形移动
switch (dirction)
{
case Dirction.EAST:
posX -= moveLength;
break;
case Dirction.NORTH:
posZ += moveLength;
break;
case Dirction.WEST:
posX += moveLength;
break;
case Dirction.SOUTH:
posZ -= moveLength;
break;
}
moveSign = false;
}
this.transform.LookAt(new Vector3(posX, 0, posZ));
float distance = Vector3.Distance(transform.position, new Vector3(posX, 0, posZ));
//当前位置与目的地距离浮点数的比较决定是否转向
if (distance > 0.9)
{
transform.position = Vector3.MoveTowards(this.transform.position, new Vector3(posX, 0, posZ), moveSpeed * Time.deltaTime);
}
else
{
dirction = dirction + 1;
if (dirction > Dirction.SOUTH)
{
dirction = Dirction.EAST;
}
moveSign = true;
}
}
}
Control/Action/SSAction
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SSAction : ScriptableObject //动作
{
public bool enable = true; //是否正在进行此动作
public bool destroy = false; //是否需要被销毁
public GameObject gameobject; //动作对象
public Transform transform; //动作对象的transform
public ISSActionCallback callback; //回调函数
protected SSAction() { } //保证SSAction不会被new
//子类可以使用下面的函数
public virtual void Start()
{
throw new System.NotImplementedException();
}
public virtual void Update()
{
throw new System.NotImplementedException();
}
public void Reset()
{
enable = false;
destroy = false;
gameobject = null;
transform = null;
callback = null;
}
}
Control/Action/SSActionManager
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SSActionManager : MonoBehaviour, ISSActionCallback
{
private Dictionary actions = new Dictionary(); //将执行的动作的字典集合
private List waitingAdd = new List(); //等待去执行的动作列表
private List waitingDelete = new List(); //等待删除的动作的key
protected void Update()
{
foreach (SSAction ac in waitingAdd)
{
actions[ac.GetInstanceID()] = ac;
}
waitingAdd.Clear();
foreach (KeyValuePair 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);
Destroy(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();
}
public void SSActionEvent(SSAction source, int intParam = 0, GameObject objectParam = null)
{
if (intParam == 0)
{
//侦查兵跟随玩家
FollowAction follow = FollowAction.GetSSAction(objectParam.gameObject.GetComponent().player);
this.RunAction(objectParam, follow, this);
}
else
{
//侦察兵按照初始位置开始继续巡逻
PatrolAction move = PatrolAction.GetSSAction(objectParam.gameObject.GetComponent().startPosition);
this.RunAction(objectParam, move, this);
//玩家逃脱
Singleton.Instance.Escape();
}
}
public void DestroyAll()
{
foreach (KeyValuePair kv in actions)
{
SSAction ac = kv.Value;
ac.destroy = true;
}
}
}
Control/Event/GameEventManager
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GameEventManager : MonoBehaviour
{
//分数变化
public delegate void ScoreEvent();
public static event ScoreEvent ScoreChange;
//游戏结束变化
public delegate void GameoverEvent();
public static event GameoverEvent GameoverChange;
//玩家逃脱
public void Escape()
{
if (ScoreChange != null)
{
ScoreChange(); //玩家逃脱则加一分
}
}
//玩家被捕
public void Gameover()
{
if (GameoverChange != null)
{
GameoverChange(); //玩家被捕即游戏结束
}
}
}
Control/Event/PlayerCollide
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerCollide : MonoBehaviour
{
void OnCollisionEnter(Collision other)
{
//当玩家与巡逻兵相撞
if (other.gameObject.tag == "Player")
{
//游戏结束,发布消息
Singleton.Instance.Gameover();
}
}
}
Control/FirstController
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FirstController : MonoBehaviour, IUserAction, ISceneController
{
public PatrolFactory patrolFactory; //巡逻者工厂
public ScoreRecorder scoreRecorder; //记分员
public PatrolActionManager actionManager; //动作管理器
public UserGUI userGUI; //用户图形界面
public GameObject player; //玩家
public List patrols; //巡逻兵队列
private GameState gameState; //游戏状态
private readonly float playerSpeed = 5; //玩家移动速度
void Start()
{
SSDirector director = SSDirector.GetInstance();
director.CurrentScenceController = this;
patrolFactory = Singleton.Instance;
actionManager = gameObject.AddComponent() as PatrolActionManager;
userGUI = gameObject.AddComponent() as UserGUI;
LoadResources();
scoreRecorder = Singleton.Instance;
}
void Update()
{
int wallSign = 0;
if (player.transform.position.x < -1 && player.transform.position.z > 1)
wallSign = 3;
if (player.transform.position.x > 1 && player.transform.position.z > 1)
wallSign = 1;
if (player.transform.position.x < -1 && player.transform.position.z < -1)
wallSign = 4;
if (player.transform.position.x > 1 && player.transform.position.z < -1)
wallSign = 2;
for (int i = 0; i < patrols.Count; i++)
{
patrols[i].gameObject.GetComponent().wallSign = wallSign;
}
if (wallSign != 0)
patrols[wallSign - 1].gameObject.GetComponent().player = player;
}
public void LoadResources()
{
Instantiate(Resources.Load("Prefabs/Plane"));
player = Instantiate(Resources.Load("Prefabs/player"), new Vector3(0, 0, -4), Quaternion.identity) as GameObject;
player.GetComponent().enabled = false;
patrols = patrolFactory.GetPatrols();
for (int i = 0; i < patrols.Count; i++)
{
patrols[i].GetComponent().enabled = false;
}
}
//用户接口
public void MovePlayer(float translationX, float translationZ)
{
//实际上移动玩家这个任务应该交给动作管理器完成,这里由于玩家动作简单,所以为了简化代码,就交给场景管理器代劳
if (translationX != 0 || translationZ != 0)
{
player.GetComponent().enabled = true;
if (translationZ > 0)
{
player.transform.localEulerAngles = new Vector3(0, 0, 0);
player.transform.Translate(0, 0, translationZ * playerSpeed * Time.deltaTime);
}
if (translationZ < 0)
{
player.transform.localEulerAngles = new Vector3(0, 180, 0);
player.transform.Translate(0, 0, -translationZ * playerSpeed * Time.deltaTime);
}
if (translationX > 0.45)
{
player.transform.localEulerAngles = new Vector3(0, 90, 0);
player.transform.Translate(0, 0, translationX * playerSpeed * Time.deltaTime);
}
if (translationX < -0.45)
{
player.transform.localEulerAngles = new Vector3(0, -90, 0);
player.transform.Translate(0, 0, -translationX * playerSpeed * Time.deltaTime);
}
}
else
{
player.GetComponent().enabled = false;
player.transform.localEulerAngles = new Vector3(0, player.transform.localEulerAngles.y, 0);
}
//防止碰撞带来的移动
if (player.transform.localEulerAngles.x != 0 || player.transform.localEulerAngles.z != 0)
{
player.transform.localEulerAngles = new Vector3(0, player.transform.localEulerAngles.y, 0);
}
if (player.transform.position.y != 0)
{
player.transform.position = new Vector3(player.transform.position.x, 0, player.transform.position.z);
}
}
public void GameStart()
{
for (int i = 0; i < patrols.Count; i++)
{
actionManager.Patrol(patrols[i]);
patrols[i].GetComponent().enabled = true;
}
}
public void Restart()
{
gameState = GameState.RUNNING;
scoreRecorder.Reset();
player.transform.position = new Vector3(0, 0, -4);
player.transform.localEulerAngles = new Vector3(0, 0, 0);
int[] pos_x = { 1, -4 };
int[] pos_z = { 4, -1 };
Vector3[] position = new Vector3[9];
int index = 0;
//生成不同的巡逻兵初始位置
for (int i = 0; i < 2; i++)
{
for (int j = 0; j < 2; j++)
{
position[index] = new Vector3(pos_x[i], 0, pos_z[j]);
index++;
}
}
for (int i = 0; i < patrols.Count; i++)
{
patrols[i].transform.position = position[i];
}
GameStart();
}
//订阅者模式
void OnEnable()
{
GameEventManager.ScoreChange += AddScore;
GameEventManager.GameoverChange += Gameover;
}
void OnDisable()
{
GameEventManager.ScoreChange -= AddScore;
GameEventManager.GameoverChange -= Gameover;
}
private void AddScore()
{
scoreRecorder.Add();
}
private void Gameover()
{
this.gameState = GameState.OVER;
player.GetComponent().enabled = false;
for (int i = 0; i < patrols.Count; i++)
{
patrols[i].GetComponent().enabled = false;
}
actionManager.DestroyAll();
}
public GameState GetGameState()
{
return gameState;
}
public int GetScore()
{
return scoreRecorder.score;
}
public void SetGameState(GameState gameState)
{
this.gameState = gameState;
}
}
Control/SSDirector
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SSDirector : System.Object
{
//singlton instance
private static SSDirector _instance;
public ISceneController CurrentScenceController { get; set; }
//get instance
public static SSDirector GetInstance()
{
if (_instance == null)
{
_instance = new SSDirector();
}
return _instance;
}
}
Model/PatrolFactory
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PatrolFactory : MonoBehaviour
{
private GameObject patrol = null; //巡逻兵
private List used = new List(); //正在被使用的巡逻兵,该游戏巡逻兵不需要回收,所以不需要free表
private Vector3[] position = new Vector3[9]; //保存每个巡逻兵的初始位置
public FirstController sceneControler; //场景控制器
public List GetPatrols()
{
int[] pos_x = { 1, -4};
int[] pos_z = { 4, -1};
int index = 0;
//生成不同的巡逻兵初始位置
for (int i = 0; i < 2; i++)
{
for (int j = 0; j < 2; j++)
{
position[index] = new Vector3(pos_x[i], 0, pos_z[j]);
index++;
}
}
for (int i = 0; i < 4; i++)
{
patrol = Instantiate(Resources.Load("Prefabs/zombie"));
patrol.transform.position = position[i];
patrol.AddComponent();
patrol.GetComponent().sign = i + 1;
patrol.GetComponent().startPosition = position[i];
used.Add(patrol);
}
return used;
}
public void StopPatrol()
{
//切换所有侦查兵的动画
for (int i = 0; i < used.Count; i++)
{
used[i].gameObject.GetComponent().SetBool("run", false);
}
}
}
Model/Singleton
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Singleton : 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 needed in the scene, but there is none.");
}
}
return instance;
}
}
}
效果展示: