- Home /
Accessing instance of prefab from its own script
Hi, I'm trying to build a turn-based game in which a player can build units and move them around on subsequent turns. I'm doing this by instantiating the different kinds of units from prefabs, and having the different kinds of units all implement the same interface, so I can access their different behaviors from their own scripts.
I'm having trouble accessing the prefab's own instance from its script. This is probably an easy fix but I just don't know it - I've been breaking my game for hours trying to figure it out lol. Here's the script for a unit "Runner" that handles what I mentioned. It has a lot more code but I think the problem is exemplified in this sample.
public class Runner : MonoBehaviour, ICharacter
{
private Tile currentTile;
private void Start()
{
currentTile = TileManager.FindTile(this.transform.position);
}
As you can see, "this.transform.position" is not what I would like it to be: it's accessing the prefab's position, not the instance's, so the FindTile method returns as null. Is the only way to fix my issue to assign a GameObject to the "Runner" class and assign it to its instance on instantiation? This seems unnecessarily clunky. Thanks a bunch.
What do you mean by:
"I'm having trouble accessing the prefab's own instance from its script."
A prefab doesn't have an intrinsic position. Its a configuration of gameobjects/ components that you use to more easily instantiate them.
If you have values that are consistent across multiple instances of a class (i.e. you change it once and it changes everywhere) then the member is static and shouldn't be.
I mean that elsewhere in my game logic (another script) I've instantiated the prefab. Each unit of the same type (instantiated from the same prefab) has the above script attached to it. I'd like to be able to move the unit around (or access its position, etc.) from said script.
You should be able to. Does your prefab have a hierarchy? are you accidentally testing the relative position of a child in said hierarchy instead of the prefabs root game objects position?
Does gameObject.transform.position work better for you than this.transform.position?
Another thing, as a friendly recommendation, depending on the scope of your game and the number of characters being spawned, it may be good to instantiate via addressables. They allow you to easily store a reference to the prefab, but don't force you to have it in memory. Regular references store the object in memory even when not instantiated. Addressables on store an address/reference to the item.
If you have some idea of what objects will be built, it may make sense to do object pooling where you enable and disable the object from a pool. It all depends on your scope.
Is this what you are trying to do?
https://www.youtube.com/watch?v=_qt-DavUo8U&ab_channel=sacredgeometry
Thanks for taking the time to make that video. I'll explain what I'm trying to do with the example you've given: imagine each of these gameobjects (the three balls) had a script attached to them that did some work. Let's say, for the sake of simplicity, all it did was print their position to the log. Normally such a method would be dead simple to write:
public void WritePosition()
{
Debug.Log(this.transform.position);
}
Yet, when I try to call this method from another script, it tries to access the prefab's transform, not the instance's transform. Here's how I've tried to set things up to avoid this: when I select the Tile my desired unit is on, I save that Tile's gameobject in my pointer's selector script. then I access the gameobject's method by calling it as whateverItsNameIs.WritePosition(). But this doesn't work either.
The only way I can get this to work is to make the WritePosition method (for example) to take a gameobject as an argument; then, in the pointer's selector, to pass in the gameobject when it calls that gameobject's method.
Answer by TimBur · Dec 31, 2020 at 01:54 AM
The keyword you're looking for is 'this'. It's actually a C# keyword, and it's a reference to the self-object.
Ex:
this.transform = {stuff};
this.Start();
this.currentTile
Note that 'this' doesn't point to a prefab. It points to an instance of a Monobehavior on an instantiated object. So once a prefab is instantiated, the Runner behavior on that instantiated object can act on itself using 'this.'
You can also use the keyword 'gameObject' with a lower-case 'g' to refer to the GameObject that the script is attached to.
Hi Timbur, thanks for taking the time to reply. Unfortunately, your comment doesn't solve my issue. You can see I did in fact use the keyword "this" in my sample code shown above, when trying to locate the position of the object my instantiated prefab.
The real problem at hand is what "this" is pointing to. It is not pointing to the instantiation of the prefab - it is pointing to the prefab itself. If I try to run Destroy(this) on my runner script, for example, it throws an error saying that it can't destroy assets for safety; for the specific issue I'm having on this script, its transform's position is null, since it doesn't exist.
You're right, I didn't read your code, or the words after your code. Apologies. That said, I think the question in the title of this post isn't asking after what you're actually looking for. Having read your words more carefully, I have some maybe helpful ideas.
First, the keyword 'this' always points to a code-type thing. A prefab is a GameObject, and so 'this' will never point to a prefab. You might try using the debugger, setting a breakpoint at the CurrentTile = line, and looking at the value of 'this'.
Your Runner class is a $$anonymous$$onobehavior, which means it is a Component. In Unity, all Components (like your Runner) must be attached to a GameObject. Also, all GameObjects have a Transform component. When you say this.transform
, the 'this' refers to the Runner instance, and 'transform' refers to the Transform component that is attached to this same GameObject. So 'this.transform' is getting a sibling component, the component attached to the same GameObject as the Runner script. Which probably isn't what you are asking for.
You asked "Is the only way to fix my issue to assign a GameObject to the "Runner" class and assign it to its instance on instantiation?" $$anonymous$$aybe. Sort of. If you want Runner to be a $$anonymous$$onobehavior, then yes. You must create a GameObject, and attach a Runner Component to it. That isn't necessarily bad. I often write a Level$$anonymous$$anager-type component, and attach it to a GameObject. That GameObject doesn't do anything other than sit invisibly in the level, but it keeps me organized, and it gives the Level$$anonymous$$anager access to useful $$anonymous$$onobehavior-type methods (Start, Update, etc.). The full list of $$anonymous$$onobehaviour methods is here)
If Runner doesn't need $$anonymous$$onobehavior methods, you can remove the $$anonymous$$onobehavior inheritance from your class definition. You could have Runner inherit from ScriptableObject instead, or just have a vanilla C# class. Neither ScriptableObjects nor vanilla classes require attachment to a GameObject.
But going back to the core of the issue. What is it that you want the Runner class to do? What do you want to do with CurrentTile? If you could talk more about this, I might be able to help better.
Hi - thanks for the reply. I'm pretty sure the "this" in this instance points to the prefab itself - like I said, I can't run destroy(this) in my code because it tells me it can't delete something from the assets folder.
I actually do already have a few "managers" attached to an empty game object - I'll kind of explain my game's architecture to help you understand what I'm having trouble with. I have a Tile $$anonymous$$anager which keeps track of which tiles have units on them, which player they belong to, etc., and a Turn $$anonymous$$anager which keeps track of whose turn it is. A player builds and makes actions with units, and the player's actions send signals to the Tile and Turn managers.
Here's where I'm having trouble. Let's say one player has built a "runner" on a tile, and the same player tries to access this unit on a following turn. The player checks the tile manager to see if it's the player's unit. Then, the "runner" itself, using the script sampled above, sends to the player a list of tiles on which it can act (in this case, move to). The runner then moves to one of these tiles. However, the runner is not moving towards the tile, because the "this" keyword doesn't refer to itself, just the prefab.
Your answer
Follow this Question
Related Questions
Script stopped working, prefabs behaving strangely 0 Answers
GameObject variable points to instance instead of prefab when prefab gets instantiated 2 Answers
Making a bubble level (not a game but work tool) 1 Answer
Show transparent version of prefab before instantiation 1 Answer
Using Resources.load with a variable 1 Answer