Unity3D仿星露谷物语开发15之创建道具晃动效果
1、目标
当角色经过某些道具时,道具会发生轻微地摇晃,这样增加了道具与角色的互动,提供了良好的视觉效果。比如经过某棵植物时,植物会发生摇晃。
同时掌握VS调试代码的能力。
2、创建Nudge脚本
在Assets -> Scripts -> Item下创建ItemNudge脚本。代码如下:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ItemNudge : MonoBehaviour
{
private WaitForSeconds pause; // 旋转时的短暂停顿
private bool isAnimating = false; // 是否开始旋转
private void Awake()
{
pause = new WaitForSeconds(0.04f);
}
private void OnTriggerEnter2D(Collider2D collision)
{
if(isAnimating == false)
{
if (gameObject.transform.position.x < collision.gameObject.transform.position.x) // 角色在当前物体的右边
{
StartCoroutine(RotateAntiClock());
}
else
{
StartCoroutine(RotateClock());
}
}
}
private void OnTriggerExit2D(Collider2D collision)
{
if(!isAnimating == false)
{
if (gameObject.transform.position.x > collision.gameObject.transform.position.x) // 当前物体在角色的右边
{
StartCoroutine(RotateAntiClock());
}
else
{
StartCoroutine(RotateClock());
}
}
}
// 逆时针转动
private IEnumerator RotateAntiClock()
{
isAnimating = true;
for(int i = 0; i < 4; i++)
{
gameObject.transform.Rotate(0f, 0f, 2f);
yield return pause;
}
for(int i = 0; i < 5; i++)
{
gameObject.transform.Rotate(0f, 0f, -2f);
yield return pause;
}
gameObject.transform.Rotate(0f, 0f, 2f);
yield return pause;
isAnimating = false;
}
// 逆顺时针转动
private IEnumerator RotateClock()
{
isAnimating = true;
for (int i = 0; i < 4; i++)
{
gameObject.transform.Rotate(0f, 0f, -2f);
yield return pause;
}
for (int i = 0; i < 5; i++)
{
gameObject.transform.Rotate(0f, 0f, 2f);
yield return pause;
}
gameObject.transform.Rotate(0f, 0f, -2f);
yield return pause;
isAnimating = false;
}
}
3、优化Item脚本
在Assets -> Scripts -> Item -> Item.cs脚本中,
完善Init(int ItemCodeParam)方法如下:
ublic void Init(int ItemCodeParam)
{
if (ItemCodeParam != 0)
{
ItemCode = ItemCodeParam;
ItemDetails itemDetails = InventoryManager.Instance.GetItemDetails(ItemCode);
spriteRenderer.sprite = itemDetails.itemSprite;
// If item type is reapable then add nudgeable component
if (itemDetails.itemType == ItemType.Reapable_scenary)
{
gameObject.AddComponent<ItemNudge>();
}
}
}
此时Item.cs完整的代码如下:
using UnityEngine;
public class Item : MonoBehaviour
{
[ItemCodeDescription]
[SerializeField]
private int _itemCode;
private SpriteRenderer spriteRenderer;
public int ItemCode { get { return _itemCode; } set { _itemCode = value; } }
private void Awake()
{
spriteRenderer = GetComponentInChildren<SpriteRenderer>(); // SpriteRenderer组件在子对象中
}
private void Start()
{
if(ItemCode != 0)
{
Init(ItemCode);
}
}
public void Init(int ItemCodeParam)
{
if (ItemCodeParam != 0)
{
ItemCode = ItemCodeParam;
ItemDetails itemDetails = InventoryManager.Instance.GetItemDetails(ItemCode);
spriteRenderer.sprite = itemDetails.itemSprite;
// If item type is reapable then add nudgeable component
if (itemDetails.itemType == ItemType.Reapable_scenary)
{
gameObject.AddComponent<ItemNudge>();
}
}
}
}
4、创建需要摇晃的物体
(1)创建预制体
在Assets -> Prefabs -> Item下创建Reapable_Scenary目录。把Prefabs -> Commodity目录也拖到Assets -> Prefabs -> Item目录下。
基于Item预制体,通过Create Prefab Variant创建Grass1.
点击ItemSprite,选择Sprite图片
最后将Grass1预制体放到Reapable_Scenary目录下。
以同样的方式创建Grass2(ItemCode为10019),PricklyCactus(ItemCode为10021)。
(2)Scene中创建物体
将PricklyCactus预制体拖入Scene中运行程序,此时会报错:
NullReferenceException: Object reference not set to an instance of an object
InventoryManager.GetItemDetails (System.Int32 itemCode) (at Assets/Scripts/Inventory/InventoryManager.cs:38)
Item.Init (System.Int32 ItemCodeParam) (at Assets/Scripts/Item/Item.cs:31)
Item.Start () (at Assets/Scripts/Item/Item.cs:22)
意思是下面代码中的itemDetailsDictionary并无实例化。
(3)VS Debug
在问题处增加断点,同时选择“附加到Unity并播放”。
点击执行,此时Unity弹出如下提示:
点击“Enable debugging for all projects”
将鼠标放到itemDetailsDictionary变量旁边,发现它是null。
(4)为什么itemDetailsDictionary为空
在InventoryManager.cs中,itemDetailsDictionary是在InventoryManager单例类的Start中初始化的。
在Item.cs的Start中,通过InventoryManager.Instance也进行了一次初始化操作。
假如Item.cs中的InventoryManager先在Item的Start中第一次初始化然后立马使用GetItemDetails方法,此时itemDetailsDictionary还没在InventoryManager的Start中初始化,就会导致itemDetailsDictionary为空。
所以,需要把itemDetailsDictionary的初始化放到Awake中,修改如下:
此时InventoryManager.cs完整代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class InventoryManager : SingletonMonobehaviour<InventoryManager>
{
private Dictionary<int, ItemDetails> itemDetailsDictionary;
[SerializeField] private SO_ItemList itemList = null;
protected override void Awake()
{
base.Awake();
// Create item details dictionary
CreateItemDetailsDictionary();
}
/// <summary>
/// Populates the itemDetailsDictionary from the scriptable object items list
/// </summary>
private void CreateItemDetailsDictionary()
{
itemDetailsDictionary = new Dictionary<int, ItemDetails>();
foreach (ItemDetails itemDetails in itemList.itemDetails)
{
itemDetailsDictionary.Add(itemDetails.itemCode, itemDetails);
}
}
/// <summary>
/// Returns the itemDetails (from the SO_ItemList) for the itemCode, or null if the item doesn't exist
/// </summary>
/// <param name="itemCode"></param>
/// <returns></returns>
public ItemDetails GetItemDetails(int itemCode)
{
ItemDetails itemDetails;
if(itemDetailsDictionary.TryGetValue(itemCode, out itemDetails))
{
return itemDetails;
}
else
{
return null;
}
}
}
5、添加更多的道具
通过拖拽Prefab -> Item -> Reapable_Scenary中的3种预制体到Scene界面,如下图所示:
最终效果如下: