该文来自学习chutianbo老师的笔记,链接b站
关于帧动画的制作
关于一个角色的动画制作,我们要先为这个角色加上一个组件Animator(动画控制器).
之后在我们使用ctrl+6打开Animation面板,选中我们Prefabs下的角色,点击创建Animation Cilp,将新建文件动画放置我们在assets下创建的Animation文件夹中。
之后我们在Characters文件夹中找到不同形态的素材
如上图,robot动作分为idle,fixed,walk三个状态,其中walk中的动画分为四个方向。我们使用Shift按住选中其中归属down的四张照片拖入Animation面板中进行帧制作即可。
问题:
1.动作只有左没有右
使用Filp X进行反转即可
2.名字右边三个符号分别表示调到指定帧,加入关键帧,加入事件
3.制作的帧动画事件尽量不要低于0.10,不然后面混合树的tigger判定会出bug
使用混合树为角色加上动画
在制作完动画之后,我们就需要为角色加上行为流程。
在制作animation时,他会自动创建一个对应的animator点击进入
会发现我们之前做的每个动画都散落在其中,所以按照归类,我们使用右键创建create state➡New Blend tree。
因为机器人的动画有朝向问题,所以我们在左边的paramemters中加入参数判定 Move X,和Move Y,都是float类型
大概做完就是这个样子
这里要记得
将混合树类型改为两个控制参数,然后加入自己做的动画即可
这里上述的动画我是没有做参数控制的,下述主角动画
分别为四个状态 idle(站立),launch(射击),Moving(奔跑),hit(受击)
Speed>0.1时 idle➡Moving
Look X和Look Y来表示面向(下面会有代码计算当前面向)
Lanuch和Hit为tigger类型当条件触发进入,且回自动跳回上一状态;
//我的角色初始面向向下
Vector2 lookDirection = new Vector2(0, -1);
Vector2 move;
void Update(){
move = new Vector2(horizontal, vertical);
//浮点数近似相等
if (!Mathf.Approximately(move.x, 0.0f) || !Mathf.Approximately(move.y, 0.0f))
{
lookDirection.Set(move.x, move.y);
//使数值归一为向量
lookDirection.Normalize();
}
animator.SetFloat("Look X", lookDirection.x);
animator.SetFloat("Look Y", lookDirection.y);
animator.SetFloat("Speed", move.magnitude);
}
做好了动画,使用参数控制动画
声明动画对象
Animator animator;
2.在start中获得动画实例
animator = GetComponent();
3.在FixedUpdate中加入判定
if (vertical)
{
position.y = position.y + speed * Time.deltaTime*direction;
animator.SetFloat("Move X",0);
animator.SetFloat("Move Y", direction);
}
else
{
position.x = position.x + speed * Time.deltaTime * direction;
animator.SetFloat("Move X", direction);
animator.SetFloat("Move Y", 0);
}
rigidbodyroot.MovePosition(position);
```
# 关于主角攻击
动画制作同上不再赘述,这里我们讲述一下关于主角射击发出子弹的事情
1.在子弹预制件中加入刚体和碰撞体,同时为子弹projecttile预制件挂上脚本
``
Rigidbody2D rigidbodyproject;
// Start is called before the first frame update
void start()
{
rigidbodyproject = GetComponent<Rigidbody2D>();
}
public void launch(Vector2 direction,float force){
//给子弹一个发射的力
rigidbodyproject.AddForce(direction*force);
}
private void OnCollisionEnter2D(Collision2D collision)
{
Debug.Log($"碰到了{collision.gameObject}");
//子弹撞见实体时消失
Destroy(gameObject);
//当子弹遇见机器人时,修复机器人
EnemyController2 emenyController2 = collision.collider.GetComponent<EnemyController2>();
if (emenyController2 != null)
{
emenyController2.Fix();
}
}
private void Update()
{
//如果没有碰到任何碰撞体,过远自动销毁
if (transform.position.magnitude>100)
{
Destroy(gameObject);
}
}
2在主角代码中加入
public GameObject projecttilePrefabs;//表示获取子弹预制件
3.为主角加入发射函数
void Launch()
{
//在指定位置创建对象
GameObject projecttileObject = Instantiate(projecttilePrefabs, rigidbody2dRuby.position + Vector2.up * 0.5f, Quaternion.identity);
projecttile projecttile = projecttileObject.GetComponent<projecttile>();
projecttile.launch(lookDirection, 300);
}
上述Instantiate表示创建实体是unity的一个api参数第一个是对象,第二个是创建位置,第三个是旋转
,Quaternion.identity表示不旋转(这里我们可以回到最开始创建主角移动时,如果不勾选冰冻z轴的样子)
4.遇见bug 233
我们会发现第一件事,unity会报错表示子弹预制件的刚体获取为null,这是因为,下述为unity官方解释:
这是因为在你创建对象时 Unity 不会运行 Start,而是在下一帧才开始运行。因此,在飞弹上调用 Launch 时,只实例化 (Instantiate),不调用 Start,因此 Rigidbody2d 仍然为空。要解决此问题,请将 Projectile 脚本中的 void Start() 函数重命名为 void Awake()。
与 Start 刚好相反,在创建对象时(调用 Instantiate 时)就会立即调用 Awake,因此在调用 Launch 之前已正确初始化 Rigidbody2d。
关于这个问题深入可以去看看MonoBehavior的生命周期
5.修完这个bug,那么我们会继续遇到bug
我们发现飞弹创建了,但是瞬间遇到主角消失了,所以这里我们需要用到图层,将主角和预制件飞弹放在不同的图层,同时在 Edit➡Project Setting➡physical 2D中取消这两个图层的勾选(即为不碰撞)
这片文章所使用的素材来自unity商店Ruby’s adventure
链接unity官网