Mecanim研究之Crowd Simulation
在Crowd Simulation场景中将实现如下目标:
- 游戏开始时不断产生Dude人形模型并不断随机运动
- 若玩家按下Fire1键则不断随机产生Teddy熊模型,直到设定的总人数
##1.创建场景
本次场景与Animator Controller相似,可以将MyAnimatorController场景复制一份,并重命名为MyCrowd
为了让场景看起来更空旷,以便能看清人群,可以移除Environment对象的三个子对象ramp_001、ramp_002和ramp_003,并移动场景中集装箱的位置,使场景中间空出来。在Hierarchy视图中移除Crates和Hurdles
添加Dude和Teddy对象
- 从Project视图Assets/Mecanim Example/Characters/DudeLow目录下找到Dude_Low,将其拖动到Hierarchy视图中,重命名为Dude。
- 从Project视图Assets/Mecanim Example/Characters/Teddy目录下找到模型Teddy,将其拖动到Hierarchy视图中,再调整Teddy的方位
此时,场景中共有三个角色,分别是Player、Dude和Teddy
##2.创建动画控制器
在Project视图的Assets/_Animators目录下将MyAnimatorAC复制一份,重命名为MyCrowdAC,将其拖动到Dude和Teddy上。勾选Dude和Teddy上Animator组件的Apply Root Motion选项
双击MyCrowdAC,在Animator编辑器中修改动画参数如下:
- float型变量Speed
- float型变量Direction
- bool型变量Jump
- bool型变量Dive
在Base Layer层上保持Idle、Run和Jump三个状态及他们之间的过渡关系不变;再仿照前面添加状态Jump的步骤添加一个新的状态Dive,实现俯冲
创建状态Run和Dive之间的Transitions:
- 由状态Run过渡到Dive的条件为Dive等于true,取消勾选Has Exit Time选项
- 由状态Dive过渡到Run的条件为默认选项
之后,动画转移状态如下:

勾选Base Layer的IK Pass选项,移除Arm Layer层;移除Environment对象上的脚本MyAnimatorUI.cs
##3.生成人群
在Project视图的Assets/_Scripts目录下新建一个子目录Crowd Simulation,新建一个脚本PlayerGenerator,并将其拖动到Environment上
在脚本如下:
using UnityEngine;
using System.Collections;
public class PlayerGenerator : MonoBehaviour {
	public GameObject dude;
	public GameObject teddy;
	public int showCount = 0;
	public int maxPlayerCount = 50;
	static int count = 0;
	static float lastTime = 0;
	private float timeSpan = 0.1f;
	// Use this for initialization
	void Start () {
		lastTime = Time.time;
	}
	
	// Update is called once per frame
	void Update () {
		if (count < maxPlayerCount) {
			bool fired = Input.GetButton("Fire1");
			if ((Time.time - lastTime) > timeSpan) {
				if (dude != null && !fired) {
					Instantiate(dude, Vector3.zero, Quaternion.identity);
				}
				if (teddy != null && fired) {
					Instantiate(teddy, Vector3.zero, Quaternion.identity);
				}
				lastTime = Time.time;
				count++;
				showCount = count;
			}
		}
	}
}
为PlayerGenerator脚本中的Dude和Teddy赋值,吧Dude和Teddy拖到上边
运行游戏,不断有克隆对象生成,但重叠在一起
##4.控制人群随机运动
在Assets/_Scripts/Crowd Simulation下新建脚本CrowdMovement.cs,分别拖动到Dude和Teddy上
CrowdMovement.cs脚本如下
using UnityEngine;
using System.Collections;
public class CrowdMovement : MonoBehaviour {
	public float AvatarRange = 25;
	private Animator animator;
	private float SpeedDampTime = .25f;
	private float DirectionDampTime = .25f;
	private Vector3 TargetPosotion = Vector3.zero;
	// Use this for initialization
	void Start () {
		animator = GetComponent<Animator> ();
	}
	
	// Update is called once per frame
	void Update () {
		if (animator == null)
			return;
		int r = Random.Range (0, 50);
		animator.SetBool ("Jump", r == 20);
		animator.SetBool ("Dive", r == 30);
		if (Vector3.Distance (TargetPosotion, animator.rootPosition) > 5) {
			animator.SetFloat ("Speed", 1, SpeedDampTime, Time.deltaTime);
			Vector3 currentDir = animator.rootRotation * Vector3.forward;
			Vector3 wantedDir = (TargetPosotion - animator.rootPosition).normalized;
			if (Vector3.Dot (currentDir, wantedDir) > 0) {
				animator.SetFloat ("Direction", Vector3.Cross (currentDir, wantedDir).y, DirectionDampTime, Time.deltaTime);
			} else {
				animator.SetFloat ("Direction", Vector3.Cross (currentDir, wantedDir).y > 0 ? 1 : -1, DirectionDampTime, Time.deltaTime);
			}
		} else {
			animator.SetFloat ("Speed", 0, SpeedDampTime, Time.deltaTime);
			if (animator.GetFloat ("Speed") < 0.01f) {
				TargetPosotion = new Vector3(
					Random.Range (-AvatarRange, AvatarRange),
					0,
					Random.Range (-AvatarRange, AvatarRange));
			}
		}
	}
}
运行游戏,可以看到不停生成直到50个Dude或者Teddy(按Fire1键),并随机运动
##5.实际运行效果
运行WebGL版本
 
  