2015-02-07 08:58:59 book_longssl 阅读数 3508
  • unity3D游戏/AR/VR在线就业班

    本套课程是一套完整的 Unity3D-游戏/AR/VR 学习课程,具体的课程内容包括《C#语言》、《Unity引擎》、《编程思想》,《商业级项目实践》等开发课程,引导您一步步循序渐进、由易到难,终获得Unity 3D/游戏/AR/VR工程师的岗位技能。

    12792 人正在学习 去看看 宋晓波


 

通过创建角色动画Avatar,在新的动画系统Mecanim中,Unity就设置了角色动画的骨架和蒙皮信息,从而就可以在Unity中实现角色动画了。

切换到动画(Animations)选项卡。选中导入动画(Import Animation)的选项。如果该文件中有动画数据,可以看到动画剪辑的列表(Clips)。

Tips:

Rig选项卡中动画类型(Animation Type)如果选则的是旧版(Legacy),Animations中的属性仍然是旧版的。


 


根节点的运动(Root Motion)。角色的位置和方向不要存放在骨骼中。角色原点在定义avatar的时候指定。Root Transform 的变化实时计算并传递到Game Object中,以使游戏物体运动。

Root Transform Rotation 根节点的旋转。

Bake into Pose:该动画播放期间,根的朝向不变。

开始帧和结束帧,比较相近时才启用这个选项。如果比较相近则显示绿点,否则显示红点。一般直线走或直线跑是可以选中该选项。

Root Transform Position (Y),和Root Transform Position (XZ) 分别锁定Y轴上的移动,和XZ平面上的移动。

遮罩 Avatar Body Mask

Create-> Avatar Body Mask.可以创建一个遮罩。Body Mask组件。身体遮罩资源可以在动画控制器中重用。通过身体遮罩可以对动画里面特定的身体部位进行激活或禁止。身体部位包括:头、左手臂、右手臂、左手、右手、左腿、右腿、Root (以脚的背面作为标志)。在身体遮罩界面,您可以给手或脚指定是否需要IK(反向运动学),由此可决定在动画混合里面是否包括IK曲线。

通过身体遮罩可以对动画里面特定的身体部位进行激活或禁止。在网格导入观察器和动画层的动画标签里面可以设置身体遮罩。身体遮罩让您能根据角色需求精确的裁剪动画。例如,您有一个常见的行走动画,包括手臂和腿的动作,但是如果现在一个角色的双手举着巨大的物体,您当然不会希望她在行走的时候手臂来回摆动。当然,您可以通过身体遮罩的切换,继续使用常规的行走动画。

Mecanim动画系统中,Animations 标签选项卡下,每一个动作剪辑Clips都有自带的Body Mask组件。

Curves 曲线

 


曲线控制动画绑定的一个值(如移动速度),动画原地播放的情况下,用脚本获取该值,精确控制移动。



 

2015-02-06 08:26:18 book_longssl 阅读数 5879
  • unity3D游戏/AR/VR在线就业班

    本套课程是一套完整的 Unity3D-游戏/AR/VR 学习课程,具体的课程内容包括《C#语言》、《Unity引擎》、《编程思想》,《商业级项目实践》等开发课程,引导您一步步循序渐进、由易到难,终获得Unity 3D/游戏/AR/VR工程师的岗位技能。

    12792 人正在学习 去看看 宋晓波


 

MecAnim通过Avatar这个代理来实现设置角色动画中的骨架和蒙皮。动画类型(Animation Type)选择人形动画(Hmumanoid)。Avatar设定选择 “Create From This Mode”。则可以为这个模型定义Avatar。如果选择 “Copy From Other Avatar”,则可以从其他定义好的Avatar拷贝过来。


在 Avatar Definition 选择 “Create From This Model”的情况下,点击一下应用(Apply)按钮。“ Configure…” 按钮变为可用状态。

点击Configure… 按钮。(如果需要保存当前场景在弹出对话框中选择save保存当前场景。)配置Avatar。


Inspector面板中Mapping选项卡下 可以看到骨骼的匹配情况,一般情况下都可以很好的正确匹配(绿色显示)。


如果需要修改,可以重新设置一下骨骼和avatar中节点的对应关系。 如果不需要修改,可以直接点完成(Done)。

通过创建Avatar中的Muscles选项卡,实现角色动画中蒙皮到Unity的映射,也就是说, 通过该映射,Unity可以设置角色中的蒙皮信息。肌肉(Muscles)设置关节直接运动限制。



