Unity how can I put multiple if statements in OnTriggerEnter2D? - c#

Good day, I'm trying to create an enemy that damages the player because that's what enemies do. I have figured it out on how to do this the only problem is that the enemy kills the player almost instantly and is too overpowered... I was trying to put some cooldown before the enemy strikes again but unfortunately, an OnTriggerEnter2D seems to only accept 1 if statement.
void OnTriggerEnter2D(Collider2D col){
//What iam trying to achieve but this doesn't work
if(Time.time > nextDamageTime) {
if(col.CompareTag("Player")){
player.curHealth -= 1;
Debug.Log("Enemy damaged player!");
nextDamageTime = Time.time + cooldownTime;
}
}
}

First of all this is c#. There is nothing that can anyhow control how many if blocks you open within a method.
So, no there is no limit on conditions and if blocks within OnTriggerEnter2D!
I don't see a direct problem in your code actually except maybe: OnTrigggerEntet2D is called exactly once namely in the moment the trigger enters without exiting. So if this already instantly kills the player then reducing 1 might simply be a too high value.
With the timer nothing happens because as said it is called only once, so in the case the timer wasn't finished yet nothing happens until the enemy leaves and re-enters eventually.
You might want to rather use OnTriggerStay2D which is rather called every frame as long as a trigger collides.
float timer;
private void OnTriggerEnter2D (Collider other)
{
// reset the timer so the first hit happens immediately
timer = 0;
}
private void OnTriggerStays2D(Collider other)
{
if(!col.CompareTag("Player")) return;
timer -= Time.deltaTime;
if(timer > 0) return;
timer = cooldownTime;
player.curHealth -= 1;
}
Which now would reduce the players health by 1 every cooldownTime seconds.
In general especially for multiple enemies I personally would rather give the player the control over timer after it was last hit so the player itself has kind of an invincible time. Enemies can simply attack like they would do usually but the player itself decides whether the damage counts or not.

Related

Getting more than one possition though code (unity c#)

i made a "turret" in unity that shoots automatically on enemies it works with one enemy but when i put 2 it shoots in the midle of then dont know why it does that
public transform enemy;
public void Update() {
enemy = Gameobject.FindgameObjectWithTag("Enemy").getComponent<Transform>;
//just put the getting the possition part because the shooting works just fine
}
Your script is refreshing the enemy position every Update() iteration.
Try to have the turret stick to the enemy until it gets out of range, or until it is dead. Before doing your variable assignation, check if enemy != null, if it is the case, then you can write :
enemy = Gameobject.FindgameObjectWithTag("Enemy").getComponent<Transform>;
Also, try to avoid refreshing every Update() call, instead, use a different function that you call every half second.
I recommend you checking this project by Brackeys, that handles the situation perfectly. Here
GLHF

Unity: Set RigidBody velocity in FixedUpdate() or Start()?

I'm developing a simple 2D game in Unity (balls hitting bricks), so i have a ball and i want to fire the ball to hit bricks, now to fire the ball i have to set Velocity on the RigidBody component attached to the ball's GameObject.
There are two ways to do that:
Method 1:
Set the velocity inside the Start() method
private void Start()
{
rb.velocity = direction * speed;
}
Method 2:
Write a FixedUpdate() method and put the statement in there. because:
Use FixedUpdate when using Rigidbody.(Unity Docs)
private void Start()
{
_setVelocity = true;
}
private void FixedUpdate()
{
if (!_setVelocity) return;
rb.velocity = direction * speed;
rb.angularVelocity = 0;
rb.gravityScale = 0;
_setVelocity = false;
}
But the thing is: i don't need the FixedUpdate() method for anything else, because i only fire the ball once in its script's lifespan, so after i fire the ball unity will continue to run the FixedUpdate()method and check for _setVelocity's value and return (exit) every time because it will always be false after i shoot the ball the first time.
And this will happen every fixed frame-rate frame which is expensive i guess for just setting the velocity only once.
SO:
Is it expensive to have a FixedUpdate() method that checks for false value and returns every frame?
Which of the two methods performs better in this case?
Is it expensive to have a FixedUpdate() method that checks for false value and returns every frame ?
A simple boolean condition such as yours is not that greedy so do not worry about that. However, any line of code that runs decrease your performances. The Update() method itself is more greedy in terms of performance than your "trivial" condition. Disabling your component script or removing the object itself is more optimize than keep it running for nothing. Just keep that in mind :)
Which of the two methods performs better in this case ?
The first one is more efficient because your velocity is fixed, so there is no point of using an update method (Update() or FixedUpdate()). You should initialize your rigidbody's velocity directly in your Start() method as you did in your post, so you can get rid of your boolean and its condition.
Unity's documentation is pretty clear about rigidbody and FixedUpdate() but I have the impression that you misunderstood some aspects.
They tell you that if you want to modify any data of your rigidbody during the run-time, prefer to use FixedUpdate() instead of Update() to avoid weird behavior.
But why should you use FixedUpdate() instead of Update() ?
Because Update() is called at each frame, so if your pc runs at 100 fps, Update() will be called 100 times. And if your pc runs at 200 fps, it'll be called 200 times. It is kind of problematic in some cases, especially when you interact with physics components.
In order to understand, lets admit that we want to apply a force to our rigidbody (rb.AddForce(100)). If you call it in your Update() method and your pc runs at 100 fps, you'll apply 10000 force in one second. However, if your pc runs at 50 fps, then you'll apply 5000 force in one second. It'll cause you some weird behavior that could be avoided thanks to FixedUpdate(), which is called at a fix number of times per second (see unity doc).
However, there is no point of using FixedUpdate() with your rigidbody component if you do not want to modify it. They tell you that if you want to update your rigidbody component, you should do it inside the FixedUpdate method instead of Update().
So for your question, you can simply do the following:
Awake() {
// getting your components
rb = GetComponent<Rigidbody2D>();
}
Start() {
// initiliaze your components
rb.velocity = direction * speed;
rb.angularVelocity = 0;
rb.gravityScale = 0;
}
Hoping that I've helped you, good luck with your project !

