- Home /
flamethrower 2D not hitting sometimes
Hi, i have a script for the flame of my flamethrower and i've debugged all i could i'm using unity remote to test, idk if could cause this, i have a turret that fires like a flamethrower and damage target's every 0.4 seconds using ontriggerstay2d and 4 enemies in front of it, sometimes hits do not land on 1, 2 or 3 of them i've tried to check if any boxes were off at the time, or if anything was disabled and nothing so here's my code.
void FixedUpdate() {
if (dot_timer > 0)
dot_timer -= Time.deltaTime;
else
{
dot_timer = base_dot_timer;
Debug.Log("should hit");
Debug.Break();
}
}
private void OnTriggerStay2D(Collider2D other)
{
if (dot_timer <= 0)
{
if (other.gameObject.layer == target_layer)
{
Enemy_Manager enemy = other.gameObject.GetComponent<Enemy_Manager>();
enemy.Hp -= damage;
Debug.Log("hit");
enemy.Blink_fx.SetActive(true);
Vector3 screenPoint = Camera.main.WorldToViewportPoint(other.gameObject.transform.position);
bool onScreen = screenPoint.x > 0 && screenPoint.x < 1 && screenPoint.y > 0 && screenPoint.y < 1;
if (onScreen)
{
for (sbyte i = 0; i < 30; i++)
{
if (!game_manager.TextInstPool[i].activeInHierarchy)
{
game_manager.TextInstPool[i].transform.position = other.gameObject.transform.localPosition;
game_manager.TextInstPool[i].SetActive(true);
game_manager.TextPool[i].text = damage.ToString();
break;
}
}
}
}
}
}
@willianbrasil your question is very unclear. Why would the turret select a different enemy every time, apparently using: Enemy_$$anonymous$$anager enemy = other.gameObject.GetComponent<Enemy_$$anonymous$$anager>();
where "other" would return the first collider it detects. So, why would it detect 4 different colliders?
Also, you should use Update()
not FixedUpdate()
in this case (you are not moving a rigidbody.
In any case, more info is needed about the problem.
Agreed, use Update with deltaTime and FixedUpdate with real time. I think that willianbrasil's Enemy_$$anonymous$$anager
is not a "manager" as we're used to but just a script attached to each enemy object.
Actually, you are right. Last time I read his code was yesterday, and forgot that his call GetComponent<Enemy_$$anonymous$$anager>()
. Today, I mistakenly thought that Enemy_$$anonymous$$anager
was the code he posted in his question.
So, indeed Enemy_$$anonymous$$anager
is not actually a "manager" per "normal "usage", as you say.
I think his code needs "untangling".
@pako sorry about that, well actually we are talking about OnTriggerStay
, so actually other will return all colliders that are colliding, one by one, and will run the code once for every collider it detecs.
So, what i have here is this scenario: a turret that tests a raycast against enemies, once it returns true, it will set active a fire of it's pool. The script that i showed here is used for the fire to hit all object colliding with it once every 0.4 seconds, from all my debug tests i can tell that: when i'm using Update()
ins$$anonymous$$d of FixedUpdate()
the fire won't hit any enemy when it should, and when it lands a hit, it will not hit all enemies, leaving one or two untouched.
What i've noted is that when i use FixedUpdate(),
it will hit all enemies almost everytime, but eventualy one or two enemies are ignored, i debugged to see if they actually should have been hit at the collision time and, from all i've tested, the enemies should definetly have been hit.
Per the documentation:
OnTriggerStay is called almost all the frames
https://docs.unity3d.com/ScriptReference/Collider.OnTriggerStay.html
...so it's not called every frame.
On the other hand, FixedUpdate()
is called multiple times per frame.
Consequently, once dot_timer
becomes less than 0 inside FixedUpdate()
, it will make the condition if (dot_timer <= 0) true
inside OnTriggerStay2D
, and all is well, however, on the same frame FixedUpdate()
will execute multiple times, so it will reset dot_timer = base_dot_timer
, so for subsequent calls of OnTriggerStay2D if (dot_timer <= 0)
will be false, and that seems to be the source of the problem, in combination with the fact that OnTriggerStay2D
might miss a frame "here and there".
So, I think it might be best if you use just the raycasting, and "play" with the ti$$anonymous$$g of the raycasting to achieve the 0.4 s fire rate.
If you think that this would work with your mechanics and your remaining code, we could work out a solution.
Yes, see my answer about the fixedupdate ti$$anonymous$$g, it's the same idea. Raycasting would be a lot cheaper but wouldn't get a residual flame effect, I suppose willianbrasil can balance between the options
BTW it might be helpful to post some more relevant code, especially where you do your raycasting.
sorry i took quite some time to reply, i'ts the first time someone answered me and i didn't knew that comments were hidden.
@pako @Nomenokes hi here is a piece of my fire turret's code, sorry that it's a little big, i use a raycast to check if there is any enemy within the turret's range so it'll start the fire, is using a RaycastAll
really cheaper then ontriggerstay
? because i've seen a lot of posts saying that raycasts are very expensive since you have to use it every frame, also one thing that bothers me is that when i try to make timed events they seem unaccurate, what is the best way to guarantee that i'll get the desired effect always at the desired time? i try to use FixedUpdate
for that.
private void FixedUpdate()
{
if (script.Activated)
{
Turret_Enemy_Raycast();
StartAtk();
}
else
CancelAtk();
EndAtk();
}
private void Turret_Enemy_Raycast()
{
hit = Physics2D.Raycast(origin, new Vector2(script.Dir.x, 0), script.Range, layer_mask_hit);
hit2 = Physics2D.Raycast(origin2, new Vector2(-script.Dir.x, 0), script.Range, layer_mask_hit);
Debug.DrawRay(origin, new Vector2(script.Dir.x, 0));
Debug.DrawRay(origin2, new Vector2(-script.Dir.x, 0));
if (hit)
{
Turret_Fire();
}
else
{
if (isAtk)
{
for (sbyte i = 0; i < 2; i++)
{
if (fire[i].Firing)
{
fire[i].Firing = false;
fire[i].Em = false;
fire[i].Fading = true;
isAtk = false;
fadingNum = i;
break;
}
}
}
}
if (hit2)
{
Turret_Fire2();
}
else
{
if (isAtk2)
{
for (sbyte i = 0; i < 2; i++)
{
if (fire2[i].Firing)
{
fire2[i].Firing = false;
fire2[i].Em = false;
fire2[i].Fading = true;
isAtk2 = false;
fadingNum2 = i;
break;
}
}
}
}
}
@willianbrasil I'm still confused... If this is (part of) the script that is attached on the turret, where is the script you posted in your question attached?
There's too much info missing to be able to give a complete answer, e.g. script names, variable declarations, your general setup...
Please read and follow the FAQ before posting.
@pako Actually i have been quite specific into that, my first words here were: i have a script for the flame of my flamethrowe, which is the first code that i have posted and when i posted this code i've said, this is a part of my turret's code, i've just posted this part so you could see that the turret itself raycasts for any enemies, so it can start shooting and the fire itself use OntriggerStay to distribute damage through all instances colliding with it, the reason i do not use a RaycastAll at the turret is because i've read in unity best practices that raycasts are expensive, since i'm going for android i'm seeking for the best optimization as much as i can
Answer by willianbrasil · Feb 12, 2018 at 08:09 PM
I've come up with and answer, it happens that ontriggerstay isn't reliable for any over time effect, and also RaycastAll
is very much more expensive than the situation demmands, to solve this i've created a enemy list, whenever an enemy collides with the fire(`OnTriggerEnter2D`), it will add him into the list, (`OnTriggerExit2D`) will remove him from the list, and when the timer reaches 0 it'll apply damage to every enemy on the list, i believe this is one of the best ways to make AoE and DoT heres my fire's code now:
private Game_Manager game_manager;
private List<Enemy_Manager> enemies;
private int target_layer;
private float base_dot_timer;
private float dot_timer = 0;
private int damage;
private Vector2 dir;
// Use this for initialization
void Start () {
Vector3 scale = transform.localScale;
scale.Set(-dir.x, 1, 1);
transform.localScale = scale;
enemies = new List<Enemy_Manager>();
}
// Update is called once per frame
void FixedUpdate() {
if (dot_timer > 0)
dot_timer -= Time.deltaTime;
else
{
for(sbyte i = 0; i < enemies.Count; i++)
{
enemies[i].Hp -= damage;
Debug.Log("hit");
enemies[i].Blink_fx.SetActive(true);
Vector3 screenPoint = Camera.main.WorldToViewportPoint(enemies[i].transform.position);
bool onScreen = screenPoint.x > 0 && screenPoint.x < 1 && screenPoint.y > 0 && screenPoint.y < 1;
if (onScreen)
{
for (sbyte s = 0; s < 30; s++)
{
if (!game_manager.TextInstPool[s].activeInHierarchy)
{
game_manager.TextInstPool[s].transform.position = enemies[i].transform.localPosition;
game_manager.TextInstPool[s].SetActive(true);
game_manager.TextPool[s].text = damage.ToString();
break;
}
}
}
}
dot_timer = base_dot_timer;
}
}
private void OnTriggerEnter2D(Collider2D other)
{
if (other.gameObject.layer == target_layer)
{
enemies.Add(other.gameObject.GetComponent<Enemy_Manager>());
}
}
private void OnTriggerExit2D(Collider2D other)
{
if (other.gameObject.layer == target_layer)
{
enemies.Remove(other.gameObject.GetComponent<Enemy_Manager>());
}
}
thank for all your answers and sorry being unclear with my problem
Answer by Nomenokes · Feb 11, 2018 at 02:31 PM
I think the problem is that OnTriggerStay
will not necessarily line up with the FixedUpdate
function. The moment that dot_timer
is 0, there might not be an OnTriggerStay
called. What you should do is make the dot_timer
dependent on OnTriggerStay
. For example,
void FixedUpdate() {
}
private void OnTriggerStay2D(Collider2D other)
{
dot_timer -=.01; //Physics is based on real time, not deltaTime.
if (dot_timer <= 0)
{
dot_timer=base_dot_timer;
//...
The problem would have been worse when you had Update going because it calls less frequently
Your answer
Follow this Question
Related Questions
OnTriggerEnter only working once. 0 Answers
Detect gameObjects within range and assign them as movetarget 1 Answer
Can't catch event of the pressing the button 1 Answer
Problem with OnTriggerStay()... 1 Answer
Work around for onTriggerStay? 1 Answer