2015-01-25 22:09:32 egostudio 阅读数 1867

在实现角色二连跳的时候发现第二次AddForce的时候有时候起跳速度会变慢甚至会跳不起来是因为这时候rigidbody2D.velocity还有下落的速度导致。解决办法就是第二次AddForce之前将下落速度设置为0即可。三连跳四连跳同样的处理办法。

rigidbody2D.velocity=new Vector2(rigidbody2D.velocity.x,0);

2014-12-09 14:24:26 zhliu1991 阅读数 4472

在这个系列,我们将关注Unity引擎提供的基于骨骼动画工具。它的主要思想是为了把它应用到你自己的游戏来介绍和教基本的2D动画原理。在这节课中,我们将添加闲置,跳跃和动画。

在开始这个课程前,我们将感谢Chenguang (DragonBonesTeam)给我们提供用于本系列教程游戏艺术。

离开的地方

在 上一个课程,我们启动项目,合成2D龙角色,然后创建三种不同的动画。如果你还没完成之前的课程,在此之前,我们强烈建议您这样做。

预览效果        

这个demo演示了龙的动画,我们的目标是 –点击Space键就跳:

点击查看

Mecanim


这时候,你的龙已经完全合成并且做出了三种定义的动画。然而,他们之间还没有连接。所以,我们最初的的目标是连接不同的动画影片剪辑和混合在一起。为了达到这个目标,Unity提供你正需要的一个非常棒的工具,叫做Mecanim

Mecanim是一个功能强大且灵活的动画系统。因为它与Unity本身集成,所以不需要第三方的软件。你可以轻松改变任何东西,从精灵变成混合的形状,甚至是灯。Mecanim允许创建状态机和混合数来控制你的角色。

但是,在继续下去之前,让我们讨论一下关于混合动画和状态机,让我们有一个更好的了解我们要做什么。

状态机是什么?

Unity里,你可以混合两种或者更多的相似动画 ――举个例子来说,你想把跑步和步行的动画依靠角色当前的速度混合到一起。基本上,在Unity中有两种不同的方法可以混合动画。在一些情况下,你可能想要使用过渡,在其他情况下可能你需要使用混合树:


· 过渡常常是用在动画之间的自然过渡。这通常适用于是否快速过渡。

· 当合并部分的数量变量是,混合树允许在多种动画顺利融合。给一个实际的例子,假设有一个射击游戏,你可能想要你的角色跑着开枪。混合树允许你将两种动画混合到一起,不需要为这个特定动作的混合物创建第三种动画,就能让角色跑着开枪。


在给定的时间,一个状态机存储的状态实体,并且能够对一个输入改变实体的状态,活导致一个行动或者输出。有关更多消息请参见有限状态机理论和实现

Unity,你使用状态机来控制游戏角色的状态。举个例子,角色的一种状态叫walk,另一种状态叫Jump。一个角色从Walk的状态变成Jump的状态取决于播放器的输入(可能是通过点击Jump按钮)。

在这里你可以从Unity文旦中。看到一个例子(更复杂)的状态机。每一个箱子表示一个状态,它们之间的箭头表示可能的转换:

02.png

我们将要用我们存在的动画创建一个状态机,然后使用过渡来把它们混合起来。

创建我们的状态机

如果你检查一下Animations文件夹,你可以在Dragon.controller文件下看到已经保存为.anim文件。这些mecanim文件与角色关联,Unity保存在你第一个动画自动生成的地方。

03.png

双击 Dragon.controller 文件,Unity将打开在场景和游戏选项卡之间动画视图的选项卡。

正如你所看到的,Unity已经添加这三种动画到文档中了。因为动画已经在那里了,所以不需要添加,但是,如果你想添加额外的动画到控制器,你只需要把.anim文件拖拽到Animator视图中。同样的,如果你想移除已经存在控制器的动画,你只需要相中Animator视图,然后点击Delete按钮即可。随时可以自己试一下这个。

Animator中有四种不同的盒子:

· 任何状态

· 闲置

· 跳跃

· 下降

05.png

任何状态是mecanim创建的一种默认状态,你不能使用它。你可以把它拖拽到Animator窗口的任何角落下,然后放在哪里。

其他三种盒子使我们关于我们创建的三个动画。你可能会注意到,Idle是橙色的,其他两个是灰色的。这是因为idle是主动画,这是角色默认播放的动画。如果你在编辑器中点击Play按钮,然后测试它,你就可以看到角色的Idle动画。在这种特殊情况下,这确实是我们想要的行为。然而,如果你想要说Fall动画成为主动画,你需要做的就是单击右键选择设置为默认。

06.png

可以看到,现在Fall动画是橙色,idle是灰色的。

