Unity3D仿星露谷物语开发16之角色拾取道具

1、目标

当角色经过道具时会拾取道具放到库存列表中,此时道具消失并打印库存信息。

2、创建新的Enum

在Assets -> Scripts -> Enums -> Enum.cs中添加库存位置相关的信息。

cs 复制代码
public enum InventoryLocation
{
    player, // 在角色手中
    chest, // 在箱子里
    count // 枚举位置的计数, 值为2,只要把count放在最后一位,自动进行了计数
}

最后一个枚举值为count,此时count值自动为枚举个数值。

此时Enum.cs完整代码如下:

cs 复制代码
public enum ToolEffect
{
    none, 
    watering
}

public enum Direction
{
    up,
    down,
    left,
    right,
    none
}

public enum ItemType
{
    Seed,  // 种子
    Commodity, // 商品
    Watering_tool, // 浇水工具
    Hoeing_tool, // 锄头
    Chopping_tool, // 砍伐工具
    Breaking_tool, // 破碎工具
    Reaping_tool, // 收割工具
    Collecting_tool, // 收集工具
    Reapable_scenary, // 可达到场景
    Furniture, // 家具
    none,
    count // 计数,记录列表中东西的个数
}

public enum InventoryLocation
{
    player, // 在角色手中
    chest, // 在箱子里
    count // 枚举位置的计数, 值为2,只要把count放在最后一位,自动进行了计数
}

3、创建InventoryItem脚本

在Assets -> Scripts -> Inventory下创建InventoryItem.cs脚本。

它是一个struct结构体,记录每个item的code以及数量。

cs 复制代码
[System.Serializable]
public struct InventoryItem 
{
    public int itemCode;
    public int itemQuantity;
}

4、增加Settings中配置项

cs 复制代码
// Inventory
public static int playerInitialInventoryCapacity = 24;
public static int playerMaximumInventoryCapacity = 48;

此时Settings.cs完整代码为:

cs 复制代码
using UnityEngine;

public static class Settings
{
    // Obscuring Item Fader
    public const float fadeInSeconds = 0.25f;
    public const float fadeOutSeconds = 0.35f;
    public const float targetAlpha = 0.45f;

    // Player Movement
    public const float runningSpeed = 5.333f;
    public const float walkingSpeed = 2.666f;

    // Inventory
    public static int playerInitialInventoryCapacity = 24;
    public static int playerMaximumInventoryCapacity = 48;

    // Player Animation Parameters
    public static int xInput;
    public static int yInput;
    public static int isWalking;
    public static int isRunning;
    public static int toolEffect;
    public static int isUsingToolRight;
    public static int isUsingToolLeft;
    public static int isUsingToolUp;
    public static int isUsingToolDown;
    public static int isLiftingToolRight;
    public static int isLiftingToolLeft;
    public static int isLiftingToolUp;
    public static int isLiftingToolDown;
    public static int isSwingingToolRight;
    public static int isSwingingToolLeft;
    public static int isSwingingToolUp;
    public static int isSwingingToolDown;
    public static int isPickingRight;
    public static int isPickingLeft;
    public static int isPickingUp;
    public static int isPickingDown;

    // Shared Animation Parameters
    public static int idleUp;
    public static int idleDown;
    public static int idleLeft;
    public static int idleRight;


    // static constructor
    static Settings()
    {
        xInput = Animator.StringToHash("xInput");
        yInput = Animator.StringToHash("yInput");
        isWalking = Animator.StringToHash("isWalking");
        isRunning = Animator.StringToHash("isRunning");
        toolEffect = Animator.StringToHash("toolEffect");
        isUsingToolRight = Animator.StringToHash("isUsingToolRight");
        isUsingToolLeft = Animator.StringToHash("isUsingToolLeft");
        isUsingToolUp = Animator.StringToHash("isUsingToolUp");
        isUsingToolDown = Animator.StringToHash("isUsingToolDown");
        isLiftingToolRight = Animator.StringToHash("isLiftingToolRight");
        isLiftingToolLeft = Animator.StringToHash("isLiftingToolLeft");
        isLiftingToolUp = Animator.StringToHash("isLiftingToolUp");
        isLiftingToolDown = Animator.StringToHash("isLiftingToolDown");
        isSwingingToolRight = Animator.StringToHash("isSwingingToolRight");
        isSwingingToolLeft = Animator.StringToHash("isSwingingToolLeft");
        isSwingingToolUp = Animator.StringToHash("isSwingingToolUp");
        isSwingingToolDown = Animator.StringToHash("isSwingingToolDown");
        isPickingRight = Animator.StringToHash("isPickingRight");
        isPickingLeft = Animator.StringToHash("isPickingLeft");
        isPickingUp = Animator.StringToHash("isPickingUp");
        isPickingDown = Animator.StringToHash("isPickingDown");
        idleUp = Animator.StringToHash("idleUp");
        idleDown = Animator.StringToHash("idleDown");
        idleLeft = Animator.StringToHash("idleLeft");
        idleRight = Animator.StringToHash("idleRight");
    }

}

