Saving Prefav Data In Unity between scene transition - unity3d

I am making a game where the data to be displayed in the Main Menu is fetched from the API. I store the data in a class and loop through the data to map the data in the Menu.
foreach(var dt in mainMenuDto.data)
{
GameObject _catogeryPrefabTemp = Instantiate(catogeryPrefab);
Vector3 originalSize = _catogeryPrefabTemp.transform.localPosition;
_tabGroup.objectsToSwap.Add(_catogeryPrefabTemp);
_catogeryPrefabTemp.transform.SetParent(catogeryParent.transform);
_catogeryPrefabTemp.transform.localPosition = originalSize;
_catogeryPrefabTemp.transform.localScale = new Vector3(1, 1, 1);
foreach (var item in dt.games)
{
Texture2D image = await (GetRemoteTexture(item.thumbnail));
tex.Add(image);
await _catogeryPrefabTemp.GetComponent<CatogeryInitialise>().InstantiateGame(gameButtonPrefab, item.slug,image);
}
}
Now , this works fine but when I change the scene and come back to The main Menu, the whole mapping process takes place again. How do I change the main menu to be saved even during scene changes.

Use DontDestroyOnLoad
This method will tell unity to keep your menu between scenes. You will be able to see it under separate DontDestroyOnLoad label in your scene hierarchy.

Related

Can't instantiate "scene object" [Unity Mirror Multiplayer]

I am trying to instantiate an object that already exists in the scene so that I can enlarge it and "zoom" in on it, but when I play the game and hover over an object, instead of showing the zoomed card, this error shows up:
Card1(Clone)(Clone) has already spawned. Don't call Instantiate for NetworkIdentities that were in the scene since the beginning (aka scene objects). Otherwise the client won't know which object to use for a SpawnSceneObject message.
NOTE: I am following a tutorial for a 2D Unity Card game with mirror (2019.2.15f1), and I am using a different version of Unity (2020.1.0f1).
First, I am instantiating objects into my game with this script, which never throws any errors.
[Command]
public void CmdDealCards()
{
for (int i = 0; i < 4; i++)
{
GameObject card = Instantiate(playerDeck[Random.Range(0, playerDeck.Count)], new Vector2(0, 0), Quaternion.identity);
NetworkServer.Spawn(card, connectionToClient);
RpcShowCard(card, "Dealt");
}
}
The problem comes when I try to instantiate these objects again later, here:
public void OnHoverEnter()
{
zoomCard = Instantiate(gameObject, new Vector2(Input.mousePosition.x, 540), Quaternion.identity);
zoomCard.transform.SetParent(Canvas.transform, true);
zoomCard.layer = LayerMask.NameToLayer("Zoom");
RectTransform rect = zoomCard.GetComponent<RectTransform>();
rect.sizeDelta = new Vector2(240, 354);
}
I know this is a really late answer, but I came across it while running into the same issue, and maybe in the future, someone will do the same as me, so:
What you've done is attached the wrong prefab to your CardZoom script in the Inspector.
Image of Inspector with Zoom Card attached
What I did, and presumably you also, was to attach Card1 here, which is where it was failing to duplicate the NetworkIdentity

Unity: How to switch scenes and when returning to the original scene, return player to the place they switched their scene

I have two scenes: Main Street & Building Scene
When the player is Main Street, if the player's trigger box touches the building and the player presses "q", the scene would switch to the Building Scene.
I want it so that when the player exits the Building Scene and returns to the Main Street Scene, the player is back to the position they entered the Building Scene that they entered from. Apologies in advance if this doesn't make sense.
sceneSwitchingScript:
public int buildingToLoad;
public Text InputText;
public movement player;
public Vector3 playerPrevPos;
void OnTriggerEnter2D(Collider2D col){
if(col.CompareTag("Player")){
InputText.text = ("[Q] to enter");
if(Input.GetKeyDown("q")){
if (gameObject.tag == "EntryPoint"){
playerPrevPos = new Vector3(player.transform.position.x, player.transform.position.y, player.transform.position.z);
}
//Debug.Log(gameObject.tag);
Application.LoadLevel(buildingToLoad);
}
}
}
void OnTriggerStay2D(Collider2D col){
if(col.CompareTag("Player")){
if(Input.GetKeyDown("q")){
//spawn = new Vector3(player.transform.position.x, player.transform.position.y, player.transform.position.z)
Application.LoadLevel(buildingToLoad);
if (gameObject.tag == "EntryPoint"){
playerPrevPos = new Vector3(player.transform.position.x, player.transform.position.y, player.transform.position.z);
}
}
}
}
void OnTriggerExit2D(Collider2D col){
if(col.CompareTag("Player")){
InputText.text = ("");
}
}
Setting the player's position when they exit the building
public switchScene ss;
void OnTriggerStay2D(Collider2D col){
if(Input.GetKeyDown("q")){
if(col.gameObject.CompareTag("ExitPoint")){
transform.position = ss.playerPrevPos;
}
}
}
However, these two scrips do not work and I'm not sure if this is related but when I make the player do the switch scene thing in-game, this error pops up:
NullReferenceException: Object reference not set to an instance of an object
movement.OnTriggerStay2D (UnityEngine.Collider2D col)
This error message mentions the error on this line:
transform.position = ss.playerPrevPos;
With Unity, the traditional loading of a scene was fairly destructive. There was the concept of setting a GameObject as safe by setting it as DontDestroyOnLoad, which, in a way, removed the object from the scene altogether, which protected it from scene loads.
Unity finally properly implemented multiscene editing and loading. It's essentially DDOL, but done properly.
Now you can actually have multiple scenes loaded at the same time. Now, what that allows you to do is to have a "Manager" scene that handles all of the objects that are common between scenes, and only load (and unload) the specific objects required for that individual scene.
You'd use it like this:
SceneManager.LoadScene("YourScene", LoadSceneMode.Additive);
That would load "YourScene" in ADDITION to the currently loaded scene. Likewise, removing a scene is:
SceneManager.UnloadScene("YourScene");
Now, if you have a Manager scene, you can include in your manager scene a script that holds data for each individual scene. As a hacky example, you might have:
public Vector3 InsideSceneLastPosition { get; set; }
Which you then assign to before loading your outside scene. When you load your inside scene again, you can read InsideSceneLastPosition again to reposition your character.
Here's the link to the LoadSceneAsync page at Unity.
There's more to it than that, for instance, you have to listen for the SceneManager.sceneLoaded event to know when you've actually loaded the next scene, so that you can reposition your GameObejcts. You can find information about that here.
You can see multiscene editing my simply dragging multiple scenes from the Project window into you Hierarchy window. All the scenes listed will additively work together. But you'll have to be careful though, as there's another "gotcha" in that you cant reference objects from one scene to another. You can call scripts cross scene, but you won't be able to drag a game object from one scene, into the object field of a component on a different scene. Don't worry, you'll get the hang on it =)

ARCore + Unity + Augmented Images - Load different prefabs for different Images

How do I assign different prefabs to different images?
right now, I have all my prefabs loading in on top of each other but how do I get it so each prefab loads in only once on top of one image so each image has a different prefab?
I've modified the sample code (the frame corners) to load in my own prefab and used a dictionary to pair the prefabs with images from the database but when the program runs it instatiates all the prefabs in the same place rather than putting one prefrab on each image it puts all the prefabs on every image - this is the code I've been using:
public GameObject obj1, obj2, obj3, obj4;
Dictionary<int, GameObject> imagePrefabPairs;
public AugmentedImage Image;
void Start()
{
instantiateDictionary();
}
// TODO: make initialisation dynamic, to match the size of the db.
public void instantiateDictionary()
{
imagePrefabPairs = new Dictionary<int, GameObject>
{
{ 0, obj1 },
{ 1, obj2 },
{ 2, obj3 },
{ 3, obj4 }
};
}
public void Update()
{
if (Image == null || Image.TrackingState != TrackingState.Tracking)
{
obj1.SetActive(false);
obj2.SetActive(false);
obj3.SetActive(false);
obj4.SetActive(false);
return;
}
Pose _centerPose = Image.CenterPose;
imagePrefabPairs[Image.DatabaseIndex].transform.localPosition = _centerPose.position;
imagePrefabPairs[Image.DatabaseIndex].transform.localScale = new Vector3(0.1f, 0.1f, 0.1f);
imagePrefabPairs[Image.DatabaseIndex].SetActive(true);
}
I figure that I need to have some kind of if statement to ask if one prefab is loaded in and then just choose to load in the next one and have them instantiate one at a time but I am not sure how to do that, or if there is a more direct way to make that happen...?
You could change your AugmentedImageVisualizer prefab, so that at the start all your different objects are inactive (.SetActive(false);) For changing a prefab you can add it to your hierarchy, set all the objects inactive and then apply the changes. After apply you can delete the prefab from your game hierarchy.
Img 1: Add existing prefab to game hierarchy
Img 2: set all the attached objecty to inactive (1) and apply the changes to the prefab (2)
So when you detect a image from your imagedatabase the AugmentedImageVisualizer prefab is attached and only the object with the given index is set to active. Then your code should work.
Because at the start all your objects are inactive you could change this part of your code:
if (Image == null || Image.TrackingState != TrackingState.Tracking)
{
obj1.SetActive(false);
obj2.SetActive(false);
obj3.SetActive(false);
obj4.SetActive(false);
return;
}
so that you only deactivate the active object:
if (Image.TrackingState != TrackingState.Tracking)
{
imagePrefabPairs[Image.DatabaseIndex].SetActive(false);
return;
}
I assign different prefabs to different images by this way:
I modified the AugmentedImageExampleController.cs:.
I added a list for prefabs:
public List<AugmentedImageVisualizer> prefabs = new List<AugmentedImageVisualizer>();
For the related image for the prefab I did a reference by using the image.DatabaseIndex in the visualizer:
visualizer = (AugmentedImageVisualizer)Instantiate(prefabs[image.DatabaseIndex], anchor.transform);
In the inspector of ExampleController you can put in the prefabs (AugmentedImageVisualizer) now.
That's it, and its working fine!