2014-09-04 13:13:25 qinyuanpei 阅读数 8924
  • unity3D游戏/AR/VR在线就业班

    本套课程是一套完整的 Unity3D-游戏/AR/VR 学习课程,具体的课程内容包括《C#语言》、《Unity引擎》、《编程思想》,《商业级项目实践》等开发课程,引导您一步步循序渐进、由易到难,终获得Unity 3D/游戏/AR/VR工程师的岗位技能。

    12792 人正在学习 去看看 宋晓波

        各位朋友,大家好,我是秦元培,欢迎大家关注我的博客,我的博客地址blog.csdn.net/qinyuanpei。今天呢,我们来说说Unity3D中的角色控制,这篇文章并非关注于Unity3D中的某项具体内容,而是博主在经过大量的实践后所得的感悟。今天的文章从内容上可以分为2种模式、1个组件、1种模型,希望对大家学习Unity3D起到良好的推动作用。好了,下面我们就正式开始今天的文章吧。

 

        一、2种模式

       众所周知,角色控制有第一人称和第三人称两种情况,在RPG游戏中通常以第三人称的形式出现。而对于第三人称角色控制而言,通常有2种模式。

       第一种模式中,角色在Z轴方向向前或者向后移动、绕自身Y轴旋转。当角色旋转时,摄像机会根据角色旋转的角度旋转到对应的位置,使摄像机始终正对着角色的背面,这样玩家在控制角色的过程中,只能看到玩家的背面。当角色移动时,摄像机会保持与玩家间的一定距离,然后跟随角色进行移动。采用这种模式的代表游戏是《仙剑奇侠传四》,这种模式的优点是操作简单,依靠四个方向键就可以完成角色的控制。这种模式的缺点同样很明显,因为摄像机始终面向角色背面,所以玩家无法看到角色的正面。从这个角度上来说,这种模式不能称之为真正的3D,因为玩家的视角是锁死的。那么在Unity3D中如何实现这种模式的角色控制呢?我们只需要为Main Camera添加一个SmoothFollow脚本,这样我们就可以使摄像机始终面向玩家的背面。对于角色这部分,我们只需要完成Z轴方向上的前进与后退,Y轴方向上的旋转即可。这部分脚本我们将放在后面来讲,因为这里需要用到一个重要的组件。


       下面来介绍第二种模式,这种模式是现下网游中较为流行的模式,在这种模式下,玩家可以按照自身坐标系向着四个不同的方向移动,当玩家按下鼠标右键时,可以绕Y轴按照一定的角度旋转摄像机,在旋转的过程中,角色将旋转相应的角度。在移动的过程中,摄像机会保持与玩家间的一定距离,然后跟随角色进行移动。这种模式的代表作有《古剑奇谭》、《仙剑奇侠传五前传》等。这种模式的优点是玩家可以自由地对角色进行观察,是真正意义上的3D。可是它不是没有缺点啊,在控制角色的时候,玩家需要双手同时进行操作。这种模式在Unity3D中的实现同样需要SmoothFollow脚本,不过需要克服对旋转角度的追踪。此外,我们还需要一个相机控制的脚本,这样我们可以在360度地欣赏游戏中的美丽场景。同样地,脚本我们放在后面来讲。


 

       二、1个组件

       在Unity3D中有一个称为角色控制器(CharacterController)的组件,从这个名称,我们就可以知道它是一个用来控制角色的组件。虽然通过Transform的方式同样可以实现角色的控制,可是相比Transform的方式,角色控制器具备了更为优越的特性。具体地说,角色控制器允许开发者在受制于碰撞的情况下很容易的进行运动,而不用处理刚体。按照博主的理解就是角色控制器具备Rigidbody组件的部分属性,如果使用角色控制器就可以不用用Rigidbody组件了。事实上,角色控制器可以忽略场景中重力的影响,但是受制于碰撞。这句话怎么理解呢?博主举一个例子,比如我们需要使角色受制于碰撞的影响,所以我们可以为角色添加一个刚体(Rigidbody),可是我们同时不希望当角色和场景中的静态物体碰撞时被撞飞,固然我们可以通过限制角色在碰撞过程中的角度和位置变化来实现这样的效果,可是事实上如果角色和场景中的物体发生了碰撞,其碰撞的结果通常是不受玩家控制的,尤其是角色启用重力因素后,对于碰撞结果的控制会显得更加艰难。那么角色控制器就是为了满足这样一个需求而产生的。具体地说,通过角色控制器我们可以实现这样的需求:

        1、重力控制 由于CharacterController不受场景的重力影响,所以,设计者可以自行添加重力因素。例如在CharacterController组件中有一个isGrounded的属性,该属性可以判断角色是否位于地面上。需要注意的是,CharacterController依赖于碰撞,即地面和角色都需要有碰撞体才可以,一个较为有效的方法是使用标准的碰撞体如Box、Sphere、Capsule来作为一个模型的父容器。具体地应用我们会在最后的脚本部分给出来。

         2、爬楼梯 CharacterController.stepOffset属性使得角色具备了爬楼梯的能力,它是一个以米为单位的角色控制器的台阶偏移量,表示角色在垂直方向上台阶的高度。试想我们如果使用Transform方式实现这样的效果,恐怕我们需要费一番周折了,好在CharacterController可以帮助我们轻松地实现这样的功能,让玩家在游戏世界里更为自由和充满乐趣。

         3、爬斜坡 和stepOffset属性类似,slopeLimit可以设置允许角色攀爬的最大角度。利用这一属性最为显著的实例是在起伏的地面上控制角色,如果我们使用Transform方式来移动角色,可能会出现角色从地面上穿过去这样的情况,显然CharacterController再次让我们的问题变得简单。

      关于更多CharacterController的特性大家可以自行查阅API文档,

      然而不过无可否认的是CharacterController让我们控制角色变得更为简单,这一点大家可以在具体的项目中得到较为深刻的体会。

 

      三、1种模型

       如果说角色控制器的出现让我们控制角色变得更为简单,那么下面要讲的Locomotion模型将会让我们控制动画变得更为简单,特别强调是在控制角色移动动画的时候。关于Locomotion系统,我们可以把它当做是一个预制的动画模板,它定义了角色具体的状态下应该采用什么样的动画,而这一切的载体就是Unity3DMecanim动画系统。或许我们对于Locomotion模型不甚了解,可是我们会在Unity3D官方的示例项目中找到它的身影。使用Locomotion模型需要导入相应的资源包,我们可以在Unity3D资源商店里下载一个名为Mecanim Locomotion System StartKit的资源包,这样我们就可以在项目中使用Locomotion模型了。从官方的介绍中博主得知该模型主要的用途是能够自动混合关键帧或捕捉动画的走和跑的循环并且调整腿部骨骼的运动来保证脚步能正确地落在地面上。这个模型能调整并改变动画的速率和曲线以任何速率、方向、曲率、步幅从一个简单平面到任何倾斜角度的地形。在英文中Locomotion是运动的意思,所以这是一个提供角色运动支持的东西。可是博主目前并没有发现它在适应不同地形方面的特性,所以这里我们实际上还是在说Mecamin动画系统。这里我们以该资源包里的示例项目来讲解Locomotion模型。如图,在Animator窗口中可以看到它是一个Mecanim动画:


        如果大家熟悉Mecanim的话,可以很清楚地看出来它关联了多种动画状态。或许在不同的项目中,大家设计的Mecamim动画可能会有所不同,不过它的实质是一样的。我们继续来看这个资源包为我们提供的东西,在Script文件夹下我们可以看到一个Locomotion的脚本,这个脚本是我们使用Locomotion模型的前提,打开脚本我们会发现,这是对Unity3D Mecanim API的一种封装。