5、创建库存变更事件

在Assets -> Scripts -> Events -> EventHandler.cs中,添加库存变更事件。

cs 复制代码
 // Inventory Updated Event
 public static event Action<InventoryLocation, List<InventoryItem>> InventoryUpdatedEvent;

 public static void CallInventoryUpdatedEvent(InventoryLocation inventoryLocation, List<InventoryItem> inventoryList)
 {
     if(InventoryUpdatedEvent != null) // 有订阅者
     {
         InventoryUpdatedEvent(inventoryLocation, inventoryList);
     }
 }

此时EventHandler.cs完整代码如下:

cs 复制代码
using System;
using System.Collections.Generic;


public delegate void MovementDelegate(float inputX, float inputY, bool isWalking, bool isRunning, bool isIdle, bool isCarrying, 
    ToolEffect toolEffect, 
    bool isUsingToolRight, bool isUsingToolLeft, bool isUsingToolUp, bool isUsingToolDown, 
    bool isLiftingToolRight, bool isLiftingToolLeft, bool isLiftingToolUp, bool isLiftingToolDown, 
    bool isPickingRight, bool isPickingLeft, bool isPickingUp, bool isPickingDown, 
    bool isSwingToolRight, bool isSwingToolLeft, bool isSwingToolUp, bool isSwingToolDown, 
    bool idleUp, bool idleDown, bool idleLeft, bool idleRight
    );

public static class EventHandler
{
    // Inventory Updated Event
    public static event Action<InventoryLocation, List<InventoryItem>> InventoryUpdatedEvent;

    public static void CallInventoryUpdatedEvent(InventoryLocation inventoryLocation, List<InventoryItem> inventoryList)
    {
        if(InventoryUpdatedEvent != null) // 有订阅者
        {
            InventoryUpdatedEvent(inventoryLocation, inventoryList);
        }
    }



    // Movement Event
    public static event MovementDelegate MovementEvent;

    // Movement Event Call For Publishers
    public static void CallMovementEvent(float inputX, float inputY, bool isWalking, bool isRunning, bool isIdle, bool isCarrying,
    ToolEffect toolEffect,
    bool isUsingToolRight, bool isUsingToolLeft, bool isUsingToolUp, bool isUsingToolDown,
    bool isLiftingToolRight, bool isLiftingToolLeft, bool isLiftingToolUp, bool isLiftingToolDown,
    bool isPickingRight, bool isPickingLeft, bool isPickingUp, bool isPickingDown,
    bool isSwingToolRight, bool isSwingToolLeft, bool isSwingToolUp, bool isSwingToolDown,
    bool idleUp, bool idleDown, bool idleLeft, bool idleRight)
    {
        if (MovementEvent != null)
        {
            MovementEvent(inputX, inputY, isWalking, isRunning, isIdle, isCarrying,toolEffect,
                isUsingToolRight, isUsingToolLeft, isUsingToolUp, isUsingToolDown,
                isLiftingToolRight, isLiftingToolLeft, isLiftingToolUp, isLiftingToolDown,
                isPickingRight, isPickingLeft, isPickingUp, isPickingDown,
                isSwingToolRight, isSwingToolLeft, isSwingToolUp, isSwingToolDown,
                idleUp, idleDown, idleLeft, idleRight);
        }
    }

}

6、更新库存管理类

在Assets -> Scritpts -> Inventory -> InventoryManager.cs中。

