This question already has answers here:
How to pass data (and references) between scenes in Unity
(6 answers)
Closed 1 year ago.
I'm new to working in unity and i don't quite understand how to transfer data between scenes. In my case it's the sprites for my inventory when the player picks up an item. here is the code for the items sprites etc.
public class Items : MonoBehaviour
{
public string ItemName;
public string ItemText;
public int itemInList;
public Sprite itemSprite;
public InventorySlot inventorySlot;
public ItemSprites itemSprites;
public Sprite blueberrySprite;
public Sprite cheesecakeSprite;
public Sprite whitebreadSprite;
public Sprite cookiesSprite;
public void Awake()
{
//DontDestroyOnLoad(this);
}
public void gettingID()
{
GameObject Go = new GameObject();
Go.AddComponent<InventorySlot>();
inventorySlot = Go.GetComponent<InventorySlot>();
inventorySlot.itemID = itemInList;
}
public void ItemList()
{
// Cheesecake
if(itemInList == 1)
{
ItemName = "Cheesecake";
ItemText = "A delishious Juicy Cheesecake";
itemSprite = cheesecakeSprite;
}
// White Bread
if (itemInList == 2)
{
ItemName = "White Bread";
ItemText = "a basic white loaf that smells amazing";
itemSprite = whitebreadSprite;
}
// Cookies
if (itemInList == 3)
{
ItemName = "Cookies";
ItemText = "just plain Chocolatechip cookies, still delishious though";
itemSprite = cookiesSprite;
}
// Blueberries
if (itemInList == 4)
{
ItemName = "Blueberries";
ItemText = "Big juicy yummy blueberries!";
itemSprite = blueberrySprite;
Debug.Log(ItemText);
}
}
it works just fine when I'm in the scene where the script exist but when I try to use it in my new scene the image will turn out white(blank). I guess that is because the gameobject is not set anymore in the new scene, but how do i transfer the set gameobject sprite to a new scene? I now that you can make stuff static but if I read correctly then it doesn't work on gameobjects. and Dontdestroyonload doen't seem to work either cus the script is not attached to a gameobject in the scene.
a) You can make your sprite a static property in your class and you should be able to access it from anywhere in unity. You can make a simple DataHolder class to hold this value and other static values in your Game (It need not be a MonoBehaviour). However, you will need to load them somehow (From resources or AssetBundles) into your static Array
b) Check my Answer here on how to properly persist a GameObject in the Game if you are using DontDestroyOnLoad and have it attached to a GameObject in the scene. This should be a good option to use, considering you already have a MonoBehaviour class. Just attach it to a GameObject and Follow the answer in the link to maintain one correct instance of the Component.
c) Consider using a simple ScriptableObject if you want to save these values across multiple Game Sessions. It is a simple data container that helps you save data Independent of Classes.
Follow this link for Unity's Documentation on ScriptableObjects
Related
I am creating a digital boardgame, which consits of muliple boards, between which the gamepieces are moved by the players.
The script of the game board needs a 2D array of positions to know where to move a gamepiece when it recives one.
Currently to mark the positions on the game boards, I added placeholder gameobjects to the prefab and named them "spawnpoint\d".In the Awake() method I use Transform.Find() to search for those gameobjects. Then, after I save their positions I call Destroy() on them, so they do not show up in the game.
I see two problems:
This is done for all Instantiated game board, altough the positions are the same on all of them.
I read that using Transform.Find() is heavily discourged by the experts in the community.
I wish to store the spawnpoint positions in a static array, so all instances refer to the same data. Furthermore I wish to easily modify these positions in the editor with visual help.
I tried serializing static members, but those do not show up in the editor to be able to modify.
[SerializeField]
public static int TestNumber;
TLDR:
How to make static members visually changeable from the Unity editor?
tl;dr you can't, static fields are not serialized.
You can do e.g.
[SerializeField] private Transform[] spawnPoints;
public static Transform[] SpawnPoints;
private void Awake ()
{
SpawnPoints = spawnPoints;
}
In general I would suggest rather using something like this:
// Simply attach this class to each GameObject that shall be a spawn point
// MAKE SURE IT IS ACTIVE AND ENABLED BY DEFAULT
public class SpawnPoint : MonoBehaviour
{
// Each SpawnPoint (un)registers itself here
private static readonly HasSet<SpawnPoint> _instances = new HashSet<SpawnPoint>();
// For the public return a new HashSet to make sure nobody can modify the
// original _instances from the outside
public static HashSet<SpawnPoint>() Instances => new HashSet<SpawnPoint>(_instancea);
private void Awake()
{
// Register yourself to the existing instances
_instances.Add(this);
// Optional: make sure this object is not destroyed when a new scene is loaded
DontDestroyOnLoad (gameObject);
// simply hide the entire gameObject
gameObject.SetActive(false);
}
private void Destroy ()
{
// Unregister yourself from the instances
_instances.Remove(this);
}
}
This way
each spawn point Auto-Registers itself to the Instances so you don't even need to serialize this via the Inspector -> you also can't forget any
the spawn points don't get Destroyed when a new scene is loaded (if you use the DontDestroyOnLoad - otherwise they are destroyed and auto-removed from the instances)
you disable the objects (though actually if they have nothing attached except this script it wouldn't matter anyway)
you can easily access all the spawn points without using expensive stuff like Find or FindObjectsOfType but rather simply via the property
var availableSpawnPoints = SpawnPoint.Instances;
I have made a scriptable object which has a one property a public sprite. I want to have one version of sprite added everywhere I want.
With the possibility that if I ever want to change it I will just pin another sprite to scriptable object and in every place where it is pinned the sprite will change.
Is there any way to walk thought this problem or any other idea to make one object where I will store one Sprite and pin it to multiple objects.
Finally is this concept possible in Unity?
I have already thought about:
Deriving prom Sprite Renderer but the class is sealed, so i cannot make my own version of it this way
Creating my own custom version of Sprite Render cause if an any update come I would have to make yet again another version.
I think you can use prefabs to achieve the same thing as you need:
You can create prefab by dragging and object to your assets.(Your prefab will be just an object with transform and e.g. Sprite Renderer)
After creating prefab you can copy it as many times as you want.
Than when you decide to change the sprite just simply go to your prefab and edit sprite there. Now every instance of your prefab will have sprite changed.
It is possible with C# events. First, make your ScriptableObject call event whenever sprite is set. Allow Sprite to be set only using property (or method), so that you could track the change, like this:
public sealed class SpriteObject : ScriptableObject
{
public event Action SpriteChanged;
[SerializeField]
private Sprite _sprite;
public Sprite Sprite
{
get { return _sprite; }
set
{
if(_sprite == value)
return;
_sprite = value;
SpriteChanged?.Invoke();
}
}
}
Then, you need script, that will react to changed and assign the changed sprite to SpriteRenderer. So, something like this:
NOTE: name is purely for example. Do NOT name your classes like this!
[RequireComponent(typeof(SpriteRenderer))]
public class ScriptThatUsedSpriteObject : MonoBehaviour
{
public SpriteObject spriteObject;
private SpriteRenderer spriteRenderer;
/// Called once the script is created.
private void Awake()
{
spriteRenderer = GetComponent<SpriteRenderer>();
if(spriteObject != null)
{
spriteRenderer.sprite = spriteObject.Sprite;
spriteObject.SpriteChanged += HandleSpriteChanged;
}
}
/// Called whenever sprite is changed inside SpriteObject.
private void HandleSpriteChanged()
{
spriteRenderer.sprite = spriteObject.Sprite;
}
}
Now, if some script changes sprite in SpriteObject, every ScriptThatUsedSpriteObject will automatically update their sprites.
public class PublicSprite : MonoBehaviour {
public Sprite publicSprite;
#region Singelton
public static PublicSprite instance;
private void Awake()
{
if (instance != null)
{
Debug.LogWarning("More than one instance found");
return;
}
instance = this;
}
}
get the sprite like this:
PublicSprite.instance.publicSprite;
as Mentioned here, you probably want to use a Singelton. It is best to avoid the use of them only if its necessary as its a very bad programming practice. (you can learn more on why to avoid them here: What is so bad about singletons?
However, I can't think of another way of doing what you seek for.
This question already has answers here:
How to pass data (and references) between scenes in Unity
(6 answers)
Closed 4 years ago.
I am creating a simple number guessing game in unity3d.
I want to open a new scene on button click and change the text of a text element present in the loaded scene from the current scene.
I have been able to open new scene on button click but how can i access the text element in other scene so that i can change its text from the current scene.
This is what i have so far but it obviously throws NullReferenceException because i can't access the text element in another scene from current scene.
SceneManager.LoadScene("End Scene");
gameResultText.text = "You Won!!"; //<-------this line throws the exception
gameResultText.color = Color.green;
Better solution I came up with:
Make a script that sets a static string variable. This script must be in your Game scene and will hold the result.
public class ResultTextScript : MonoBehaviour
{
public static string ResultText;
void EndGame(){
if (won){ //if won game
ResultText = "You won!"
}
else //if lost game
{
ResultText = "You lost, try again another time!"
}
//Change scene with SceneManager.LoadScene("");
}
}
Put this script on your result text in the end scene. This script will retrieve the result and display it.
Using UnityEngine.UI;
public class EndGameDisplayResult{
Text text;
OnEnable(){
Text.text = ResultTextScript.ResultText
}
}
This way, it will set the text as soon as the new scene is loaded.
Previous/alternative method:
If you already have the scene open, one option would be to add a script to the "You won!" text which holds a static variable with a reference to itself.
So like this.
public class ResultTextScript : MonoBehaviour
{
public static ResultTextScript Instance;
void Awake(){
if (Instance == null)
Instance = this;
}
}
Then you can call the reference to that GameObject from anywhere in the other scripts, including between scenes.
Like this ResultTextScript.Instance
Note though that you cannot call the reference in the Awake method, as that is where the variable is initialized, you can use it after the Awake methods have been called though.
Basically
Add the ResultTextScript to your Text object in the 'End Scene'
Open the 'End Scene' from the 'Game Scene', for example with your SceneManager approach as you already do.
Ensure that the End Scene has loaded, then say in the script you wish to change the text gameObject go = ResultTextScript.Instance.gameObject
I do not believe there is a way to modify the context or objects of a scene that is not currently open.
public class GameResults {
public static string finalText = "";
}
In your function where you are loading the scene, right before you call load scene you can access that text like so:
GameResults.finalText = "You Win!";
or
GameResults.finalText = "You Lose!";
load your scene, and on your text object give it a script like this:
using UnityEngine;
using UnityEngine.UI;
public class ResultTextScript : MonoBehaviour
{
public Text textResults;
void Start() {
textResults = getComponent<Text>();
if(textResults != null) {
textResults.text = GameResults.finalText;
}
}
}
There are other things you can use as well is, storing the game results in PlayerPrefs and loading the string or int you stored in PlayerPrefs preferences at the start of your end scene. This will help you avoid creating an unnecessary class or static variable.
So Like before you can do:
PlayerPrefs.SetString("GameResults", "You Win!");
or
PlayerPrefs.SetString("GameResults", "You Lose!");
load your scene, and on your text object give it a script like this:
using UnityEngine;
using UnityEngine.UI;
public class ResultTextScript : MonoBehaviour
{
public Text textResults;
void Start() {
textResults = getComponent<Text>();
if(textResults != null) {
textResults.text = PlayerPrefs.GetString("GameResults", "");
}
}
}
I am struggeling to come up with a smart way to deal with cutscenes in my 2D game. I think I have decided to Create/Control the cutscenes using the Animator, and moving the actual GameObjects around.
What I am struggeling with is:
This approach means each GameObject needs it's own Animator. So how do I then trigger the correct Cutscene? I am going to use game stages to control the game and its progress, so for example:
if(gameStage == 34 && entering scene castle)
{
playCorrectCutscene();
}
How and where do I keep references to all my cutscenes? Say I have 22 NPCs in a scene, and 15 of those have their own Animator since they have a cutscene, how do I play the Animation from NPC_11?
I guess what I am looking for is some sort of "Cutscene Manager". The best possible solution would be to have a List of every cutscene (animation) with an ID. Then I could just use the ID to play the correct animation. But since each Animation is stored in the actual GameObject I have no idea if that is possible.
Hope Im making sense.
Once you create complete prefab with 1 Animator per prefab, then you can use bellow method to create instances on the fly:
Let say you have CutsceneManager script like this:
class CutsceneManager : MonoBehaviour {
public GameObject InstantiateResource(string folder, string name)
{
GameObject resource = Instantiate<GameObject>(Resources.Load<GameObject>(folder + "/" + name));
if (resource == null)
{
Debug.LogErrorFormat("Cannot find resource {0} in {1}", name, folder);
return null;
}
resource.name = name;
return resource;
}
}
The easiest way to use it is to attach it to Empty object. Then you can create public variable for manager in other scripts, where you would need to show cutscene, like this:
class SomeOtherScript : MonoBehaviour {
public CutsceneManager CutsceneManager;
}
This will let you drag&drop CutsceneManager empty into each script where you need it. This way you have 1 instance of cutscenemanager for all classes.
Now, in place where you would need to play custscene, you instantiate:
class SomeOtherScript : MonoBehaviour {
public CutsceneManager CutsceneManager;
public void PlayMyCutscene() {
GameObject cutscene = CutsceneManager.InstantiateResource("Cutscenes", "SomeOtherCutscene");
cutscene.setActive(true); // Or whetever other method to fire it off
}
}
On folder structure you would need to have: Assets\Resources\Cutscenes
Cutscenes would be called: SomeOtherCutscene.prefab
Notice there is no need to include .prefab when you are instantiating one - Unity framework will "know" and add it for you.
I'm trying to make a Pinata GameObject, that when clicked bursts and gives a variable number of Gift GameObjects with various images and behaviors in them.
I'm also not sure what the unity vocabulary for this is so as to look this up in unity docs.
Can anyone please lend me a hand here? Thanks!
There are several ways to handle this.
The simple way is to use Object.Instantiate, Object Instantiation is the vocab you're after.
This will create a copy of a predefined Unity object, this can be a gameobject or any other object derived from UnityEngine.Object, check the docs for more info https://docs.unity3d.com/ScriptReference/Object.Instantiate.html.
In your case, your Pinata would have an array, or list, of prefabs. These prefabs are created by you with a certain behaviour and sprite for each one. When the Pinata bursts, you instantiate random prefabs at random positions surrounding the Pinata, up to you how to position these objects.
Something along these lines should do the trick:
class Pinata : Monobehaviour
{
public GameObject[] pickupPrefabs;
public int numberOfItemsToSpawn; //This can be random
//any other variables that influence spawning
//Other methods
public void Burst()
{
for(int i = 0; i < numberOfItemsToSpawn; i++)
{
//Length - 1 because the range is inclusive, may return
//the length of the array otherwise, and throw exceptions
int randomItem = Random.Range(0, pickupPrefabs.Length - 1);
GameObject pickup = (GameObject)Instantiate(pickupPrefabs[randomItem]);
pickup.transform.position = transform.position;
//the position can be randomised, you can also do other cool effects like apply an explosive force or something
}
}
}
Bare in mind, if you want the game to be consistent, then each behaviour prefab would have there own predefined sprite, this would not be randomised. The only thing randomised would be the spawning and positioning.
If you did want to randomise the sprites for the behaviours then you'd have to add this to the Pinata class:
public class Pinata : Monobehaviour
{
//An array of all possible sprites
public Sprite[] objectSprites;
public void Burst()
{
//the stuff I mentioned earlier
int randomSprite = Random.Range(0, objectSprites.Length - 1);
SpriteRenderer renderer = pickup.GetComponent<SpriteRenderer>();
//Set the sprite of the renderer to a random one
renderer.sprite = objectSprites[randomSprite];
float flip = Random.value;
//not essential, but can make it more random
if(flip > 0.5)
{
renderer.flipX = true;
}
}
}
You can use Unity random for all your random needs, https://docs.unity3d.com/ScriptReference/Random.html
Hopefully this'll lead you in the right direction.