Prefab instantiated as an object with my methods - unity3d

I have an idea that there must be a way to attach a class to a prefab - basically to add my own methods to the code. Then, when I instantiate my prefab in runtime I want it to be an instance of this class.
Now, I can attach a script to a prefab and it's Start and Update works, but my idea is something like this:
public class PowerupWidget : MonoBehaviour {
void Start() {
Debug.Log("PowerupWidget created!!!");
}
void SetTitle(string title) {
// ... set the prefab's title widget text ...
}
}
When I instantiate my prefab, the Start method is called. But the instance is of course the GameObject type.
// in my scene script:
// prefab is my PowerupWidget
GameObject p = Instantiate(prefab, transform.position, Quaternion.identity, this.transform);
// here p is GameObject of course
Is there a way how to get to the instance of the PowerupWidget class, or am I completely wrong in my assuptions?

Structure in unity :
Things are divided into entities and component, when entities are gameobject and components are classes(Script) that are attached to this gameobject.
You just need to do this
PowerupWidget yourWidget = p.GetComponent<PowerupWidget>();

Related

Passing Gameobject throught class in Unity

im trying to make a card game. There is something I can't understand:
I have a class named HandManagement that has a list of GameObject and a method AddToHand that add to a list (the hand) a gameobject passed through variable (bringing in hand)
I have a Tile GameObject on the screen with a script that, when collides with mousedown:
the variable hand of HandManagement type is called with method AddToHand to add that gameobject to hand (AddToHand(this.gameobject)
When I try unity tells me that the object doesnt exist..
Why?
Thank you
Tile Class:
{
m_ObjectCollider.isTrigger = true;
tileInHand.PickTile (tileName);
HandManagement Class:
public GameObject tile;
public List<GameObject> hand = new List<GameObject>();
public void PickTile(string pickedTile)
{
Debug.Log ("picked");
tile = Instantiate (tile);
tile.transform.SetParent (transform, false); //i put it in a grid of hand card
hand.Add (tile); //I add it to the list of card in hand
}}
first of all, change the string to a GameObject, then Instantiate it
public void PickTile(GameObject pickedTile)//change it from string to gameobject
{
Debug.Log ("picked");
tile = Instantiate (pickedTile);//instantiate the picked tile.
tile.transform.SetParent (transform, false);
hand.Add (tile);
}}
now tile holds the GameObject "pickedTile" so it will be added to the grid and to the hand.
Next, you need to change the referenced object from a string to a GameObject
tileInHand.PickTile(tileGameObject);
The Instantiate function copies the object tile into tile, this is empty to begin with. Try instantiating a prefab gameobject into the tile, or make sure gameobject tile is never empty (eg. by setting it in the Start()).
https://docs.unity3d.com/ScriptReference/Object.Instantiate.html

Get Prefab in the script of an Instantiated Object - Unity 2D

I am making a space invaders game, and have a script attached to a separated gameObject that instantiates Enemies. I can drag in the prefab and make it work, but then I want the enemies to spawn bullets themselves, with the bullet being a prefab.
I have started with just creating a variable for the bullet, but how would I access the prefab in script, as if I just drag it into the prefab, it won't be useable after the prefab is instantiated.
GameObject bullet;
void Start() {
bullet = // Insert Function to access prefab
}
Thanks
If you put your prefab in a Root Folder named "Resources" you can load prefabs from there like this:
Instantiate(Resources.Load("YourBulletPrefab"));
But you can also make the variable public...
public GameObject bullet; // or use private + [SerializeField]
...and then in Inspector, drag your Prefab in. No need to initialize it in Start() in that case.
I usually solve this by adding a public GameObject bullet variable to the "enemy" script AND the "enemy spawner" script. In the inspector you give reference to the "enemy spawner" to access the bullet prefab. And then, when you spawn an enemy, you pass its reference to the enemy. Something like this:
Enemy script:
class Enemy
{
public GameObject bullet;
private Shoot()
{
//Shoot bullet
}
}
EnemySpawner script:
class EnemySpawner
{
public GameObject enemy;
public GameObject bullet;
private Spawn()
{
GameObject newEnemy = Instantiate(enemy);
enemy.GetComponent<Enemy>().bullet = bullet;
}
}
By doing so, you will be able to access the bullets later as well.

Is there a way to make an instantiated text follow a game object?

