## 在 GameMaker 中实现网格物品栏:逐步详解
基于网格的物品栏系统允许你在 ds_grid 中放置各种尺寸的物品,类似于 Deus Ex 或 S.T.A.L.K.E.R. 中的机制。要开始,首先创建一个带有精灵的玩家对象 oPlayer,以及一个没有默认精灵的容器对象 oContainer。准备物品精灵:spApple (32x32)、spWaterBottle (32x64)、spMysteriousPackage (64x64)、spItemError (32x32)。在房间 rTest 中,设置 Instances 和 UI 层,并添加 oPlayer 和 oContainer 的实例。
在起始房间 rInit 中创建一个持久化对象 oGameManager。在 Create 事件中:
global.inventoryDebugMode = true;
#macro InteractionDistance 100
global.ItemDB = {};
loadItemDefinitions();
room_goto(rTest);
这会初始化调试模式、交互距离常量以及物品数据库。
从 JSON 加载物品数据库
将文件 items.json 放入 datafiles 中。一件物品的结构包括 Name、Type、Width、Height、MaxStack、Sprite、Description。脚本 scItemDatabase 包含 loadItemDefinitions() 函数:
function loadItemDefinitions()
{
var _filename = "items.json";
if (!file_exists(_filename) && global.inventoryDebugMode)
{
show_debug_message("Fayl " + _filename + " not found!");
return;
}
var _buffer = buffer_load(_filename);
var _jsonString = buffer_read(_buffer, buffer_string);
buffer_delete(_buffer);
var _parsedData = json_parse(_jsonString);
var _itemIDs = struct_get_names(_parsedData);
for (var i = 0; i < array_length(_itemIDs); ++i)
{
var _id = _itemIDs[i];
var _item = _parsedData[$ _id];
if (variable_struct_exists(_item, "Sprite"))
{
_item.Sprite = asset_get_index(_item.Sprite);
if (_item.Sprite == -1)
{
_item.Sprite = spItemError;
if (global.inventoryDebugMode)
show_debug_message("Withprayt not found for predmeta " + _id);
}
}
global.ItemDB[$ _id] = _item;
}
if (global.inventoryDebugMode)
show_debug_message("Baza predmetov zagruzhena. Count: " + string(array_length(_itemIDs)));
}
函数 getItemData(_itemID) 返回物品结构体或 undefined。
- ds_struct 的优势:通过 [$ _id] 使用字符串键进行动态访问。
- 错误处理:如果精灵缺失,则回退到 spItemError。
- 访问器:对于 ds_grid 使用 [# i, j],对于 struct 使用 [$ key]。
网格中存储物品的基础知识
物品栏是一个 ds_grid,其中:
- 物品的主要单元格存储一个结构体 {itemID, quantity}。
- 辅助单元格存储 {refX, refY},包含主要单元格的坐标。
- 空单元格为 noone。
物品会根据数据库中的 Width/Height 占用空间,并根据 MaxStack 支持堆叠。
脚本 scInventory 从基本函数开始:
创建和销毁物品栏
function inventoryCreate(_width, _height)
{
var grid = ds_grid_create(_width, _height);
ds_grid_clear(grid, noone);
return grid;
}
function inventoryDestroy(_inventory)
{
if (ds_exists(_inventory, ds_type_grid))
{
ds_grid_destroy(_inventory);
return true;
}
else
{
return false;
}
}
网格检查和操作函数
接下来,实现空闲检查:
- inventoryIsEmpty(_inventory):扫描网格中是否存在 noone。
关键物品操作:
- 添加:根据 Width/Height 寻找空闲空间,放置主要单元格并在其他位置填充引用。
- 移动:检查重叠,更新 refX/refY。
- 移除:清除主要单元格和引用单元格。
交互检查:oPlayer 到 oContainer 的距离 < InteractionDistance。
显示物品栏 UI
在 UI 层,使用 draw_sprite_part 绘制网格中的物品。处理鼠标悬停高亮,通过 global.dragItem 实现拖放。
在 oPlayer 中添加 inventoryPlayer = inventoryCreate(10, 6);容器也同样处理。
关键要点
- 使用 ds_grid 进行高效的 2D 存储,支持 [#x,y] 访问器。
- 主要单元格仅存储 itemID/quantity;其余通过 getItemData 获取。
- 确保原子操作:在放置前检查所有单元格。
- DebugMode 简化加载和操作的调试。
- 持久化 oGameManager 确保 global ItemDB 状态。
— Editorial Team
暂无评论。