系列文章目录
麦田物语第十四天
文章目录
前言
昨天制作了人物的移动(利用混合树),然后发现人物移动时的动画出问题了,所以今天先查找出问题的原因,我先把Arm和Body的动画重新做了一遍,结果发现还是相同的问题,最后问了大佬发现原来是混合树的Parameters设置错了,都给设置成InputX了,改正后就开始了今天的项目总结。
一、实现选中物品触发举起动画
本小节我们想要实现的功能是点击背包中的物品人物就会举起物品,并且学习了这个方法之后我们也可以实现人物使用工具,更换衣服等功能。
因为我们要举起物体,因此在Player下创建一个空物体HoldItem并添加Sprite Renderer组件,同时调整渲染方式(Sprite Sort Point)为Povit,渲染层级(Sorting Layer)为Instance,并更改层级顺序(Order In Layer)为2,避免头发出现在该物体的前面;接着调整该物体到合适位置,最后禁用其Sprite Renderer组件。
当我们举起物品时,只需要实现的是手臂动画的切换,所以我们通过代码来实现Animator Override的更换:
首先创建Scripts-> Player -> AnimatorOverride脚本,在这个脚本中需要拿到所有的 Animator 组件和 HoldItem的Sprite Renderer组件;并在Awake方法中获取Animators,我们就可以返回Unity进行拖拽赋值了。
因为我们想要实现手臂动画的切换,那么需要制作出来当玩家举起物体时的手臂动画,也上节一样,我们需要创建Animator Override Controller,并将Base Controller赋值给它,接着将需要的动画添加上去即可(这些动画都在文件夹中,也可自己制作)。
接下来我们就要实现切换动画的逻辑了。
首先我们需要实现的就是通过点击物品的类型实现相应的逻辑从而实现动画切换,那我们需要实现字典的方式是Player身体的各个部位的Animator匹配想要的部件和物品的ID等相关的信息。
接下来我们需要创建一些新的枚举类型,首先是PartType,表示这个物体的功能类型是什么;
PartType的枚举代码如下:
public enum PartType
{
None,Carry,Hoe,Break
}
接下来需要创建的是PartName,枚举出我们身体的各个位置;
PartName的枚举代码如下:
public enum PartName
{
Body,Hair,Arm,Tool
}
相信看到这个我们就能发现这些类型的作用了,我们根据物品的类型从而切换身体的不同部分的动画,然后我们来具体看看代码怎么写:
我们刚才所说的逻辑需要存储下来,因此返回DataCollection脚本,编写一个新的类型AnimatorType,记得需要序列化,在该类型中我们首先需要PartType类型的变量来存储物品功能,PartName类型的变量来存储身体部位,以及最重要的需要使用的Animator Override Controller,表名要切换那个Animator。
DataCollection脚本的AnimatorType类型代码如下:
[System.Serializable]
public class AnimatorType
{
public PartType partType;
public PartName partName;
public AnimatorOverrideController overrideController;
}
接着我们就可以回到AnimatorOverride脚本中定义AnimatorType列表,返回Unity对其进行赋值,大致如下:
我们还需要编写字典来存储身体的每个部位对应的Animator,将Animator的名字和Animator本身存入这个字典中。那么怎么生成这个字典呢,我们只需要遍历一遍animators数组,然后将其名字和本身存入字典即可。
当我们点击Slot_UI上的物品时,才会进行这个切换,在切换时我们需要传递物品的信息从而对HoldItem的Sprite Renderer组件赋值,所以我们仍然采取呼叫的方式来编写这个方法。(单击选中,再次单击取消选中)
EventHandler脚本的ItemSelectEvent代码如下
public static event Action<ItemDetails, bool> ItemSelectedEvent;
public static void CallItemSelectedEvent(ItemDetails itemDetails, bool isSelected)
{
ItemSelectedEvent?.Invoke(itemDetails, isSelected);
}
接下来我们在点选物品的方法OnPointerClick中发送这个呼叫,并且只有在背包中点选物品时才会发送(在商店中的物品我们无法使用)。
SlotUI的OnPointerClick方法如下:
public void OnPointerClick(PointerEventData eventData)
{
if (itemAmount == 0) return;
isSelected = !isSelected;
inventoryUI.UpdateSlotHightlight(slotIndex);
if (slotType == SlotType.Bag)
{
//通知物品被选中的状态和信息
EventHandler.CallItemSelectedEvent(itemDetails, isSelected);
}
}
接下来返回AnimatorOverride脚本中,编写OnEnable和OnDisable方法,即添加和取消了OnItemSelectedEvent方法,首先我们通过语法糖来实现当前物品类型(ItemDetails)对应的PartType的什么类型currentType。我们目前想要实现的是将商品和种子视为可举起的类型,那么就先只编写这两个,剩下的之后在写即可(当我们添加新的工具之后一定会有新的动画,此时我们就需要将这个语法糖补充完整)。然后我们编写新的方法SwitchAnimator通过currentType实现动画的切换,在SwitchAnimator方法中我们先要遍历AnimatorType列表,如果某一项的PartType刚好等于currentType,此时我们就要切换动画,那么我们怎么切换动画嘞?
还记得当时定义的字典嘛,我们可以使用AnimatorType列表中的PartName匹配到字典中的Animator组件,并切换该组件的AnimatorController为AnimatorType列表中的override Controller。
我们还需要调用这个方法,但是调用这个方法前我们还需要对是否选择了该物体的bool值进行判断,如果该物体没有被选择,将currentType赋值为PartType.None,并且将holdItem设置为未激活状态;如果该物体被选择的话,那么将ItemDetails的itemOnWorldSprite赋值给holdItem的Sprite Renderer,同时激活HoldItem即可。
AnimatorOverride脚本代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AnimatorOverride : MonoBehaviour
{
private Animator[] animators;
public SpriteRenderer holdItem;
[Header("各部分动画列表")]
public List<AnimatorType> animatorTypes;
private Dictionary<string, Animator> animatorNameDict = new Dictionary<string, Animator>();
private void Awake()
{
animators = GetComponentsInChildren<Animator>();
foreach (var anim in animators)
{
animatorNameDict.Add(anim.name, anim);
}
}
private void OnEnable()
{
EventHandler.ItemSelectedEvent += OnItemSelectedEvent;
}
private void OnDisable()
{
EventHandler.ItemSelectedEvent -= OnItemSelectedEvent;
}
private void OnItemSelectedEvent(ItemDetails itemDetails, bool isSelected)
{
//WORKFLOW:不同的工具返回不同的动画在这里补全
//语法糖
PartType currentType = itemDetails.itemType switch
{
ItemType.Seed => PartType.Carry,
ItemType.Commodity => PartType.Carry,
_=> PartType.None
};
if (isSelected == false)
{
currentType = PartType.None;
holdItem.enabled = false;
}
else
{
if (currentType == PartType.Carry)
{
holdItem.sprite = itemDetails.itemOnWorldSprite;
holdItem.enabled = true;
}
}
SwitchAnimator(currentType);
}
private void SwitchAnimator(PartType partType)
{
foreach(var item in animatorTypes)
{
if (item.partType == partType)
{
animatorNameDict[item.partName.ToString()].runtimeAnimatorController = item.overrideController;
}
}
}
}
二、绘制房子和可以被砍伐的树
本小节我们将我们的场景丰富一些。
首先先进行房子的绘制(绘制步骤我忘了,所以返回之前的文章重新复习了一遍)。过程如下:首先点击Tile Palette,然后创建新的Palette(TileMaps文件夹-> Palettes),接着将房子的图片拖入新建的Palette,这些图片存放在TileMap文件夹->Tiles中,然后选择除了房顶外的部分并且选择Active Tilemap为Ground Top,然后在地图中进行绘制,接着将房顶绘制在Front1层中,这样就会完全遮盖Player。
接着我们要为房子画上碰撞体,切换至Collision的Palette,然后选择Active Tilemap为Collision,并将01.Field场景中的Collision物体身上的Tilemap Renderer激活,这样我们绘制的碰撞体就可以在地图上看到了,方便绘制(我们绘制完成后关闭即可)。
我绘制的房子碰撞体如下
接着我们制作可以被砍倒的树,首先需在M_Studio->Art->Items->Tree01和Tree02,我们制作一个,另一个同理即可。
首先将Tree01_Bottom拖入场景,然后更改其渲染方式为Povit,更改层级为Instance,同理也将Tree01_Top也拖入场景,将其层级也改为Instance,同时层序为1,这样确保了树叶可以在树根上面显示,接着创建一个空物体改名为Tree01,然后将其位置与Buttom位置设置为相同的,并将Bottom和Top拖入作为其子物体,接着给该空物体添加Sorting Group组件,将其层级也设置为Instance,再添加Box Collider 2D,设置其大小,勾选Is Trigger,给其子物体都添加ItemFade脚本,最后在给Bottom添加Box Collider 2D并设置其大小。
最后将树拖拽至Prefab->Tree文件夹中作为预制体。
标签:麦田,动画,PartType,public,第十四天,物语,物品,我们,Animator From: https://blog.csdn.net/qq_52276934/article/details/140662953Tree的Box Collider 2D的大小设置如下: