今天开始做人物--会跑会跳的韩菱纱(如何制作第三人称人物控制)

37 篇文章 1 订阅

一个游戏最直观的体验就是上手的游戏化身操作了,如何让游戏化身会跑会跳,甚至卖卖萌,虽然看起来基本,但确是游戏中最重要的,因为游戏化身与游戏内的其他原件交互,都是建立在人物化身的操控系统之下的,比如说,我们想与场景中的一个npc做交互,比如对话,或者购买物品,第一点,我们要跑(走)到npc面前,而想做到这个我们就需要对玩家化身做操控。u3d这一点做得非常好,甚至自己自带了一个玩家的控制系统,包括第一人称和第三人称两种模式,我们这里主要讨论第三人称,因为不是讲解系统提供的控制系统所以我们主要入正题,讲解UnityChan插件系统,为啥用它呢,因为开始我想用Character System4来做,但用了之后发现,Character System4因为主要是针对摇杆控制器操作的,动作匹配也与摇杆的运动很契合,但实际用到电脑游戏中他的动作有时看起来就有些奇怪,所以最后选用了UnityChan插件系统,这个系统不仅包括一个比较完善的人物动作控制,还包括脸部运动的(表情系统--不过这个需要美术的支持,我这里没能实现面部表情,-具体想学习的话,某网站有一套免费的11课时的专门讲解UnityChan插件的视频教程,喜欢的可以自己看看,如果将来有条件,我会在后续Demo里加入表情系统)我们先看一下这个插件的运行效果


看下两个开发窗口

插件中使用的预制件,主要控制的人物预制件,unitychan上面捆绑了2个脚本,我们看下代码

//
// Mecanimのアニメーションデータが、原点で移動しない場合の Rigidbody付きコントローラ
// サンプル
// 2014/03/13 N.Kobyasahi
//
using UnityEngine;
using System.Collections;

// 必要なコンポーネントの列記
[RequireComponent(typeof (Animator))]
[RequireComponent(typeof (CapsuleCollider))]
[RequireComponent(typeof (Rigidbody))]

public class UnityChanControlScriptWithRgidBody : MonoBehaviour
{

	public float animSpeed = 1.5f;				// アニメーション再生速度設定
	public float lookSmoother = 3.0f;			// a smoothing setting for camera motion
	public bool useCurves = true;				// Mecanimでカーブ調整を使うか設定する
												// このスイッチが入っていないとカーブは使われない
	public float useCurvesHeight = 0.5f;		// カーブ補正の有効高さ(地面をすり抜けやすい時には大きくする)

	// 以下キャラクターコントローラ用パラメタ
	// 前進速度
	public float forwardSpeed = 7.0f;
	// 後退速度
	public float backwardSpeed = 2.0f;
	// 旋回速度
	public float rotateSpeed = 2.0f;
	// ジャンプ威力
	public float jumpPower = 3.0f; 
	// キャラクターコントローラ(カプセルコライダ)の参照
	private CapsuleCollider col;
	private Rigidbody rb;
	// キャラクターコントローラ(カプセルコライダ)の移動量
	private Vector3 velocity;
	// CapsuleColliderで設定されているコライダのHeiht、Centerの初期値を収める変数
	private float orgColHight;
	private Vector3 orgVectColCenter;
	
	private Animator anim;							// キャラにアタッチされるアニメーターへの参照
	private AnimatorStateInfo currentBaseState;			// base layerで使われる、アニメーターの現在の状態の参照