using UnityEngine;
using System.Collections;

public class Locomotion
{
    private Animator m_Animator = null;
    
    private int m_SpeedId = 0;
    private int m_AgularSpeedId = 0;
    private int m_DirectionId = 0;

    public float m_SpeedDampTime = 0.1f;
    public float m_AnguarSpeedDampTime = 0.25f;
    public float m_DirectionResponseTime = 0.2f;
    
    public Locomotion(Animator animator)
    {
        m_Animator = animator;

        m_SpeedId = Animator.StringToHash("Speed");
        m_AgularSpeedId = Animator.StringToHash("AngularSpeed");
        m_DirectionId = Animator.StringToHash("Direction");
    }

    public void Do(float speed, float direction)
    {
        AnimatorStateInfo state = m_Animator.GetCurrentAnimatorStateInfo(0);

        bool inTransition = m_Animator.IsInTransition(0);
        bool inIdle = state.IsName("Locomotion.Idle");
        bool inTurn = state.IsName("Locomotion.TurnOnSpot") || state.IsName("Locomotion.PlantNTurnLeft") || state.IsName("Locomotion.PlantNTurnRight");
        bool inWalkRun = state.IsName("Locomotion.WalkRun");

        float speedDampTime = inIdle ? 0 : m_SpeedDampTime;
        float angularSpeedDampTime = inWalkRun || inTransition ? m_AnguarSpeedDampTime : 0;
        float directionDampTime = inTurn || inTransition ? 1000000 : 0;

        float angularSpeed = direction / m_DirectionResponseTime;
        
        m_Animator.SetFloat(m_SpeedId, speed, speedDampTime, Time.deltaTime);
        m_Animator.SetFloat(m_AgularSpeedId, angularSpeed, angularSpeedDampTime, Time.deltaTime);
        m_Animator.SetFloat(m_DirectionId, direction, directionDampTime, Time.deltaTime);
    }	
}

    如果我们需要对角色进行控制,只需要使用下面的代码:

 /// <summary>
/// 
/// </summary>

using UnityEngine;
using System;
using System.Collections;
  
[RequireComponent(typeof(Animator))]  

//Name of class must be name of file as well

public class LocomotionPlayer : MonoBehaviour {

    protected Animator animator;

    private float speed = 0;
    private float direction = 0;
    private Locomotion locomotion = null;

	// Use this for initialization
	void Start () 
	{
        animator = GetComponent<Animator>();
        locomotion = new Locomotion(animator);
	}
    
	void Update () 
	{
        if (animator && Camera.main)
		{
            JoystickToEvents.Do(transform,Camera.main.transform, ref speed, ref direction);
            locomotion.Do(speed * 6, direction * 180);
		}		
	}
}

       我们发现此时我们的脚本变得简单了许多,因为大量的脚本被转移到了Locomotion脚本中。而这就是博主想为大家介绍的Locomotion模型,通过该模型我们可以更容易地控制角色,不过恕博主直言,在国内如果采用这种模型来做游戏的话,可能会不太适应我们自己的游戏。因为在《仙剑奇侠传五》中最初就是因为采用类似这种的自动视角,导致游戏最初的游戏体验并不是很完美。再者像《仙剑奇侠传》、《古剑奇谭》这类中国传统风格的游戏在美学设计上更倾向于好看而不是真实,所以直接采用这样的模型会有点困难。不过还是那句话,我们不能因为某些客观的因素就停止学习啊,好了,Locomotion就先说到这里吧,下面我们来重点讲解脚本。

    下面讲述如何使用角色控制器来控制角色,我们一起来看下面的脚本:

using UnityEngine;
using System.Collections;

public class PlayerController : MonoBehaviour {
	
	//移动速度
	public float MoveSpeed=1.5F;
	//奔跑速度
	public float RunSpeed=4.5F;
	//旋转速度
	public float RotateSpeed=30;
	//重力
	public float Gravity=20;
	//动画组件
	private Animator mAnim;
	//声音组件
	private AudioSource mAudio;
	//速度
	private float mSpeed;
	//移动方式,默认为Walk
	public TransportType MoveType=TransportType.Walk;
	//游戏管理器
	private GameManager mManager;
	//角色控制器
	private CharacterController mController;
	

	void Start () 
	{
	   //获取动画组件
	   mAnim=GetComponentInChildren<Animator>();
	   //获取声音组件
	   mAudio=GetComponent<AudioSource>();
	   //获取游戏管理器
	   mManager=GameObject.Find("GameManager").GetComponent<GameManager>();
	   //获取角色控制器
	   mController=GetComponent<CharacterController>();
	}

	void Update () 
	{
		//只有处于正常状态时玩家可以行动
		if(mManager.Manager_State==GameState.Normal)
		{
	        MoveManager();
	    }
	}
    
