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. UNIX高级环境编程(10)进程控制(Process Control)- 竞态条件,exec函数,解释器文件和system函数

    本篇主要介绍一下几个内容: 竞态条件(race condition) exec系函数 解释器文件    1 竞态条件(Race Condition) 竞态条件:当多个进程共同操作一个数据,并且结果依赖 ...

  2. python Pipe 双管道通信

    管道:是python多进程中一种交换数据的方式 from multiprocessing import Process,current_process,Queue,Pipe import time i ...

  3. How to Be Assertive Asking for What You Want Firmly and Fairly

    What Is Assertiveness? It's not always easy to identify truly assertive behavior. This is because th ...

  4. 【mpu6050】学习笔记——基础知识点记录

    如图: 假设为三维空间有一个向量R 满足关系:  即为加速度计的x轴,y轴,z轴. 对于MPU6050 其求出的数值为:    分母为灵敏度, ADCRx为读出值. 我关心的是Axr,Ayr,Azr即 ...

  5. 如何删除sharepoint列表List中的全部数据。

    可以使用excel,但是powershell会比较方便 (admin mode - Sharepoint powershell) [System.reflection.Assembly]::LoadW ...

  6. [LOJ 6029]「雅礼集训 2017 Day1」市场

    [LOJ 6029] 「雅礼集训 2017 Day1」市场 题意 给定一个长度为 \(n\) 的数列(从 \(0\) 开始标号), 要求执行 \(q\) 次操作, 每次操作为如下四种操作之一: 1 l ...

  7. 题解 P1312 【Mayan游戏】

    题面 过长已遮挡 题意 体面已经陈述题意(这题没有考语文阅读理解) 题解 ** 我还记得我曾经给自己找的锅,给某些人讲课的时候说过一句话:体面越长的题,越简单.** 这句话没有错,我会用接下来解决这道 ...

  8. 团队作业——Alpha冲刺 7/12

    团队作业--Alpha冲刺 冲刺任务安排 杨光海天 今日任务:将编辑界面与标题栏合并.与同队成员,讨论部分功能合并的问题. 明日任务:编辑界面与另一队员完成的字体设置弹窗合并. 郭剑南 今日任务:使用 ...

  9. Linux基础第二课——系统架构

    网址 192.168.0. 172.16. 10.0. 以上是内网网段 127.0.0.1 这是本地回环网卡 表示本身 查看网络是否连通 windows Linux 都是通过 ping 先ping网关 ...

  10. char a='1'和char a=1区别

    char a='1'表示:把字符为1,ASSIC码为49的值赋值给a: char a= 1表示:把ASSIC码为1的值赋值给a