using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GeneralPeopleController : MonoBehaviour {

protected ContactFilter2D contactFilter;
protected RaycastHit2D[] hitBuffer = new RaycastHit2D[16];
protected List<RaycastHit2D> hitBufferList = new List<RaycastHit2D>(16);
protected Rigidbody2D rigid2D;
ConstantForce2D constantForce2D;
private SpriteRenderer spriteRenderer;
private Animator animator;

public LayerMask layerMask;//接触的物体层
public LayerMask tarLayerMask;//攻击的物体层
public float minGroundNormalY = 0.6f;//与物体碰撞的法向量的用于定义是否站在地面上的y值
public bool grounded = false;
public bool walled = false;
public bool pited = false;
public bool isGetPlayer=false;
public float jumpForce = 20f;//跳跃力度
public float horizonForce = 30f;//水平运动的驱动力
public Vector2 maxSpeed;
public int horizonDir;

public float climbSpeed = 2f;

bool inLadder = false;
public bool lockIgnore = false;

public Collider2D targetCollider2D;

public float fightTime = 0.2f;//攻击状态持续时间
float fightTimer = 0f;
public float fightSpaceTime = 2f;
float fightSpaceTimer=0f;
//攻击的范围框体
public Vector2 boxFightZoneSize;
public Vector2 boxFightZonePos;

//被攻击状态的持续时间
public float beAttackedContinueTime = 0.1f;
float beAttackedContinueTimer = 0;

float minGroundNormalX=0.7f;

//检测坑的框体范围
public Vector2 boxCheckPitSize;
public Vector2 boxCheckPitPos;
//侦测目标的框体范围
public Vector2 boxCheckTargetSize;
public Vector2 boxCheckTargetPos;

//目标对象
public Transform targetTransform;

//角色行为模式定义:平民,敌人
public int role;
//或者称为状态模式,建立在多个情绪运动的基础上,表示现在进入哪种行为动作,逃跑,攻击,巡逻;比如市民被攻击的时候第一时间是逃跑,
//而敌人就应该是发起反击
public int state;
//角色的情绪状态,表示角色现在想做什么,这是角色行为最高层的状态:巡逻(随机运动),警戒(一直水平运动,遇到墙体或者坑就返回),
//追踪(向目标位置运动,到达位置后在左右徘徊),攻击(面向目标位置,直接攻击)
public int emotionState;
//运动状态(向左运动,向右运动,静止)
public int moveState;

//时间参数
//状态行为持续时间参数
float stateTime=0f;
//情绪行为持续时间
float emotionTime = 0f;
//运动状态持续时间
float moveTime=0f;

// Use this for initialization
void Start()
{
constantForce2D = GetComponent<ConstantForce2D>();
rigid2D = GetComponent<Rigidbody2D>();
contactFilter.SetLayerMask(layerMask);
}

private void Update()
{
CoolTimer();
}

private void FixedUpdate()
{
grounded = false;
CheckGround();
walled = false;
CheckWalled(horizonDir);
pited = false;
CheckPit(horizonDir);
isGetPlayer = false;
CheckTarget(horizonDir);

Role();
}

//---------------------------------------------------------------角色行为------------------------------------------------------------------
//表示角色的行为模式,
void Role() {
switch (role) {
case 0:Enemy(); break;
case 1:CommonRole(); break;
}
}

//---------------------------------------------------------------状态行为------------------------------------------------------------------
//或者称为状态模式,建立在多个情绪运动的基础上,表示现在进入哪种行为动作,逃跑,攻击,巡逻;比如市民被攻击的时候第一时间是逃跑,
//而敌人就应该是发起反击

//市民
void CommonRole() {
switch (emotionState) {
case 0:RandomMove();

break;
case 1:WarningMove(); break;
}
}

//敌人
//默认是巡逻状态;如果发生警报那么进入警戒状态;如果发现目标,并且目标在追踪范围内,那么进入追踪状态;如果目标在攻击范围内那么进入攻击状态
void Enemy() {
switch (state) {
case 0:
Patrol();
break;
case 1:
Warning();
if (targetTransform) {
state = 3;
emotionState = 0;
}
if (stateTime <= 0) {
state = 0;
emotionState = 1;
moveState = 0;
}
break;
case 2:
Track();
break;
case 3:
Fight();
if (!targetTransform || Vector3.Distance(transform.position, targetTransform.position) > 20f) {
state = 1;
emotionState = 0;
moveState = Random.Range(1, 3);
rigid2D.drag = 0f;
stateTime = Random.Range(20, 40);
}
break;
}
}

//---------------------------------------------------------------情绪运动------------------------------------------------------------------
//情绪行为是建立在基本行为的基础上,由多个基本行为组成,表示为了表达当前的情绪而进行的行为,比如攻击情绪,先进行基本追踪寻路,
//然后到达攻击位置后进入攻击行为

//巡逻,是随机运动和警戒运动结合的更上一层的随机行为运动
void Patrol() {
switch (emotionState) {
case 0:WarningMove();
if (emotionTime <= 0) {
emotionTime = Random.Range(5, 10);
emotionState = 1;
}
break;
case 1:RandomMove();
if (emotionTime <= 0)
{
emotionTime = Random.Range(5, 10);
moveState = 1;
emotionState = 0;
rigid2D.drag = 0f;
}
break;
}
}

//警戒
void Warning()
{
switch (emotionState) {
case 0:WarningMove();break;
}
}

//追踪
void Track() {
switch (emotionState)
{
case 0: TrackingMove(); break;
}
}

//攻击
void Fight() {
switch (emotionState)
{
case 0: TrackingMove();
if (grounded &&targetTransform&& Vector3.Distance(transform.position,targetTransform.position) < 1f) {
horizonDir = (int)Mathf.Sign(targetTransform.position.x - transform.position.x);
emotionState = 1;
moveState = 0;
rigid2D.drag = 20f;
}
break;
case 1: FightMove();
if (grounded&&targetTransform && (Vector3.Distance(transform.position, targetTransform.position) > 1f||horizonDir!= (int)Mathf.Sign(targetTransform.position.x - transform.position.x))&&moveState==1)
{
horizonDir = (int)Mathf.Sign(targetTransform.position.x - transform.position.x);
emotionState = 0;
rigid2D.drag = 0f;
}
break;
}
}

//---------------------------------------------------------------基本行为------------------------------------------------------------------
//攻击行为
void FightMove() {
switch (moveState) {
case 0://攻击状态,对应攻击的动作时间
if (fightTimer <= 0) {
moveState = 1;
fightSpaceTimer = fightSpaceTime;
}
break;
case 1://预备攻击状态
if (fightSpaceTimer <= 0) {
moveState = 0;
fightTimer = fightTime;
}
break;
}
}

//追踪运动
void TrackingMove() {
int tarDir=0;
if(targetTransform)
tarDir = (int)(Mathf.Sign(targetTransform.position.x - transform.position.x));
switch (moveState) {
case 1:
HorizonMove(1);
horizonDir = 1;
if ((pited || walled) && grounded)
{
moveState = 2;
horizonDir = -1;
if (tarDir == 1) moveTime = 0.3f;//如果在追踪目标的方向碰到坑道,那么朝相反地方方向移动一段时间,表示在边缘徘徊
}
else if (tarDir != 1&&moveTime<=0 && grounded) {
moveState = 2;
horizonDir = -1;
}
break;
case 2:
HorizonMove(-1);
horizonDir = -1;
if ((pited || walled) && grounded)
{
moveState = 1;
horizonDir = 1;
if (tarDir == -1) moveTime = 0.3f;
}
else if (tarDir != -1 && moveTime <= 0 && grounded)
{
moveState = 1;
horizonDir = 1;
}
break;
}
}

//警戒运动
void WarningMove() {
switch (moveState) {
case 1:
HorizonMove(1);
horizonDir = 1;
if ((pited || walled) && grounded)
{
moveState = 2;
horizonDir = -1;
}
break;
case 2:
HorizonMove(-1);
horizonDir = -1;
if ((pited || walled) && grounded)
{
moveState = 1;
horizonDir = 1;
}
break;
}
}

//随机运动
void RandomMove() {
switch (moveState) {
case 0://静止状态
if (moveTime <= 0) {
moveTime = Random.Range(1, 3);
float randFloat = Random.Range(0, 100);

if (randFloat >= 0 && randFloat < 50)
{
moveState = 1; rigid2D.drag = 0f; horizonDir = 1;
}
else if (randFloat >= 50 && randFloat < 100)
{
moveState = 2; rigid2D.drag = 0f; horizonDir = -1;
}
}
break;
case 1://向右运动

//运动,如果时间到或者遇到墙体,坑地,转换运动方向
HorizonMove(1);

//状态转换
if (moveTime <= 0)
{
moveTime = Random.Range(1, 5);
float randFloat = Random.Range(0, 100);

if (randFloat >= 0 && randFloat < 40)
{
moveState = 0;
//设置角色减速
if (grounded) rigid2D.drag = 20f;
constantForce2D.force = new Vector2(0, 0);
}
else if (randFloat >= 40 && randFloat < 100) {
moveState = 2;
horizonDir = -1;
}
}
else if ((pited || walled)&&grounded) {
moveState = 2;
horizonDir = -1;
}
break;
case 2://向左运动
HorizonMove(-1);

//状态转换
if (moveTime <= 0)
{
moveTime = Random.Range(1, 5);
float randFloat = Random.Range(0, 100);

if (randFloat >= 0 && randFloat < 40)
{
moveState = 0;
//设置角色减速
if (grounded) rigid2D.drag = 20f;
constantForce2D.force = new Vector2(0, 0);
}
else if (randFloat >= 40 && randFloat < 100) {
moveState = 1;
horizonDir = 1;
}
}
else if ((pited || walled)&&grounded)
{
moveState = 1;
horizonDir = 1;
}
break;
}
}

//---------------------------------------------------------------底层运动------------------------------------------------------------------
//水平运动
void HorizonMove(int dir) {
if (Mathf.Sign(rigid2D.velocity.x)!=Mathf.Sign(dir*maxSpeed.x) || Mathf.Sign(rigid2D.velocity.x) == Mathf.Sign(dir * maxSpeed.x) && Mathf.Abs( rigid2D.velocity.x) < maxSpeed.x)
constantForce2D.force = new Vector2(dir*horizonForce, 0);
else constantForce2D.force = new Vector2(0, 0);
}

private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.tag.Equals("Ladder"))
{
inLadder = true;
targetCollider2D = collision.gameObject.GetComponent<LaderClimb>().targetCollider2D;
}
}

