【问题标题】:Issue loading saved booleans in Android build of Unity game在 Unity 游戏的 Android 版本中加载保存的布尔值问题
【发布时间】:2019-12-12 13:28:38
【问题描述】:

我完成了一个新游戏,其中我使用序列化数据实现了保存管理器,而不是使用 PlayerPrefs 获取所有玩家信息。一切似乎都运行良好,但由于某种原因,我的 Android 构建没有正确保存或加载一些布尔值。在 Unity 编辑器中,它似乎完全可以保存和加载这些布尔变量......其他一切(保存的整数、字符串)似乎在移动设备中运行良好。

基本上,我要保存的是玩家是否与特定 NPC 交谈并正确回答了问题。

到目前为止,我已经尝试了几件事,希望它可以解决:

  • 我尝试将数据保存为 List 以及 bool。最初我将此播放器信息的数据存储在列表中。当他们正确回答 NPC 时,NPC 将作为字符串添加到列表中。它也不起作用,所以我认为这是序列化列表的问题,所以我尝试了 bools。
  • 已将我的项目升级到最新版本的 Unity。对问题没有影响。
  • 我已经完全重建了几次,对我的代码进行了修改,并在 Android 上进行了测试,但均未成功。它仍在 Unity 编辑器中正常工作。

应该发生什么与(正在发生什么)

  • 对话应该增加,以便下一次对话有所不同(这有效,直到我进入主菜单并返回,或退出游戏并返回)。

  • 它应该触发一个动作,比如奖励玩家(我得到了奖励,但是由于它不记得我和谁说话,所以它让我在离开游戏并返回时收回奖励)。

我的代码: 对不起,有点厚,所以我试图在每个块之前总结一下目的。

Storyteller.cs - 附加到主游戏场景中的游戏对象。保存玩家正确回答 NPC 时的奖励方法。

using UnityEngine;

public class Storyteller : MonoBehaviour
{
    [SerializeField] RPGTalk rpgTalk; // I am using this asset to help with dialogues

    SaveManager saveManager;

    private void Start()
    {
        saveManager = GameObject.FindGameObjectWithTag("SaveManager").GetComponent<SaveManager>();
    }

    //NPC Methods
    public void RewardFromFlowerLady()
    {
        if (saveManager.myStats.flowerLadyGiftClaimed == true)
        {
            saveManager.myStats.coins += 700;
            saveManager.Save();
        }
    }

    public void RewardFromHippie()
    {
        if (saveManager.myStats.hippieGiftClaimed == true)
        {
            ReceiveMedicinalHerbsEvent(1.1f);
            saveManager.Save();
        }
    }
    public void PetFromTrashCan()
    {
        if (saveManager.myStats.trashCanGiftClaimed == true)
        {
            saveManager.myStats.trashcat = 1;
            saveManager.Save();
        }
    }
}

DialogueProgress.cs - 附加到主游戏场景中的 Player 对象。当玩家正确回答 NPC 时,它会将我保存文件中的布尔值设置为 True 并使用保存管理器中的 Save 方法保存游戏。

using UnityEngine;

public class DialogueProgress : MonoBehaviour
{
    SaveManager saveManager;

    private void Awake()
    {
        saveManager = GameObject.FindGameObjectWithTag("SaveManager").GetComponent<SaveManager>();
    }

    public void NPCSpokenAdder(string npcname)
    {
        switch (npcname){
            case "FlowerLady":
                saveManager.myStats.flowerLadyGiftClaimed = true;
                saveManager.Save();
                break;
            case "Hippie":
                saveManager.myStats.hippieGiftClaimed = true;
                saveManager.Save();
                break;
            case "TrashCan":
                saveManager.myStats.trashCanGiftClaimed = true;
                saveManager.Save();
                break;
        }
    }
}

QuestionAndChoiceID.cs - 这个脚本附加到我的三个 NPC 游戏对象上,在每个游戏对象上,我都在检查器中添加了一个问题 ID 和正确答案作为整数。

using UnityEngine;

public class QuestionAndChoiceID : MonoBehaviour
{
    public string questionID;
    public int correctchoiceID;
    RPGTalk rpgTalk;
    RPGTalkArea talkArea;
    SaveManager saveManager;
    DialogueProgress dialogueProgress;
    Storyteller storyTeller;

