2019-12-23 10:11:36 u013628121 阅读数 19

 

状态表

状态 说明
待机 1.原点动画 2.小范围内随机走动 3.播放一些休闲动画
追击 移动到敌人面前
攻击 1.播放攻击动画 2.当攻击间隔时播放idle动画
回家 移动到出生点
死亡 1.播放死亡动画

 

条件或者事件表

条件或者事件 说明
丢失敌人 1.敌人死亡 2.敌人超出追击距离
看见敌人 1.敌人在追击距离内
我死了 我自己没血了
   

状态转换表

状态 条件或者事件 转变后状态
待机 看见敌人 追击
追击 丢失敌人 回家
攻击 丢失玩家 回家
回家 回到家了 待机
任意状态 我死了 死亡

 

 

2016-05-23 14:55:43 u014725878 阅读数 15592

角色控制脚本Character.cs,为角色添加角色控制器Character controller。

<span style="font-size:14px;">using UnityEngine;
using System.Collections;

public class Character : MonoBehaviour {

	float attackInterval = 1.5f;
	float timer = 0;
	public float speed = 5.0F;
	public float jumpSpeed = 10.0F;
	public float gravity = 20.0F;
	private Vector3 moveDirection = Vector3.zero;
	private Animator anim;
	private CharacterController controller;
	public Transform cameraTarget;
	public GameObject effect1;
	public GameObject effect2;
	public GameObject effect3;
	public GameObject AttackObj;
	float Rot_y;
	Health heroHealth;
	Enemy enemy;
	private bool isRun = false;
	private bool isAttack = false;
	private bool isDeath = false;

	void Start () {
		heroHealth = GetComponent<Health> ();
		anim = GetComponent<Animator> ();
		controller = GetComponent<CharacterController> ();

	}

	// Update is called once per frame
	void Update () {

		if (heroHealth.currentheroHelth > 0) {
			if (!isAttack) {
				Move ();
			}else
				Attack ();
		} else if(!isDeath){
			anim.SetTrigger ("Death");
			isDeath = true;
		}

	}
	void OnTriggerEnter(Collider col){
		if (col.gameObject.tag == "Monster") {
			isAttack = true;
			anim.SetBool ("isAttack", true);
			AttackObj = col.gameObject;
			enemy = AttackObj.GetComponent<Enemy> ();
		}
	}
	void OnTriggerExit(Collider col){
		if (col.gameObject.tag == "Monster") {
			isAttack = false;
			anim.SetBool("isAttack",false);
			AttackObj = null;
		}
	}
	void Move(){
		if (Input.GetKeyDown (KeyCode.LeftShift)) {
			if (!isRun) {
				isRun = true;
				anim.SetBool ("isRun", true);
			} else {
				isRun = false;
				anim.SetBool ("isRun", false);
			}
		}

		if (controller.isGrounded) {

			if (Input.GetButtonDown ("Jump")) {
				moveDirection.y = jumpSpeed;
			}
		}
		moveDirection.y -= gravity * Time.deltaTime;
		controller.Move( moveDirection * Time.deltaTime);

		if (Input.GetKey (KeyCode.A) || Input.GetKey (KeyCode.D) || Input.GetKey (KeyCode.W) || Input.GetKey (KeyCode.S)) {
			anim.SetInteger ("ActionID", 1);
			if (Input.GetKeyDown (KeyCode.W)) {
				Rot_y = cameraTarget.rotation.eulerAngles.y + 180;
			} else if (Input.GetKeyDown (KeyCode.S)) {
				Rot_y = cameraTarget.rotation.eulerAngles.y;
			} else if (Input.GetKeyDown (KeyCode.A)) {
				Rot_y = cameraTarget.rotation.eulerAngles.y + 90;
			} else if (Input.GetKeyDown (KeyCode.D)) {
				Rot_y = cameraTarget.rotation.eulerAngles.y - 90;
			}
			if (!isRun) {
				controller.Move (transform.forward * -speed * Time.deltaTime);
			} else
				controller.Move (transform.forward * -speed * 2 * Time.deltaTime);
			
			transform.rotation = Quaternion.Slerp (transform.rotation, Quaternion.Euler (0, Rot_y, 0), Time.deltaTime * 6);
		} else if (Input.GetMouseButton (1)) {
			anim.SetInteger ("ActionID", 1);
		} else { 
			anim.SetInteger ("ActionID", 0);
			anim.SetInteger ("AttackID", 0);
		}
	}

