【unity实战】Unity中基于瓦片的网格库存系统——类似《逃离塔科夫》的库存系统(上)

简介: 【unity实战】Unity中基于瓦片的网格库存系统——类似《逃离塔科夫》的库存系统

最终效果


前言

在这一集中我将使用Unity制作基于瓦片的网格库存系统。 就像在《逃离塔科夫》、《暗黑破坏神》或《流放之路》等游戏中一样。

素材下载

https://assetstore.unity.com/packages/2d/gui/icons/gui-parts-159068

图片配置

配置图片为重复

不懂UI画布适配查看:【Unity小技巧】最简单的UI设置适配方案

修改UI画布适配

新增UI图片,类型改为平铺,默认图片是256的,太大了,所以我们选择缩小4倍,每单位像素为4,同时注意修改轴心在左上角

获取格子坐标

新增ItemGrid代码

public class ItemGrid : MonoBehaviour
{
    // 定义每个格子的宽度和高度
    const float tileSizeWidth = 256 / 4;
    const float tileSizeHeight = 256 / 4;
    // 计算在格子中的位置
    Vector2 positionOnTheGrid = new Vector2();
    Vector2Int tileGridPosition = new Vector2Int();
    RectTransform rectTransform;
    Canvas canvas;
    private void Start()
    {
        rectTransform = GetComponent<RectTransform>();
        canvas = FindObjectOfType<Canvas>();
    }
    private void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            // 获取当前鼠标位置在网格中的格子坐标,并打印到控制台
            Debug.Log(GetTileGridPosition(Input.mousePosition));
        }
    }
    
    // 根据鼠标位置计算在格子中的位置
    public Vector2Int GetTileGridPosition(Vector2 mousePosition)
    {
        // 计算鼠标位置相对于 RectTransform 的偏移量
        positionOnTheGrid.x = mousePosition.x - rectTransform.position.x;
        positionOnTheGrid.y = rectTransform.position.y - mousePosition.y;
        // 将偏移量转换为网格位置
        // 这里假设 tileSizeWidth 和 tileSizeHeight 是单个瓦片的宽度和高度
        // canvas.scaleFactor 是 Canvas 的缩放因子(通常用于 UI 适配不同分辨率)
        tileGridPosition.x = (int)(positionOnTheGrid.x / tileSizeWidth / canvas.scaleFactor);
        tileGridPosition.y = (int)(positionOnTheGrid.y / tileSizeHeight / canvas.scaleFactor);
        // 返回计算出的网格位置
        return tileGridPosition;
    }
}

挂载脚本

效果,点击格子打印位置

动态控制背包大小

修改ItemGrid

[SerializeField] int gridSizeWidth = 10;
[SerializeField] int gridSizeHeight = 10;
private void Start()
{
    rectTransform = GetComponent<RectTransform>();
    canvas = FindObjectOfType<Canvas>();
    Init(gridSizeWidth, gridSizeHeight);
}
void Init(int width, int height){
    Vector2 size = new Vector2(width * tileSizeWidth, height * tileSizeHeight);
    rectTransform.sizeDelta = size;
}

配置

效果

添加物品

配置物品预制体。修改尺寸和去掉光线投射目标

新增Item脚本,挂载在物品上

public class Item : MonoBehaviour {
    
}

动态添加测试物品,修改ItemGrid

Item[,] itemSlot;//存储物品位置信息
private void Start()
{
  itemSlot= new Item[gridSizeWidth, gridSizeHeight];
    rectTransform = GetComponent<RectTransform>();
    canvas = FindObjectOfType<Canvas>();
    Init(gridSizeWidth, gridSizeHeight);
    //动态添加测试物品
    Item item = Instantiate(itemPrefab).GetComponent<Item>();
    PlaceItem(item, 0, 0);
    item = Instantiate(itemPrefab).GetComponent<Item>();
    PlaceItem(item, 3, 2);
    item = Instantiate(itemPrefab).GetComponent<Item>();
    PlaceItem(item, 2, 4);
}
    
//按格子坐标添加物品
public void PlaceItem(Item item, int posX, int posY){
    itemSlot[posX, posY] = item;
    item.transform.SetParent(transform, false);
    Vector2 positon = new Vector2();
    positon.x = posX * tileSizeWidth + tileSizeWidth / 2;
    positon.y = -(posY * tileSizeHeight + tileSizeHeight / 2);
    item.transform.localPosition = positon;
}

