Is there a way in Unity3d framework to register an object that would be accessible to all entities in all scenes in the game without resorting to singleton pattern (like a game state object)?
You could take the following approach:
In the scene where the GameObject is created, in a MonoBehavior script attached to the GameObject:
Name the GameObject with "name = ..." in Awake() (or in the Editor)
Example: myObject.name = "FindMeInEveryScene";
Ref: https://docs.unity3d.com/ScriptReference/Object-name.html
Call "DontDestroyOnLoad" on the GameObject in Awake()
Example: DontDestroyOnLoad( myObject );
Ref: https://docs.unity3d.com/ScriptReference/Object.DontDestroyOnLoad.html
In the same scene and in subsequent scenes, in MonoBehavior scripts attached to other GameObjects.
Find the object with GameObject.Find(...) in Start()
Example: globalGameObject = GameObject.Find( "FindMeInEveryScene" );
Ref: https://docs.unity3d.com/ScriptReference/GameObject.Find.html
This will allow you to access the GameObject named "FindMeInEveryScene" without a singleton.
To pull it all together...
GlobalGameObject.cs
// Attached to the GameObject that needs to be accessd by any other
// GameObjects and needs to survive between loading scenes
public class GlobalGameObject : MonoBehaviour
{
static bool gameObjectInitialized = false;
void Awake()
{
if ( !gameObjectInitialized )
{
gameObject.name = "FindMeInEveryScene";
DontDestroyOnLoad( gameObject );
gameObjectInitialized = true;
}
}
}
UsingGameObject.cs
// Attached to GameObjects that need access to the GlobalGameObject
public class UsingGameObject : MonoBehaviour
{
private GameObject globalGameObject = null;
void Start()
{
globalGameObject = GameObject.Find( "FindMeInEveryScene" );
}
}
You could possibly do it using events but it isn't really the optimal way to use events. I'm intrigued to know why a singleton pattern doesn't work for you as this is pretty much their main purpose.
If you aren't going to do this with a static or a singleton, I don't see any point in you using any kind of game state object in your system design. It would do the same thing, but slower (avoid GameObject.Find at all costs). It would lack the guaaranteed uniqueness of the singleton and the easy reference provided by both. I don't understand what tradeoffs you mean but I'll assume they're sound to try and answer your question.
Doing this without a central game state object is going to require a more decentralized solution. Tasks that would be performed by a static game manager are now controlled by the individual objects themselves and notified by other objects.
https://unity3d.com/learn/tutorials/topics/scripting/delegates
The beauty of this method is that objects don't need to know about eachother at all. They just need to listen for events and notify the system that an event has occured. I really can't think of another alternative. Hope this helps. Best of Luck!
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 search a lot but couldn't find any answer. My problem: I instantiate several Gameobjects of a Prefab, each consists of two child objects: an obstacle and a scorezone. I want to destroy the scorezone (only of that specific instance), when the player touches the obstacle. So in Runtime my hierarchy looks like this:
-ObjectPrefab(clone)
---Scorezone
---Obstacle
-ObjectPrefab(Clone)
---Scorezone
---Obstacle
...
So I need to find ONE child gameobject (not the Transform) to then destroy it. I tried several codes but none worked. Here is the code with the three alternatives I tried, but none of them worked.:
private void OnCollisionEnter2D(Collision2D collision) {
if (collision.gameObject.tag == "obstacle") {
Alternative 1:
Destroy(GameObject.FindWithTag("scorezone"));
Alternative 1 comes really close, but it destroys all scorezones of all instantiated objects.
Alternative 2:
Destroy(collision.gameObject.transform.parent.gameObject.FindWithTag("scorezone"));
Alternative 2 would sound logic to me but I get the error message "Transform does not contain a definition for "FindWithTag".
Alternative 3:
foreach (Transform child in collision.gameObject.transform)
{
if (child.tag == "scorezone")
{
Destroy(child.gameObject);
}
}
}
Alternative 3 does not give an error but actually does nothing at all when the player hits the obstacle.
I really appreciate some help, thanks in advance!
Store the reference!
If it is really a "prefab" so meaning you have it as an asset and instantiate it on runtime the best way (in my eyes and performance wise) would actually be to use a serialized field:
// Put this component on your obstacle
public class ObjectController : MonoBehaviour
{
// Drag the according object here via the Inspector in the prefab
// => when the prefab is Instantiated this field is already referenced
[SerializeField] private GameObject _scoreZone;
// Read-only access to the reference
public GameObject ScoreZone => _scoreZone;
}
This field reference is stored together with your prefab asset. So every instance of the prefab already has its own child references set up.
and then do
private void OnCollisionEnter2D(Collision2D collision)
{
// Rather use CompareTag which is slightly more efficient
// and adds some security (== silently fails if typo, CompareTag throws an exception instead)
if (collision.gameObject.CompareTag("obstacle"))
{
var controller = collision.gameObject.GetComponent<ObjectController>();
if(controller)
{
Destroy(controller.ScoreZone);
}
}
}
By Index
Alternatively but error prone you could simply use the index. You already know that there are only 2 childs, the first one being the Scorezone with index 0 so you can also do
if (collision.gameObject.CompareTag("obstacle"))
{
var scoreZone = collision.transform.parent.GetChild(0);
Destroy(scoreZone);
}
By Name
Using the name you could also use Find - I would not recommend this! It is error prone and slow
if (collision.gameObject.CompareTag("obstacle"))
{
var scoreZone = collision.transform.parent.Find("ScoreZone");
Destroy(scoreZone);
}
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.
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.
In my Unity3D project I got a complex GameObject of a truck that his hierarchy looks like this.
+ Truck
+FrontPivotPoint
+LeftWheel
Tire
Rim
Hindge
+RightWheel
Tire
Rim
Hindge
+CenterPivotPoint
+Body
Arm
Screw
Pin
Basically what's happening is I got a lot of complex parenting and I want to go trough every child and add to him a RigidBody.
I think it should be something nested but I don't have something in mind.
Any help would be appreciated!
Unity3d allows you to easily automate every routine. You may consider to add custom menu item.
public class MakeRigidBodies : EditorWindow
{
[MenuItem ("Make rigid bodies %&r")]
private static void Execute()
{
var selectedObject = UnityEditor.Selection.activeObject;
if( selectedObject && selectedObject is GameObject )
{
// for all children of selectedObject
{
// add rigid body
}
}
}
}
From what I can see, the structure of your game objects is like a tree data structure. Truck as its parent / root node, and the others as its child. So, you must traverse all the objects / nodes in your structure with tree traversing algorithm, the best that I know is Depth-First-Search (DFS) algorithm.
DFS work like nested loop, the main algorithm are:
Start from the root node
Find its node children
Visit the child
Back to step 2 until all children is visited
This algorithm can be implemented in Unity3d since GameObject stores its child information in transform properties (see http://answers.unity3d.com/questions/416730/get-the-child-gameobject-of-a-parent-and-not-the-t.html). And last, we can add RigidBody to the GameObject with the AddComponent() method (see http://answers.unity3d.com/questions/19466/how-do-i-add-a-rigidbody-in-script.html).
Here is my script to answer your question:
using UnityEngine;
using System.Collections;
public class AddRigidBody : MonoBehaviour {
private void addRigidBody(GameObject gameObject)
{
Rigidbody rigidBody = gameObject.AddComponent<Rigidbody>();
foreach(Transform t in gameObject.transform)
{
addRigidBody(t.gameObject);
}
}
void Start () {
addRigidBody (this.gameObject);
}
}
This script attached to the parent / root GameObject. The AddRigidBody() method is called when this script start (as in Start() method), and this method will traverse all child and add a RigidBody through it.
If I'm not mistaken, and bear with me since it's been a while since I've worked with Unity3D, this could fix it:
* Drag your model onto the stage
* Your entire model should now be selected; parent and its underlying children
* Select the option to add a rigidbody and it should add it to all selected objects.
* Make a prefab of the model and you should be done.
Once again, don't trust me on this. It's been a while and since I don't have access to Unity3D right now, I am unable to check this method myself.
If it's the code approach you want, try this (C#, methods may vary for Boo or Java):
Getting a child: http://forum.unity3d.com/threads/get-child-c.83512/ (see second reply)
Adding a rigidbody: http://answers.unity3d.com/questions/19466/how-do-i-add-a-rigidbody-in-script.html
I hope this helps.