Unity做一个类似星露谷物语的游戏【3】

这个是目录

    • 前言
    • UI搭建
    • 捡起物品的逻辑
    • 实时显示物品更新

前言

Hello and welcome back 送大家个迟来的国庆节祝福 国庆节出去玩了鸽了一段时间,今天我们来讲一下主角物品栏的课程,主要内容是物品栏的建立,物品的捡起,捡起物品后ui栏显示相应的物品

UI搭建

我们先来建立一个空物体 命名Canvas(我这里这样做是为了后期方便管理不同的canvas 如果不考虑后续维护的话可以直接create一个canvas)然后创建子物体canvas 命名为MainCanvas,证明是主界面的canvas 内部属性设为下图就可以
在这里插入图片描述
然后我们创建子物体UIInventory,添加cs文件UIInventory.cs,image和grid layout group
我们先来写uiinventory的逻辑

	private RectTransform rectTransform;
    private bool isInventoryPositionIsButton = true;//工具UI是否在底端
    public bool IsInventoryPositionIsButton { get => isInventoryPositionIsButton; set => isInventoryPositionIsButton = value; }
    public GameObject inventoryDragItem;
    [SerializeField] private Sprite blanksprite = null;
    [SerializeField] private InventorySort[] inventorySort = null;
    [HideInInspector] public GameObject inventoryTextGameObject;
    private void Awake()
    {
        rectTransform = GetComponent<RectTransform>();
    }
    private void Update()
    {
        SwitchInventoryPosition();
    }
    //监控主角位置改变对应的位置
    private void SwitchInventoryPosition()
    {
        Vector3 playerPosition=Player.Instance.GetPlayerViewPortPosition();
        ///离开了下方并且ui在顶端,就把ui放还原到下方
        if (playerPosition.y > 0.3f && isInventoryPositionIsButton == false)
        {
            rectTransform.pivot = new Vector2(0.5f, 0);
            rectTransform.anchorMin = new Vector2(0.5f, 0);
            rectTransform.anchorMax = new Vector2(0.5f, 0);
            rectTransform.anchoredPosition = new Vector2(0, 0);
            isInventoryPositionIsButton = true;
        }
        if (playerPosition.y <0.3f && isInventoryPositionIsButton == true)
        {
            rectTransform.pivot = new Vector2(0.5f, 1);
            rectTransform.anchorMin = new Vector2(0.5f, 1);
            rectTransform.anchorMax = new Vector2(0.5f, 1);
            rectTransform.anchoredPosition = new Vector2(0, 0);
            isInventoryPositionIsButton = false;
        }
    }

这个代码目前的作用是监测主角的位置,如果主角的位置特别靠下的话 就把这个ui显示到最上端,防止ui遮住主角
inventoryDragItem是拖拽物体后显示的对应的物品,我们这里可以做一个预制体来让它根据拖拽物体的id自动生成相对应的物体,我们可以做一个canvas 子物体添加一个image,然后拖拽到prefab中
在这里插入图片描述
image选择sprite是inventoryBar ,这个是ui的背景图,
grid layout group的作用是对其子物体按照该component来布局,成品如下图
在这里插入图片描述
效果图如下
在这里插入图片描述

然后我们创建12个子物体来显示捡到的物品 我们先来创建一个image 添加代码inventorysort.cs,负责相应位置物体的逻辑,在子节点添加image 取名为InventoryHighLight,负责高光显示,添加子物体textmeshpro,负责显示物品的个数
在这里插入图片描述