配置

运行效果

移动物品

修改ItemGrid,按格子坐标获取物品

//按格子坐标获取物品
public Item PickUpItem(int x, int y){
    Item toReturn = itemSlot[x, y];
    itemSlot[x, y] = null;
    return toReturn;
}

新增InventoryController,实现物品交互功能

public class InventoryController : MonoBehaviour
{
    public ItemGrid selectedItemGrid;//操作的背包
    Item selectedItem;//选中物品
    private void Update()
    {
        if (selectedItemGrid == null) return;
        if (Input.GetMouseButtonDown(0))
        {
            // 获取当前鼠标位置在网格中的格子坐标,并打印到控制台
            Debug.Log(selectedItemGrid.GetTileGridPosition(Input.mousePosition));
            //获取物品
            Vector2Int tileGridPosition = selectedItemGrid.GetTileGridPosition(Input.mousePosition);
            if(selectedItem == null){
                selectedItem = selectedItemGrid.PickUpItem(tileGridPosition.x, tileGridPosition.y);
            }else{
                selectedItemGrid.PlaceItem(selectedItem, tileGridPosition.x, tileGridPosition.y);
                selectedItem = null;
            }
        }
    }
}

新增GridInteract,动态赋值背包数据

[RequireComponent(typeof(ItemGrid))]
public class GridInteract : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler
{
    private InventoryController inventoryController;
    private ItemGrid itemGrid;
    private void Awake()
    {
        inventoryController = FindObjectOfType<InventoryController>();
        itemGrid = GetComponent<ItemGrid>();
    }
    // 鼠标进入触发
    public void OnPointerEnter(PointerEventData eventData)
    {
        inventoryController.selectedItemGrid = itemGrid;
    }
    // 鼠标退出触发
    public void OnPointerExit(PointerEventData eventData)
    {
        inventoryController.selectedItemGrid = null;
    }
}

挂载

效果

物品跟随鼠标

修改InventoryController

private void Update()
{
    //物品跟随鼠标
    if(selectedItem) selectedItem.transform.position = Input.mousePosition;
  
  //...
}

效果

创建物品的容器,定义不同物品

新增ItemData

[CreateAssetMenu]
public class ItemData : ScriptableObject
{
    public int width = 1;
    public int height = 1;
    public Sprite itemIcon;
}

配置物品

修改Item

public class Item : MonoBehaviour
{
    public ItemData itemData;
    
    public void Set(ItemData itemData){
        this.itemData = itemData;
        GetComponent<Image>().sprite = itemData.itemIcon;
    }
}

修改InventoryController

[SerializeField] List<ItemData> items;
[SerializeField] GameObject itemPrefab;
Canvas canvas;
private void Start() {
    canvas = FindObjectOfType<Canvas>();
}
private void Update()
{
    //TODO: 方便测试,动态随机添加物品
    if (Input.GetKeyDown(KeyCode.Q))
    {
        CreateRandomItem();
    }
    //...
}
//随机添加物品
private void CreateRandomItem()
{
  if (selectedItem) return;
    Item item = Instantiate(itemPrefab).GetComponent<Item>();
    selectedItem = item;
    selectedItem.transform.SetParent(canvas.transform, false);
    int index = UnityEngine.Random.Range(0, items.Count);
    item.Set(items[index]);
}

配置

效果,按Q生成不同物品

修改物品尺寸

修改Item

public void Set(ItemData itemData){
    this.itemData = itemData;
    GetComponent<Image>().sprite = itemData.itemIcon;
    //修改物品尺寸
    Vector2 size = new Vector2();
    size.x = itemData.width * ItemGrid.tileSizeWidth;
    size.y = itemData.height * ItemGrid.tileSizeHeight;
    GetComponent<RectTransform>().sizeDelta = size;
}

效果

修复物品放置位置问题

修改ItemGrid

//按格子坐标添加物品
public void PlaceItem(Item item, int posX, int posY){
    itemSlot[posX, posY] = item;
    item.transform.SetParent(transform, false);
    Vector2 positon = new Vector2();
    positon.x = posX * tileSizeWidth + tileSizeWidth * item.itemData.width / 2;
    positon.y = -(posY * tileSizeHeight + tileSizeHeight * item.itemData.height / 2);
    item.transform.localPosition = positon;
}