private void OnTriggerExit2D(Collider2D collision)
{
if (collision.tag.Equals("Ladder"))
{
inLadder = false;
}

if (collision.gameObject.tag.Equals("LockIgnore")) lockIgnore = false;
}

private void OnTriggerStay2D(Collider2D collision)
{
if (collision.gameObject.tag.Equals("LockIgnore")) lockIgnore = true;
}

private void OnDrawGizmos()
{
Gizmos.color = Color.blue;
Vector2 pos = new Vector2(transform.position.x + horizonDir * boxFightZonePos.x, transform.position.y + boxFightZonePos.y);
Gizmos.DrawWireCube(pos, boxFightZoneSize);

Gizmos.color = Color.red;
pos = new Vector2(transform.position.x + horizonDir*boxCheckPitPos.x, transform.position.y + boxCheckPitPos.y);
Gizmos.DrawWireCube(pos, boxCheckPitSize);

Gizmos.color = Color.yellow;
pos = new Vector2(transform.position.x + horizonDir * boxCheckTargetPos.x, transform.position.y + boxCheckTargetPos.y);
Gizmos.DrawWireCube(pos, boxCheckTargetSize);
}

//检查是否有坑
void CheckPit(int dir) {
Vector2 pos = new Vector2(transform.position.x +dir* boxCheckPitPos.x, transform.position.y + boxCheckPitPos.y);
RaycastHit2D[] hit2Ds = Physics2D.BoxCastAll(pos, boxCheckPitSize, 0, Vector2.right, 0.1f,layerMask);
if (hit2Ds.Length == 0) pited = true;
}