代码inventorysort:

 	[HideInInspector] public Image inventorySlotHighLight;
    [HideInInspector] public Image inventorySlotImage;
    [HideInInspector] public TextMeshProUGUI textMeshProUGUI;
    [HideInInspector] public ItemDetial itemDetial;
    [HideInInspector] public int itemQuantity;

    private Camera mainCamera;
    private Transform parentItem;
    private GameObject draggedItem;
    public GameObject itemPrefab;
    private Canvas parentCanvas;
    [SerializeField] private UIInventory inventoryBar;
    [SerializeField] private GameObject inventoryTextBoxPrefab;
    public int sortNumber;

    [HideInInspector] public bool isSelect = false;

    private void Awake()
    {
        parentCanvas = gameObject.GetComponentInParent<Canvas>();
        inventorySlotHighLight = gameObject.transform.GetChild(0).GetComponent<Image>();
        textMeshProUGUI = gameObject.transform.GetChild(1).GetComponent<TextMeshProUGUI>();
        inventorySlotImage = gameObject.GetComponent<Image>();
        mainCamera = Camera.main;
        GameObject go = GameObject.Find("ItemParentTransform");
        parentItem = GameObject.FindGameObjectWithTag(Tags.ItemParentTransform).transform;
        inventoryBar = GameObject.Find("UIInventory").GetComponent<UIInventory>();
        sortNumber =int.Parse( gameObject.name.Replace("InventorySort", ""));
    }

我们来简单介绍一下这些成员变量
mainCamera就是主相机
inventorySlotHighLight 是高光的图片,如果预制体和我做的不一样不能用getchild的方法获取,可以拖拽
textMeshProUGUI 用来显示捡到物品的个数
inventorySlotImage 显示当前格子物品
inventoryBar就是它的整个ui物品栏即组件里有uiinventory的物品
sortnumber是物品栏编号 从0到11

parentItem 这个可有可无是我为了方便场景管理做的一个空物品 如果大家要用的话就建立一个tags脚本然后设置它的子成员一个string 然后在场景中建立一个空物体 把tag设置为string
在这里插入图片描述
在这里插入图片描述
这样生成的物体就会用一些逻辑就可以自动成为item的子物体了
这样我们的ui就算是建好了
在这里插入图片描述

捡起物品的逻辑

捡物品很简单的就给主角添加一个碰撞检测的脚本,如果这个物品可以被捡起来那么就添加到物品栏中

public class ItemPickUp : MonoBehaviour
{
    private void OnTriggerEnter2D(Collider2D collision)
    {
        Item item = collision.gameObject.GetComponent<Item>();
        if (item != null)
        {
            ItemDetial itemDetial = InventoryManager.Instance.GetItemDetial(item.ItemCode);
            if (itemDetial.canBePickUp)
                InventoryManager.Instance.AddItemTest(InventoryLocation.player, item, item.gameObject);
        }
    }
}
public struct InventoryItem
{
    public int itemCode;
    public int itemQuantity;
}

code是物品编码,itemquantity是物品个数

public class InventoryManager : SingleMono<InventoryManager>
{
	private Dictionary<int, ItemDetial> itemDetialDic;
    [SerializeField] private OS_ItemList itemDetialList = null;

    public List<InventoryItem>[] inventoryLists;
    [HideInInspector] public int[] inventoryListCapactiyIntArray;

    private int[] selectedInventoryItem;//存放item的code 2个成员 0为玩家身上的 1为箱子里的
	override protected void Awake()
    {
        base.Awake();
        CreateItemDetialDic();
        CreateInventoryLists();
        selectedInventoryItem = new int[(int)InventoryLocation.count];
        for(int i = 0; i < selectedInventoryItem.Length; ++i)
        {
            selectedInventoryItem[i] = -1;
        }
    }