	void Attack(){
		timer += Time.deltaTime;
		if (Input.GetKeyDown (KeyCode.LeftShift)) {
			if (!isRun) {
				isRun = true;
				anim.SetBool ("isRun", true);
			} else {
				isRun = false;
				anim.SetBool ("isRun", false);
			}
		}

		if (Input.GetKey (KeyCode.A) || Input.GetKey (KeyCode.D) || Input.GetKey (KeyCode.W) || Input.GetKey (KeyCode.S)) {
			anim.SetInteger ("AttackID", 4);
			if (Input.GetKeyDown (KeyCode.W)) {
				Rot_y = cameraTarget.rotation.eulerAngles.y + 180;
			} else if (Input.GetKeyDown (KeyCode.S)) {
				Rot_y = cameraTarget.rotation.eulerAngles.y;
			} else if (Input.GetKeyDown (KeyCode.A)) {
				Rot_y = cameraTarget.rotation.eulerAngles.y + 90;
			} else if (Input.GetKeyDown (KeyCode.D)) {
				Rot_y = cameraTarget.rotation.eulerAngles.y - 90;
			}
			if (!isRun) {
				controller.Move (transform.forward * -speed * Time.deltaTime);
			} else
				controller.Move (transform.forward * -speed * 2 * Time.deltaTime);

			transform.rotation = Quaternion.Slerp (transform.rotation, Quaternion.Euler (0, Rot_y, 0), Time.deltaTime * 6);
		} else if (Input.GetKeyDown (KeyCode.J)) {
			transform.LookAt (AttackObj.transform.position);
			transform.eulerAngles += new Vector3 (0, 180, 0);
			if (timer >= attackInterval && enemy.currentEnemyHp > 0) {
				anim.SetInteger ("AttackID", 1);
				WaitAttackMonster (effect1);
				timer = 0;
			}
		} else if (Input.GetKeyDown (KeyCode.K)) {
			transform.LookAt (AttackObj.transform.position);
			transform.eulerAngles += new Vector3 (0, 180, 0);
			if (timer >= attackInterval && enemy.currentEnemyHp > 0) {
				anim.SetInteger ("AttackID", 2);
				WaitAttackMonster (effect2);
				timer = 0;
			}
		} else if (Input.GetKeyDown (KeyCode.L)) {
			transform.LookAt (AttackObj.transform.position);
			transform.eulerAngles += new Vector3 (0, 180, 0);
			if (timer >= attackInterval && enemy.currentEnemyHp > 0) {
				anim.SetInteger ("AttackID", 3);
				WaitAttackMonster (effect3);
				timer = 0;
			}
		}else if (Input.GetMouseButton (1)) {
			anim.SetInteger ("AttackID", 4);
		} else {
			anim.SetInteger ("AttackID", 0);
			anim.SetInteger ("ActionID", 0);
		}
	}
	void WaitAttackMonster(GameObject effect){
		enemy.currentEnemyHp -= heroHealth.attack;
		GameObject proObj = (GameObject)Instantiate (effect, AttackObj.transform.position, Quaternion.identity);
	}
		
}</span>
角色属性脚本Health.cs
<span style="font-size:14px;">using UnityEngine;
using System.Collections;

public class Health : MonoBehaviour {

	public int attack = 20;
	public int heroHealth = 100;
	public int currentheroHelth;
	public bool isDamage = false;
	UIManager uiManager;

	void Awake(){
		currentheroHelth = heroHealth;
		uiManager = GameObject.FindGameObjectWithTag ("UIManager").GetComponent<UIManager> ();
	}
	public void TakeDamage(int damage){
		isDamage = true;
		currentheroHelth -= damage;
		uiManager.updateHealthBar (currentheroHelth);
	}

}</span>
敌人AI战斗脚本 Enemy.cs,为敌人添加标签(tag)“Monster”,添加寻路组件NavMeshAgent,把环境静态变量变成True进行路径(Navigation)渲染Bake
<span style="font-size:14px;">using UnityEngine;
using System.Collections;
using UnityEngine.UI;

public class Enemy : MonoBehaviour {