//判断是否碰到墙体
void CheckWalled(int dir) {
int count = rigid2D.Cast(Vector2.right*dir, contactFilter, hitBuffer, 0.02f);//对碰撞体向下的方向检测是否站在某个物体上,精度由检测的射线数和射线长度决定
hitBufferList.Clear();
for (int i = 0; i < count; i++)
{
hitBufferList.Add(hitBuffer[i]);
}
//如果有一个 射线的碰撞点的法线的y大于minGroundNormalY那么设置grounded为true表示站在了物体上
//这里minGroundNormalY一般设置为1到0.6之间即可决定站的平面的倾斜度
for (int i = 0; i < hitBufferList.Count; i++)
{
Vector2 currentNormal = hitBufferList[i].normal;
if (Mathf.Abs(currentNormal.x) > minGroundNormalX)
{
walled = true;
}
}
}

//判断是正站在某个物体上
void CheckGround()
{
int count = rigid2D.Cast(Vector2.down, contactFilter, hitBuffer, 0.02f);//对碰撞体向下的方向检测是否站在某个物体上,精度由检测的射线数和射线长度决定
hitBufferList.Clear();
for (int i = 0; i < count; i++)
{
hitBufferList.Add(hitBuffer[i]);
}
//如果有一个 射线的碰撞点的法线的y大于minGroundNormalY那么设置grounded为true表示站在了物体上
//这里minGroundNormalY一般设置为1到0.6之间即可决定站的平面的倾斜度
for (int i = 0; i < hitBufferList.Count; i++)
{
Vector2 currentNormal = hitBufferList[i].normal;
if (currentNormal.y > minGroundNormalY)
{
grounded = true;
}
}
}