	//移动管理
	void MoveManager()
	{
		//移动方向
		Vector3 mDir=Vector3.zero;
		if(mController.isGrounded)
		{
	       if(Input.GetAxis("Vertical")==1)
	       {
		      SetTransportType(MoveType);
			  mDir=Vector3.forward * RunSpeed * Time.deltaTime;
	       }
	       if(Input.GetAxis("Vertical")==-1)
	       {
		      SetTransportType(MoveType);
			  mDir=Vector3.forward * -RunSpeed * Time.deltaTime;
	       }
	       if(Input.GetAxis("Horizontal")==-1)
	       {
		      SetTransportType(MoveType);
		      Vector3 mTarget=new Vector3(0,-RotateSpeed* Time.deltaTime,0);
		      transform.Rotate(mTarget);
	       }
	       if(Input.GetAxis("Horizontal")==1)
	       {
		      SetTransportType(MoveType);
		      Vector3 mTarget=new Vector3(0,RotateSpeed* Time.deltaTime,0);
		      transform.Rotate(mTarget);
	       }
	       if(Input.GetAxis("Vertical")==0 && Input.GetAxis("Horizontal")==0)
	       {
		      mAnim.SetBool("Walk",false);
		      mAnim.SetBool("Run",false);
	       }
	   }
		//考虑重力因素
		mDir=transform.TransformDirection(mDir);
		float y=mDir.y-Gravity *Time.deltaTime;
		mDir=new Vector3(mDir.x,y,mDir.z);
		mController.Move(mDir);

	   //使用Tab键切换移动方式
	   if(Input.GetKey(KeyCode.Tab))
	   {
		  if(MoveType==TransportType.Walk){
			MoveType=TransportType.Run;
		  }else if(MoveType==TransportType.Run){
			MoveType=TransportType.Walk;
		  }
	   }
	}


    
	//设置角色移动的方式
	public void SetTransportType(TransportType MoveType)
	{
	   switch(MoveType)
	   {
			case TransportType.Walk:
				MoveType=TransportType.Walk;
				mAnim.SetBool("Walk",true);
				mSpeed=MoveSpeed;
				break;
			case TransportType.Run:
				MoveType=TransportType.Run;
				mAnim.SetBool("Run",true);
				mSpeed=RunSpeed;
				break;
	   }
	}

}
     以上这段脚本是博主在做的一个游戏中使用的代码,在这段脚本中你可以看到博主是如何利用CharacterController来控制角色的,即根据玩家的输入轴计算移动方向,如果玩家不在地面上(通过isGrounded属性来判断),则需要考虑重力因素,理论上角色从高处下落的时候应该是加速运动,根据物理学公式h=1/2gt^2,这里只是为了模拟重力,所以采用了简化的匀速运动,希望大家注意。这里还可以进一步扩展,比如我们所熟悉的经典射击游戏CS,玩家是可以跳跃的,那么利用角色控制器来实现这种效果该怎么做呢?很简单,这和模拟重力是一样的,即设定一个跳跃速度,如果玩家按下了空格键,则改变mDir中的y即可。这里我们使用的是第一种控制模式,即锁视角的控制模式,可能是因为博主最早接触的3D游戏是《仙剑奇侠传四》吧,所以博主更喜欢这样的控制模式。在这段脚本中有一个SetTransportType()的方法,用来切换角色移动的方式,主要是切换动画和移动速度。我知道很多朋友对不锁视角的控制模式可能更感兴趣,因为这是目前的主流,例如《新剑侠传奇》在宣传之初就以不锁视角、即时战斗作为主要的噱头,不过游戏发布后效果并没有预期的那样好,好在游戏制作方敢于承担错误,及时将游戏回炉重铸,足以看出制作方想做好游戏的诚意。不过,这种模式博主该没有研究出来,现在手上有了新模型,所以有时间的话博主会尝试这种新的模式,希望大家关注我的博客啊,博主会不定期地更新博客的。那么下面为大家分享一个从《UnityChan》找到的关于相机控制部分的脚本,可以实现对角色的自由观察。下面给出脚本:

//CameraController.cs for UnityChan
//Original Script is here:
//TAK-EMI / CameraController.cs
//https://gist.github.com/TAK-EMI/d67a13b6f73bed32075d
//https://twitter.com/TAK_EMI
//
//Revised by N.Kobayashi 2014/5/15 
//Change : To prevent rotation flips on XY plane, use Quaternion in cameraRotate()
//Change : Add the instrustion window
//Change : Add the operation for Mac
//




using UnityEngine;
using System.Collections;

namespace CameraController
{
	enum MouseButtonDown
	{
		MBD_LEFT = 0,
		MBD_RIGHT,
		MBD_MIDDLE,
	};

	public class CameraController : MonoBehaviour
	{
		[SerializeField]
		private Vector3 focus = Vector3.zero;
		[SerializeField]
		private GameObject focusObj = null;

		public bool showInstWindow = true;

		private Vector3 oldPos;

		void setupFocusObject(string name)
		{
			GameObject obj = this.focusObj = new GameObject(name);
			obj.transform.position = this.focus;
			obj.transform.LookAt(this.transform.position);

			return;
		}

		void Start ()
		{
			if (this.focusObj == null)
				this.setupFocusObject("CameraFocusObject");

			Transform trans = this.transform;
			transform.parent = this.focusObj.transform;

			trans.LookAt(this.focus);

			return;
		}
	
		void Update ()
		{
			this.mouseEvent();

			return;
		}

		//Show Instrustion Window
		void OnGUI()
		{
			if(showInstWindow){
				GUI.Box(new Rect(Screen.width -210, Screen.height - 100, 200, 90), "Camera Operations");
				GUI.Label(new Rect(Screen.width -200, Screen.height - 80, 200, 30),"RMB / Alt+LMB: Tumble");
				GUI.Label(new Rect(Screen.width -200, Screen.height - 60, 200, 30),"MMB / Alt+Cmd+LMB: Track");
				GUI.Label(new Rect(Screen.width -200, Screen.height - 40, 200, 30),"Wheel / 2 Fingers Swipe: Dolly");
			}

		}

		void mouseEvent()
		{
			float delta = Input.GetAxis("Mouse ScrollWheel");
			if (delta != 0.0f)
				this.mouseWheelEvent(delta);

			if (Input.GetMouseButtonDown((int)MouseButtonDown.MBD_LEFT) ||
				Input.GetMouseButtonDown((int)MouseButtonDown.MBD_MIDDLE) ||
				Input.GetMouseButtonDown((int)MouseButtonDown.MBD_RIGHT))
				this.oldPos = Input.mousePosition;

			this.mouseDragEvent(Input.mousePosition);

			return;
		}

