Setting Variable to an Instance on disk without calling AssetDatabase.LoadAssetAtPath - c#

Let's say I have two classes, both of which inherit from UnityEngine.ScriptableObject:
-CutScene
-AnimationCollection
The CutScene class holds the dialogue tree, camera cuts, has a public AnimationCollection field. The Animation Collection holds the actual animation data for animations used in a given cut scene.
In our pipeline, CutScene instances are generated by an export process, which saves them as unity .asset files.
An animator uses a unity editor tool to put animations into an AnimationCollection, then saves it out as a unity .asset file. Then, using the unity editor, the animator drags the asset file for the AnimationCollection into the AnimationCollection field for the CutScene that uses it.
When the CutScene is changed and exported, the export code will overwrite the existing .asset file for that CutScene with a new one. We do want to retain the reference to the AnimationCollection that the animator set up. Currently this is done with code like this:
cutScene.AnimationCollection = AssetDatabase.LoadAssetAtPath(pathToAnimCollection, typeof(AnimationCollection)) as AnimationCollection;
The problem is that loading the AnimationCollection can take a long time (12s for some of our bigger cut scenes). If I know the path to the AnimationCollection, can I somehow set the AnimationCollection field of the CutScene without having to actually load the AnimationCollection into memory? The AssetDatabase.AssetPathToGUID seems promising, but I'm not sure what I can do with a GUID string?

In C#, when you set a field's value, one of two things will happen.
If it's a reference type (i.e. a class), a reference to it in memory will be stored. This is a small number that points to a unique location in memory where that item is loaded.
If it's a value type (i.e. structs, int, float, etc), the actual value is stored.
In your case, you're setting an animation collection (a reference type) from an asset loaded by Unity. For your system to work you have to have loaded that whole asset into memory first, so that you can reference it. This means you cannot use GUIDs, or file paths without changing your cutscene system.
To make your system work without loading the files in the interim, consider storing the file path to the animation collection in the cutscene, instead of the animation collection itself. Then load that path when the whole cutscene is loaded.

Related

An assigned variable in Unity 3D becomes null during code even though nothing is done to it directly

I have a building grid system in Unity that is based on the TileMap system that already exists. I have systems for placing objects by clicking, and randomly generating objects that also get placed. Placing an object paints the tile a different color to indicate that it's placed.
I'm trying to make a system where you can remove placed objects, which means that the tilemap also needs to be updated to reflect the removal. This doesn't work at all, and creates some issues that I have never encountered and I have no idea how to solve.
I have a function that paints the tiles, and it goes like this:
public void TakeArea(Vector3Int start, Vector3Int size)
{
MainTilemap.BoxFill(start, tileName, start.x, start.y, start.x + size.x, start.y + size.y);
}
This all works for placing, but when I try to do it using an OnClick function, I get some errors. The "start" and "size" are passed on correctly, and when printing the values for the area, I get the correct ones. The problem is with the "tileName" variable this time, as it randomly becomes "Null" when I try to print it, even though in the game itself I an see that the assigned value is not "Null", but it references a tile that should be painted.
I have tried so many things for a couple of hours, but I think I finally found a solution. I made some adjustments to that function so that it's exactly the same as it is when an object is generated, as that already works, so I'm first Instantiating the object with this code:
GameObject oreObj = Instantiate(prefab, poss, Quaternion.identity);
Then I will get the object position and size from there the same way I would as if I was placing the object, so it would be like this:
Vector3Int start = gridLayout.WorldToCell(oreBeingRemoved.GetStartPosition()); TakeAreaWithTile(start, oreBeingRemoved.Size, emptyTile);
When I do all this, I get an error that says
"The variable 'grid' of 'BuildingSystem' has not been assigned. You probably need to assign the grid variable of the 'BuildingSystem' script in then inspector."
The problem is that the variable is assigned. The "grid" is always there in the editor, just like that tile that I talked about was, but it says that it isn't for some reason.
Here's a screenshot:
I have tried rewriting the code so many times, I spent about 10 hours here but nothing seems to be working.
As said in the comments the lines you show do not contain the assignment of the variables or what happens to them in between. Nor does it show how your object is instantiated.
One thing that could be your problem: Prefabs can only be serialized with references to themselves or other Prefabs. References to other objects in the scene will get lost.
A fix would be a method that assigns your grid reference in the Awake method of your BuildingSystem class.

Destroying Instantiated Clone Components Breaks Original Game Object Components?