	private GameObject cameraObject;	// メインカメラへの参照
		
// アニメーター各ステートへの参照
	static int idleState = Animator.StringToHash("Base Layer.Idle");
	static int locoState = Animator.StringToHash("Base Layer.Locomotion");
	static int jumpState = Animator.StringToHash("Base Layer.Jump");
	static int restState = Animator.StringToHash("Base Layer.Rest");

// 初期化
	void Start ()
	{
		// Animatorコンポーネントを取得する
		anim = GetComponent<Animator>();
		// CapsuleColliderコンポーネントを取得する(カプセル型コリジョン)
		col = GetComponent<CapsuleCollider>();
		rb = GetComponent<Rigidbody>();
		//メインカメラを取得する
		cameraObject = GameObject.FindWithTag("MainCamera");
		// CapsuleColliderコンポーネントのHeight、Centerの初期値を保存する
		orgColHight = col.height;
		orgVectColCenter = col.center;
}
	
	
// 以下、メイン処理.リジッドボディと絡めるので、FixedUpdate内で処理を行う.
	void FixedUpdate ()
	{
		float h = Input.GetAxis("Horizontal");				// 入力デバイスの水平軸をhで定義
		float v = Input.GetAxis("Vertical");				// 入力デバイスの垂直軸をvで定義
		anim.SetFloat("Speed", v);					// Animator側で設定している"Speed"パラメタにvを渡す
		anim.SetFloat("Direction", h); 					// Animator側で設定している"Direction"パラメタにhを渡す
		anim.speed = animSpeed;						// Animatorのモーション再生速度に animSpeedを設定する
		currentBaseState = anim.GetCurrentAnimatorStateInfo(0);	// 参照用のステート変数にBase Layer (0)の現在のステートを設定する
		rb.useGravity = true;//ジャンプ中に重力を切るので、それ以外は重力の影響を受けるようにする
		
		
		
		// 以下、キャラクターの移動処理
		velocity = new Vector3(0, 0, v);		// 上下のキー入力からZ軸方向の移動量を取得
		// キャラクターのローカル空間での方向に変換
		velocity = transform.TransformDirection(velocity);
		//以下のvの閾値は、Mecanim側のトランジションと一緒に調整する
		if (v > 0.1) {
			velocity *= forwardSpeed;		// 移動速度を掛ける
		} else if (v < -0.1) {
			velocity *= backwardSpeed;	// 移動速度を掛ける
		}
		
		if (Input.GetButtonDown("Jump")) {	// スペースキーを入力したら

			//アニメーションのステートがLocomotionの最中のみジャンプできる
			if (currentBaseState.nameHash == locoState){
				//ステート遷移中でなかったらジャンプできる
				if(!anim.IsInTransition(0))
				{
						rb.AddForce(Vector3.up * jumpPower, ForceMode.VelocityChange);
						anim.SetBool("Jump", true);		// Animatorにジャンプに切り替えるフラグを送る
				}
			}
		}
		

		// 上下のキー入力でキャラクターを移動させる
		transform.localPosition += velocity * Time.fixedDeltaTime;

		// 左右のキー入力でキャラクタをY軸で旋回させる
		transform.Rotate(0, h * rotateSpeed, 0);	
	

		// 以下、Animatorの各ステート中での処理
		// Locomotion中
		// 現在のベースレイヤーがlocoStateの時
		if (currentBaseState.nameHash == locoState){
			//カーブでコライダ調整をしている時は、念のためにリセットする
			if(useCurves){
				resetCollider();
			}
		}
		// JUMP中の処理
		// 現在のベースレイヤーがjumpStateの時
		else if(currentBaseState.nameHash == jumpState)
		{
			cameraObject.SendMessage("setCameraPositionJumpView");	// ジャンプ中のカメラに変更
			// ステートがトランジション中でない場合
			if(!anim.IsInTransition(0))
			{
				
				// 以下、カーブ調整をする場合の処理
				if(useCurves){
					// 以下JUMP00アニメーションについているカーブJumpHeightとGravityControl
					// JumpHeight:JUMP00でのジャンプの高さ(0〜1)
					// GravityControl:1⇒ジャンプ中(重力無効)、0⇒重力有効
					float jumpHeight = anim.GetFloat("JumpHeight");
					float gravityControl = anim.GetFloat("GravityControl"); 
					if(gravityControl > 0)
						rb.useGravity = false;	//ジャンプ中の重力の影響を切る
										
					// レイキャストをキャラクターのセンターから落とす
					Ray ray = new Ray(transform.position + Vector3.up, -Vector3.up);
					RaycastHit hitInfo = new RaycastHit();
				// 高さが useCurvesHeight 以上ある時のみ、コライダーの高さと中心をJUMP00アニメーションについているカーブで調整する
					if (Physics.Raycast(ray, out hitInfo))
					{
						if (hitInfo.distance > useCurvesHeight)
						{
							col.height = orgColHight - jumpHeight;		// 調整されたコライダーの高さ
							float adjCenterY = orgVectColCenter.y + jumpHeight;
							col.center = new Vector3(0, adjCenterY, 0);	// 調整されたコライダーのセンター
						}
						else{
							// 閾値よりも低い時には初期値に戻す(念のため)					
							resetCollider();
						}
					}
				}
				// Jump bool値をリセットする(ループしないようにする)				
				anim.SetBool("Jump", false);
			}
		}
		// IDLE中の処理
		// 現在のベースレイヤーがidleStateの時
		else if (currentBaseState.nameHash == idleState)
		{
			//カーブでコライダ調整をしている時は、念のためにリセットする
			if(useCurves){
				resetCollider();
			}
			// スペースキーを入力したらRest状態になる
			if (Input.GetButtonDown("Jump")) {
				anim.SetBool("Rest", true);
			}
		}
		// REST中の処理
		// 現在のベースレイヤーがrestStateの時
		else if (currentBaseState.nameHash == restState)
		{
			//cameraObject.SendMessage("setCameraPositionFrontView");		// カメラを正面に切り替える
			// ステートが遷移中でない場合、Rest bool値をリセットする(ループしないようにする)
			if(!anim.IsInTransition(0))
			{
				anim.SetBool("Rest", false);
			}
		}
	}