		void mouseDragEvent(Vector3 mousePos)
		{
			Vector3 diff = mousePos - oldPos;

			if(Input.GetMouseButton((int)MouseButtonDown.MBD_LEFT))
			{
				//Operation for Mac : "Left Alt + Left Command + LMB Drag" is Track
				if(Input.GetKey(KeyCode.LeftAlt) && Input.GetKey(KeyCode.LeftCommand))
				{
					if (diff.magnitude > Vector3.kEpsilon)
						this.cameraTranslate(-diff / 100.0f);
				}
				//Operation for Mac : "Left Alt + LMB Drag" is Tumble
				else if (Input.GetKey(KeyCode.LeftAlt))
				{
					if (diff.magnitude > Vector3.kEpsilon)
						this.cameraRotate(new Vector3(diff.y, diff.x, 0.0f));
				}
				//Only "LMB Drag" is no action.
			}
			//Track
			else if (Input.GetMouseButton((int)MouseButtonDown.MBD_MIDDLE))
			{
				if (diff.magnitude > Vector3.kEpsilon)
					this.cameraTranslate(-diff / 100.0f);
			}
			//Tumble
			else if (Input.GetMouseButton((int)MouseButtonDown.MBD_RIGHT))
			{
				if (diff.magnitude > Vector3.kEpsilon)
					this.cameraRotate(new Vector3(diff.y, diff.x, 0.0f));
			}
				
			this.oldPos = mousePos;	

			return;
		}

		//Dolly
		public void mouseWheelEvent(float delta)
		{
			Vector3 focusToPosition = this.transform.position - this.focus;

			Vector3 post = focusToPosition * (1.0f + delta);

			if (post.magnitude > 0.01)
				this.transform.position = this.focus + post;

			return;
		}

		void cameraTranslate(Vector3 vec)
		{
			Transform focusTrans = this.focusObj.transform;

			vec.x *= -1;

			focusTrans.Translate(Vector3.right * vec.x);
			focusTrans.Translate(Vector3.up * vec.y);

			this.focus = focusTrans.position;

			return;
		}

		public void cameraRotate(Vector3 eulerAngle)
		{
			//Use Quaternion to prevent rotation flips on XY plane
			Quaternion q = Quaternion.identity;
 
			Transform focusTrans = this.focusObj.transform;
			focusTrans.localEulerAngles = focusTrans.localEulerAngles + eulerAngle;

			//Change this.transform.LookAt(this.focus) to q.SetLookRotation(this.focus)
			q.SetLookRotation (this.focus) ;

			return;
		}
	}
}
   下面是演示效果:

1、Locomotion演示



2、角色控制器演示(为节省容量只好牺牲质量啦.......)


好了,感谢大家关注我的博客,今天的内容就是这样了,希望大家喜欢。


每日箴言当我真心在追寻着我的梦想时,每一天都是缤纷的,因为我知道每一个小时都是在实现梦想的一部分。—— 保罗·科埃略





   喜欢我的博客请记住我的名字:秦元培,我博客地址是blog.csdn.net/qinyuanpei。
   转载请注明出处,本文作者:秦元培,本文出处:http://blog.csdn.net/qinyuanpei/article/details/39050631


2014-09-08 01:32:37 qinyuanpei 阅读数 22009
  • unity3D游戏/AR/VR在线就业班

    本套课程是一套完整的 Unity3D-游戏/AR/VR 学习课程,具体的课程内容包括《C#语言》、《Unity引擎》、《编程思想》,《商业级项目实践》等开发课程,引导您一步步循序渐进、由易到难,终获得Unity 3D/游戏/AR/VR工程师的岗位技能。

    12792 人正在学习 去看看 宋晓波

       各位朋友,大家好,我是秦元培,欢迎大家关注我的博客,我的博客地址是blog.csdn.net/qinyuanpei。在上一篇文章<[Unity3D]Unity3D游戏开发之角色控制漫谈>一文中,博主与大家分享自己在角色控制方面的一些感悟。今天呢,我们继续来探讨Unity3D角色控制的内容,今天博主将解决在上一篇文章中没有解决的问题,即自由视角下的角色控制。如图是博主非常喜欢的《古剑奇谭》游戏截图,在这款游戏中就使用了博主今天要讲解的自由视角,所谓自由视角是指玩家可以按照自身坐标系向着四个不同的方向移动,当玩家按下鼠标右键时,可以绕Y轴按照一定的角度旋转摄像机,在旋转的过程中,角色将旋转相应的角度。在移动的过程中,摄像机会保持与玩家间的一定距离,然后跟随角色进行移动。好了,下面我们正式开始今天的内容吧!


     

     在开始今天的内容前,首先让我们来学习下Unity3D中较为重要的一部分知识,理解这些知识是我们开始学习今天内容的基础。

     1、Input.GetAxis():该方法用于在Unity3D中根据坐标轴名称返回虚拟坐标系中的值,通常情况下,使用控制器和键盘输入时此值范围在-1到1之间。这段话怎么理解呢?我们来看下面这段脚本:

using UnityEngine;
using System.Collections;

public class example : MonoBehaviour {

	//水平速度
	public float HorizontalSpeed = 2.0F;
	//垂直速度
	public float VerticalSpeed = 2.0F;

	void Update() 
	{
		//水平方向
		float h = HorizontalSpeed * Input.GetAxis("Mouse X");
		//垂直方向
		float v = VerticalSpeed * Input.GetAxis("Mouse Y");
		//旋转
		transform.Rotate(v, h, 0);
	}
}
这段脚本呢是根据鼠标的位置来旋转物体从而实现对物体的观察,从这段脚本中我们可以看出,通过获取输入轴的办法,我们可以获得鼠标移动的方向进而实现对于物体的旋转控制。在Unity3D中我们可以通过Edit->Project Setting->Input来查看项目中的坐标轴名称:


在后面,我们还将使用这种方式,大家可以对这个方法有进一步的了解。


     2、欧拉角eulerAngles:该值是Vector3类型的值,x、y、z分别代表绕x轴旋转x度,绕y轴旋转y度,绕z轴旋转z度。因此,该值最为直观的形式是可以允许我们直接以一个三维向量的形式来修改一个物体的角度,例如下面的脚本:

float mY = 5.0;