    private void CreateItemDetialDic()
    {
        itemDetialDic = new Dictionary<int, ItemDetial>();
        foreach (ItemDetial itemDetial in itemDetialList.itemDetialList)
        {
            itemDetialDic.Add(itemDetial.itemCode, itemDetial);
        }
    }
    public ItemDetial GetItemDetial(int itemCode)
    {
        ItemDetial itemDetial;
        if (itemDetialDic.TryGetValue(itemCode, out itemDetial))
            return itemDetial;
        return null;
    }
      private void CreateInventoryLists()
    {
        inventoryLists = new List<InventoryItem>[(int)InventoryLocation.count];
        for (int i = 0; i < (int)InventoryLocation.count; ++i)
        {
            inventoryLists[i] = new List<InventoryItem>();
        }
        inventoryListCapactiyIntArray = new int[(int)InventoryLocation.count];
        inventoryListCapactiyIntArray[(int)InventoryLocation.player] = Settings.PlayerInitialInventoryCapacity;
    }
     public 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;
    }
        public void AddItem(InventoryLocation inventoryLocation, Item item)
    {
        int itemCode = item.ItemCode;
        List<InventoryItem> inventoryList = inventoryLists[(int)inventoryLocation];
        int itemPosition = FindItemInInventory(inventoryLocation, itemCode);
        if (itemPosition != -1)
        {
            AddItemAtPosition(inventoryList, itemCode, itemPosition);
        }
        else
        {
            AddItemAtPosition(inventoryList, itemCode);
        }
        EventHandler.CallInventoryUpdateEvent(inventoryLocation, inventoryLists[(int)inventoryLocation]);
    }
     private void AddItemAtPosition(List<InventoryItem> inventoryList, int itemCode)
    {
        InventoryItem inventoryItem = new InventoryItem();
        inventoryItem.itemCode = itemCode;
        inventoryItem.itemQuantity = 1;
        inventoryList.Add(inventoryItem);
        DebugPrintInventoryList(inventoryList);
    }

    private void AddItemAtPosition(List<InventoryItem> inventoryList, int itemCode, int itemPosition)
    {
        InventoryItem inventoryItem = new InventoryItem();
        int quantity = inventoryList[itemPosition].itemQuantity + 1;
        inventoryItem.itemQuantity = quantity;
        inventoryItem.itemCode = itemCode;
        inventoryList[itemPosition] = inventoryItem;
        Debug.ClearDeveloperConsole();
        DebugPrintInventoryList(inventoryList);
    }
    private void DebugPrintInventoryList(List<InventoryItem> list)
    {
        foreach (var item in list)
        {
            Debug.Log("拿到的是" + InventoryManager.Instance.GetItemDetial(item.itemCode).itemDescription + "个数是:" + item.itemQuantity);
        }
    }
    public void AddItemTest(InventoryLocation location,Item item,GameObject gameObject)
    {
        AddItem(location, item);
        Destroy(gameObject);
    }
     public void SetSelectedInventoryItem(InventoryLocation inventoryLocation,int itemCode)
    {
        selectedInventoryItem[(int)inventoryLocation] = itemCode;
    }
    public void ClearSelectedInventoryItem(InventoryLocation inventoryLocation)
    {
        selectedInventoryItem[(int)inventoryLocation] = -1;
    }
}

这个代码的逻辑比较复杂,简单来介绍一下
itemDetialDic是来存放全部的物品,物品来源是之前做的那个list,key为物品id,value为物品
itemDetialList是上个课程的工具列表
inventoryLists存放的位置,0为玩家1为箱子
CreateItemDetialDic函数的作用是把工具列表的信息全部存到字典中
CreateInventoryLists是初始化主角的物品栏和箱子
GetItemDetial是根据item的code来获取相应的item
FindItemInInventory是根据item的code来判断物品栏中是否有这个物品了,如过有就返回对应的位置,没有就返回-1
AddItemAtPosition是一个多态函数,如果FindItemInInventory返回的不是-1那么证明这个物品存在背包中,只需要在相应的位置把个数加1就可以,如果是-1说明物品在该背包中不存在需要在list中添加这个物品
SetSelectedInventoryItem是设置选中的物品id
ClearSelectedInventoryItem是清除当前选中的物品id
之前的EventHandler.cs需要添加监听数量变化的事件

 public static event Action<InventoryLocation, List<InventoryItem>> InventoryEvent;
    public static void CallInventoryUpdateEvent(InventoryLocation inventoryLocation,List<InventoryItem> list)
    {
        if (InventoryEvent != null)
            InventoryEvent(inventoryLocation, list);
    }

创建一个空物体,把InventoryManager添加到该物体上就可以实现捡起物品了,但是现在还不能更新ui,所以我们写了个DebugPrintInventoryList函数来输出当前拿到的物品

实时显示物品更新