首先,创建两个变量inventoryLists和inventoryListCapacityIntArray,前面记录了每个位置(比如玩家身上的,玩家箱子里的等等)的库存情况,后面记录了每个位置库存的item数量。

然后,在Awake中通过CreateInventoryLists初始化inventoryLists和inventoryListCapacityIntArray值。

接着,创建添加Item的方法AddItem(InventoryLocation inventoryLocation, Item item),即在哪个位置放什么Item。需要先搜索一遍该位置的库存,检查下是否已经存在,存在则返回库存清单的索引值。然后在哪个索引对应的InventoryItem更新下库存数。如果找不到这个item,那么直接在位置对应的库存清单末尾增加这个InventoryItem。

最后,再创建AddItem重载方法 AddItem(InventoryLocation inventoryLocation, Item item, GameObject gameObjectToDelete)。这个对应的逻辑就是:当角色拾取某个道具时,需要更新下对应的库存,同时将这个道具删除下。

cs 复制代码
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class InventoryManager : SingletonMonobehaviour<InventoryManager>
{
    private Dictionary<int, ItemDetails> itemDetailsDictionary;

    public List<InventoryItem>[] inventoryLists; // 每个位置的库存清单

    // 每个位置的库存数。 The index of the array is the inventory list(from the
    // InventoryLocation enum), and the value is the capacity of that inventory list
    [HideInInspector] public int[] inventoryListCapacityIntArray; 


    [SerializeField] private SO_ItemList itemList = null;

    protected override void Awake()
    {
        base.Awake();

        // Create Inventory lists
        CreateInventoryLists();

        // Create item details dictionary
        CreateItemDetailsDictionary();
    }

    private void CreateInventoryLists()
    {
        inventoryLists = new List<InventoryItem>[(int)InventoryLocation.count];

        for (int i = 0; i < (int)InventoryLocation.count; i++)
        {
            inventoryLists[i] = new List<InventoryItem>();
        }

        // initialize inventory list capacity array
        inventoryListCapacityIntArray = new int[(int)InventoryLocation.count];

        // initialize player inventory list capacity
        inventoryListCapacityIntArray[(int)InventoryLocation.player] = Settings.playerInitialInventoryCapacity;
    }

    /// <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>
    /// Add an item to the inventory list for the inventoryLocation and then destroy the gameObjectToDelete
    /// 角色拾取到物品后,物品需要消失掉
    /// </summary>
    /// <param name="inventoryLocation"></param>
    /// <param name="item"></param>
    /// <param name="gameObjectToDelete"></param>
    public void AddItem(InventoryLocation inventoryLocation, Item item, GameObject gameObjectToDelete)
    {
        AddItem(inventoryLocation, item);

        Destroy(gameObjectToDelete);
    }


    /// <summary>
    /// Add an item to the inventory list for the inventoryLocation
    /// </summary>
    /// <param name="inventoryLocation"></param>
    /// <param name="item"></param>
    public void AddItem(InventoryLocation inventoryLocation, Item item)
    {
        int itemCode = item.ItemCode;
        List<InventoryItem> inventoryList = inventoryLists[(int)inventoryLocation];

        // Check if inventory already contains the item
        int itemPosition = FindItemInInventory(inventoryLocation, itemCode);

        if(itemPosition != -1)
        {
            AddItemPosition(inventoryList, itemCode, itemPosition);
        }
        else
        {
            AddItemPosition(inventoryList, itemCode);
        }

        // Send event that inventory has been updated
        EventHandler.CallInventoryUpdatedEvent(inventoryLocation, inventoryLists[(int)inventoryLocation]);  
    }

    /// <summary>
    /// Add item to position in the inventory
    /// </summary>
    /// <param name="inventoryList"></param>
    /// <param name="itemCode"></param>
    /// <param name="position"></param>
    /// <exception cref="NotImplementedException"></exception>
    private void AddItemPosition(List<InventoryItem> inventoryList, int itemCode, int position)
    {
        InventoryItem inventoryItem = new InventoryItem();

        int quantity = inventoryList[position].itemQuantity + 1;
        inventoryItem.itemQuantity = quantity;
        inventoryItem.itemCode = itemCode;
        inventoryList[position] = inventoryItem;

        Debug.ClearDeveloperConsole();
        DebugPrintInventoryList(inventoryList);
    }

    private void DebugPrintInventoryList(List<InventoryItem> inventoryList)
    {
        foreach(InventoryItem inventoryItem in inventoryList)
        {
            Debug.Log("Item Description:" + InventoryManager.Instance.GetItemDetails(inventoryItem.itemCode).itemDescription + "    Item Quantity:" + inventoryItem.itemQuantity);
        }

        Debug.Log("*******************************************************************************");
    }

    /// <summary>
    /// Add item to the end of the inventory 
    /// </summary>
    /// <param name="inventoryList"></param>
    /// <param name="itemCode"></param>
    /// <exception cref="NotImplementedException"></exception>
    private void AddItemPosition(List<InventoryItem> inventoryList, int itemCode)
    {
        InventoryItem inventoryItem = new InventoryItem(); 

        inventoryItem.itemCode = itemCode;
        inventoryItem.itemQuantity = 1;
        inventoryList.Add(inventoryItem);

        DebugPrintInventoryList(inventoryList);
    }



    /// <summary>
    /// Find if an itemCode is already in the inventory. Returns the item position
    /// in the inventory list, or -1 if the item is not in the inventory
    /// </summary>
    /// <param name="inventoryLocation"></param>
    /// <param name="itemCode"></param>
    /// <returns></returns>
    /// <exception cref="NotImplementedException"></exception>
    private int FindItemInInventory(InventoryLocation inventoryLocation, int itemCode)
    {
        List<InventoryItem> inventoryList = inventoryLists[(int)inventoryLocation];

        for (int i = 0; i < inventoryList.Count; i++)
        {
            if(inventoryList[i].itemCode == itemCode)
            {
                return i;
            }
        }

        return -1;
    }




    /// <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;
        }
    }
}

