对于这次的作业,想要弄得好,好看的人物模型自然是必不可少的。直接从unitystore里下载相关的资源。
巡逻兵设计
设计回调函数,用来决定下一个动作做什么
public void SSEventAction(SSAction source,SSActionEventType events=SSActionEventType.COMPLETED,int intParam=0,string strParam=null,Object objParam = null) {
// 改变当前状态
currentState = currentState > ActionState.WALKBACK ? ActionState.IDLE : (ActionState)((int)currentState + 1);
// 执行下个动作
switch (currentState) {
case ActionState.WALKLEFT:
walkLeft();
break;
case ActionState.WALKRIGHT:
walkRight();
break;
case ActionState.WALKFORWARD:
walkForward();
break;
case ActionState.WALKBACK:
walkBack();
break;
default:
idle();
break;
}
}
turnNextDirection() 函数用使物体当碰到墙壁或者出了巡逻区域后,往反方向走。
public void turnNextDirection() {
// 销毁当前动作
currentAction.destory = true;
// 往相反方向走
switch (currentState) {
case ActionState.WALKLEFT:
currentState = ActionState.WALKRIGHT;
walkRight();
break;
case ActionState.WALKRIGHT:
currentState = ActionState.WALKLEFT;
walkLeft();
break;
case ActionState.WALKFORWARD:
currentState = ActionState.WALKBACK;
walkBack();
break;
case ActionState.WALKBACK:
currentState = ActionState.WALKFORWARD;
walkForward();
break;
}
}
本次设计的主要是使用了发布订阅模式。
这个模式的用法就是当在玩家身上遇到了一些事件时,比如在进入怪物攻击范围是可以通过trigger的实例发布相应的消息,而根据相应回复的消息内容,就如微信公众号一般,有对该公众号感兴趣的订阅者,就会做出相应的回应动作,比如怪物回去追击玩家,加分。
下面是相应的代码
ActorController.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(Animator))]
[RequireComponent(typeof(Rigidbody))]
[RequireComponent(typeof(CapsuleCollider))]
public class ActorController : MonoBehaviour {
private Animator ani;
private AnimatorStateInfo currentBaseState;
private Rigidbody rig;
private Vector3 velocity;
// 旋转速度,奔跑速度
private float rotateSpeed = 15f;
private float runSpeed = 5f;
void Start () {
ani = GetComponent<Animator>();
rig = GetComponent<Rigidbody>();
}
void FixedUpdate() {
//如果小姐姐死亡,不执行所有动作
if (!ani.GetBool("isAlive")) return;
float x = Input.GetAxis("Horizontal");
float z = Input.GetAxis("Vertical");
//设置速度,用来判断做哪个动作
ani.SetFloat("Speed", Mathf.Max(Mathf.Abs(x), Mathf.Abs(z)));
//跑步时候的动画速度
ani.speed = 1 + ani.GetFloat("Speed") / 3;
velocity = new Vector3(x, 0, z);
if (x != 0 || z != 0) {
Quaternion rotation = Quaternion.LookRotation(velocity);
//ani正面的方向就是z轴正方向
//如果需要转向,球形插值转向
if (transform.rotation != rotation) transform.rotation = Quaternion.Slerp(transform.rotation, rotation, Time.fixedDeltaTime * rotateSpeed);
}
//小姐姐位置移动
transform.position += velocity * Time.fixedDeltaTime * runSpeed;
}
// 用于检测Actor进入某个区域
private void OnTriggerEnter(Collider other) {
if (other.gameObject.CompareTag("Area")) {
//进入区域后,发布消息
Publish publish = Publisher.getInstance();
int patrolType = other.gameObject.name[other.gameObject.name.Length - 1] - '0';
publish.notify(ActorState.ENTER_AREA, patrolType, this.gameObject);
}
}
/// 用于检测Actor与Patrol碰撞后死亡
private void OnCollisionEnter(Collision collision) {
if (collision.gameObject.CompareTag("guawu") && ani.GetBool("isAlive")) {
Debug.Log("death");
//调整相应条件以满足状态转移,执行死亡动作
ani.SetBool("isAlive", false);
ani.SetTrigger("toDie");
//碰撞后,发布死亡信息
Publisher publisher = Publisher.getInstance();
publisher.notify(ActorState.DEATH, 0, null);
}
}
}
BaseAction.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Tem.Action {
public enum SSActionEventType : int { STARTED, COMPLETED }
public interface ISSActionCallback {
void SSEventAction(SSAction source, SSActionEventType events = SSActionEventType.COMPLETED,
int intParam = 0, string strParam = null, Object objParam = null);
}
public class SSAction : ScriptableObject {
public bool enable = true;
public bool destory = false;
public GameObject gameObject { get; set; }
public Transform transform { get; set; }
public ISSActionCallback callback { get; set; }
public virtual void Start() {
throw new System.NotImplementedException("Action Start Error!");
}
public virtual void FixedUpdate() {
throw new System.NotImplementedException("Physics Action Start Error!");
}
public virtual void Update() {
throw new System.NotImplementedException("Action Update Error!");
}
}
public class CCSequenceAction : SSAction, ISSActionCallback {
public List<SSAction> sequence;
public int repeat = -1;
public int start = 0;
public static CCSequenceAction GetSSAction(List<SSAction> _sequence,int _start = 0,int _repeat = 1) {
CCSequenceAction actions = ScriptableObject.CreateInstance<CCSequenceAction>();
actions.sequence = _sequence;
actions.start = _start;
actions.repeat = _repeat;
return actions;
}
public override void Start() {
foreach (SSAction ac in sequence) {
ac.gameObject = this.gameObject;
ac.transform = this.transform;
ac.callback = this;
ac.Start();
}
}
public override void Update() {
if (sequence.Count == 0) return;
if (start < sequence.Count) sequence[start].Update();
}
public void SSEventAction(SSAction source, SSActionEventType events = SSActionEventType.COMPLETED,
int intParam = 0, string strParam = null, Object objParam = null) //通过对callback函数的调用执行下个动作
{
source.destory = false;
this.start++;
if (this.start >= this.sequence.Count) {
this.start = 0;
if (this.repeat > 0) this.repeat--;
if (this.repeat == 0) {
this.destory = true;
this.callback.SSEventAction(this);
}
}
}
private void OnDestroy() {
this.destory = true;
}
}
public class IdleAction : SSAction {
// 站立持续时间
private float time;
private Animator ani;
public static IdleAction GetIdleAction(float time,Animator ani) {
IdleAction currentAction = ScriptableObject.CreateInstance<IdleAction>();
currentAction.time = time;
currentAction.ani = ani;
return currentAction;
}
public override void Start() {
// 进入站立状态
ani.SetFloat("Speed", 0);
}
public override void Update() {
// 永久站立
if (time == -1) return;
// 减去站立时间
time -= Time.deltaTime;
if (time < 0) {
this.destory = true;
this.callback.SSEventAction(this);
}
}
}
public class WalkAction : SSAction {
// 移动速度和目标地点
private float speed;
private Vector3 target;
private Animator ani;
public static WalkAction GetWalkAction(Vector3 target,float speed,Animator ani) {
WalkAction currentAction = ScriptableObject.CreateInstance<WalkAction>();
currentAction.speed = speed;
currentAction.target = target;
currentAction.ani = ani;
return currentAction;
}
public override void Start() {
// 进入走路状态
//Debug.Log("walk");
ani.SetFloat("Speed", 0.5f);
}
public override void Update() {
Quaternion rotation = Quaternion.LookRotation(target - transform.position);
// 进行转向,转向目标方向
if (transform.rotation != rotation) transform.rotation = Quaternion.Slerp(transform.rotation, rotation, Time.deltaTime * speed * 5);
//沿着直线方向走到目标位置
this.transform.position = Vector3.MoveTowards(this.transform.position, target, speed * Time.deltaTime);
if (this.transform.position == target) {
this.destory = true;
this.callback.SSEventAction(this);
}
}
}
public class RunAction : SSAction {
// 移动速度和人物的transform
private float speed;
private Transform target;
private Animator ani;
public static RunAction GetRunAction(Transform target,float speed,Animator ani) {
RunAction currentAction = ScriptableObject.CreateInstance<RunAction>();
currentAction.speed = speed;
currentAction.target = target;
currentAction.ani = ani;
return currentAction;
}
public override void Start() {
// 进入跑步状态
ani.SetFloat("Speed", 1);
}
public override void Update() {
Quaternion rotation = Quaternion.LookRotation(target.position - transform.position);
// 进行转向,转向目标方向
if (transform.rotation != rotation) transform.rotation = Quaternion.Slerp(transform.rotation, rotation, Time.deltaTime * speed * 5);
this.transform.position = Vector3.MoveTowards(this.transform.position, target.position, speed * Time.deltaTime);
if (Vector3.Distance(this.transform.position, target.position) < 0.5) {
this.destory = true;
this.callback.SSEventAction(this);
}
}
}
public class SSActionManager : MonoBehaviour {
private Dictionary<int, SSAction> dictionary = new Dictionary<int, SSAction>();
private List<SSAction> waitingAddAction = new List<SSAction>();
private List<int> waitingDelete = new List<int>();
protected void Start() {
}
protected void Update() {
// 将待加入动作加入dictionary执行
foreach (SSAction ac in waitingAddAction) dictionary[ac.GetInstanceID()] = ac;
waitingAddAction.Clear();
// 判断waitingList中的动作对是否执行,如果要删除,加入要删除的list,否则更新
foreach (KeyValuePair<int,SSAction> dic in dictionary) {
SSAction ac = dic.Value;
if (ac.destory) waitingDelete.Add(ac.GetInstanceID());
else if (ac.enable) ac.Update();
}
foreach (int id in waitingDelete) {
SSAction ac = dictionary[id];
dictionary.Remove(id);
DestroyObject(ac);
}
// 将deletelist中的动作删除
waitingDelete.Clear();
}
public void runAction(GameObject gameObject,SSAction action,ISSActionCallback callback) {
action.gameObject = gameObject;
action.transform = gameObject.transform;
action.callback = callback;
waitingAddAction.Add(action);
action.Start();
}
}
public class PYActionManager : MonoBehaviour {
private Dictionary<int, SSAction> dictionary = new Dictionary<int, SSAction>();
private List<SSAction> waitingAddAction = new List<SSAction>();
private List<int> waitingDelete = new List<int>();
protected void Start() {
}
protected void FixedUpdate() {
// 将待加入动作加入dictionary执行
foreach (SSAction ac in waitingAddAction) dictionary[ac.GetInstanceID()] = ac;
waitingAddAction.Clear();
// 如果要删除,加入要删除的list,否则更新
foreach (KeyValuePair<int, SSAction> dic in dictionary) {
SSAction ac = dic.Value;
if (ac.destory) waitingDelete.Add(ac.GetInstanceID());
else if (ac.enable) ac.FixedUpdate();
}
// 将deletelist中的动作删除
foreach (int id in waitingDelete) {
SSAction ac = dictionary[id];
dictionary.Remove(id);
DestroyObject(ac);
}
waitingDelete.Clear();
}
public void runAction(GameObject gameObject, SSAction action, ISSActionCallback callback) {
action.gameObject = gameObject;
action.transform = gameObject.transform;
action.callback = callback;
waitingAddAction.Add(action);
action.Start();
}
}
}
ObjectFactory.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ObjectFactory : MonoBehaviour {
// 正在使用的对象链表
private static List<GameObject> used = new List<GameObject>();
// 正在空闲的对象链表
private static List<GameObject> free = new List<GameObject>();
public GameObject setObjectOnPos(Vector3 targetposition, Quaternion faceposition) {
if (free.Count==0) {
// 新建实例,将位置设置成为targetposition,将面向方向设置成faceposition
GameObject aGameObject = Instantiate(Resources.Load("prefabs/guawu"), targetposition, faceposition) as GameObject;
used.Add(aGameObject);
}
else {
used.Add(free[0]);
free.RemoveAt(0);
used[used.Count - 1].SetActive(true);
used[used.Count - 1].transform.position = targetposition;
used[used.Count - 1].transform.localRotation = faceposition;
}
return used[used.Count - 1];
}
public void freeObject(GameObject obj) {
obj.SetActive(false);
used.Remove(obj);
free.Add(obj);
}
}
PublisherAndObservser.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public enum ActorState { ENTER_AREA, DEATH }
public interface Publish {
// 发布函数
void notify(ActorState state, int pos, GameObject actor);
// 委托添加事件
void add(Observer observer);
// 委托取消事件
void delete(Observer observer);
}
public interface Observer {
//实现接收函数
void notified(ActorState state, int pos, GameObject actor);
}
public class Publisher : Publish {
private delegate void ActionUpdate(ActorState state, int pos, GameObject Actor);
private ActionUpdate updatelist;
private static Publisher _instance;
public static Publisher getInstance() {
if (_instance == null) _instance = new Publisher();
return _instance;
}
public void notify(ActorState state, int pos, GameObject actor) {
if (updatelist != null) updatelist(state, pos, actor);
}
public void add(Observer observer) {
updatelist += observer.notified;
}
public void delete(Observer observer) {
updatelist -= observer.notified;
}
}
PatrolUI.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Tem.Action;
[RequireComponent(typeof(Animator))]
[RequireComponent(typeof(CapsuleCollider))]
[RequireComponent(typeof(Rigidbody))]
public class PatrolUI : SSActionManager, ISSActionCallback, Observer {
// 各种动作
public enum ActionState : int { IDLE, WALKLEFT, WALKFORWARD, WALKRIGHT, WALKBACK }
// 动作
private Animator ani;
// 保证当前只有一个动作
private SSAction currentAction;
private ActionState currentState;
// 跑步和走路的速度
private const float walkSpeed = 1f;
private const float runSpeed = 3f;
// Use this for initialization
new void Start () {
ani = this.gameObject.GetComponent<Animator>();
// 添加巡逻兵的事件
Publish publisher = Publisher.getInstance();
publisher.add(this);
// 开始时,静止状态
currentState = ActionState.IDLE;
idle();
}
// Update is called once per frame
new void Update () {
base.Update();
}
//回调函数,用来决定下一个动作做什么
public void SSEventAction(SSAction source,SSActionEventType events=SSActionEventType.COMPLETED,int intParam=0,string strParam=null,Object objParam = null) {
// 改变当前状态
currentState = currentState > ActionState.WALKBACK ? ActionState.IDLE : (ActionState)((int)currentState + 1);
// 执行下个动作
switch (currentState) {
case ActionState.WALKLEFT:
walkLeft();
break;
case ActionState.WALKRIGHT:
walkRight();
break;
case ActionState.WALKFORWARD:
walkForward();
break;
case ActionState.WALKBACK:
walkBack();
break;
default:
idle();
break;
}
}
public void idle() {
currentAction = IdleAction.GetIdleAction(Random.Range(1, 1.5f), ani);
this.runAction(this.gameObject, currentAction, this);
}
public void walkLeft() {
Vector3 target = Vector3.left * Random.Range(3, 5) + this.transform.position;
currentAction = WalkAction.GetWalkAction(target, walkSpeed, ani);
this.runAction(this.gameObject, currentAction, this);
}
public void walkRight() {
Vector3 target = Vector3.right * Random.Range(3, 5) + this.transform.position;
currentAction = WalkAction.GetWalkAction(target, walkSpeed, ani);
this.runAction(this.gameObject, currentAction, this);
}
public void walkForward() {
Vector3 target = Vector3.forward * Random.Range(3, 5) + this.transform.position;
currentAction = WalkAction.GetWalkAction(target, walkSpeed, ani);
this.runAction(this.gameObject, currentAction, this);
}
public void walkBack() {
Vector3 target = Vector3.back * Random.Range(3, 5) + this.transform.position;
currentAction = WalkAction.GetWalkAction(target, walkSpeed, ani);
this.runAction(this.gameObject, currentAction, this);
}
//当碰到墙壁或者出了巡逻区域后,向反方向走
public void turnNextDirection() {
// 销毁当前动作
currentAction.destory = true;
// 往相反方向走
switch (currentState) {
case ActionState.WALKLEFT:
currentState = ActionState.WALKRIGHT;
walkRight();
break;
case ActionState.WALKRIGHT:
currentState = ActionState.WALKLEFT;
walkLeft();
break;
case ActionState.WALKFORWARD:
currentState = ActionState.WALKBACK;
walkBack();
break;
case ActionState.WALKBACK:
currentState = ActionState.WALKFORWARD;
walkForward();
break;
}
}
public void getGoal(GameObject gameobject) {
currentAction.destory = true;
currentAction = RunAction.GetRunAction(gameobject.transform, runSpeed, ani);
this.runAction(this.gameObject, currentAction, this);
}
public void loseGoal() {
currentAction.destory = true;
idle();
}
public void stop() {
currentAction.destory = true;
currentAction = IdleAction.GetIdleAction(-1f, ani);
this.runAction(this.gameObject, currentAction, this);
}
private void OnCollisionEnter(Collision collision) {
Transform parent = collision.gameObject.transform.parent;
if (parent != null && parent.CompareTag("Wall")) turnNextDirection();
}
private void OnTriggerEnter(Collider other) {
// 走出巡逻区域
if (other.gameObject.CompareTag("Door")) turnNextDirection();
}
// name="state" 角色状态
// name="pos" 角色所在区域
// name="actor" 角色
public void notified(ActorState state, int pos, GameObject actor) {
if (state == ActorState.ENTER_AREA) {
// 如果进入自己的区域,进行追击
// 如果离开自己的区域,放弃追击
if (pos == this.gameObject.name[this.gameObject.name.Length - 1] - '0')
getGoal(actor);
else loseGoal();
}
else stop();
// 角色死亡,结束动作
}
}
SceneController.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class SceneController : MonoBehaviour, Observer {
public Text scoreText;
public Text centerText;
private ScoreRecorder record;
private UIController UI;
private ObjectFactory fac;
private float[] posx = { -5, 7, -5, 5 };
private float[] posz = { -5, -7, 5, 5 };
void Start () {
record = new ScoreRecorder();
record.scoreText = scoreText;
UI = new UIController();
UI.centerText = centerText;
fac = Singleton<ObjectFactory>.Instance;
//订阅者添加事件
Publish publisher = Publisher.getInstance();
publisher.add(this);
LoadResources();
}
private void LoadResources() {
Instantiate(Resources.Load("prefabs/player1"), new Vector3(2, 0, -2), Quaternion.Euler(new Vector3(0, 180, 0)));
for (int i = 0; i < 4; i++) {
GameObject patrol = fac.setObjectOnPos(new Vector3(posx[i], 0, posz[i]), Quaternion.Euler(new Vector3(0, 180, 0)));
patrol.name = "Patrol" + (i + 1);
}
}
public void notified(ActorState state, int pos, GameObject actor) {
if (state == ActorState.ENTER_AREA) record.addScore(1);
else {
UI.loseGame();
Debug.Log("lose game");
}
}
}
ScoreRecorder.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class ScoreRecorder {
// 计分板
public Text scoreText;
// 纪录分数
private int score = -1;
public void resetScore() {
score = -1;
}
//走入新场景加分
public void addScore(int addscore) {
score += addscore;
scoreText.text = "Score:" + score;
}
public void setDisActive() {
scoreText.text = "";
}
public void setActive() {
scoreText.text = "Score:" + score;
}
}
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 needed in the scene, but it not!");
}
}
return instance;
}
}
}
UIController.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class UIController {
public Text centerText;
public void loseGame() {
centerText.text = "LOSE!";
}
public void resetGame() {
centerText.text = "";
}
}