07.png

因为你想要idle成为主动画,仅仅只需要重复这个过程是颜色变为橙色即可。

现在是连接动画的时候了。右击Idle,然后选择 Make Transition。

08.png

这会在Idle这个创建一个小箭头。点击Jump动画来制作一个箭头来连接两个动画。

09.png

如果你选择你刚刚创建的箭头,你将会看到在Inspector选项卡中会显示一个新的属性。

10.png

你可以看到,有一个时间轴,还有IldeJump动画。在Idle动画的开始有一个蓝色带,但是后来变成Jump。同样的,在这两个动画之间有一段重叠的时间。

11.png

因为预览区域是空白的,即时你点击了预览的Play按钮,也不能看到什么变化。

为了预览现在做的过渡,只需要选中层级视图的选项卡中Dragon游戏物体,并把它拖拽到预览区域即可。现在你可以看到角色的预览,如果点击play按钮,你就可以看到两个动画之间的过渡。

图片1.png

在监控器中,看到IdleJump转变的蓝色带区域就是过渡:

图片2.png

在过渡区域,你可以通过拖拽时间轴上蓝色箭头来编辑过渡。通过改变它们的位置,你可以让过渡变得更加快和平缓。

接下来你可以定义你什么时候想让过渡发生。为了达到这个目标,通过点击在Parameters 列表下的+标志创建一个新的参数。

图片3.png

接下来,选择Float选项并叫为VerticalMovement:

图片4.png

现在,返回到Inspector,在 Conditions下会显示一个VerticalMovement值,然后选择它。

图片5.png

你刚刚定义的条件是来确定何时改变状态机的状态:如果VerticalMovement的值比0大,那角色就会开始Jump的动画。

我们也需要Jump动画到Fall动画的过渡:


VerticalMovement的最大值将达到1,因此,为了JumpFall的过渡,需要把值设置低于0.5.


现在我们需要在Fall之后设置角色返回到Idle。因为idle播放时角色应该是在地板上,我们应该创建一个从FallIdle的过渡。

图片6.png

为了完成,你必须确保活动时角色是在地面上的。为了达到这个效果可以通过设置VerticalMovement 过渡参数低于0.1—这基本上意味着的VerticalMovement值为0,表示角色在地面上。

图片7.png

我们需要确保在空中JumpFall动画直接角色没有出现Idle动画。为了达到这个效果,还需要创建一个新的参数,这次是一个布尔Bool

图片8.png

把它叫做OnGround。

图片9.png

选中JumpFall之间的过渡。你要这个角色的过渡依旧是在空中,对吗?所以到Inspector,点击+,然后在过渡中添加一个新的参数。从根本上上讲,当的OnGround值为 false时你想要的这样的事情就可以发生

。 图片10.png

接下来,从FallIdle的动画过渡,添加参数 OnGround 并设置为true。

图片11.png

我们做的Mecanim开始动了。现在是到我们的脚本了。

脚本动画

在你的asset目录下,创建一个叫Scripts的文件夹。接下来,创建一个名为CharacterMove.cs新的C#脚本。请注意,你现在创建的脚本是一个非常简单的,它的主要目标就是显示如何通过代码改变角色的动画。

当你想要创建一个健壮的游戏,最好的实践就是使用Unity本身。然而,为了简单起见和理解,我们将创建一个小的模拟。

图片12.png

在脚本中创建四个变量:一个是Animator组件,另一个是下降的速度,第三个是垂直运动,和一个标记检查角色是否在地上。

  1. 在Start()方法中,我们需要确保速度设置为0.03(或者你举得适合动画的值)并且角色在地面上。void Start () {
  2.     // The character starts on the ground
  3.     onGround = true;
  4.      
  5.     // Set the fall speed
  6.     fallSpeed = 0.03f;
  7. }
复制代码

现在,在 Update() 方法中,有几个地方需要检查。第一,你需要检测当空格键被点击时,角色在跳。当它被按下时,设置水平运动为1onGround 标志为false。

  1. void Update () {
  2.     // If the space bar is pressed and the character is on the ground
  3.     if (Input.GetKeyDown(KeyCode.Space) == true && onGround == true)
  4.     {
  5.         verticalMovement = 1f;
  6.         onGround = false;
  7.     }
  8. }
复制代码

如果不是空格键被按下呢?非常好,你需要检测角色在空气中和它的水平运动大于0;如果这样的话,你需要通过减少下降速度来减少水平运动。

  1. void Update () {

  2.     // If the space bar is pressed and the character is on the ground
  3.     if (Input.GetKeyDown(KeyCode.Space) == true && onGround == true)
  4.     {
  5.         verticalMovement = 1f;
  6.         onGround = false;
  7.     }
  8.     else
  9.     {
  10.         // Check if the character is in the air and the vertical movement greater than 0
  11.         if(onGround == false && verticalMovement > 0)
  12.         {
  13.             // Reduce vertical movement
  14.             verticalMovement -= fallSpeed;
  15.         }
  16.     }
  17. }