	public Sprite Head;
	public int EnemyHp = 200;
	public int currentEnemyHp;
	GameObject hero;
	NavMeshAgent agent;
	Animator anim;
	CharacterController controller;
	public int attack = 20;
	public float attackInterval = 2f;
	float timer = 0;
	Health heroHealth;
	float dis;
	float times = 10.0f;
	int Action;
	bool heroInRange;
	bool isDeath = false;
	void Awake(){
		currentEnemyHp = EnemyHp;
		hero = GameObject.FindGameObjectWithTag ("Player");
		heroHealth = hero.GetComponent<Health> ();
		agent = GetComponent<NavMeshAgent> ();
		anim = GetComponent<Animator> ();
		controller = GetComponent<CharacterController> ();

	}
	void Update(){
		dis = Vector3.Distance (this.transform.position, hero.transform.position);

		if (currentEnemyHp > 0) {
			Activity ();
		} else if (!isDeath) {
			anim.SetTrigger ("Death");
			isDeath = true;
		}
	}
	void Activity(){
		if (dis > 15) {
			anim.SetBool ("isRun", false);
			anim.SetBool ("isAttack", false);
			times -= Time.deltaTime;
			if (times < 0) {
				Action = Random.Range (0, 5);
				times = 10.0f;
			}
			switch (Action) {
			case 0:
				anim.SetBool ("isWalk", false);
				transform.Rotate (0, 8 * Time.deltaTime, 0);
				break;
			case 1:
				anim.SetBool ("isWalk", false);
				transform.Rotate (0, -8 * Time.deltaTime, 0);
				break;
			case 2:
				anim.SetBool ("isWalk", true);
				controller.Move (transform.forward * -3 * Time.deltaTime);
				break;
			default:
				anim.SetBool ("isWalk", false);
				break;
			}
		} else if (dis > 7 && dis <= 15) {
			anim.SetBool ("isWalk", false);
			anim.SetBool ("isAttack", false);
			anim.SetBool ("isRun", true);
			transform.LookAt (hero.transform.position);
			transform.eulerAngles += new Vector3 (0, 180, 0);
			agent.SetDestination (hero.transform.position);
		} else {
			anim.SetBool ("isWalk", false);
			anim.SetBool ("isRun", false);
			anim.SetBool ("isAttack", true);
			transform.LookAt (hero.transform.position);
			transform.eulerAngles += new Vector3 (0, 180, 0);
			timer += Time.deltaTime;
			if (timer >= attackInterval) {
				Attack ();
				timer = 0;
			}
		}
	}
	void Attack(){
		int ActionAttack = Random.Range (1, 5);
		if (heroHealth.currentheroHelth > 0) {
			switch (ActionAttack) {
			case 1:
				anim.SetInteger ("AttackID", 1);
				break;
			case 2:
				anim.SetInteger ("AttackID", 2);
				break;
			case 3:
				anim.SetInteger ("AttackID", 3);
				break;
			default:
				anim.SetInteger ("AttackID", 4);
				break;
			}
			if(heroInRange)
				heroHealth.TakeDamage (attack);
		}else
			anim.SetInteger ("AttackID", 0);
	}
	void OnTriggerEnter(Collider col){
		if (col.gameObject == hero) {
			heroInRange = true;
		}
	}
	void OnTriggerExit(Collider col){
		if (col.gameObject == hero) {
			heroInRange = false;
		}
	}
}</span>
UI管理脚本UIManager.cs

<span style="font-size:14px;">using UnityEngine;
using System.Collections;
using UnityEngine.UI;

public class UIManager : MonoBehaviour {
	
	Health heroHealth;
	Character character;
	public Slider HealthBar;
	public Slider EnemyHpBar;
	public Image Head;
	public Image flashScreen;
	public float flashSpeed = 0.5f;
	public Color flashColor = new Color (1f, 0f, 0f, 0.1f);

	void Awake(){
		heroHealth = GameObject.FindGameObjectWithTag ("Player").GetComponent<Health> ();
		character = GameObject.FindGameObjectWithTag ("Player").GetComponent<Character> ();
	}
	public void updateHealthBar(int value){
		HealthBar.value = (float)value / heroHealth.heroHealth;
	}
	void Update(){
		if (!character.AttackObj) {
			EnemyHpBar.gameObject.SetActive (false);
			Head.gameObject.SetActive (false);
		} else {
			Enemy enemy = character.AttackObj.GetComponent<Enemy> ();
			EnemyHpBar.gameObject.SetActive (true);
			Head.gameObject.SetActive (true);
			EnemyHpBar.value = (float)enemy.currentEnemyHp / enemy.EnemyHp;
			Head.sprite = enemy.Head;
		}
		if (heroHealth.isDamage) {
			flashScreen.color = flashColor;
		} else {
			flashScreen.color = Color.Lerp (flashScreen.color, Color.clear, flashSpeed * Time.deltaTime);
		}
		heroHealth.isDamage = false;
	}
}</span>
最后几张运行效果图




