Unity3D仿星露谷物语开发23之拿起道具的动画
1、目标
当点击库存栏上可以carry的道具时,首先arms替换为carry状态,同时手上拿着被点击的道具。当再次点击同一个道具时,ams替换为idle状态,手上放下之前的道具。
这个最主要的是要学会使用AnimatorOverrideController类。
2、概念
AnimatorOverrideController类:用于控制动画其重写控制器的接口。
动画器重写控制器的用途是重写某个控制器的动画剪辑,从而为给定化身定制动画。在运行时基于相同的AnimatorController交换Animator.runtimeAnimatorController和AnimatorOverrideController不会重置状态机的当前状态。
(1)一般使用方法
定义变量:AnimatorOverrideController animatorOverrideController;
初始化变量:animatorOverrideController = new AnimatorOverrideController(animator.runtimeAnimatorController);
animator.runtimeAnimatorController = animatorOverrideController;
通过上面的赋值,后续对animatorOverrideController的修改将直接作用于animator.runtimeAnimatorController(运行态的动画控制器)。
运行中更新动画:animatorOverrideController["shot"] = weaponAnimationClip[weaponIndex];
(2)每帧更新多个动画剪辑
使用AnimatorOverrideController.ApplyOverrides方法,它能够将动画器剪辑绑定重新分配的数量减少到每次调用只有一个。
定义新老动画的映射:List<KeyValuePair<AnimationClip, AnimationClip>> clipOverrides;
初始化:animatorOverrideController.GetOverrides(clipOverrides);
运行中更新动画:animatorOverrideController.ApplyOverrides(clipOverrides);
其中ApplyOverrides函数:对该动画器重写控制器应用写列表
GetOverrides函数:获取该动画器重写控制器中当前定义的动画剪辑重写的列表。
3、思路
首先,创建SO_AnimationType类描述所有的动画剪辑。
其次,创建CharacterAttribute类定义想要替换的动画信息。
然后,基于AnimatorOverrideController类,根据CharacterAttribute类定义的动画信息,将Player下所有当前状态的动画替换为CharacterAttribute类定义的动画。
最后,如果是carry状态,给Player下的equippedItem对象赋以点击道具的图像。
4、创建可交互的动画的描述类
在Assets -> Scripts -> Animation下创建SO_AnimationType.cs脚本。
[CreateAssetMenu(fileName = "so_AnimationType", menuName = "Scriptable Objects/Animation/Animation Type")]
public class SO_AnimationType : ScriptableObject
{public AnimationClip animationClip; // a reference to the actual animation clip 动画剪辑public AnimationName animationName; // an enum of the animation name like "AnimationName.IdleUp" 动画名称public CharacterPartAnimator characterPart; // the gameobject name the Animator is on that controls these animation clips e.g. "arms" 动画位置对象public PartVariantColour partVariantColour; // to enable colour variations on a animation type to be specified e.g. "none", "bronze", "silver", "gold" 动画颜色变化public PartVariantType partVariantType; // the variant type to specify what variant this animation type refers to e.g. "none", "carry", "hoe", "pickaxe", "axe"..etc 动画类型
}
5、创建一个角色属性结构描述想要交换的动画
在Assets -> Scripts -> Animation 下创建CharacterAttribute.cs脚本,目标动画剪辑的属性结构。
[System.Serializable]
public struct CharacterAttribute
{public CharacterPartAnimator characterPart; // 示例: CharacterPartAnimator.arms 动画位置对象public PartVariantColour partVariantColour; // 示例: ParVariantColour.none 动画颜色变化public PartVariantType partVariantType; // 示例: PartVariantType.carry 动画类型public CharacterAttribute(CharacterPartAnimator characterPart, PartVariantColour partVariantColour, PartVariantType partVariantType) {this.characterPart = characterPart;this.partVariantColour = partVariantColour;this.partVariantType = partVariantType;}
}
6、创建Enum对象
在Assets -> Scripts -> Enums -> Enums.cs 中增加如下信息:
public enum AnimationName
{idleDown,idleUp,idleLeft,idleRight,walkUp,walkDown,walkLeft,walkRight,runUp,runDown,runLeft,runRight,useToolUp,useToolDown,useToolLeft,useToolRight,swingToolUp,swingToolDown,swingToolLeft,swingToolRight,liftToolUp,liftToolDown,liftToolLeft,liftToolRight,holdToolUp,holdToolDown,holdToolLeft,holdToolRight,pickUp,pickDown,pickLeft,pickRight,count
}public enum CharacterPartAnimator
{body,arms,hair,tool,hat,count
}public enum PartVariantColour
{none,count
}public enum PartVariantType
{none,carry,hoe,pickaxe,axe,scythe,wateringCan,count
}
7、创建动画重写控制器AnimationOverrides
流程:
1)创建2个动画剪辑的映射,第1个是 动画剪辑 到 动画描述类的映射,第2个是 动画表示名 到 动画描述类的映射。
2)通过配置的动画描述信息,分别填充上一步的两个映射表。
3)输入characterAttributesList,需要的动作的动画描述信息,比如partVariantType为carry的动画描述。
4)根据characterPart(比如arm)扫描当前角色下已有的动画剪辑,找到characterPart这个名称的动画剪辑为currentAnimator
5)找到characterPart对应controller下所有的动画剪辑,得到animationsList列表

