Accessing other script method from instantiated object [duplicate] - c#

This question already has answers here:
Store References to Scene Objects in prefabs?
(1 answer)
How can I grab a reference of a game-object in a scene from a prefab
(1 answer)
Closed 2 years ago.
I am trying to set my game up to where there is a player, you touch the screen to throw the object, once that object collides with another object 1 point is added to the score. I added a script to the instantiated object that prints a message, to the console, when a collision is detected. The script also has a reference to another script holding the score variable.
The script is:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CrackEgg : MonoBehaviour
{
public Stats stats;
void OnCollisionEnter(Collision collision)
{
if (collision.gameObject.tag == "BreakEgg")
{
stats.DepositMoney();
Debug.Log("I am actually working");
}
}
}
The Stats script is:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Stats : MonoBehaviour
{
[Header("Bank")]
public int bankAmount;
public int depositAmount;
public void DepositMoney()
{
bankAmount += depositAmount;
}
}
I know I have to have the Stats script attached to a game object(I have it attached to an empty game object). The thrown object is a prefab and has the CrackEgg script as a component. I try to drag the Stats game object into the Stats field of the CrackEgg script but it doesn't work. It only works if the Stats game object is also a prefab. The main issue I'm having is that even when I have the Stats game object prefab in the hierarchy, when I test the code, the bankAmount variable does not increase at all. As a summary, I want the instantiated object to detect a collision then run the DepositMoney() method, from the Stats script, but am not able to find a way to make it work.