复制代码

你要记得,一旦verticalMovement 低于0.5Fall动画将开始播放。

然而,你永远不想从verticalMovement 减去fallSpeed ,因为这个角色将会回到地面上。如果水平运动的值等于或者小于0,这意味着角色将抵达地面上。


  1. void Update () {
  2.     // If the space bar is pressed and the character is on the ground       if (Input.GetKeyDown(KeyCode.Space) == true && onGround == true)        {
  3.         verticalMovement = 1f;
  4.         onGround = false;
  5.     }
  6.     else
  7.     {
  8.     // Check if the character is in the air and the vertical movement greater than 0
  9.     if(onGround == false && verticalMovement > 0)
  10.     {
  11.         // Reduce vertical movement
  12.         verticalMovement -= fallSpeed
  13.          
  14.         // If the vertical movement is less or equal to 0, the character is on the floor
  15.         if (verticalMovement < 0)
  16.         {
  17.             verticalMovement = 0;
  18.             onGround = true;
  19.         }
  20.     }
  21.     }
  22. }
复制代码


在Update() 方法的结尾,你需要把verticalMovement和onGround 的值传递给Animator组件。

  1. void Update () {

  2.     // If the space bar is pressed and the character is on the ground
  3.     if (Input.GetKeyDown(KeyCode.Space) == true && onGround == true)
  4.     {
  5.         verticalMovement = 1f;
  6.         onGround = false;
  7.     }
  8.     else
  9.     {
  10.         // Check if the character is in the air and the vertical movement greater than 0
  11.         if(onGround == false && verticalMovement > 0)
  12.         {
  13.             // Reduce vertical movement
  14.             verticalMovement -= fallSpeed;
  15.             // If the vertical movement is less or equal to 0, the character is on the floor
  16.             if (verticalMovement < 0)
  17.             {
  18.             verticalMovement = 0;
  19.             onGround = true;
  20.             }
  21.         }
  22.     }

  23.     // Update the animator variables
  24.     anim.SetFloat("VerticalMovement", verticalMovement);
  25.     anim.SetBool("OnGround", onGround);
  26. }
复制代码

脚本完成了。现在,你需要把它添加到Dragon游戏物体上,并添加到Animator组件的引用。为了完成这个,一旦你添加脚本,脚本Animator拖拽到的合适字段上。

26.png

如果你点击Play按钮并测试,动画应该像他们想象的改变。龙开始是闲置状态,但是一旦你点击空格键,它就会跳上来接着播放下降动画,然后恢复到之前的闲置状态。

外部的工具和技术

尽管这个课程中我们仅仅使用到Unity默认的工具,在Unity资源存储中有很多非常好的2D工具来帮助你使这个制作过程更容易和更快。

这样的插件提供一些配件,像添加2D“骨骼”的能力,是整个动画制作过程更容易和变形更现实。如果你的想法是利用一些细节2D动画,我们强烈建议你去看看这些插件。

总结

我们这一系列关于如何在Unity创建基于骨骼2D动画的教程到此结束。我们涉及到很多简短的系列,你现在应该知道的足以开始你的2D动画。如果你有一些问题或者评论,和往常一样,随时在评论给我们写道。

参考资料

龙精灵表:从Chenguang 的DragonBonesTeam获得适用权限


2012-09-14 15:10:28 wuqinxiang 阅读数 2980

//--------------------------------------------------------------------------------------MyCameraScrolling.js

private var target : Transform;//摄像机要跟随的目标

var distance = 25.0;//摄像机距离目标多远

var springiness = 40.0;//相机跟随目标的严格程度,值越小摄像机会变懒


private var targetLock = false;

private var savedInterpolationSetting = RigidbodyInterpolation.None;


function Awake () {
 // Set up our convenience references.
 
}


function SetTarget (newTarget : Transform, snap : boolean){
 
 if  (target) {//假如摄像机跟随的目标不为空 
  targetRigidbody = target.GetComponent (Rigidbody);//获取刚体组件
  if  (targetRigidbody)//目标具有刚体属性
   targetRigidbody.interpolation = savedInterpolationSetting;//设置刚体插值
 }
 
 target = newTarget;//重新设置目标
 
 if (target) {
  targetRigidbody = target.GetComponent (Rigidbody);
  if (targetRigidbody) {
   savedInterpolationSetting = targetRigidbody.interpolation;
   targetRigidbody.interpolation = RigidbodyInterpolation.Interpolate;
  }
 }
 
 if  (snap) {//是否关闭摄像机
  transform.position = GetGoalPosition ();
 }
 
}