效果

按物品尺寸占用对应大小的格子

修改ItemGrid

//按格子坐标添加物品
public void PlaceItem(Item item, int posX, int posY)
{
    item.transform.SetParent(transform, false);
    // 按物品尺寸占用对应大小的格子
    for (int ix = 0; ix < item.itemData.width; ix++){
        for (int iy = 0; iy < item.itemData.height; iy++){
            itemSlot[posX + ix, posY + iy] = item;
        }
    }
    item.onGridPositionX = posX;
    item.onGridPositionY = posY;
    Vector2 positon = new Vector2();
    positon.x = posX * tileSizeWidth + tileSizeWidth * item.itemData.width / 2;
    positon.y = -(posY * tileSizeHeight + tileSizeHeight * item.itemData.height / 2);
    item.transform.localPosition = positon;
}
//按格子坐标获取物品
public Item PickUpItem(int x, int y)
{
    Item toReturn = itemSlot[x, y];
    
  if(toReturn == null) return null;
  
    CleanGridReference(toReturn);
    
    return toReturn;
}
//按物品尺寸取消对应大小的格子的占用
void CleanGridReference(Item item){
    for (int ix = 0; ix < item.itemData.width; ix++)
    {
        for (int iy = 0; iy < item.itemData.height; iy++)
        {
            itemSlot[item.onGridPositionX + ix, item.onGridPositionY + iy] = null;
        }
    }
}

运行看是否正常

