Understanding Unity's GameObject.Find(), GetComponent() and objects recycling - c#

New to unity.
So I created a simple a simple muzzle flash particle animation that is supposed to be displayed on enemies gun when the player gets close to him, simulating a shot without the actual bullet. However I get a null reference exception in this part muzzleFlash.Play(); I believe it's because I am not actually getting the muzzle flash component in the start function with the code I have, actually I know that is it after going to in to debug mode I found out. I am having a really hard time figuring out how to access that component. Below is my code and I'm also posting a picture of my hierarchy. Thanks in advance.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class StaticShootingEnemy : MonoBehaviour
{
[SerializeField] private float _range = 12f;
private Transform _player;
private bool _alive;
private float _distance;
private ParticleSystem muzzleFlash;
// Use this for initialization
void Start()
{
_player = GameObject.Find("Player").transform;
_alive = true;
muzzleFlash = (ParticleSystem)this.gameObject.GetComponent("muzzleFLash");
}
// Update is called once per frame
void Update()
{
_distance = Vector3.Distance(this.transform.position, _player.transform.position);
if (_alive && _distance < _range)
AttackPlayer();
}
private void AttackPlayer()
{
//Turning enemy to look at player
transform.LookAt(_player);
Ray ray = new Ray(transform.position, transform.forward);
RaycastHit hit;
if (Physics.SphereCast(ray, 0.75f, out hit))
{
//TODO: Fix enemy shooting fast when gettting close to him.
GameObject hitObject = hit.transform.gameObject;
if (hitObject.GetComponent<PlayerController>())
{
muzzleFlash.Play();
Debug.Log("Player Hit!");
}
else
muzzleFlash.Stop();
}
}
public void SetAlive(bool alive)
{
_alive = alive;
}
}

You probably have an object "muzzleFlash" as child to object your script attached to. So, in this case you'd better have a reference to your ParticleSystem object that is called muzzleFlash.
[SerializeField] private ParticleSystem muzzleFlash; // drag and drop your ParticleSystem muzzleFlash in inspector
or at least you could find that muzzleFlash like this
GameObject muzzleFlashObj = GameObject.Find("muzzleFlash");
ParticleSystem muzzleFlash = muzzleFlashObj.GetComponent<ParticleSystem>();
In your case it's null because there is probably no component that is called MuzzleFlash on that object. The component you want to get is ParticleSystem.

What component is the staticshootingenemy script on? if it is not on the same component as the particle system then its not finding it because this.gameObject.GetComponent("muzzleFLash") does not exist on that component. You can use GameObject.Find("muzzleFLash") to search for the particle system.

So back to your comment, you could implement something like a pool for your muzzle flashes.
public class MuzzleFlashEffect : MonoBehaviour
{
[SerializeField] private ParticleSystem particleEffect;
private Queue<MuzzleFlashEffect> poolQueue;
public void SetPoolQueue(Queue<MuzzleFlashEffect> queue)
{
poolQueue = queue;
}
public void Play()
{
StartCoroutine(Playing());
}
private IEnumerator Playing()
{
particleEffect.Play();
while (particleEffect.isPlaying)
{
yield return null; // wait until particle animation is done, then recycle effect
}
particleEffect.Stop();
poolQueue.Enqueue(this); // recycle this effect
}
// you can do the same thing for Animation as well, or even write some abstract PoolableVFX class that would be usefull for Animation , ParticleSystems etc..
}
//assume you have some game controller that manage what is going on in the scene
public class GameController : MonoBehaviour
{
[SerializeField] private MuzzleFlashEffect muzzleFlashPrefab;
private Queue<MuzzleFlashEffect> poolQueue = new Queue<MuzzleFlashEffect>(10); // 10 is enough i guess and it's good to set it at instantiation to avoid memory fragmentation
private MuzzleFlashEffect GetMuzzleFlash(Vector3 pos, Quaternion rot)
{
MuzzleFlashEffect muzzleFlash;
// if we already have some effects, then play them, otherwise make a new one and recycle it then
if (poolQueue.Count > 0)
{
muzzleFlash = poolQueue.Dequeue();
}
else
{
muzzleFlash = Instantiate(muzzleFlashPrefab);
muzzleFlash.SetPoolQueue(poolQueue);
}
muzzleFlash.transform.position = pos;
muzzleFlash.transform.rotation = rot;
return muzzleFlash;
}
void Update()
{
// your fancy logic ...
GameObject mutantGunEnd = new GameObject("mutant");
//assume that here you want your muzzle flash effect, so you do:
var muzzleFlash = GetMuzzleFlash(mutantGunEnd.transform.position, mutantGunEnd.transform.rotation); // or you might want to pass mutantGunEnd.transform.forward instead, it depends...
muzzleFlash.Play();
// your fancy logic ...
}
}
So, in this case you have only as many instance of ParticleEffect as you need and saving some resources. You could also create a universal generic pool for any type of object you want to recycle. (you want to recycle instead of instantiation, cuz Instantiation is cpu expensive).
M.b this is a bit overkill here, but i just wanted to share how would i think about this here

