Unity弹珠游戏跨场景UI装备槽实例化与存档问题求助
弹珠台游戏装备配置与存档加载问题
问题概述
我开发了一款弹珠台(Pachinko)风格的游戏,支持玩家在进入游戏场景前,通过独立的装备配置(Loadout)场景创建自定义弹珠组合。目前遇到两个核心问题:
- 引用返回为空,导致进入游戏场景时触发空引用错误崩溃
- 存档加载(SaveLoad)系统无法记录每个装备槽中的物品
Game脚本(挂载在每个场景的空GameObject上)
using System.Collections.Generic; using UnityEngine; using UnityEngine.SceneManagement; public class Game : MonoBehaviour { [HideInInspector] public ulong highestScore = 0; [HideInInspector] public ulong totalCurrentCredits = 0; [HideInInspector] public string playerName = " "; [HideInInspector] public short ballSlotsUnlocked = 5; [HideInInspector] public string[] ballSetAssigned = new string[20]; [HideInInspector] public List<GameObject> slots; private void Awake() { for (int i = 0; i < slots.Count; i++) { try { ballSetAssigned[i] = slots[i].GetComponent<EquipmentSlot>().GetBallName(); Debug.Log(ballSetAssigned[i] + " From gameObject " + gameObject.name + " inside " + SceneManager.GetActiveScene().name); } catch { Debug.Log($"null from Game Awake at index {i}"); continue; } } } public void SaveGame() { SaveLoad.SaveGame(this); } public void LoadGame() { GameData gameData = SaveLoad.LoadGame(); highestScore = gameData.highestScore; totalCurrentCredits = gameData.totalCurrentCredits; playerName = gameData.playerName; ballSlotsUnlocked = gameData.ballSlotsUnlocked; for (int i = 0; i < ballSetAssigned.Length; i++) { if (ballSetAssigned[i] == null) { Debug.Log($"{gameObject.name} reporting in: ballSetAssigned at index {i} is null. moving on"); continue; } ballSetAssigned[i] = gameData.ballSetAssigned[i]; } } }
GameData脚本(用于存储可序列化数据)
using UnityEngine; [System.Serializable] public class GameData { public ulong highestScore = 0; public ulong totalCurrentCredits = 0; public string playerName = " "; public short ballSlotsUnlocked = 5; public string[] ballSetAssigned = new string[20]; public GameData(Game game) { highestScore = game.highestScore; totalCurrentCredits = game.totalCurrentCredits; playerName = game.playerName; ballSlotsUnlocked = game.ballSlotsUnlocked; for (int i = 0; i < ballSetAssigned.Length; i++) { try { ballSetAssigned[i] = game.ballSetAssigned[i]; } catch { Debug.Log($"null at {i}"); continue; } } } }
SaveLoad脚本(负责序列化和反序列化数据,改编自Brackeys实现)
using UnityEngine; using System.IO; using System.Runtime.Serialization.Formatters.Binary; public static class SaveLoad { public static void SaveGame(Game game) { BinaryFormatter bf = new BinaryFormatter(); string path = Application.persistentDataPath + "/SavedGameData.gd"; FileStream file = new FileStream(path, FileMode.Create); GameData gameData = new GameData(game); bf.Serialize(file, gameData); file.Close(); } public static GameData LoadGame() { string path = Application.persistentDataPath + "/SavedGameData.gd"; if (File.Exists(path)) { BinaryFormatter bf = new BinaryFormatter(); FileStream file = new FileStream(path, FileMode.Open); GameData gameData = (GameData)bf.Deserialize(file); file.Close(); return gameData; } else { Debug.LogError("Save file not found in " + path); return null; } } }
EquipmentSlot脚本(挂载在装备配置场景UI Canvas的每个slot GameObject及滚动视图Viewport上,处理拖拽逻辑)
using System; using UnityEngine; using UnityEngine.EventSystems; public class EquipmentSlot : MonoBehaviour, IDropHandler { public static string slotName; Game game; private void Awake() { if(!gameObject.name.Contains("View")) { game = GameObject.Find("GameDataManager").GetComponent<Game>(); game.slots.Add(gameObject); } } public void OnDrop(PointerEventData eventData) { if(transform.childCount == 0) { try { GameObject dropped = eventData.pointerDrag; DraggableItem draggableItem = dropped.GetComponent<DraggableItem>(); draggableItem.parentAfterDrag = transform; } catch { return; } } else if(transform.name == "Viewport") { try { GameObject dropped = eventData.pointerDrag; DraggableItem draggableItem = dropped.GetComponent<DraggableItem>(); Debug.Log(draggableItem.parentAfterDrag); Debug.Log(transform); draggableItem.parentAfterDrag = transform; } catch { return; } } } //之前尝试留下的代码,后续可能有用 int GetSlotIndex() { if(transform.name.Length == 13) { string indexCharacters = transform.name[transform.name.Length - 2].ToString() + transform.name[transform.name.Length - 1].ToString(); int index = Convert.ToInt32(indexCharacters); return index; } else if(transform.name.Length == 12) { int index = Convert.ToInt32(transform.name[transform.name.Length - 1].ToString()); return index; } return 0; } public string GetBallName() { if(transform.childCount > 0) { return transform.GetComponentInChildren<DraggableItem>().ReferenceBallPathByUIName(); } return null; } }
DraggableItem脚本(挂载在代表弹珠的UI图片上,处理拖拽交互)
using UnityEngine.EventSystems; using UnityEngine; using UnityEngine.UI; public class DraggableItem : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler { [HideInInspector] public Transform parentAfterDrag; public Image image; public string imageName; public void OnBeginDrag(PointerEventData eventData) { parentAfterDrag = transform.parent; transform.SetParent(transform.root); transform.SetAsLastSibling(); image.raycastTarget = false; imageName = gameObject.name; } public void OnDrag(PointerEventData eventData) { transform.position = Input.mousePosition; } public void OnEndDrag(PointerEventData eventData) { transform.SetParent(parentAfterDrag); image.raycastTarget = true; } public string ReferenceBallPathByUIName() { switch(transform.name) { case "BombBallUIElement": return "Prefabs/Balls/Inactive/InactiveBombBall.prefab"; } return null; } }
背景与理想逻辑
最初尝试序列化GameObject数组,但Unity不支持该操作,因此改为字符串类型存储弹珠资源路径。当前代码是在不破坏存档加载系统前提下的最终版本,期望实现的逻辑是:
- 通过EquipmentSlot脚本读取装备槽中的UI元素(如"BombBallUIElement"),将对应的弹珠资源路径添加至Game类的字符串数组
- 读取该数组时,若路径有效,则通过
Resources.Load(ballSetAssigned[index])加载并实例化弹珠
内容的提问来源于stack exchange,提问作者Kevin Will
相关产品推荐
相关产品推荐

