I have a singleton class that contains general information about my game.
public class GeneralGameData : ScriptableObject
{
private static GeneralGameData _currentGeneralGameData;
public static GeneralGameData CurrentGeneralGameData
{
get
{
if (_currentGeneralGameData == null)
{
_currentGeneralGameData = CreateInstance<GeneralGameData>();
}
DontDestroyOnLoad(_currentGeneralGameData);
return _currentGeneralGameData;
}
}
public string GameName;
public string GameVersion;
}
This class has no presents in the scene.
I also have a window to show and change that
public class GeneralGameDataMenuItem : EditorWindow
{
[MenuItem("CoreMaker/GeneralGameData")]
private static void _generalGameData()
{
GetWindow<GeneralGameDataMenuItem>("GeneralGameData");
}
void OnGUI()
{
GeneralGameData.CurrentGeneralGameData.GameName = EditorGUILayout.TextField("GameName", GeneralGameData.CurrentGeneralGameData.GameName);
GeneralGameData.CurrentGeneralGameData.GameVersion = EditorGUILayout.TextField("GameVersion", GeneralGameData.CurrentGeneralGameData.GameVersion);
EditorUtility.SetDirty(GeneralGameData.CurrentGeneralGameData);
}
}
The problem is that it wont save my changes after i hit play or restart unity.
any solutions??
A ScriptableObject is intended to be saved as an asset. Add the line [CreateAssetMenu(menuName = "MyGame/GeneralGameData")] above your ScriptableObject class declaration, then right click in the project pane, click Create > MyGame > GeneralGameData. Fill in all the fields you need. Any script that needs to reference it can just add a public field of type GeneralGameData and add that asset in the inspector.
This is the fixed code based on Nailuj29s answer but you do not need to get reference to it by having a public field , instead you just need to use GeneralGameData.CurrentGeneralGameData.
public class GeneralGameData : ScriptableObject
{
private static GeneralGameData _currentGeneralGameData;
public static GeneralGameData CurrentGeneralGameData
{
get
{
if (_currentGeneralGameData == null)
{
if (AssetDatabase.FindAssets("GeneralGameData", new []{ "Assets/CoreMaker" }).Length != 1)
{
_currentGeneralGameData = CreateInstance<GeneralGameData>();
if (!AssetDatabase.IsValidFolder("Assets/CoreMaker"))
{
AssetDatabase.CreateFolder("Assets", "CoreMaker");
}
AssetDatabase.CreateAsset(_currentGeneralGameData,"Assets/CoreMaker/GeneralGameData.asset");
}
}
_currentGeneralGameData = AssetDatabase.LoadAssetAtPath<GeneralGameData>("Assets/CoreMaker/GeneralGameData.asset");
return _currentGeneralGameData;
}
}
public string GameName;
public string GameVersion;
}
Keep in mind that when you reference GeneralGameData.CurrentGeneralGameData it is going to create and asset , if you delete that asset you are going to lose you data.
The reason you lose your data is because there are 2 ways people use ScriptableObject for singletons. The key difference is how the static value is set. (e.g. the _currentGeneralGameData = CreateInstance<GeneralGameData>() line in your code)
From an ASSET RESOURCE:
This is a shared, "actual" object/file that exists in your project Resources. When you change it, the changes are "permanent" (saved to that actual object), which I believe is what you're looking for. In your case, this would mean simply grabbing a reference to your existing asset resource, instead of creating a new instance, something along the lines of:
_currentGeneralGameData = Resources.Load<GeneralGameData>("GeneralGameData");
or
_currentGeneralGameData = Resources.FindObjectsOfTypeAll<GeneralGameData>().FirstOrDefault();
Note: Skipping verification/error handling code for clarity.
Important: Runtime (standalone) versus editor will get different behavior: you may need to put an explicit reference to the ScriptableObject asset in your scene to make sure it is not optimized out. More details at: https://baraujo.net/unity3d-making-singletons-from-scriptableobjects-automatically/
From an INSTANCE of an asset:
This is your current code. Instead of grabbing a shared asset, you create a FRESH instance, disconnected from what is saved on disk, which means you will not be saving any of the changes you make at runtime.
This is perfectly fine, and possibly preferable in some cases, but if you do want to keep changes then you should use the asset resource method.
Related
I need help to get all the traits from a list from another class then set them a specific image and show them simultaneously to the player.
To do so, I created in my UI a traitSlotContainer with a grid layout and a traitSlotTemplate GameObject with an Image. I would like this traitSlotTemplate to be used for each Trait.
I've got a class called "Npc", which represents one instance of auto generated instanciate NPCs containing their name, stats, traits, skills etc...
Class is in shorter something like that:
[Serializable]
public class Npc
{
public string NameLocalizationKey;
public int Height;
public int Weight;
public string EyeColor;
public string HairColor;
public string Haircut;
public List<Trait> Traits;
}
I would like to show the traits proper to the selected NPC.
Note that the instantiate NPC is "Selected.Npc"
I reckon that I need first to load the selected NPC trait list, get one trait, instantiate it as GameObject then change it's sprite to the adequate one. And redo.
How am I suppose to do that?
my trait class is as below:
[Serializable]
public class Trait
{
public string NameLocalizationKey;
public string DescriptionLocalizationKey;
public string[] IncompatibleTraitLocalizationKey;
public StatName Stat1AffectedName;
public int Stat1AffectedValue;
public TraitStatEffectUnit Stat1AffectedUnit;
public StatName Stat2AffectedName;
public int Stat2AffectedValue;
public TraitStatEffectUnit Stat2AffectedUnit;
public TraitCondition Condition;
public bool StartingTrait;
public bool IsNegative;
public bool AffectStat;
}
NameLocalizationKey is always like that: trait_name_trait1, trait_name_trait2 etc.. and value are stored into a GameData.json
And my traitAsset class is like that:
public class TraitAssets : MonoBehaviour
{
public static TraitAssets Instance { get; private set; }
private void Awake()
{
Instance = this;
}
public Sprite spendthriftySprite;
public Sprite thriftySprite;
public Sprite studiousSprite;
public Sprite lazySprite;
}
I found an online tutorial and did that:
public Sprite GetTraitSprite(string NameLocalizationKey)
{
switch (NameLocalizationKey)
{
default:
case "trait_name_lazy": return TraitAssets.Instance.lazySprite;
case "trait_name_spendthrifty": return TraitAssets.Instance.spendthriftySprite;
case "trait_name_studious": return TraitAssets.Instance.studiousSprite;
case "trait_name_thrifty": return TraitAssets.Instance.thriftySprite;
}
}
I am really new to Unity and I try to figure out what my dev (who abandoned me due to lack of motivation but did an incredible job so far) have done.
Sorry for my unclear formulation and thanks for your answer.
Instead of a "all done result" or answer, if one could explain me step by step how to achieve what I asked that would be marvelously kind. If you give a hungry man a fish, you feed him for a day, but if you teach him how to fish, you feed him for a lifetime.
Im not quite sure what the vision of this implementation is, since there are a few ways to do this vague idea. Seems like that code right there is doing what its supposed to, so i dont see the problem. Call the function to get the sprites and instantiate them.
I reckon that I need first to load the selected NPC trait list, get one trait, instantiate it as GameObject then change it's sprite to the adequate one. And redo.
How am I suppose to do that?:(
Given this exact question, i say just make a new function somewhere (im not familiar with your project) and in it have a for loop that goes through the Traits List and calls that GetTraitSprite function to get it. Then use the unity Instantiate function to spawn it in. To spawn it in game (im assuming you display the sprites onto the unity UI canvas), first create a GameObject prefab in the editor that just holds a sprite component. Spawn in that placeholder with the instantiate. Then in the loop at run time, assign that components sprite to the one GetTraitSprite returns after you've spawned in the template prefab. You could also put an if condition to check the sprite is != null or skip it. The unity Instantiate function returns the spawned gameobject, so you can do whatever you like with it then.
I have a scene that is a Paddle (like the one in PONG game). But my paddles can be either horizontal either vertical.
So I made one parent abstract class "Paddle" that contains the common logic, and two derived classes that extends Paddle "PaddleHorizontal" and "PaddleVertical" where the movements are different (one go up and down, the other go left and right).
At the beginning, I want to create my paddles and attach correct script to each of them but I got this error
" Script inherits from native type 'KinematicBody2D', so it can't be instanced in object of type: 'PackedScene' "
My Main.cs is like so :
using Godot;
public class Main : Node2D
{
private PackedScene _paddleScene;
public override void _Ready()
{
base._Ready();
_paddleScene = GD.Load<PackedScene>("res://src/scenes/entities/paddle/Paddle.tscn");
var script = GD.Load<Reference>("res://src/scenes/entities/paddle/PaddleHorizontal.cs");
_paddleScene.SetScript(script);
this.InitPaddles();
}
private void InitPaddles()
{
this.AddPaddle(new Vector2(PaddlePositions.Top.x, PaddlePositions.Top.y));
this.AddPaddle(new Vector2(PaddlePositions.Bottom.x, PaddlePositions.Bottom.y));
}
private void AddPaddle(Vector2 paddlePosition)
{
KinematicBody2D paddleInstance = (KinematicBody2D)_paddleScene.Instance();
paddleInstance.Position = paddlePosition;
AddChild(paddleInstance);
}
}
// -- Paddle.cs --
using Godot;
public abstract class Paddle : KinematicBody2D
{
// common methods & properties
// the one method that is different and should be override
public abstract Vector2 GetMovement();
}
// -- PaddleHorizontal.cs --
using Godot;
public class PaddleHorizontal : Paddle
{
public override Vector2 GetMovement()
{
// different from PaddleVertical
}
}
I guess the error come from the fact that PaddleHorizontal don't extends KinematicBody2D directly but there is a lot of logic that will be in common between the 2 types of Paddle... .. If you guys have a solution or a workaround...
EDIT: A workaround has beed found by mxmissile in comments. Instead of setting the script of PackedScene, he proposed to set it on the Instance. And it works. Check https://github.com/godotengine/godot/issues/31994 for more details and DO READ the first comment in order to avoid another issue.
The solution suggested by #mxmissile looks like:
private void AddPaddle(Vector2 paddlePosition)
{
KinematicBody2D paddleInstance = (KinematicBody2D)_paddleScene.Instance();
ulong paddleInstanceId = paddleInstance.GetInstanceId();
Resource script = GD.Load("res://src/scenes/entities/paddle/PaddleHorizontal.cs");
paddleInstance.SetScript(script);
paddleInstance = (KinematicBody2D)GD.InstanceFromId(paddleInstanceId);
paddleInstance.Position = paddlePosition;
AddChild(paddleInstance);
}
The Error
Script inherits from native type 'KinematicBody2D', so it can't be instanced
in object of type: 'PackedScene'
This error is caused by setting the script of an Object to a script of a
different object hierarchy.
The same error will occur if you, for example, set the script of a Control node
to a script that inherits from Node2D.
The Solution
Instead of set_script() of the _paddleScene you should set_script() of the
paddleInstance which is the instance of KinematicBody2D.
KinematicBody2D paddleInstance = (KinematicBody2D)_paddleScene.Instance();
paddleInstance.SetScript(thePaddleScript);
I'm making a card game where I assign random effects to cards, so I need to load the effect's code at runtime with just the class name.
I don't know if my abstract class and child are done properly, and I also don't exactly know how to get the class needed from a path.
I know Resouces.Load won't work but I'll leave it there to convey what I wanna do more easily.
public class GameManager : MonoBehaviour
{
public Effect effect;
...
effect = Resources.Load<Effect>("Card/Effects/" + c.cardActual.effect1);
if (effect.Execution())
{
StartCoroutine(TargetAndCastSpell(c,p));
}
This is the code for my abstract class
public abstract class Effect : MonoBehaviour
{
public string targetType;
public List<int> availableTargets;
public int effectTier;
public PlayerHolder playerTarget;
public CardPhysicalInstance minionTarget;
public PlayerHolder caster;
public void EffectX(PlayerHolder PlayerTarget, CardPhysicalInstance MinionTarget)
{
}
public bool Execution()
{
return false;
}
}
And lastly the child I want to load in runtime
class Spark : Effect
{
string targetType = "any";
//Deal 1 damage to any target
public bool Execution ()
{
bool canTarget = false;
caster = GameManager.singleton.currentPlayer;
availableTargets = SpellHelper.AvailableTargets();
if (targetType == "any") //Placeholder check
{
canTarget = true;
caster.playerState = GameManager.PlayerState.targeting;
}
return canTarget;
}
...
Any help is deeply appreciated, thanks and sorry about my clear lack of understanding of abstract classes.
Based on comments, I think Overriding is the Droid you are looking for. With Polymorphy there is two ways different Implementations can be resolved.
hiding is possibly by default. However, it is also pretty much useless. It is one of those things we thought we need and now everyone adds it to their OOP language. But aside from not using hiding when I wanted to overwrite, I have never had any use for it.
Overriding is the important thing. However, overriding has to be allowed for a function in the base class that first added it.
In Effect:
//Stil work how it does, but now can be overridden
public virtual bool Execution()
{
return false;
}
In Spark:
//We are actually overriding - not just hiding - Effect.Execution() here
public override bool Execution ()
{
bool canTarget = false;
caster = GameManager.singleton.currentPlayer;
availableTargets = SpellHelper.AvailableTargets();
if (targetType == "any") //Placeholder check
{
canTarget = true;
caster.playerState = GameManager.PlayerState.targeting;
}
return canTarget;
}
You can assign a Spark to a Effect variable, call Execution() and Polymorphy will deal with calling the version of Spark.
Add anotehr Effect sub-class? As long as it also overrides Execution() it works the same.
The Effect version could be empty/turned abstract. Or be kept as a default version for all subclasses.
With hiding you would have to cast it back to Spark to get access to it's variant of the Method. Wich is just extra work with no apparent advantage.
This question already has answers here:
How to pass data (and references) between scenes in Unity
(6 answers)
Closed 3 years ago.
I am making a game that involves the score of the player. This score is always displayed on the screen. However when i change scenes the score resets back to 0. How can i make it so that the score gets saved across all of my scenes?
I have looked on the internet for hours but i was not able to fix it that way. The 2 main things that i came across were this:
Static variables
DontDestroyOnLoad()
While i am sure that these would fix my issue, i have no idea how to implement them in my code. Do i make a new script? Do i put them in an existing script? And if so, which one? How can call upon variables if they are static?
Thanks in advance for any help!
If you're just trying to be able to access simple data across screens, static variables seem like the most straightforward solution. It's fine if you've never used them before, they're very easy to use once you get used to them. :)
The first thing to do is create a class that will hold your static variables (yes, this means writing a new script and putting it in your Scripts folder). It's possible to add new static variables on a class you're already using, but if you're just learning I would recommend keeping things like that separated so it's more organized.
public class GameValues {
public static int score { get; set; }
// You can add other static variables here.
}
Notice I didn't inherit from MonoBehaviour. This is fine because we don't need to add this script to a GameObject, it's just going to be used to store our data. Now if we need to change or read the score, we can do it like this.
public class Example : MonoBehaviour {
public int scoreLimit = 100;
private void Update () {
/*
* When accessing static members of a class, no object reference is needed.
* Instead you access it as though it were a member of the class.
*/
int currentScore = GameValues.score;
if (Input.GetKeyDown (KeyCode.Space) && (currentScore < scoreLimit)) {
GameValues.score += 1;
}
}
}
Obviously this is a rather silly example, but you get the idea. If you'd like to do some further reading, here is the official Microsoft documentation on the static keyword.
Happy coding!
You have a couple of options.
1) PlayerPrefs
Unity has an in-built save procedure called PlayerPrefs that will store data between scenes and game restarts.
//Store Score Value
PlayerPrefs.SetInt("Score", Score);
// Retrieve Score Value if there is one
if (PlayerPrefs.HasKey("Score"))
{
Score = PlayerPrefs.GetInt("Score");
}
PlayerPrefs Documentation
2) Unity Singleton
The Unity Singleton differs from a normal programming languages slightly in its use of DontDestroyOnLoad to keep the GameObject holding the Singleton alive between scenes.
public class UnitySingleton : MonoBehaviour
{
public static UnitySingleton Instance { get; private set; }
public int Score { get; set; }
private void Awake()
{
if (Instance != null && Instance != this)
{
Destroy(gameObject);
}
else
{
Instance = this;
DontDestroyOnLoad(gameObject);
}
}
}
Usage
//Store Score
UnitySingleton.Instance.Score = value;
//Retrieve Score if set otherwise (it will) return 0
Score = UnitySingleton.Instance.Score;
Alright here's a minimal example
Save data object:
using UnityEngine;
public class SaveDataHere : MonoBehaviour {
public string myData;
public static string staticData = "Static data is still here";
// Use this for initialization
void Start () {
DontDestroyOnLoad(this);
myData = "I didn't get destroyed haha";
}
}
In the new scene:
using UnityEngine;
public class InNewScene : MonoBehaviour {
// Use this for initialization
void Start () {
var saveData = FindObjectOfType<SaveDataHere>();
Debug.Log("instance data is " + saveData.myData);
Debug.Log("static data is " + SaveDataHere.staticData);
}
}
I'm working on the save system for a local co-op game I'm working on. The goal of the code is to establish a Serializable Static class that has instances of the four players and the relevant data they need to store for saving in binary.
[System.Serializable]
public class GameState {
//Current is the GameState referenced during play
public static GameState current;
public Player mage;
public Player crusader;
public Player gunner;
public Player cleric;
public int checkPointState;
//Global versions of each player character that contains main health, second health, and alive/dead
//Also contains the checkpoint that was last activated
public GameState()
{
mage = new Player();
crusader = new Player();
gunner = new Player();
cleric = new Player();
checkPointState = 0;
}
}
The Player Class just contains the ints that track player stats and a bool for if they are in the alive state or not. My issue comes when one of my classes in the game scene needs to get data from this static class. When the static class is referenced, it throws the error.
void Start () {
mageAlive = GameState.current.mage.isAlive;
if (mageAlive == true)
{
mageMainHealth = GameState.current.mage.mainHealth;
mageSecondHealth = GameState.current.mage.secondHealth;
} else
{
Destroy(this);
}
}
I am new to coding so I'm not sure how Unity interacts with static classes that don't inherit from MonoBehaviour. I based this code off of a tutorial that worked pretty similarly, so I'm not sure what the issue is.
Nothing is initialising current.
One quick solution is to initialise current like this:
public static GameState current = new GameState();
This is the Singleton pattern, you can read a about it all over the web, but this post by Jon Skeet is a fairly decent place to start.
I'd consider making the GameState constructor private, and making current (aka Instance normally) into a property with only a getter:
private static GameState current = new GameState();
public static GameState Current
{
get { return current; }
}
There are many more ways to do this, especially if multi-threading is a concern, then you should read the Jon Skeet post.
To give another perspective: If you want to implement that as a static class, then this works differently, starting from referencing the class and its data and not ending with the constructor:
public class GameState {
// not needed here, because static
// public static GameState current;
public static Player mage;
public static Player crusader;
public static Player gunner;
[...]
public static GameState() {
[...]
Of course your methods would reference this static class' static data different now too:
void Start () {
mageAlive = GameState.mage.isAlive;
if (mageAlive == true) {
mageMainHealth = GameState.mage.mainHealth;
mageSecondHealth = GameState.mage.secondHealth;
If you want a (serializable!) Singleton - see DaveShaws answer.