Related

Activating animation when within radius

I am trying to create a script for my enemy turret, but it is not going well. I have a couple animations of the turret being activated and deactivated. What I need is that based on the distance from the player, it plays either animation. So once it moves inside the detection radius it plays the activation animation and once it is outside it plays the deactivation animation. Most of the other ways I try require me to create an Animation Controller, which I have little experience in using. I want a simple way to play one animation once it is inside and play a different one when it is outside. I think there was a way to store the animation clip in the script, and then play it. I have attached my current script, so you know what I mean.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyTurret : MonoBehaviour
{
public GameObject Player;
public float DistanceToPlayer;
public float DetectionRadius = 75;
// Start is called before the first frame update
void Start()
{
Player = GameObject.FindGameObjectWithTag("PlayerTank");
}
// Update is called once per frame
void Update()
{
DistanceToPlayer = Vector3.Distance(transform.position, Player.transform.position);
if (DistanceToPlayer<=DetectionRadius)
{
Debug.Log("Within Radius");
}
if (DistanceToPlayer >= DetectionRadius)
{
Debug.Log("Outside Radius");
}
}
}
Calculating and checking the distance of the player for every update() is not ideal. It will work, but it will do more work than it needs to when the player isn't even near it. Its not efficient.
What you may want to do if your player is a Rigidbody, is add a SphereCollider to the turret, set isTrigger=true, set the radius to be your detection radius, and handle the OnTriggerEnter() and OnTriggerExit() events to play or stop animations.
You can also add two public Animiation objects to the script, drag and drop your animations in the editor, then you can use animation.Play() and .Stop() etc. to control the animations.
Something similar to this. Not tested fully, but you can get the idea.
public float detectionRadius = 75;
public Animation activateAnimation;
public Animation deactivateAnimation;
void Start()
{
SphereCollider detectionSphere = gameObject.AddComponent<SphereCollider>();
detectionSphere.isTrigger = true;
detectionSphere.radius = detectionRadius;
detectionSphere.center = Vector3.zero;
}
private void OnTriggerEnter(Collider other)
{
if (other.gameObject.tag == "PlayerTank")
{
activateAnimation.Play();
}
}
private void OnTriggerExit(Collider other)
{
if (other.gameObject.tag == "PlayerTank")
{
deactivateAnimation.Play();
}
}
Your animations must not loop otherwise you will have to add more logic to check if animation.isPlaying and do your own animation.Stop() etc.

Can only spawn one object at once in unity

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());
}

Calling Transform component of an object in another class C#

