I am creating prefabs for farmland in my 2D game. Since I want all ground tiles to turn into farmable-land when hit with a hoe, I am worried about performance (Since there will be hundreds of these GameObjects in a scene).
Would the best thing be to Destroy the ground tile and Instantiate a farm tile in it's position, or would it be better to create a more generic Script that is attached to every Ground tile(?), which has states like:
GROUND, FARMABLE, PLANTED
, then depending on the state I change behaviour and set a sprite like: tile.GetComponent<Image>().sprite = Resources.Load<Sprite>(pathToSprite);
Maybe I'm missing a better option but these are the ones I can think of.
Destroyin and instantiating hundreds of game objects during runtime is a recipe for disaster, memory fragmentation and GC going wild which will kill performance.
The second solution is way better. Use an enum for all the possible states of a tile in your game logic, and then change the Sprite field of the Sprite Renderer component accordingly.
PSA: Don't use GetComponent and Resources.Load every time you need to change a sprite, get a reference to the Sprite Renderer component and to a Sprite[] array which contains all possible state images to use in Awake, and then use those references to change the sprite image when needed.
Edit: Answering your question in the comment.
Be sure that the Sprites are all in the Resources folder of your
project, they can be in a subfolder, i.e.: Resources/Sprites.
Check that the path string is correct, i.e. if the sprite asset is called Circle, and it's in the Resources/Sprites folder,
path must be "Sprites/Circle".
Code example:
public class MyClass : MonoBehaviour {
Sprite[] spritesArray = new Sprite[10];
void Awake() {
spritesArray[0] = Resources.Load<Sprite>("Sprites/Circle");
}
}
Try This:{ tile.GetComponent(/).sprite = Resources.Load(pathtosprite); }
Related
I'm currently developing a game in Unity using C# and I've run into a small problem.
I need to spawn a certain gameobjects relative to the Spawnposition and length of another game object. Now I figured that bounds.size would be the best function to use in this instance. As shown bellow I declare first the variable that uses this in my start method:
public void Start()
{
GameObject PointBar = (GameObject) Resources.Load("PointBar Horizontal");
PointBarVectorLength = PointBar.GetComponent<BoxCollider2D>().bounds.size.x;
PointBarVectorConv = Camera.main.WorldToViewportPoint(new Vector2(PointBarVectorLength, 0f));
}
However, the gameobjects in question are inactive at start's call and thus I presume don't return any value for bounds.size when called.
Does anyone know how I can access the bounds.size or equivalent of an inactive gameobject or is there something else I'm missing here?
As noted in the documentation for the Collider.bounds property, bounds "will be an empty bounding box if the collider is disabled or the game object is inactive". So your assumption was pretty much right. Since the object doesn't exist in worldspace that makes sense.
I'm not sure about the most elegant solution for your use case but two options spring to mind.
Calculate the object's spawn dimensions by accessing its transform.localScale and factoring in the scale of prospective parent objects. That could get messy but you could probably also write a helper method to make it more manageable.
Instantiate the object somewhere off screen after you load it, access its Collider.bounds property to get the information you need then move it where ever you want it or store the information for later.
There may be better solutions but that's what leaps to mind for me.
I solved the issue by using GetComponent().bounds.size.x;
instead of BoxCollider. This component can get accessed when the game object is not active.
Sorry, I know this is super basic! Finished my first Unity course and am working on my first game and just want a hockey puck to spawn at the position of the PlayerPuckSpawn GameObject in the hierarchy after the opponent scores a goal. Currently just writing a method for when the goal is scored.
public void EnemyGoalScored()
{
StartCoroutine(EnemyScored());
Destroy(gameObject);
Instantiate(gameObject, PlayerPuckSpawn.transform.position);
}
This is in the script associated with the puck, so the gameObject refers to the puck. Just can't remember how to write the script part that tells the puck to spawn at the position of the PlayerPuckSpawn, which is a GameObject in the hierarchy. I know it's super basic, but I've been searching online and through my class notes and can't find it. Super thanks to anyone who can help! :)
One variant of the Instantiate method has a location and rotation parameter. to use the world coordinates of the calling item, do:
Instantiate(prefab_to_inst,transform.position,transform.rotation);
If you do not want to copy the rotation, can also use default rot:
Instantiate(prefab_to_inst,transform.position,Quaternion.identity);
To copy the position of any item, prepend the reference before transform to use that item's location.
If you have GameObject puckspawn; defined and filled,
Instantiate(prefab_to_inst,puckspawn.transform.position,puckspawn.transform.rotation);
I have the following hierarchy on my Player prefab for what will be a very simple multiplayer shooter.
It works this way, the Controller object has the scripts that deal with player input, the PlayerShip object has all of the turning, moving, shooting scripts etc and the Camera is just as it sounds, a camera with a few scripts on it for moving it about.
When a new Player is instantiated the Control script needs to locate its relevant PlayerShip, simple enough.
This can be achieved using the following code:
_playerShip = gameObject.transform.parent.gameObject.transform.FindChild("PlayerShip").GetComponent<PlayerShip>();
Which works fine, the only thing is, to me that looks very clunky, inelegant and brittle. Consequently, I'm wondering if there's a better, more efficient, less ugly way of achieving the same thing?
First of all,
_playerShip = gameObject.transform.parent.gameObject.transform.FindChild("playerShip").GetComponent<PlayerShip>();
is redundant. That can be reduced to
_playerShip = gameObject.transform.parent.FindChild("playerShip").GetComponent<PlayerShip>();
or even use '/' just like you do with folder names.
_playerShip = GameObject.Find("Player/playerShip").GetComponent<PlayerShip>();
Now, instead of instantiating the Object and searching for it later on, you can actually instantiate it and retrieve the reference at the-same time.
GameObject obj = Instantiate(prefab,Vector3.zero,Quaternion.identity) as GameObject;
_playerShip = obj.GetComponent<PlayerShip>();
If it is a network game that you instantiate with Network.Instantiate:
GameObject obj = Network.Instantiate(prefab,Vector3.zero,Quaternion.identity,0) as GameObject;
_playerShip = obj.GetComponent<PlayerShip>();
Now, send the PlayerShip reference to the Control script.
If this isn't being called every frame consider this:
_playerShip = GameObject.Find("PlayerShip").GetComponent>PlayerShip>();
Read more about the above here: https://docs.unity3d.com/ScriptReference/GameObject.Find.html
If it is being called every frame look in to this:
https://docs.unity3d.com/ScriptReference/GameObject.FindWithTag.html
Either one should be easier to understand.
EDIT: I may have misunderstood. Would rearranging the hierarchy so that controller is a parent of playerShip? Then you should be able to just look at the child, rather than going through a parent to a sibling.
I have a 2D game. I have game objects that are made of a singular shapes with colliders and some that are made out of several shapes and included in an empty game object to which I have added a character collider. All the game objects have particle systems added and the single shaped game objects work as expected and explode onCollision, the multi shaped objects do not.
The explosions work as expected when using Play On Awake and Looping to Test them, but they do not explode onCollision. I have tried putting the particle system on one of the shapes inside the outer game object and then it shows an error Missing Component System, trying to access particle system for x object, which makes sense.
Within each game Object C# class I have the following methods:
private void OnCollisionEnter(Collision coll)
{
Explode();
}
private void Explode()
{
var exp = GetComponent<ParticleSystem>();
exp.Play();
GetComponent<Renderer>().enabled = false;
Destroy(gameObject, exp.duration);
}
The bombs are set to a rate of 0, to go off in one burst.
I have tried searching and cannot find the missing information required when using particle systems in game objects that are made up from multiple 3D game object shapes.
What am I missing?
I sorted it out, there were a few issues created from not understanding what to do and creating hacks.
The first issue is the renderers were being created in the child objects, so it was better to get all the child objects and loop through these and then disable the renderer in each of these.
I also removed the character colliders, as this was not needed and used sphere colliders.
I then unchecked is kinematic,as this had been enabled to stop my gameobjects in the air from being kicked about, instead I added mass in the rigid body.
In the explode method I commented out removing the rendering visibility as this had interfered with the explosion:
private void Explode()
{
var exp = GetComponent<ParticleSystem>();
exp.Play();
//GetComponent<Renderer>().enabled = false;
Destroy(gameObject, exp.duration);
}
I also extended the particle system start lifetime to exceed the duration. To ensure the particles were visible when the object is destroyed.
I'm still needing to tweak the explosions, but this has made a huge dent for me.
As per the title, I'm essentially looking for a way to associate a Prefab with a script that doesn't implement MonoBehaviour. From the Unity editor, I can normally drag a Prefab and drop it directly into the script, but as soon as I remove MonoBehaviour, I no longer have the option to. I need to remove MonoBehaviour because I'm creating instances of the class that I can pass onto a "manager".
I've seen plenty of examples of people referencing a Prefab through a script through code at runtime, but that seems unnecessary since I'm never going to change the prefab I'm associating with that particular variable, and I have the prefab object ready before any code is actually run. I'm leaning towards Resources.Load() as my answer, but I'm not exactly sure whether that's the ideal thing to do.
Is there any trick to specifying a Prefab before runtime, or what's the most efficient way to associate a GameObject variable with a Prefab in a way similar to the "drag and drop" method of association in the Unity Editor?
You can have your normal C# class point to a GameObject or a prefab.
using UnityEngine;
public class NotMonoBehaviour
{
private GameObject gObject;
public NotMonoBehaviour()
{
gObject = Resources.Load("MyPrefab") as GameObject;
}
}
In this case the code goes into the Resources folder and finds the Prefab.