Unity3D homework_7
智能巡逻兵
一、要求
1、设计
- 创建一个地图和若干巡逻兵(使用动画);
- 每个巡逻兵走一个3~5个边的凸多边形,位置数据是相对地址。即每次确定下一个目标位置,用自己当前位置为原点计算;
- 巡逻兵碰撞到障碍物,则会自动选择下一个点为目标;
- 巡逻兵在设定范围内感知到玩家,会自动追击玩家;
- 失去玩家目标之后,继续巡逻;
- 计分:玩家每次甩掉一个巡逻兵计一分,与巡逻兵碰撞游戏结束
2、 程序设计要求
- 必须使用订阅与发布模式传递消息
- subject:OnLostGoal
- Publisher:
- Subscriber:
- 工厂模式生产巡逻兵
- 友善提示1:生成3~5个边的凸多边形
- 随机生成矩形
- 在每个矩形上面随机找点,可以找到3-4的凸多边形
- 5 ?
二、制作
1、 动画状态机
-
将所使用的动画都拖入人物动画器的Base Layer中,实现人物动作:
-
按WASD进行行走和转向
-
站立不动时按下空格后跳一小步,行走时按下空格向前翻滚
-
奔跑时按下空格向前跳跃,在空中跳跃完成后下落,下落到地面时翻滚
-
按SHIFT奔跑
人物模型使用的是YBoy的
2、Animator的状态图
3、ground动画混合树
ground由站立,走路,奔跑三个动画组成,将这三个动画拖进来。
修改混合树名字和参数名字,在Motion中新增三个状态,并拖入如图所示的三个动画,并调整阈值。拖动红色标尺能看到动画的渐变。
创建过渡与参数控制
右键->创建过渡,将所有状态都连接起来。状态之间的过渡通过参数来控制。
使用的所有参数如下:
forward为Float,用于控制ground混合树中行走奔跑的过渡;
OnGround为Bool,表示模型是否在地面上;
jump为Trigger,用于控制跳跃和后跳;
roll为Trigger,用于控制翻滚;
jabVelocity为Float,表示后跳的速度;
rollVelocity为Float,表示翻滚的速度。
下面是一个从ground到jump的过渡例子:
还有更多细节就不一一赘述了
三、脚本
1、脚本结构
分为四个部分,分别管理动作,场景,守卫和玩家
我们挑重要的部分讲
2、守卫追人范围实现GuardActionManager
当每个圈圈的范围的intParam=0的时候,就开始追人。如果是其他值就继续安装原来的路径走。
public class GuardActionManager : SSActionManager, ISSActionCallback {
private GuardPatrolAction patrol;
private GameObject player;
public void GuardPatrol(GameObject guard, GameObject _player) {
player = _player;
patrol = GuardPatrolAction.GetSSAction(guard.transform.position);
this.RunAction(guard, patrol, this);
}
public void SSActionEvent(
SSAction source, SSActionEventType events = SSActionEventType.Competeted,
int intParam = 0, GameObject objectParam = null) {
if (intParam == 0) {
//追逐
GuardFollowAction follow = GuardFollowAction.GetSSAction(player);
this.RunAction(objectParam, follow, this);
} else {
//巡逻
GuardPatrolAction move = GuardPatrolAction.GetSSAction(objectParam.gameObject.GetComponent<GuardData>().start_position);
this.RunAction(objectParam, move, this);
Singleton<GameEventManager>.Instance.PlayerEscape();
}
}
}
3、
追人启停机制,当脱离该区域时候,便继续巡逻。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GuardFollowAction : SSAction {
private GameObject player;
private GuardData data;
private Animator anim;
private Rigidbody rigid;
private Vector3 planarVec; // 平面移动向量
private float speed;
private GuardFollowAction() {}
public override void Start() {
data = gameobject.GetComponent<GuardData>();
anim = gameobject.GetComponent<Animator>();
rigid = gameobject.GetComponent<Rigidbody>();
speed = data.runSpeed;
anim.SetFloat("forward", 2.0f);
}
public static GuardFollowAction GetSSAction(GameObject player) {
GuardFollowAction action = CreateInstance<GuardFollowAction>();
action.player = player;
return action;
}
public override void Update() {
//保留供物理引擎调用
planarVec = gameobject.transform.forward * speed;
}
public override void FixedUpdate() {
transform.LookAt(player.transform.position);
rigid.velocity = new Vector3(planarVec.x, rigid.velocity.y, planarVec.z);
//如果玩家脱离该区域则继续巡逻
if (data.playerSign != data.sign) {
this.destroy = true;
this.callback.SSActionEvent(this, SSActionEventType.Competeted, 1, this.gameobject);
}
}
}
4、玩家控制
可以设置行走的速度,并且按下空格,自动播放跳跃或者翻滚的动画,并且还可以按下shift加速。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ActorController : MonoBehaviour {
public GameObject model;
public PlayerInput pi;
public float walkSpeed = 1.5f;
public float runMultiplier = 2.7f;
public float jumpVelocity = 4f;
public float rollVelocity = 1f;
[SerializeField]
private Animator anim;
private Rigidbody rigid;
private Vector3 planarVec; // 平面移动向量
private Vector3 thrustVec; // 跳跃冲量
private bool lockPlanar = false; // 跳跃时锁死平面移动向量
void Awake() {
pi = GetComponent<PlayerInput>();
anim = model.GetComponent<Animator>();
rigid = GetComponent<Rigidbody>();
}
//刷新每秒60次
void Update() {
//修改动画混合树
/*1.从走路到跑步没有过渡*/
/*anim.SetFloat("forward", pi.Dmag * (pi.run ? 2.0f : 1.0f));*/
/*2.使用Lerp加权平均解决*/
float targetRunMulti = pi.run ? 2.0f : 1.0f;
anim.SetFloat("forward", pi.Dmag * Mathf.Lerp(anim.GetFloat("forward"), targetRunMulti, 0.3f));
//播放翻滚动画
if (rigid.velocity.magnitude > 1.0f) {
anim.SetTrigger("roll");
}
//播放跳跃动画
if (pi.jump) {
anim.SetTrigger("jump");
}
//转向
if(pi.Dmag > 0.01f) {
/*1.旋转太快没有补帧*/
/*model.transform.forward = pi.Dvec;*/
/*2.使用Slerp内插值解决*/
Vector3 targetForward = Vector3.Slerp(model.transform.forward, pi.Dvec, 0.2f);
model.transform.forward = targetForward;
}
if(!lockPlanar) {
//保存供物理引擎使用
planarVec = pi.Dmag * model.transform.forward * walkSpeed * (pi.run ? runMultiplier : 1.0f);
}
}
//物理引擎每秒50次
private void FixedUpdate() {
//Time.fixedDeltaTime 50/s
//1.修改位置
//rigid.position += movingVec * Time.fixedDeltaTime;
//2.修改速度
rigid.velocity = new Vector3(planarVec.x, rigid.velocity.y, planarVec.z) + thrustVec;
//一帧
thrustVec = Vector3.zero;
}
/// <summary>
/// Message processing block
/// </summary>
public void OnJumpEnter() {
pi.inputEnabled = false;
lockPlanar = true;
thrustVec = new Vector3(0, jumpVelocity, 0);
}
public void OnRollEnter() {
pi.inputEnabled = false;
lockPlanar = true;
}
public void OnRollUpdate() {
thrustVec = model.transform.forward * anim.GetFloat("rollVelocity") * 1.0f;
}
public void OnGround() {
anim.SetBool("OnGround", true);
}
public void NotOnGround() {
anim.SetBool("OnGround", false);
}
public void OnGroundEnter() {
pi.inputEnabled = true;
lockPlanar = false;
}
public void OnFallEnter() {
pi.inputEnabled = false;
lockPlanar = true;
}
public void OnJabEnter() {
pi.inputEnabled = false;
lockPlanar = true;
}
public void OnJabUpdate() {
thrustVec = model.transform.forward * anim.GetFloat("jabVelocity")*1.4f;
}
}
5、检测被抓住
用两者的collision来判断,如果撞上了则代表玩家被抓住,游戏结束
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerCollide : MonoBehaviour {
void OnCollisionEnter(Collision other) {
//当玩家与侦察兵相撞
if (other.gameObject.tag == "Guard") {
Singleton<GameEventManager>.Instance.PlayerGameover();
}
}
}
四、展示
1、初始页面,绿色的为自己,黑色的为守卫者。左上角有分数,每躲过一个守卫者则加一分
2、被抓到游戏结束
3、跳跃和翻滚
童鞋们也快去试一试吧。