Maybe im missing something obvious but to keep it short. I have working code for a character. When you "dash" I want to leave behind an after-image type effect, by cloning the player, removing its unneeded components and then applying a shader. Issue is as soon as I instantiate the clone the original player stops functioning (cant move etc). It still has all its components and everything as normal, and the clone does get the correct components removed. But it still breaks. As soon as I remove that line, its back to normal. Any ideas?
This is the only relevant code in the script that instantiates the clone.
private void DodgeEffect()
{
GameObject _DodgeSFX = Instantiate(gameObject, transform.position, transform.rotation );
Destroy(_DodgeSFX.GetComponent<PlayerController>());
Destroy(_DodgeSFX.GetComponent<PlayerCombat>());
Destroy(_DodgeSFX.GetComponent<WarpController>());
Destroy(_DodgeSFX.GetComponent<Animator>());
}
its because you are making a copy of gameObject. while Instantiate() returns a GameObject, it also returns whatever you put into the first section of the method. instead, make a seperate gameObject than the player in the editor, and make _DodgeSFX public so you can put the copy into the slot. Then, just instantiate that seperate GameObject and you wont have to destroy the components through script(because you remove the components in the editor), saving time
Ok so from some testing I think its down to the Unity.InputSystem only allowing 1 instance of each Input to be enabled at once. When I instantiated a clone of the player, it still goes through the Enable/Awake functions before those components are destroyed, and since the Inputs on that clone were never disabled, that becomes the "main" one. If I set the main player's scripts with Inputs deactive and then active again, it all works as normal. Note that the script was still working on the main character just fine, it was only the inputs that was broken. Im still not sure why this is the case, if its a limitation of the input system, or intentional, cant seem to find documentation of this experience anywhere.

Loading scene in Unity makes the references in that scene null

I am making my first game in Unity and I'm trying to load the first level of it when the cutscene at the start ends. I don't know if it's possible to make the script do something after a video clip ends, so I wrote my code like this:
using UnityEngine;
using System.Collections;
using UnityEngine.SceneManagement;
public class CutsceneEnd : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
StartCoroutine("wait");
}
IEnumerator wait()
{
yield return new WaitForSeconds(36);
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex + 1);
}
}
But the problem is not with my method of waiting for the end of the video, it's with the scene it loads. I can't move my character because all the references in the scripts are null. I have no idea what I did wrong.
Unity references should be stored in the scene file. Are you using any source control like git? If someone else did not push the changes to the .unityscene file or the .meta files for the associated scripts/prefabs, it might break the references.
I think we need some more information here.
If you referring to references (public variables) to Assets/GameObjects in the loaded scene, then you may have simply never placed the references in the first place. Unity's public variables should be saved within the Scene file and should always load with the Scene.
Double check to see if the references are actually null. Are you using an animation when you load into the Scene? This can keep you from being able to manual move anything. For example, if you animate the character when loading the scene, it could get stuck in the animation clip and you won't be able to move it.
Lastly, if you are referring to references created within the code to other scripts, objects, or variables, then these may break between scenes if you don't utilize 'DontDestroyOnLoad'.
Just spit-balling here, I need more information to correctly find the solution.

How to pass a variable to a script in another scene

I have a main menu for a game where you can select difficulties, easy, normal etc.
Currently, I load a separate scene for each difficulty, I have a scene for easy, a scene for normal, hard, etc.
I know you can pass variables between scenes, such as
something.GetComponent<someScript>().someVariable
But how do you pass a variable to a script on a GameObject that doesn't exist yet?
You could use PlayerPrefs to solve this issue. For example, in your script on your main menu where the difficulty is set you would say:
PlayerPrefs.SetString("Difficulty", "Medium");
Then in your next scene, when you need to access that variable on a different script, simply use:
String difficulty = PlayerPrefs.GetString("Difficulty");
You could also look at using DontDestroyOnLoad to keep track of your variables on a persistent script which stays with you on an object that remains loaded even as scenes change. Then you could access the variables you need similar to how you have already described.
To have the variable available in the next scene, the standard way is to have a game object that doesn't get destroyed when the scene changes. You do this by calling DontDestroyOnload() in any script on that game object, e.g.
void Awake () {
DonDestroyOnLoad(gameObject);
}
You can use an existing game object or create one just for this purpose, with a single script that calls DontDestroyOnload() and also has variables that you want to pass to the next scene. You can set the variables, before loading the next scene, like in your example code:
someGameObject.GetComponent<GameVariablesScript>().someVariable = someValue;

How to find all assets of a type?

In my Unity project a have several instanced of the class ArmourType (these are assets, scriptable objects). I'm trying to show these in a dropdown list in the inspector, and this works. However, I use
List<ArmourType> armourTypes = Resources.FindObjectsOfTypeAll<ArmourType>();
to find all these instances. This only finds the objects that are loaded into memory, so occasionally it only finds some of the required assets. This is documented, so isn't a bug, but very annoying at times.
So my question is, is there a different way of getting all these assets that does return those that aren't loaded into memory? Or perhaps is there a way to make Unity load the assets when they are looked for?
Note: I'm using Unity5 and c#.
Is this what you are looking for?
string[] guids = AssetDatabase.FindAssets ("t:ArmourType", null);
foreach (string guid in guids) {
Debug.Log (AssetDatabase.GUIDToAssetPath(guid));
}
http://docs.unity3d.com/ScriptReference/AssetDatabase.FindAssets.html
AssetDatabase is an Editor script. If you want to do a similar thing in-game place the relevant scripts in a Resources folder (this ensures they will be included in the build even if not linked in the scene) and use:
Resources.LoadAll<ArmourType>("");

Categories