I'm doing a mechanic that if you shoot at a specific object it adds ammo, but I tried a lot of things and ammo is not being added when it collides with the object.
Here is the code:
public GlobalAmmo ammo;
void OnTriggerEnter2D(Collider2D collision)
{
if (collision.transform.tag == "Enemy")
{
Destroy(gameObject);
}
if (collision.gameObject.CompareTag("Ammo"))
{
ammo.ammo += 3;
}
}
Edit: sorry!
I already put this code and I put in the images that the "if" is working, so I don't understand why this is not working because the ammo decrease correctly.
I think it must be a very silly mistake, I'm actually doing this project to test everything I'm learning
if (collision.transform.tag == "Ammo")
{
print("Hit!");
ammo.ammo += 3;
}
Does the shooting itself work?
Try to write:
if (collision.gameObject.CompareTag("Ammo"))
{
Debug.Log("Work");
ammo.ammo += 3;
}
If you do not write "Work" on the console, then you have an error somewhere else.
Since your debugging shows that the if is properly executed, I see one main possibility (an easy mistake to make when not used to Unity).
Is that a script you dragged-dropped over 2 different game objects?
You are probably not modifying the proper instance of the GlobalAmmo class (script) . There might be two (or more) GlobalAmmo.
Instead you should refer to only one instance, which is the "source of truth".
For instance, during Init the public GlobalAmmo ammo; should be initialized by fetching the proper script instance if it is on another object etc.
As per your comments, it seems you figured it out! Feel free to add more details if this is not the case.
Related
Hey StackOverflow community, I am making my very first game and I've run into a problem. The game is about a ball dodging obstacles in order to reach the end of a level. So my issue is that I want to implement a "Woosh" sound whenever the ball passes an obstacle. I have created an invisible object which is supposed to execute the "Woosh" sound whenever the ball passes the invisible object.
These are the settings on the "invisible" object:
If you look at the image you can see that there is a "Woosh" script which contains these lines of code:
using UnityEngine;
public class Woosh : MonoBehaviour
{
void OnCollisionEnter(Collision collisionInfo)
{
if (collisionInfo.collider.tag == "Player")
{
FindObjectOfType<AudioManager>().Play("Woosh");
}
}
}
This code is supposed to make the invisible object play the "Woosh" audio whenever it collides with the Player. However, when I try it out in game, the audio never gets played when I pass the invisible object.
For your information, I do have an audio manager, which contains all my sounds that I have implemented so far. It can be seen here:
Note: All the other sounds work, but not the "Woosh" sound.
Any help is appreciated!
Thanks in advance,
E.W
Comments aren't sufficient.
What I'm trying to tell you is that you can't just automatically assume that the problem is with the "Woosh" sound. That's just one of the things that can go wrong. You have this code:
public class Woosh : MonoBehaviour
{
void OnCollisionEnter(Collision collisionInfo)
{
if (collisionInfo.collider.tag == "Player")
{
FindObjectOfType<AudioManager>().Play("Woosh");
}
}
}
Your expectation is that when OnCollisionEnter is called, the "Woosh" sound will be played. You say the sound isn't playing, so you have to figure out what's wrong. This is what I would check:
Can you make the "Woosh" sound play somewhere else? You should be able to add code to your program to make the "Woosh" sound play. Maybe replace code that plays "ShatterSmall" with "Woosh". If the sound doesn't play there, then there's probably something wrong with the "Woosh" sound, and you need to fix it. If the sound does play there, then there's some other problem.
If "Woosh" plays in other places, but not in this code, then you have to check to see if the code is even being executed. If you're running this code in your debugger, put a breakpoint on the OnCollisionEnter method to see if it gets called. If it doesn't get called, then the problem is somewhere else. If you don't have a debugger, then you can output some kind of message like "Hey, I got here!" to tell you that the OnCollisionEnter function actually was called.
If you're getting to OnCollisionEnter, then you need to check to see if collisionInfo.collider.tag does indeed contain the value "Player". Again, use some kind of output function to tell you what is in that variable.
If collisionInfo.collider.tag == "Player", then check to see if the call to FindObjectOfType<AudioManager> returns the expected value.
You can break up your code:
var mgr = FindObjectOfType<AudioManager>();
if (mgr == null)
{
// some error here. Maybe throw an exception.
}
else
{
// Make sure (somehow) that it's really the object you wanted
}
mgr.Play("Woosh");
If the above tells you that you're getting the right object, then perhaps there's a problem playing the sound. Does Play throw any exceptions?
The point I'm trying to make here is that debugging involves questioning all of your assumptions, and doing tests to see where things are failing. This is best done with a debugger, because it lets you move through your code step-by-step to see exactly what's happening.
If you don't know how to use the debugger, now is the perfect time to learn. It will save you days of debugging.
It won't be making the sound because your collider is a trigger not a collision, so if you disable that box of Is Trigger, the sound will play when you collide with it.
If you want to pass through the collider like a trigger you need to use this functions.
void OnTriggerEnter(Collider other) {}
void OnTriggerStay(Collider other) {}
void OnTriggerExit(Collider other) {}
EDIT:
Also using FindObjectOfType is an expensive function, just make the Audio Manager a Singleton
Ok so I have this coroutine :
IEnumerator ShowCharTraits()
{
while(!hasPlayerChosen)
{
yield return null;
traitPanel.SetActive(true);
}
hasPlayerChosen = false;
traitPanel.SetActive(false);
// Debug.Log("Got called! job done");
}
It's being called like this from the awake method in my GameManager:
players = GameObject.FindGameObjectsWithTag("Player");
foreach (GameObject g in players)
{
ui_Controller.StartShowCharTraits();
g.GetComponent<PlayerToken>().isTurn = false;
}
StartShowCharTraits() is a simple method that does this :
public void StartShowCharTraits()
{
StartCoroutine("ShowCharTraits");
}
Now, I have checked the tags, no null reference exception, actually no errors or warnings are being thrown. If i load the scene in the editor and then play it everything works fine. traitPanel.SetActive(true); get called and my panel shows up. However when I load my scene from another scene using SceneManager.LoadScene(1); the above mentioned line is never reached. Any ideas why this is happening ?
Say you want to have one central place that is "like a singleton" in a Unity project. Example,
SoundEffects
LapTimer
Scores
SocialMediaConnections
All you do is this.
make your script SoundEffects.cs
recall that every Unity project must have a "preload" scene. (it's impossible to not have one)
in the preload scene, have a empty game object called "holder". make sure it is marked "DontDestroyOnLoad"
attach SoundEffects.cs to that holder
you're done.
there's just nothing more to it.
you're finished.
it's "just that simple"
So, any particular script, which happens to be attached to any particular object, in any particular scene, may need to access "SoundEffects"...
To do so, simply do this in Awake:
SoundEffects soundEffects = Object.FindObjectOfType<SoundEffects>();
then just use soundEffects anywhere in that script, for example soundEffects.PlayBoom(13) etc etc.
So in Awake()
Laps laps = Object.FindObjectOfType<Laps>();
Social social = Object.FindObjectOfType<Social>();
AI ai = Object.FindObjectOfType<AI>();
or whatever.
Unity is simple. Incredibly simple. It's just that easy.
write your script, LapTimer.cs or whatever
put it on a holder object in your preload scene (where else could it be?)
there's no "3", that's all there is. It's just that ridiculously simple.
Note that...
In the early days of Unity, someone unfortunately mentioned "singletons" on a QA board somewhere. (You can't have "singletons" in an ECS system, it's meaningless.) At the time, this led to huge discussions about how you can make a "singleton-like thingy" in Unity, which is piling confusion on confusion. Unfortunately from that day to this, you get people learning Unity, who see "singleton" mentioned here and there on the net, and it leads to endless confusion.
Once again, note that the idea of "managers" is just impossibly simple in Unity - explained above. It's trivial. It just couldn't be easier.
Note, above I mention you might want syntactic candy to avoid the incredible chore of typing Laps laps = Object.FindObjectOfType<Laps>();
There are very unfortunately no macros in Unity/c# so you need another way.
All you do is use a "Grid" script http://answers.unity3d.com/answers/1124859/view.html which has been popular for years in Unity.
But honestly, it's so simple to use Scores scores = Object.FindObjectOfType<Scores>(); that I usually just do that nowadays.
Ok how do I explain this. I have a singleton that acts as a data holder. While developing the scene with the game manager I had attached the singleton to the gamemanger object that hold a bunch of scripts. Now when I made the main menu I ofc added my singleton to it so I can carry info to the game scene and because of this my whole game manger object was being deleted. This is the culprit from the DataHolder :
void Awake()
{
if (instance == null)
instance = this;
else if (instance != this)
Destroy(gameObject);//This right here.
DontDestroyOnLoad(gameObject);
}
So I changed that line to Destroy(gameObject.GetComponent(instance.GetType()));
I have a strange problem with DontDestroyOnLoad. I have a map as a starting scene. From there, the user can click on certain map-objects and a new level is loaded with Application.LoadLevel() When the level is finished the map is loaded again. But, the objects with DontDestroyOnLoad attached are duplicated on the second load.
Current script:
void Awake()
{
DontDestroyOnLoad(transform.gameObject);
}
I searched online and found this possible solution to remove the duplicates:
public class NoDestroy : MonoBehaviour {
public static NoDestroy instance;
void Awake()
{
if (instance == null)
{
instance = this;
}
else
{
Destroy(this.gameObject);
return;
}
DontDestroyOnLoad(this.gameObject);
}
}
The above script simply does not work. How can I fix the problem?
Because the above script uses a static instance, it'll only work if a `single GameObject has it attached - worse yet, it'll delete other objects which try to use the same behavior.
The biggest problem is that whenever you load a scene, all objects in that scene get loaded. If they weren't unloaded (thanks to DontDestroyOnLoad) they'll be duplicated, since the scene doesn't know that they "already exist"
The above script might work better if you try to box your persistant objects under an umbrella, and only the umbrella object (usually called Toolbox) isn't destroyed. This is mostly appropriate for manager scripts, however.
If you know the objects that are meant to not be destroyed, consider loading them via a "Loading" scene. Since this moves the persistent objects out of your map scene, they won't get duplicated when reloading the map scene. Bonus to this pattern since it makes it easier to implement curtain drop.
If you want to implement this as a simple behavioural script, consider adding an ID like so
public class NoDestory : MonoBehaviour
{
private static Dictionary<string, GameObject> _instances = new Dictionary<string, GameObject>();
public string ID; // HACK: This ID can be pretty much anything, as long as you can set it from the inspector
void Awake()
{
if(_instances.ContainsKey(ID))
{
var existing = _instances[ID];
// A null result indicates the other object was destoryed for some reason
if(existing != null)
{
if(ReferenceEquals(gameObject, existing)
return;
Destroy(gameObject);
// Return to skip the following registration code
return;
}
}
// The following code registers this GameObject regardless of whether it's new or replacing
_instances[ID] = gameObject;
DontDestroyOnLoad(gameObject);
}
}
This will prevent the duplication of an object with the same ID value as one that already exists, as well as allowing recreation if said object has been Destory-ed elsewhere. This can be further refined by adding a special Editor script to hand over a new ID each time the script is implemented.
You could possibly make an "Initializer" scene that's the starting scene of the project, and all your "Don't Destroy On Load" objects get initialized in there. Then you immediately change to the real starting scene (your map screen) and all your objects exist and aren't duplicated. If you have a starting screen already, you might be able to use that instead of creating a whole new scene.
In case if anyone still needs the answer:
Answer which is available everywhere (WRONG ONE):
private static Sample sampleInstance;
void Awake()
{
DontDestroyOnLoad(this);
if (sampleInstance == null)
{
sampleInstance = this;
}
else
{
DestroyObject(gameObject);
}
}
Tl;dr Jump to solution.
What happens here is, when new scene is loaded, everything's fine. But as soon as you go back to previous scene, your original "Sample" game object is NOT destroyed and the new "Sample" game object which get created, is destroyed. So, what happens is, wherever you have referenced your Sample script (like in button onclick etc.) start referencing the duplicate script (which was destroyed of course), due to which they don't reference any script. Hence button.onClick no longer works.
So, the correct solution is to destroy the original Sample game object and not the duplicate, hence making the duplicate as the new original.
Here it is (THE CORRECT SOLUTION):
private static GameObject sampleInstance;
private void Awake()
{
if (sampleInstance != null)
Destroy(sampleInstance);
sampleInstance = gameObject;
DontDestroyOnLoad(this);
}
I have tested it. This works for me!!
if (Instance==null)
{
Instance = this;
DontDestroyOnLoad(gameObject);
}
For use cases where you have some startup logic that only needs to be initialized once, consider creating a Startup scene that only loads once at the beginning of your game. That way, whatever scene switching you do from that point on won't create duplicates of the game objects created with the Startup scene.
In relation to networking, that's what Unity did in their Boss Room example:
https://github.com/Unity-Technologies/com.unity.multiplayer.samples.coop/blob/main/Assets/Scripts/ApplicationLifecycle/ApplicationController.cs#L94
I want to check if object is active and if it is I want to inactivate other objects. Here is my method. It is not the best one but I just want it to work. This method I have doesn't work right because it only runs once even though I have it in update(). Need some help here...
update: the problem is that when I choose the selection that selection becomes true and because another selection is already true this code is not working and right now I don't know how I can fix it.
public GameObject selectionTop, selectionBot, selectionSplash, selectionFloor;
private void Update() => Check();
public void Check()
{
if (selectionTop.activeSelf)
{
selectionBot.SetActive(false);
selectionSplash.SetActive(false);
selectionFloor.SetActive(false);
}
else if (selectionBot.activeSelf)
{
selectionTop.SetActive(false);
selectionSplash.SetActive(false);
selectionFloor.SetActive(false);
}
else if (selectionSplash.activeSelf)
{
selectionTop.SetActive(false);
selectionBot.SetActive(false);
selectionFloor.SetActive(false);
}
else if (selectionFloor.activeSelf)
{
selectionTop.SetActive(false);
selectionBot.SetActive(false);
selectionSplash.SetActive(false);
}
}
Update only gets called when the GameObject its script is on is activeInHierarchy and the script is enabled. My guess is that you have this script on one of the GameObjects that is being disabled and that is causing update not to get called.
So I would move this script to another game object that is not used one of the "selection" game objects so that update will always be called.
Use a variable to store what the current selection is.
Then when a new selection is made, hide the current selection, then call check.
or -
Make a function that hides all the items. Call that each time. Then set the items you want active.
I'm not sure if this is a silly answer, I've never worked with the game sdk but just looking at the code...
the
if(...)
{
}
else if (...)
{
}
I've always seen it written this way, but maybe it's just me, you can check if your code is executed both using the debugger and if it is not, you can use the Debug.Writeline to let something be printed inside the routines on your debug screen to see which code is executed.
I'd like some help with the following issue. I'm making a simple game where you have a character running and he has to jump over obstacles coming at him.
And I'm currently stuck when it comes to creating GameObjects and randomly generating them within the game scene at run time.
I've written a class to help accomplish this:
using UnityEngine;
using System.Collections;
public class randomObstacles : MonoBehaviour {
public GameObject myCube;
public Vector3 spawnLocation = new Vector3(0,2,0);
// Use this for initialization
void Start () {
GameObject SpawnLocation = (GameObject)Instantiate(myCube, spawnLocation, Quaternion.identity);
}
// Update is called once per frame
void Update () {
}
}
The above code is what I wrote to simply create Objects, one after the other. But when I run the game, it comes up empty -_- !
Can anyone please tell me where am going wrong, and from the look of it my code doesn't seem to do what I am hoping to achieve :(
I've attached the above script into an empty GameObject as I saw in a tutorial from the Unity Community forum, but that did not help either.
(I've looked around and it seems like no one has come across such an issue - I could bee wrong)
It would seem that your myCube variable is the root of your problems, having tested it in a scene of my own. By subsituting
(GameObject)Instantiate(myCube, ...
with
(GameObject)Instantiate(GameObject.CreatePrimitive(PrimitiveType.Cube), ...
I was able to produce a cube at (0,2,0) with no qualms. Perhaps your myCube GameObject is missing a Mesh Renderer, in which case it would appear in the Hierarchy during runtime even though it would not appear visible in Game View. Perhaps you are not assigning it in the Inspector before you run the game, in which case the myCube variable would refer to null and thus not be created. Additionally, though you may be assigning the GameObject you instantiate to SpawnLocation, it is an unused local variable (something MonoDevelop or your code editor should notify you of). Make sure you either provide a reference to a myCube GameObject in the Inspector before runtime or through script referencing to a loaded prefab/GameObject during runtime. It would also be helpful to provide a fallback (say, to PrimitiveType.Cube perhaps?), which will make your code more robust and able to handle errors in referencing should they arise.
Regardless, in order to achieve the functionality you have described, first make sure yo have properly prepared whatever you would desire myCube to be. Also, for future posterity, you may want to initialize your spawnLocation in the Start routine, and assign the variable coordinates by substituting Random.value * yourValueCeiling in for each random coordinate you would like myCube to spawn on. You could even go as far to make a helper method externalized and thus independent from the start routine, such that you will not have to have a hundred instances of a single script to create a hundred instances of what you need; rather, you can call the method through a single script, and save yourself trouble in this way. If you would so appreciate it, here is my implementation of your objective, hope this helps!
using UnityEngine;
using System.Collections;
public class randomObstacles : MonoBehaviour {
public Vector3 spawnLocation;
public GameObject myCube;
// Use this for initialization
void Start () {
if (myCube != true) {
Debug.Log("myCube not set");
myCube = GameObject.CreatePrimitive(PrimitiveType.Cube);
}
if (myCube.renderer.enabled == false) {
Debug.Log("myCube not rendered");
myCube = GameObject.CreatePrimitive(PrimitiveType.Cube);
}
CreateCube();
}
// Update is called once per frame
void Update () {
}
void CreateCube() {
spawnLocation = new Vector3(0, Random.value * 10, 0);
Instantiate(myCube, spawnLocation, Quaternion.identity);
}
}
It may be worth pointing out that you're creating your object in the Start method, which means your code will only run once: from the name of your class, I'd assume you want to create more than one object using this code.
If you move the code into Update you'll create one object per frame, which is most likely too many. My guess would be that you want something like a coroutine that will run on a random interval, and then spawn cubes repeatedly over time, something like this:
void Start () {
StartCoroutine("SpawnObjects");
}
IEnumerator SpawnObjects()
{
while (keepAddingObjects) // a boolean - could just be "true" or could be controlled elsewhere
{
GameObject SpawnLocation = (GameObject)Instantiate(myCube, spawnLocation, Quaternion.identity);
float delay = Random.Range(1f, 5f); // adjust this to set frequency of obstacles
yield return new WaitForSeconds(delay);
}
}
Taken from my own code in a game that auto generate mazes:
public class Cell
{
private GameObject instance;
public void CreateVisual()
{
// Load a GameObject that exist inside the "Resources" folder.
GameObject prefab = (GameObject)Resources.Load("Models/Walls/3W1");
// Create an instance of the prefab
instance = (GameObject)GameObject.Instantiate(prefab);
instance.transform.position = myPosition;
}
}
I think the part you are missing is the Resources.Load() method.