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


    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我就放弃了,引擎这玩意不是一个人能搞定的。好吧,吐槽一下,类似书籍,外国人写书真的是简单明了,国人就喜欢搞玄学化。

  • 相关阅读:
    vue使用Echarts图表
    在vue中子组件修改props引发的对js深拷贝和浅拷贝的思考
    团队开发前端VUE项目代码规范
    Vue项目开发最新、最全代码规范文档
    Material Icons 查找的替代办法
    Material icons 全图标一览
    VueCropper 图片裁剪
    分区
    linux 安装图行界面
    Spotlight LGWR1 一直告警
  • 原文地址:https://www.cnblogs.com/xiaoahui/p/10204812.html
Copyright © 2020-2023  润新知