Quick check: create a CrackEgg on the scene. Try to drag the Stats object into the field. It should work. Now drag this CrackEgg to the Project View in order to create a prefab. The Stats field is empty on the prefab. Why?
A prefab can be instantiated anywhere in your game, so it's possible this reference to a scene object is not valid in case the prefab is instantiated in another scene.
So you must get the reference to the Stats object in runtime.
You can accomplish that in two ways:
METHOD 1
Tag your Stats object with a "Stats" tag.
Then locate it in the scene whenever you start a CrackEgg.
class CrackEgg : MonoBehaviour {
void Start() {
stats = GameObject.FindGameObjectWithTag("Stats").GetComponent<Stats>();
}
}
METHOD 2
Make your Stats class static, and not a MonoBehaviour component.
public class Stats
{
private static int bankAmount;
public static void DepositMoney(int depositAmount)
{
bankAmount += depositAmount;
}
}
Then you don't add it to any object, since there's always a single instance of each parameter on the program. This is an usual pattern for score classes.
To access it, you don't need to store a reference to Stats, just use the class name:
if (collision.gameObject.tag == "BreakEgg")
{
Stats.DepositMoney(depositAmount);
Debug.Log("I am actually working");
}
Since you can't assign values to static classes on the Editor, I've suggested a modification where the caller defines the deposit amount when calling. You might need to think about the safety issues (don't allow values out of a range, etc).
Also, it's usually recommended you use CompareTag instead of ==: https://answers.unity.com/questions/200820/is-comparetag-better-than-gameobjecttag-performanc.html

Related

Multiple characters in videogame using Unity

I am developing a videogame using Unity and I want the user to be able to choose from a group of characters who have different attributes and weapons.
Do I have to create a scene for every character for each level or there is a dynamic way of doing it?
Your scripts :
GameManager :
public class GameManager : MonoBehavior
{
public static GameObject character; // this field is to remember the selection. Since it is 'static', it will keep value in between scenes.
}
LevelManager :
public class LevelManager : MonoBehavior
{
public void Start()
{
// read info stored in GameManager
GameManager.character.transform.position = /* your start position */
}
}
In scene 1 (selection):
store charcter selected in GameManager.character
In scene 2 (level):
An empty object with LevelManager script.
Note: Since we use static, I'm actually unsure that you even need the GameManager script to be attached to an empty object, for both scenes.
In the selection scne, just make sure to assign the proper thing to character (instance of Zelda or Megaman character prefab)

GameObject in SerializedField - OverRide - Apply All: Prefab Doesn't Update

I added a script to my prefab. Then I put my prefab into my scene and drag/dropped the GameObjects into the appropriate fields within the inspector. I then clicked OverRides>>Apply All. (Which is common and I do this all of the time)
However, the game object I'm using for the Spawn Zone when the Game Object will be Instantiated will not apply to my Prefab. (The Prefab in the scene does have the Empty Game Object attached but not the main Prefab in the Project panel)
I'm using the same method that I use for Instantiating bullets but for some unknown reason the Project view prefab will say "None(GameObject)"
Here's the code:
[SerializeField]
private GameObject boss;
[SerializeField]
private float bossSpd;
[SerializeField]
private GameObject bossSpawnZone1; // This won't apply to my prefab in Project panel
private void Update()
{
if(startBoss == true)//StartBoss occurs when a GameObject is touched by player on another script. Just OnTriggerEnter2D set var to true
{
GetTheBoss();
}
}
void GetTheBoss()
{
Instantiate(boss, bossSpawnZone1.transform.position, Quaternion.identity);
startBoss = false;
}
I would include pics but I don't know how to upload them in here. Never used the IMG URL before. But trust me, I inserted the empty gameobject(SpawnZone) into the field. I also used the override to apply all. Yet the spawn zone field remains empty.
What I've tried:
1) Removing the script and re-adding it then filling the fields again.
2) Made the spawn zone public static and tried using GameObject.Find("PirateSpawnZone") in another script pertaining to the player.
3) Changing the variable name and saving the script.
4) Restarting Unity / Restarted computer completely then open Unity back up.
You can not have Scene references in a prefab. It is not possible. It only works for GameObject reference that are themselves a prefab living in the asset or references within one and the same prefab hierarchy.
However some workarounds exist
dependency injection
using dependency injection (e.g. Zenject)
ScriptableObject references
a ScriptableObject for setting the reference on scene start and referencing the ScriptableObject instead.
[CreateAssetMenu(filename = "new GameObjectReference", menuName = "ScriptableObject/GameObjectReference")]
public GameObjectReference : ScriptableObject
{
public GameObject value;
}
Create an Asset by right click in the Assets &rightarrow; Create &rightarrow; ScriptableObject &rightarrow; GameObjectReference
Then change your script field to
[SerializeField] private GameObjectReference bossSpawnZone1;
and everywhere where you use it use bossSpawnZone1.value instead.
This works now since the ScriptableObject instance itself lives in the Assets.
Then finally you need the setter component on the bossSpawnZone1 GameObject like
public GameObjectReferenceSetter : MonoBehaviour
{
[SerializeField] private GameObject scriptableAsset;
private void Awake()
{
scriptableAsset.value = gameObject;
}
}
Singleton pattern
having only the setter and making a public static reference (only works if there is only exactly one of those reference in the entire scene/game
public class SpawnZoneReference : MonoBehaviour
{
public static GameObject instance;
private void Awake ()
{
instance = gameObject;
}
}
And in your script instead use
Instantiate(boss, SpawnZoneReference.instance.transform.position, Quaternion.identity);
Or very similar using only a certain type like
public class BossSpawnZone : MonoBehaviour { }
On the GameObject and in your script do
bossSpawnZone1 = FindObjectOfType<BossSpawnZone>();

Passing values from one script to another without a game object in the scene

I’ve found a lot of information on passing parameters from one script to another when a game object is present in the hierarchy. My problem is that my Transform object is created on the fly using Instantiate(myPrefab). Is there any way to access the position of myPrefab game object from another script?
You could store a reference to the instantiated GameObject after it has been instantiated (see example below). If there are more GameObjects, use a list to store them instead.
Call the InstantiateGO() and GetGOPosition() where it makes sense for you.
public class YourClass: MonoBehaviour
{
public GameObject yourPrefab;
public GameObject yourGameObject;
// Use this for initialization
void Start ()
{
}
// Update is called once per frame
void Update ()
{
}
void InstantiateGO()
{
yourGameObject = Instantiate(yourPrefab); // assign the newly instantiated GameObject to yourGameObject
}
void GetGOPosition()
{
var x = yourGameObject.position;
//Do something here
}
}

How to call a public static variable from another class, change its value and then check that its changed from its original class [duplicate]

So im trying to change a variable in another script by touching a cube.
Current setup
1x Player
1x Enemy
Each with their own script Enemy_Stats & Character_Stats
As you can see in this little snippet it's quite a workaround to access the variable from another script.
void OnCollisionEnter(Collision collision)
{
if (collision.gameObject.tag == "Enemy")
{
collision.gameObject.GetComponent<Enemy_Stats>().Health =
collision.gameObject.GetComponent<Enemy_Stats>().Health
- gameObject.GetComponent<Character_Stats>().AttackDamage;
if (collision.gameObject.GetComponent<Enemy_Stats>().Health <= 0)
{
Destroy(collision.gameObject);
}
}
}
Iam new to Unity, but isn't there a way to just refer it with something like:
collision.Health?
How to access variables/functions from another Class. The variable or function you want to access or called must be public not private.
public class ScriptA : MonoBehaviour{
public int playerScore = 0;
void Start()
{
}
public void doSomething()
{
}
}
Access variable playerScore in ScriptA from ScriptB. First, find the GameObject that the script or component is attached to with the GameObject.Find function then use the GetComponent function to retrieve that script or component that is attached to it.
public class ScriptB : MonoBehaviour{
ScriptA scriptInstance = null;
void Start()
{
GameObject tempObj = GameObject.Find("NameOfGameObjectScriptAIsAttachedTo");
scriptInstance = tempObj.GetComponent<ScriptA>();
//Access playerScore variable from ScriptA
scriptInstance.playerScore = 5;
//Call doSomething() function from ScriptA
scriptInstance.doSomething();
}
}
No since Health is not part of the collision object, but Enemy_Stats. You can cache a Component (that's what Enemy_Stats is) if you use it multiple times to save you some typing (and some performance, but that is rather marginal for this example). Also you can cache "known" components like in this case Player_Stats. You can do this e.g. in Start or with a public variable and the inspector.
What you should probably do though is to make the enemy be responsible for his life and not the player, so move the Destroy-part to Enemy_Stats (into the Health property to be exact).
The first thing to make this shorter (and eventually faster) would be to store this: gameObject.GetComponent<Character_Stats>() on Start() in a private variable (you should avoid calling GetComponent on a frequent basis if you can avoid it).
For the Health variable, a way of avoiding GetComponent calls could be caching: you create a Dictionary<GameObject, Enemy_Stats> and read from that as soon as this gameobject collided once
At the very beginning i mean in Awake() method you can find a game-object with tag
and get it's Heath after that in Collision() method you should just decrease the health but, here the condition is there is only one enemy and only one player.

Unity3D/C# - add a variable to gameobject

How can I add a variable to a GameObject?
GameObjects have a series of variables (name, transform, ...) that can be accessed and modified from any script. How could I add a variable such as for example "color" or "type" that could be accessed or modified from other scripts.
Thanks.
If GameObject wasn't a sealed class then inheritance could have been the solution to your problem. In this ideal situation you could try the following.
public class ExtraGameObject : GameObject
{
// Logically you could make use of an enum for the
// color but I picked the string to write this example faster.
public string Color { get; set; }
}
A solution to your problem could be to declare your class like below:
public class ExtraGameObject
{
public string Color { get; set; }
public GameObject GameObject { get; set; }
}
I am aware of this that this is not exactly that you want, but that you want I don't think that it can be done due to the sealed nature of GameObject.
As every object in c# GameObject class inheritance Object class. If you right click on GameObject and select go to definition you will see that below.
In order to reach one object's variables you need a reference(not considering static variables). So if you want to change variables of one GameObject from other you need a ref.
On Unity inspector you can assign values to public variables of a script. Below you can see Obj end of the arrow. Now assign cube(is a GameObject ) to Obj.
After drag and drop
Now you can reach every subComponent of cube object. In red rectangle I reached it's Transform component and changed it's position.
I hope I understood your question correct.GL.
Christos answer is correct. You cannot add a variable to the GameObject datatype because it was built into the language as a base. To add variables to premade data type, extend off of them. For more information on inheritance see
https://msdn.microsoft.com/library/ms173149(v=vs.100).aspx
You can add a script to the prefab from which an object is created using the Instantiate method. In this script, you declare public variable.
Then you can change and get the value of this variable using the GetComponent method.
The code in script file added to the prefab (prefab name is thatPrefab, script file name is IdDeclarer) as a component:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class IdDeclarer : MonoBehaviour
{
public int id;
}
Declaring an object's characteristics (ID in example):
GameObject newObject = Instantiate(thatPrefab, new Vector3(0, 0, 0), Quaternion.identity) as GameObject;
newObject.GetComponent<IdDeclarer>().id = 99;
Debug.Log(newObject.GetComponent<IdDeclarer>().id);
Output: 99
I hope I helped you

Categories