2016-09-29 19:25:00 weixin_30455365 阅读数 23

在这个实例中,我们要做一些敌人AI的简单实现,其中自动跟随和动画是重点,我们要达到的目标如下:

1.敌人能够自动跟随主角  

2.敌人模型一共有四个动作:Idle(空闲) Run(奔跑) Attack(攻击) Death(死亡).

3.要求敌人在合适的时机能够做出合适动作

 

(一)自动跟随的实现

1)首先,新建一个场景  如图,场景里至少有两个角色:  有一个敌人(刀骷髅兵) 还有一个主角(没错,就是那个胶囊体)

2)先选择场景模型,然后在 Inspector 窗口选项 Static旁边的小三角显示出下拉菜单,确定其中 Navigation Static 被选中.  对于与场景地形无关的模型选项,则要确定没有被选中,如图所示。

                            

Navigation 窗口的选项主要是定义地形对寻路的影响。Radius 和 Height 可以理解为寻路者的半径和高度。Max Slope 是最大坡度,超过这个坡度寻路者则无法通过。Step Height 是楼梯的最大高度 ,超过这个高度寻路者则无法通过。Drop Height表示寻路者可以跳落的高度极限。Jump Distance 表示寻路者的跳跃距离极限。
 
3)选择菜单栏里的Window 选项里的Navigation选项   点击下面的bake   渲染完成后是这个样子  这些蓝色的区域就是能自动寻路的区域
                    
 
4) 然后给主角写 移动控制脚本 和 镜头控制脚本  并赋给主角(胶囊体):
 
  1 public class PlayerControl : MonoBehaviour
  2 {
  3 
  4     //定义玩家的Transform
  5     public Transform m_transform;
  6     //定义玩家的角色控制器
  7     CharacterController m_ch;
  8     //定义玩家的移动速度
  9     float m_movespeed = 10.0f;
 10     //定义玩家的重力
 11     float m_gravity = 2.0f;   
 12     //定义玩家的生命
 13     public int m_life = 5;
 14 
 15     //定义摄像机的Transform
 16     Transform m_cameraTransform;
 17     //定义摄像机的旋转角度
 18     Vector3 m_cameraRotation;
 19     //定义摄像机的高度
 20     float m_cameraHeight = 1.4f;
 21     //定义小地图摄像机
 22     public Transform m_miniMap;
 23 
 24     //定义枪口的Transform m_muzzlepPoint;
 25     Transform m_muzzlePoint;
 26     //定义射击时,射线射到的碰撞层
 27     public LayerMask m_layer;
 28     //定义射中目标后粒子效果的Transform
 29     public Transform m_fx;
 30     //定义射击音效
 31     public AudioClip m_shootAudio;
 32     //定义射击间隔时间计时器
 33     float m_shootTimer = 0;
 34 
 35     
 36 
 37     // Use this for initialization
 38     void Start()
 39     {
 40         //获取玩家本身的Transform 赋给 m_transform
 41         m_transform = this.transform;
 42         //获取玩家本身的CharacterController组件 赋给 m_ch
 43         m_ch = this.GetComponent<CharacterController>();       
 44 
 45         //摄像机的控制的初始化
 46         //获取摄像机的Transform
 47         m_cameraTransform = Camera.main.transform;
 48         //定义一个三维向量用来表示摄像机位置 并把玩家的位置赋给它 设置摄像机初始位置
 49         Vector3 pos = m_transform.position;
 50         //摄像机的Y轴坐标 为 本来的坐标加上上面定义的摄像机高度
 51         pos.y += m_cameraHeight;
 52         //把修改后的摄像机坐标重新赋给m_cameraTransform
 53         m_cameraTransform.position = pos;
 54         //把主角的旋转角度 赋给 摄像机的旋转角度
 55         m_cameraTransform.rotation = m_transform.rotation;
 56         //获取摄像机的角度
 57         m_cameraRotation = m_transform.eulerAngles;        
 58 
 59         //隐藏鼠标
 60         Cursor.visible = false;
 61     }
 62 
 63     // Update is called once per frame
 64     void Update()
 65     {
 66         //如果玩家的生命小于等于0 什么也不做
 67         if (m_life <= 0)
 68         {
 69             return;
 70         }
 71 
 72         //如果玩家的生命大于0 那么调用玩家控制函数 
 73         //移动函数
 74         MoveControl();
 75         //摄像机控制函数
 76         CameraControl();
 77         //跳跃函数
 78         Jump();
 79     }
 80 
 81 
 82     //定义玩家的控制函数
 83     void MoveControl()
 84     {
 85 
 86         //定义玩家在XYZ轴上的移动量
 87         float xm = 0, ym = 0, zm = 0;
 88 
 89         //玩家的重力运动 为 减等于玩家的重力乘以每帧时间
 90         ym -= m_gravity * Time.deltaTime;
 91 
 92         //实现玩家上下左右的运动
 93         //如果按下 W键 玩家在Z轴上的量增加
 94         if (Input.GetKey(KeyCode.W))
 95         {
 96             zm += m_movespeed * Time.deltaTime;
 97         }
 98         //如果按下 S键 玩家在Z轴上的量减少  这里用else if是因为每帧只能按下相反方向的一个键
 99         else if (Input.GetKey(KeyCode.S))
100         {
101             zm -= m_movespeed * Time.deltaTime;
102         }
103         //如果按下 A键 玩家在X轴上的量减少
104         if (Input.GetKey(KeyCode.A))
105         {
106             xm -= m_movespeed * Time.deltaTime;
107         }
108         //如果按下 D键 玩家在X轴上的量增加
109         else if (Input.GetKey(KeyCode.D))
110         {
111             xm += m_movespeed * Time.deltaTime;
112         }
113 
114         ////当玩家在地面上的时候 才能前后左右移动  在空中不能移动
115         if (!m_ch.isGrounded)
116         {
117             xm = 0;
118             zm = 0;
119         }
120 
121         //通过角色控制器的Move()函数,实现移动
122         m_ch.Move(m_transform.TransformDirection(new Vector3(xm, ym, zm))); 
123         
124 
125     }
126 
127 
128     //定义玩家的摄像机控制函数
129     void CameraControl()
130     {
131 
132         //实现对摄像机的控制
133         //定义主角在horizon方向X轴移动的量  也就是获取主角鼠标移动的量
134         float rh = Input.GetAxis("Mouse X");
135         //定义主角在Vertical 方向Y轴移动的量  
136         float rv = Input.GetAxis("Mouse Y");
137 
138 
139         //旋转摄像机
140         //把鼠标在屏幕上移动的量转化为摄像机的角度  rv(上下移动的量) 等于 角色X轴的角度    rh(水平移动的量) 等于 角色Y轴上的角度
141         m_cameraRotation.x -= rv;
142         //Debug.Log(rv);  向下时 rv 为正值(顺时针)   向上时 rv 为负值(逆时针)
143         m_cameraRotation.y += rh;
144         //Debug.Log(rh);  向右时 rh 为正值(顺时针)   向左时 rh 为负值(逆时针)
145 
146 
147         //限制X轴的移动在-60度到60度之间
148         if (m_cameraRotation.x >= 60)
149         {
150             m_cameraRotation.x = 60;
151         }
152         if (m_cameraRotation.x <= -60)
153         {
154             m_cameraRotation.x = -60;
155         }
156         m_cameraTransform.eulerAngles = m_cameraRotation;
157 
158         //使主角的面向方向与摄像机一致  用Vector3定义一个中间变量是因为 eularAngles 无法直接作为变量
159         Vector3 camrot = m_cameraTransform.eulerAngles;
160         //初始化摄像机的欧拉角为0
161         camrot.x = 0;
162         camrot.z = 0;
163         //把摄像机的欧拉角 赋给 主角
164         m_transform.eulerAngles = camrot;
165 
166         //使摄像机的位置与主角一致  用Vector3定义一个中间变量是因为 position 无法直接作为变量
167         Vector3 pos = m_transform.position;
168         //摄像机的Y轴位置 为 主角的Y轴位置加上摄像机的高度
169         pos.y += m_cameraHeight;
170         //把主角的位置 赋给 摄像机的位置
171         m_cameraTransform.position = pos;
172 
173     }
174 
175 
176     //定义玩家的Jump函数
177     void Jump()
178     {
179         //当玩家在地面上的时候  玩家的i跳才有效果
180         if (m_ch.isGrounded)
181         {
182             //此时玩家的重力为10
183             m_gravity = 10;
184             //如果按下 space键 玩家的重力变为负数  实现向上运动
185             if (Input.GetKey(KeyCode.Space))
186             {
187                 m_gravity = -8;
188             }
189         }
190         //此时玩家跳了起来
191         else
192         {
193             //玩家的重力 为 玩家的重力10 乘以 每帧的时间
194             m_gravity +=10f*Time.deltaTime;
195             //如果玩家的重力大于10的话 让他等于10
196             if (m_gravity>=10)
197             {
198                 m_gravity = 10f;
199             }
200         }
201     
202     }


5)给敌人写自动追踪脚本并赋给敌人 :

 1 public class Enemy : MonoBehaviour
 2 {
 3 
 4     //定义敌人的Transform
 5     Transform m_transform;
 6     //CharacterController m_ch;
 7 
 8     //定义动画组件
 9     Animator m_animator;
10 
11     //定义寻路组件
12     NavMeshAgent m_agent;
13 
14     //定义一个主角类的对象
15     PlayerControl m_player;
16     //角色移动速度
17     float m_moveSpeed = 0.5f;
18     //角色旋转速度
19     float m_rotSpeed = 120;
20     //定义生命值
21     int m_life = 15;
22 
23     //定义计时器 
24     float m_timer = 2;
25     //定义生成点
26     //protected EnemySpawn m_spawn;
27 
28 
29     // Use this for initialization
30     void Start()
31     {
32         //初始化m_transform 为物体本身的tranform
33         m_transform = this.transform;
34 
35         //初始化动画m_ani 为物体的动画组件
36         m_animator = this.GetComponent<Animator>();
37 
38         //初始化寻路组件m_agent 为物体的寻路组件
39         m_agent = GetComponent<NavMeshAgent>();
40 
41         //初始化主角
42         m_player = GameObject.FindGameObjectWithTag("Player").GetComponent<PlayerControl>();
43 
44 
45     }
46 
47     // Update is called once per frame
48     void Update()
49     {
50         //设置敌人的寻路目标
51         m_agent.SetDestination(m_player.m_transform.position);
52 
53         //调用寻路函数实现寻路移动
54         MoveTo();        
55 
56         
57     }
58 
59 
60     //敌人的自动寻路函数
61     void MoveTo()
62     {
63         //定义敌人的移动量
64         float speed = m_moveSpeed * Time.deltaTime;
65 
66         //通过寻路组件的Move()方法实现寻路移动
67         m_agent.Move(m_transform.TransformDirection(new Vector3(0, 0, speed)));
68     }
69 
70 
71 
72 }