我们找到了arms的animatorController,

再点击进入控制器的Run分类,

点击RunRight,其对应的动画剪辑为ArmsNoneNoneRunRight = characterPart + partVariantColour + partVariantType + animationName。
6)在animationsList列表中,依次遍历得到动画剪辑animationClip对应的动画描述类so_AnimationType(已有的动画)
7)然后根据characterAttribute(目标动画属性结构)中的信息+ 动画名称(比如RunRight)搜索目标动画剪辑swapSO_AnimationType
8)然后将animationClip 和 swapSO_AnimationType.animationClip的动画剪辑KeyValuePair<AnimationClip, AnimationClip>结构的animsKeyValuePairList中
9)通过aoc.ApplyOverrides让这些动画剪辑替换生效。
代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class AnimationOverrides : MonoBehaviour
{[SerializeField] private GameObject character = null;[SerializeField] private SO_AnimationType[] soAnimationTypeArray = null;private Dictionary<AnimationClip, SO_AnimationType> animationTypeDictionaryByAnimation;private Dictionary<string, SO_AnimationType> animationTypeDictionaryByCompositeAttributeKey;private void Start(){// Initialise animation type dictionary keyed by animation clipanimationTypeDictionaryByAnimation = new Dictionary<AnimationClip, SO_AnimationType>();foreach(SO_AnimationType item in soAnimationTypeArray){animationTypeDictionaryByAnimation.Add(item.animationClip, item);}// Initialise animation type dictionary keyed by stringanimationTypeDictionaryByCompositeAttributeKey = new Dictionary<string, SO_AnimationType>();foreach (SO_AnimationType item in soAnimationTypeArray) {string key = item.characterPart.ToString() + item.partVariantColour.ToString() + item.partVariantType.ToString() + item.animationName.ToString();animationTypeDictionaryByCompositeAttributeKey.Add(key, item);}}public void ApplyCharacterCustomisationParameters(List<CharacterAttribute> characterAttributesList){// Loop through all character attributes and set the animation override controller for eachforeach (CharacterAttribute characterAttribute in characterAttributesList){Animator currentAnimator = null;List<KeyValuePair<AnimationClip, AnimationClip>> animsKeyValuePairList = new List<KeyValuePair<AnimationClip, AnimationClip>>();string animatorSOAssetName = characterAttribute.characterPart.ToString();// Find animators in scene that match scriptable object animator typeAnimator[] animatorArray = character.GetComponentsInChildren<Animator>();foreach (Animator animator in animatorArray){if (animator.name == animatorSOAssetName){currentAnimator = animator;break;}}// Get base current animations for animatorAnimatorOverrideController aoc = new AnimatorOverrideController(currentAnimator.runtimeAnimatorController);List<AnimationClip> animationsList = new List<AnimationClip>(aoc.animationClips);foreach (AnimationClip animationClip in animationsList){// find animation in dictionarySO_AnimationType so_AnimationType;bool foundAnimation = animationTypeDictionaryByAnimation.TryGetValue(animationClip, out so_AnimationType);if (foundAnimation){string key = characterAttribute.characterPart.ToString() + characterAttribute.partVariantColour.ToString() +characterAttribute.partVariantType.ToString() + so_AnimationType.animationName.ToString(); // 目标动画stringSO_AnimationType swapSO_AnimationType;bool foundSwapAnimation = animationTypeDictionaryByCompositeAttributeKey.TryGetValue(key, out swapSO_AnimationType);if (foundSwapAnimation){AnimationClip swapAnimationClip = swapSO_AnimationType.animationClip;animsKeyValuePairList.Add(new KeyValuePair<AnimationClip, AnimationClip>(animationClip, swapAnimationClip));}}}// Apply animation updates to animation override controller and then update animator with the new controlleraoc.ApplyOverrides(animsKeyValuePairList);currentAnimator.runtimeAnimatorController = aoc;}}}
8、修改Player.cs脚本
创建相关的变量:
private AnimationOverrides animationOverrides; // 动画重写控制器
private List<CharacterAttribute> characterAttributeCustomisationList; // 目标动作列表[Tooltip("Should be populated in the prefab with the equippped item sprite renderer")]
[SerializeField] private SpriteRenderer equippedItemSpriteRenderer = null; // 武器的图片
在Awake中初始化相关变量:
// Initialise swappable character attributes
armsCharacterAttribute = new CharacterAttribute(CharacterPartAnimator.arms, PartVariantColour.none, PartVariantType.none);// Initialise character attribute list
characterAttributeCustomisationList = new List<CharacterAttribute>();
展示拿起物体/放下物体的动画:
public void ShowCarriedItem(int itemCode){ItemDetails itemDetails = InventoryManager.Instance.GetItemDetails(itemCode);if(itemDetails != null){equippedItemSpriteRenderer.sprite = itemDetails.itemSprite;equippedItemSpriteRenderer.color = new Color(1f, 1f, 1f, 1f);// Apply 'carry' character arms customisationarmsCharacterAttribute.partVariantType = PartVariantType.carry;characterAttributeCustomisationList.Clear();characterAttributeCustomisationList.Add(armsCharacterAttribute);animationOverrides.ApplyCharacterCustomisationParameters(characterAttributeCustomisationList);isCarrying = true;}}public void ClearCarriedItem(){equippedItemSpriteRenderer.sprite = null;equippedItemSpriteRenderer.color = new Color(1f, 1f, 1f, 0f);// Apply base character arms customisationarmsCharacterAttribute.partVariantType = PartVariantType.none;characterAttributeCustomisationList.Clear();characterAttributeCustomisationList.Add(armsCharacterAttribute);animationOverrides.ApplyCharacterCustomisationParameters(characterAttributeCustomisationList);isCarrying = false;}
完整的代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Player : SingletonMonobehaviour<Player>
{private AnimationOverrides animationOverrides; // 动画重写控制器private List<CharacterAttribute> characterAttributeCustomisationList; // 目标动作列表[Tooltip("Should be populated in the prefab with the equippped item sprite renderer")][SerializeField] private SpriteRenderer equippedItemSpriteRenderer = null; // 武器的图片// Player attributes that can be swappedprivate CharacterAttribute armsCharacterAttribute;private CharacterAttribute toolCharacterAttribute;private float xInput;private float yInput;private bool isWalking;private bool isRunning;private bool isIdle;private bool isCarrying = false;private ToolEffect toolEffect = ToolEffect.none;private bool isUsingToolRight;private bool isUsingToolLeft;private bool isUsingToolUp;private bool isUsingToolDown;private bool isLiftingToolRight;private bool isLiftingToolLeft;private bool isLiftingToolUp;private bool isLiftingToolDown;private bool isPickingRight;private bool isPickingLeft;private bool isPickingUp;private bool isPickingDown;private bool isSwingToolRight;private bool isSwingToolLeft;private bool isSwingToolUp;private bool isSwingToolDown;private Camera mainCamera;private Rigidbody2D rigidbody2D;private Direction playerDirection;private float movementSpeed;private bool _playerInputIsDisabled = false;public bool PlayerInputIsDisabled { get => _playerInputIsDisabled; set => _playerInputIsDisabled = value; }protected override void Awake(){base.Awake();rigidbody2D = GetComponent<Rigidbody2D>();animationOverrides = GetComponentInChildren<AnimationOverrides>();// Initialise swappable character attributesarmsCharacterAttribute = new CharacterAttribute(CharacterPartAnimator.arms, PartVariantColour.none, PartVariantType.none);// Initialise character attribute listcharacterAttributeCustomisationList = new List<CharacterAttribute>();mainCamera = Camera.main;}private void Update(){#region Player Inputif (!PlayerInputIsDisabled){ResetAnimationTrigger();PlayerMovementInput();PlayerWalkInput();// Send event to any listeners for player movement inputEventHandler.CallMovementEvent(xInput, yInput, isWalking, isRunning, isIdle, isCarrying, toolEffect,isUsingToolRight, isUsingToolLeft, isUsingToolUp, isUsingToolDown,isLiftingToolRight, isLiftingToolLeft, isLiftingToolUp, isLiftingToolDown,isPickingRight, isPickingLeft, isPickingUp, isPickingDown,isSwingToolRight, isSwingToolLeft, isSwingToolUp, isSwingToolDown,false, false, false, false);}#endregion Player Input}private void FixedUpdate(){PlayerMovement();}/// <summary>/// 展示拿东西的动画/// </summary>/// <param name="itemCode"></param>public void ShowCarriedItem(int itemCode){ItemDetails itemDetails = InventoryManager.Instance.GetItemDetails(itemCode);if(itemDetails != null){equippedItemSpriteRenderer.sprite = itemDetails.itemSprite;equippedItemSpriteRenderer.color = new Color(1f, 1f, 1f, 1f);// Apply 'carry' character arms customisationarmsCharacterAttribute.partVariantType = PartVariantType.carry;characterAttributeCustomisationList.Clear();characterAttributeCustomisationList.Add(armsCharacterAttribute);animationOverrides.ApplyCharacterCustomisationParameters(characterAttributeCustomisationList);isCarrying = true;}}public void ClearCarriedItem(){equippedItemSpriteRenderer.sprite = null;equippedItemSpriteRenderer.color = new Color(1f, 1f, 1f, 0f);// Apply base character arms customisationarmsCharacterAttribute.partVariantType = PartVariantType.none;characterAttributeCustomisationList.Clear();characterAttributeCustomisationList.Add(armsCharacterAttribute);animationOverrides.ApplyCharacterCustomisationParameters(characterAttributeCustomisationList);isCarrying = false;}private void PlayerMovement(){Vector2 move = new Vector2(xInput * movementSpeed * Time.deltaTime, yInput * movementSpeed * Time.deltaTime);rigidbody2D.MovePosition(rigidbody2D.position + move);}private void ResetAnimationTrigger(){toolEffect = ToolEffect.none;isUsingToolRight = false;isUsingToolLeft = false;isUsingToolUp = false;isUsingToolDown = false;isLiftingToolRight = false;isLiftingToolLeft = false;isLiftingToolUp = false;isLiftingToolDown = false;isPickingRight = false;isPickingLeft = false;isPickingUp = false;isPickingDown = false;isSwingToolRight = false;isSwingToolLeft = false;isSwingToolUp = false;isSwingToolDown = false;}private void PlayerMovementInput(){xInput = Input.GetAxisRaw("Horizontal");yInput = Input.GetAxisRaw("Vertical");// 斜着移动if (xInput != 0 && yInput != 0) {xInput = xInput * 0.71f;yInput = yInput * 0.71f;}// 在移动if (xInput != 0 || yInput != 0) {isRunning = true;isWalking = false;isIdle = false;movementSpeed = Settings.runningSpeed;// Capture player direction for save gameif (xInput < 0){playerDirection = Direction.left;}else if (xInput > 0){playerDirection = Direction.right;}else if (yInput < 0){playerDirection = Direction.down;}else{playerDirection = Direction.up;}}else if(xInput == 0 && yInput == 0){isRunning = false;isWalking = false;isIdle = true;}}// 按住Shift键移动为walkprivate void PlayerWalkInput(){if(Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift)){isRunning = false;isWalking = true;isIdle = false;movementSpeed = Settings.walkingSpeed;}else{isRunning = true;isWalking = false;isIdle = false;movementSpeed = Settings.runningSpeed;}}public Vector3 GetPlayerViewportPosition(){// Vector3 viewport position for player (0,0) viewport bottom left, (1,1) viewport top rightreturn mainCamera.WorldToViewportPoint(gameObject.transform.position);}public void DisablePlayerInputAndResetMovement(){DisablePlayerInpupt();ResetMovement();// Send event to any listeners for player movement inputEventHandler.CallMovementEvent(xInput, yInput, isWalking, isRunning, isIdle, isCarrying, toolEffect,isUsingToolRight, isUsingToolLeft, isUsingToolUp, isUsingToolDown,isLiftingToolRight, isLiftingToolLeft, isLiftingToolUp, isLiftingToolDown,isPickingRight, isPickingLeft, isPickingUp, isPickingDown,isSwingToolRight, isSwingToolLeft, isSwingToolUp, isSwingToolDown,false, false, false, false);}private void ResetMovement(){// Reset movementxInput = 0f;yInput = 0f;isRunning = false;isWalking = false;isIdle = true;}public void DisablePlayerInpupt(){PlayerInputIsDisabled = true;}public void EnablePlayerInput(){PlayerInputIsDisabled = false;}
}
9、修改UIInventorySlot.cs脚本
在SetSelectedItem中新增如下代码:
if (itemDetails.canBeCarried == true)
{// Show player carrying itemPlayer.Instance.ShowCarriedItem(itemDetails.itemCode);
}
else
{Player.Instance.ClearCarriedItem();
}
在ClearSelectedItem中新增如下代码:
// Clear player carrying item
Player.Instance.ClearCarriedItem();
完整的代码如下:
using UnityEngine;
using TMPro;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using System;public class UIInventorySlot : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler, IPointerEnterHandler, IPointerExitHandler, IPointerClickHandler
{private Camera mainCamera;private Transform parentItem; // 场景中的物体父类private GameObject draggedItem; // 被拖动的物体private Canvas parentCanvas;public Image inventorySlotHighlight;public Image inventorySlotImage;public TextMeshProUGUI textMeshProUGUI;[SerializeField] private UIInventoryBar inventoryBar = null;[SerializeField] private GameObject itemPrefab = null;[SerializeField] private int slotNumber = 0; // 插槽的序列号[SerializeField] private GameObject inventoryTextBoxPrefab = null;[HideInInspector] public ItemDetails itemDetails;[HideInInspector] public int itemQuantity;[HideInInspector] public bool isSelected = false;private void Awake(){parentCanvas = GetComponentInParent<Canvas>();}private void Start(){mainCamera = Camera.main;parentItem = GameObject.FindGameObjectWithTag(Tags.ItemsParentTransform).transform;}public void OnBeginDrag(PointerEventData eventData){if(itemDetails != null) {// Disable keyboard inputPlayer.Instance.DisablePlayerInputAndResetMovement();// Instatiate gameobject as dragged itemdraggedItem = Instantiate(inventoryBar.inventoryBarDraggedItem, inventoryBar.transform);// Get image for dragged itemImage draggedItemImage = draggedItem.GetComponentInChildren<Image>();draggedItemImage.sprite = inventorySlotImage.sprite;SetSelectedItem();}}public void OnDrag(PointerEventData eventData){// move game object as dragged itemif(!draggedItem != null){draggedItem.transform.position = Input.mousePosition;}}public void OnEndDrag(PointerEventData eventData){// Destroy game object as dragged itemif (draggedItem != null) {Destroy(draggedItem);// if drag ends over inventory bar, get item drag is over and swap thenif (eventData.pointerCurrentRaycast.gameObject != null && eventData.pointerCurrentRaycast.gameObject.GetComponent<UIInventorySlot>() != null) {// get the slot number where the drag endedint toSlotNumber = eventData.pointerCurrentRaycast.gameObject.GetComponent<UIInventorySlot>().slotNumber;// Swap inventory items in inventory listInventoryManager.Instance.SwapInventoryItems(InventoryLocation.player, slotNumber, toSlotNumber);// Destroy inventory text boxDestroyInventoryTextBox();// Clear selected itemClearSelectedItem();}else{// else attemp to drop the item if it can be droppedif (itemDetails.canBeDropped){DropSelectedItemAtMousePosition();}}// Enable player inputPlayer.Instance.EnablePlayerInput();}}/// <summary>/// Drops the item(if selected) at the current mouse position. called by the DropItem event/// </summary>private void DropSelectedItemAtMousePosition(){if(itemDetails != null && isSelected){Vector3 worldPosition = mainCamera.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, -mainCamera.transform.position.z));// Create item from prefab at mouse positionGameObject itemGameObject = Instantiate(itemPrefab, worldPosition, Quaternion.identity, parentItem);Item item = itemGameObject.GetComponent<Item>();item.ItemCode = itemDetails.itemCode;// Remove item from player's inventoryInventoryManager.Instance.RemoveItem(InventoryLocation.player, item.ItemCode);// If no more of item then clear selectedif(InventoryManager.Instance.FindItemInInventory(InventoryLocation.player, item.ItemCode) == -1){ClearSelectedItem();}}}public void OnPointerEnter(PointerEventData eventData){// Populate text box with item detailsif(itemQuantity != 0){// Instantiate inventory text boxinventoryBar.inventoryTextBoxGameobject = Instantiate(inventoryTextBoxPrefab, transform.position, Quaternion.identity);inventoryBar.inventoryTextBoxGameobject.transform.SetParent(parentCanvas.transform, false);UIInventoryTextBox inventoryTextBox = inventoryBar.inventoryTextBoxGameobject.GetComponent<UIInventoryTextBox>();// Set item type descriptionstring itemTypeDescription = InventoryManager.Instance.GetItemTypeDescription(itemDetails.itemType);// Populate text boxinventoryTextBox.SetTextboxText(itemDetails.itemDescription, itemTypeDescription, "", itemDetails.itemLongDescription, "", "");// Set text box position according to inventory bar positionif (inventoryBar.IsInventoryBarPositionBottom){inventoryBar.inventoryTextBoxGameobject.GetComponent<RectTransform>().pivot = new Vector2(0.5f, 0f);inventoryBar.inventoryTextBoxGameobject.transform.position = new Vector3(transform.position.x, transform.position.y + 50f, transform.position.z);}else{inventoryBar.inventoryTextBoxGameobject.GetComponent<RectTransform>().pivot = new Vector2(0.5f, 1f);inventoryBar.inventoryTextBoxGameobject.transform.position = new Vector3(transform.position.x, transform.position.y - 50f, transform.position.z);}}}public void OnPointerExit(PointerEventData eventData){DestroyInventoryTextBox();}private void DestroyInventoryTextBox(){if (inventoryBar.inventoryTextBoxGameobject != null) {Destroy(inventoryBar.inventoryTextBoxGameobject);}}public void OnPointerClick(PointerEventData eventData){// if left clickif (eventData.button == PointerEventData.InputButton.Left){// if inventory slot currently selected then deselectif (isSelected == true){ClearSelectedItem();}else // 未被选中且有东西则显示选中的效果{if(itemQuantity > 0){SetSelectedItem();}}}}/// <summary>/// Set this inventory slot item to be selected/// </summary>private void SetSelectedItem(){// Clear currently highlighted itemsinventoryBar.ClearHighlightOnInventorySlots();// Highlight item on inventory barisSelected = true;// Set highlighted inventory slotsinventoryBar.SetHighlightedInventorySlots();// Set item selected in inventoryInventoryManager.Instance.SetSelectedInventoryItem(InventoryLocation.player, itemDetails.itemCode);if (itemDetails.canBeCarried == true){// Show player carrying itemPlayer.Instance.ShowCarriedItem(itemDetails.itemCode);}else {Player.Instance.ClearCarriedItem();}}private void ClearSelectedItem(){// Clear currently highlighted iteminventoryBar.ClearHighlightOnInventorySlots();isSelected = false;// set no item selected in inventoryInventoryManager.Instance.ClearSelectedInventoryItem(InventoryLocation.player);// Clear player carrying itemPlayer.Instance.ClearCarriedItem();}
}
10、创建对象/资源
(1)创建动画描述资源
在Assets -> Scriptable Object Assets下创建Animation目录。
在其下再新建Animation Types的目录,在其下再创建arms的目录,在其下再创建NoneCarry(partVariantColour=None, partVariantType=Carry)和NoneNone的目录。

新建动画描述示例:


创建的内容罗列如下:其中NoneCarryxxx的放在NoneCarry目录下,NoneNonexxx的放在NoneNone目录下。
| Name | Animation Clip | Animation Name | Character Part | Part Variant Colour | Part Variant Type |
| ArmsNoneCarryIdleDown | ArmsNoneCarryIdleDown | Idle Down | Arms | None | Carry |
| ArmsNoneCarryIdleLeft | ArmsNoneCarryIdleLeft | Idle Left | Arms | None | Carry |
| ArmsNoneCarryIdleRight | ArmsNoneCarryIdleRight | Idle Right | Arms | None | Carry |
| ArmsNoneCarryIdleUp | ArmsNoneCarryIdleUp | Idle Up | Arms | None | Carry |
| ArmsNoneCarryRunDown | ArmsNoneCarryRunDown | Run Down | Arms | None | Carry |
| ArmsNoneCarryRunLeft | ArmsNoneCarryRunLeft | Run Left | Arms | None | Carry |
| ArmsNoneCarryRunRight | ArmsNoneCarryRunRight | Run Right | Arms | None | Carry |
| ArmsNoneCarryRunUp | ArmsNoneCarryRunUp | Run Up | Arms | None | Carry |
| ArmsNoneCarryWalkDown | ArmsNoneCarryWalkDown | Walk Down | Arms | None | Carry |
| ArmsNoneCarryWalkLeft | ArmsNoneCarryWalkLeft | Walk Left | Arms | None | Carry |
| ArmsNoneCarryWalkRight | ArmsNoneCarryWalkRight | Walk Right | Arms | None | Carry |
| ArmsNoneCarryWalkUp | ArmsNoneCarryWalkUp | Walk Up | Arms | None | Carry |
| ArmsNoneNoneIdleDown | ArmsNoneNoneIdleDown | Idle Down | Arms | None | None |
| ArmsNoneNoneIdleLeft | ArmsNoneNoneIdleLeft | Idle Left | Arms | None | None |
| ArmsNoneNoneIdleRight | ArmsNoneNoneIdleRight | Idle Right | Arms | None | None |
| ArmsNoneNoneIdleUp | ArmsNoneNoneIdleUp | Idle Up | Arms | None | None |
| ArmsNoneNoneRunDown | ArmsNoneNoneRunDown | Run Down | Arms | None | None |
| ArmsNoneNoneRunLeft | ArmsNoneNoneRunLeft | Run Left | Arms | None | None |
| ArmsNoneNoneRunRight | ArmsNoneNoneRunRight | Run Right | Arms | None | None |
| ArmsNoneNoneRunUp | ArmsNoneNoneRunUp | Run Up | Arms | None | None |
| ArmsNoneNoneWalkDown | ArmsNoneNoneWalkDown | Walk Down | Arms | None | None |
| ArmsNoneNoneWalkLeft | ArmsNoneNoneWalkLeft | Walk Left | Arms | None | None |
| ArmsNoneNoneWalkRight | ArmsNoneNoneWalkRight | Walk Right | Arms | None | None |
| ArmsNoneNoneWalkUp | ArmsNoneNoneWalkUp | Walk Up | Arms | None | None |

(2)创建动画重写控制器物体
在Hierarchy -> Player下创建新物体命名为CharacterCustomiser。
添加Animation Overriders组件。

(3)修改equippedItem对象
修改position y 为1.5。

(4)Player对象设置equip对象

运行程序,效果如下:

相关文章:
Unity3D仿星露谷物语开发23之拿起道具的动画
1、目标 当点击库存栏上可以carry的道具时,首先arms替换为carry状态,同时手上拿着被点击的道具。当再次点击同一个道具时,ams替换为idle状态,手上放下之前的道具。 这个最主要的是要学会使用AnimatorOverrideController类。 2、…...
素描风格渲染
素描风格渲染(Hatching Style Rendering),是一种非真实感渲染(NPR),主要目的是使3D模型看起来像 手绘素描的视觉效果。这种风格的渲染常用于游戏、动画和电影中,用来创造一种独特的艺术风格 1、…...
STM32使用DSP库 Keil方式添加
文章目录 前言一、添加DSP库二、使能FPU及配置1. 使能FPU2. 增加编译的宏3.增加头文件的检索路径三. 验证1. 源码中添加2.代码测试前言 添加DSP有两种方案,本文采用的是是Keil 中添加。 一、添加DSP库 在创建好的工程中添加DSP库:步骤如下: 步骤1:选择运行环境管理; 步…...
【机器学习实战入门项目】MNIST数字分类机器学习项目
Python 深度学习项目:手写数字识别 为了使机器更加智能,开发者们正在深入研究机器学习和深度学习技术。人类通过不断练习和重复来学习执行某项任务,从而记住如何完成这些任务。然后,大脑中的神经元会自动触发,他们能够…...
利用 LNMP 实现 WordPress 站点搭建
部署MySQL数据库 在主机192.168.138.139主机部署数据库服务 包安装数据库 apt-get install mysql-server 创建wordpress数据库和用户并授权 mysql> create database wordpress;#MySQL8.0要求指定插件 mysql> create user wordpress192.168.138.% identified with mys…...
模块化架构与微服务架构,哪种更适合桌面软件开发?
前言 在现代软件开发中,架构设计扮演着至关重要的角色。两种常见的架构设计方法是模块化架构与微服务架构。它们各自有独特的优势和适用场景,尤其在C#桌面软件开发领域,模块化架构往往更加具有实践性。本文将对这两种架构进行对比࿰…...
2025.1.17——1200
2025.1.17——1200 Q1. 1200 Jellyfish has n n n green apples with values a 1 , a 2 , … , a n a_1, a_2, \dots, a_n a1,a2,…,an and Gellyfish has m m m green apples with values b 1 , b 2 , … , b m b_1,b_2,\ldots,b_m b1,b2,…,bm. They will …...
vite工程化
Vite 通过直接利用浏览器的模块加载能力、将 CommonJS 模块转换为 ES 模块并缓存结果、基于原生 ES 模块的 HMR 以及对 TypeScript 的直接支持,提供了更快的开发体验和更高的开发效率。 1.直接利用浏览器模块加载功能 更快加载速度:不需要打包…...
Mysql常见问题处理集锦
Mysql常见问题处理集锦 root用户密码忘记,重置的操作(windows上的操作)MySQL报错:ERROR 1118 (42000): Row size too large. 或者 Row size too large (> 8126).场景:报错原因解决办法 详解行大小限制示例:内容来源于网…...
Android SystemUI——CarSystemBar添加到窗口(十)
上一篇文章我们看到了车载状态栏 CarSystemBar 视图的创建流程,这里我们继续分析将车载状态栏添加到 Windows 窗口中。 一、添加状态栏到窗口 前面我们已经分析了构建视图对象容器和构建视图对象内容,接下来我们继续分析 attachNavBarWindows() 方法将视…...
《重生到现代之从零开始的C++生活》—— 类和对象1
类 我嘞个豆,类可是太重要了,简直是重中之重 class为定义类的关键字,stack为类的名字,{}为类的主题 class stack {void add (int a,int b){return ab;}//类的方法,成员函数int _c;int _d;//类的属性,成…...
《FMambaIR:一种基于混合状态空间模型和频域的方法用于图像恢复》学习笔记
paper:(PDF) FMambaIR: A Hybrid State Space Model and Frequency Domain for Image Restoration 目录 摘要 一、引言 二、相关工作 1、图像恢复 2、频率学习 3、状态空间模型(SSM) 三、框架 1、基本知识 2、整体框架 3、F-Mamba…...
每日十题八股-2025年1月18日
1.服务器处理并发请求有哪几种方式? 2.讲一下io多路复用 3.select、poll、epoll 的区别是什么? 4.epoll 的 边缘触发和水平触发有什么区别? 5.redis,nginx,netty 是依赖什么做的这么高性能? 6.零拷贝是什么…...
海康威视摄像头RTSP使用nginx推流到服务器直播教程
思路: 之前2020年在本科的时候,由于项目的需求需要将海康威视的摄像头使用推流服务器到网页进行直播。这里将自己半个月琢磨出来的步骤给大家发一些。切勿转载!!!! 使用网络摄像头中的rtsp协议---------通…...
搭建一个基于Spring Boot的书籍学习平台
搭建一个基于Spring Boot的书籍学习平台可以涵盖多个功能模块,例如用户管理、书籍管理、学习进度跟踪、笔记管理、评论和评分等。以下是一个简化的步骤指南,帮助你快速搭建一个基础的书籍学习平台。 — 1. 项目初始化 使用 Spring Initializr 生成一个…...
Go 语言的slice是如何扩容的?
Go 语言中的 slice 是一种灵活、动态的视图,是对底层数组的抽象。当对 slice 进行追加元素等操作导致其长度超过容量时,就会发生扩容。 一、扩容的基本原理 当 slice 需要扩容时,Go 语言会根据当前的容量来确定新的容量。一般来说ÿ…...
Apache Hive--排序函数解析
在大数据处理与分析中,Apache Hive是一个至关重要的数据仓库工具。其丰富的函数库为数据处理提供了诸多便利,排序函数便是其中一类非常实用的工具。通过排序函数,我们能够在查询结果集中为每一行数据分配一个排名值,这对于数据分析…...
Java 接口安全指南
Java 接口安全指南 概述 在现代 Web 应用中,接口(API)是前后端交互的核心。然而,接口的安全性常常被忽视,导致数据泄露、未授权访问等安全问题。本文将详细介绍 Java 中如何保障接口安全,涵盖以下内容&am…...
合合信息名片全能王上架原生鸿蒙应用市场,成为首批数字名片类应用
长期以来,名片都是企业商务沟通的重要工具。随着企业数字化转型,相较于传统的纸质名片,数字名片对于企业成员拓展业务、获取商机、提升企业形象等方面发挥着重要作用。近期,合合信息旗下名片全能王正式上线原生鸿蒙应用市场&#…...
38.【3】CTFHUB web sql 报错注入
进入靶场 按照提示输入1 显示查询正确 既然是报错注入,先判断整形还是字符型注入 先输入1 and 11 再输入1 and 12 都显示查询正确,可知此为字符串型注入,不是数字型注入 然后就不会了 求助AI和其他wp 由以上2张搜索结果知updatexml是适用…...
IDEA运行Tomcat出现乱码问题解决汇总
最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…...
生成xcframework
打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式,可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...
k8s从入门到放弃之Ingress七层负载
k8s从入门到放弃之Ingress七层负载 在Kubernetes(简称K8s)中,Ingress是一个API对象,它允许你定义如何从集群外部访问集群内部的服务。Ingress可以提供负载均衡、SSL终结和基于名称的虚拟主机等功能。通过Ingress,你可…...
React Native在HarmonyOS 5.0阅读类应用开发中的实践
一、技术选型背景 随着HarmonyOS 5.0对Web兼容层的增强,React Native作为跨平台框架可通过重新编译ArkTS组件实现85%以上的代码复用率。阅读类应用具有UI复杂度低、数据流清晰的特点。 二、核心实现方案 1. 环境配置 (1)使用React Native…...
VTK如何让部分单位不可见
最近遇到一个需求,需要让一个vtkDataSet中的部分单元不可见,查阅了一些资料大概有以下几种方式 1.通过颜色映射表来进行,是最正规的做法 vtkNew<vtkLookupTable> lut; //值为0不显示,主要是最后一个参数,透明度…...
Android15默认授权浮窗权限
我们经常有那种需求,客户需要定制的apk集成在ROM中,并且默认授予其【显示在其他应用的上层】权限,也就是我们常说的浮窗权限,那么我们就可以通过以下方法在wms、ams等系统服务的systemReady()方法中调用即可实现预置应用默认授权浮…...
MySQL中【正则表达式】用法
MySQL 中正则表达式通过 REGEXP 或 RLIKE 操作符实现(两者等价),用于在 WHERE 子句中进行复杂的字符串模式匹配。以下是核心用法和示例: 一、基础语法 SELECT column_name FROM table_name WHERE column_name REGEXP pattern; …...
多种风格导航菜单 HTML 实现(附源码)
下面我将为您展示 6 种不同风格的导航菜单实现,每种都包含完整 HTML、CSS 和 JavaScript 代码。 1. 简约水平导航栏 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport&qu…...
pikachu靶场通关笔记22-1 SQL注入05-1-insert注入(报错法)
目录 一、SQL注入 二、insert注入 三、报错型注入 四、updatexml函数 五、源码审计 六、insert渗透实战 1、渗透准备 2、获取数据库名database 3、获取表名table 4、获取列名column 5、获取字段 本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关࿰…...
MySQL 8.0 事务全面讲解
以下是一个结合两次回答的 MySQL 8.0 事务全面讲解,涵盖了事务的核心概念、操作示例、失败回滚、隔离级别、事务性 DDL 和 XA 事务等内容,并修正了查看隔离级别的命令。 MySQL 8.0 事务全面讲解 一、事务的核心概念(ACID) 事务是…...