//检查攻击目标是否存在
void CheckTarget(int dir) {
Vector2 pos = new Vector2(transform.position.x + dir * boxCheckTargetPos.x, transform.position.y + boxCheckTargetPos.y);
RaycastHit2D[] hit2Ds = Physics2D.BoxCastAll(pos, boxCheckTargetSize, 0, Vector2.right, 0.1f, tarLayerMask);
foreach (RaycastHit2D ray in hit2Ds)
{
if (ray.transform.tag.Equals("Player")&&(!targetTransform||Vector3.Distance(transform.position,targetTransform.position)>20))
{
targetTransform = ray.transform;
Debug.Log(targetTransform.tag);
}
}
}

//正常运动状态:跳跃,走动
void NormalMove()
{
float j = Input.GetAxis("Jump");
if (grounded && (Input.GetButtonUp("Jump") || j > 0.99f))
{
rigid2D.AddForce(new Vector2(0, j * jumpForce));
}

if (Input.GetKey(KeyCode.D))
{
if (rigid2D.velocity.x < maxSpeed.x)
constantForce2D.force = new Vector2(horizonForce, 0);
else constantForce2D.force = new Vector2(0, 0);
}
else if (Input.GetKey(KeyCode.A))
{
if (rigid2D.velocity.x > -maxSpeed.x)
constantForce2D.force = new Vector2(-horizonForce, 0);
else constantForce2D.force = new Vector2(0, 0);
}
else constantForce2D.force = new Vector2(0, 0);

//设置角色减速
if (grounded && !(Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.D) || Input.GetKeyDown(KeyCode.Space)) && !lockIgnore) rigid2D.drag = 20f;
else rigid2D.drag = 0f;
}

//攀爬楼梯
void ClimbLadder()
{
if (Input.GetKey(KeyCode.W))
{
rigid2D.velocity = new Vector2(0, climbSpeed);
Physics2D.IgnoreCollision(GetComponent<Collider2D>(), targetCollider2D);
}
else if (Input.GetKey(KeyCode.S))
{
rigid2D.velocity = new Vector2(0, -climbSpeed);
Physics2D.IgnoreCollision(GetComponent<Collider2D>(), targetCollider2D);
}
else
{
rigid2D.velocity = new Vector2(0, 0);
}

if ((Mathf.Abs(Input.GetAxis("Horizontal")) > 0) && (Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.D))) rigid2D.velocity = new Vector2(climbSpeed * Input.GetAxis("Horizontal"), rigid2D.velocity.y);

}

void fight()
{
Vector2 pos = new Vector2(transform.position.x + boxFightZonePos.x, transform.position.y + boxFightZonePos.y);
RaycastHit2D[] hit2Ds = Physics2D.BoxCastAll(pos, boxFightZoneSize, 0, Vector2.right, 0.1f);
for (int i = 0; i < hit2Ds.Length; i++)
{
if (hit2Ds[i].collider.tag.Equals("Enemy"))
Debug.Log(hit2Ds[i].collider.tag);
}
}

void CoolTimer()
{
if (fightTimer > 0) fightTimer -= Time.deltaTime;
if (beAttackedContinueTimer > 0) beAttackedContinueTimer -= Time.deltaTime;
if (moveTime > 0) moveTime -= Time.deltaTime;
if (fightSpaceTimer > 0) fightSpaceTimer -= Time.deltaTime;
if (emotionTime > 0) emotionTime -= Time.deltaTime;
if (stateTime > 0) stateTime -= Time.deltaTime;
}
}

为了简洁明了,当前层不能调用同层或者上层,但可以随意调用比它低的任何层。

最后:昨晚元旦跨年,忙活了一晚上,记录一下,看不懂的不要紧,这里纯粹是为了提醒自己别忘了这个方法,因为一个月后我自己可能也看不懂了,上面的构思大多来自《Windows游戏编程:大师技巧》,老外写的,迄今为止我买过的最好的书,他还有一个版本是关于写3d游戏,但是偏底层,能读懂搞定的话你就可以自己写个游戏引擎了,大学为了学这玩意丢了一个妹子,真的,伤心的故事。学到一半搞了个demo我就放弃了,引擎这玩意不是一个人能搞定的。好吧,吐槽一下,类似书籍,外国人写书真的是简单明了,国人就喜欢搞玄学化。

