Unity3d 学习之路——简单巡逻兵

Unity3d 学习之路——简单巡逻兵

游戏规则与游戏要求

智能巡逻兵

  • 游戏规则
    使用WSAD或方向键上下左右移动player,进入巡逻兵的追捕后逃脱可积累一分,若与巡逻兵碰撞则游戏结束,收集完地图上的所有水晶即可获胜。
  • 游戏设计要求:

    • 创建一个地图和若干巡逻兵(使用动画);
    • 每个巡逻兵走一个3~5个边的凸多边型,位置数据是相对地址。即每次确定下一个目标位置,+ 用自己当前位置为原点计算;
    • 巡逻兵碰撞到障碍物,则会自动选下一个点为目标;
    • 巡逻兵在设定范围内感知到玩家,会自动追击玩家;
    • 失去玩家目标后,继续巡逻;
    • 计分:玩家每次甩掉一个巡逻兵计一分,与巡逻兵碰撞游戏结束;
  • 程序设计要求:

    • 必须使用订阅与发布模式传消息
      subject:OnLostGoal
      Publisher: ?
      Subscriber: ?
    • 工厂模式生产巡逻兵

成品图

先看一下成品图吧,感觉是除了第一次作业之外,这次是自己做得最好的一次了。

做了整整两天,界面还没很完美。

游戏UML图

这里写图片描述

游戏实现

用到了C#的委托和事件实现发布与订阅模式,没有实现动作分离管理。
+ SSDirector,IUserAction,和ISceneController和之前的差不多甚至一样
+ 地图的绘制
建好一个Plane,再绘制好围墙,和几个触发器:

  • 巡逻兵的动作(Patrol.cs):
    • 属性与变量
public enum PatrolState { PATROL,FOLLOW};
public int sign;           //the patrol in which area
public bool isFollowPlayer = false;
public GameObject player=null;         //the player
public Vector3 startPos,nextPos;
private float minPosX,minPosZ;     // the range of this patrol can move;
private bool isMoving = false;
private float distance;
private float speed = 1.2f;
PatrolState state = PatrolState.PATROL;
  • 玩家没进到该巡逻兵管辖的领地时,巡逻兵到处随机巡逻:
public void GoPatrol()
{
     if (isMoving)
     {
         transform.position = Vector3.MoveTowards(this.transform.position, nextPos, speed * Time.deltaTime);
         distance = Vector3.Distance(this.transform.position, nextPos);
         if(distance < 0.5)
         {
             isMoving = false;
         }
         return;
     }
     float posX = Random.Range(0f, 5f);
     float posZ = Random.Range(0f, 5f);
     nextPos = new Vector3(minPosX+posX, 0, minPosZ+posZ);
     isMoving = true;    
 }
  • 当有玩家进入它的领地,追踪玩家,巡逻兵朝着玩家的位置移动,移动结束的条件是玩家离开该区域内了:
public void Follow()
{
    if(player != null)
    {
        nextPos = player.transform.position;
        transform.position = Vector3.MoveTowards(this.transform.position, nextPos, speed * Time.deltaTime);
    }
}
  • 巡逻兵工厂(PatrolFactory.cs)
    这次直接创建了3个巡逻兵,因为场景一开始就需要3个了
public class PatrolFactory:MonoBehaviour
{
    private List<GameObject> used = new List<GameObject>();    // the used patrol
    private Vector3[] PatrolPos = new Vector3[3];
    private bool isProduce = false;
    FirstController firstController;
    private void Start()
    {
        firstController = SSDirector.getInstance().currentScenceController as FirstController;
    }
    public List<GameObject> GetPatrols()
    {
        firstController = SSDirector.getInstance().currentScenceController as FirstController;
        if (!isProduce)
        {
            int index = 0;
            float[] posZ = { 3.75f, -3.75f };
            float[] posX = { 3.75f, -3.75f };
            for (int i = 0; i < 2; i++)
            {
                for (int j = 0; j < 2; j++)
                {
                    if(posX[j] > 0 && posZ[i] > 0)
                    {
                        continue;
                    }
                    PatrolPos[index++] = new Vector3(posX[j], 0, posZ[i]);
                }
            }
            for (int i = 0; i < 3; i++)
            {
                GameObject patrol = Instantiate(Resources.Load<GameObject>("Prefabs/Patrol"));
                patrol.transform.parent = firstController.plane.transform;
                patrol.transform.position = PatrolPos[i];
                patrol.GetComponent<Patrol>().sign = i + 1;
                patrol.GetComponent<Patrol>().startPos = PatrolPos[i];
                used.Add(patrol);
            }
            isProduce = true;
        }     
        return used;
    }

    public void destoryFactory()
    {
        foreach(var a in used)
        {
            DestroyImmediate(a);
        }
        used = new List<GameObject>();
        isProduce = false;
    }
}
  • 场景类(FirstController.cs)感觉没什么好说的,主要是加载资源和管理一下游戏的状态,讲一下本次作业重点的内容:发布与订阅模式,这里推荐一个很详细地讲解这个模式的博客:C#中的委托和事件
    首先在触发器(AreaCollide.cs)里定义事件
//AreaCollide.cs
 public delegate void CanFollow(int state,bool isEnter);
 public static event CanFollow canFollow;

关心该事件的是巡逻兵,因为当玩家进来它要追踪,玩家出去了它要继续巡逻,所以巡逻兵要订阅或者说注册该事件:

//Patrol.cs
private void Start()
{
     // ...
     AreaCollide.canFollow += changeStateToFollow;
}
public void changeStateToFollow(int sign_,bool isEnter)
{
   if(sign == sign_ )
   {
       if (isEnter)
       {
           state = PatrolState.FOLLOW;
           player = (SSDirector.getInstance().currentScenceController as FirstController).player;
           isFollowPlayer = true;
       }           
       else
       {
           isFollowPlayer = false;
           state = PatrolState.PATROL;
           player = null;
           isMoving = false;
       }
   }

另外还有一个要发布与订阅的事件,就是当玩家逃脱了巡逻兵的追捕记分员要计分,这件事是记分员感兴趣的:

//AreaCollide.cs
public delegate void AddScore();
public static event AddScore addScore;

ScoreRecorder订阅该事件:

//ScoreRecorder.cs
void Start()
{
    AreaCollide.addScore += SetScore;//订阅事件  
}

总结一下自己对发布与订阅模式的理解,有点想回调的用法,就是订阅者对那些事件感兴趣,他就去告知发布者一声,如果该事件发生了,发布者就通知订阅者执行相应的代码。比如说你要去一家餐厅吃饭,但没位置了,你告知那里的店长(注册时间(订阅事件)),有位置时打电话给你,你就会马上赶过去。所以,当有位置了(事件触发条件),店长就会打电话给你(发布),你就会赶过去(对应的事件)。

总结

这两天真的是盯着电脑做这次作业,感觉过得很充实,学了很多东西,不仅对unity的使用进了一步,还学会了一种新的设计模式。
详细代码可以看我的GitHub——传送门,里面有游戏视频的地址。

猜你喜欢

转载自blog.csdn.net/ke1950523491/article/details/80287848