void Update () 
{
	mY += Input.GetAxis("Horizontal");
	transform.eulerAngles =new Vector3(0,mY, 0);
}
     如果你已经理解了上面的话,那么不出意外的,这段脚本会如你所愿的,按照鼠标在水平方向上移动的方向绕Y轴旋转。通常情况下,我们不会单独设置欧拉角其中一个轴,例如eulerAngles.x = 10,因为这将导致偏移和不希望的旋转。当设置它们一个新的值时,要同时设置全部。好在我们可以通过Quaternion.Euler()方法将一个Vector3类型的值转化为一个四元数,进而通过修改Transform.Rotation来实现相同的目的。

    

     3、插值:所谓插值是指离散数据的基础上补插连续函数,使得这条连续曲线通过全部给定的离散数据点。插值是离散函数逼近的重要方法,利用它可通过函数在有限个点处的取值状况,估算出函数在其他点处的近似值。在某些情况下,如果我们希望过程中处理得较为平滑,此时我们就可以使用插值的方法来实现对中间过程的模拟。在Unity3D中我们可以使用两种插值方法,即线性插值Lerp,球形插值SLerp。我们来看下面的脚本:

void Rotating (float horizontal, float vertical)
	{
		// Create a new vector of the horizontal and vertical inputs.
		Vector3 targetDirection = new Vector3(horizontal, 0f, vertical);
		
		// Create a rotation based on this new vector assuming that up is the global y axis.
		Quaternion targetRotation = Quaternion.LookRotation(targetDirection, Vector3.up);
		
		// Create a rotation that is an increment closer to the target rotation from the player's rotation.
		Quaternion newRotation = Quaternion.Lerp(rigidbody.rotation, targetRotation, turnSmoothing * Time.deltaTime);
		
		// Change the players rotation to this new rotation.
		rigidbody.MoveRotation(newRotation);
	}
插值的方法很简单,只要我们给出初始和结束的状态、时间就可以了,大家可以自己看API。


      好了,有了这三部分的基础,我们就可以开始今天的内容了,今天的脚本分为两个部分,第一部分是角色控制的部分,主要负责的角色在场景中的移动、转身和动画处理。第二部分是相机控制的部分,主要涉及相机旋转、相机缩放的相关内容。下面,我们分别来讲这两个部分,场景依然是博主自己在做的小游戏:


本次的主角呢,是博主非常喜欢的角色谢沧行,好了,我们回到今天的内容里吧!在第一部分,主要的是完成角色向各个方向的转身,这里博主定义四个方向(其实八个方向是一样的!),脚本如下:

using UnityEngine;
using System.Collections;

public class NoLockiVew_Player : MonoBehaviour {

	/*自由视角下的角色控制*/
	/*作者:秦元培*/

	//玩家的行走速度
	public float WalkSpeed=1.5F;
	//重力
	public float Gravity=20;

	//角色控制器
	private CharacterController mController;
	//动画组件
	private Animation mAnim;
	//玩家方向,默认向前
	private DirectionType mType=DirectionType.Direction_Forward;

	[HideInInspector]
	//玩家状态,默认为Idle
	public PlayerState State=PlayerState.Idle;

	//定义玩家的状态枚举
	public enum PlayerState
	{
		Idle,
		Walk
	}

	//定义四个方向的枚举值,按照逆时针方向计算
	protected enum DirectionType
	{
		Direction_Forward=90,
		Direction_Backward=270,
		Direction_Left=180,
		Direction_Right=0
	}
	
	void Start () 
	{
	   //获取角色控制器
	   mController=GetComponent<CharacterController>();
	   //获取动画组件
	   mAnim=GetComponentInChildren<Animation>();
	}
	
	
	void Update () 
	{
		MoveManager();
		//MouseEvent();
	}

	//玩家移动控制
	void MoveManager()
	{
		//移动方向
		Vector3 mDir=Vector3.zero;
		if(mController.isGrounded)
		{
			//将角色旋转到对应的方向
			if(Input.GetAxis("Vertical")==1)
			{
				SetDirection(DirectionType.Direction_Forward);
				mDir=Vector3.forward * Time.deltaTime * WalkSpeed;
				mAnim.CrossFade("Walk",0.25F);
				State=PlayerState.Walk;
			}
			if(Input.GetAxis("Vertical")==-1)
			{
				SetDirection(DirectionType.Direction_Backward);
				mDir=Vector3.forward * Time.deltaTime * WalkSpeed;
				mAnim.CrossFade("Walk",0.25F);
				State=PlayerState.Walk;
			}
			if(Input.GetAxis("Horizontal")==-1)
			{
				SetDirection(DirectionType.Direction_Left);
				mDir=Vector3.forward * Time.deltaTime * WalkSpeed;
				mAnim.CrossFade("Walk",0.25F);
				State=PlayerState.Walk;
			}
			if(Input.GetAxis("Horizontal")==1)
			{
				SetDirection(DirectionType.Direction_Right);
				mDir=Vector3.forward * Time.deltaTime * WalkSpeed;
				mAnim.CrossFade("Walk",0.25F);
				State=PlayerState.Walk;
			}
			//角色的Idle动画
			if(Input.GetAxis("Vertical")==0 && Input.GetAxis("Horizontal")==0)
			{
				mAnim.CrossFade("Idle",0.25F);
				State=PlayerState.Idle;
			}

		}
		//考虑重力因素
		mDir=transform.TransformDirection(mDir);
		float y=mDir.y-Gravity *Time.deltaTime;
		mDir=new Vector3(mDir.x,y,mDir.z);
		mController.Move(mDir);
	}

	//设置角色的方向,有问题
	void SetDirection(DirectionType mDir)
	{
		if(mType!=mDir)
		{
			transform.Rotate(Vector3.up*(mType-mDir));
			mType=mDir;
		}
	}
}
这里定义四个方向,是按照逆时针方向转的,相邻的两个方向间相差90度,所以我们只需要将当前的角度和目标角度相减就可以转到目标角度的方向(其实这是以前写的代码,现在回头再看,直接用欧拉角似乎更为简单啊,呵呵)。这里主要的内容就是这样了。下面我们来看相机控制部分的代码吧,这里的代码参考了MouseOrbit脚本,主要完成了鼠标右键旋转控制,博主在此基础上增加了相机缩放的代码。提到相机缩放,其实就是根据鼠标滚轮滚动的方向和大小重新计算角色与相机的距离,与之类似地还有小地图的放缩,其实同样是通过修改距离来实现的。博主今天的一个体会是官方的代码能自己写一遍的最好自己写一遍,这样好多东西就能在这个过程中给理解了。我们一起来看脚本
using UnityEngine;
using System.Collections;