    void Start()
    {
        saveManager = GameObject.FindGameObjectWithTag("SaveManager").GetComponent<SaveManager>();
        rpgTalk = GameObject.FindGameObjectWithTag("RPGTalk").GetComponent<RPGTalk>();
        storyTeller = FindObjectOfType<Storyteller>();
        talkArea = this.gameObject.GetComponent<RPGTalkArea>();
        dialogueProgress = FindObjectOfType<DialogueProgress>();
        LoadCorrectResponses();
        rpgTalk.OnMadeChoice += OnMadeChoice;
    }

    private void OnDisable()
    {
        rpgTalk.OnMadeChoice -= OnMadeChoice;
    }

    private void OnMadeChoice(string question, int choice)
    {
        if (question == questionID && choice == correctchoiceID)
        {
            switch (this.gameObject.name)
            {
                case "FlowerLady":
                    dialogueProgress.NPCSpokenAdder(this.gameObject.name); 
                    IncrementDialogue(); 
                    storyTeller.RewardFromFlowerLady(); 
                    break;
                case "Hippie":
                    dialogueProgress.NPCSpokenAdder(this.gameObject.name);
                    IncrementDialogue();
                    storyTeller.RewardFromHippie();
                    break;
                case "TrashCan":
                    dialogueProgress.NPCSpokenAdder(this.gameObject.name);
                    IncrementDialogue();
                    storyTeller.PetFromTrashCan();
                    break;
            }
        }
    }

    private void IncrementDialogue()
    {
        if (saveManager.myStats.flowerLadyGiftClaimed && this.gameObject.name == "FlowerLady")
        {
            talkArea.lineToStart = this.gameObject.name + 1;
            talkArea.lineToBreak = this.gameObject.name + 1 + "_end";
            Debug.Log(this.gameObject.name + " dialogue incremented correctly;");
        }
        else if (saveManager.myStats.hippieGiftClaimed && this.gameObject.name == "Hippie")
        {
            talkArea.lineToStart = this.gameObject.name + 1;
            talkArea.lineToBreak = this.gameObject.name + 1 + "_end";
        }
        else if (saveManager.myStats.trashCanGiftClaimed && this.gameObject.name == "TrashCan")
        {
            talkArea.lineToStart = this.gameObject.name + 1;
            talkArea.lineToBreak = this.gameObject.name + 1 + "_end";
        }
    }

    private void LoadCorrectResponses()
    {
        if (saveManager.myStats.flowerLadyGiftClaimed)
        {
            if (this.gameObject.name == "FlowerLady")
            {
                talkArea.lineToStart = this.gameObject.name + 1;
                talkArea.lineToBreak = this.gameObject.name + 1 + "_end";
            }
        }
        else if (saveManager.myStats.hippieGiftClaimed)
        {
            if (this.gameObject.name == "Hippie")
            {
                talkArea.lineToStart = this.gameObject.name + 1;
                talkArea.lineToBreak = this.gameObject.name + 1 + "_end";
            }
        }
        else if (saveManager.myStats.trashCanGiftClaimed)
        {
                if (this.gameObject.name == "TrashCan")
            {
                talkArea.lineToStart = this.gameObject.name + 1;
                talkArea.lineToBreak = this.gameObject.name + 1 + "_end";
            }

        }
    }
}

这里也包括我的 SaveManager.cs 脚本:

using UnityEngine;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization;

public class SaveManager : MonoBehaviour
{
    public Stats myStats;

    private void Awake()
    {
        if (File.Exists(Application.persistentDataPath + "/Player.dat"))
        {
            Load();
        }

        InvokeRepeating("Save", 1, 15);
    }

    private void OnApplicationQuit() 
    {
       Save();
    }

    public void Save()
    {
        Debug.Log("Saving actual game.");

        FileStream file = new FileStream(Application.persistentDataPath + "/Player.dat", FileMode.OpenOrCreate); // Create a file or open a file to save to

        try
        {
            BinaryFormatter formatter = new BinaryFormatter(); // Binary formatter -- allows us to write data to a file
            formatter.Serialize(file, myStats); 
        }
        catch (SerializationException e) 
        {
            Debug.LogError("Issue serialising this data: " + e.Message);
        }
        finally
        {
            file.Close(); 
        }
    }

