2016-10-22 15:09:36 u013108312 阅读数 1438
using UnityEngine;
using System.Collections;

public class SteeringForPursuit : Steering {

    public GameObject target;
    private Vector3 desiredVelocity;
    private Vehicle m_vehicle;
    private float maxSpeed;


    void Start () {
        m_vehicle = GetComponent<Vehicle>();
        maxSpeed = m_vehicle.maxSpeed;
    }


    public override Vector3 Force()
    {
        Vector3 toTarget = target.transform.position - transform.position;

        float relativeDirection = Vector3.Dot(transform.forward, target.transform.forward);

        if ((Vector3.Dot(toTarget, transform.forward) > 0) && (relativeDirection < -0.95f))
        {
            desiredVelocity = (target.transform.position - transform.position).normalized * maxSpeed;
            return (desiredVelocity - m_vehicle.velocity);
        }

        float lookaheadTime = toTarget.magnitude / (maxSpeed + target.GetComponent<Vehicle>().velocity.magnitude);

        desiredVelocity = (target.transform.position + target.GetComponent<Vehicle>().velocity * lookaheadTime - transform.position).normalized * maxSpeed;
        return (desiredVelocity - m_vehicle.velocity);

    }
}
2013-10-15 09:41:25 woailvmengmeng 阅读数 2512

    很久没有写Unity3D相关的东西了,是因为这段时间我深陷一款IOS的软件开发中。不过以后我还是会回归Unity3D游戏开发的。什么语言都在用,生活与工作都挺给力的嚯嚯。今天还是打开了久违的Unity3D编辑器,那我就写点东西吧,今天我想说的是游戏中的人工智能。人工智能这个东西在游戏中是非常重要的,人工智能说简单了就是根据随机的数字让敌人执行一些动作或逻辑,说难了TA需要一个非常复杂的算法,本文我主要说说Unity3D中人工智能的脚本如何来编写。

        首先你应该搞清楚的一点AI脚本属于一个工具类脚本,工具类脚本的含义就是他应当是由策划人员来绑定游戏对象使用的。也就是说AI脚本程序员应当写的非常的灵活,策划人员可以通过修改脚本对外的变量数值接口就能控制其中的敌人AI。接着创建一个c#脚本AI.CS ,如下图所示,目前脚本对外留出枚举接口变量,策划人员在使用这条脚本时选择对应敌人类型即可。(注:这里仅仅是示例,细致的话还可以将很多敌人详细的信息写入,如:攻击速度、技能类型、移动速度、命中率、攻击百分比、等等,但是一定要让你的脚本写的比较灵活,策划人员在外面选择即可完成)因为目前是一个示例,所以我在这里只简单的区分的敌人类型。

 

 

下面时这段简单AI的脚本

AI.CS

