public class player_movement : MonoBehaviour
{
// Start is called before the first frame update
public Transform position;
void Start()
{
position = GetComponent<Transform>();
}
how do I acsess the position variable in another script
There is no reason for any of this at all.
Your component player_movement is of type MonoBehaviour which is a Behaviour which is a Component and therefore already has the inherited property transform returning the Transform reference if the GameObject your component is attached to.
So whoever has a reference to your component immediately also has access to its .transform.position anyway.
As to how get that reference, there are thousands of different ways. The most typical ones
public player_movement playerMovement;
simply drag and drop it in vis the Inspector.
On another component attached to the same GameObject use
var playerMovement = GetComponent<player_movement>();
or if there is only one in the scene anyway
var playerMovement = FindObjectOfType<player_movement>();
either way in the end as said you can simply use
var playerTransform = playerMovement.transform;
and do with it whatever you want like accessing its position
var playerPosition = playerTransform.position;
In my game I have a game object called ExclamationMark which I want to spawn above enemies heads when the player gets into range and they become "Alerted".
I've made this simple script to do that, but for some reason it will only work on one game object.
My enemy script:
void CheckForPlayer()
{
// Define player and get position
var player = GameObject.FindWithTag("Player");
var playerPos = (int)player.transform.position.x;
if (transform.Find("Graphics"))
{
// Define gameobject position
var enemyPos = transform.Find("Graphics").gameObject.transform.position.x;
// Define range to spawn tiles in
var range = 5;
var rangeInfront = enemyPos + range;
var rangeBehind = enemyPos - range;
if (playerPos >= rangeBehind && playerPos <= rangeInfront)
{
enemyIsActive = true;
if (transform.Find("ExclamationMark"))
{
var exMark = transform.Find("ExclamationMark").gameObject.GetComponent<ExclamationMarkSpawn>();
exMark.SpawnExclamationMark();
}
}
else
{
enemyIsActive = false;
}
}
}
My ! script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ExclamationMarkSpawn : MonoBehaviour {
public GameObject spawnPos;
public GameObject exclamationMark;
public GameObject exclamationMarkAudio;
public void SpawnExclamationMark()
{
StartCoroutine(GameObject.FindGameObjectWithTag("MainCamera").GetComponent<CameraShake>().Shake(0.2f, 0.2f, 0.2f));
Instantiate(exclamationMark, spawnPos.transform.position, Quaternion.identity);
if (exclamationMarkAudio)
Instantiate(exclamationMarkAudio, spawnPos.transform.position, Quaternion.identity);
StartCoroutine(DestroyExclamationMark());
}
IEnumerator DestroyExclamationMark()
{
yield return new WaitForSeconds(1);
var children = new List<GameObject>();
foreach (Transform child in transform) children.Add(child.gameObject);
children.ForEach(child => Destroy(child));
}
}
Just to be sure: I assume every player has its own instance of both of your scripts attached (some maybe nested further in their own hierarchy).
I assume that since you are using transform.Find which looks for the object by name within it's own children.
In general using Find and GetComponent over and over again is very inefficient! You should in both classes rather store them to fields and re-use them. Best would be if you can actually already reference them via the Inspector and not use Find and GetComponent at all.
In general finding something by name is always error prone. Are you sure they are all called correctly? Or are others maybe further nested?
Note: Find does not perform a recursive descend down a Transform hierarchy.
I would prefer to go by the attached components. You say it has e.g. a RigidBody. If this is the only Rigidbody component in the hierarchy below your objects (usually this should be the case) then you could instead rather simply use
// pass in true to also get disabled or inactive children
Rigidbody graphics = GetComponentInChildren<Rigidbody>(true);
the same for the ExclamationMarkSpawn
// Would be even beter if you already reference these in the Inspector
[SerializeField] private Rigidbody graphics;
[SerializeField] private ExclamationMarkSpawn exclamationMark;
[SerializeField] private Transform player;
private void Awake()
{
if(!player) player = GameObject.FindWithTag("Player");
if(!graphics) graphics = GetComponentInChildren<Rigidbody>(true);
if(!exclamationMark) exclamationMark = GetComponentInChildren<ExclamationMarkSpawn>(true);
}
private void CheckForPlayer()
{
// If really needed you can also after Awake still use a lazy initialization
// this adds a few later maybe unnecessary if checks but is still
// cheaper then using Find over and over again
if(!player) player = FindWithTag("Player");
if(!graphics) graphics = GetComponentInChildren<Rigidbody>(true);
if(!exclamationMark) exclamationMark = GetComponentInChildren<ExclamationMarkSpawn>(true);
var playerPos = (int)player.position.x;
// always if making such a check also give a hint that something might be missing
if (!graphics)
{
// by adding "this" you can now simply click on the message
// in the console and it highlights the object where this is happening in the hierarchy
Debug.LogWarning("graphics is missing here :'( ", this);
return;
}
// Define gameobject position
var enemyPos = graphics.transform.position.x;
// Define range to spawn tiles in
// this entire block can be shrinked down to
if (Mathf.Abs(playerPos - enemyPos) <= 5)
{
enemyIsActive = true;
if (exclamationMark) exclamationMark.SpawnExclamationMark();
}
else
{
enemyIsActive = false;
}
}
The same also in ExclamationMarkSpawn.cs.
I would additionally only allow 1 exclamation mark being visible at the same time. For example when a player jitters in the distance especially assuming both, the player and the enemy, I would move the entire instantiation to the routine and use a flag. Especially since this is called every frame in Update while the player stays in the range!
Also re-check and make sure your enemies are not maybe referencing the same spawnPos and thus all instantiating their exclamation marks on top of each other.
public class ExclamationMarkSpawn : MonoBehaviour
{
public Transform spawnPos;
public GameObject exclamationMark;
public GameObject exclamationMarkAudio;
[SerializeField] private CameraShake cameraShake;
// only serialized for debug
[SerializeField] private bool isShowingExclamation;
private void Awake()
{
if(!cameraShake) cameraShake = Camera.main.GetComponent<CameraShake>();
// or assuming this component exists only once in the entire scene anyway
if(!cameraShake) cameraShake = FindObjectOfType<CameraShake>();
}
public void SpawnExclamationMark()
{
StartCoroutine(ShowExclamationMark());
}
private IEnumerator ShowExclamationMark()
{
// block concurrent routine call
if(isShowingExclamation) yield brake;
// set flag blocking concurrent routines
isShowingExclamation = true;
// NOTE: Also for this one you might want to rather have a flag
// multiple enemy instances might call this so you get concurrent coroutines also here
StartCoroutine(cameraShake.Shake(0.2f, 0.2f, 0.2f));
Instantiate(exclamationMark, spawnPos.position, Quaternion.identity);
if (exclamationMarkAudio) Instantiate(exclamationMarkAudio, spawnPos.position, Quaternion.identity);
yield return new WaitForSeconds(1);
var children = new List<GameObject>();
foreach (var child in transform.ToList()) children.Add(child.gameObject);
children.ForEach(child => Destroy(child));
// give the flag free
isShowingExclamation = false;
}
}
Try this;
if (transform.Find("ExclamationMark"))
{
var exMark = transform.Find("ExclamationMark").gameObject.GetComponent<ExclamationMarkSpawn>();
exMark.SpawnExclamationMark(transform.position); //Add transform.position here
}
public void SpawnExclamationMark(Vector3 EnemyPos)
{
StartCoroutine(GameObject.FindGameObjectWithTag("MainCamera").GetComponent<CameraShake>().Shake(0.2f, 0.2f, 0.2f));
Instantiate(exclamationMark, EnemyPos, Quaternion.identity);
if (exclamationMarkAudio)
Instantiate(exclamationMarkAudio, EnemyPos, Quaternion.identity);
StartCoroutine(DestroyExclamationMark());
}
I made a simple message box that should show a message to the user. It's a prefab and does a couple of things, mostly animations upon instantiation. To run code upon instantiation I used the Start() function.
It worked when I already knew what to message but I need something like a constructor, that runs before Start(), but upon instantiation and can take parameters.
Now, I'm fully aware that I could instantiate, set the message and run everything - so using 3 lines of code where I instantiate it, but I'm curious if there is another, more proper solution? All I found on the net was that instantiate, then do something.
EDIT:
My calling of a messagebox to show up:
var timeBox =
Instantiate(messageBox, penaltySpawnLoc.position, penaltyPrefab.transform.rotation, transform);
var scr = timeBox.GetComponent<MessageBox>();
scr.OnCreated(message);
OnCreated does the initialization, show up animations, so basically everything.
But it needs a string input to know what to show up and I don't want to set the text value "on the fly" - it would make appear some weird flickering when the messagebox is visible but the text isn't set.
EDIT2:
the last parameter transform in the Instantiation is the Canvas this script is on as this is a UI script. That parameter means that the freshly instantiated GameObject is the child of that.
EDIT3:
timeBox is just an instance of messageBox, they are GameObjects.
One message box is used only once. Its purpose is to appear with the message and after like 0.5 seconds fade out and move away. After moving away it destroys itself.
You can do this with a function or extension method.
In this case, extension method is more appropriate.
We will make an extension object with Object instead of GameObject. Since GameObject inherits from Object, this extension method should work for both Object, GameObject and Transform.
Create a class called ExtensionMethod then paste everything below inside it.
using UnityEngine;
public static class ExtensionMethod
{
public static Object Instantiate(this Object thisObj, Object original, Vector3 position, Quaternion rotation, Transform parent, string message)
{
GameObject timeBox = Object.Instantiate(original, position, rotation, parent) as GameObject;
MessageBox scr = timeBox.GetComponent<MessageBox>();
scr.OnCreated(message);
return timeBox;
}
}
Usage:
Only one line call in the Start function should handle all other tasks.
public class Test: MonoBehaviour
{
GameObject messageBox = null;
Transform penaltySpawnLoc = null;
GameObject penaltyPrefab = null;
void Start()
{
gameObject.Instantiate(messageBox, penaltySpawnLoc.position, penaltyPrefab.transform.rotation, transform, "Hello");
}
}
I'd use a Factory for the message boxes, so something like
var timebox = MessageBoxFactory.Create(message);
timebox.Show();
and in the factory class do your setup and return the message box
public static MessageBox Create(string message) {
var newTimeBox = Instantiate(messageBox, penaltySpawnLoc.position, penaltyPrefab.transform.rotation, transform);
var scr = newTimeBox.GetComponent<MessageBox>();
scr.SetMessage(message);
return scr;
where Show() is the new OnCreated(). I find this pattern helps out with reducing the calling code and if I need tweaks to how the thing I want gets created it is all in one place with the shared factory code.
Had this problem today and came up with this. The idea is to replace the Start() method with a custom one and implement a wrapper that takes your prefab and its script's constructor as arguments for its (the wrapper) constructor, then have the wrapper instantiate and call your constructor. You can modify this to support more of the overloads provided by unity, but for now it works just fine for me.
EditableGO.cs:
public class EditableGO<T>
{
GameObject toInstantiate;
Action<T> constructor;
public EditableGO(Action<T> constructor, GameObject toInstantiate = null)
{
this.constructor = constructor;
this.toInstantiate = toInstantiate == null? new GameObject() : toInstantiate;
}
public GameObject Instantiate(Vector3 position, Quaternion rotation, Transform parent = null)
{
GameObject instance;
if(parent != null)
instance = GameObject.Instantiate(toInstantiate, position, rotation, parent);
else
instance = GameObject.Instantiate(toInstantiate, position, rotation);
constructor(instance.GetComponent<T>());
return instance;
}
}
To pass the Action<T> constructor to the constructor of EditableGO, what I do is implement a static constructor generator. Here's an example of a simple customizable particle, notice the constructor generator:
public class SimpleParticle : MonoBehaviour
{
public float Duration, Distance, Speed, traveledDistance, birth;
public Vector3 Direction, Size;
public static Action<SimpleParticle> ConstructorGenerator(float duration, float distance, Vector3 size, float speed, Vector3 direction)
{
return (self) => {
self.Duration = duration;
self.Distance = distance;
self.Size = size;
self.Direction = direction;
self.Speed = speed;
self.Init();
};
}
void Init()
{
birth = Time.time;
transform.localScale = Size;
}
void Update()
{
float deltaDist = Speed * Time.deltaTime;
traveledDistance += deltaDist;
transform.position += Direction * deltaDist;
if(Time.time - birth > Duration)
Destroy(this.gameObject);
}
}
And then, instantiating new custom particles is as easy as
EditableGO<SimpleParticle> particle = new EditableGO<SimpleParticle>(SimpleParticle.ConstructorGenerator(duration, distance, size, speed, direction), Game.Resources.SimpleParticle);
particle.Instantiate(position, rotation);
I have a GameObject(EnemyProducer) that instantiate another GameObject(EnemyFormation) which has several children (actual enemies).
However, when i instantiate EnemyFormation Gameobject it does not have any children!
EnemyFormation is a prefab with all the required children.
This is how it looks like:
Here is the EnemyProducer code that Instantiates EnemyFormation:
public class EnemyProducer : MonoBehaviour {
EnemyFormation enemyGroup;
Transform enemyFormationTransform;
public float speed;
float boundary, currentY;
bool goingDown = true;
public GameObject enemyFormation;
// Use this for initialization
void Start () {
// Create enemyformation
enemyFormation = Instantiate (enemyFormation);
enemyFormation.transform.parent = transform;
enemyGroup = enemyFormation.GetComponent<EnemyFormation>();
boundary = Camera.main.orthographicSize;
enemyFormationTransform = enemyFormation.transform;
}
void Update () {
// if all enemies are killed, create a new one
if (!enemyGroup.hasEnemy ()) {
enemyFormation = Instantiate (enemyFormation);
enemyFormation.transform.parent = transform;
enemyGroup = enemyFormation.GetComponent<EnemyFormation>();
enemyFormationTransform = enemyGroup.gameObject.transform;
}
}
}
The first instantiation may be successful because it cloned the prefab from the Editor but then you reassigned the newly cloned prefab to the enemyFormation. When all enemies (children) were destroyed (assume that you were using Destroy()) then enemyFormation would contain no child. Next time you Instantiate(enemyFormation) you were expected to get a gameObject without children because it were not the prefab from the Editor anymore (it was reassigned by you).
Sorry I have to delete the previous answer. It is wrong anyway.
Basically is there an actually time saving way to creat a function that lets you create a GameObject specified by the function's parameter(s)?
like:
public void thing_maker(string gameobject_name, string sprite_name, string rg_body_name)
this example should do what you need.
it requires that you have a folder named Resources and that the sprite you want to load will be inside that folder.
another option is doing GameObject go = Instantite(SomePrefabName) as GameObject insted of new GameObject(), in case you have a prefab which is ready and you only want to maybe change some of its components' values.
Good luck.
public GameObject thing_maker(string gameobject_name, string sprite_name, string rg_body_name)
{
GameObject go = new GameObject(gameobject_name);
SpriteRenderer sr = go.AddComponent<SpriteRenderer>();
sr.sprite = Resources.Load<Sprite>(sprite_name);
Rigidbody rb = go.AddComponent<Rigidbody>();
/* now if you want to change values of the components you can just do it
by accesing them directly. for instance: rb.isKinematic = true; will
change the isKinematic value of this rigidbody to true. */
return go;
}
It seems to me that you could try something like this:
public void ThingMaker(string gameobject_name, Sprite spriteToDraw) {
GameObject newObj;
newObj= new GameObject(gameobject_name);
newObj.AddComponent<Rigidbody2D>();
newObj.AddComponent<SpriteRenderer>();
newObj.GetComponent<SpriteRenderer>().sprite = spriteToDraw;
}