I am working on a tutorial and the "Side objectives" that they don't walk you through to try and get a feel for it.
So, the way things work at this time is that there is the Player object. The player object has the player script.
public class Player : MonoBehaviour {
private Animator anim;//reference for animator component
private Rigidbody rigidBody;//reference to component for rigidbody
private AudioSource audioSource;
[SerializeField] private float force = 100f;
[SerializeField] private AudioClip sfxJump;
[SerializeField] private AudioClip sfxDeath;
}
void Awake() {//these are assertions that will ensure when writing the cocde that you wont miss them. use for team work.
Assert.IsNotNull (sfxJump);
Assert.IsNotNull (sfxDeath);
}
private bool jump = false; //check for jump
// Use this for initialization
void Start () {//all these are getting components at the start to update them as the code goes onwards.
anim = GetComponent<Animator> ();
rigidBody = GetComponent<Rigidbody> ();
audioSource = GetComponent<AudioSource> ();
positionStart = GetComponent<Transform> ();
}
// Update is called once per frame
void Update () {
if (!GameManager.instance.GameOver && GameManager.instance.GameStarted) {
if (Input.GetMouseButtonDown (0)) {//if press mouse key
GameManager.instance.PlayerStarted ();
rigidBody.useGravity = true;//turn gravity on for component so it goes back down.
audioSource.PlayOneShot (sfxJump);
anim.Play ("jump");//play the animation jump
jump = true;
}
}
}
//Fixed update for physics
void FixedUpdate() {//use this for any physics due to frame rate. time.deltatime wont cut it.
if (jump == true) {//if we are jumping, turn the jump off.
jump = false;
rigidBody.velocity = new Vector2 (0, 0);//turn velocity to 0 so speed doesnt increase while falling
rigidBody.AddForce (new Vector2 (0, force), ForceMode.Impulse);//give a impulse upwards.
}
//print (rigidBody.velocity.y);//print velocity. turn this shit off.
}
//Code to create collision with obstacles and then die and fall through the floor.
void OnCollisionEnter (Collision collision) {//call collision component
if (collision.gameObject.tag == "obstacle") {//if you slap a tagged object called obstacle
rigidBody.AddForce (new Vector2 (-50, 20), ForceMode.Impulse);//add force to push back cause you ded
rigidBody.detectCollisions = false;//turn off the ability to detect collisions
audioSource.PlayOneShot (sfxDeath);//play ded noise
GameManager.instance.PlayerCollided ();
GameManager.instance.Restart ();
}
}
}
The game manager, of course exists in the camera to control the states of the game.
public static GameManager instance = null;//only one in memory. only one gamemanager ever.
[SerializeField] private GameObject mainMenu;
[SerializeField] private GameObject replayBtn;
[SerializeField] private GameObject playBtn;
private bool gameEnd = false;
private bool gameStarted = false;
private bool playerActive = false;
private bool gameOver = false;
//getters setters start
public bool PlayerActive {
get { return playerActive; }
}
public bool GameOver {
get { return gameOver; }
}
public bool GameStarted {
get { return gameStarted; }
}
//to create a state between gameover and main menu
public bool GameEnd {
get {return gameEnd; }
}
//getter setters end
void Awake(){
if (instance == null) {
instance = this;//this means the current instance. one instance of this class.
} else if (instance != this) {//if a seocnd one gets created destroy that bitch.
Destroy (gameObject);
}
DontDestroyOnLoad (gameObject);//allows a game object to persist between the scene. Dont need with one scene.
}
// Use this for initialization
void Start () {
replayBtn.SetActive (false);
}
// Update is called once per frame
void Update () {
}
public void PlayerCollided(){
gameOver = true;
}
public void PlayerStarted(){
playerActive = true;
}
public void EnterGame (){
mainMenu.SetActive(false);
gameStarted = true;
}
//When player dies start coroutine Hold.
public void Restart (){
StartCoroutine (Holdexit());
}
//The hole Coroutine waits 2 seconds then turns on the menu.
IEnumerator Holdexit (){
yield return new WaitForSeconds (2);
playBtn.SetActive (false);
replayBtn.SetActive (true);
mainMenu.SetActive (true);
//add character movement to location
}
}
So, When the player hits the object he dies, loses the ability to touch colliders and falls through the map, after 2 seconds the main menu comes back and the play button is replaced with a replay button. When I press replay, I need to reset the position, the state of the game, and the ability to collide.
I Tried all kinds of things. I did a get component for transform and tried to call it in the coroutine and then set it there, but I couldnt figure it out. I tried just changing the position after etc after the game managers state to restart gets called but the position change occurs before the main menu comes back on because its not being used in the coroutine.
Once thing I though would work, is i created a new method,
public void PlayerReset (){
if (GameManager.instance.Restart()){
//put new changes to player here.
}
}
The errors I came across here was I could not convert type void to bool, I assume its cause I was trying to say if the restart instance existed then function, but the way the restart function is created isn't true or false its just - is.
I really appreciate any help. I think what im going to try and do is make another script to the side and have it call the class of player to pull the components, and then manipulate them from there maybe. AUGH. So confusing. Lmao.
You can just set a public Transform variable on your object, and then in the inspector, drag the selected transform you want to call to that variable.
When that's done, you can use that transform variable in any way you want.
You can save the start position of the player in GameManager as it's a singleton. So, you set the position of the player to this saved position after restart.
You'll need to reinitialize all the variables(i.e. gameEnd,gameStarted, playerActive, gameOver etc.) on restart.
if(GameManager.instance.Restart()) will not work, as Restart() returns void not a boolean.