001 using UnityEngine;
002 using System.Collections;
003  
004 //这里是枚举选择敌人类型
005 public enum EnemyType
006 {
007     Enemy0,
008     Enemy1
009 }
010  
011 public class AI : MonoBehaviour {
012  
013     //敌人类型枚举 有策划人员选择
014     public EnemyType enemyType = EnemyType.Enemy0;
015  
016     //主角游戏对象
017     public GameObject player;
018  
019     //敌人状态 普通状态 旋转状态 奔跑状态 追击主角状态 攻击主角状态
020     private const int EMEMY_NORMAL=0;
021     private const int EMEMY_ROTATION=1;
022     private const int EMEMY_RUN = 2;
023     private const int EMEMY_CHASE = 3;
024     private const int EMEMY_ATTACK = 4;
025  
026     //记录当前敌人状态 根据不同类型 敌人播放不同动画
027     private int state;
028     //旋转状态,敌人自身旋转
029     private int rotation_state;
030     //记录敌人上一次思考时间
031     private float aiThankLastTime;
032  
033     void Start ()
034     {
035         //初始话标志敌人状态 以及动画为循环播放
036         state = EMEMY_NORMAL;
037         this.animation.wrapMode = WrapMode.Loop;
038     }
039  
040     void Update ()
041     {
042         //根据策划选择的敌人类型 这里面会进行不同的敌人AI
043         switch(enemyType)
044         {
045         case EnemyType.Enemy0:
046             updateEnemyType0();
047             break;
048         case EnemyType.Enemy1:
049             updateEnemyType1();
050             break;
051         }
052     }
053  
054     //更新第一种敌人的AI
055     void updateEnemyType0()
056     {
057         //这个AI比较简单, 当主角与他的距离小于10米时,他将始终朝向这主角
058         if(Vector3.Distance(player.transform.position,this.transform.position) <= 10)
059         {
060             this.transform.LookAt(player.transform);
061         }
062     }
063  
064     //更新第二种敌人的AI
065     void updateEnemyType1()
066     {
067  
068         //判断敌人是否开始思考
069         if(isAIthank())
070         {
071             //敌人开始思考
072             AIthankEnemyState(3);
073         }else
074         {
075             //更新敌人状态
076             UpdateEmenyState();
077         }
078     }
079  
080     int getRandom(int count)
081     {
082  
083          return new System.Random().Next(count);
084  
085     }
086  
087     bool isAIthank()
088     {
089         //这里表示敌人每3秒进行一次思考
090         if(Time.time - aiThankLastTime >=3.0f)
091         {
092             aiThankLastTime = Time.time;
093             return true;
094  
095         }
096         return false;
097     }
098  
099     //敌人在这里进行思考
100     void AIthankEnemyState(int count)
101     {
102         //开始随机数字。
103         int d = getRandom(count);
104  
105         switch(d)
106         {
107         case 0:
108             //设置敌人为站立状态
109             setEmemyState(EMEMY_NORMAL);
110             break;
111         case 1:
112             //设置敌人为旋转状态
113             setEmemyState(EMEMY_ROTATION);
114             break;
115         case 2:
116             //设置敌人为奔跑状态
117             setEmemyState(EMEMY_RUN);
118             break;
119         }
120  
121     }
122  
123     void setEmemyState(int newState)
124     {
125         if(state == newState)
126             return;
127         state = newState;
128  
129         string animName = "Idle";
130         switch(state)
131         {
132         case EMEMY_NORMAL:
133             animName  =  "Idle";
134             break;
135         case EMEMY_RUN:
136             animName  =  "Run";
137             break;
138         case EMEMY_ROTATION:
139             animName  =  "Run";
140             //当敌人为旋转时, 开始随机旋转的角度系数
141             rotation_state = getRandom(4);
142             break;
143         case EMEMY_CHASE:
144             animName  =  "Run";
145             //当敌人进入追击状态时,将面朝主角方向奔跑
146             this.transform.LookAt(player.transform);
147             break;
148         case EMEMY_ATTACK:
149             animName  =  "Attack";
150             //当敌人进入攻击状态时,继续朝向主角开始攻击砍人动画
151             this.transform.LookAt(player.transform);
152             break;
153         }
154  
155         //避免重复播放动画,这里进行判断
156         if(!this.animation.IsPlaying(animName))
157         {
158             //播放动画
159             this.animation.Play(animName);
160         }
161  
162     }
163  
164     //在这里更新敌人状态
165     void UpdateEmenyState()
166     {
167         //判断敌人与主角之间的距离
168         float distance = Vector3.Distance(player.transform.position,this.transform.position);
169         //当敌人与主角的距离小于10 敌人将开始面朝主角追击
170         if(distance <= 10)
171         {
172             //当敌人与主角的距离小与3 敌人将开始面朝主角攻击
173             if(distance <= 3)
174             {
175                 setEmemyState(EMEMY_ATTACK);
176             }else
177             {
178                 //否则敌人将开始面朝主角追击
179                 setEmemyState(EMEMY_CHASE);
180             }
181  
182         }else
183         {
184             //敌人攻击主角时 主角迅速奔跑 当它们之间的距离再次大于10的时候 敌人将再次进入正常状态 开始思考
185             if(state == EMEMY_CHASE || state == EMEMY_ATTACK)
186             {
187                 setEmemyState(EMEMY_NORMAL);
188             }
189  
190         }
191  
192         switch(state)
193         {
194         case EMEMY_ROTATION:
195             //旋转状态时 敌人开始旋转, 旋转时间为1秒 这样更加具有惯性
196             transform.rotation = Quaternion.Lerp (transform.rotation, Quaternion.Euler(0,rotation_state * 90,0),  Time.deltaTime * 1);
197             break;
198         case EMEMY_RUN:
199             //奔跑状态,敌人向前奔跑
200             transform.Translate(Vector3.forward *0.02f);
201             break;
202         case EMEMY_CHASE:
203             //追击状态 敌人向前开始追击
204             transform.Translate(Vector3.forward *0.02f);
205             break;
206         case EMEMY_ATTACK:
207  
208             break;
209         }
210  
211     }
212 }

 

 

如下图所示,我们在游戏世界中添加两个敌人,此时给敌人们都绑定上AI的脚本,编辑器中设置不同的敌人类型,敌人执行各自的生命周期,当你控制主角与接近敌人时,敌人开始追击你并且向你展开攻击。

 

 

         demo仅仅是一个示例,主要希望大家明白一个道理。在编写Unity游戏脚本时,一定要想想这条脚本和对象的生命周期,切记每一条脚本只管和自己有关的东西,和自己无关的东西一概不要管。不然你会发现你的脚本会越写越乱,下一篇文章我会写点我平时在Unity3D开发中时如何搭建架构的,希望大家多多讨论,互相学习。

下载地址:http://vdisk.weibo.com/s/hmJUs