目录
相关文章
|
3月前
|
开发者 图形学 Java
揭秘Unity物理引擎核心技术:从刚体动力学到关节连接,全方位教你如何在虚拟世界中重现真实物理现象——含实战代码示例与详细解析
【8月更文挑战第31天】Unity物理引擎对于游戏开发至关重要,它能够模拟真实的物理效果,如刚体运动、碰撞检测及关节连接等。通过Rigidbody和Collider组件,开发者可以轻松实现物体间的互动与碰撞。本文通过具体代码示例介绍了如何使用Unity物理引擎实现物体运动、施加力、使用关节连接以及模拟弹簧效果等功能,帮助开发者提升游戏的真实感与沉浸感。
79 1
|
2月前
|
图形学 C++ C#
Unity插件开发全攻略:从零起步教你用C++扩展游戏功能,解锁Unity新玩法的详细步骤与实战技巧大公开
【8月更文挑战第31天】Unity 是一款功能强大的游戏开发引擎,支持多平台发布并拥有丰富的插件生态系统。本文介绍 Unity 插件开发基础,帮助读者从零开始编写自定义插件以扩展其功能。插件通常用 C++ 编写,通过 Mono C# 运行时调用,需在不同平台上编译。文中详细讲解了开发环境搭建、简单插件编写及在 Unity 中调用的方法,包括创建 C# 封装脚本和处理跨平台问题,助力开发者提升游戏开发效率。
208 0
|
2月前
|
图形学 开发者 UED
Unity游戏开发必备技巧:深度解析事件系统运用之道,从生命周期回调到自定义事件,打造高效逻辑与流畅交互的全方位指南
【8月更文挑战第31天】在游戏开发中,事件系统是连接游戏逻辑与用户交互的关键。Unity提供了多种机制处理事件,如MonoBehaviour生命周期回调、事件系统组件及自定义事件。本文介绍如何有效利用这些机制,包括创建自定义事件和使用Unity内置事件系统提升游戏体验。通过合理安排代码执行时机,如在Awake、Start等方法中初始化组件,以及使用委托和事件处理复杂逻辑,可以使游戏更加高效且逻辑清晰。掌握这些技巧有助于开发者更好地应对游戏开发挑战。
122 0
|
3月前
|
开发者 图形学 API
从零起步,深度揭秘:运用Unity引擎及网络编程技术,一步步搭建属于你的实时多人在线对战游戏平台——详尽指南与实战代码解析,带你轻松掌握网络化游戏开发的核心要领与最佳实践路径
【8月更文挑战第31天】构建实时多人对战平台是技术与创意的结合。本文使用成熟的Unity游戏开发引擎,从零开始指导读者搭建简单的实时对战平台。内容涵盖网络架构设计、Unity网络API应用及客户端与服务器通信。首先,创建新项目并选择适合多人游戏的模板,使用推荐的网络传输层。接着,定义基本玩法,如2D多人射击游戏,创建角色预制件并添加Rigidbody2D组件。然后,引入网络身份组件以同步对象状态。通过示例代码展示玩家控制逻辑,包括移动和发射子弹功能。最后,设置服务器端逻辑,处理客户端连接和断开。本文帮助读者掌握构建Unity多人对战平台的核心知识,为进一步开发打下基础。
123 0
|
3月前
|
开发者 图形学 C#
揭秘游戏沉浸感的秘密武器:深度解析Unity中的音频设计技巧,从背景音乐到动态音效,全面提升你的游戏氛围艺术——附实战代码示例与应用场景指导
【8月更文挑战第31天】音频设计在游戏开发中至关重要,不仅能增强沉浸感,还能传递信息,构建氛围。Unity作为跨平台游戏引擎,提供了丰富的音频处理功能,助力开发者轻松实现复杂音效。本文将探讨如何利用Unity的音频设计提升游戏氛围,并通过具体示例代码展示实现过程。例如,在恐怖游戏中,阴森的背景音乐和突然的脚步声能增加紧张感;在休闲游戏中,轻快的旋律则让玩家感到愉悦。
87 0
|
3月前
|
图形学 C# 开发者
Unity粒子系统全解析:从基础设置到高级编程技巧,教你轻松玩转绚丽多彩的视觉特效,打造震撼游戏画面的终极指南
【8月更文挑战第31天】粒子系统是Unity引擎的强大功能,可创建动态视觉效果,如火焰、爆炸等。本文介绍如何在Unity中使用粒子系统,并提供示例代码。首先创建粒子系统,然后调整Emission、Shape、Color over Lifetime等模块参数,实现所需效果。此外,还可通过C#脚本实现更复杂的粒子效果,增强游戏视觉冲击力和沉浸感。
188 0
|
3月前
|
开发者 图形学 前端开发
绝招放送:彻底解锁Unity UI系统奥秘,五大步骤教你如何缔造令人惊叹的沉浸式游戏体验,从Canvas到动画,一步一个脚印走向大师级UI设计
【8月更文挑战第31天】随着游戏开发技术的进步,UI成为提升游戏体验的关键。本文探讨如何利用Unity的UI系统创建美观且功能丰富的界面,包括Canvas、UI元素及Event System的使用,并通过具体示例代码展示按钮点击事件及淡入淡出动画的实现过程,助力开发者打造沉浸式的游戏体验。
95 0
|
3月前
|
图形学
Unity动画☀️Unity动画系统Bug集合
Unity动画☀️Unity动画系统Bug集合
|
3月前
|
图形学 C#
超实用!深度解析Unity引擎,手把手教你从零开始构建精美的2D平面冒险游戏,涵盖资源导入、角色控制与动画、碰撞检测等核心技巧,打造沉浸式游戏体验完全指南
【8月更文挑战第31天】本文是 Unity 2D 游戏开发的全面指南,手把手教你从零开始构建精美的平面冒险游戏。首先,通过 Unity Hub 创建 2D 项目并导入游戏资源。接着,编写 `PlayerController` 脚本来实现角色移动,并添加动画以增强视觉效果。最后,通过 Collider 2D 组件实现碰撞检测等游戏机制。每一步均展示 Unity 在 2D 游戏开发中的强大功能。
174 6
|
2月前
|
测试技术 C# 图形学
掌握Unity调试与测试的终极指南:从内置调试工具到自动化测试框架,全方位保障游戏品质不踩坑,打造流畅游戏体验的必备技能大揭秘!
【9月更文挑战第1天】在开发游戏时,Unity 引擎让创意变为现实。但软件开发中难免遇到 Bug,若不解决,将严重影响用户体验。调试与测试成为确保游戏质量的最后一道防线。本文介绍如何利用 Unity 的调试工具高效排查问题,并通过 Profiler 分析性能瓶颈。此外,Unity Test Framework 支持自动化测试,提高开发效率。结合单元测试与集成测试,确保游戏逻辑正确无误。对于在线游戏,还需进行压力测试以验证服务器稳定性。总之,调试与测试贯穿游戏开发全流程,确保最终作品既好玩又稳定。
103 4