I'm currently experiencing a coder's block right now as I'm trying to instantiate text on a game object, specifically a Zombie prefab. I've gotten down the enemy's position for the text to be spawned but can't seem to make it follow the zombie's movement as it walks towards the player.
This is my 'Word Spawner' script.
public GameObject wordpb; // my word prefab
public Transform canvas; // connected to a canvas ui that has world camera set
public EnemyOne enemy;
public DisplayWord Spawn()
{
Vector2 targetPos = new Vector2(enemy.transform.position.x, enemy.transform.position.y);
GameObject wordobj = Instantiate(wordpb, targetPos, Quaternion.identity, canvas);
DisplayWord displayWord = wordobj.GetComponent<DisplayWord>();
return displayWord;
}
and this is where the DisplayWord class is derived from.
public Text text;
public void SetWord(string word)
{
text.text = word;
}
public void ThrowLetter()
{
text.text = text.text.Remove(0, 1);
text.color = Color.red;
}
public void ThrowWord()
{
Destroy(gameObject);
}
My best guess is that I should be implementing a void Update method in which I use transform.Translate? Or should I put a placeholder that acts as a child class to my Zombie prefab and attach the DisplayWord script there? Please help a poor soul out.
How does the text relate to the Zombie game object? Knowing what the text is for in relation to the zombie might inform the best way to make it follow the game object.
If the text is something like a worldspace nametag, instead of translating in Update you could make the text a child of the game object it needs to follow. It might not be the most elegant solution but if you give each text its own worldspace canvas you could assign the text as a child of the zombie, or, if you know you'll always have text following a zombie, you could just add a worldspace canvas and text to the zombie prefab...
To instantiate as a child you'd need to rework your word prefab to be its own worldspace canvas with your text as its child.
You can assign a parent when you instantiate:
wordobj = Instantiate(wordpb, enemy);
Or set the parent after instantiation:
wordobj.transform.SetParent(enemy.gameObject.transform, true);
The second parameter in SetParent is 'worldPositionStays', and controls whether the child object keeps its world position (true) or whether it's transform is evaluated relative to its new parent's transform. You could make it work either way: if you leave it 'true' you don't need to change the rest of your code, but I think if you make it false you don't need to get the enemy GameObject's position... The same is true for Instantiate when a parent is assigned but no transform position/rotation. So I think you could skip the step of finding the enemy's position and rewrite your Spawn code to:
public GameObject wordpb; // my word prefab
public EnemyOne enemy;
public DisplayWord Spawn()
{
GameObject wordobj = Instantiate(wordpb, enemy, false);
DisplayWord displayWord = wordobj.GetComponent<DisplayWord>();
return displayWord;
}
You'd need to assign the newly instantiated canvas's worldspace camera to make this work...

Children collider not getting called

Context
I'm working in a pickup system in my game. I've a component called AbstractSightCollider that has a sphere collider and some AbstractPickupableObject that are the objects meant to be picked up.
AbstractSightCollider is attached to the main character, but could be attached to any alive entity or anything that is able to contain inventory objects.
The way i designed it, it's that when AbstractSightCollider detects an object, it fires an UnityEvent called PickupDetected and when the player leaves the range of pickup, it call an UnityEvent called PickupLeave
The problem
I can't make OnCollisionEnter and OnCollisionExit trigger.
Some code
This is attached to AbstractSightCollider
public class AbstractObjectSight : MonoBehaviour
{
public OnPickupableDetected pickupDetected;
public OnPickupableLeave pickupLeave;
private void OnCollisionEnter(Collision col) {
GameObject gameObject = col.gameObject;
AbstractPickupableObject abstractPickupableObject =
gameObject.transform.GetComponent<AbstractPickupableObject>();
if (abstractPickupableObject != null) {
pickupDetected.Invoke(abstractPickupableObject);
}
}
private void OnCollisionExit(Collision col) {
GameObject gameObject = col.gameObject;
AbstractPickupableObject abstractInventoryObject =
gameObject.transform.GetComponent<AbstractPickupableObject>();
if (abstractInventoryObject != null) {
pickupLeave.Invoke(abstractInventoryObject);
}
}
[System.Serializable]
public class OnPickupableDetected : UnityEvent<AbstractPickupableObject> { }
[System.Serializable]
public class OnPickupableLeave : UnityEvent<AbstractPickupableObject> { }
}
And here are the properties:
Thanks for your time
Make sure both objects (the one with the script and the one that will cause the trigger) have colliders and rigidbodys, I find if one doesn't have those the triggers and collisions will not work.
I just found out the problem.
OnCollisionEnter and OnCollisionExit aren't the events that i needed to listen, because they work with rigidbody. My AbstractSight is that, a non body abstract sphere where the entities are allowed to grab items.
Instead, i used OnTriggerEnter, OnTriggerExit and now it works like a charm.
Rigid body on the parent object means the collider will work on ANY child component but the rigid body MUST be on the parent object.
Example:
MyProjectile has a rigid body and a collider - set isTrigger = true
MyEnemy parent object just holds scripts
MyEnemy childObject holds the enemy prefab including its collider,
Everything is set to isKinematic = true, with no gravity.
Everything works fine.

Scriptable Object as a custom version of sprite

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.