Instantiate Object Only once on game start Unity 3D - c#

I have a game with several levels, each level has 6 scenes, the game start directly without any menu scene, and when the player open the game he can continue from the last scene that he already reached.
I want to instantiate some elements only on game opening (like Best score, Tap to play etc...), I mean that they should be instantiated only once on the start of the game (on the level he reached).
I tried this code in GameManager but it instantiate the elements in every scene:
public GameObject PlayButton;
bool GameHasEnded = false;
public float RestartDelay = 2f;
public float NextLevelDelay = 5f;
public int level_index;
private static bool loaded = false;
private void Start()
{
if (!loaded)
{
loaded = true;
level_index = PlayerPrefs.GetInt("Last_Level");
SceneManager.LoadScene(level_index);
}
GameObject canvas = GameObject.Find("Canvas");
GameObject play = Instantiate(PlayButton, canvas.transform.position, Quaternion.identity);
play.transform.SetParent(canvas.transform, false);
}
public void CompleteLevel()
{
Invoke("NextLevel", NextLevelDelay);
}
public void EndGame()
{
if (GameHasEnded == false)
{
GameHasEnded = true;
Invoke("Restart", RestartDelay);
}
}
void NextLevel()
{
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex +1);
level_index = SceneManager.GetActiveScene().buildIndex + 1;
PlayerPrefs.SetInt("Last_Level", level_index);
PlayerPrefs.Save();
}
void Restart()
{
SceneManager.LoadScene(SceneManager.GetActiveScene().path);
}

You already have an if block there with the static flag loaded
Since you there load another scene you need a similar second flag e.g.
private static bool loadedPrefab = false;
private void Start()
{
if (!loaded)
{
loaded = true;
level_index = PlayerPrefs.GetInt("Last_Level");
SceneManager.LoadScene(level_index);
// Return because you don't want to execute the rest yet
// but instead in the new loaded scene
return;
}
// The same way skip if the prefab was already loaded before
if(!loadedPrefab)
{
loadedPrefab = true;
GameObject canvas = GameObject.Find("Canvas");
GameObject play = Instantiate(PlayButton, canvas.transform.position, Quaternion.identity);
play.transform.SetParent(canvas.transform, false);
}
}

Related

C# Coroutine repeating a specific part?

