当前位置: 首页 > article >正文

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界面,如下图所示:

最终效果如下:


http://www.kler.cn/a/470043.html

相关文章:

  • atrust异常导致ERR_NETWORK_CHANGED
  • 51单片机——步进电机模块
  • STM32 软件I2C读写
  • 生信技能69 - 使用deepvariant进行对基因组指定区域Calling SNPs/Indels
  • 前后端环境配置java/vue/maven/node.js/mysql
  • nodejs实现https://localhost在window系统自签名99年+授信
  • Vue——使用html2pdf插件,下载pdf文档到本地
  • VAxios
  • 深入了解 Nginx:进程、代理及用途解析
  • 苍穹外卖-day07(Spring Cache 购物车业务逻辑)
  • Flink 实时数据处理中的问题与解决方案
  • 【简博士统计学习方法】3. 统计学习方法的三要素
  • laravel 批量更新:‌INSERT ... ON DUPLICATE KEY UPDATE
  • 移动端自动化测试Appium-java
  • AI大模型的搭建和训练
  • Linux——修改文件夹的所属用户组和用户
  • 【A-Lab】部署手册:开源AAA解决方案 —FreeRADIUS
  • Git - 记录一次由于少输入了一个命令导致的更改丢失
  • HTML——72. 下拉列表分组(下拉列表中多选)
  • STM32 I2C通信外设
  • 用ResNet50+Qwen2-VL-2B-Instruct+LoRA模仿Diffusion-VLA的论文思路,在3090显卡上训练和测试成功
  • frameworks 之 Winscope 工具
  • 5. CSS引入方式
  • 安装和配置 Apache 及 PHP
  • 【蓝桥杯——物联网设计与开发】Part1:GPIO
  • AWS ELB基础知识