Unleash the Power of Raycasts with Dot Product (Unity Tutorial)
Вставка
- Опубліковано 14 жов 2024
- Sign up for the Level 2 Game Dev Newsletter: eepurl.com/gGb8eP
This Unity tutorial will show you how to dramatically improve your raycasts using the dot product of two vectors.
#Raycasts #Unity3d #UnityTutorial
📦 Download the project at / 27222309
This Unity tutorial was created in part with Jason Storey (apieceoffruit). Learn more about Jason by visiting www.firepanda.c...
👨💻 Join our community: / discord
❤️ Support the channel: / infalliblecode
My Favorite Unity Assets 💯⤵️
1️⃣ Odin Inspector: assetstore.uni...
2️⃣ Shapes: assetstore.uni...
3️⃣ Easy Save: assetstore.uni...
4️⃣ Dialogue System for Unity: assetstore.uni...
5️⃣ Editor Console Pro: assetstore.uni...
⚡ Learn more about Unity 3D Plus at prf.hn/click/ca...
👋 Contact me directly at charles@infalliblecode.com
Disclosure: These are affiliate links, which means I'll receive a commission if you use them to make a purchase.
❤️ Consider supporting this channel by becoming a Patron: www.patreon.com/infalliblecode
Trying to get a Ui Icon to face in a certain direction when the player moves in X world space, but can't get it to work, it works in the z direction world space but not x? The Z code is:
if (Mathf.Abs(Vector3.Dot(Player.transform.forward, Vector3.forward)) == 1) aligned = true;
{
if(aligned) { WindArrow.transform.Rotate(Vector3.up, 180f); }else { WindArrow.transform.Rotate(Vector3.up, 0f); }
}
The X is:
if (Mathf.Abs(Vector3.Dot(Player.transform.right, Vector3.right)) == 1) aligned = true;
{
if(aligned) { WindArrow.transform.Rotate(Vector3.forward, 180f); }else{ WindArrow.transform.Rotate(Vector3.forward, 0f); }
}
Don't know why the Z direction works but not the x as Vector3.right should be x direction? Any takers on this one lol??? Thanks guys much appreciated. Would a Raycast help to determine the direction and if so how would the code look like just a snippet.
Charles everything about this video is clean.
Thanks man! Means a lot coming from someone who knows what it takes to put one these things together 😄
This is method is a great start. For anybody watching this, you have to realize that IT IS just a start. I think it goes without saying that you'll need to implement the adding/removing of selectables from the list based on the needs of your game. From a workflow standpoint, I like this method better than adding a Sphere Collider that's larger than the object. Especially if you're talking about VR where objects may need to be selectable and still collide with natural looking collisions. Implementing it in code means that every object can be "selectable" based on a specific tag or component.
I'm going to pin this comment because you hit the nail on the head. The focus of this tutorial the dot product so I chose a simple use case to make it easy to understand.
In the next video I'll be covering chunking. We'll add some more sophisticated code that'll update the list of selectable objects based on the player's position.
@@InfallibleCode looking forward to the update. I intend to integrate this behavior with an FPS controller (not mine) that will soon be up on GitHub.
I'm fairly new to coding but to add to this, another thing to take into account would be the size of each selectable object. Since transform.position returns the centre of the object it might mean that threshold falls within larger objects. That's probably fine more often than not but I imagine on large enough objects that might mean having the eye pointed directly at the transform but being far enough away from the local vector3.zero to not select the object.
Easy enough fix but while I'm learning I find it helpful to try and think stuff like that through.
It's so refreshing to see a dev using not only Rider, but also Vim.
What about a sphere cast instead of ray casts? I agree with the others that the code here is far from infallible. Many comments also seem to suggest some alternative implementation of iterating over a list to perform some calculation(s) or using the physics engine via a collider.
A sphere cast effectively addresses the exact problem stated at the beginning of the video with raycasts being "too precise", the unity docs describe it as a 'thick raycast'. It's essentialy a compromise between the two approaches while solving pretty much all of the pointed out problems with either. It returns the first object it hits, effectively sorting to the closest object; however, SphereCastAll could still be used to implement custom sorting. It doesn't require any objects declared prior to operation in a list or for colliders in the scene to change in any way or extra ones added to behave as expected. I believe this to be the most robust solution, and requiring less manual iteration which is good at large scales since the physics engine already iterates in the background with a spatial search that I'd have to assume physX implemented fairly efficiently, while keeping code and in-engine setup to a minimum.
Yeah I've seen a lot of this same feedback and it's totally valid. The main focus of the tutorial was to demonstrate a practical use case for dot product, which is why I didn't bother optimizing how selectable objects are collected. In the next video I'm going to cover chunking to and update the code so that the list gets updated based on the player's location. That way it will only ever have a handful of selectable objects at any given time.
@@InfallibleCode Glad you're so calm about this. Great video as I immediately thought the same thing when you said list. As I immediately imagined a larger game where you might loot over 50 objects and looping through each would be insane.
As they say; I would use a spherical cast to get the object then get the distance from it's center to my view's center and select the nearest.
Saves on memory of having a list and having to loop each time, especially if it's like you showed and it does this every single frame. That's an insane load
@@InfallibleCode I know this is a year old and I absolutely love your videos but one concern I have is that these complaints you're seeing are from more experienced developers who were able to recognize a potential issue in your design right away and address them in the comments, but an inexperienced developer (who happens to be the target audience) likely won't recognize these issues and take this tutorial at face value for how it should be done. This can sow the seeds for bad coding habits. I know the video is meant to be more of an example of how to use dot product rather than a tutorial for this specific use case, but a lot of people in the target audience are not going to realize this.
Just something I think is worth thinking about.
@@InfallibleCode Hey Charles, I can't seem to find the video on chunking and culling the list of selectable objects. Is also part of this playlist?
I use spherecast for my grappling mechanics to add an "aim assist" it works wonderfully
Well done. Expanded my horizons of the subject. Can't wait for the next one.
Thank you very much.
The title Improved Raycasts made my nerdy experimenting mind drool.
I must've been asleep during this part in linear algebra
Haha me too! I only have this knowledge because of game development xD
Infallible Code haha i find it way easier to learn math when i have to use it for a game. I made a pretty good grappling hook using linear algebra but I don’t think I would’ve been able to do anything similar in a math class lol.
MAN! Do I like that dual screen and that microphone! Looks pro and cool! Great videos too! :)
Thank you! This desk is like my second home so I put a lot of work into it :D
I was using a different approach than this, using a mix of ray casts and sphere casts (non alloc version) to eliminate the need to manually specify the list of selectable objects, anyways great video keep up!
I used a static list of selectable objects to keep the tutorial simple and focused. In the next vid I'm going to implement chunking so the selectable object list gets updated based on the position of the player :)
@@InfallibleCodeGreat!
This approach is pretty clever but still has some major flaws.
Imagine we draw a circle around each object to represent its selectable region.
Based on our implementation from the video:
- ALL CIRCLES WILL BE THE SAME SIZE, regardless of the size of the object
- As we move around the scene, the circles can move but will not change size
- The size of the circles will only be affected by the "threshold"
But...
- SELECTABLE OBJECTS ARE DIFFERENT SIZES
- Selectable objects will appear larger/smaller depending how far away they are
So here are the issues that still need addressed:
- Objects further away will still interfere with the selection of nearby objects
- The player is forced to find the origin of objects that are bigger than their circle
- This is not compatible with objects with different interaction distances
- Objects hidden behind walls (or other objects) will still be selectable
And the drawbacks:
- All selectable objects must have their origin at or near their volumetric center
- If the player is short, there will be a lot of overlap between selectable regions
do you have fixes for these issues?
@@jamesmahnke1431 use an oversized mesh for collision
plus, you'll notice there's a lot of places where it falls apart. For example, at 11:46 they look directly at the plant, but the dot product says they're looking perpendicular and slightly away. At some points near 11:49 it selects the plant that is much further away from the center point, and says that the closer plant is 0.88 while the further plant is 0.97, which is wrong.
All in all, yeah, maybe don't use dot product for this specific example. Instead, just make a collision hull for the mesh, like a sphere collider that's large enough. As well, design the levels so that selectable objects aren't near enough to cause overlapping problems, like "If the player is short, there will be a lot of overlap between selectable regions"
Edit: there's also the idea I had of using AnimationCurve for interpolating the size of the collider. Specifically, when transform.position - ray.origin is low enough, it makes the collider much larger, and when it is high enough, the collider becomes much smaller. You could use Mathf.Lerp, but that hard codes the numbers in and forces you to rely on your imagination and understanding of the math, rather than with AnimationCurve which you can edit easily and is actually a real thing you can see.
Thanks a lot! This solved a problem for a project I worked on several years ago. Too bad I didn’t have it back then.
Haha well the timing wasn't right but I'm glad it helped anyways :D
I appreciate the explanation, it is very useful to show how dot products work, and also one clever use of them.
Honestly though, I would recommend not using this approach to solve your problem. To solve your problem I would simply add another collider that is specifically for selection. You can put it on it's own layer and make it a sphere trigger so it does not affect other objects.
Why?
Many reasons.
1.) Looping over all objects in the scene every frame can be insane. If you have a large world with thousands of objects you will end up with a performance problem. It is true you could mitigate this by doing a spherecast at your players position to find a bucket of objects to loop over (but this will have a max distance).
2.) If you have multiple objects that are all in the cone at the same time which one do you select? Your loop will simply overwrite your selection until an arbitrary object wins. With your current code distance isn't a thing at all, so objects that are on the other side of the map will also be in the selection list.
You could do a distance check to find the one that is closest also, but this adds cost, and may not be what you want. If you have 3 grasses in a small cluster, you will be frustrated by your inability to select a specific one of the 3.
It would be very helpful to learn if you have other uses of dotproduct that you like to use it for.
Hey Ben, we are well aware of those issues and had intended to make other videos around the topic but moved onto other things, it would have eventually ended up with a demonstration of how with some extra checks it can be far better than raycast. User comfort/feedback is one of the few areas that cutting corners for performance is a bad idea. After all the point of a game is the users experience. in the end we would have ended up with this kind of example, with quad tree filtering and selection rules. a far more rewarding user experience than a simple collision trigger: imgur.com/rYCkp1r
Would be nice to invest toward any raycast call that utilize NonAlloc() calls. These special functions save performance costs without producing any garbage collections! (Plus, it returns you a controllable array set without having to iterate through all collision detection, which controls your notation factor!)
Thank you! Finally this Dot makes sense :)
I need this!
Interesting video, problematic is that the dot product doesn't depend on the size of the objects just the "angle of those norm vectors". When up close a large object might be unselectable, since its center is not in front of you or near it
Very true, the selection behavior should prioritize any object directly hit by the ray to avoid edge cases like this. This would also prevent weirdness with selectable objects directly behind each other.
Thanks for the great tut. I am wondering how do i determine if the object is really visible to the player and not behind a wall? A simple raycast does only check if the objects pivot is visible, but we should avoid that. Am I right?
Hi. I'm curious what extensions you use? For finding files and putting lines on code in for loop etc..?
Great, valuable tutorials 👍 👏
Could this cause a massive overhead though? I feel like the selectable items should be adding/removing them selves from the list when visible. Shouldn't need to be calculating stuff you can't see
This is method is great start. I had the same thought you did, but I think it goes without saying that you'll need to implement the adding/removing from the list based on the needs of your game. Which should be fairly easy to implement.
Excellent stuff ! One question and you probably answered it a while ago in a previous video, but i noticed that you have extra comment-like-information near your variable declarations (ie, "set By Unity"...) DO you know how i can install that function ? I am also using VStudio
Those features come from the IDE I use called Rider. I HIGHLY recommend it. Here's an affiliate link to Rider on the Unity asset store: assetstore.unity.com/packages/tools/utilities/rider-128844?aid=1100l3e8M
@@InfallibleCode Thanks very much !
Outside of this video, it seems that you have been improving your life quality! Care to share us some of your success stories and secrets in balancing life quality and coding in Unity on every day basis?
i made an extra layer just for selecting. and generell interactiong. so i can difine a trigger collider (that dont collide with anything) on an child gameobject of the main object.
so you have the normal colliders on xy layer. and the "interaction" child with its own layer. then its easy to setup the "hitbox" or watheer in the scene
i wonder if this is cheaper then the method in the video.
Very nice videos an professional coding 😊👌 keep up the great work.
Would like to know how I can pick up items and put them into a inventory via raycast 😄 though I used spherical colliders to get the same effect you have shown and a raycast connected to my first person camera 😊
Using a spherical collider is a great alternative to this approach. That's the great thing about game development, there are many ways to accomplish the same goal! What's "best" comes down to your needs. Look out for an inventory tutorial because I may do one soon ;)
I personally find that the treshold you use is too low but I guess it's all a matter of preference.
Great video, well explained, not going too deep into what is the dot product. Cool stuff! :)
Thanks! Yeah I think that threshold works better at greater distances but I'll want to raise it when I factor in distance.
This. Is. Amazing! Taught so cleanly and very well produced! Your videos are exactly what I need for the mini project I’m attempting.
lol @ all the dumb ppl who think this is smart.
Nice Video...
By the way, which code editor are you using?!
It's called Rider and it's AWESOME. Here's an affiliate link to Rider on the asset store if you want to check it out: assetstore.unity.com/packages/tools/utilities/rider-128844?aid=1100l3e8M
Great video, congrats.
I have a question, how did you setup this auto comments on the code?
like "Set by Unity" in front of variables for instance and the number of method references on top of them?
That's a feature from Rider, the programming IDE he is using. If you can afford a license I'd definitely recommend it.
@@lupidan Thanks a lot mate! :)
I'll definitely check it out.
Yeah it's an amazing tool. Here's an affiliate link to Rider on the asset store if you want to check it out: assetstore.unity.com/packages/tools/utilities/rider-128844?aid=1100l3e8M
This algorithm doesn't take the size of the selectable into account right? So if I had a really large item I could be pointing at it and not have it highligh, if I am not pointing close enough to the center of the object.
Nice video to understand the dot product but be aware that this solution doesn't scale very good. The cool thing about Ray cast philosophy is that is solving not to maintain a list of interactive objects... anyway thanks for your videos. I like them very much. It's very difficult to find quality content that is not just introductory for begginers.
Awesome tutorial :) there's a lot of ways to do that i think that the most easy it's just create a collider in thoose objects which just collide with player raycast and nothing else but i don't know if it's good pratice in unity.
And now, how do you color the contour of the object when you somehow select them? Omg it's so frustrated you explain everything but not what I'm looking for TT
11:52 we've got a rebel tree.
Wouldn't it be much simpler to have a trigger collider with a simpler shape, on top of the actual collider (if any). Then the raycast would detect the trigger collider and the actual collider, then see if the trigger collider is attached to a selectable object, and its done. Also the look percentage isn't gonna take objects obstructing the view into account.
How would you visualize this?? for example to apply some sort of lantern/line of sight
great!
Except it doesn't work. Look at 11:52, since you're only testing against the dot product to the object's origin then the closer you get, the harder something will be to select. At that distance, you have to look almost directly at the base of the plant, looking at the leaves will move the 2 vectors apart enough that it won't be selectable. It's, ironically, worse than the solution you had before when you're standing near object, and since you generally want to interact with objects that are close to you, I'd say this method is not appropriate at all.
Just add a better collider to raycast against. A simple box collider should work fine for that grass, and the ray won't slip between the leaves.
Well the usecase for this is for example VR hand selection. yes you would use a distance check for close objects the point of this kind of system is that you have a selection value to each object, the next video will demonstrate that you can use that percentage to have a sweeping highlight across multiple object. A sphere collider works with only one object. everything has its uses, and we will demonstrate the differences between this solution and others in the next video.
4:59 The dot product is not 0 given the horizontal position you gave, it needs to be along the dotted line.
Nice video, how you feel using Mesh Collider the Convex option to solve that problem of the leafs in particular?
It just depends on the needs of my game. Changing the collider is certainly a valid solution but what if I wanted the object to keep its original collider so I could achieve realistic collisions?
@@InfallibleCode in that case normaly I use SphereCast. "Physics.SphereCast(transform.position,1f,transform.forward,out hit,Mathf.Infinity), Unity docs: "Think of the sphere cast like a thick raycast. In this case the ray is specified by a start vector and a direction", but still you solution was amazing with the Dot product.
Am I missing something or is this an incredibly bloated solution when you could easily just cast a box/sphere to solve the same problem?
You're absolutely right. You could also just use larger, simpler colliders for the objects you want to raycast against. There's no good reason to be using such complex/accurate colliders for those plants, for example.
good toturial.
I think checking all objects is bad. Imagine that the number is 300 or more, possibly causing a drop in the frames. I think it is best to use the CheckShpere function to know if there are objects around you, and then use the OverlapSphere function to get those objects.
Sorry for my English.
Is better to use the hit method version with distance parameter to increase performances reducing max distance instead of default one which is INFINITE.
Very interesting. But I am even more interested how to use raycasting within ECS
Me too!
@@InfallibleCode github.com/Unity-Technologies/EntityComponentSystemSamples/tree/master/UnityPhysicsExamples
I think you missed the simple observation that for normalized vectors, the dot product is just the cosine of the angle between them. All that stuff about percentages is rather misleading, IMO. In essence, your implementation is to select an object if its centre is inside a conical view frustum. That has advantages and disadvantages. I feel the disadvantages outweigh the advantages, but it will work as a simple example.
Dude this is an awesomely optimized way to tackle the situation..thanks man 👍
Glad to help!
Just put another sphere slightly bigger than mesh... :)
This approach has some major flaws that can be seen in the video like selecting things that you are not looking at because your camera is facing up or down but your XZ direction faces the object. My approach for cases like the grass is to create an invisible collider with the layer set to someone that doesn't interact with anything and doing only the raycast to that layer. Therefore you can have a big selection object and you dont need to precisely point at the object and also you won't collide with the object.
If the threeshold is 0 or close to 0, wich method is more eficient?
Finally calculus is useful for something ;)
But this is linear algebra
why not just use angles betwen the view and the center of the object?
Why the value of dot product is almost equal to 0, when you point a plant on 11:55?
dot product of normalized vectors, important to note.
I'm not sure tbh. I'll have to take a look at that.
Good video. Whether or not this is the best way to do something like this is debatable as you can see in other comments, but it was a good way to show people something new. One issue I can see is that having to maintain a list of selectables could be tricky and hard to scale, whether you do it with code or by hand. If you do it by hand in the inspector you've gotta remember to go add shit to it when you add stuff to the scene. If you do it with code you've gotta have some kind of class that periodically checks for new selectables using gameobject.find and/or gameobject.getcomponent, which could get costly and ugly. I guess you could do some kind of custom editor code that has a button you can click when editing the scene to go out and refresh the list, but that won't help you if you make a game that has selectables that get spawned or despawned during gameplay.
I was toying around with something similar recently. They weren't selectables but I was just randomly playing around and asked myself "How can I get myself a reference to components of a certain type but only worry about them if they are on the screen?" Unity has a OnBecameVisible and OnBecameInvisible you can put on MonoBehaviours, so I thought hmmm what I have these things register with some kind of object keeps a copy of the list as they enter and leave the screen? I ended up with something that was a pure C# object that got injected by Zenject that each monobehaviour could just ask for a reference too with an [Inject] attribute on a private field. It worked fairly well except for one weird thing: OnBecameVisible/OnBecameInvisible are called not just based on whether the mesh enters or leaves the screen, but also the SHADOW of the mesh.
Several things not great about this video (but it's a simple approach):
The fudged factor for the dot product is meh but the normalized vectors make it ok.
I hate running an o(n) linear search for every ray instead of using selection volumes (sphere colliders).
You would also need to regularly sort the objects by distance from the player. Another o(n*log(n)).
But most importantly, You don't benefit from the BVH structure that makes raycasts relatively efficient.
I'd better changed grass colliders to convex mesh. This way it would still be precise, and way easier on selecting grass.
Please share that highlight shader.
Here ya go! This is an affiliate link that'll take you right to the shader in the asset store: assetstore.unity.com/packages/tools/particles-effects/quick-outline-115488?aid=1100l3e8M
So, Why not sphereCast?
THIS JUST-IN: Subscriber Count just went Up by One.
CAN DO IT WITHOUT COILLDERS
I feel this is not the best example for using the dot product. It is just not working as intended. A better example would be calculating the angle between a bullet impact direction and a wall surface normal. You could make the bullet more likely to ricochet when the dot product between the bullet direction and the impact normal approaches 0 ( the bullet direction is close to perpendicular to the wall)
I mean.. good lesson on DotProduct but not a great approach to this problem. I personally would go for custom colliders for selectables. So I'll put a sphere collider (trigger) on that grass.
So the only reason I need to build UI from scratch is I am using ARfoundation and I'm not skilled enough to hack GoogleVR to play nice with ARKit. I can't get this working in my own project because this is WAY more advanced than I am and I don't expect hacking this to work with ARfoundation will be something I can accomplish either. Does anyone know of it's possible to have UI attached to an ARfoundation camera, and maybe with a tutorial, ideally more basic than this? Also, unlike this tutorial, I need to know how and why it's built a certain way -- this tutorial feels like part 3 of 3, and I have no idea how it's built. I just need basic UI in an ARfoundation project.
I became 100% stuck when I got to the "Graphic Raycaster" script that claims it "cannot be run in DOS mode" and won't open in Visual Studio and isn't in this project when I search for it. I don't know how to edit that script or even find it, so not very likely I will be able to copy it into my project...
var variable = new IVariable();
just "ew"
Glad you made the video but let's be honest - the solution sucked up close and from far away. Wouldn't work on long objects like pipes. Always having a list of all selectables and updating that is wonky also. It's all fine if you wanted to demonstrate how to use Vector3.Dot() but should have mention it. My wish for next video - fix all that?
i also think this method is over engineered, i would just use a big simple collider like a box or a sphere and check the ray against that, i also dont know why a simple scene object has to have such a detailed collider , why would a plant need a mesh collider with all of its leafs, that sounds like a performance disaster in the long run, the simpler the enviroment is, the better, the visuals are always an illusion in games
Agreed.
Your original raycast hit approach is vastly superior. Are you presenting this approach just to demonstrate now dot products work? If you want objects to be selected more easily, just use a spherecast with the size of the sphere as your tolerance. The dot product approach adds unnecessary complexity and likely performance degradation.
This technique is interesting but I wouldn't use a raycast for this.
For noobs, Noob Tip: Playback Speed 0.5 X
Just sphere cast...
6:10
"Normalized vectors are going to give us a value between 0 and 1."
Not important unless someone decides to make that an assumption in their code, but you misspoke.
Just give it a larger hitbox lol
I think this is a pretty good idea. I immediately thought of spherecasting to solve the problem you talked about but there was someone else who said that requiring a collider on all your lootable objects might not be practical (Unless you have sort of a child object on your GameObject like a Loot child that has its own collider.... not sure).
Also, I wonder if an object is behind and directly in line with another object that you´re looking at, wouldn´t it be returned as selectable? I think you could still use raycast to account for occlusion. And of course checking what selectables are in the chunk/voxel/space you currently occupy.