function SetTarget (newTarget : Transform) {
 SetTarget (newTarget, false);
}


function LateUpdate () {
 // Where should our camera be looking right now?
 var goalPosition = GetGoalPosition ();
 
 // Interpolate between the current camera position and the goal position.
 // See the documentation on Vector3.Lerp () for more information.
 transform.position = Vector3.Lerp (transform.position, goalPosition, Time.deltaTime * springiness);
 transform.LookAt(target.position);//摄像机注视着玩家
}

function GetGoalPosition () {

 if  (!target)//如果没有目标就返回摄像机的位置
  return transform.position;
  
 var heightOffset = 6.0;//摄像机偏离目标的高度
 var distanceModifier = 1.0;//摄像机与目标的水平便宜量
 
 // 默认情况下,我们不会考虑任何目标速度的计算;
 var velocityLookAhead = 0.0;
 var maxLookAhead = Vector2 (0.0, 0.0);
 
 // 获取目标的额外属性组件,这里指的是-----脚本
 // 根据相对高度和距离粗略计算摄像机位置, 不考虑目标的速度。这个vector的x为0是 因为摄像机时正对着目标的所以x为0 摄像机是在目标的侧面而不是目标的背面
 var goalPosition = target.position + Vector3 (0, heightOffset, -distance * distanceModifier);
 
 //下面开始将目标的速度考虑在内
 var targetVelocity = Vector3.zero;//初始化无速度
 
 var targetRigidbody = target.GetComponent (Rigidbody);
 if (targetRigidbody)//获取目标的速度
  targetVelocity = targetRigidbody.velocity;
  
 // If we find a PlatformerController on the target, we can access a velocity from that!
 targetPlatformerController = target.GetComponent (MyPlatformerController);
 if (targetPlatformerController)
  targetVelocity = targetPlatformerController.GetVelocity ();//从该脚本中获取目标的速度
  
 var lookAhead = targetVelocity * velocityLookAhead;
 
 //限制lookAhead。x的值在-maxLookAhead.x和maxLookAhead.x之间
 //限制value的值在min和max之间, 如果value小于min,返回min。 如果value大于max,返回max,否则返回value
 
 lookAhead.x = Mathf.Clamp (lookAhead.x, -maxLookAhead.x, maxLookAhead.x);
 lookAhead.y = Mathf.Clamp (lookAhead.y, -maxLookAhead.y, maxLookAhead.y);
 
 // We never want to take z velocity into account as this is 2D.  Just make sure it's zero.
 lookAhead.z = 0.0;
 
 goalPosition += lookAhead;
 
 var clampOffset = Vector3.zero;
 
 var cameraPositionSave = transform.position;//摄像机的旧的位置
 transform.position = goalPosition;//重新设置摄像机的位置
 
 //一下四行计算的是摄像机有没有超过整个大场景边框
 /*var targetViewportPosition = camera.WorldToViewportPoint (target.position);//目标在窗口(记住是窗口)中的位置
 var upperRightCameraInWorld = camera.ViewportToWorldPoint (Vector3 (1.0, 1.0, targetViewportPosition.z));
 // Find out how far outside the world the camera is right now.
 clampOffset.x = Mathf.Min (levelBounds.xMax - upperRightCameraInWorld.x, 0.0);
 clampOffset.y = Mathf.Min ((levelBounds.yMax - upperRightCameraInWorld.y), 0.0);*/
 
 return goalPosition;
}
function Start () {

}

function Update () {

}

 

//-------------------------------------------------MyCameraForcus.js

private var cameraScrolling : MyCameraScrolling;

// Who is the player controlling
private var selected = 0;

// List of objects to control
public var targets : Transform;

function Awake () {

 // Get the reference to our CameraScrolling script attached to this camera;
 cameraScrolling = GetComponent(MyCameraScrolling);
 
 // Set the scrolling camera's target to be our character at the start.
 cameraScrolling.SetTarget (targets, true);
 
}

 

//-------------------------------------------------------------MyPlatformerController.js


//是否需要对输入做出反应
//sendmessage第二个参数的用法请看下面
//SendMessageOptions.RequireReceiver //如果没有找到相应函数,会报错(默认是这个状态)
//SendMessageOptions.DontRequireReceiver //即使没有找到相应函数,也不会报错,自动忽略
var canControl = true;

var spawnPoint : Transform;//玩家重生点

class MyPlatformerControllerMovement {
 // The speed when walking
 var walkSpeed = 3.0;
 // when pressing "Fire1" button (control) we start running
 var runSpeed = 10.0;