    public void Load()
    {
        FileStream file = new FileStream(Application.persistentDataPath + "/Player.dat", FileMode.Open);
        try
        {
            BinaryFormatter formatter = new BinaryFormatter();
            myStats = (Stats)formatter.Deserialize(file);
        }
        catch (SerializationException e)
        {
            Debug.LogError("Error deserialising this data: " + e.Message);
        }
        finally
        {
            file.Close();
        }
    }
}

来自 Stats.cs 的相关 sn-p 包含我要保存的所有数据:

using UnityEngine;

[System.Serializable]
public class Stats
{
    public bool flowerLadyGiftClaimed;
    public bool hippieGiftClaimed;
    public bool trashCanGiftClaimed;
}

希望有人可能遇到类似问题并可以提供帮助。我是序列化数据以进行保存和加载的新手,所以也许我缺少一些东西。由于我的其他数据保存和加载正常,所以我认为这是由于我上面的代码中的一些不正确的逻辑。只是不确定为什么它在 Unity 编辑器中运行良好!这是我游戏的最后一步,也是我完成的第一步,很高兴能解决它。

感谢阅读。

【问题讨论】:

  • 您在 Android 上遇到任何错误吗?
  • 一般提示:我不会使用对象字符串名称,但您应该引入enum,例如public enum NpcType { FlowerLady, Hippie, TrashCan } 并在 switch - case 中使用它。对命名错误会更有效,更健壮;)
  • 另外:你知道talkArea.lineToStart = this.gameObject.name + 1; 会导致FlowerLady1 对吗?或者你想在那里获得一个索引,例如使用类似talkArea.lines.IndexOf(name) + 1?
  • 您好 derHugo,感谢您的回复。我没有为测试设置调试的 Android 环境,所以我不知道。 (对这一切仍然很陌生,需要学习如何设置这种东西,我的下一个重点肯定是......)。 APK 运行,但我没有设置查看调试的方法。注意到枚举。我以前使用枚举来处理玩家状态等问题,但需要以其他方式合并它们——我会尝试的。是的,我希望 lineToStart 更改为 FlowerLady1,因为这是他们之前与她交谈时应该播放的“谈话”。

标签: c# android unity3d serialization save


【解决方案1】:

稍微重新组织我的代码后,我已经解决了保存和加载的问题。它与布尔值或列表无关。我能够恢复使用列表而不是布尔值。

我认为脚本中的执行顺序可能是把事情搞砸了。我更改了 Start 和 Awake 中发生的情况,还删除了一些多余的 if 检查和可能影响它的 if/else 语句的顺序。

我还删除了 DialogueProgress 脚本并将其内容添加到我的 Storyteller 类中。

最终代码是这样的:

Storyteller.cs

public class Storyteller : MonoBehaviour
{
    [SerializeField] RPGTalk rpgTalk;
    [SerializeField] UIManager uiMgr;

    [Header("NPCs with changing dialogues")]
    [SerializeField] GameObject flowerLady;
    [SerializeField] GameObject hippie;
    [SerializeField] GameObject trashcan;

    [SerializeField] List<string> tempNPCSpoken;

    [Header("Saved stats access")]
    SaveManager saveManager;

    [Header("Music")] 
    [SerializeField] AudioClip successSFX;

    public delegate void StorytellerDelegate(float statChange);  
    public static event StorytellerDelegate ReceiveMedicinalHerbsEvent;

    // ADDED THIS IN FROM DIALOGUE PROGRESS SCRIPT
    private void Awake()
    {
        saveManager = GameObject.FindGameObjectWithTag("SaveManager").GetComponent<SaveManager>();
        if (saveManager.myStats.NPCSpoken.Count == 0)
        {
            // There are no NPCs saved in the list.
            Debug.Log("AWAKE in DialogueProgress: NPCSpoken was empty. Making a new tempList.");
            tempNPCSpoken = new List<string>(); // Create a new list object that is empty.
        }
        else
        {   // There are NPCs that are in the saved list.
            Debug.Log("AWAKE in DialogueProgress: There are NPCs saved. NPCSpoken count is " + saveManager.myStats.NPCSpoken.Count);
            tempNPCSpoken = saveManager.myStats.NPCSpoken; // Save a local list equal to save one.
        }
    }

