上一篇讲到了基本的模式,通过操作一系列预设的指令集来实现游戏对象的智能,这一篇当然还是要实现太该有的追踪功能,那么这个涉及应该如何以下逻辑:1、当符合追踪范围,追踪目标,反之继续按照以前的模式继续执行。当然还可以继续深入的添加功能2、如果符合攻击范围则停止并保留当前的移动状态而切换到攻击动画,如果不在攻击范围则恢复之前的移动状态继续追踪或者模式运动。
实现这个功能只需要在前面的模式执行的基础上在加一个if语句来判断与追踪目标的距离判断就OK啦。代码如下:
void BasicPattern_AI()
{
if (m_currentPattern == null)
{
m_patternIndex = Random.Range(1, 5);
// m_patternIndex = 3;
m_currentPattern = TextToolControl.GetInstance().GetContent(m_patternIndex);
m_currentOperand = 0;
m_timeCounter = 0;
}
float vx = target.transform.position.x - this.transform.position.x;
float vz = target.transform.position.z - this.transform.position.z;
float length = PointDistance_2D(vx, vz);
//如果达到距离就追踪
if (length < Min_Tracker_Dis)
{
if (length <= Min_Attack_Dis)
{
isMove = false;
m_animation.CrossFade("orc_attack_repeatedly");
}
else
{
isMove = true;
vx = vx / length;
vz = vz / length;
ChangeDirection(vx, vz);
}
}
//否则继续进行预设的模式序列
else
{
//每个具体操作的操作时间到了就执行下一个操作
m_timeCounter -= Time.deltaTime;
if (m_timeCounter <= 0)
{
// moveDirection = MoveDirection.Random;
moveDirection = (MoveDirection)m_currentPattern[m_currentOperand];
m_timeCounter = m_currentPattern[m_currentOperand + 1];
switch (moveDirection)
{
case MoveDirection.East:
{
ChangeDirection(-1, 0);
} break;
case MoveDirection.North:
{
ChangeDirection(0, -1);
} break;
case MoveDirection.South:
{
ChangeDirection(0, 1);
} break;
case MoveDirection.West:
{
ChangeDirection(1, 0);
} break;
case MoveDirection.Stop:
{
ChangeDirection(0, 0);
}
break;
case MoveDirection.End:
{
ChangeDirection(0, 0);
m_patternIndex = Random.Range(1, 5);
m_currentPattern = TextToolControl.GetInstance().GetContent(m_patternIndex);
m_currentOperand = 0;
m_timeCounter = 0;
} break;
case MoveDirection.Random:
{
//随机的改变方向
float x = Random.Range(-10.0f, 10.0f) / 10.0f;//一定是要是浮点型不是整数型不然得不到-1到1之间的小数,如果是整数等到的永远是0
float z = Random.Range(-10.0f, 10.0f) / 10.0f;
// Debug.Log("x:" + x + "z:" + z);
ChangeDirection(x, z);
} break;
}
if (m_currentOperand < m_currentPattern.Count - 1)
{
m_currentOperand += 1;
}
}
}
}
当然前面的关于距离判断就要改y为z了,如下:
float PointDistance_2D(float x, float z)
{
//使用了泰勒展开式来计算,有3.5%的误差,直接使用开方计算会比较慢,但是测试了我的电脑好像没有什么变化可能是数据量不大体现不出来
/*x = Mathf.Abs(x);
y = Mathf.Abs(y);
float mn = Mathf.Min(x, y);//获取x,y中最小的数
float result = x + y - (mn / 2) - (mn / 4) + (mn / 8);*/
float result = Mathf.Sqrt(x * x + z *z );
return result;
}
完整的代码如下:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
//移动模式枚举
public enum MoveDirection:int
{
East=0,South=1,West=2,North=3,Stop=4,Random=5,End=6
}
public class AIBasicPattern : MonoBehaviour {
public Transform target;
public float Min_Tracker_Dis;
public float Min_Attack_Dis;
public Animation m_animation;
int m_patternIndex;
int m_currentOperand;//当前模式中的方向索引
MoveDirection moveDirection;
float m_timeCounter;//当前模式的时间
List<int> m_currentPattern;
public float moveVx=0;
public float moveVz=0;
bool isMove = true;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
BasicPattern_AI();
CheckBoundary();
if (isMove)
{
Move();
}
Debug.Log("模式:"+m_patternIndex+"操作数:"+m_currentOperand+"Vx:"+moveVx+",VZ:"+moveVz);
}
void BasicPattern_AI()
{
if (m_currentPattern == null)
{
m_patternIndex = Random.Range(1, 5);
// m_patternIndex = 3;
m_currentPattern = TextToolControl.GetInstance().GetContent(m_patternIndex);
m_currentOperand = 0;
m_timeCounter = 0;
}
float vx = target.transform.position.x - this.transform.position.x;
float vz = target.transform.position.z - this.transform.position.z;
float length = PointDistance_2D(vx, vz);
//如果达到距离就追踪
if (length < Min_Tracker_Dis)
{
if (length <= Min_Attack_Dis)
{
isMove = false;
m_animation.CrossFade("orc_attack_repeatedly");
}
else
{
isMove = true;
vx = vx / length;
vz = vz / length;
ChangeDirection(vx, vz);
}
}
//否则继续进行预设的模式序列
else
{
//每个具体操作的操作时间到了就执行下一个操作
m_timeCounter -= Time.deltaTime;
if (m_timeCounter <= 0)
{
// moveDirection = MoveDirection.Random;
moveDirection = (MoveDirection)m_currentPattern[m_currentOperand];
m_timeCounter = m_currentPattern[m_currentOperand + 1];
switch (moveDirection)
{
case MoveDirection.East:
{
ChangeDirection(-1, 0);
} break;
case MoveDirection.North:
{
ChangeDirection(0, -1);
} break;
case MoveDirection.South:
{
ChangeDirection(0, 1);
} break;
case MoveDirection.West:
{
ChangeDirection(1, 0);
} break;
case MoveDirection.Stop:
{
ChangeDirection(0, 0);
}
break;
case MoveDirection.End:
{
ChangeDirection(0, 0);
m_patternIndex = Random.Range(1, 5);
m_currentPattern = TextToolControl.GetInstance().GetContent(m_patternIndex);
m_currentOperand = 0;
m_timeCounter = 0;
} break;
case MoveDirection.Random:
{
//随机的改变方向
float x = Random.Range(-10.0f, 10.0f) / 10.0f;//一定是要是浮点型不是整数型不然得不到-1到1之间的小数,如果是整数等到的永远是0
float z = Random.Range(-10.0f, 10.0f) / 10.0f;
// Debug.Log("x:" + x + "z:" + z);
ChangeDirection(x, z);
} break;
}
if (m_currentOperand < m_currentPattern.Count - 1)
{
m_currentOperand += 1;
}
}
}
}
float PointDistance_2D(float x, float z)
{
//使用了泰勒展开式来计算,有3.5%的误差,直接使用开方计算会比较慢,但是测试了我的电脑好像没有什么变化可能是数据量不大体现不出来
/*x = Mathf.Abs(x);
y = Mathf.Abs(y);
float mn = Mathf.Min(x, y);//获取x,y中最小的数
float result = x + y - (mn / 2) - (mn / 4) + (mn / 8);*/
float result = Mathf.Sqrt(x * x + z *z );
return result;
}
void ChangeDirection(float vx,float vz)
{
moveVx = vx;
moveVz = vz;
}
void Move()
{
if(moveVz!=0||moveVx!=0)
{
m_animation.CrossFade("orc_walk");
}
else if(moveVx==0&&moveVz==0)
{
m_animation.CrossFade("orc_idle");
}
//位置的移动,此时是在X,Z平面
this.transform.position += new Vector3(moveVx * Time.deltaTime, 0, moveVz * Time.deltaTime);
//运动朝向的改变
float yAngles = Mathf.Atan(moveVx / moveVz) * (180 / Mathf.PI);
//此时的角度是与Z轴的夹角,但是模型对应的是转Y轴
if (moveVz == 0)
{
yAngles = moveVx >= 0 ? 90 : -90;
}
if (moveVz < 0)
{
yAngles = yAngles - 180;
}
Debug.Log("角度转动为:"+yAngles);
Vector3 tempAngles = new Vector3(0, yAngles,0);
Quaternion tempQua = this.transform.rotation;
tempQua.eulerAngles = tempAngles;
this.transform.rotation = tempQua;
}
void CheckBoundary()
{
if (Mathf.Abs(this.transform.position.x) > 10 || (Mathf.Abs(this.transform.position.z) > 10))
{
if (isMove)
{
m_animation.CrossFade("orc_hit_front");
}
isMove = false;
if (!m_animation.IsPlaying("orc_hit_front"))
{
//改变方向
moveVx = -moveVx;
moveVz = -moveVz;
isMove = true;
}
}
}
}
控制Player作为追踪目标的代码如下:
using UnityEngine;
using System.Collections;
public class BasicPatternPlayerMove : MonoBehaviour {
public float moveSpeed;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
Move();
}
void Move()
{
float x = -Input.GetAxis("Horizontal") * 100;
float z = -Input.GetAxis("Vertical") * 100;
//如果超出屏幕范围则让它出现在另一面
transform.Translate(x * Time.deltaTime * moveSpeed, 0, z * Time.deltaTime * moveSpeed);
}
}
有关模式的处理还是相对来说比较简单的,只要预先设定好指令集,配合好代码的Switch处理逻辑就能够写出比完全是随机过程的行走巡逻代码要看上去智能很多。
附上我的工程下载地址:点击打开链接