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是适用…...

超短脉冲激光自聚焦效应
前言与目录 强激光引起自聚焦效应机理 超短脉冲激光在脆性材料内部加工时引起的自聚焦效应,这是一种非线性光学现象,主要涉及光学克尔效应和材料的非线性光学特性。 自聚焦效应可以产生局部的强光场,对材料产生非线性响应,可能…...

CTF show Web 红包题第六弹
提示 1.不是SQL注入 2.需要找关键源码 思路 进入页面发现是一个登录框,很难让人不联想到SQL注入,但提示都说了不是SQL注入,所以就不往这方面想了 先查看一下网页源码,发现一段JavaScript代码,有一个关键类ctfs…...

【JavaEE】-- HTTP
1. HTTP是什么? HTTP(全称为"超文本传输协议")是一种应用非常广泛的应用层协议,HTTP是基于TCP协议的一种应用层协议。 应用层协议:是计算机网络协议栈中最高层的协议,它定义了运行在不同主机上…...

盘古信息PCB行业解决方案:以全域场景重构,激活智造新未来
一、破局:PCB行业的时代之问 在数字经济蓬勃发展的浪潮中,PCB(印制电路板)作为 “电子产品之母”,其重要性愈发凸显。随着 5G、人工智能等新兴技术的加速渗透,PCB行业面临着前所未有的挑战与机遇。产品迭代…...
【Java学习笔记】Arrays类
Arrays 类 1. 导入包:import java.util.Arrays 2. 常用方法一览表 方法描述Arrays.toString()返回数组的字符串形式Arrays.sort()排序(自然排序和定制排序)Arrays.binarySearch()通过二分搜索法进行查找(前提:数组是…...

STM32F4基本定时器使用和原理详解
STM32F4基本定时器使用和原理详解 前言如何确定定时器挂载在哪条时钟线上配置及使用方法参数配置PrescalerCounter ModeCounter Periodauto-reload preloadTrigger Event Selection 中断配置生成的代码及使用方法初始化代码基本定时器触发DCA或者ADC的代码讲解中断代码定时启动…...
django filter 统计数量 按属性去重
在Django中,如果你想要根据某个属性对查询集进行去重并统计数量,你可以使用values()方法配合annotate()方法来实现。这里有两种常见的方法来完成这个需求: 方法1:使用annotate()和Count 假设你有一个模型Item,并且你想…...

第一篇:Agent2Agent (A2A) 协议——协作式人工智能的黎明
AI 领域的快速发展正在催生一个新时代,智能代理(agents)不再是孤立的个体,而是能够像一个数字团队一样协作。然而,当前 AI 生态系统的碎片化阻碍了这一愿景的实现,导致了“AI 巴别塔问题”——不同代理之间…...
css的定位(position)详解:相对定位 绝对定位 固定定位
在 CSS 中,元素的定位通过 position 属性控制,共有 5 种定位模式:static(静态定位)、relative(相对定位)、absolute(绝对定位)、fixed(固定定位)和…...

前端开发面试题总结-JavaScript篇(一)
文章目录 JavaScript高频问答一、作用域与闭包1.什么是闭包(Closure)?闭包有什么应用场景和潜在问题?2.解释 JavaScript 的作用域链(Scope Chain) 二、原型与继承3.原型链是什么?如何实现继承&a…...