I have a script with a public UnityEvent and I am trying to drag a scene object to the object slot so I can access its methods but its not accepting the object. I'm not sure what i'm doing wrong.
The scene object is a prefab instance and I did try unpacking the prefab but it didnt make a difference.
In a comment you mentioned the script you are talking about is a StateMachineBehaviour.
StateMachineBehaviour does not inherit from MonoBehaviour but rather from ScriptableObject
ScriptableObject instances "live" in the Assets not a certain Scene
You (usually) can not have any Scene references in any assets like Prefabs or ScriptableObjects.
There are some workarounds however that still allow you to do that. You can e.g. create ScriptableObject containers for every value you want to pass. Something like e.g.
[CreateAssetMenu]
public class GameObjectReference : ScriptableObject
{
public GameObject gameObject;
public void SetActive(bool value)
{
if(gameObject) gameObject.SetActive(value);
}
}
such an instance of GameObjectReference now is a ScriptableObject and thus also "lives" in the Assets => you can reference it in any other asset as Prefabs and other ScriptableObjects.
So all you need to do is make sure that you set this value from within your scene e.g. using
[ExecuteInEditMode]
public class GameObjectReferenceSetter : MonoBehaviour
{
public GameObjectReference gameObjectReferenceAsset;
private void Awake()
{
gameObjectReferenceAsset.gameObject = gameObject;
}
}
You can create such a reference-asset and setter pair for each type you need and transpass the required public methods so you can call them from the UnityEvent.
Having the [ExecuteInEditMode] this should also be set already in EditMode. However since "officially" it is still not possible to have a scene reference on a ScriptableObject field the value usually says Type missmatch but references the correct object as you should see when you click on the field.
Another alternative is using some kind of dependency-injection (for Unity an often mentioned and free solution is e.g. Zenject). It is a bit complex to set it up the first time but once you got it working it is more flexible and better scaleable since you wouldn't need to implement a wrapper for each type you want to pass in to a StateMachineBehaviour.
More information and how-tos about Zenject can be found on their github page
Related
I'm trying to create a static GameAssets class where I can drag into it's references my Prefabs in order to manage every GameObject of my game.
The problem I have here is that when I start the game, the instance of my GameAssets is null (which I don't want) and it's instantiating a clone of GameAssets without the references linked to it.
Code of the GameAssets class
public class GameAssets : MonoBehaviour
{
private static GameAssets _i;
public static GameAssets i
{
get
{
if (_i == null)
_i = Instantiate(Resources.Load<GameAssets>("GameAssets"));
return _i;
}
}
public GameObject ProjectileLaserBall;
}
Hierarchy & Inspector
We can see that I have an empty GameObject called GameAssets with prefabs linked to it's references already!
How can I make Unity understand to use the existing GameAssets instead of creating a clone of it without it's references?
(As asked in my Script, a clone is created)
Clone of class
In this case as shown in your hierarchy image, you don’t need to create a new instance of your class. You already have an instance in your scene.
As such, when you start the game, you want the static reference to point to the already instantiated class that Unity already created after the scene was loaded.
The code would then look like:
public class GameAssets : MonoBehaviour
{
private static GameAssets _i;
public static GameAssets i
{
get
{
if (_i == null)
_i = this;
return _i;
}
}
public GameObject ProjectileLaserBall;
}
Your code does make use of the Resources folder. This make me think that you’ve also got a prefab if this GameAssets item in there as well. In this case, maybe your intention was to not define a GamesAssets object in your scene, but to load one in at runtime.
With prefabs, you can’t add references to scene objects (in the common sense), but you can add references to other prefabs. So, if you want to do it that way instead, make sure that your ProjectileLaserBall is also a prefab, instantiate the GameAssets object, add make sure you don’t already have a GameAssets object in the scene (as a new one is going to be created). The code for this class would still be the same as I’ve shown above, but you’d instantiate the asset from a different class altogether. If you’ve done everything correctly, you should have a single GameAssets item in the hierarchy that can be accessed by GameAssets.i
I found a way around that problem, still it is not solving it.
So instead of including my GameAssets directly into the scene, I leave it in the project folder and assign the references in the prefab GameAssets.
This works as it is creating a clone of GameAssets with the references when I'm calling it.
The problem here is that when I call it from a script, because it is not instantiated, the function doesnt show after GameAssets.i.
And we can't get the instance of a static class with the 'this' keyword so I really don't know how to get this to work properly
I have the following class
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PixelSystem
{
public GameObject pixelPrefab;
public Texture2D[] style;
public List<Chunk> chunks;
public GameObject mGameObject;
public PixelSystem(GameObject _mGameObject)
{
mGameObject = _mGameObject;
chunks = new List<Chunk>();
}
}
Chunk and Pixel are structs but I don't think they are relevant.
I would like pixelPrefab and style to be serialized but cannot seem to be able to make that work. This script is not attatched to a GameObject.
I have looked into getting the assets at runtime with Resources or Addressables but I would like to be able to set these fields from the editor.
I have tried making the class inherit from MonoBehaviour and having [SerializeField] public GameObject pixelPrefab; and [SerializeField] public Texture2D[] style;, but the result of this is that pixelPrefab is settable from the editor but instead of showing style it shows mGameObject, for some reason... ¯\_(ツ)_/¯
Edit: Turns out that the [SerializeFields] do nothing here, the result is the same without them.
I've also tried puting [System.Serializable] before the class, but as far as I can tell this does nothing at all.
Someone plz help.
Edit:
Due to some misunerstandings I would like to clarify a few things:
I have the prefab asset already, it is in my assets folder not in my scene, creating the assets is not the problem.
pixelPrefab would work fine as a static property, however, style would not.
Style only needs serializing for a default value and so is not a priority.
This code is very incomplete, the constructor is incomplete, I want an answer to this qusetion before I spend time writing code that may be unusable.
I want to select the assets in the editior
I do not want to have a function that must be called before the classes are used, so that the assets are loaded
I do not want to load the assets each time they are referenced, so no get {load();}
This is what I want to see in the editor when I select my script:
And this is the closest thing to what I want so far:
Which I get by setting the class to inherit from MonoBehaviour, I'd rather not inherit from anything, and idk if you are meant to have MonoBehaviour classes that aren't attatched to gameObjects, but if this is necessary then so be it.
complete .cs file here
A few things here. I'm going to assume this point is the most important:
I want to select the assets in the editior
So, in order to be able to do this, every field you want to see has to be able to be serialized by Unity and also Unity needs to be able to draw the GUI for that thing. You said,
I would like pixelPrefab and style to be serialized but cannot seem to be able to make that work.
and again here I'm taking "serialized" to really mean that you just want those fields exposed in the Editor so you can stick things into them. So when you give me the following snippet I immediately see several issues:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PixelSystem
{
public GameObject pixelPrefab;
public Texture2D[] style;
public List<Chunk> chunks;
public GameObject mGameObject;
public PixelSystem(GameObject _mGameObject)
{
mGameObject = _mGameObject;
chunks = new List<Chunk>();
}
}
The first is that PixelSystem itself isn't serialized. You either need to tag the class with [System.Serializable] to use Unity's serializer or you need to inherit from something that Unity already knows should be serialized, like Component or MonoBehaviour (MonoBehaviour inherits Component). The issue with Component and subclasses is that they need to exist on a GameObject, so you can't new a Component, you have to .AddComponent<YourComponent>().
Now as you've discovered, when you have your script inherit MonoBehaviour, you see
that pixelPrefab is settable from the editor but instead of showing style it shows mGameObject, for some reason... ¯\_(ツ)_/¯
And the reason it's showing mGameObject can be seen in your class' member variables:
public GameObject pixelPrefab;
public Texture2D[] style;
public List<Chunk> chunks;
public GameObject mGameObject;
You can see that pixelPrefab is a public GameObject. Great! (1) your class is inheriting MonoBehaviour, so Unity knows to serialize it, (2) pixelPrefab is public, so the Editor knows to show it, and GameObject is itself a serializable object that Unity knows how to draw, so the end result is that Unity adds pixelPrefab to the Editor GUI for your class.
Then you get to style, which is public, so the Editor knows it should show it, but it's an array and Unity doesn't know how to draw the Editor GUI for an array. It's an array of Texture2D, which it does know how to draw, but it doesn't know how to handle the array aspect, so it doesn't add style to your GUI.
Then you get to chunks, which is public, so the Editor knows it should show it, and it's a List, which Unity does know how to handle, but it's a List of Chunk, which I'm betting is not inheriting MonoBehaviour and is also not tagged with [System.Serializable], so Unity doesn't know that it's supposed to be serializing Chunk. This means that it does know it's supposed to be drawing a list, but it think it's not supposed to be drawing any contents for the list, so it skips the list completely.
Finally you get to public GameObject mGameObject, which gets drawn for all the same reasons that pixelPrefab gets drawn, and finally you get what you see in the Editor in your last screenshot: Only pixelPrefab and mGameObject are drawn in your class GUI:
So, what to do if you want to view your class in an Editor pane and you also want to see pixelPrefab and style?
Have something that can be serialized, like a MonoBehavior, hold a reference to PixelSystem OR have PixelSystem inherit MonoBehavior.
If PixelSystem is not a MonoBehavior then it needs to have [System.Serializable] on the line above public class PixelSystem.
Do what you've already done and have public GameObject pixelPrefab.
Convert style from an array to a list so Unity knows how to draw it.
You should wind up with the following:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public class PixelSystem
{
public GameObject pixelPrefab;
public List<Texture2D> style = new List<Texture2D>();
public List<Chunk> chunks;
public GameObject mGameObject;
public PixelSystem(GameObject _mGameObject)
{
mGameObject = _mGameObject;
chunks = new List<Chunk>();
}
}
You can also have Chunk be serialized and shown in your Editor pane if you tag that class with [System.Serializable], and you only need the [SerializeField] tag for fields that aren't already public. You can still add that tag to public fields just like you can add [System.Serializable] to a MonoBehaviour but it's redundant. Not required.
When I put this script into the object, a cube, it does not register the platform it hits beneath when I make the gameObject the platform. I'm trying to get the two objects to detect collision between each other but it does not seem to work. Neither objects are trigger checked in the Box Colliders. The cube has a rigid body with gravity checked but the platform does not. This is a 3D game.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Collision : MonoBehaviour
{
public Transform gameObject;
void onCollisionEnter(Collision col)
{
if (col.gameObject);
{
Debug.Log("Yes");
}
}
}
As Sven Viking already said, you have to change the syntax from onCollisionEnter to OnCollision enter. Also, let's take a look at your logic:
You want to check that, if your cube object collides with another object, and if that object is the same as the one you're referencing, you want to see a log of that in your console. Currently, however, you're asking for something else.
First of all, your platform is not stored as a gameObject, it's stored as a Transform. Those two are different things, the Transform stores the position/rotation/scale of an object, while 'GameObject' is the entire object, includng its Transform. Also, you named your variable gameObject, which you should never do - Never name variables with the same name as existing elements.
'col' represents the collision. col.gameObject is the gameObject of that collision. There's no logic demanded there. You're just making a reference. What you want, instead, is compare the gameObject of the collision with your referenced gameObject(the platform) and, if both of them are the same, then you can proceed. Therefore, what you want is:
public class CollisionScript : MonoBehaviour
{
public GameObject platform;
void OnCollisionEnter(Collision col)
{
if (col.gameObject == platform);
{
Debug.Log("Yes");
}
}
}
Let me know if this solves your problem.
EDIT: I see now you also named your class 'Collision', which I'm pretty sure would break things, as Collision is already a class within Unity(you can check that looking at "Collision col", that indicates there is a class named Collision). You should change both the class name on the top of the script and the file name to reflect that change.
During gameplay, is there any way to check whether a game object is an instance of a prefab, and get a reference to that prefab? I tried various methods in PrefabUtility, but they're all saying that my scene's instance of a prefab is not a prefab.
The thing is that PrefabUtility like e.g. PrefabUtility.IsPartOfAnyPrefab is in the UnityEditor namespace. So anyway it won't help you unless you need it only inside the UnityEditor itself.
Also this thread might be relevant if the PrefabUtility isn't working for you in the Editor because you are in the prefab mode:
In Prefab Mode the GameObject are simply regular gameobjects unless you have a nested prefab.
For runtime in a build or as soon as you enter the PlayMode the concept of prefab instance doesn't really exist anymore. They are then only GameObjects with no information about where they came from ;)
You could however store the reference to the prefab in a component like e.g.
#if UNITY_EDITOR
using UnityEditor;
#endif
[ExecuteInEditMode]
public class PrefabInfo : MonoBehaviour
{
public GameObject original;
#if UNITY_EDITOR
private void Awake()
{
// See https://docs.unity3d.com/ScriptReference/PrefabUtility.GetCorrespondingObjectFromOriginalSource.html
original = PrefabUtility.GetCorrespondingObjectFromOriginalSource(gameObject);
}
#endif
}
and on runtime also do
var obj = Instantiate(yourPrefab);
if(obj.TryGetComponent<PrefabInfo>(out prefabInfo)
{
prefabInfo.original = yourPrefab.gameObject;
}
else
{
obj.AddComponent<PrefabInfo>().original = yourPrefab.gameObject;
}
This way you would know if something is a prefab instance on runtime and which is the original prefab.
If you want to kind of hack this in order to automate it you could even create an Inspector for all MonoBehaviour and attach the PrefabInfo component automatically if something is a prefab or a prefab instance ;)
No - the information is removed from the instantiated GameObjects in the runtime for performance reasons.
The only way to do this would be to store it yourself via Editor script, run it before you enter the runtime, map out all prefabs that are interesting to you, and use this newly created information.
I do my exercise following a blog,and met with trouble.
After coding, I create a ShapeFactory asset in project,and sign the prefabs.size to 3,add the Shape component to a cube,after these work, I tried to drag the cube to the ShapeFactory asset,but it stoped by editor without nothing info but a forbident icon.
Here the code is:
ShapeFactory.cs
public class ShapeFactory :ScriptableObject
{
[SerializeField] private Shape[] prefabs;
}
Shape.cs
public class Shape : PersistableObject
{
private int shapeId=int.MinValue;
}
PersistableObject.cs
public class PersistableObject : MonoBehaviour {}
ScriptableObject are assets that "live" only in the Assets and are not related to any Scene.
You (usually) can not have any references from a Scene in a Prefab/asset that lives only in the Assets. The reason is savety. If the according Scene is not loaded then the references simply would not exist.
You would need some kind of persistent dependency injection in order to reconstruct those references once the Scene is loaded (afaik e.g. the serialization system of Odin Inspector is capable of that) or use dynamic dependency injection on runtime (some Assets exist for doing that in the AssetStore as well).
According to the field name ShapeFactory.prefabs what you want to use is a Prefab of that cube GameObject instead.
Simply drag & drop that cube from the Scene (Hierachy) into the Assets folder (ProjectView). This creates a Prefab from it. That new created prefab you can now reference in the ScriptableObject asset and other prefabs.
Before I said (usually) because some workarounds exists in case you really need a reference from a Scene. The simpliest one I know would be to use another ScriptableObject like e.g.
Change your script to
public class ShapeFactory : ScriptableObject
{
[SerializeField] private PersistableObjectReference[] prefabsReferences;
}
then have the reference type
[CreateAssetMenu]
public class PersistableObjectReference : ScriptableObject
{
public PersistableObject value;
}
Then on the according object in the scene have a
[RequireComponent(typeof(PersistableObject))]
public class PersistentObjectReferenceSetter : MonoBehaviour
{
[SerializeField] private PersistableObjectReference reference;
// use Awake so it is also called if this object is
// deactivated in the hierachy
private void Awake()
{
reference.value = GetComponent<PersitableObject>();
}
}
and now reference the according PersistableObjectReference asset in reference. This will on runtime set the correct reference from the Scene into the ScriptableObject (it will be displayed as Type missmatch but work as expected).
Now all you have to do is later instead of using the former prefabs[index] rather use prefabReferences[index].value in ShapeFactory.
INVALID: You can't assign scene Game Objects to Scriptable Objects.
Scene Game Objects are in the Hierarchy Window.
VALID: You can assign Prefabs to Scriptable Objects.
Prefabs are in the Project Window.
Since you're creating a Factory you want the Prefab there and not a scene Game Object.
HOW TO DO SO
To achieve that you can simply do this:
CREATE A PREFAB: Drag the -Cube Scene GameObject- (the one in the Hierarchy Window) to the Project Folder => so you create a prefab
ASSIGN THE PREFAB IN THE SO: Drag the -Cube Prefab- (the one in the Project Window) on the SO.
Now you can use the Prefab in the SO factory for instantiation or any other goal.