How to optimise countdown timer?

Dear stackoverflow community,
I have countdown timer for my double points power up and now I have problem because my code works fine in game,but when timer is active game is lagging , not too much but any lag is not good for my game because the player needs to play smoothly without any unoptimised component..
I have this code and I bet the game is lagging because the code is in update method ( I tried to put it in game manager script but then timer won't countdown so that is not solution )
This is the code ( Thanks to stackoverflow user #siusiulala , who wrote me the working code)
but seems like it needs to be in another method or something because Update method running performance when has countdown inside.
private void Update(){
if (isDoublePoints)
{
// Countdown the timer with update time
powerUpTimer -= Time.deltaTime;
Debug.Log("TIMER ISS " + powerUpTimer);
if (powerUpTimer <= 0)
{
// End of power up time
isDoublePoints = false;
}
}
}
public void OnPickPowerUp(float buffTime)
{
powerUpTimer += buffTime;
}
I hope someone will give solution to lagg because I saw a lot of games that has power up systems without any laggs inside...
Thank you stackoverflow, without you my game would not ever come to end :)
what trollingchar's answer says about the Debug.Log is correct.
To use the [SerializeField] might be considered as a dirty and lazy hack by some people. Because it has the side-effect that it is now serialized, that means the value is stored in the assets. It's not bad but if you are exact it shouldn't be done with fields that will be changed on runtime anyway.
Instead you can simply go to the Inspector, open the context menu and set it to Debug Mode
this makes the Inspector not use the Custom EditorScripts but instead reveal all private fields (of Serializable types).
For example for the Transform component
However way more efficient than using the Update method with a flag at all would be to rather use a Coroutines.
Coroutines can be started and run parallel (every frame right after) the Update method but the advantage: when a coroutine is finished - it is finished and doesn't continue checking the bool flag every frame.
So whenever you pickup a PowerUp instead of setting the flag to true rather use
StartCoroutine(PowerUpRoutine());
and implement a routine like
private IEnumerator PowerUpRoutine()
{
isDoublePoints = true;
while(powerUpTimer > 0)
{
// Countdown the timer with update time
powerUpTimer -= Time.deltaTime;
//Debug.Log("TIMER ISS " + powerUpTimer);
// yield in simple words makes Unity "pause"
// the execution here, render this frame and continue from here
// in the next frame
yield return null;
}
// End of power up time
isDoublePoints = false;
}
public void OnPickPowerUp(float buffTime)
{
powerUpTimer += buffTime;
// avoid concurrent routines
if(!isDoublePoints) StartCoroutine(PowerUpRoutine());
}
In order to display it in your game you can use a Text or TextMeshPro and set the text like e.g.
[SerializeField] private Text _text;
private IEnumerator PowerUpRoutine()
{
isDoublePoints = true;
while(powerUpTimer > 0)
{
// Countdown the timer with update time
powerUpTimer -= Time.deltaTime;
//Debug.Log("TIMER ISS " + powerUpTimer);
// set the text of the Text component to display the value
// for the $ symbol google for "c# string interpolation"
_text.text = $"TIMER IS {powerUpTimer:00.00}";
// yield in simple words makes Unity "pause"
// the execution here, render this frame and continue from here
// in the next frame
yield return null;
}
// End of power up time
isDoublePoints = false;
}
From my experience, Debug.Log() is a very expensive method. It will cause lag when called every frame. My IDE even highlights usage of Debug.Log() in Update() as warning because of that. Use this method only for debugging, and then remove.
If you want to be able to see the timer value, add [SerializeField] attribute to your field and it will show up in the inspector.
You can use the profiler by selecting Window-Analysis-Profiler, assuming you are using Unity 2018.x. It records how much time processing takes, and helps locating bottlenecks.

How do I make the program do something when an object is destroyed?

I have plane(as in air plane) objects in my program, they get destroyed when bullets hit them but they also get destroyed after 5 seconds, when they exit the screen.
I also have a health script that resets the whole thing when it goes down to 0, and I want to remove a point every time the object is destroyed, but only when off screen. So I keep the scripts separate.
I use this in the ships spawn script to destroy them after 5 seconds, simple enough.
Destroy(spawnedPlane, 5f);
It would be perfect if I could just have some code that does "Destroy this object after X seconds AND add this to this value". Because as I understand it, "destroy" only accepts 2 parameters and nothing else.
Surely it is possible but I am at a loss. Still very new to this. Sorry if this is very unclear but I barely know what I'm doing myself.
You can use events to cleanly achieve what you are after. Below is an example of an event you might find useful. Other objects can listen to the event and once it is triggered, they will be notified.
[Serializable]
public class PlaneEvent : UnityEvent<Plane> { }
Once you have defined your event, you can then add it as a field in your Plane. Once your plane has been destroyed, you can fire the event and it will in turn notify anyone who is listening!
public class Plane : MonoBehaviour {
public PlaneEvent OnDestroyed;
public void Destroy () {
Destroy(gameObject);
OnDestroyed.Invoke(this);
OnDestroyed.RemoveAllListeners();
}
}
Now in our score class, we add a method that will be called once the OnDestroyed plane event is triggered.
public class Score : MonoBehaviour {
public void AddPointsFor (Plane plane) {
Debug.Log("A Plane was destroyed!");
//Tick a counter, add points, do whatever you want!
}
}
Once we have these pieces, it is trivial to make them work together. We take the plane and we add the score as a listener to the OnDestroyed event. Then once the plane is destroyed, the event is fired and score is told to add points.
public class Game : MonoBehaviour {
[SerializeField]
private Score _score;
[SerializeField]
private Plane _plane;
public void Start () {
// When you are destroyed let me know so I can add some points.
_plane.OnDestroyed.AddListener(_score.AddPointsFor);
_plane.Destroy();
}
}
Another big advantage in using events is your plane has no idea that a score even exists, it will let anyone who cares know that it has been destroyed. In the same way this event could also be used to trigger particle effects, animations and sound effects when the plane is destroyed and all you need to do is add more listeners.
Just use a coroutine to wait and then subtract a point and destroy the object at the same time.
void Start()
{
// your startup script
StartCoroutine(DestroyAfterSeconds(5f));
}
IEnumerator DestroyAfterSeconds(float seconds)
{
// wait for X amount of seconds before continuing on
yield return new WaitForSeconds(seconds);
/*
* this runs after the wait.
* if the coroutine is on the same gameobject that you are
* destroying, it will stop after you run Destroy(), so subtract
* the point first.
* */
points--;
Destroy(spawnedPlane);
}
If it was me I would surely go with events as suggested by CaTs.
Coroutine are another way to do that, but events are better in general at least in this case. Also using a Coroutine for just one Invoke is a bit an overkill (and unity Coroutines are a bit not performant.) Also the coroutine must be outside of the object you want to destroy because Unity Coroutines die when their MonoBehaviour is destroyed.
If you are still uncomfortable with events you...
well you should overcome it and try them anyway.
You could take a shortcut you can use More Effective Coroutine - Free.
And launch this code:
Timing.CallDelayed(5f, DestroyAndRemoveHP());
Basically this will run your logic with the delay you want to apply.
DestroyAndRemoveHP will be your method to destroy the platform and do anything else you like.
Full method description.
On the plus side you will start using MEC that are better than unity coroutine, but learning events makes you also a better programmer. You might do both.

How to wait some seconds before doing something in OnMouseUp Event

I'm writing a game with some animations, and use those animations when the user clicks on a button. I would like to show the user the animation, and not "just" call a new level with Application.loadLevel. I thought I could use the Time.DeltaTime in the onMouseUp method and add it to a predefined 0f value, then check if it is bigger than (for example) 1f, but it just won't work as the onMouseUp method adds just "it's own time" as the delta time.
My script looks like this now:
public class ClickScriptAnim : MonoBehaviour {
public Sprite pressedBtn;
public Sprite btn;
public GameObject target;
public string message;
public Transform mesh;
private bool inAnim = true;
private Animator animator;
private float inGameTime = 0f;
// Use this for initialization
void Start () {
animator = mesh.GetComponent<Animator>();
}
// Update is called once per frame
void Update () {
}
void OnMouseDown() {
animator.SetBool("callAnim", true);
}
void OnMouseUp() {
animator.SetBool("callAnim", false);
animator.SetBool("callGoAway", true);
float animTime = Time.deltaTime;
Debug.Log(inGameTime.ToString());
// I would like to put here something to wait some seconds
target.SendMessage(message, SendMessageOptions.RequireReceiver);
}
}
}
Im not entirely sure what your trying to do by using Time.deltaTime in onMouseUp. This is just the time in seconds since the last frame was rendered, and should act the same no matter where you try to access it. Normally it is used in a function that is called every frame, not one-off events like onMouseUp.
Despite not being certain what you are trying to achieve, it sounds like you should be using Invoke:
http://docs.unity3d.com/ScriptReference/MonoBehaviour.Invoke.html
Just put the code you wish to be delayed into a separate function, and then invoke that function with a delay in onMouseUp.
EDIT: To backup what some others have said here I would not use Thread.Sleep() in this instance.
You want to do this (and all waiting functions that do not appear to make the game "freeze") by blocking the Update loop by using a Coroutine.
Here is a sample of what you are probably looking for.
void OnMouseUp()
{
animator.SetBool("callAnim", false);
animator.SetBool("callGoAway", true);
//Removed the assignement of Time.deltaTime as it did nothing for you...
StartCoroutine(DelayedCoroutine());
}
IEnumerator DoSomethingAfterDelay()
{
yield return new WaitForSeconds(1f); // The parameter is the number of seconds to wait
target.SendMessage(message, SendMessageOptions.RequireReceiver);
}
Based on your example it is difficult to determine exactly what you want to accomplish but the above example is the "correct" way to do something after a delay in Unity 3D. If you wanted to delay your animation, simply place the calling code in the Coroutine as I did the SendMessage invocation.
The coroutine is launched on it's own special game loop that is somewhat concurrent to your game's Update loop. These are very useful for many different things and offer a type of "threading" (albeit not real threading).
NOTE:
Do NOT use Thread.Sleep() in Unity, it will literally freeze the game loop and could cause a crash if done at a bad time. Unity games run on a single thread that handles all of the lifecycle events (Awake(), Start(), Update(), etc...). Calling Thread.Sleep() will stop the execution of these events until it returns and is most likely NOT what you're looking for as it will appear that the game has frozen and cause a bad user experience.

Categories