	void OnGUI()
	{
		GUI.Box(new Rect(Screen.width -260, 10 ,250 ,150), "Interaction");
		GUI.Label(new Rect(Screen.width -245,30,250,30),"Up/Down Arrow : Go Forwald/Go Back");
		GUI.Label(new Rect(Screen.width -245,50,250,30),"Left/Right Arrow : Turn Left/Turn Right");
		GUI.Label(new Rect(Screen.width -245,70,250,30),"Hit Space key while Running : Jump");
		GUI.Label(new Rect(Screen.width -245,90,250,30),"Hit Spase key while Stopping : Rest");
		GUI.Label(new Rect(Screen.width -245,110,250,30),"Left Control : Front Camera");
		GUI.Label(new Rect(Screen.width -245,130,250,30),"Alt : LookAt Camera");
	}


	// キャラクターのコライダーサイズのリセット関数
	void resetCollider()
	{
	// コンポーネントのHeight、Centerの初期値を戻す
		col.height = orgColHight;
		col.center = orgVectColCenter;
	}
}
看下UnityChanControlScriptWithRgidBody.cs这个类,UnityChan这个插件自带了日文和英文两种语言的文档,代码中注释也相当详细,代码也并不难懂,这里我大致的分析下,这段代码开头,
[RequireComponent(typeof (Animator))]
[RequireComponent(typeof (CapsuleCollider))]
[RequireComponent(typeof (Rigidbody))]

使得人物预制件需要3个系统组件来支持,作用呢,animator是u3d全新的动画系统的控制组件,CapsuleCollider是一个包裹人物预制件的碰撞体,而最后那个是刚体系统,

代码的FixedUpdate()方法中主要是anim.SetFloat("Speed", v);这种动画控制代码那么,具体代码是怎么控制的呢,我们看下animator中载入的控制器


我们可以看到控制器默认动作未Idle,而通过迁移线与其他动作相连,而左下方提供了控制动作变化的变量,我们就是通过变量的控制来达到动作的转换的,

我们以Jump变量为例,代码中if (Input.GetButtonDown("Jump")){}也就是空格按下,这时候如果符合条件,如人物正在跑动,那么anim.SetBool("Jump", true);这样动作控制就完成了,而另外一个类FaceUpdate.cs这个类非常简单

using UnityEngine;
using System.Collections;

public class FaceUpdate : MonoBehaviour
{
	public AnimationClip[] animations;

	Animator anim;

	public float delayWeight;

	void Start ()
	{
		anim = GetComponent<Animator> ();
	}

	void OnGUI ()
	{
		foreach (var animation in animations) {
			if (GUILayout.Button (animation.name)) {
				anim.CrossFade (animation.name, 0);
			}
		}
	}

	float current = 0;


	void Update ()
	{

		if (Input.GetMouseButton (0)) {
			current = 1;
		} else {
			current = Mathf.Lerp (current, 0, delayWeight);
		}
		anim.SetLayerWeight (1, current);
	}
}
其实就是个由屏幕按钮控制人物表情,播放的功能,这里需要注意一点可能就是一个不太注意的细节就是

这个遮罩,如果按照这个系统做的表情播放不出来,那么建议去注意下这个,其他的因为这个代码很好理解,也没什么可说的,如果真的就不懂,网上有一套UnityChan的11集的免费视频教程,大家可以去看看,里面包括如果用maya来建立人物都写得很详细,最后我们就看看如何利用这些代码呢?首先我们在网上下载一个人物模型我们以韩菱纱为例,注意我们使用的模型可以没有绑定动作,但必须顶点骨骼绑定完成的,否则,我们导出的fbx文件将是石膏模,我们在场景中是无法使用的,具体怎么导出,在菜单里找导出,然后选fbx文件格式就行了,当然导其他的格式也可以,但需要注意的是,要是u3d支持的3d格式,然后我们把导好的模型复制进工程文件夹,,记得先把贴图赋一下,之后,我们点击hls0这个fbx文件我们设置生成Avatar具体为什么设置avatar我建议不明白的小伙伴去看这篇文章配置Avatar,然后把hls0做成预制件,具体做法就是拖到就可以了,之后我们在属性窗口中,绑定我们需要的里面的参数需要自己按照自己的模型来调整关于数据我们可以按照需要自己调整,比如我们可以改变JumpPower来改变化身跳跃的高度,其实这个场景中还有一个比较重要的预制件,
就是摄像机,我们依靠这个来达到相机跟谁化身效果,但,untiychan里面的两个摄像机控制方式都跟我们平时玩的3剑游戏控制方式不同,所以在我的仙剑Demo中没有用到这个预制件,等后面我会讲解我们在Demo中用到的镜头跟随方法,最后我们看下我们最终做的人物是什么样的吧,

评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值