Unity点击游戏Steam定时物品掉落系统开发技术求助
1. Steamworks ItemDefinitions.json 结构
以下是符合Steam Inventory Schema规范的物品定义文件结构,适配你的普通/稀有掉落池需求:
{ "schema_version": "1.0", "itemdefcount": 4, "itemdefs": [ { "defid": 1001, "name": "普通道具A", "description": "2小时定时掉落的普通物品", "type": "item", "icon_url": "icons/common_a.png", "background_color": "#FFFFFF", "tradable": false, "marketable": false }, { "defid": 1002, "name": "普通道具B", "description": "2小时定时掉落的普通物品", "type": "item", "icon_url": "icons/common_b.png", "background_color": "#FFFFFF", "tradable": false, "marketable": false }, { "defid": 2001, "name": "稀有道具X", "description": "10小时定时掉落的稀有物品", "type": "item", "icon_url": "icons/rare_x.png", "background_color": "#FFD700", "tradable": false, "marketable": false }, { "defid": 2002, "name": "稀有道具Y", "description": "10小时定时掉落的稀有物品", "type": "item", "icon_url": "icons/rare_y.png", "background_color": "#FFD700", "tradable": false, "marketable": false } ] }
关键注意点:
defid:需与后续脚本中物品池的ID完全对应icon_url:填写你上传至Steam后台的图标文件路径tradable/marketable:点击游戏建议设为false,避免物品交易
2. 适配定时掉落逻辑的Unity脚本
该脚本实现了2小时普通掉落、10小时稀有掉落,且强制要求时段内至少点击一次才触发掉落,同时修复了原脚本中Steam回调处理的错误:
using UnityEngine; using Steamworks; using System.Collections.Generic; public class ItemDropManager : MonoBehaviour { // 时间间隔常量(秒) private const int NormalDropInterval = 7200; // 2小时 private const int RareDropInterval = 36000; // 10小时 // 物品池:对应ItemDefinitions.json中的defid private List<int> normalLootPool = new List<int> { 1001, 1002 }; private List<int> rareLootPool = new List<int> { 2001, 2002 }; // 状态记录 private bool hasClickedInCurrentSession = false; private const string LastNormalDropKey = "LastNormalDropTime"; private const string LastRareDropKey = "LastRareDropTime"; private const string LastClickTimeKey = "LastPlayerClickTime"; private Callback<SteamInventoryResultReady_t> _inventoryCallback; private void Awake() { // 注册Steam库存回调 _inventoryCallback = Callback<SteamInventoryResultReady_t>.Create(OnInventoryResultReady); } private void Start() { // 每3分钟检查一次掉落条件 InvokeRepeating(nameof(CheckDropConditions), 0f, 180f); // 初始化点击状态,处理玩家重启游戏的情况 CheckInitialClickState(); } // 供点击图片脚本调用:玩家点击时触发 public void OnPlayerClicked() { hasClickedInCurrentSession = true; long currentTime = GetCurrentUnixTime(); PlayerPrefs.SetInt(LastClickTimeKey, (int)currentTime); PlayerPrefs.Save(); } private void CheckDropConditions() { long currentTime = GetCurrentUnixTime(); long lastNormalDrop = PlayerPrefs.GetInt(LastNormalDropKey, 0); long lastRareDrop = PlayerPrefs.GetInt(LastRareDropKey, 0); // 检查普通掉落:时间达标 + 时段内有点击 if (currentTime - lastNormalDrop >= NormalDropInterval && HasValidClick(lastNormalDrop, currentTime)) { GrantRandomItem(normalLootPool); PlayerPrefs.SetInt(LastNormalDropKey, (int)currentTime); PlayerPrefs.Save(); hasClickedInCurrentSession = false; } // 检查稀有掉落:时间达标 + 时段内有点击 if (currentTime - lastRareDrop >= RareDropInterval && HasValidClick(lastRareDrop, currentTime)) { GrantRandomItem(rareLootPool); PlayerPrefs.SetInt(LastRareDropKey, (int)currentTime); PlayerPrefs.Save(); hasClickedInCurrentSession = false; } } // 验证是否在指定时段内有有效点击 private bool HasValidClick(long intervalStart, long currentTime) { long lastClick = PlayerPrefs.GetInt(LastClickTimeKey, 0); // 两种情况满足:当前会话内点击过,或上次点击在当前掉落时段内 return hasClickedInCurrentSession || (lastClick >= intervalStart && lastClick <= currentTime); } // 从物品池随机选一个,通过Steam发放 private void GrantRandomItem(List<int> pool) { if (pool.Count == 0) { Debug.LogError("Loot pool is empty!"); return; } int randomIndex = Random.Range(0, pool.Count); SteamItemDef_t itemDef = new SteamItemDef_t((uint)pool[randomIndex]); SteamInventoryResult_t resultHandle; bool success = SteamInventory.AddPromoItem(out resultHandle, itemDef); if (!success) Debug.LogError("Failed to request Steam item drop."); } // Steam库存操作结果回调 private void OnInventoryResultReady(SteamInventoryResultReady_t callback) { if (callback.m_result == EResult.k_EResultOK) { Debug.Log("Item granted successfully."); // 这里可以添加掉落提示、UI更新逻辑 } else { Debug.LogError($"Item grant failed: {callback.m_result}"); } // 必须释放结果句柄,避免内存泄漏 SteamInventory.DestroyResult(callback.m_handle); } // 初始化点击状态,处理玩家重启游戏的场景 private void CheckInitialClickState() { long currentTime = GetCurrentUnixTime(); long lastNormalDrop = PlayerPrefs.GetInt(LastNormalDropKey, 0); long lastRareDrop = PlayerPrefs.GetInt(LastRareDropKey, 0); long lastClick = PlayerPrefs.GetInt(LastClickTimeKey, 0); if (currentTime - lastNormalDrop >= NormalDropInterval && lastClick >= lastNormalDrop) { hasClickedInCurrentSession = true; } else if (currentTime - lastRareDrop >= RareDropInterval && lastClick >= lastRareDrop) { hasClickedInCurrentSession = true; } } // 获取当前UTC Unix时间戳(秒) private long GetCurrentUnixTime() { return (long)(System.DateTime.UtcNow - new System.DateTime(1970, 1, 1)).TotalSeconds; } private void OnDestroy() { _inventoryCallback?.Unregister(); } }
使用说明:
- 将脚本挂载到场景中的持久化GameObject(如GameManager)上
- 在你的点击图片脚本中,调用
ItemDropManager.Instance.OnPlayerClicked()(需实现单例或直接引用) - 替换物品池中的ID为你
ItemDefinitions.json里的实际defid - 确保Steam后台已开启Inventory功能,测试时需用Steam客户端登录测试账号
内容的提问来源于stack exchange,提问作者Zsolt Jambor
相关产品推荐
相关产品推荐