 var inAirControlAcceleration = 1.0;

 // The gravity for the character
 var gravity = 60.0;
 var maxFallSpeed = 20.0;

 // How fast does the character change speeds?  Higher is faster.
 var speedSmoothing = 5.0;

 // This controls how fast the graphics of the character "turn around" when the player turns around using the controls.
 var rotationSmoothing = 10.0;

 // The current move direction in x-y.  This will always been (1,0,0) or (-1,0,0)
 // The next line, @System.NonSerialized , tells Unity to not serialize the variable or show it in the inspector view.  Very handy for organization!
 @System.NonSerialized
 var direction = Vector3.zero;

 // The current vertical speed
 @System.NonSerialized
 var verticalSpeed = 0.0;

 // The current movement speed.  This gets smoothed by speedSmoothing.
 @System.NonSerialized
 var speed = 0.0;

 // Is the user pressing the left or right movement keys?
 @System.NonSerialized
 var isMoving = false;

 // The last collision flags returned from controller.Move
 @System.NonSerialized
 var collisionFlags : CollisionFlags; //这是unity3d的碰撞信息,具体可以参考http://game.ceeger.com/Script/Enumerations/CollisionFlags/CollisionFlags.None.html

 // We will keep track of an approximation of the character's current velocity, so that we return it from GetVelocity () for our camera to use for prediction.
 @System.NonSerialized
 var velocity : Vector3;
 
 // This keeps track of our current velocity while we're not grounded?
 @System.NonSerialized
 var inAirVelocity = Vector3.zero;//在空中的时候的速度

 // This will keep track of how long we have we been in the air (not grounded)
 @System.NonSerialized
 var hangTime = 0.0;//在空中停留的时间
}

var movement : MyPlatformerControllerMovement;

// We will contain all the jumping related variables in one helper class for clarity.
class MyPlatformerControllerJumping {
 // Can the character jump?
 var enabled = true;

 // How high do we jump when pressing jump and letting go immediately
 var height = 1.0;
 // We add extraHeight units (meters) on top when holding the button down longer while jumping
 var extraHeight = 4.1;
 
 // This prevents inordinarily too quick jumping
 // The next line, @System.NonSerialized , tells Unity to not serialize the variable or show it in the inspector view.  Very handy for organization!
 @System.NonSerialized
 var repeatTime = 0.05;

 @System.NonSerialized
 var timeout = 0.15;

 // Are we jumping? (Initiated with jump button and not grounded yet)
 @System.NonSerialized
 var jumping = false;
 
 @System.NonSerialized
 var reachedApex = false;// 达到最高点?
 
 // Last time the jump button was clicked down
 @System.NonSerialized
 var lastButtonTime = -10.0;
 
 // Last time we performed a jump
 @System.NonSerialized
 var lastTime = -1.0;

 // the height we jumped from (Used to determine for how long to apply extra jump power after jumping.)
 @System.NonSerialized
 var lastStartHeight = 0.0;
}

var jump : MyPlatformerControllerJumping;

private var controller : CharacterController;

// Moving platform support.
private var activePlatform : Transform;
private var activeLocalPlatformPoint : Vector3;
private var activeGlobalPlatformPoint : Vector3;
private var lastPlatformVelocity : Vector3;

private var areEmittersOn = false;//粒子是否开启 可能是跑步的时候的粒子效果

function Awake () {
 movement.direction = transform.TransformDirection (Vector3.forward);
 controller = GetComponent (CharacterController);
 Spawn ();
}

function Spawn () {
 // reset the character's speed
 movement.verticalSpeed = 0.0;
 movement.speed = 0.0;
 
 // reset the character's position to the spawnPoint
 transform.position = spawnPoint.position;//玩家位置被送到重生点
}

function OnDeath () {
 Spawn ();
}


function UpdateSmoothedMovementDirection () {

 //通过坐标轴名称返回一个不使用平滑滤波器的虚拟值。往左为-1往右边卫+1没有输入的时候为0
 var h = Input.GetAxisRaw ("Horizontal");
 Debug.Log(h);
 if (!canControl)
  h = 0.0;
 
 movement.isMoving = Mathf.Abs (h) > 0.1;
  
 if (movement.isMoving)
  movement.direction = Vector3 (h, 0, 0);
 
 // Grounded controls
 if (controller.isGrounded) {//判断玩家是否在地面上
  // Smooth the speed based on the current target direction
  var curSmooth = movement.speedSmoothing * Time.deltaTime;//玩家的加速度
  
  // Choose target speed
  var targetSpeed = Mathf.Min (Mathf.Abs(h), 1.0);
 
  // Pick speed modifier
  if (Input.GetButton ("Fire2") && canControl)
   targetSpeed *= movement.runSpeed;
  else
   targetSpeed *= movement.walkSpeed;
  
  movement.speed = Mathf.Lerp (movement.speed, targetSpeed, curSmooth);
  
  movement.hangTime = 0.0;//一旦落到地上空中滞留时间就设置为0;
 }
 else {
  // In air controls
  movement.hangTime += Time.deltaTime;
  if (movement.isMoving)
   movement.inAirVelocity += Vector3 (Mathf.Sign(h), 0, 0) * Time.deltaTime * movement.inAirControlAcceleration;//空中移动速度
 }
}