How to make a projectile turn with a arc

I have a cannon that fires a bullet in a parabolic arc. Right now when I fire the bullet stays in the same rotation as it was when it fired out of the cannon.
How do I make it so the bullet's rotation follows the arc as it travels through the air?
I tried the following as a script running on the bullet
Exhibit 1
public class PointingBehaviour:MonoBehaviour
{
private Rigidbody rb;
private void Awake()
{
rb = GetComponent<Rigidbody>();
}
public void Update()
{
transform.up = rb.velocity;
}
}
And that works fairly well. but I see a slight flicker on the first frame the object exists (I think this is because the velocity is still 0 at this point) and the object spins uncontrollably once it hits the ground.
I got it to stop flickering at the start and stop spinning when it lands by doing the following
public class BulletController : MonoBehaviour
{
private Rigidbody _rb;
private bool _followArc;
private bool _firstFrame;
private void Start()
{
_rb = GetComponent<Rigidbody>();
_firstFrame = true;
_followArc = true;
}
public void LateUpdate()
{
if (_followArc && !_firstFrame)
transform.up = _rb.velocity;
_firstFrame = false;
}
public void OnCollisionEnter(Collision collision)
{
_followArc = false;
}
}
But if I happen to bump something in the air it stops following the arc and just does a free tumble till it lands. What is the "Correct" way to do what I want to do?
Because people wanted to see it, here is the code for spawning the bullet.
public class TankController : MonoBehaviour
{
private Transform _barrelPivot;
private Transform _bulletSpawn;
public GameObject Bullet;
public float FirePower;
public float RotationSpeed;
public float TiltSpeed;
private void Start()
{
_barrelPivot = GetComponentsInChildren<Transform>().First(x => x.CompareTag("BarrelPivotPoint"));
_bulletSpawn = GetComponentsInChildren<Transform>().First(x => x.CompareTag("BulletSpawn"));
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
FireCannon();
}
//(SNIP) Handle turning left and right and pivoting up and down.
}
private void FireCannon()
{
var newBullet = SpawnBullet();
var rb = newBullet.GetComponent<Rigidbody>();
rb.AddForce(_bulletSpawn.up * FirePower, ForceMode.Impulse);
}
private GameObject SpawnBullet()
{
var newBullet = (GameObject) Instantiate(Bullet, _bulletSpawn.position, _bulletSpawn.rotation);
newBullet.transform.localScale = Bullet.transform.localScale;
return newBullet;
}
}
I believe what you're saying is - your're script exhibit1 works great.
If you think about it, all you have to do is turn off that behavior, when you want to.
In this case, you're thinking "that behavior should stop when it hits the ground .. I assume that's what you mean physically.
It's very easy to turn off a MonoBehaviour, as you know just enabled = false;
So, you have some script somewhere like Bullet or perhaps BulletBoss
In there you'll have a variable private PointingBehaviour pb and you'll just pb.enabled = false
(I can't tell you "when" you want to do that, it depends on your game .. so it might be something like "when altitude is less than blah" or "when I hit something" ... probably OnCollisionEnter related.)
Note that - I'm sure you know this - for pointing behavior for a projectile, just setting it along the tangent is pretty basic.
A lovely very easy thing to do when you're writing a pointing behavior for a projectile like that, try lerping it gently to the tangent. The result is amazing real looking. Next perhaps add some random "wiggles" which is very bomb-like. It's amazing how the user can see such things, only only a few frames. (The next step up would be real air physics I guess!)
Note that, certainly, PointingBehaviour should just be its own script. You must keep behaviors totally isolated.
Regarding LateUpdate mentioned, there is never a need to use it.
Unity offer a "script order execution" system (see Preferences menu option), if one truly wants to order within the frame. But about the only reason to do that would be perhaps for some experimentation reason.
It's rather like "using a global" - there's just no reason for it. As with any code, if you have to do something in a given order, just call in order.

Transferring a Vector3 variable from one script (WayPointPositioner) to another and change it into a transform (Walking)

I'm having a bit of trouble getting the Vector3 wayPointPosition to my other script called Walking and changing it into the Transform target. My troubles lie in the fact that I'm trying to grab this dynamic variable from WayPointPositioner (it changes depending on what object is clicked in the stage and whether the player overlaps with this waypoint) and import and use it in another script.
Below is the code I'm using.
WayPointPositioner
using UnityEngine;
using System.Collections;
public class WayPointPositioner : MonoBehaviour {
public Vector3 wayPointPosition = Vector3.zero;
private bool checkPlayerWaypointCollision;
void Start()
{
}
void OnTriggerStay2D (Collider2D other)
{
// Check if collision is occuring with player character.
if (other.gameObject.name == "Player")
{
checkPlayerWaypointCollision = true;
}
else
{
checkPlayerWaypointCollision = false;
}
}
//Check if object is clicked
void OnMouseDown ()
{
// If its the player, then return a new position for the player to move to for walking
// Else debug that its not so
if (checkPlayerWaypointCollision == false)
{
Debug.Log ("Object not colliding and retrieving position");
Debug.Log (wayPointPosition);
Debug.Log (gameObject.name);
wayPointPosition = new Vector3 (transform.position.x, transform.position.y, 10);
wayPointPosition = Camera.main.ScreenToWorldPoint(wayPointPosition);
}
else
{
Debug.Log ("Object is colliding, no movement needed");
}
}
}
Walking
using UnityEngine;
using System.Collections;
public class Walking : MonoBehaviour {
public Transform target;
public WayPointPositioner wayPointPosition;
public bool walkingAnimation = false;
private Animator anim;
void Awake ()
{
anim = GetComponent<Animator> ();
wayPointPosition = GameObject.FindGameObjectWithTag ("Waypoint").GetComponent<WayPointPositioner> ();
}
void Start ()
{
}
void Update ()
{
Debug.Log ("This is in Walking, WPP =" + wayPointPosition);
}
}
As you can see I'm trying to import the wayPointPosition from the seperate class which is attached to the gameobjects called "Waypoint" (In my current layout those are empty objects with circle colliders to check if they have been clicked). However when I run this, I am not getting my Vector, but I'm getting the name of the last waypoint in the hierarchy (I have currently 6 waypoints which can be clicked) and not a Vector.
I hope someone is able to help me / point out my mistake. I'm still learning C# so I might've made a strange / odd assumption which isn't working.
Kind regards,
Veraduxxz.
It looks like invoking GameObject.FindGameObjectWithTag("Waypoint").GetComponent<WayPointPositioner>(); retrieves a component from the game object which matches the specified tag, as well as a type argument T which derives from MonoBehavior.
Calling this should actually give you an instance of your WayPointPositioner class, which you can then pass to whichever methods you want, and interact with its Vector3 however you would like.

Categories