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. cdn刷新和对应的浏览器现象

    1.浏览器手动点刷新,会发起网络请求,从cdn判断last-modify时间是否一致,未过期则返回304,如果已经过期则返回200,重新请求 关键在于发起的网络请求中'If-Modified-Sinc ...

  2. 记一次隐秘的XSS漏洞挖掘

    前言 在为某客户网站做渗透测试时发现一个有趣的事情.当我访问该网站的某条链接时服务器返回的是404页面.看到这里我当时就下意识的忽略它,但是后来又想了想这也不是完全没有价值,毕竟中间件及其版本都出来了 ...

  3. nginx 添加虚拟主机 支持php 伪静态

    1添加虚拟主机 进入 /usr/local/nginx/conf/vhost 目录, 创建虚拟主机配置文件 demo.neoease.com.conf ({域名}.conf). 2. 打开配置文件, ...

  4. 一道经典面试题-----setTimeout(function(){},0)

    一道经典面试题-----setTimeout(function(){},0) 转载: http://www.w3cfuns.com/notes/17398/e8a1ce8f863e8b5abb5300 ...

  5. 关于datatables与jquerUI版本冲突问题

    今天开发项目是,需要用到表格,于是想到,之前的项目中使用过datatables插件,于是就拿了过来,但是发现无法识别 Uncaught TypeError: $(...).DataTable is n ...

  6. vue-cli静态资源处理

    vue-cli是利用webpack进行打包部署,其中静态资源的路径问题是一个比较麻烦的部分. 项目中共有两个存放静态文件的地方. /static 根目录下的static文件夹 assets src目录 ...

  7. An Introduction to Protocol Oriented Programming in Swift

    swift面向协议编程的根本原因在于值类型的存在:面向对象必须要有引用类型的支持: Protocol Oriented approach was introduced to resolve some ...

  8. 1008. [HNOI2008]越狱【快速幂】

    Description 监狱有连续编号为1...N的N个房间,每个房间关押一个犯人,有M种宗教,每个犯人可能信仰其中一种.如果 相邻房间的犯人的宗教相同,就可能发生越狱,求有多少种状态可能发生越狱 I ...

  9. 【转】 [置顶] Android 通知栏Notification的整合 全面学习 (一个DEMO让你完全了解它)

    在Android的应用层中,涉及到很多应用框架,例如:Service框架,Activity管理机制,Broadcast机制,对话框框架,标题栏框架,状态栏框架,通知机制,ActionBar框架等等. ...

  10. uname -a输出内容分析

    uname -a输出内容分析 uname --help 将每个参数都单独执行一次,得到: ------------------------------------------------------- ...