#bunp-collision-detection-issue
1 messages · Page 1 of 1 (latest)
Yes, CollisionAvoidance is only called by the player manager right now
The other object does not yet call it
Okay, so, let's do another debug thingy. You have most of it, but let's expand a bit. Make this the first line of CollisionAvoidance:
Debug.Log($"Collision Avoidance is happening between {collider.gameObject.name} and {collisionPoint.collider.gameObject.name} with {collisionPoint.contactCount} points of contact");
First line before the IF or after the IF?
Before the if
Okay
It'll spam the console a lot but it'll give valuable information
commented out mine, putting yours in
It thinks something is null, let me solve that
Ah, collisionPoint is null because I havent collided with anything yet
because the debug is before the if statement that checks if collisionPoint is null
collisionPoint.collider.gameObject.name cant be tested because its null
Right, forgot about that. Wrap it in it's own if that only does the null check
Will do
What we should see here, is that whenever the player is colliding with something, we get the player's name, the collider (9) or whatever object you touch, and 1. There might be a single instance of it being 0, and that could be the root cause of the issue
That is without the dog in the scene which causes the break
now with the dog..
hrm that shouldnt be showing, should it
why is the dog firing off that if statement, there is only one reference to that method and its on the player
If you click on that last debug log, you get a stacktrace, just like for an error. Can you send a screenshot of that?
Oh
this is why
the dog has this on it
it was just similar is why I mistook it
Yeah, the first one should fire for both
The collision avoidance is only happening with the player
clicking on the last debug log
As it should be
I think the issue is what I thought before, CollisionStay seems to fire one last time on the frame CollisionExit happens, when the contact points are 0
Yes, I just wanted to get a sense of the call stack, so I know where in the code this is all happening
Is it a single instance, or does it stack up as long as you aren't touching a wall? You have collapse enabled, does the count of logs continue to tick up?
It does the zero call if I walk away from the wall, if I stand next to it, no zero
Does the 1 line tick up in that case? It might also be a good idea to disable collapse for now
Just so you can see the most recent stuff last
this will help you understand the weirdness maybe
what you are seeing here is the full entire avoidance method being executed, but it only happens when the ball is there
I noticed it calls the zero check over and over, when the ball is there
but not when the ball is gone
When the ball is gone, do you get spammed with 0 logs?
And when the ball is there, do you get spammed with 1 logs?
I only get spammed from the one on the dog
if the dogball exists
and I dont move my player adjacent to the wall
Okay, so collision avoidance is not being called when the dogball is touching the wall
hat seems to be the case, it starts only after I touch the wall with the player
and if I step away, I get 1 single instance of zero
but if I pull the DOG away from the wall, I then get spammed with zeros
Are those "collision avoidance" zeros, or "collision happened" zeros? In fact, let's repeat this experiment with the OnCollisionEnter one commented out
Will do
this is getting a bit hard to keep track of, and that log's served its purpose for now
Okay, yeah
What we should be seeing now:
- No logs until the player first touches a wall
- Spams of
1logs while the player is touching a wall - Spams of
0logs after the player leaves the wall - 2 and 3 should not change after spawning the dogball
Okay, dog touch wall, nothing in the console
Player touch wall, avoidance check called with 1 collision continuously
Player stops touching wall, avoidance called with 0 collision, once
If I move or delete the dog at this point, avoidancecheck called with 0 collisions, continuously
Just to have it in the thread, can you put the top of CollisionAvoidance function? With our new log and your old if statement?
If I then walk towards the wall, avoidance check calls with 1 collision, despite the player not colliding with the wall
after that final step
but only if the dog is colliding with the wall
Do the player and dogball have any sort of parent/child relationship at all?
None, no links what so ever
Okay, so, there's some weird spaghetti happening here, and we can keep unravelling it, or try to solve the original problem a completely different and less convoluted way. Which do you want to do?
I always like to know why something is breaking so I can avoid it happening again in the future
We could fix this instance in a less convoluted way, but then we wouldnt know what caused it in the first place?
It is interesting the wall returns 1 collsion, but never 2 collisions
it returns 1 collision when the player is not touching it but the ball is
or if the player is and the ball isnt
but never both 2
Right. I do want to know what's happening here, but I do think the root cause is the use of OnCollisionStay and never clearing that data
So we might unravel this and find that the solution is "do this a completely different way"
Yeah fair, I can set that stored thing to null on CollisionExit
I was just concerned if I did that, sometimes it would screw up
Honestly, that could be a quick fix. If you want to try that
I have been having weird issues with unity collisions in extremely hard to reproduce situations I will get a return of no collision, despite havin collision
Ill set it to null on exit now though
That did appear to fix it, but confuses me immensely why it was working without nulling that value back out before I added a second object to the scene that could collide
I will try adding like, five dogballs to see if anything changes
I am not entirely sure either
I was intending to give the dogballs this same collisionavoidance method as well, I just hadnt gotten that far yet because of the weird behaviour i was getting from the player
What we should be seeing is that after you exit the wall, you are spammed with 0 logs
Since it sets to 0 once, and the collision object still has a collider to pass that null check
What happens if you are colliding with 2 walls and you stop colliding with just one after this?
I will test that, that was my biggest concern with setting it null on exit
Double collider, no dogballs in scene, never see a 0 return (expected)
adding one dogball back in now
also still setting to null on exit, didnt appear to mess up when leaving one collider but still touching the other
Still nothing return 0 with one dogball and null on exit
Here's what I'd do:
- Make a Dictionary of
Collider2D, Collision2Dthat will store what objects you are currently colliding with - In
OnCollisionStay, add the collision to the dictionary with that collision's collider as the key - In
OnCollisionExit, remove the collider from the dictionary (which will remove it's associated collision)
Then you can query that diction to find everything you're touching, and can operate on them all at once via currentCollisions.Values to get a list of Collisions
That's very clean
I aspire to reach digiholic's level of knowledge and patience someday
let me see why it doesnt want those as key/value
cannot convert from collider2d to collision2d
oh I have them backwards I think
that fixed it
Yeah, collider first, then collision
The Collision object each frame of OnCollisionStay will be different, but the Collider that generated them is the same
Yeah, that should do it
Same
Okay so now I should use the Dictionary in my If test for object avoidance?
Yeah, now you have a convenient way to get every collision currently happening on this object
collisionDictionary conveniently has two things for you:
.Keys will get you a list of every collider object you are currently touching at all times, while .Values will get you a list of all of the collision events with all their data about your current collision state with that specific object
So, if the goal of CollisionAvoidance was "check if I am currently colliding with a thing", you can just iterate over .Values. If you aren't touching anything, the dictionary still exists but is empty, so it won't throw an NRE
Okay so now instead of passing in the collision 2D, I pass in the Dictionary. The CricleCollider I am not sure if I still need
I have not used a Dictionary before, they always sounded interesting butI havent had the usecase until now
You can also think of it as a "map"
You put in object A, and it gives you the corresponding object B
So, you can do collisionDictionary[someObjectCollider], and if there is a collision with that collider, it'll return it. Otherwise, it'll return null
So now with the Dictionary<Collider2D, Collision2D> collisionDictionary being passed in..
I need to replace those with... check if the dictiionary has ANYTHING in it
and that should cover both those checks I think?
if (inputAngle != new Vector2(0, 0) &&
collisionDictionary.Count != 0)
{```
and now I have to fix where I used that info
Yep. That will pass if the angle is not 0,0 and you are currently colliding with anything, that'll pass
Then, if you need info about each individual collision object, you can use a foreach loop
This is where I was using the collision data before, I was naievely just taking the first collision which seemed to be working in all test cases
Are you using a repository? Be cautious when refactoring like this
Possible, although one issue with dictionaries is that they aren't ordered. The "first" collision could be different every frame you check it
Whats a repository? I use github to backup my unity project
I am the only programmer on this project, its solo
That would be a repository
There's no risk of push conflict since its just me
Even if you are a solo developer, you still need a repository :p
Yeah, but having a checkpoint before doing all this, just to look at, is good
Okay in that case I do have a repo on github
What's a checkpoint?
A branch?
A push that works before I make all these changes?
Imagine you accidentally deleted one block of code when refactoring this and you forgot what it was
Ahh
A commit
A commit
Okay yes in that case I have a clean commit that works before doing this 👍 For exactly that reason, if I break something and dont remember what
I have not often broken my entire project that badly as to need the rollback, but Its happened enough that I do this commit thing
Okay so now I should replace
collisionPoint.contacts[0].point with the dictionary somehow
hmm
Can you not pull from a dictionary at [0]?
I can do contains key, or value, but I am unsure what to pass in
I am unsure how to replace that bit with the Dictionary's equivalent
oh I can just do Dictionary[0].thing
hmm
am I dictionarying the wrong thing?
I cant seem to find contacts[0].point
I should check the unity doccumentation on dictionaries
hm no page for that, maybe csharp then
@fresh jetty I'm stuck, I can't figure out how to actually retrieve what I need from the Dictionary after passing it in
I think I might be dictionarying the wrong thing
Sorry, I stepped out for a bit. You can't get [0] from a dictionary because it doesn't actually have an order
do I want a dictionary of collisions, or colliders?
"collisionPoint.contacts[0].point " is the info I need from the dictionary
You can get a list of keys or values from a dictionary and get the first element there, but that won't always be the same one
So, you can do collisionDictionary.Values[0].contacts[0].point
But if you're touching multiple walls, that might be a different point each time
If that's not a deal-breaker, that should work
Cannot Apply indexing to dictionary
I was just doing [0] because it was easy
I should really be using... uhh.. I dont want to check every single collision, I dont know how to resolve that
the first one I guess?
Oh, it's not a list, it's a different kind of IEnumerable, hang on, checking some docs
It's one of these:
https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.dictionary-2.valuecollection?view=net-5.0
That's what .Values gets
Resolve the collision or the issue? The best way to resolve collision is to iterate through each colliding object
I will try to make sense of this doc but I fear I am not programmer enough
Yeah, it's basically just a list-like structure that doesn't actually have an indexer
My before method was just to check [0] and it it worked in 100% of my use cases
so Ideally I just want to do that again, because I would have to re-write like 300 lines of code to make it work with multiple simultanious collisions
Ah, found it:
collisionDictionary.Values.ElementAt(0).contacts[0].point
Really? Maybe it's from a namespace? I just ran it in my VS and it worked
Does your IDE suggest anything?
Yes, that makes sense
it seems Linq-like
That's also a good sign. Linq is hella fast
oh really? I was literally just about to say "I heard linq was slow and never to use it"
So obviously I heard wrong
some Linq things are slow
but they're the fastest way to do the thing
If it's slow, it's because it can't be fast
Ah, I dont remember what I was not using it for before, maybe it was that thing that was slow, because it was not this thing for sure
Like sorting a list is never going to be faster than n log(n), and in most cases will be n^2
Don't worry if that makes no sense, it just means Linq is usually the best algorithms available
okay now that ive made these changes, let me fix all the wrong variables being passed around to get it to compile
You might be able to make a specific case implementation better, but no one beats general-case speed of the mathematically determined best algorithm
Good to know, I do like best in slot
I got it to compile, testing if the behaviour is unchanged
Null reference on my dictionary
I might have to initialize it in awake maybe?
Oh, you gotta initialize it
I figured
You can just do it in the declaration
Since dictionaries aren't serialized, Unity doesn't do it for you like it does for public lists
Yeah, that should do it
Oh, right, don't use add
use collisionDictionary[other.collider] = other
it'll add if it doesn't exist
or change it if it does
Hmm, the behaviour has totally changed
the only lines different were replacements of
contacts[0].point with collisionDictionary.Values.ElementAt(0).contacts[0].point
so I guess they were not cleanly equivalent in this case
Give me a moment to debug how its returning weird
hmm none of my debug hooks tripped that's even more concerning, deeper I go
Ah
weird corner case is the thing thats going wrong in this weird situation, of course it is, now to debug it
actually this shouldnt being called at all
god its incredibly dificutl to debug this function
because it only works in real time
I cant just set a hook because it gets called every single frame
You can use Visual Studio's debugging tool
I think I am but you must be refering to something I dont know about
I do hook to unity debug
Oh I see
and set red dots to pause code
this method gets called every single frame and requires player input values so it pauses constantly
okay I have no idea whats going wrong, but i dont think it has anything to do with the dictionary
as far as I can tell, the dictionary is working
this will probably take me a few hours to debug what is going wrong
Something is completely FUBAR with my method now but I've figured out what
for some reason all my raycasts are just completely FUBAR and I have no idea why, all the values are completely wrong
Ill stop reporting this and keep trying to fix it
because its at least something I think i can fix myself
@fresh jetty @surreal siren Everything appears to be working. I rolled back to the previous version of this function before adding the dictionary, re-added JUST the dictionary to it, and all behaviour is normal. Just like you said it was good to have the backup before the changes. I will take some time to try to determine what differs between current version of the method and the previous version.