I am making a cookie clicker game for a school project and for my achievement system I want to have a simple animation pop up, and text in a separate menu script that already works perfectly fine.
The issue I am having is that the first two achievements work fine, but after the second one pops up, it just repeats with "Test B" and "Test C" repeating infinitely in the console.
I've tried to have values try to make an 'if' statement void but nothing seems to work.
I feel like I am just eating my own tail with all of the Boolean values I created to get this far.
This is my code below, from only a single script in my Unity file.
Hopefully this has enough information needed for guidance.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class Achievements : MonoBehaviour
{
[SerializeField] GameObject achievementBanner;
[SerializeField] GameObject achievementText;
[SerializeField] GameObject achievementBannerAnimation;
[SerializeField] GameObject achievementTextAnimation;
[SerializeField] GameObject achiOne;
[SerializeField] GameObject achiTwo;
[SerializeField] GameObject achiThree;
[SerializeField] GameObject achiFour;
[SerializeField] GameObject achiFive;
[SerializeField] GameObject achiSix;
[SerializeField] public static int achiementcount;
bool achievementrec = false;
bool achievementani = false;
bool achidone = false;
// Start is called before the first frame update
void Start()
{
achiOne.SetActive(false);
achiTwo.SetActive(false);
achiThree.SetActive(false);
achiFour.SetActive(false);
achiFive.SetActive(false);
achiSix.SetActive(false);
achievementBanner.SetActive(false);
achievementText.SetActive(false);
achievementText.GetComponent<Text>().text = "Achievement: First cookie!";
}
// Update is called once per frame
void Update()
{
print(achiementcount);
print(achidone);
if (achiementcount == 1)
{
if (!achievementani)
{
if (!achievementrec)
{
print("Test D");
achiOne.SetActive(true);
achievementBanner.SetActive(true);
achievementText.SetActive(true);
StartCoroutine(AchievementGot());
///achidone = true;
}
}
}
if (achidone == true)
{
///print("Test");
///achidone = false;
if (achiementcount == 2)
{
achievementText.GetComponent<Text>().text = "Achievement: First Clicker!";
achievementani = false;
achievementrec = false;
if (!achievementani)
{
if (!achievementrec)
{
print("Test C");
achiTwo.SetActive(true);
achievementBanner.SetActive(true);
achievementText.SetActive(true);
///achidone = true;
StartCoroutine(AchievementGot());
}
}
}
}
///print(achidone);
if (achidone == true)
{
///achidone = false;
if (achiementcount == 3)
{
achievementText.GetComponent<Text>().text = "Achievement: First Upgrade!";
achievementani = false;
achievementrec = false;
if (!achievementani)
{
if (!achievementrec)
{
print("Test A");
achiThree.SetActive(true);
achievementBanner.SetActive(true);
achievementText.SetActive(true);
StartCoroutine(AchievementGot());
}
}
}
}
}
IEnumerator AchievementGot()
{
///achievementrec = true;
achievementBannerAnimation.GetComponent<Animation>().Play("Achievement");
achievementTextAnimation.GetComponent<Animation>().Play("AchiText");
yield return new WaitForSeconds(6);
achidone = true;
print("Test B");
///print(achidone);
achievementBanner.SetActive(false);
achievementText.SetActive(false);
achievementani = true;
achievementrec = true;
///achidone = false;
}
}
First of all you should use a List/Array.
Then you probably also want to show the banner only ONCE when the achievement is unlocked the first time so you should also keep track of which one you already showed.
E.g. something like
[Serializable]
public class Achievement
{
// text to display in the banner
public string Label;
// the object to enable
public GameObject GameObject;
// has this been achieved
public bool Unlocked;
// has this achievement been displayed
public bool Displayed;
}
public class Achievements : MonoBehaviour
{
// in general instead of many GetComponent calls use the correct type right away in the Inspector
[SerializeField] private GameObject achievementBanner;
[SerializeField] private Text achievementText;
[SerializeField] private Animation achievementBannerAnimation;
[SerializeField] private Animation achievementTextAnimation;
[SerializeField] private Achievement[] achievements;
// is currently a display routine running already?
private bool currentlyDisplaying;
private void Start()
{
// initially hide all objects
foreach (var achievement in achievements)
{
achievement.GameObject.SetActive(false);
}
achievementBanner.SetActive(false);
achievementText.gameObject.SetActive(false);
}
private void Update()
{
// if currently a display routine is running do nothing
if (currentlyDisplaying) return;
// otherwise go through all achievements
foreach (var achievement in achievements)
{
// check if one is unlocked but hasn't been displayed yet
if (achievement.Unlocked && !achievement.Displayed)
{
// set to displayed and display it
StartCoroutine(DisplayAchievement(achievement));
// break to only handle one at a time
break;
}
}
}
private IEnumerator DisplayAchievement(Achievement achievement)
{
// just in case if other routine is already running do nothing
if (currentlyDisplaying) yield break;
// block other routines from being started
currentlyDisplaying = true;
// set displayed so it is not displayed again
achievement.Displayed = true;
// Enable your objects and set the text accordingly
achievementText.text = achievement.Label;
achievementText.gameObject.SetActive(true);
achievementBanner.SetActive(true);
achievement.GameObject.SetActive(true);
achievementBannerAnimation.Play("Achievement");
achievementTextAnimation.Play("AchiText");
yield return new WaitForSeconds(6);
achievementBanner.SetActive(false);
achievementText.gameObject.SetActive(false);
// allow the next routine to start
currentlyDisplaying = false;
}
}
even better than poll checking within Update would be to rather go for events like e.g.
[Serializable]
public class Achievement
{
// text to display in the banner
public string Label;
// the object to enable
public GameObject GameObject;
// has this been achieved
// private but (de)serializable e.g. for saving via Json etc later on
// and editable via Inspector
[SerializeField] private bool unlocked = false;
// public read-only access
public bool Unlocked => unlocked;
public void Unlock()
{
// is this already unlocked anyway?
if(!unlocked)
{
// if not unlock and invoke event
unlocked = true;
OnUnlocked?.Invoke(this);
}
}
// global event to subscribe to for all achievement unlocks
public static event Action<Achievement> OnUnlocked;
}
and then listen to this
public class Achievements : MonoBehaviour
{
// in general instead of many GetComponent calls use the correct type right away in the Inspector
[SerializeField] private GameObject achievementBanner;
[SerializeField] private Text achievementText;
[SerializeField] private Animation achievementBannerAnimation;
[SerializeField] private Animation achievementTextAnimation;
[SerializeField] private Achievement[] achievements;
// is currently a display routine running already?
private bool currentlyDisplaying;
// First-In first-out collection to work off the unlocked achievements in the UI
private readonly Queue<Achievement> achievementsToDisplay = new ();
private void Start()
{
// initially hide all objects
foreach (var achievement in achievements)
{
achievement.GameObject.SetActive(false);
}
achievementBanner.SetActive(false);
achievementText.gameObject.SetActive(false);
// listen to the event
Achievement.OnUnlocked += OnAchievementUnlocked;
}
private void OnDestroy()
{
// to avoid exceptions unregister the listener from the event
Achievement.OnUnlocked -= OnAchievementUnlocked;
}
// called everytime an achievement is unlocked
private void OnAchievementUnlocked(Achievement achievement)
{
// add achievement to the queue to work
achievementsToDisplay.Enqueue(achievement);
// if there isn't a worker routine running already anyway
if (!currentlyDisplaying)
{
// start one now
StartCoroutine(DisplayAchievements());
}
}
private IEnumerator DisplayAchievement()
{
// just in case if other routine is already running do nothing
if (currentlyDisplaying) yield break;
// block other routines from being started
currentlyDisplaying = true;
// as long as there are new achievements in the queue simply continue directly with the next one
while(achievementsToDisplay.Count > 0)
{
var achievement = achievementsToDisplay.Dequeue();
// Enable your objects and set the text accordingly
achievementText.text = achievement.Label;
achievementText.gameObject.SetActive(true);
achievementBanner.SetActive(true);
achievement.GameObject.SetActive(true);
achievementBannerAnimation.Play("Achievement");
achievementTextAnimation.Play("AchiText");
yield return new WaitForSeconds(6);
achievementBanner.SetActive(false);
achievementText.gameObject.SetActive(false);
}
// when queue is worked off terminate for now
// and allow the next routine to be started later
currentlyDisplaying = false;
}
}