我们打开刚才写的uiinventory.cs添加以下代码

 private void OnEnable()
    {
        EventHandler.InventoryEvent += InventoryUpdate;
    }
    private void OnDisable()
    {
        EventHandler.InventoryEvent -= InventoryUpdate;
    }
 private void ClearInventorySlots()
    {
        if (inventorySort.Length > 0)
        {
            for(int i = 0; i < inventorySort.Length; ++i)
            {
                inventorySort[i].inventorySlotImage.sprite = blanksprite;
                inventorySort[i].textMeshProUGUI.text = " ";
                inventorySort[i].itemDetial = null;
                inventorySort[i].itemQuantity = 0;
               // SetHightLightOnInventorySolt(i);
            }
        }
    }
private void InventoryUpdate(InventoryLocation inventoryLocation, List<InventoryItem> inventoryList)
    {
        if(inventoryLocation == InventoryLocation.player)
        {
            ClearInventorySlots();
            if (inventorySort.Length > 0 && inventoryList.Count > 0)
            {
                for(int i = 0; i < inventorySort.Length; ++i)
                {
                    if (i < inventoryList.Count)//库存里的物品拿完之前
                    {
                        int itemCode = inventoryList[i].itemCode;
                        ItemDetial itemDetial = InventoryManager.Instance.GetItemDetial(itemCode);
                        if (itemDetial != null)
                        {
                            inventorySort[i].inventorySlotImage.sprite = itemDetial.itemSprite;
                            inventorySort[i].itemQuantity = inventoryList[i].itemQuantity;
                            inventorySort[i].itemDetial = itemDetial;
                            inventorySort[i].textMeshProUGUI.text = inventoryList[i].itemQuantity.ToString();
                           // SetHightLightOnInventorySolt(i);
                        }
                    }
                    else
                    {
                        break;
                    }
                }
            }
        }
    }

到这里逻辑就很简单了 我们只需要先把全部的图片制空然后遍历inventoryList,把它对应的item的sprite和个数更新一下就可以了,这里全部清空再显示也是为了方便后续的拖拽换位之类的功能

这样我们再运行游戏就可以实现捡起物品物品更新在物品栏中了~
好啦,时间不早代码刚好,本来想今天把物品的移动,物品丢弃,详细物品展示这几个功能都写了的,奈何坐了一天的车,人困马乏啦,这个逻辑就在下期来写把~感谢您的观看

### 关于Unity3D开发类似星露游戏的教程和资源 #### 使用ScriptableObject管理物品数据 为了实现类似于《星露谷物》中的复杂道具系统,在Unity3D中可以利用`ScriptableObject`来高效管理和扩展各类物品属性。通过编写继承自`ScriptableObject`的C#脚本,能够创建可序列化的资产文件(.asset),这些文件可用于定义游戏中不同类型的工具、作物或其他交互对象[^3]。 ```csharp using UnityEngine; [CreateAssetMenu(fileName = "NewItem", menuName = "Game/Item")] public class Item : ScriptableObject { public string itemName; public Sprite icon; public int value; } ``` 此代码片段展示了如何声明一个新的`ScriptableObject`类型——`Item`,并为其添加基本字段用于表示名称、图标以及价值等信息。借助内置编辑器功能,开发者可以在Inspector视窗内直观地设置各项参数而无需修改源码。 #### 参考完整的项目案例 对于希望深入学习整个流程的新手而言,《制作100个Unity游戏》系列提供了详尽的教学指导,特别是其中有关2D横板卷轴动作类别的章节,虽然侧重点有所不同,但许多概念和技术同样适用于模拟经营风格的作品。该系列不仅涵盖了基础理论还分享了实际操作经验,并提供完整工程下载链接供读者参考实践[^1]。 #### 设计思路与规划建议 针对想要模仿特定作品如《星露谷物》的新晋创作者来说,遵循一套系统的创作方法论至关重要。这包括但不限于:确定核心玩法机制;构建世界背景故事框架;设计角色成长路径等内容策划工作。同时也要注意平衡难度曲线,确保玩家体验流畅自然[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值