function FixedUpdate () {
 // Make sure we are absolutely always in the 2D plane.
 transform.position.z = 0;//时刻保持z不变

}


function ApplyJumping () {
 // Prevent jumping too fast after each other
 if (jump.lastTime + jump.repeatTime > Time.time)
  return;

 if (controller.isGrounded) {
  // Jump
  // - Only when pressing the button down
  // - With a timeout so you can press the button slightly before landing  
  if (jump.enabled && Time.time < jump.lastButtonTime + jump.timeout) {
   movement.verticalSpeed = CalculateJumpVerticalSpeed (jump.height);
   movement.inAirVelocity = lastPlatformVelocity;
   

   SendMessage ("DidJump", SendMessageOptions.DontRequireReceiver);
  }
 }
}

function ApplyGravity () {
 // Apply gravity
 var jumpButton = Input.GetButton ("Jump");
 
 if (!canControl)
  jumpButton = false;
 
 // When we reach the apex of the jump we send out a message
 if (jump.jumping && !jump.reachedApex && movement.verticalSpeed <= 0.0) {//达到最高点
  jump.reachedApex = true;
  SendMessage ("DidJumpReachApex", SendMessageOptions.DontRequireReceiver);//不接受输入
 }
 
 // * When jumping up we don't apply gravity for some time when the user is holding the jump button
 //   This gives more control over jump height by pressing the button longer
 var extraPowerJump =  jump.jumping && movement.verticalSpeed > 0.0 && jumpButton && transform.position.y < jump.lastStartHeight + jump.extraHeight && !IsTouchingCeiling ();
 
 if (extraPowerJump == true)//额外的跳跃不需要重力,因为不符可重力学
 
  return;
 else if (controller.isGrounded)
  movement.verticalSpeed = -movement.gravity * Time.deltaTime;//当在地面的时候才垂直速度固定
 else
  movement.verticalSpeed -= movement.gravity * Time.deltaTime;//在空中的时候根据重力加速度 进行速度递减
  
 // Make sure we don't fall any faster than maxFallSpeed.  This gives our character a terminal velocity.
 //玩家往上的时候速度为正故  movement.verticalSpeed肯定大于-movement.maxFallSpeed,当玩家掉下的时候速度为负数,故可以和-movement.maxFallSpeed比较大小
 movement.verticalSpeed = Mathf.Max (movement.verticalSpeed, -movement.maxFallSpeed);
}


function CalculateJumpVerticalSpeed (targetJumpHeight : float) {//根据v = sqrt(2Gh)来计算出速度
 // From the jump height and gravity we deduce the upwards speed
 // for the character to reach at the apex.
 return Mathf.Sqrt (2 * targetJumpHeight * movement.gravity);
}

function DidJump () {
 jump.jumping = true;
 jump.reachedApex = false;
 jump.lastTime = Time.time;
 jump.lastStartHeight = transform.position.y;
 jump.lastButtonTime = -10;
}


function UpdateEffects () {
 wereEmittersOn = areEmittersOn;
 areEmittersOn = jump.jumping && movement.verticalSpeed > 0.0;
 
 // By comparing the previous value of areEmittersOn to the new one, we will only update the particle emitters when needed
 if (wereEmittersOn != areEmittersOn)
 {
  for (var emitter in GetComponentsInChildren (ParticleEmitter)) {
   emitter.emit = areEmittersOn;
  }
 }
}

function Update () {
 if (Input.GetButtonDown ("Jump") && canControl) {//按下跳跃键并且可以控制
   jump.lastButtonTime = Time.time;//记录当前按键的时间
 }
 
 UpdateSmoothedMovementDirection();
 ApplyGravity ();

 ApplyJumping ();
 
 if (activePlatform != null) {
  var newGlobalPlatformPoint = activePlatform.TransformPoint(activeLocalPlatformPoint);//自身坐标转化为世界坐标
  var moveDistance = (newGlobalPlatformPoint - activeGlobalPlatformPoint);
  transform.position = transform.position + moveDistance;
  lastPlatformVelocity = (newGlobalPlatformPoint - activeGlobalPlatformPoint) / Time.deltaTime;
 } else {
  lastPlatformVelocity = Vector3.zero; 
 }
 
 activePlatform = null;
 
 // Save lastPosition for velocity calculation.
 lastPosition = transform.position;
 
 var currentMovementOffset = movement.direction * movement.speed + Vector3 (0, movement.verticalSpeed, 0) + movement.inAirVelocity;
 
 // We always want the movement to be framerate independent.  Multiplying by Time.deltaTime does this.
 currentMovementOffset *= Time.deltaTime;
 
    // Move our character!
 movement.collisionFlags = controller.Move (currentMovementOffset);
 
 // Calculate the velocity based on the current and previous position. 
 // This means our velocity will only be the amount the character actually moved as a result of collisions.
 movement.velocity = (transform.position - lastPosition) / Time.deltaTime;
 
 if (activePlatform != null) {
  activeGlobalPlatformPoint = transform.position;
  activeLocalPlatformPoint = activePlatform.InverseTransformPoint (transform.position);
 }
 
 // Set rotation to the move direction 
 if (movement.direction.sqrMagnitude > 0.01)
  transform.rotation = Quaternion.Slerp (transform.rotation, Quaternion.LookRotation (movement.direction), Time.deltaTime * movement.rotationSmoothing);
 
 // We are in jump mode but just became grounded
 if (controller.isGrounded) {
  movement.inAirVelocity = Vector3.zero;
  if (jump.jumping) {
   jump.jumping = false;
   SendMessage ("DidLand", SendMessageOptions.DontRequireReceiver);

   var jumpMoveDirection = movement.direction * movement.speed + movement.inAirVelocity;
   if (jumpMoveDirection.sqrMagnitude > 0.01)
    movement.direction = jumpMoveDirection.normalized;
  }
 } 

 // Update special effects like rocket pack particle effects
 UpdateEffects ();
}


function GetSpeed () {
 return movement.speed;
}

function GetVelocity () {
 return movement.velocity;
}


function IsMoving () {
 return movement.isMoving;
}

function IsJumping () {
 return jump.jumping;
}

function IsTouchingCeiling () {//头碰到了顶部
 return (movement.collisionFlags & CollisionFlags.CollidedAbove) != 0;
}

function GetDirection () {
 return movement.direction;
}

function GetHangTime() {
 return movement.hangTime;
}

function Reset () {
 gameObject.tag = "Player";
}

function SetControllable (controllable : boolean) {
 canControl = controllable;
}

// Require a character controller to be attached to the same game object
@script RequireComponent (CharacterController)
//AddComponentMenu属性允许你在"Component"菜单中放置一个无论在哪的脚本,而不是仅仅在"Component->Scripts"菜单中。
//@script AddComponentMenu ("2D Platformer/Platformer Controller")

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

2017-02-13 14:30:00 weixin_33767813 阅读数 366
using UnityEngine;
using System.Collections;

public class HeroColtrol : MonoBehaviour
{
    private Rigidbody2D HeroRd;
    public float MoveSpeed;
    public float JumpHeight;
    void Awake()
    {
        HeroRd = transform.GetComponent<Rigidbody2D>();
    }


    void FixedUpdate()
    {
        float h = Input.GetAxis("Horizontal");
        if (!h.Equals(0))
        {
            HeroRd.velocity = new Vector2(h * MoveSpeed, HeroRd.velocity.y);
        }
        if (Input.GetKeyDown(KeyCode.Space))
        {
            if (HeroRd.velocity.y.Equals(0))
                HeroRd.velocity = new Vector2(HeroRd.velocity.x, JumpHeight);
        }

    }

}

img_b31e84f622515094669153d0387f082d.png
图片.png
2015-01-28 23:19:43 linkfly1 阅读数 828

       所谓角色换肤也就是游戏中常见的角色自定制功能:玩家开始游戏前,一般需要创建一个游戏角色,选择性别、容貌、发型等等。进入游戏后,就可以操作自己创建的角色做出行走、跳跃、攻击等动作。很多游戏还允许玩家在中途变换发型甚至改变体态(例如升级后肌肉变得发达)等等。下图就是WOW中角色选择的画面示例。



从游戏开发者的角度来看,由于可定制元素排列组合的结果非常多,不可能事先将所有结果都一个一个建出来并施加动作。所以必须采取分部位建模,然后通过编程控制,在游戏中将用户选择的各部位重新组合成完整角色。这个制作流程大致包括下列几步:


步骤 执行人员(建议)
1.规划换肤区域 策划
2.制作标准模型 美工
3.制作动作 动作
4.制作可替换部位的模型 美工
5.导出标准模型和动作 程序
6.导出可替换部位的模型 程序
7.在Unity中导入上述模型 程序
8.编写控制代码 程序

1.规划换肤区域


        这主要是一项策划工作。负责策划的朋友应该事先规划好角色的哪些身体部位可以替换,并作出原图,方便美工和动作进行设计。需要注意一点就是,对于可能经常离开人体的对象,例如盔甲、武器等,不应包括在内。


2.制作标准模型


       标准模型就是包含了角色网格、骨骼和动作的模型。策划设计的角色可能有多种组合方案,但美工可以随便选取其中一种来做标准模型。标准模型的用途有两个,首先是美工可以基于它制作余下的各种可选部位的模型;其次在游戏中程序也是通过替换标准模型中的某一部分来完成换装的。在3DS Max中制作标准模型要点是:


  • 必须包含全套骨骼和一张带蒙皮的网格。只有包含骨骼和蒙皮的模型导入Unity后才能作为后续编程的基础。注意网格必须有且仅有一张,但其形状可以随便。因为最后我们用到的只是骨骼,这张蒙皮的作用仅仅是导入时触发Unity的一个自动功能而已(美工不必关心,我就不展开说了)。它最后会在换肤时被替换掉的。
  • 必须为每块骨骼用英文取一个好识别的名字(拼音也行)。因为后续程序要用到这些名字,太怪异程序员兄弟难理解,中文或太长也不利于程序处理。
  • 材质和动作可有可无,按美工自己的喜好来就行。反正材质导入Unity后还得重新设,动作全部做在一个文件里或者分开文件单独做Unity都能正确识别;
  • 角色最好站立在原点,这样便于导入Unity后定位;
  • 文件最好干净点,不要包含不必要的几何或其他元素。
  • Max里角色身高方向一般是Z向,而Unity里面是Y向。所以为了导入Unity后好用,可以在Max里就把角色建成身高沿Y向,面朝Z向。不过这样美工可能觉得别扭,不做强求,Unity导入后加个旋转也能解决。
  • 单位的问题。Max里按1单位=1米或者1单位=1厘米都可以,导入Unity的时候可以选缩放比例的。


3.制作动作


动作师应该基于标准模型的骨骼来制作角色的全套动作。把所有动作做在一个文件里或者分开做在多个文件里都可以。Unity导入的时候处理下就行了。


特别说明一下,有些文章说制作动作时不能使用IK。其实是可以的,对此官方也有说明。只要导出前处理下就行。


4.制作可替换部位的模型


美工应该基于标准模型的骨骼来制作角色的各个可替换部位,并将该部位的蒙皮做好。每个部位只能包含一张网格和一个材质。看美工的喜好,既可以把所有部位都做在一个文件里,也可以分开做。反正导出的时候人工控制好一个一个分开就行了。至于接缝什么的美工应该懂得怎么处理好。


5.导出标准模型和动作


Unity接收FBX格式的文件。如果程序懂一点max的话,我建议导出工作都让程序来做,省得导不合适的话反复传文件,当然美工来导也是可以的。

标准模型需要导出的是蒙皮和骨骼,如果包含动画的话也可以导出动画。我在3DS Max8中的导出FBX设置如下图



 

注意:如果骨骼动作使用了IK,导出前需要做一下处理,否则导入Unity后动不了。选择所有骨骼和IK,然后在“运动”面板中选“轨迹”,在下面点“塌陷”按钮,就可以创建基于关键帧的动作。然后再把所有IK节点删除,检查下动作正常,就可以继续导出了。


如果动作时单独做在其他文件里的,那么打开该文件,仅导出动画和骨骼,按如下设置导出。文章出处狗刨学习网



注意导出的FBX文件名必须参考标准模型按规则来取。假设标准模型导出FBX文件取名叫AAA.fbx ,那么动作导出的FBX取名就必须以AAA@开头,例如AAA@jump.fbx, AAA@kick.fbx等等。


6.导出可替换部位的模型


可替换部位的模型主要导出网格蒙皮和材质。导的时候注意一个fbx应该只包含一个部位。导出设置如下图



7.在Unity中导入上述模型


标准模型、动作和各部位的FBX都导好后,就可以将它们放置到Unity游戏工程目录的Assets目录下了。Unity会自动检查文件并导入它们。该环节主要注意2点


  • 不要把所有fbx都放在Assets根目录下。应该建立一些便于管理的子目录来存放。
  • 如果一个fbx包含多个动作的,记得在Unity项目视图中选择该fbx文件对应的节点,在面板中设置各段动作的起始帧、结束帧位置,让Unity将动作分段提取出来。
没有更多推荐了,返回首页