    // NPC ADDER
    public void NPCSpokenAdder(string npcname)
    {
        // Called from Question and ChoiceID script
        tempNPCSpoken.Add(npcname); // Add the NPC to local list.
        saveManager.myStats.NPCSpoken = tempNPCSpoken; // Set the save list equal to this list.
        saveManager.Save(); // Save the game.
        Debug.Log("NPCSpokenAdder ran: " + npcname + " was added to Saved List");
        Debug.Log("Current saved list: It contains this.gameObject now: " + saveManager.myStats.NPCSpoken.Contains(npcname));
    }


    //NPC Callbacks
    public void RewardFromFlowerLady()
    {
        saveManager.myStats.coins += 700;
        saveManager.Save();
    }

    public void RewardFromHippie()
    {
        ReceiveMedicinalHerbsEvent(1.1f);
        saveManager.Save();
    }
    public void PetFromTrashCan()
    {
        saveManager.myStats.trashcat = 1;
        saveManager.Save();
    }
  }

QuestionAndChoiceID.cs

public class QuestionAndChoiceID : MonoBehaviour
{
    public string questionID;
    public int correctchoiceID;
    RPGTalk rpgTalk;
    RPGTalkArea talkArea;
    SaveManager saveManager;
   // DialogueProgress dialogueProgress;
    Storyteller storyTeller;

    void Start()
    {
        saveManager = GameObject.FindGameObjectWithTag("SaveManager").GetComponent<SaveManager>();
        rpgTalk = GameObject.FindGameObjectWithTag("RPGTalk").GetComponent<RPGTalk>();
        talkArea = GetComponent<RPGTalkArea>();
        rpgTalk.OnMadeChoice += OnMadeChoice;
        //dialogueProgress = FindObjectOfType<DialogueProgress>();
        storyTeller = FindObjectOfType<Storyteller>();
        LoadCorrectResponses();
    }

    private void OnMadeChoice(string question, int choice)
    {
       // Debug.Log("OnMadeChoice: Player just made an RPGTalk choice.");

        if (question == questionID && choice == correctchoiceID) // If they answer NPC correctly
        {
            Debug.Log("OnMadeChoice: Player just made an RPGTalk choice that has actions attached.");
            switch (this.gameObject.name) // check which NPC it is by name
            {
                case "FlowerLady":
                    storyTeller.NPCSpokenAdder(this.gameObject.name); // NAME ADDED TO LIST, SAVED
                    IncrementDialogue(); // TALK LINES UPDATED
                    storyTeller.RewardFromFlowerLady(); // REWARD
                    break;
                case "Hippie":
                    storyTeller.NPCSpokenAdder(this.gameObject.name);
                    IncrementDialogue();
                    storyTeller.RewardFromHippie();
                    break;
                case "TrashCan":
                    storyTeller.NPCSpokenAdder(this.gameObject.name);
                    IncrementDialogue();
                    storyTeller.PetFromTrashCan();
                    break;
            }
        }
    }

    private void IncrementDialogue()
    {
        //if (saveManager.myStats.NPCSpoken.Contains(this.gameObject.name))
        //    {
                talkArea.lineToStart = this.gameObject.name + 1;
                talkArea.lineToBreak = this.gameObject.name + 1 + "_end";
            //}
    }

    private void LoadCorrectResponses()
    {
        if (SaveManager.saveExists) 

        {
            if (saveManager.myStats.NPCSpoken != null) // If NPC list is not null

            { 
                // If the NPC is the array when this is called, the dialogue on that NPC is incremented.
                if (saveManager.myStats.NPCSpoken.Contains(this.gameObject.name)) 
                {
                    talkArea.lineToStart = this.gameObject.name + 1; 
                    talkArea.lineToBreak = this.gameObject.name + 1 + "_end";
                }
                else
                {
                    Debug.Log("LoadCorrectResponses(): List does not contain NPC " + this.gameObject.name);
                }
            }
        }
    }
}

我当前的 Stats.cs

public class Stats
{
    [Header("NPC Talk Saves")]
    public List<string> NPCSpoken = new List<string>();
}

我会在这个问题解决后关闭它。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-04-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多