这时,运行游戏,敌人就能自动跟随了.

 
 
(二)敌人动画的逻辑实现
 
1)首先,在Project面板里面create一个Animator Controller   双击它 就会发现多了一个BaseLayer面板  如下面第一个图  这个是用来控制动画的逻辑关系的    在敌人模型的动画分类里 如下图中间  选择自己需要的动画  然后拖到BaseLayer面板里面  右键标签可以创建箭头,这里为了便于讲解,选了四个动画(idle空闲  run奔跑  attack攻击   death死亡)  按照下面右图把动画标签的关系调节好.
 
           
 
2)给敌人添加动画播放脚本  这个脚本与上面的敌人脚本不同 注释的很清楚 很容易理解
  1 public class Enemy : MonoBehaviour
  2 {
  3 
  4     //定义敌人的Transform
  5     Transform m_transform;
  6     //CharacterController m_ch;
  7 
  8     //定义动画组件
  9     Animator m_animator;
 10 
 11     //定义寻路组件
 12     NavMeshAgent m_agent;
 13 
 14     //定义一个主角类的对象
 15     PlayerControl m_player;
 16     //角色移动速度
 17     float m_moveSpeed = 0.5f;
 18     //角色旋转速度
 19     float m_rotSpeed = 120;
 20     //定义生命值
 21     int m_life = 15;
 22 
 23     //定义计时器 
 24     float m_timer = 2;
 25     //定义生成点
 26     //protected EnemySpawn m_spawn;
 27 
 28 
 29     // Use this for initialization
 30     void Start()
 31     {
 32         //初始化m_transform 为物体本身的tranform
 33         m_transform = this.transform;
 34 
 35         //初始化动画m_ani 为物体的动画组件
 36         m_animator = this.GetComponent<Animator>();
 37 
 38         //初始化寻路组件m_agent 为物体的寻路组件
 39         m_agent = GetComponent<NavMeshAgent>();
 40 
 41         //初始化主角
 42         m_player = GameObject.FindGameObjectWithTag("Player").GetComponent<PlayerControl>();
 43 
 44 
 45     }
 46 
 47     // Update is called once per frame
 48     void Update()
 49     {
 50         ////设置敌人的寻路目标
 51         //m_agent.SetDestination(m_player.m_transform.position);
 52 
 53         ////调用寻路函数实现寻路移动
 54         //MoveTo();        
 55 
 56         //敌人动画的播放与转换
 57         //如果玩家的生命值小于等于0时,什么都不做 (主角死后 敌人无需再有动作)
 58         if (m_player.m_life <= 0)
 59         {
 60             return;
 61         }
 62 
 63         //获取当前动画状态(Idle Run Attack Death 中的一种)
 64         AnimatorStateInfo stateInfo = m_animator.GetCurrentAnimatorStateInfo(0);
 65 
 66         //Idle   如果角色在等待状态条 并且 没有处于转换状态  (0代表的是Base Layer)
 67         if (stateInfo.fullPathHash == Animator.StringToHash("Base Layer.Idle") && !m_animator.IsInTransition(0))
 68         {
 69             //此时把Idle状态设为false  (此时把状态设置为false 一方面Unity 动画设置里面has exit time已经取消  另一方面为了避免和后面的动画冲突 )
 70             m_animator.SetBool("Idle", false);
 71 
 72             //待机一定时间后(Timer)  之所以有这个Timer 是因为在动画播放期间 无需对下面的语句进行判断(判断也没有用) 从而起到优化的作用
 73             m_timer -= Time.deltaTime;
 74 
 75             //如果计时器Timer大于0  返回 (什么也不干,作用是优化 优化 优化)
 76             if (m_timer > 0)
 77             {
 78                 return;
 79             }
 80 
 81             //如果距离主角小于3米 把攻击动画的Bool值设为true  (激活指向Attack的通道)
 82             if (Vector3.Distance(m_transform.position, m_player.m_transform.position) < 3f)
 83             {
 84                 m_animator.SetBool("Attack", true);
 85             }
 86             //如果距离主角不小于3米        
 87             else
 88             {
 89                 //那么把计时器重置为1
 90                 m_timer = 1;
 91                 //重新获取自动寻路的位置
 92                 m_agent.SetDestination(m_player.m_transform.position);
 93                 //激活指向Run的通道
 94                 m_animator.SetBool("Run", true);
 95             }
 96         }
 97 
 98 
 99         //Run   如果角色指向奔跑状态条  并且  没有处于转换状态  (0代表的是Base Layer)
100         if (stateInfo.fullPathHash == Animator.StringToHash("Base Layer.Run") && !m_animator.IsInTransition(0))
101         {
102             //关闭指向Run的通道
103             m_animator.SetBool("Run", false);
104             //计时器时间随帧减少
105             m_timer -= Time.deltaTime;
106             //计时器时间小于0时 重新获取自动寻路的位置  重置计时器时间为1
107             if (m_timer < 0)
108             {
109                 m_agent.SetDestination(m_player.m_transform.position);
110                 m_timer = 1;
111             }
112 
113             //调用跟随函数
114             MoveTo();
115 
116             //当角色与主角的距离小于等于3米时  
117             if (Vector3.Distance(m_transform.position, m_player.m_transform.position) <= 3f)
118             {
119                 //清楚当前路径 当路径被清除  代理不会开始寻找新路径直到SetDestination 被调用
120                 m_agent.ResetPath();
121                 //激活指向Attack的通道
122                 m_animator.SetBool("Attack", true);
123 
124             }
125         }
126 
127 
128         //Attack 如果角色指向攻击状态条  并且  没有处于转换状态   (0代表的是Base Layer)
129         if (stateInfo.fullPathHash == Animator.StringToHash("Base Layer.Attack") && !m_animator.IsInTransition(0))
130         {
131             //调用转向函数
132             RotationTo();
133 
134             //关闭指向Attack的通道
135             m_animator.SetBool("Attack", false);
136 
137             //当播放过一次动画后  normalizedTime 实现状态的归1化(1就是整体和全部)  整数部分是时间状态的已循环数  小数部分是当前循环的百分比进程(0-1)            
138             if (stateInfo.normalizedTime >= 1.0f)
139             {
140                 //激活指向Idle的通道
141                 m_animator.SetBool("Idle", true);
142                
143                 //计时器时间重置为2
144                 m_timer = 2;
145 
146 
147                 //m_player.OnDamage(1);
148 
149             }
150         }
151 
152 
153         //Death  如果角色指向死亡状态条  并且  没有处于转换状态   (0代表的是Base Layer)
154         if (stateInfo.fullPathHash == Animator.StringToHash("Base Layer.Death") && !m_animator.IsInTransition(0))
155         {
156             //摧毁这个物体的碰撞体
157             Destroy(this.GetComponent<Collider>());
158 
159             //自动寻路时间被归零  角色不再自动移动
160             m_agent.speed = 0;
161 
162             //死亡动画播放一遍后 角色死亡
163             if (stateInfo.normalizedTime >= 1.0f)
164             {
165                 //OnDeath()
166             }
167 
168 
169         }
170     }
171 
172 
173     //敌人的自动寻路函数
174     void MoveTo()
175     {
176         //定义敌人的移动量
177         float speed = m_moveSpeed * Time.deltaTime;
178 
179         //通过寻路组件的Move()方法实现寻路移动
180         m_agent.Move(m_transform.TransformDirection(new Vector3(0, 0, speed)));
181     }
182 
183 
184     //敌人转向目标点函数
185     void RotationTo()
186     {
187         //定义当前角度 
188         Vector3 oldAngle = m_transform.eulerAngles;
189         //获得面向主角的角度
190         m_transform.LookAt(m_player.m_transform);
191 
192         //定义目标的方向  Y轴方向  也就是敌人左右转动面向玩家
193         float target = m_transform.eulerAngles.y;
194         //转向目标的速度 等于时间乘以旋转角度
195         float speed = m_rotSpeed * Time.deltaTime;
196         //通过MoveTowardsAngle() 函数获得转的角度
197         float angle = Mathf.MoveTowardsAngle(oldAngle.y, target, speed);
198 
199         //实现转向
200         m_transform.eulerAngles = new Vector3(0, angle, 0);
201     }
202 
203 }