Unity游戏AI记录(2d横板为例)的更多相关文章

  1. Unity 游戏框架搭建 (三) MonoBehaviour单例的模板

      上一篇文章讲述了如何设计C#单例的模板.也随之抛出了问题: 如何设计接收MonoBehaviour生命周期的单例的模板? 如何设计? 先分析下需求:   1.约束脚本实例对象的个数.   2.约束 ...

  2. Unity 游戏框架搭建 2018 (二) 单例的模板与最佳实践

    Unity 游戏框架搭建 2018 (二) 单例的模板与最佳实践 背景 很多开发者或者有经验的老手都会建议尽量不要用单例模式,这是有原因的. 单例模式是设计模式中最简单的也是大家通常最先接触的一种设计 ...

  3. Unity 游戏框架搭建 (十三) 无需继承的单例的模板

    之前的文章中介绍的Unity 游戏框架搭建 (二) 单例的模板和Unity 游戏框架搭建 (三) MonoBehaviour单例的模板有一些问题. 存在的问题: 只要继承了单例的模板就无法再继承其他的 ...

  4. Unity 游戏框架搭建 (十) QFramework v0.0.2小结

    从框架搭建系列的第一篇文章开始到现在有四个多月时间了,这段时间对自己来说有很多的收获,好多小伙伴和前辈不管是在评论区还是私下里给出的建议非常有参考性,在此先谢过各位. 说到是一篇小节,先列出框架的概要 ...

  5. C#开发Unity游戏教程循环遍历做出推断及Unity游戏演示样例

    C#开发Unity游戏教程循环遍历做出推断及Unity游戏演示样例 Unity中循环遍历每一个数据,并做出推断 非常多时候.游戏在玩家做出推断以后.游戏程序会遍历玩家身上大量的所需数据,然后做出推断. ...

  6. Unity 用ml-agents机器学习造个游戏AI吧(1)(Windows环境配置)

    前言:以前觉得机器学习要应用于游戏AI,还远得很. 最近看到一些资料后,突发兴致试着玩了玩Unity机器学习,才发觉机器学习占领游戏AI的可能性和趋势. Unity训练可爱柯基犬Puppo 机器学习训 ...

  7. Unity教程之-基于行为树与状态机的游戏AI

    AI.我们的第一印象可能是机器人,现在主要说在游戏中的应用.关于AI的相关文章我们在前面也提到过,详细请戳这现代的计算机游戏中已经大量融入了AI元素,平时我们进行游戏时产生的交互都是由AI来完成的.比 ...

  8. 游戏AI之路径规划(3)

    目录 使用路径点(Way Point)作为节点 洪水填充算法创建路径点 使用导航网(Navigation Mesh)作为节点 区域分割 预计算 路径查询表 路径成本查询表 寻路的改进 平均帧运算 路径 ...

  9. 游戏AI之决策结构—有限状态机/行为树(2)

    目录 有限状态机 行为树 控制节点 条件节点 行为节点 装饰节点 总结 额外/细节/优化 游戏AI的决策部分是比较重要的部分,游戏程序的老前辈们留下了两种经过考验的用于AI决策的结构: 有限状态机 行 ...

随机推荐

  1. 【转】MySQL双主一致性架构优化

    [原文]https://www.toutiao.com/i6594414914838725133/ 一.双主保证高可用 MySQL数据库集群常使用一主多从,主从同步,读写分离的方式来扩充数据库的读性能 ...

  2. CIDR概述及其地址块计算

    CIDR概述 英文:Classless Inter-Domain Routing,中文是:无分类域间路由选择.一般叫做无分类编址. 设计目的:解决路由表项目过多过大的问题. 表示法:{<网络前缀 ...

  3. Rancher 添加主机无法显示、添加主机无效的解决办法

    在 Rancher UI 中,添加主机,在 Shell ssh 运行了,然后 点击 “关闭” 按钮,发现没有显示如何主机. 第一步,先去查看应用是否正常,就是 应用 - 全部应用 如果显示是 unhe ...

  4. 实例化list

    List<String> lists = new ArrayList<String>();list.add("123");

  5. 解决Windows Server2008 R2中IE开网页时弹出阻止框

    使用Windows Server2008,用IE打开网站时会弹出“Internet Explorer增强安全配置正在阻止来自下列网站的此应用程序中的内容”的对话框.如下图所示: 2011-10-14_ ...

  6. swift的类型推断

    类型推断的前提是有待定类型和上下文. 1.由定义推断实现的类型: 2.由赋值推断声明的类型: 3.由实现推断泛型的类型: Type inference refers to the automatic ...

  7. 动态显示checkbox选中条数

    <script> $('input[type=checkbox]').click( function () { $('span#cheak_len').empty(); var len = ...

  8. Linux下安装与配置snmp服务

    一.安装snmp服务 1.检查系统是否已经安装snmp的rpm包 以下是安装snmp服务需要的rpm包: libsensors3-2.10.6-55.el5.i386.rpm lm_sensors-2 ...

  9. rpm -qa 查找文件

    系统环境:centos6.6 yum install 安装的文件找不到安装路径,使用whereis和find -name都无效 rpm -qa | grep -i 关键字  查找 rpm -ql fi ...

  10. 测试计划&性能测试分析报告模板(仅供参考)

    一.测试计划 1. 引言 1.1  编写目的 2. 参考文档 3. 测试目的 4. 测试范围 4.1  测试对象 4.2  需要测试的特性 4.3  无需测试的特性 5. 测试启动与结束准则 5.1  ...