public class NoLockView_Camera : MonoBehaviour 
{
	//观察目标
	public Transform Target;
	//观察距离
	public float Distance = 5F;
	//旋转速度
	private float SpeedX=240;
	private float SpeedY=120;
	//角度限制
	private float  MinLimitY = 5;
	private float  MaxLimitY = 180;

	//旋转角度
	private float mX = 0.0F;
	private float mY = 0.0F;

    //鼠标缩放距离最值
	private float MaxDistance=10;
	private float MinDistance=1.5F;
	//鼠标缩放速率
	private float ZoomSpeed=2F;

	//是否启用差值
	public bool isNeedDamping=true;
	//速度
	public float Damping=2.5F;

	void Start () 
	{
		//初始化旋转角度
		mX=transform.eulerAngles.x;
		mY=transform.eulerAngles.y;
	}
	
	void LateUpdate () 
	{
		//鼠标右键旋转
		if(Target!=null && Input.GetMouseButton(1))
		{
		    //获取鼠标输入
			mX += Input.GetAxis("Mouse X") * SpeedX * 0.02F;
			mY -= Input.GetAxis("Mouse Y") * SpeedY * 0.02F;
			//范围限制
			mY = ClampAngle(mY,MinLimitY,MaxLimitY);
		}

		//鼠标滚轮缩放

		Distance-=Input.GetAxis("Mouse ScrollWheel") * ZoomSpeed;
		Distance=Mathf.Clamp(Distance,MinDistance,MaxDistance);

		//重新计算位置和角度
		Quaternion mRotation = Quaternion.Euler(mY, mX, 0);
		Vector3 mPosition = mRotation * new Vector3(0.0F, 0.0F, -Distance) + Target.position;

		//设置相机的角度和位置
		if(isNeedDamping){
		   //球形插值
		   transform.rotation = Quaternion.Lerp(transform.rotation,mRotation, Time.deltaTime*Damping); 
		   //线性插值
		   transform.position = Vector3.Lerp(transform.position,mPosition, Time.deltaTime*Damping); 
		}else{
		   transform.rotation = mRotation;
		   transform.position = mPosition;
		}
		//将玩家转到和相机对应的位置上
		if(Target.GetComponent<NoLockiVew_Player>().State==NoLockiVew_Player.PlayerState.Walk)
		{
			Target.eulerAngles=new Vector3(0,mX,0);
		}
	}
	
	private float  ClampAngle (float angle,float min,float max) 
	{
		if (angle < -360) angle += 360;
		if (angle >  360) angle -= 360;
		return Mathf.Clamp (angle, min, max);
	}
    



}



这里很多朋友可能对我设置一个状态很不理解吧,这其实是为了让玩家有一个自由查看角色的机会,否则当玩家按下鼠标右键的话,角色就会转向相机正对着的位置,这样玩家就看不到角色的正面了。当然,这里用到了插值,这样能使角色在转身的时候平滑一点,效果会更好。

唉,不知不觉已经这个时候了,开学已经一周了,这学期我们只上九周课,然后就是实习、毕设、找工作一大堆事情在等着我们。可是不知道为什么博主宿舍的同学居然还能每天什么事情都不做,从早上一直看电视剧到晚上十点,虽然这件事情和博主没有什么关系吧,可是整个宿舍的人都安安静静地做着自己的事情,就有这么一个人整天声音开得很大在那里外放,这样真的好吗?以前和这个人闹过一次,因为他聚了一帮人在宿舍打麻将,我觉得吵就说了他一顿,结果就和我僵上了。说真的,博主对目前的专业真的没有什么情感,大学四年里最让博主开心的事情就是博主学会了好多自己想要学的东西,认识了几个不错的老师和朋友,仅此而已。或许我们注定要越走越远吧,因为我们根本就不是同一种人。

前几天网上有人通过博客联系到博主,希望博主能到他哪里工作,可惜博主目前仍然有一大堆的琐事缠身,不然离开这喧嚣吵闹的宿舍,去做博主喜欢的事情,去寻找博主自己的梦想,博主相信,博主一定可以做得更好吧。人生或许就是这样,你越是在乎的可能越让你得不到,你无心插柳可能会得到一片绿荫。对于未来,不管怎么样,坦然面对就好了,博主改变不了周围的这群人,只能努力地去改变自己。好了,晚安吧!

效果演示(2M的限制让很多展示都无可奈何)



每日箴言:所有的悲伤,总会留下一丝欢乐的线索。所有的遗憾,总会留下一处完美的角落。我在冰封的深海,找寻希望的缺口。却在午夜惊醒时,蓦然瞥见绝美的月光。——几米


       喜欢我的博客请记住我的名字:秦元培,我博客地址是blog.csdn.net/qinyuanpei。
       转载请注明出处,本文作者:秦元培,本文出处:http://blog.csdn.net/qinyuanpei/article/details/39125353


      文章更新:

      9月10日,博主在测试这个项目的时候意外地发现了一个Bug。Bug出现在如下位置:

		//设置玩家跟随角度
		if(Target.GetComponent<NoLockiVew_Player>().State==NoLockiVew_Player.PlayerState.Walk)
		{
			Target.rotation=Quaternion.Euler(new Vector3(0,mX,0));
		}

该方法主要的作用是当玩家同时按下方向控制键和鼠标右键,玩家可以随着鼠标旋转到对应的角度,这主要是为了满足玩家双手操作的需求,不过由于这行代码,导致玩家在向左、向右、向后三个方向上的转身失效,如果除去这行代码,则原来的方向控制没有任何问题,可是没有这行代码,玩家的操作感就会下降。后来博主想到我们对角色的旋转实际上应该是放在鼠标右键事件里的,所以博主将代码修改如下,这样就解决了这个Bug:

using UnityEngine;
using System.Collections;

public class NoLockView_Camera : MonoBehaviour 
{
	//观察目标
	public Transform Target;
	//观察距离
	public float Distance = 5F;
	//旋转速度
	private float SpeedX=240;
	private float SpeedY=120;
	//角度限制
	private float  MinLimitY = 5;
	private float  MaxLimitY = 180;

	//旋转角度
	private float mX = 0.0F;
	private float mY = 0.0F;

    //鼠标缩放距离最值
	private float MaxDistance=10;
	private float MinDistance=1.5F;
	//鼠标缩放速率
	private float ZoomSpeed=2F;

	//是否启用差值
	public bool isNeedDamping=true;
	//速度
	public float Damping=10F;

	private Quaternion mRotation;

	void Start () 
	{
		//初始化旋转角度
		mX=transform.eulerAngles.x;
		mY=transform.eulerAngles.y;
	}
	
	void LateUpdate () 
	{
		//鼠标右键旋转
		if(Target!=null && Input.GetMouseButton(1))
		{
		    //获取鼠标输入
			mX += Input.GetAxis("Mouse X") * SpeedX * 0.02F;
			mY -= Input.GetAxis("Mouse Y") * SpeedY * 0.02F;
			//范围限制
			mY = ClampAngle(mY,MinLimitY,MaxLimitY);
			//计算旋转
			mRotation = Quaternion.Euler(mY, mX, 0);
			//根据是否插值采取不同的角度计算方式
			if(isNeedDamping){
				transform.rotation = Quaternion.Lerp(transform.rotation,mRotation, Time.deltaTime*Damping); 
			}else{
				transform.rotation = mRotation;
			}
			//处理同时按下鼠标右键和方向控制键
			if(Target.GetComponent<NoLockiVew_Player>().State==NoLockiVew_Player.PlayerState.Walk){
				Target.rotation=Quaternion.Euler(new Vector3(0,mX,0));
			}
		}

		//鼠标滚轮缩放
		Distance-=Input.GetAxis("Mouse ScrollWheel") * ZoomSpeed;
		Distance=Mathf.Clamp(Distance,MinDistance,MaxDistance);

		//重新计算位置
		Vector3 mPosition = mRotation * new Vector3(0.0F, 0.0F, -Distance) + Target.position;

		//设置相机的角度和位置
		if(isNeedDamping){
		   transform.position = Vector3.Lerp(transform.position,mPosition, Time.deltaTime*Damping); 
		}else{
		   transform.position = mPosition;
		}

	}


	//角度限制
	private float  ClampAngle (float angle,float min,float max) 
	{
		if (angle < -360) angle += 360;
		if (angle >  360) angle -= 360;
		return Mathf.Clamp (angle, min, max);
	}
}


不过经过博主测试,如果不采用插值的话,似乎效果更为真实啊(为什么会和第一次测试的感觉不一样啊,囧!)




2015-02-05 08:34:06 book_longssl 阅读数 5169
  • unity3D游戏/AR/VR在线就业班

    本套课程是一套完整的 Unity3D-游戏/AR/VR 学习课程,具体的课程内容包括《C#语言》、《Unity引擎》、《编程思想》,《商业级项目实践》等开发课程,引导您一步步循序渐进、由易到难,终获得Unity 3D/游戏/AR/VR工程师的岗位技能。

    12792 人正在学习 去看看 宋晓波

导入动画文件后,在工程(Porject)面板中选中,通过Inspector面板修改参数设置。

在项目视图中选中模型文件。如果想用旧版3.x的动画系统导入和编辑动画,请选择legacy选项。如果你有一个人性角色,比如,一个脑袋两条胳膊的两足动物,请选择Humanoid和‘Create from this model’,这样,一个完美匹配你角色骨骼关系的替身就可以被创建,或者你也可以选择其他预设好的替身。

如果你的角色不是人形物体,比如一个四足动物或者任何其它的你想编辑动画的物体-并且你希望新的动画系统Mecanim,请选择Generic选项,然后你必须在下拉菜单中为你的动画模型定义一个骨骼作为根节点。

Model选项卡

属性

Scale Factor 缩放系数。

Mesh Compression网格压缩.降低网格文件大小,但有可能导致变形。

Mesh Optimization 网格优化.优化三角形的排列顺序。

Generate Colliders产生碰撞体。生成网格碰撞体。

Swap UVs交换UV(通过改变纹理坐标,实现动态效果的纹理动画)当有光照贴图的物体的UV通道不正确时启用此选项。这将交换你的主UV通道与次UV通道。

Generate Lightmap产生光照贴图。

Normals 法线

Import 导入. 从文件导入法线。

Calculate 计算。根据平滑角度(Smoothing angle)计算法线。

None 不使用。如果网格既没有法线贴图,也不受实时光照影响,则可使用此选项。

Tangents 切线

Import 导入

Calculate计算切线和副法线。只有当法线为导入或计算时,此选项可用。

None禁用切线和副法线。如网格没有切线则不能使用法线贴图着色器。

Smoothing Angle 平滑角。设置边会被作为硬边处理的锋利程度。它还被用来切分法线贴图切线。

Split Tangents 分离切线。如果您的网格上的接缝破坏了法线贴图光照,启用此选项。这通常只适用于角色。

Import Materials 导入材质

Material Naming 材质命名。 此选项决定了Unity材质的命名方式.

Material Search 材质搜索。决定Unity如何根据材质命名选项中定义的名字查找已有的材质。

Rig选项卡

属性

Animation Type 动画类型

None 无

没有预制动画

Legacy 旧版

旧版系统动画

Generic 通用

通用Mecanim动画

Humanoid 人形

人形Mecanim动画系统

Avatar Definition 替身定义

在这里定义动画控制融合等。

Create from this model    从这个模型创建

替身物体将依据选择的模型创建

Copy from other Avatar    从其它替身拷贝

依据其它模型替身定义创建

Configure... 配置...

动画剪辑存储着所有可使用于角色动画或简单动画的动画数据。

没有更多推荐了,返回首页