Move objects in a specific location

Trying to move the object to a specific location I used the below code, but after releasing the mouse, the position changed to a different position.
So what I'm trying to do is, forklift picks up object and places it in specific place.
Does anybody know where I am going wrong or have another idea?
public GameObject item;
public GameObject tempParent;
public Transform guide;
void Start()
{
item.GetComponent<Rigidbody>().useGravity = true;
}
// Update is called once per frame
void Update()
{
}
private void OnMouseDown()
{
item.GetComponent<Rigidbody>().useGravity = false;
item.GetComponent<Rigidbody>().isKinematic = true;
item.transform.position = guide.transform.position;
item.transform.rotation = guide.transform.rotation;
item.transform.parent = tempParent.transform;
}
private void OnMouseUp()
{
float horizontalInput = 24.63001f;
float verticalInput = 1.017074f;
item.GetComponent<Rigidbody>().useGravity = false;
item.GetComponent<Rigidbody>().isKinematic = false;
transform.position = new Vector3(horizontalInput, verticalInput, 23.95186f);
}

Everything breaks after using PlayOneShot

In a game I am working on, the player uses a cube to open doors. When the player interacts with the cube while near the door, the door slides open. I am now trying to make it so that while it slides open, it makes a sound as doors should but I kinda ran into some issues. When I tried to add in the sound for the door, it ignored the part where the door should open altogether.
Here is what I did:
I added an AudioSource to the Cube's child object, CORE because the Cube object already contains an AudioSource which will play at the same time as this sound and assigned it in my Cube's script...
public AudioSource Woosh;
void Start()
{
//Debug.Log("Script initialized!");
Hm = gameObject.GetComponent<AudioSource>();
anim = GameObject.Find("TheFirstDoor").GetComponent<Animator>();
//Door = GameObject.Find("TheFirstDoor").GetComponent<AudioSource>();
Woosh = GameObject.Find("CORE").GetComponent<AudioSource>();
}
Here's the interactive part of the script, it runs a check but for some reason, it is pretending CanBeKey is false...
public void OnActivate()
{
if(HasPlayed == false) //Have I played? Has the Time out passed?
{
HasPlayedFirstTime = true;
Hm.Play();
HasPlayed = true;
PlayTimeOut = 30.0f;
if(CanBeKey == true)
{
anim.Play("Door_Open");
//Door.Play();
StartCoroutine(PlayDaWoosh());
Debug.Log("OPENING!");
}
}
}
Now here is the IEnumerator part of the script, PlayDaWoosh()
IEnumerator PlayDaWoosh()
{
Woosh.PlayOneShot(Woosh.clip);
yield return new WaitForSeconds(Woosh.clip.length);
}
I am aware that the last code snippet is a bit messy but it was the best thing I can think of.
Here is the full script in case you are that curious.....
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CUBE_CORE : MonoBehaviour
{
public AudioSource Hm; // HM!!!
private bool HasPlayed = false;
public float PlayTimeOut = 0.0f;
public static bool CanBeKey = false;
public Animator anim;
public static bool HasPlayedFirstTime = false;
public static AudioSource Door;
public AudioSource Woosh;
// Start is called before the first frame update
void Start()
{
//Debug.Log("Script initialized!");
Hm = gameObject.GetComponent<AudioSource>();
anim = GameObject.Find("TheFirstDoor").GetComponent<Animator>();
//Door = GameObject.Find("TheFirstDoor").GetComponent<AudioSource>();
Woosh = GameObject.Find("CORE").GetComponent<AudioSource>();
}
// Update is called once per frame
void Update()
{
if(HasPlayed == true)
{
if (PlayTimeOut < 0.0f)
{
HasPlayed = false;
}
PlayTimeOut -= Time.smoothDeltaTime;
}
}
public void OnActivate()
{
if(HasPlayed == false) //Have I played? Has the Time out passed?
{
HasPlayedFirstTime = true;
Hm.Play();
HasPlayed = true;
PlayTimeOut = 30.0f;
if(CanBeKey == true)
{
anim.Play("Door_Open");
//Door.Play();
StartCoroutine(PlayDaWoosh());
Debug.Log("OPENING!");
}
}
}
private void OnTriggerEnter(Collider other)
{
if(other.gameObject.name == "SecondDoor")
{
anim = GameObject.Find("DoorSecond").GetComponent<Animator>();
}
if(other.gameObject.name == "ThirdDoor")
{
anim = GameObject.Find("TheThirdDoor").GetComponent<Animator>();
}
if(other.gameObject.name == "FourthDoor")
{
anim = GameObject.Find("TheFourthDoor").GetComponent<Animator>();
}
}
IEnumerator PlayDaWoosh()
{
Woosh.PlayOneShot(Woosh.clip);
yield return new WaitForSeconds(Woosh.clip.length);
}
}
Expected result: Door opening and sound playing
Actual result: Neither happening unless I remove anything that has to do with the Woosh
I apologize ahead of time if I wasn't specific enough or if the question was answered somewhere else. I am relatively new here.

