I have created a simple scriptable object with a list, 1 button and something to the list, second load its into the text ui. When I test this on Unity, it works when I already build the app from the build settings, android on the other hand doesnt. Anyone know why, or does it even work on android?
Thanks!
2 main scripts:
https://drive.google.com/file/d/1QS9SjzEJ5BdIhu3PPpg-ePSqktm_6Um8/view?usp=sharing, https://drive.google.com/file/d/1T9iV_zK8NlvCI2jFkEEAafkrMFAsoFMa/view?usp=sharing
EDIT:
proof:
https://drive.google.com/file/d/1rkN4W-u3G6-Uj7ReSkVRVtLKI4zgCwEh/view?usp=sharing
mp4 file btw
Scriptable Objects are platform-independent which means they work on any platform.
Here's what Unity's official Scriptable Object's documentation says:
When you use the Editor, you can save data to ScriptableObjects while editing and at run time because ScriptableObjects use the Editor namespace and Editor scripting. In a deployed build, however, you can’t use ScriptableObjects to save data, but you can use the saved data from the ScriptableObject Assets that you set up during development.
In order to save data to Scriptable Object (when you click on Save button in UI) you have to save the Scriptable Object to player prefs.
Something like this:
create auxiliary class i.e SaveSystemData as a separate file and mark it as [Serializable] (must do this) and add lists from Scriptable object from it
[Serializable]
public sealed class SaveSystemData
{
public List<string> List1;
public List<string> List2;
public List<string> List3;
public List<string> List4;
public List<string> List5;
}
reference that class in your Scriptable object instead of lists
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(fileName ="ScriptableSaver", menuName ="SaveSystem")]
public class SaveSystem : ScriptableObject
{
public SaveSystemData saveSystemData;
}
save SaveSystemData to JSON and put it into player prefs when you click on Save button in UI
private const string SAVED_DATA = "SavedData";
public void SaveGame()
{
SOScript.saveSystemData.List1 = DictionaryWords;
SOScript.saveSystemData.List2 = DictionaryMeaning;
SOScript.saveSystemData.List3 = DictionaryInRussian;
SOScript.saveSystemData.List4 = DictionaryAddInfo;
SOScript.saveSystemData.List5 = DictionaryAVN;
PlayerPrefs.SetString(SAVED_DATA, JsonUtility.ToJson(SOScript.saveSystemData))
}
load SaveSystemData from JSON
public void LoadGame()
{
if (PlayerPrefs.HasKey(SAVED_DATA))
{
SOScript.saveSystemData = JsonUtility.FromJson<SaveSystemData>(PlayerPrefs.GetString(SAVED_DATA));
}
DictionaryWords = SOScript.saveSystemData.List1;
DictionaryMeaning = SOScript.saveSystemData.List2;
DictionaryInRussian = SOScript.saveSystemData.List3;
DictionaryAddInfo = SOScript.saveSystemData.List4;
DictionaryAVN = SOScript.saveSystemData.List5;
}
That's it in general
Related
I'm creating a system where I have my cards in ScriptableObject, but each card has a unique function. What I thought of was creating a new class for each skill that inherits from "skills". So I put this script for each letter in my ScriptableObject, but now I'm trying to access it and I can't, because I couldn't use the AddComponent.
public class Cards : ScriptableObject
{
public Sprite cardSprite;
public int cardAttack;
public int cardHealth;
public int cardCost;
public Object cardAbility;
}
As you realized, you can't use AddComponent with ScriptableObjects, because it can only be used to attach components to GameObjects.
ScriptableObjects however do support a somewhat similar concept of main assets and sub assets.
You can use AssetDatabase.AddObjectToAsset to add new sub assets, and they'll appear as nested children below the main asset in the Project view.
public abstract class Skill : ScriptableObject
{
#if UNITY_EDITOR
protected static void AddToCard<TSkill>(MenuCommand command) where TSkill : Skill
{
Card card = (Card)command.context;
TSkill skill = CreateInstance<TSkill>();
skill.name = skill.GetType().Name;
card.skill = skill;
string path = AssetDatabase.GetAssetPath(card);
AssetDatabase.AddObjectToAsset(skill, path);
AssetDatabase.ImportAsset(path);
}
[MenuItem("CONTEXT/Card/Remove Skill")]
protected static void RemoveSkill(MenuCommand command)
{
Card card = (Card)command.context;
string path = AssetDatabase.GetAssetPath(card);
Skill skill = AssetDatabase.LoadAssetAtPath<Skill>(path);
AssetDatabase.RemoveObjectFromAsset(skill);
AssetDatabase.ImportAsset(path);
}
#endif
}
using UnityEditor;
using UnityEngine;
[CreateAssetMenu]
public class ExampleSkill : Skill
{
#if UNITY_EDITOR
[MenuItem("CONTEXT/Card/Add Skill/Example Skill")]
private static void AddToCard(MenuCommand command) => AddToCard<ExampleSkill>(command);
#endif
}
using UnityEngine;
[CreateAssetMenu]
public class Card : ScriptableObject
{
public Skill skill;
}
A scriptable object is a data container. A material or lighting settings are scriptable objects. You can save them as assets and use them as settings for other scripts, but not attach them to GameObjects. Read more here.
To create ScriptableObjects in the asset browser, you need to add the following before the start of your class.
[CreateAssetMenu(fileName = "DefaultFileName", menuName = "ScriptableObjects/NameOfYourObjects", order = 1)]
To add them to a script, you use public Cards cards; then you can drag them from your asset browser to the script in the inspector.
The only things you can add to a GameObject are MonoBehaviors
I tried to create nested object in C# script in Unity and change the values in it.
Class of nested object
using UnityEngine;
public class Replica : ScriptableObject
{
public string Text;
public int Speed = 1;
public int PersonaID = 0;
}
Class with nested object
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
[CreateAssetMenu(fileName = "Data", menuName = "ScriptableObjects/DialogContainer", order = 1)]
public class DialogContainer : ScriptableObject {
public Replica[] Replicas; // Here is nested object
public Sprite[] Avatars;
}
And when I created the ScriptableObject I saw that:
Photo of ScriptableObject interface
Here I can only put an instance of the class there, but I cannot configure it.
But i want to change values right in inspector without creating and inserting object of class "Replica" like in InputManager where I can create one more obj in axis, open it and change values in inspector like that.
Photo of interface I want to see
If u want configurate your class in inspector, that class must be not derived from MonoBehaviour or ScriptableObject and be with tag [System.Serializable] than u can serialize it in nested class without creating instance.
Replica:
[System.Serializable]
public class Replica
{
public string Text;
public int Speed = 1;
public int PersonaID = 0;
}
Dialog Container:
using System.Collections.Generic;
using UnityEngine;
public class DialogContainer : MonoBehaviour{
public List<Replica> Replicas;
public Sprite[] Avatars;
}
Than I saw that:Inspector screenshot
Unless you make a custom editor for DialogContainer, you can't do what you want to do. If you have a parent ScriptableObject that contains a child list of ScriptableObjects, you can't edit each child's fields when you have the parent selected in your Project View.
You will have to edit each child Replica by selecting the Replica instance in the Project View. Then the Inspector will let you edit that Replica.
How can I pass score value from one scene to another?
I've tried the following:
Scene one:
void Start () {
score = 0;
updateScoreView ();
StartCoroutine (DelayLoadlevel(20));
}
public void updateScoreView(){
score_text.text = "The Score: "+ score;
}
public void AddNewScore(int NewscoreValue){
score = score + NewscoreValue;
updateScoreView ();
}
IEnumerator DelayLoadlevel(float seconds){
yield return new WaitForSeconds(10);
secondsLeft = seconds;
loadingStart = true;
do {
yield return new WaitForSeconds(1);
} while(--secondsLeft >0);
// here I should store my last score before move to level two
PlayerPrefs.SetInt ("player_score", score);
Application.LoadLevel (2);
}
Scene two:
public Text score_text;
private int old_score;
// Use this for initialization
void Start () {
old_score = PlayerPrefs.GetInt ("player_score");
score_text.text = "new score" + old_score.ToString ();
}
but nothing displayed on screen, and there's no error.
Is this the correct way to pass data ?
I am using Unity 5 free edition, develop game for Gear VR (meaning the game will run in android devices).
Any suggestion?
There are many ways to do this but the solution to this depends on the type of data you want to pass between scenes. Components/Scripts and GameObjects are destroyed when new scene is loaded and even when marked as static.
In this answer you can find
Use the static keyword
Use DontDestroyOnLoad
Store the data local
3a PlayerPrefs
3b serialize to XML/JSON/Binary and use FileIO
1. Use the static keyword.
Use this method if the variable to pass to the next scene is not a component, does not inherit from MonoBehaviour and is not a GameObject then make the variable to be static.
Built-in primitive data types such as int, bool, string, float, double. All those variables can be made a static variable.
Example of built-in primitive data types that can be marked as static:
static int counter = 0;
static bool enableAudio = 0;
static float timer = 100;
These should work without problems.
Example of Objects that can be marked as static:
public class MyTestScriptNoMonoBehaviour
{
}
then
static MyTestScriptNoMonoBehaviour testScriptNoMono;
void Start()
{
testScriptNoMono = new MyTestScriptNoMonoBehaviour();
}
Notice that the class does not inherit from MonoBehaviour. This should work.
Example of Objects that cannot be marked as static:
Anything that inherits from Object, Component or GameObject will not work.
1A.Anything that inherits from MonoBehaviour
public class MyTestScript : MonoBehaviour
{
}
then
static MyTestScript testScript;
void Start()
{
testScript = gameObject.AddComponent<MyTestScript>();
}
This will not work because it inherits from MonoBehaviour.
1B.All GameObject:
static GameObject obj;
void Start()
{
obj = new GameObject("My Object");
}
This will not work either because it is a GameObject and GameObject inherit from an Object.
Unity will always destroy its Object even if they are declared with the static keyword.
See #2 for a workaround.
2.Use the DontDestroyOnLoad function.
You only need to use this if the data to keep or pass to the next scene inherits from Object, Component or is a GameObject. This solves the problem described in 1A and 1B.
You can use it to make this GameObject not to destroy when scene unloads:
void Awake()
{
DontDestroyOnLoad(transform.gameObject);
}
You can even use it with the static keyword solve problem from 1A and 1B:
public class MyTestScript : MonoBehaviour
{
}
then
static MyTestScript testScript;
void Awake()
{
DontDestroyOnLoad(transform.gameObject);
}
void Start()
{
testScript = gameObject.AddComponent<MyTestScript>();
}
The testScript variable will now be preserved when new scene loads.
3.Save to local storage then load during next scene.
This method should be used when this is a game data that must be preserved when the game is closed and reopened. Example of this is the player high-score, the game settings such as music volume, objects locations, joystick profile data and so on.
Thare are two ways to save this:
3A.Use the PlayerPrefs API.
Use if you have just few variables to save. Let's say player score:
int playerScore = 80;
And we want to save playerScore:
Save the score in the OnDisable function
void OnDisable()
{
PlayerPrefs.SetInt("score", playerScore);
}
Load it in the OnEnable function
void OnEnable()
{
playerScore = PlayerPrefs.GetInt("score");
}
3B.Serialize the data to json, xml or binaray form then save using one of the C# file API such as File.WriteAllBytes and File.ReadAllBytes to save and load files.
Use this method if there are many variables to save.
General, you need to create a class that does not inherit from MonoBehaviour. This class you should use to hold your game data so that in can be easily serialized or de-serialized.
Example of data to save:
[Serializable]
public class PlayerInfo
{
public List<int> ID = new List<int>();
public List<int> Amounts = new List<int>();
public int life = 0;
public float highScore = 0;
}
Grab the DataSaver class which is a wrapper over File.WriteAllBytes and File.ReadAllBytes that makes saving data easier from this post.
Create new instance:
PlayerInfo saveData = new PlayerInfo();
saveData.life = 99;
saveData.highScore = 40;
Save data from PlayerInfo to a file named "players":
DataSaver.saveData(saveData, "players");
Load data from a file named "players":
PlayerInfo loadedData = DataSaver.loadData<PlayerInfo>("players");
There is another way:
ScriptableObject
ScriptableObjects are basically data containers but may also implement own logic. They "live" only in the Assets like prefabs. They can not be used to store data permanently, but they store the data during one session so they can be used to share data and references between Scenes ... and - something I also often needed - between Scenes and an AnimatorController!
Script
First you need a script similar to MonoBehaviours. A simple example of a ScriptableObject might look like
// fileName is the default name when creating a new Instance
// menuName is where to find it in the context menu of Create
[CreateAssetMenu(fileName = "Data", menuName = "Examples/ExamoleScriptableObject")]
public class ExampleScriptableObject : ScriptableObject
{
public string someStringValue = "";
public CustomDataClass someCustomData = null;
public Transform someTransformReference = null;
// Could also implement some methods to set/read data,
// do stuff with the data like parsing between types, fileIO etc
// Especially ScriptableObjects also implement OnEnable and Awake
// so you could still fill them with permanent data via FileIO at the beginning of your app and store the data via FileIO in OnDestroy !!
}
// If you want the data to be stored permanently in the editor
// and e.g. set it via the Inspector
// your types need to be Serializable!
//
// I intentionally used a non-serializable class here to show that also
// non Serializable types can be passed between scenes
public class CustomDataClass
{
public int example;
public Vector3 custom;
public Dictionary<int, byte[]> data;
}
Create Instances
You can create instances of ScriptableObject either via script
var scriptableObject = ScriptableObject.CreateInstance<ExampleScriptableObject>();
or to make things easier use the [CreateAssetMenu] as shown in the example above.
As this created ScriptabeObject instance lives in the Assets it is not bound to a scene and can therefore be referenced everywhere!
This when you want to share the data between two Scenes or also e.g. the Scene and an AnimatorController all you need to do is reference this ScriptableObject instance in both.
Fill Data
I often use e.g. one component to fill the data like
public class ExampleWriter : MonoBehaviour
{
// Here you drag in the ScriptableObject instance via the Inspector in Unity
[SerializeField] private ExampleScriptableObject example;
public void StoreData(string someString, int someInt, Vector3 someVector, List<byte[]> someDatas)
{
example.someStringValue = someString;
example.someCustomData = new CustomDataClass
{
example = someInt;
custom = someVector;
data = new Dictionary<int, byte[]>();
};
for(var i = 0; i < someDatas.Count; i++)
{
example.someCustomData.data.Add(i, someDatas[i]);
}
example.someTransformReference = transform;
}
}
Consume Data
So after you have written and stored your required data into this ExampleScriptableObject instance every other class in any Scene or AnimatorController or also other ScriptableObjects can read this data on just the same way:
public class ExmpleConsumer : MonoBehaviour
{
// Here you drag in the same ScriptableObject instance via the Inspector in Unity
[SerializeField] private ExampleScriptableObject example;
public void ExampleLog()
{
Debug.Log($"string: {example.someString}", this);
Debug.Log($"int: {example.someCustomData.example}", this);
Debug.Log($"vector: {example.someCustomData.custom}", this);
Debug.Log($"data: There are {example.someCustomData.data.Count} entries in data.", this);
Debug.Log($"The data writer {example.someTransformReference.name} is at position {example.someTransformReference.position}", this);
}
}
Persistence
As said the changes in a ScriptableObject itself are only in the Unity Editor really persistent.
In a build they are only persistent during the same session.
Therefore if needed I often combine the session persistence with some FileIO (as described in this answer's section 3b) for loading and deserializing the values once at session begin (or whenever needed) from the hard drive and serialize and store them to a file once on session end (OnApplicationQuit) or whenever needed.
(This won't work with references of course.)
Besides playerPrefs another dirty way is to preserve an object during level loading by calling DontDestroyOnLoad on it.
DontDestroyOnLoad (transform.gameObject);
Any script attached to the game object will survive and so will the variables in the script.
The DontDestroyOnLoad function is generally used to preserve an entire GameObject, including the components attached to it, and any child objects it has in the hierarchy.
You could create an empty GameObject, and place only the script containing the variables you want preserved on it.
I use a functional approach I call Stateless Scenes.
using UnityEngine;
public class MySceneBehaviour: MonoBehaviour {
private static MySceneParams loadSceneRegister = null;
public MySceneParams sceneParams;
public static void loadMyScene(MySceneParams sceneParams, System.Action<MySceneOutcome> callback) {
MySceneBehaviour.loadSceneRegister = sceneParams;
sceneParams.callback = callback;
UnityEngine.SceneManagement.SceneManager.LoadScene("MyScene");
}
public void Awake() {
if (loadSceneRegister != null) sceneParams = loadSceneRegister;
loadSceneRegister = null; // the register has served its purpose, clear the state
}
public void endScene (MySceneOutcome outcome) {
if (sceneParams.callback != null) sceneParams.callback(outcome);
sceneParams.callback = null; // Protect against double calling;
}
}
[System.Serializable]
public class MySceneParams {
public System.Action<MySceneOutcome> callback;
// + inputs of the scene
}
public class MySceneOutcome {
// + outputs of the scene
}
You can keep global state in the caller's scope, so scene inputs and outputs states can be minimized (makes testing easy). To use it you can use anonymous functions:-
MyBigGameServices services ...
MyBigGameState bigState ...
Splash.loadScene(bigState.player.name, () => {
FirstLevel.loadScene(bigState.player, (firstLevelResult) => {
// do something else
services.savePlayer(firstLevelResult);
})
)}
More info at https://corepox.net/devlog/unity-pattern:-stateless-scenes
There are various way, but assuming that you have to pass just some basic data, you can create a singelton instance of a GameController and use that class to store the data.
and, of course DontDestroyOnLoad is mandatory!
public class GameControl : MonoBehaviour
{
//Static reference
public static GameControl control;
//Data to persist
public float health;
public float experience;
void Awake()
{
//Let the gameobject persist over the scenes
DontDestroyOnLoad(gameObject);
//Check if the control instance is null
if (control == null)
{
//This instance becomes the single instance available
control = this;
}
//Otherwise check if the control instance is not this one
else if (control != this)
{
//In case there is a different instance destroy this one.
Destroy(gameObject);
}
}
Here is the full tutorial with some other example.
you have several options.
The first one I see is to use static variables, which you will not lose their information or value passing from scenes to scenes (since they are not bound to the object). [you lose the information when closing the game, but not when passing between scenes]
the second option is that the player or the object of which you do not want to lose the information, you pass it through the DontDestroyOnLoad function
Here I give you the documentation and the sample code. [You lose the information when you close the game, but not when you go between scenes]
https://docs.unity3d.com/ScriptReference/Object.DontDestroyOnLoad.html
Third is to use the playerPrefab [https://docs.unity3d.com/ScriptReference/PlayerPrefs.html]
that allow you to save information and retrieve it at any time without hanging it even after closing the game [you must be very careful with the latter if you plan to use it to save data even after closing the game since you can lose the data if you close the game suddenly , since player prefab creates a file and retrieves the information from there, but it saves the file at the end or closes the app correctly]
So i have 2 classes (Sound class, AudioManager class)
I dont understand why we can access Sound's field without creating instance of Sound class. ()
Like what are the differences between
public Sound test1;
AND
Sound test2 = new Sound();
Its weird to me cause when i was learning c# tutorials / u could only access variables from another class only by creating instance of that class but in Unity u cas access it just by typing public Sound test1;
Sound Class
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Audio;
[System.Serializable]
public class Sound
{
public AudioClip clip;
public string name;
public float volume;
public float pitch;
public AudioSource soundclass_source;
}
AudioManager class
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Audio;
public class AudioManager : MonoBehaviour
{
public Sound test1;
Sound test2 = new Sound();
test1.name = "test1"; // without new
test2.name = "test2"; // with new
}
Since it is [Serializable] and the Unity Inspector of MonoBehaviour and ScriptableObject (etc) classes automatically initializes serialized fields with a default instance there is no difference between
public Sound test1;
and
public Sound test1 = new Sound();
(public fields are serialized by default if the type is serializable)
Also note that once you created an instance in Unity and the values are set via the Inspector those serialized values will always overrule the ones you hardcode here. If you later e.g. decide to pre-initliaze some values and change it to
public Sound test1 = new Sound() { name = "test1" };
it will have no effect since the value configured in the Inspector is taken instead. This coded value only is used for the very forst time the instance is created or if you hit Reset in the components context menu.
If you want later that the coded value is used instead you have to assign the new value in a method instead (see below).
No accesibility definition for a field (/class/method/property/etc) like public, protected or private in c# always means private.
If a field is private like in your case you have to initialize it "manually" otherwise the default value for classes is always null (== no instance reference).
You can do this like you did staticly in the class
private Sound test2 = new Sound();
this means the instance is already created as soon as an instance of your class is created or can do it laso later in a method like
private Sound test2;
private void Awake()
{
Sound test2 = new Sound();
}
Finally you can/should make as private as possible for capsulation but can still make them serialized => displayed in the Inspector and stored using the [SerializeField] attribute:
[SerilaizeField] private Sound test2;
You can read more about Unity's serialization here
I have a problem when changing values and trying to play game these values disappear. For example, if I enable bool in editor and the start game bool disables itself. I use custom editor to change these values.
This is my settings script and [system.serializable] doesn't help:
using UnityEngine;
[System.Serializable]
public class Settings : MonoBehaviour
{
public static bool testMode;
public static string androidUnitID;
public static string iphoneUnitID;
}
And this is my editor script:
using UnityEngine;
using UnityEditor;
[CustomEditor(typeof(Settings))]
class TestEditor : Editor {
public override void OnInspectorGUI() {
GUILayout.Label("Settings", EditorStyles.boldLabel);
Settings.testMode = EditorGUILayout.Toggle("Test Mode", Settings.testMode);
Settings.androidUnitID = EditorGUILayout.TextField("Android UnitID", Settings.androidUnitID);
Settings.iphoneUnitID = EditorGUILayout.TextField("Android UnitID", Settings.iphoneUnitID);
}
First I'm changing values on editor:
Then when play mode is active all values disappear:
Is there any way to fix this problem?
Thank you for your help.
Unity doesn't support serialising static variables "out-of-the-box" (you can rewrite the serialisation to accommodate it, but effort vs. reward and all that). So, based on that, what you're essentially seeing is the values that you're setting being stored in the class during Edit, but when Unity goes into Play mode, all the class are re-initialised, deserialised and prepared for game-play. Your static values are lost.
It's the same reason why changed values during game play aren't kept when you go back the other way to Edit mode. Edit mode and Play mode are essentially two separate processes.
Your best bet here would be to create a ScriptableObject, with those fields you've marked as static in your Settings class, create a reference to the new ScriptableObject, and then, if you want, modify the values directly from the ScriptableObject.
Something along the lines of:
[CreateAssetMenu ( fileName = "Settings", menuName = "Settings/Create Settings SO", order = 1 )]
public class SettingsScriptableObject : ScriptableObject
{
public bool testMode;
public string androidUnitID;
public string iphoneUnitID;
//... Any further configuration/settings items you want should go here.
}
You would then modify your Settings class:
public class Settings : MonoBehaviour
{
public SettingsScriptableObject SettingsSO;
}
And your editor script:
[CustomEditor(typeof(Settings))]
class TestEditor : Editor
{
public override void OnInspectorGUI()
{
if ( target == null ) return;
var settings = target as Settings;
GUILayout.Label("Settings", EditorStyles.boldLabel);
settings.SettingsSO.testMode = EditorGUILayout.Toggle("Test Mode", settings.SettingsSO.testMode);
settings.SettingsSO.androidUnitID = EditorGUILayout.TextField("Android UnitID", settings.SettingsSO.androidUnitID);
settings.SettingsSO.iphoneUnitID = EditorGUILayout.TextField("Android UnitID", settings.SettingsSO.iphoneUnitID);
}
}
There's an excellent video at Unity about the Edit and Play modes, and how ScriptableObjects can be used to overcome the serialisation problem.