How place two or more 3D assets in ARCore using Unity

I just want to have two buttons #1, #2 on canvas and after plane detection when I select button #1 and tap on screen one 3D asset should be placed and when I click on button #2 and tap on screen second 3D asset should be placed and the previous asset needs to be removed.
At a time one asset needs to be placed and when selecting another asset the previously placed asset needs to be removed.
Did you look at the HelloAR Example? There you have: public GameObject AndyAndroidPrefab; This will be placed on touch. So you can modify that code from the example project to fit your needs.
You can add two public GameObjects where you place your 3D assets and remove the AndyAndroidPrefab asset.
public GameObject obj1_prefab;
public GameObject obj2_prefab;
Then you cann add a bool variable that shows you which button is clicked.
private bool firstBtnClicked = true;
Then you can make two public void functions wich handles the button clicks:
public void firstButtonClick()
{
firstBtnClicked = true;
}
public void secondButtonClick()
{
firstBtnClicked = false;
}
Then you can add the function to your canvas buttons:
Then you have to make changes where you instantiate the object:
//Test if already an object exists and delete it:
if (GameObject.Find("Anchor/myobject") != null)
{
GameObject parentanchor = GameObject.Find("Anchor/myobject").transform.parent.gameObject;
Destroy(parentanchor);
}
GameObject obj = null;
if (firstBtnClicked == true)
{
// Instantiate first Asset model at the hit pose.
obj = Instantiate(obj1_prefab, hit.Pose.position, hit.Pose.rotation);
}
else
{
// Second button clicked: Instantiate second Asset model at the hit pose.
obj = Instantiate(obj2_prefab, hit.Pose.position, hit.Pose.rotation);
}
// Compensate for the hitPose rotation facing away from the raycast (i.e. camera).
obj.transform.Rotate(0, k_ModelRotation, 0, Space.Self);
obj.name = "myobject";
// Create an anchor to allow ARCore to track the hitpoint as understanding of the physical
// world evolves.
var anchor = hit.Trackable.CreateAnchor(hit.Pose);
// Make the object model a child of the anchor.
obj.transform.parent = anchor.transform;
So now if you click button one you select your first object and after a touch on the screen the first asset is placed. When you click on the second button you'll place the second object on screen touch.

Get Children of New Scene

I have a GameManager script which manages loading scenes, putting characters in the scene, reading map information from game objects, and so on. The GameManager script is set to DontDestroyOnLoad.
I'm trying to figure out how to access objects within my new scene from GameManager after a new scene loads. I'm using the SceneManager.sceneLoaded event to run my "scene initialization" code. Here's the event handler:
void OnLevelFinishedLoading(Scene scene, LoadSceneMode mode)
{
// I want to access GameObjects within the newly loaded scene here
//
// SceneManager.GetActiveScene().GetRootGameObjects() returns
// System.ArgumentException: the scene is not loaded
// I want to do something like this
foreach (MapFeature mapFeature in rootObject.GetComponentsInChildren<MapFeature>())
{
// Do something
}
}
I'm want to get the root level GameObject of the new scene, and then use GetComponentInChildren on that root object in order to dynamically grab various components in the scene and store them in GameManager. However, SceneManager.GetActiveScene().GetRootGameObjects() returns System.ArgumentException: the scene is not loaded
How do I get objects from my newly loaded scene within my GameManager? If there's a better method than getting the new scene's root object and using that to get its children, I'm all ears.
This seems to be possible by a workaround, where the sceneLoaded Event starts a coroutine waiting for the next frame. Relevant snippet below.
For reference, I read this thread on unityforums, recently: https://forum.unity3d.com/threads/scenemanager-sceneloaded-event-when-fired-checking-scene-isloaded-false.429659/
void Awake () {
instance = this;
DontDestroyOnLoad (gameObject);
SceneManager.sceneLoaded += OnSceneLoadedWrapper;
}
void OnSceneLoadedWrapper(Scene scene, LoadSceneMode mode) {
StartCoroutine ("OnSceneLoaded");
}
IEnumerator OnSceneLoaded(){
yield return new WaitForEndOfFrame ();
Scene scene = SceneManager.GetActiveScene ();
int count = scene.GetRootGameObjects ().Length;
string name = scene.GetRootGameObjects ()[0].name;
Debug.LogFormat ("{0} root objects in Scene, first one called {1}", count, name);
}