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;
Related
I have set up a random spawner that creates new game objects but they are being created outside of my canvas and therefore can't be seen when play the game. Is there any way to fix this? The objects 'neg thoughts' are UI Buttons and are being created outside of the canvas even though I need them to appear on screen so they can be used.
I did see a similar question but the suggestions didn't work for my problem.
this is very frustrating for me and any help would be awesome!
You can simply pass the parent into Instantiate
parent Parent that will be assigned to the new object
var newObj = Instantiate(prefab, parentTransform);
or with additional transforms
var newObj = Instantiate (prefab, position, rotation, parentTransform);
Or as others already said you can still do it afterwards at any time either by simply assigning a new transform.parent
newObj.transform.parent = parentTransform;
or using transform.SetParent
newObj.transform.SetParent(parentTransform, worldPositionStays);
The advantage of the later is that you have an optional parameter worldPositionStays
worldPositionStays If true, the parent-relative position, scale and rotation are modified such that the object keeps the same world space position, rotation and scale as before.
assigning a new
transform.parent = parentTransform;`
will always act the same way as
transform.SetParent(parentTransform);
since the default value for worldPositionStays (so if you don't explicitly pass it) is true.
So for the specific case of UI in a Canvas you could use
public TheSpawnComponent : MonoBehaviour
{
// Via the Inspector drag&drop any object here that is inside the canvas
// or the canvas itself
[SerializeField] private GameObject parentInCanvas;
[SerializeField] private Button buttonPrefab;
public void DoInstantiate()
{
var newButton = Instantiate (buttonPrefab, parentInCanvas);
//Todo Position and callback
}
}
Or if the spawner script is attached to an object inside the canvas anyway you could also spawn as child of this one directly using
var newButton = Instantiate(buttonPrefab, transform);
You can specify the transforms parent after you have created the game object.
spawnedObject.transform.parent = canvas.transform;
You have to use below code
public GameObject Prefab; // Object to Create
public Transform ParentOfObj;// Must Be inside the canvas or canvas it self
void Start()
{
GameObject g = Instantiate(Prefab) as GameObject;
g.transform.SetParent(ParentOfObj);
g.transform.localScale = Vector3.one;
}
I am trying to create a procedural created dungeon in Unity.
I have it creating the dungeon, and have a script attached to each prefab that goes to make up the walls, floor etc. Each one of these is attached to a single parent, to make it easy to delete and rebuild the entire dungeon. The script contains details:
public class WallPrefab
{
public bool use;
private int roomId;
public WallTypes type; //enum = torch, switch, item etc
public WallDirections direction; //direction wall is facing
public GameObject myPrefab; //nested
}
The prefab also contains an empty game object used as a mount point for torches, switches etc.
myPrefab
prefab //prefab for the actual 3d object
wallmount //empty game object to store location and rotation details
....
My questions:
1. How do I get the wall type?
int childs = transform.childCount;
for (var i = childs - 1; i >= 0; i--)
{
c = transform.GetChild(i).gameObject;
Debug.Log("child:" + c.name);
//WallTypes g = c.GetComponentInChildren<WallPrefab>().type;
WallTypes g = c.transform.GetComponentInChildren<WallSettings>().type;
(Edited to insert working solution to this part of my problem!)
This code does not work, because Unity complains that:
ArgumentException: GetComponent requires that the requested component
'WallPrefab' derives from MonoBehaviour or Component or is an
interface.
How do I get the transform of the mount point?
I know how to get the location and rotation, once I get the mount point, but how do I actually get the mount point?
Question 1 seems pretty self explainatory.
Question 2 looks a bit more interessting.
If i understood you correctly, you have a script, that "creates" torches on specified mount points. Assuming those mount points are empty game objects and you dont want to add torches directly into the prefab (would be nice if we got a little bit more detail here on how exactly your dungeon+mount point is generated), i would add a script to the prefab, add a GameObject instance variable and use the inspector to drag the mount point onto the script. then i would use "gameobjectVariable.transform.position" to get the position.
something like:
public class prefabClassWithMountPoints{
GameObject mountpoint; //drag mount point from inspector
GameObject torchGameObject; //drag torch prefab here
public void createTorch(){
GameObject torchObject = Instantiate(torchGameObject, mountpoint.transform.position, Quaternion.identity);
//...
}
//rest of code
}
this is assuming the mount points are not randomly generated WITHIN the prefab.
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!
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.