HW7 模型与动画

智能巡逻兵

  • 游戏设计要求:

    • 创建一个地图和若干巡逻兵(使用动画);
    • 每个巡逻兵走一个3~5个边的凸多边型,位置数据是相对地址。即每次确定下一个目标位置,用自己当前位置为原点计算;
    • 巡逻兵碰撞到障碍物,则会自动选下一个点为目标;
    • 巡逻兵在设定范围内感知到玩家,会自动追击玩家;
    • 失去玩家目标后,继续巡逻;
    • 计分:玩家每次甩掉一个巡逻兵计一分,与巡逻兵碰撞游戏结束;
  • 程序设计要求:

    • 必须使用订阅与发布模式传消息

    • 工厂模式生产巡逻兵

实验内容:

  1. 订阅与发布模式,图示如下:

    在这里插入图片描述

    • 在现在的发布订阅模式中,称为发布者的消息发送者不会将消息直接发送给订阅者,这意味着发布者和订阅者不知道彼此的存在。在发布者和订阅者之间存在第三个组件,称为消息代理或调度中心或中间件,它维持着发布者和订阅者之间的联系,过滤所有发布者传入的消息并相应地分发它们给订阅者。

    • 在本次作业中,玩家作为消息发布者,将自己的相关信息(如:生存状态,位置,所在区域)发送给订阅者,订阅者根据自己的需求,从所有信息中得到自己想要的部分(如:场景控制器判断游戏是否结束,巡逻兵判断是否在自己所在区域,是否追击),并根据信息判断决定下一步动作。
      游戏设计过程中,订阅者有巡逻兵:根据玩家所在区域判断是否追击、根据玩家状态判断是否追击、根据玩家位置判断追击方向;场景控制器:根据玩家状态判断游戏是否结束;记分员:根据玩家所在区域判断是否得分。

  2. 工厂模式:
    与之前的打飞碟游戏的工厂模式生成飞碟类似,不再做过多解释。

  3. 主要代码实现如下:

    • 巡逻兵控制器PatrolController.cs:

      实现巡逻兵对Hero的感知和追踪、在游戏场景中碰到障碍物墙壁改变方向以及追捕到Hero时判定游戏结束。

      检测巡逻兵所处的位置变量的函数值,若与Hero相同,则触发追赶玩家的条件,巡逻兵追击Hero;对于直接接触的碰撞,游戏结束。代码如下:

      using System.Collections;
      using System.Collections.Generic;
      using UnityEngine;
      
      public class PatrolController : MonoBehaviour {
      	public SceneController sceneController;
      	private Animator animator;
      
      	void Start() {
      		animator = GetComponent<Animator> ();
      		sceneController = (SceneController)SSDirector.getInstance ().currentSceneController;
      	}
      
      	void FixedUpdate() {
      		if (sceneController.hero.GetComponent<MoveData> ().planeNum == GetComponent<MoveData> ().planeNum && !sceneController.gameOver) {
      			sceneController.actionManager.CatchRunAction (gameObject, sceneController.hero);
      		} else {
      			if (!shouldChangeState ())
      				sceneController.actionManager.MoveRunAction (gameObject, gameObject.GetComponent<MoveData> ().state);
      			else {
      				
      				sceneController.actionManager.MoveRunAction (gameObject, (gameObject.GetComponent<MoveData> ().state + 3) % 4);
      			}
      		}
      		animator.SetBool ("run", true);
      	}
      
      	bool shouldChangeState() {
      		if (gameObject.transform.localPosition.x >= 4) {
      			gameObject.transform.localPosition = new Vector3 (3.9f, gameObject.transform.localPosition.y, gameObject.transform.localPosition.z);
      			return true;
      		}
      		else if (gameObject.transform.localPosition.x <= -4) {
      			gameObject.transform.localPosition = new Vector3 (-3.9f, gameObject.transform.localPosition.y, gameObject.transform.localPosition.z);
      			return true;
      		}
      		else if (gameObject.transform.localPosition.z >= 4) {
      			gameObject.transform.localPosition = new Vector3 (gameObject.transform.localPosition.x, gameObject.transform.localPosition.y, 3.9f);
      			return true;
      		}
      		else if (gameObject.transform.localPosition.z <= -4) {
      			gameObject.transform.localPosition = new Vector3 (gameObject.transform.localPosition.x, gameObject.transform.localPosition.y, -3.9f);
      			return true;
      		}
      		return false;
      	}
      
      	void OnCollisionStay(Collision e) {
      		if (e.gameObject.Equals (sceneController.hero)) {
      			sceneController.GameOver ();
      		}
      	}
      }
      
    • 玩家控制器HeroController.cs:

      实现W、A、S、D键对玩家移动的控制,并使用scoreKeep作为发布者,实现信息的发送和传递,代码如下:

      using System.Collections;
      using System.Collections.Generic;
      using UnityEngine;
      
      public class HeroController : MonoBehaviour{
      	//发布与订阅模式
      	public delegate void ScoreKeep();
      	public static event ScoreKeep scoreKeep;
      
      	public SceneController sceneController;
      
      	private Animator animator;
      
      	void Start () {
      		animator = GetComponent<Animator> ();
      		sceneController = (SceneController)SSDirector.getInstance ().currentSceneController;
      	}
      
      	void FixedUpdate() {
      		if (scoreKeep != null) {
      			scoreKeep ();  //计分
      			
      			// 获取控制的方向
      			float KeyVertical = Input.GetAxis ("Vertical");
      			float KeyHorizontal = Input.GetAxis ("Horizontal");
      			if (KeyVertical == -1) {
      				sceneController.actionManager.MoveRunAction (gameObject, 2); // 下
      			} else if (KeyVertical == 1) {
      				sceneController.actionManager.MoveRunAction (gameObject, 0); // 上
      			}
      			if (KeyHorizontal == 1) {
      				sceneController.actionManager.MoveRunAction (gameObject, 1); // 右
      			} else if (KeyHorizontal == -1) {
      				sceneController.actionManager.MoveRunAction (gameObject, 3); // 左
      			}
      			if (KeyVertical == 0 && KeyHorizontal == 0) {
      				animator.SetBool ("run", false);
      			} else {
      				animator.SetBool ("run", true);
      			}
      		}
      	}
      }
      
    • Scorekeeper.cs:

      作为发布者,当玩家得分时,实现信息向调度中心的传递,代码如下:

      using System.Collections;
      using System.Collections.Generic;
      using UnityEngine;
      
      public class Scorekeeper {
      
      	public SceneController sceneController { get; set; }
      
      	private static Scorekeeper _instance;
      	public static Scorekeeper getInstance(){
      		if (_instance == null)
      			_instance = new Scorekeeper();
      		return _instance;
      	}
      
      	public int score = 0;
      
      	public void reset(){
      		score = 0;
      	}
      
      	//更改plane加分
      	public void judge() {
      		GameObject[] plane = sceneController.plane;
      		for (int i = 0; i < plane.Length; ++i) {
      			if (Vector3.Distance (plane [i].transform.position, sceneController.hero.transform.position) <= 2.8) {
      				if (i != sceneController.hero.GetComponent<MoveData> ().planeNum) {
      					sceneController.hero.GetComponent<MoveData> ().planeNum = i;
      					++score;
      					Debug.Log (score);
      				}
      				return;
      			}
      
      		}
      		//处于交界
      		sceneController.hero.GetComponent<MoveData>().planeNum = -1;
      	}
      }
      
    • CameraFlow.cs:

      实现玩家的游戏视角在Hero身上的绑定,提升游戏的交互体验,代码如下:

      using UnityEngine;
      using System.Collections;
      
      public class CameraFlow : MonoBehaviour {
      	public Transform target;
      	private Vector3 offset;
      	private Vector3 pos;
      	public SceneController sceneController;
      	public float speed = 2;
      
      	void Start() {
      		offset = target.position - this.transform.position;
      		sceneController = (SceneController)SSDirector.getInstance ().currentSceneController;
      	}
      
      	void FixedUpdate() {
      		if (sceneController.isGameOver ()) {
      			pos = target.position + new Vector3(0,20,0);
      			this.transform.position = Vector3.Lerp (this.transform.position, pos, speed * Time.deltaTime);//调整相机与玩家之间的距离
      			Quaternion angel = Quaternion.LookRotation (target.position - this.transform.position);//获取旋转角度
      			this.transform.rotation = Quaternion.Slerp (this.transform.rotation, angel, speed * Time.deltaTime);
      		} else
      			this.transform.position = target.position - offset;
      	}
      }
      
    • PatrolFactory.cs:

      以工厂的模式产生巡逻兵,代码如下:

      using System.Collections;
      using System.Collections.Generic;
      using UnityEngine;
      
      public class PatrolFactory : System.Object {
          private static PatrolFactory _instance;
          public SceneController sceneController { get; set; }
          public List<GameObject> used;
          public List<GameObject> free;
      
          public static PatrolFactory getInstance(){
              if (_instance == null) {
                  _instance = new PatrolFactory();
                  _instance.used = new List<GameObject>();
                  _instance.free = new List<GameObject>();
              }
              return _instance;
          }
      
          public GameObject getPatrol() {
      
              GameObject newPatrol;
              if (free.Count == 0)
      			newPatrol = GameObject.Instantiate(Resources.Load("prefabs/Patrol")) as GameObject;
              else {
                  newPatrol = free[0];
                  free.Remove(free[0]);
              }
      		newPatrol.SetActive(true);
              used.Add(newPatrol);
      		if (!newPatrol.GetComponent<Rigidbody> ()) {
      			newPatrol.AddComponent<Rigidbody> ();  
      		}
              return newPatrol;
          }
      
          public void freePatrol(GameObject g) {
              for (int i = 0; i < used.Count; i++) {
                  if (used[i] == g) {
                      used.Remove(g);
                      g.SetActive(false);
                      free.Add(g);
                  }
              }
          }
      
      	public void hideAll() {
      		for (int i = 0; i < used.Count; i++)
      			used [i].SetActive (false);
      		for (int i = 0; i < free.Count; i++)
      			free [i].SetActive (false);
      	}
      }
      
    • 场景管理和动作管理的实现在前几次作业中已做过详细分析和说明,所以本次实验不再赘述。

  4. 运行效果截图:

在这里插入图片描述

  1. 项目地址:
    https://gitee.com/liuhz5/game-unity/tree/master/hw7
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值