自此,一个会自动寻找主角 并 攻击 而且 有动画 的敌人就做好了

 
 
---未完待续---

 

转载于:https://www.cnblogs.com/qiaogaojian/p/5920998.html

2016-12-16 14:38:55 mango9126 阅读数 1544

人工智能这个东西在游戏中是非常重要的,人工智能说简单了就是根据随机的数字让敌人执行一些动作或逻辑,说难了TA需要一个非常复杂的算法,本文我主要说说Unity3D中人工智能的脚本如何来编写。

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

 

Unity3D研究院之游戏开发中的人工智能AI(三十八) - 雨松MOMO程序研究院 - 1

 

下面时这段简单AI的脚本

AI.CS

 

 

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

 

Unity3D研究院之游戏开发中的人工智能AI(三十八) - 雨松MOMO程序研究院 - 2

 

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

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

2017-03-26 18:16:25 yu__jiaoshou 阅读数 958

Unity 3D - AI锁定玩家 :

这里写图片描述

官方API 描述 :

这里写图片描述

调整朝向C#代码 :

//Zombie.cs  敌人的C#脚本  
//通常AI锁定 在Update里处理

void Update () {

    //开始角度  敌人自身角度
    Quaternion beginAngle = transform.rotation;

    //需要旋转角度
    //通过 Quaternion.LookRotation 函数 传入 (玩家的位置) 和 (敌人的位置) 的差值 
    Quaternion needRotateAngle = Quaternion.LookRotation (player.transform.position - transform.position);

    transform.rotation = Quaternion.Slerp (beginAngle, needRotateAngle, 0.2f);
}

调用Quaternion.Slerp方法 , 传入三个参数 :

  • 第一个参数 : 旋转前的角度 , 通常是自己的角度 ( rotation ) .
  • 第二个参数 : 调用 Quaternion 的 LookRotation 方法 , 获取需要旋转的角度 .
  • 第三个参数 : 旋转速度 ( float ) .

刷新敌人,限制敌人数量。

博文 来自: Testiness_Wind
没有更多推荐了,返回首页