Unity游戏AI记录(2d横板为例)

Wesley13
• 阅读 450

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

public class GeneralPeopleController : MonoBehaviour {

protected ContactFilter2D contactFilter;
protected RaycastHit2D[] hitBuffer = new RaycastHit2D[16];
protected List hitBufferList = new List(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();
rigid2D = GetComponent();
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().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(), targetCollider2D);
}
else if (Input.GetKey(KeyCode.S))
{
rigid2D.velocity = new Vector2(0, -climbSpeed);
Physics2D.IgnoreCollision(GetComponent(), 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我就放弃了,引擎这玩意不是一个人能搞定的。好吧,吐槽一下,类似书籍,外国人写书真的是简单明了,国人就喜欢搞玄学化。

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Karen110 Karen110
3年前
一篇文章带你了解JavaScript日期
日期对象允许您使用日期(年、月、日、小时、分钟、秒和毫秒)。一、JavaScript的日期格式一个JavaScript日期可以写为一个字符串:ThuFeb02201909:59:51GMT0800(中国标准时间)或者是一个数字:1486000791164写数字的日期,指定的毫秒数自1970年1月1日00:00:00到现在。1\.显示日期使用
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
4个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Wesley13 Wesley13
3年前
Unity横屏
Android下发现Unity里面的Player设置,并不能完全有效,比如打开了自动旋转,启动的时候还是会横屏,修改XML添加以下代码<applicationandroid:icon"@drawable/ic\_launcher"                    android:label"@string/app\_name"
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
10个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这