Animation OnTrigger Unity

I'm trying to make a fly-pan camera of a room when my first person player goes inside it. I've already recorded the animation on the camera but I'm having trouble when I change between cameras. I have read a lot a research about it but I actually don't know what I'm doing wrong. I would appreciate any kind of help. Thanks!
void OnTriggerEnter(Collider other)
{
inTrigger = true;
}
void OnTriggerExit(Collider other)
{
inTrigger = false;
}
void Start()
{
anim = GetComponent<Animation>();
cam = GetComponent<Camera>();
}
// Update is called once per frame
void Update()
{
if (inTrigger/*&& !anim.isPlaying*/)
{
anim.Play("AnimateCameraFirst");
}
if (anim.IsPlaying("AnimateCameraFirst"))
{
cam.gameObject.SetActive(true);
FPCam.gameObject.SetActive(false);
}
else
{
FPCam.GetComponent<Camera>().enabled = true;
cam.GetComponent<Camera>().enabled = false;
}
}
Using Update() is not necessary here. Following code should be enough:
first, change FPCam GameObject to Camera to avoid GetComponent calls:
public Camera FPCam; // you have to reassign camera reference through inspector.
Camera cam; // as usual
void Start()
{
anim = GetComponent<Animation>();
cam = GetComponent<Camera>();
}
void OnTriggerEnter(Collider other)
{
anim.Play("AnimateCameraFirst");
cam.enabled = true;
FPCam.enabled = false;
}
void OnTriggerExit(Collider other)
{
FPCam.enabled = true;
cam.enabled = false;
}
To Run some code when animation finishes you have to create animation event and register your method to it. Have a look at how to implement this.
you can create this method and register it to the event which fires right at the end of animation:
public void CameraFirstAnimation_Stopped()
{
FPCam.enabled = true;
cam.enabled = false;
}
Hope this helps.