该代码目前还有个遗憾:库存更新事件还没有被订阅,这个后续再进一步处理。

7、更新ItemPickup脚本

之前的代码时,用户经过道具时可以感知到,会打印道具的名称。

现在是:用户经过道具时,道具消失,同时用户库存清单的值被更新。

新增如下代码:

cs 复制代码
// If item can be picked up
if(itemDetails.canBePickedUp == true)
{
    // Add item to inventory
    InventoryManager.Instance.AddItem(InventoryLocation.player, item, collision.gameObject);
}

此时ItemPickup.cs完整代码是:

cs 复制代码
using UnityEngine;

public class ItemPickup : MonoBehaviour
{
    private void OnTriggerEnter2D(Collider2D collision)
    {
        Item item = collision.GetComponent<Item>();

        if(item != null)
        {
            // Get item details
            ItemDetails itemDetails = InventoryManager.Instance.GetItemDetails(item.ItemCode);

            // If item can be picked up
            if(itemDetails.canBePickedUp == true)
            {
                // Add item to inventory
                InventoryManager.Instance.AddItem(InventoryLocation.player, item, collision.gameObject);
            }
        }
    }
}

效果如下:

相关推荐
qq_4286396115 小时前
虚幻基础:角色受击
游戏引擎·虚幻
ellis197016 小时前
LuaC API知识点汇总
unity·lua
maki07717 小时前
虚幻版Pico大空间VR入门教程 05 —— 原点坐标和项目优化技巧整理
android·游戏引擎·vr·虚幻·pico·htc vive·大空间
2501_9291576817 小时前
NEOGEOCD模拟器+全游戏ISO+工具+特典美术+文档
游戏
da_vinci_x20 小时前
2D角色动画进阶:Spine网格变形与序列帧特效的混合工作流
游戏·设计模式·设计师·photoshop·spine·游戏策划·游戏美术
一线灵21 小时前
跨平台游戏引擎 Axmol-2.9.0 发布
游戏引擎
maki0771 天前
VR大空间资料 01 —— 常用VR框架对比
android·ue5·游戏引擎·vr·虚幻·pico
maki0772 天前
VR大空间资料 02 —— 常用Body IK对比
android·游戏引擎·vr·虚幻·pico·ik
雪下的新火2 天前
爆炸特效-Unity-04-shader&粒子系统
经验分享·笔记·unity·游戏引擎·shader·粒子系统
Predestination王瀞潞2 天前
UE4报错:无法编译项目
游戏引擎·虚幻·解决方案