认识未深,记录备忘
动画融合
新建状态机
面板上的Motion
//Motion运动
//Locomotion,运动,自己的命名
//Loco,机车
//Blend,混合
与普通动画在面板上的区别
//普通动画的Motion是一个动画Clip
新建行为树
//双击点进去
添加动画
//点击节点
//点击右边的先小圈圈,进行动画的添加
//动画项不够,就点击下面的“+”
(1D例子) Speed的大小控制由走到跑
//右下角可以拖其他模型
效果
//gif卡3M大小上传真不容易
可以拖其他模型到右下角的窗口
(2D例子)
unity TimeLine应用教程 酷炫开场动画 23-31
//cartesian,笛卡尔
//1、选择一个2D(哪个2D都试一试,主要出效果),选参数(Horizontal表示水平/左右,Vertical表示垂直/前后)
//2、随便选(可能就是角速度控制左右可以参考,其他的不知道要参考哪些要素),只要出3的效果
切/调整动画
一般是一个模型的所有动画都放在一个总Clip中,需要什么状态的Clip,已切好或自己切。
//1、切图看循环播放时是否连贯
//2、可以旋转,打钩(比如Idle/Walk/RunForward,就是不能旋转;相反地,RunLeft/RunRight没打钩·,可以旋转)
//3、Y轴没影响,都打钩,表示不会在Y(高度上变化)
//4、边移动边旋转,就是位移靠动画(以前不考就是靠角色控制器的SimpleMove()、MoveForward()之类的)
//5、镜像,省功夫的,比如RunRight是RunLeft的镜像,设置数据都是抄RunLeft的
//6、比如RunLeft/RunRight,Y角速度变化明显,所以在行为树中,适合用角速度来控制RunLeft/RunRight(详细有以下的“角速度”)
角速度
//角速度对转向的影响比较大
//转弯靠转身,而不是“螃蟹”横着走
效果
(问题) Animator打开为空
重启
IK
抗木头
unity TimeLine应用教程 酷炫开场动画 43-47
FixAnimatorIKPositionAndRotation(骨骼部位, 具体位置, 权重)【自定义的函数】
[Tooltip("肩膀上的木头,一开始不激活")] public GameObject wood;
[Tooltip("模型中左手腕处")] public Transform leftHand;
[Tooltip("模型中右手腕处")] public Transform rightHand;
//
private void OnTriggerEnter(Collider other)//接触木头
{
if (other.tag == "Log")
{
Destroy(other.gameObject);
print("碰撞");
CarryWood();
}
}
private void CarryWood()//扛木头
{
wood.SetActive(true);
animator.SetBool(willHoldLogID,true);
}
private void OnAnimatorIK(int layerIndex)//每帧调用,需要层启用IK
{
if (layerIndex == 1)//对应HoldLog层
{
int weight = animator.GetBool(willHoldLogID) == true ? 1:0;//willHoldLogID的值
FixAnimatorIKPositionAndRotation(AvatarIKGoal.LeftHand,leftHand, weight);
FixAnimatorIKPositionAndRotation(AvatarIKGoal.RightHand,rightHand, weight);
}
}
private void FixAnimatorIKPositionAndRotation(AvatarIKGoal target, Transform match, int weight)//使用match的位置角度来调整target
{
animator.SetIKPosition(target, match.position);//设置位置
animator.SetIKRotation(target, match.rotation);
animator.SetIKPositionWeight(target, weight);//设置优先级
animator.SetIKRotationWeight(target, weight);
}
第一个参数 骨骼部位
AvatarIKGoal.LeftHand
AvatarIKGoal.RightHand
意思是,这一层的状态机只动用到左右手。其他部分不归它管
第二个参数,位置LeftHand/RightHand
//具体操作是,在木头下新建两个空节点,来存储“抗木头”时左右手的Transform
//旋转手,使之贴合木头达到效果
//把此时左右手的Transform赋值给木头下的两个空节点。左右手复位
第三个参数 IK认骨骼的位置还是target(自己设置)的位置
//原理不清楚,反正不都设为1(认target的位置),都会出现位置错误
方法参数对比
目标匹配 MatchTarget
翻墙
//绿球是以下代码生成的
animator.MatchTarget(matchTarget,
Quaternion.identity,
avatarTarget,
new MatchTargetWeightMask(matchTargetWeightMask, 0),
startTime,
endTime
);
private void Vault()//腾跃,尝试过和Slide重构复用,太麻烦
{
//1、播放动画
//速度>3 && 是 LocalMotion行为树
//射线检测
float testDistance = 4f;//检测距离
float vaultDistance = 3f;//起跳距离
bool willVault = false;//是否要进行翻墙
//rayPos,direction,射线,长度
bool isHit = Physics.Raycast(transform.position+Vector3.up*0.3f, transform.forward, out RaycastHit hit, testDistance);
if (isHit && hit.collider.tag == "Obstacle")//障碍物是墙
{
if (hit.distance > vaultDistance)//
{
willVault = true;
//
Vector3 point = hit.point;
point.y = hit.collider.transform.position.y + hit.collider.bounds.size.y;//位置加自身高度
matchTarget = point;//得到着手点
matchTarget.y +=matchTargetOffsetY;
}
}
animator.SetBool(willVaultID, willVault);
//2、MatchTarget
//有一个状态叫ClimbOver && 在翻墙中, 不在状态切换过程中(差值)
if (animator.GetCurrentAnimatorStateInfo(0).IsName("ClimbOver") && animator.IsInTransition(0) == false)
{
//1、MatchTarget
float startTime = 0.3f;
float endTime = 0.4f;
MatchTarget(matchTarget, AvatarTarget.LeftHand, Vector3.one, startTime, endTime);
}
}
void MatchTarget(Vector3 matchTarget,AvatarTarget avatarTarget, Vector3 matchTargetWeightMask, float startTime, float endTime)
{
//动画在(startTime,endTime)时
animator.MatchTarget(matchTarget,
Quaternion.identity,
avatarTarget,
new MatchTargetWeightMask(matchTargetWeightMask, 0),
startTime,
endTime
);
}
滑地
//我主要调endTime
//视频matchTarget = hit.point+transform.forward * 2(越大越远,视频看效果时设为10,滑地了很远)
//matchTarget决定改动画在(startTime, endTime)期内,整个模型会向前移动多远
void Slide()//滑地
{
//1、播放动画
//速度>3 && 是 LocalMotion行为树
if (animator.GetFloat(verticalID) > runSpeed && animator.GetCurrentAnimatorStateInfo(0).IsName("LocalMotion"))
{
//射线检测
float testDistance = 4f;//检测距离
float slideDistance = 2f;//滑地距离
bool willSlide = false;//是否要进行翻墙
//start,direction,射线,长度
bool isHit = Physics.Raycast(transform.position + Vector3.up * 1.5f, transform.forward, out RaycastHit hit, testDistance);
if (isHit && hit.collider.tag == "Obstacle")//障碍物是墙
{
if (hit.distance > slideDistance)//起跳距离
{
willSlide = true;
//设置match
Vector3 point = hit.point;
point.y = 0;//滑行不影响高度
point = point + transform.forward*10;
matchTarget = point;//滑行个2米
}
}
animator.SetBool(willSlideID, willSlide);
}
//
if (animator.IsInTransition(0) == false && animator.GetCurrentAnimatorStateInfo(0).IsName("Slide"))
{
//1、MatchTarget
float startTime = 0.38f;//0.17
float endTime = 0.67f;//0.67f;//30
//float startTime = 0.17f;//0.17
//float endTime = 0.30f;//0.67f;//30
MatchTarget(matchTarget, AvatarTarget.Root, new Vector3(1, 0, 1), startTime, endTime );
}
}
void MatchTarget(Vector3 matchTarget,AvatarTarget avatarTarget, Vector3 matchTargetWeightMask, float startTime, float endTime)
{
//动画在(startTime,endTime)时
animator.MatchTarget(matchTarget,
Quaternion.identity,
avatarTarget,
new MatchTargetWeightMask(matchTargetWeightMask, 0),
startTime,
endTime
);
}
导播
public PlayableDirector director;//过场动画的相机,播放时间轴
private void OnTriggerEnter(Collider other)//接触木头
{
if (other.tag == "Playable")
{
director.Play();//播放Timeline
}
}