I am trying to write a code in Unity2D that displays a different gameobject after each set amount of time. I am a complete beginner and just started today. This is my code
using System.Collections.Generic;
using UnityEngine;
public class DestroyEnemy : MonoBehaviour
{
public GameObject anim;
public GameObject anim2;
// Start is called before the first frame update
void Start()
{
StartCoroutine(Run());
}
// Update is called once per frame
void Update()
{
}
void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.tag == "FriendlyBullet")
{
IEnumerator run()
{
Destroy(gameObject);
Instantiate(anim, transform.position, transform.rotation);
yield return new WaitForSeconds(2);
Instantiate(anim2, transform.position, transform.rotation);
}
}
}
}
The error message says "The local function "run" is declared but never used." What is wrong here?
First of all, I'd like to point out, that you're calling your coroutine only at the start. As a result, it won't ever run again while the game is already running.
I'm assuming the game is a kind of shooter and you want to run the coroutine function when the bullet hits the enemy. So first check if that's what you really want and otherwise I'd move the StartCoroutine to OnCollisionEnter2D.
Secondly, as UnholySheep pointed out, you've missed a capital letter. You're calling run but you have only declared Run. Yes, that's a difference, C# is case sensitive. Functions in C# are supposed to start with a capital letter :).
That's why the IDE or whatever editor is telling you that you declared a function, but you never used it. Local function means that it's declared inside of another function and won't be accessible anywhere else.
It's hard to fix a piece of code from the top of the head as I'm not all that experienced in Unity, only a couple of months so far, but I've fixed those said issues and it should help :).
using System.Collections;
using UnityEngine;
public class DestroyEnemy : MonoBehaviour
{
public GameObject anim;
public GameObject anim2;
void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.CompareTag("FriendlyBullet"))
{
StartCoroutine(Run());
}
}
IEnumerator Run()
{
Destroy(gameObject);
Instantiate(anim, transform.position, transform.rotation);
yield return new WaitForSeconds(2);
Instantiate(anim2, transform.position, transform.rotation);
}
}
PS. I've switched your tag logic for CompareTag as it seems to be a better option for doing the same thing :).
Good luck with your game!
Related
Well, I'm trying to make an fps where you shoot tagets and appears an "Arcade-Style" score over them on unity 5, but, I don´t really know how to do it, already tried with an OnCollisionEnter(), but I did something wrong and it didn't worked, What can I do? Below you can see my code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Diana : MonoBehaviour {
public GameObject diana;
public AnimationClip Score;
private Animation myAnimation;
/* IEnumerator Wait()
{
myAnimation = GetComponent<Animation>();
myAnimation.Play("DianaScore");
yield return new WaitForSeconds(3);
} */
void OnTriggerEnter(Collider other)
{
Debug.Log("Funcó wacho");
myAnimation = GetComponent<Animation>();
myAnimation.Play("DianaScore");
// StartCoroutine(Wait());
}
void Update () {
}
}
(Sorry for my bad english, I´m argentinian and new in all this coding thing)
I suggest putting this line of code: myAnimation = GetComponent(); into a Awake() function. If the above method does not work, I suggest checking that the Is Trigger is checked for OnTriggerEnter. If the console message (Debug.Log) shows, the problem might be something to do to the animation not the script. Last but not least, check that the bullet actually collides. I know this might sound crazy, but sometimes the bullet might be able to "not get detected" if it has enough velocity. Hope these suggestions work :D .
im trying to make it so that if my character continues to touch a gameobject with a damage script the player continuesly gets damaged. instead of this result i only get damaged once when touching the gameobject. there are no error messages. ive tried to replace the if with a while loop and it ended up crashing my game. is there any way to loop a if statement preferably with a way to time it.
if (other.tag == "Player")
{
healthScript.healthPoints -= damage;
}
this is the if statement im trying to loop.
First, I am under the assumption you are using Colliders as triggers with the Is Trigger attribute selected. I am also assuming this is a 2D game. If not, this same approach will work, you will just have to change the methods from 2D to 3D.
You will want to add the OnTriggerEnter2D() and OnTriggerExit2D() methods to your player health script. These will allow us to detect when the player is standing on the damaging object. From here, we will start a coroutine which can be used to deal damage in a timed manner.
using System.Collections;
using UnityEngine;
public class HealthScript : MonoBehaviour
{
public float healthPoints = 100f;
public float damage = 5f;
public bool OnDamagingObject = false;
IEnumerator DealDamage()
{
while (OnDamagingObject)
{
healthPoints -= damage;
yield return new WaitForSeconds(1f);
}
}
void OnTriggerEnter2D(Collider2D hitInfo)
{
GameObject collider = hitInfo.gameObject;
if (collider.tag == "DamagingObject")
{
OnDamagingObject = true;
StartCoroutine(DealDamage());
}
}
void OnTriggerExit2D(Collider2D hitInfo)
{
GameObject collider = hitInfo.gameObject;
if (collider.tag == "DamagingObject")
{
OnDamagingObject = false;
}
}
}
For those who wish to use OnTriggerStay(), I will also provide a solution for that. This time, we will keep the script on the damaging object, following the structure the question asker is using.
using UnityEngine;
public class DamageScript : MonoBehaviour
{
public HealthScript healthScript;
public float damage = 5f;
void OnTriggerStay2D(Collider2D hitInfo)
{
GameObject other = hitInfo.gameObject;
if (other.CompareTag("Player"))
{
healthScript.healthPoints -= damage;
}
}
}
As mentioned in the comments of my other answer, implementing both OnTriggerEnter() and OnTriggerExit() allow more control over dealing damage to the player. One of the downsides to using OnTriggerStay() is I am unsure how to deal the damage in a timed manner. Additionally, OnTriggerStay() limits how you can deal damage to the player. If in the future you wanted to give 5 damage to the player when they first touch the damaging object but 2 damage for every second thereafter while touching it, it is not possible to do so using OnTriggerStay().
I have the following code:
void Start()
{
gameObject.SetActive(false);
StartCoroutine(Load());
}
IEnumerator Load()
{
yield return new WaitForSeconds(waitTime);
gameObject.SetActive(true);
}
This gives me an error that says:
Coroutine couldn't be started because the the game object 'NameOfObj'
is inactive!
This makes sense, since the game object has been set to deactivate before running the script. Even still, what is it that I'm supposed to do then? I tried moving gameObject.SetActive(false) to the coroutine, before WaitForSeconds(). Doing this stopped the game object from loading at all.
From my understanding, when the line gameObject.SetActive(false) is executed, the script stops running until the game object is reactivated. However, if this is the case, would it not be impossible to then reactivate the game object (as the script is disabled)?
Regardless, how would I delay my game object from loading until 2-3 (or any arbitrary length of time) after the game has started?
You cannot start a coroutine function from a script that has its GameObject de-activated.
The StartCoroutine function is a function under the MonoBehaviour class. When you have to start a coroutine on a deactivated GameObject, you need a reference to a MonoBehaviour object that has an active GameObject.
Two ways to do this:
1. Use an already existing GameObject that's unlikely to be deactivated. In my case, I usually use the camera. I access the camera's MonoBehaviour since it's likely to be activated then use it to start the coroutine function.
I suggest you use this method.
Replace the code in your Start function with the one below:
//De-activate this GameObject
gameObject.SetActive(false);
//Get camera's MonoBehaviour
MonoBehaviour camMono = Camera.main.GetComponent<MonoBehaviour>();
//Use it to start your coroutine function
camMono.StartCoroutine(Load());
2. Attach the script to an empty GameObject and the script on the empty GameObject will control or be able to activate/de-activate the other GameObject.
The script with the coroutine function you expect to run on a de-activated GameObject (Attach it to the GameObject you wish to de-activate):
public class YourDeactivatableScript: MonoBehaviour
{
public IEnumerator Load()
{
yield return new WaitForSeconds(waitTime);
gameObject.SetActive(true);
}
}
Now, let's say that you want to deactivate a GameObject named "Cube" that has the YourDeactivatableScript script attached to it but still be able to start its Load coroutine function, create an empty GameObject with a new script, then start the Load function from it.
Create an empty GameObject then attach this script to it:
public class LoadFuncCallerScript: MonoBehaviour
{
GameObject targetObject;
public void Start()
{
//Find the GameObject you want to de-activate
targetObject = GameObject.Find("Cube");
//De-activate it
targetObject.SetActive(false);
//Get it's component/script
YourDeactivatableScript script = targetObject.GetComponent<YourDeactivatableScript>();
//Start coroutine on the other script with this MonoBehaviour
StartCoroutine(script.Load());
}
}
The coroutine is now started from another script named LoadFuncCallerScript.
What I do to avoid stopping coroutine is have a game object that does not get deactivated.
public class CoroutineHandler : MonoBehaviour
{
private static CoroutineHandler instance = null;
public static CoroutineHandler Instance
{
get
{
if(instance == null)
{
GameObject inst = new GameObject("CoroutineHandler");
DontDestroyOnLoad(inst);
instance = inst.AddComponent<CoroutineHandler>();
}
return instance;
}
}
}
Then use this for such coroutines by using CoroutineHandler.Instance.StartCoroutine(RoutineMethodHere());.
If you do not want something like this, since it can go wrong or cause leaks if not handled correctly, you can try using Invoke("MethodName", delay);
I am trying to make my player hit an object, destroying the object and triggering an animation, but everything I try causes an error. I am relatively new at c# so the answer may be obvious but I need help. How can I set it up so that the collision will cause the object to disappear and the player to play an animation? Here is the script I am currently trying.
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class succ : MonoBehaviour
{
public float speed = .15f;
public static float jumpSpeed = 170f;
void Start()
{
GetComponent<ConstantForce2D>().enabled = false;
GameObject.Find("goal");
}
public bool animation_bool;
private object coll;
private object other;
void Update()
{
OnCollisionStay2D(Collision2D coll);
{
if (coll.gameObject.tag == "succ") ;
{
animation_bool = true;
GetComponent<Animator>().SetBool("succ", animation_bool);
GetComponent<ConstantForce2D>().enabled = true;
Destroy(other.object);
}
}
}
private void Destroy(object gameObject)
{
throw new NotImplementedException();
}
private void OnCollisionStay2D(Collision2D collision2D, object coll)
{
throw new NotImplementedException();
}
}
There are a few things I can see that are wrong, but I'll start by answering your question.
I suggest you change your MonoBehaviour method OnCollisionStay2D to OnCollisionEnter2D. OnCollisionStay2D is "sent each frame where a collider on another object is touching this object's collider". OnCollisionEnter2D is "sent when an incoming collider makes contact with this object's collider".
I believe you are looking for the latter since you only want to trigger this once during the collision. You are also destroying the other object, making it impossible to call OnCollisionStay2D anymore even if you wanted to do so.
You should also remove your Update method. I honestly do not understand what you are trying to achieve there now. All of the OnCollision methods get called automatically; you do not have to call them yourself.
Then you can use the Awake and OnCollisionEnter2D methods as follows
public class Succ : MonoBehaviour
{
private Animator animator;
private void Awake()
{
// You can already get a reference to the Animator on Awake
// This way you do not have to do it on every collision
animator = GetComponent<Animator>();
}
// Use OnCollisionEnter2D instead since the code
// needs to be excecuted only once during the collision
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.CompareTag("succ")
{
// Assuming that you only want to trigger an animation once
// to reflect attacking or colliding, you could use SetTrigger
// instead. Otherwise you need to use SetBool again to set it
// back to false. You should then change the Animator parameter
// accordingly, from a bool to a trigger.
animator.SetTrigger("succ");
Destroy(collision.gameObject);
}
}
}
Apart from this, I have a few things I would like to comment on:
I am not sure what you are trying to achieve by setting your ConstantForce2D component to false on Start and then setting it to true on collision.
You seem to be using GameObject.Find on Start. GameObject.Find is something that should be very rarely used. It can be extremely expensive, especially if your Scene has a lot of GameObjects in it; this is because it simply goes through the Hiearchy, comparing the parameter string to names of GameObjects until it either finds a match or runs out of GameObjects.
Moreover, you are using GameObject.Find on Start to look for a GameObject, but then you do not store that anywhere, making the whole finding process completely pointless.
Overall, I recommend you to take a look at all of the different learning resources offered by Unity themselves. Your question is about fairly basic functionality that is certainly covered during all of the different tutorials.
I am working on a game, like a fire fighter, here is scenario in which player has to extinguish the fire through cloth, I had successfully made a cloth, and I am able to pick it but when I throw it, it doesn't throw, it remained there.
Here I want to throw cloth at fire(specific distance). Here is the code I did so far.. Any suggestion, where is the problem? Or what to do?
using UnityEngine;
using System.Collections;
public class pickup : MonoBehaviour {
public Transform OnHand;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
if (Input.GetButtonDown ("E")) {
GetComponent<Rigidbody>().useGravity=false;
this.transform.position = OnHand.position;
this.transform.parent = GameObject.Find ("FPSController").transform;
this.transform.parent = GameObject.Find ("FirstPersonCharacter").transform;
}
if (Input.GetMouseButtonDown (0)) {
this.transform.parent = null;
GetComponent<Rigidbody>().useGravity=false;
// Rigidbody.AddForce (new Vector2(1,4), ForceMode.Impulse);
}
}
}
Looks like you have everything pretty much correct. You referred to the Rigidbody correctly to turn off the gravity, but then tried referring to the rigidbody incorrectly to add force.
Do GetComponent().AddForce (new Vector2(1,4), ForceMode.Impulse);
I don't see why that wouldn't work. Take out the '//' of course.
Also, for optimization purposes, you can store the rigidbody in awake or start, than use the variable instead of using GetComponent several times.
something like rb = GetComponent();
then you can just use rb.AddForce().
Hope it helps! If it does, if you can mark it as the correct answer and upvote, do others know it has been answered :).