FindObjectOfType returning null

The issue I am having is with a dropped item i pick up adding ammo to a gun.
Built a Gun class with all the methods and variables.
Built a Rifle class derived from the Gun class
The Rifle works perfect No Issues
I now am adding a "PickUp" system where x amount of enemies drop a pickup.
This is the script on the item to pick up
public class AddARAmmo : MonoBehaviour
{
private Rifle rifle;
private void Awake()
{
rifle = FindObjectOfType<Rifle>();
}
private void OnTriggerEnter(Collider other)
{
if (other.tag == string.Format("Player"))
{
rifle.AddAmmo(30);
Destroy(gameObject);
}
}
}
The rifle and gun scripts are kind of long but here is the relevant stuff from the Gun base class is public abstract class ......
public int bulletsInStock;
public void AddAmmo(int ammoToAdd)
{
bulletsInStock += ammoToAdd;
UpdateAmmo();// updates on screen Ammo
}
......
Then in the Rifle Class
public override void Modifiers() // This is where the guns starts are stored
{
bulletSpeed = 2777f;
bulletsInStock = 200;
bulletsInMag = 30;
bulletPoolSize = 40;
desiredRPS = 15;
muzzleFlashPoolSize = 10;
}
I am getting an Object Reference Not Set To An Instance
The Rifle script is on the rifle in the game hierarchy so it should find it.
Does anyone see anything wrong?
Here is the full Gun script
public abstract class Gun : MonoBehaviour
{
[SerializeField] protected GameObject muzzleFlash;// spawns on barrelEnd
[SerializeField] protected Transform muzzleFlashFolder;
[SerializeField] protected Transform bulletFolder;// is the parent of bullets
[SerializeField] protected Transform barrelEnd;// Gameobject at the end of barrel
[SerializeField] protected Rigidbody bullet; // The bullet Prefab
[SerializeField] protected Text ammo; // OSD
[SerializeField] protected Text weaponType; // OSD
[HideInInspector] protected float bulletSpeed;
[HideInInspector] public int bulletsInStock;
[HideInInspector] protected int bulletsInMag;
[HideInInspector] protected float desiredRPS;// Rounds Per Second
[HideInInspector] protected List<Rigidbody> poolOfBullets; // Make pool for bullets
[HideInInspector] protected int bulletPoolSize; // The size off the buletpool 10 works really well
[HideInInspector] protected List<GameObject> muzzleFlashPool;// pool for muzzleflash
[HideInInspector] protected int muzzleFlashPoolSize; // size of the muzzle pool
[HideInInspector] protected int bulletsLeft; // In mag
[HideInInspector] protected bool isReloading = false;
[HideInInspector] protected float timeLeft;// for fire speed
[HideInInspector] protected float fireSpeedTimer;
[HideInInspector] protected Weapons weaponsScript;
[HideInInspector] protected PlayerController playerController;
protected void FixedUpdateStuff()
{
if (playerController.canMove && Input.GetAxisRaw(string.Format("Fire1")) > 0)
{
FireSpeedControl();// call the fire timer controller
}
if (playerController.canMove && Input.GetAxisRaw(string.Format("Fire1")) == 0)
{
timeLeft = 0f;
}
if (playerController.canMove && Input.GetKeyDown(KeyCode.R) && !isReloading)
{
Reload();
}
UpdateAmmoOnInput();
}
protected void UpdateStuff()
{
if (gameObject.activeInHierarchy)// when a gun become active it updates OSD
{
UpdateWeaponType();// With its Name
}
}
protected void RPSFinder()// finds the Rounds Per Second the gun will fire
{
fireSpeedTimer = (100 / desiredRPS) / 100;
timeLeft = fireSpeedTimer;
}
protected void Fire()// Instatiates a clone of the desired bullet and fires it at bulletSpeed
{
if (!Empty())
{
Rigidbody bulletClones = GetPooledBullet();
if (bulletClones != null)
{
bulletClones.transform.SetPositionAndRotation(barrelEnd.position, barrelEnd.rotation);
bulletClones.gameObject.SetActive(true);
}
GameObject muzzleFlashClone = GetMuzzleFlash();
if (muzzleFlashClone != null)
{
muzzleFlashClone.transform.position = barrelEnd.position;
muzzleFlashClone.gameObject.SetActive(true);
}
bulletClones.AddForce(-bulletClones.transform.up * bulletSpeed * .304f); //add the force in FPS * .304 = MPS
bulletsLeft--;// the holder to know how many bullets are left in the magazine
isReloading = false;// Gun cannot reload unless it has been fired
UpdateAmmo();// Updates the on screen ammo count and the stock usage
return;
}
}
protected void Reload()
{// this removes full magazine from the stock and the stock can still go negitive FIX FIX FIX FIX FIX FIX FIX
if (bulletsInStock > 0)
{
isReloading = true;
bulletsInStock -= bulletsInMag;
bulletsLeft = bulletsInMag;
UpdateAmmo();
}
}
protected bool Empty()// Checks the magazine to see if there are bullets in it
{
if (bulletsLeft == 0)
return true;
else
return false;
}
protected void FireSpeedControl()// controls the RPS fired by the gun Controled by Update() Input
{
if (timeLeft > 0f)
{
timeLeft -= Time.deltaTime;
}
else if (timeLeft <= 0f)
{
Fire();
timeLeft = fireSpeedTimer;
}
}
protected Rigidbody GetPooledBullet()// retrieve a preInstatiated bullet from the pool to use when shooting
{
for (int i = 0; i < poolOfBullets.Count; i++)
{
if (!poolOfBullets[i].gameObject.activeInHierarchy)
{
return poolOfBullets[i];
}
}
return null;
}
protected GameObject GetMuzzleFlash()
{
for (int i = 0; i < muzzleFlashPool.Count; i++)
{
if (!muzzleFlashPool[i].gameObject.activeInHierarchy)
{
return muzzleFlashPool[i];
}
}
return null;
}
protected void UpdateAmmo()// Update the on screen ammo information
{
ammo.text = bulletsLeft + string.Format( " : ") + bulletsInStock;
}
protected abstract void UpdateWeaponType();
protected void UpdateAmmoOnInput()
{
if (weaponsScript.updateAmmo)
{
UpdateAmmo();
weaponsScript.updateAmmo = false;
}
}
public abstract void Modifiers();
protected void StartStuff()
{
Modifiers();// Call first to store indvidual gun stats
playerController = FindObjectOfType<PlayerController>();
weaponsScript = FindObjectOfType<Weapons>();
poolOfBullets = new List<Rigidbody>();
for (int i = 0; i < bulletPoolSize; i++)
{
Rigidbody bulletClone = (Rigidbody)Instantiate(bullet);
bulletClone.gameObject.SetActive(false);// Builds the Inspector list
poolOfBullets.Add(bulletClone); //and populates the elements with clones
bulletClone.transform.parent = bulletFolder.transform;
}
muzzleFlashPool = new List<GameObject>();
for (int i = 0; i < muzzleFlashPoolSize; i++)
{
GameObject muzzleFlashClone = (GameObject)Instantiate(muzzleFlash);
muzzleFlashClone.gameObject.SetActive(false);
muzzleFlashPool.Add(muzzleFlashClone);
muzzleFlashClone.transform.parent = muzzleFlashFolder.transform;
}
bulletsLeft = bulletsInMag;
ammo.text = string.Format( " 0 : 0 ");
RPSFinder();// Run last to set the RPS of the gun
}
public void AddAmmo(int ammoToAdd)
{
bulletsInStock += ammoToAdd;
UpdateAmmo();
}
}
}
and here is the full Rifle script
public class Rifle : Gun
{
//All variables are stored in the "Gun" Script
//Copy this onto guns
void Start()
{
StartStuff();
}
private void FixedUpdate()
{
FixedUpdateStuff();
}
void Update()
{
UpdateStuff();
}
public override void Modifiers() // This is where the guns starts are stored
{
bulletSpeed = 2777f;
bulletsInStock = 200;
bulletsInMag = 30;
bulletPoolSize = 40;
desiredRPS = 15;
muzzleFlashPoolSize = 10;
}
protected override void UpdateWeaponType()
{
weaponType.text = string.Format("Assault Rifle");
}
}
There are three reasons why FindObjectOfType may return null:
Let's say the script name to find is Rifle:
And FindObjectOfType<Rifle>() is returning null.
1.The GameObject the Rifle script is attached to is in-active. You must make sure that the GameObject is active.
2.The Rifle is not attached to any GameObject at-all. Make sure that the Rifle script is attached to a GameObject..
It doesn't matter if the script is enabled or disabled, it should find it as long as the GameObject it is attached to is active. See #1.
3.When loading new scene:
When you trigger scene loading with functions such as SceneManager.LoadScene
and Application.LoadLevel, FindObjectOfType will not be able to find anything until the scene loading it done.
See here for how to check when scene has finished loading.
If you still have problems which you shouldn't, simply find the GameObject then get the Rifle component from it. Assuming that the name of the GameObject is "Rifle" too.
GameObject.Find("Rifle").GetComponent<Rifle>();
I figured out how to fix this for anyone having a similar issue.
The issue was when the enemy dropped the item, the Awake() method ran. When it ran the gun was inactive in the scene so FindObjectOfType did not find the reference script because as mentioned above, It has to be active in the scene to be found.
So what I did was create a Holder script i called EnemyDrops and this script calls the findobjectoftypes for the guns. That way the call is done on the initial game start .
I then changed the pickup to find the EnemyDrops script(which is on en empty game object) and send the call to it.
EnemyDrops Script
private Handgun handgun;
private void Awake()
{
handgun = FindObjectOfType<Handgun>();
}
public void AddHandgunAmmo(int x)
{
handgun.AddAmmo(x);
}
and the new pickup script
public class AddHandgunAmmo : MonoBehaviour
{
private EnemyDrops enemyDrops;
private void Awake()
{
enemyDrops = FindObjectOfType<EnemyDrops>();
}
private void OnTriggerEnter(Collider other)
{
if (other.tag == string.Format("Player"))
{
enemyDrops.AddHandgunAmmo(50);
Destroy(gameObject);
}
}
}
As you can see it all still works with passing values through the methods just had to have a "middle man" to relay the information. But this works just fine
Thanks for everyones feedback and I hope this helps someone

Categories