Search Unity

Question Can I detect a projectile collision without using colliders?

Discussion in 'Scripting' started by Valasty, Aug 17, 2023.

  1. Valasty

    Valasty

    Joined:
    Aug 22, 2013
    Posts:
    125
    Yes, I know it's a weird question, but just to give you a background, I'm building a Final Fantasy Tactics style game and It's pretty much entirely built based on grid and math:

    upload_2023-8-17_18-38-16.png

    I want to add projectile skills to the game (such as a Bow attack in FFT) and I know that it can be easily done with linecasts and trigger colliders, but I wanted to avoid doing that since it feels like an overkill for such a small functionality. I'm kind of a performance freak so I'm very afraid of overkilling stuff lol

    I could lerp the trajectory and add some detection points, but I have no idea how to detect a collision with a mesh without a collider. Is this even possible?

    I could also lerp the trajectory and map the tile positions (as they are all pretty much "Vector3(1, 0.2, 1)"), but that doesn't quite work on advanced obstacles such as bridges or trees, just terrains.

    Any suggestions?
     
  2. Cloudwalker_

    Cloudwalker_

    Joined:
    Jan 3, 2014
    Posts:
    140
    You should have zero concern about using raycasts and colliders. Your game looks very simple CPU wise.
     
  3. wideeyenow_unity

    wideeyenow_unity

    Joined:
    Oct 7, 2020
    Posts:
    728
    To agree with Cloudwalker, true, it shouldn't be that much a worry. However raycasts can get expensive, performance wise, if using a bunch of them all in one frame, plus it also matters how many vertices are on said collider. But if using a simple box collider, that should have no issues at all.

    But only other way(that I can think), would be having a list of the enemies, getting their positions, and calculating the numbers for distance, direction, or even height difference. Then just a simple iteration through that list, and decide which one of those enemies should be the current focus.

    As far as trajectory, like an arrow or mortar shot, that's just simple physics calculations(I've seen tons of tutorials on this) with adding the proper force with gravity. Or you could define a simple curve with a set height, and make that the projectiles path to follow.

    Another way past raycasts, is Physics.SphereCollider, and just set the radius as the units range. When it's turn is up, one quick sphere check would show it (yes or no) that an enemy is in range, and since it returns a list of colliders, you can easily calculate distance and position of enemy, for your attack calculations.
     
  4. Valasty

    Valasty

    Joined:
    Aug 22, 2013
    Posts:
    125
    Yeah, I'm not really THAT concerned about performance, it's more wanting to avoid the micromanagement of setting colliders individually every map for something that has very little use within the game.

    My problem is with mapping the colliders on the terrain, as I mentioned above I would like to avoid that. As for trajectory and collision detection they are pretty much done already.
     
  5. wideeyenow_unity

    wideeyenow_unity

    Joined:
    Oct 7, 2020
    Posts:
    728
    yeah, I meant to just map the enemies positions. The collider is just another component on the enemy, same as the transform. But to be honest, I found using a list of colliders way more performant than referencing the gameObjects. So I could only assume mapping their transforms is even more performant than colliders.

    But I'm still confused as you would need to still get their colliders for projectile hits. Unless you mean you want to avoid that as well? Then that would be a simple distance check to the enemies transform, and when close to that transforms position, round the Vector3(with your own function) and have the enemy see that position as a static call of hitPos, and verify it is it's own position. But again, it seems well out of the way..

    Personally I wouldn't see it as overkill just to do it the standard way
     
  6. Valasty

    Valasty

    Joined:
    Aug 22, 2013
    Posts:
    125
    The game is based on tiles, so pretty much everything can be mapped based on them. Attacks hits tiles, not colliders (it's a turn based game). However, I still can't allow a projectile to go through a terrain, such as a tree or a rock (or just any elevation).

    What I'm tempted to do is Lerping the projectile path and doing a few checks within that to find their current tile (easily done by comparile tile/projectile X and Z), and if the project Y position is below a tile Y position, then the projectile collides. It gets a little trickier to detect if it colliders with a character in that tile, I would probably have to map the distance between the ray and the center of the tile. There may be other complications that I have not considered as well.

    The problem with the above is that I can only map ground objects, but it makes it hard to make "flying" objects such as bridges or trees:
    upload_2023-8-18_10-50-11.png

    There are two tiles occupying the same X and Y space (the one below the bridge and the other above it), so it gets a little trickier. It's not impossible, just trickier.

    Doing that I avoid having to setup my scenes individually with colliders, and I probably save some performance as well (even if not much).

    TL;DR I have a solution but I'm wanted to know if there's a better one I'm missing :D
     
  7. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,415
    So:
    • Spatial positions are limited to regular grid positions in XY
    • You can stack XY positions so you might have two objects in the same XY but at different N? How do you store this?
    • Do the volumes you hit occupy the whole voxel volume of XY at stack position N or do you need mesh collision?

    Can you not have these objects simply automatically use bounding boxes gathered from their renderers (Renderer.bounds)? You can then use the following API: https://docs.unity3d.com/ScriptReference/Bounds.IntersectRay.html
     
  8. wideeyenow_unity

    wideeyenow_unity

    Joined:
    Oct 7, 2020
    Posts:
    728
  9. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,415
    The OP doesn't want to author physics components.
     
  10. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,662
    This gets into what constitutes LoS (line of sight) and blocking.

    Games like XCOM and such use various rules, such as tiles that contain 50% blocking (like a bush), or tiles that block view but don't block projectiles, or block only laser projectiles but physical ones go through.

    In a FLAT tiled game, you absolutely should not be using physics for colliders.

    But if you have altitudes and elevations and other cover, you will probably want to take a step back and generate some kind of visibility analysis system to ask (generally), "If I am in tile A and looking at someone in tile B, how well can I see them / hit them / set them on fire?"

    Raycasting (indeed multiple raycasts) can be helpful here because you can try one from the source's eyes to a sampling of points on the target. You could even get crazier and decide "I can see his toes but nothing else, so I will make a called shot on his left foot."
     
  11. Valasty

    Valasty

    Joined:
    Aug 22, 2013
    Posts:
    125
    Wow, so many responses haha

    • Yes
    • Yes, but not many. Since there should be just a few I have just created a variable to store the tile above if there's one. I though about mapping the grid with Vector3 instead of Vector2 but it would be an overkill for just a few elevated tiles (if any).
    • No, but it shouldn't deviate too much from that (very similar to FFT, Triangle Strategy, Tactics Ogre, etc). I would add some gap on my checks, it doesn't have to be perfect as it's not a competitive game or anything, hence the reason I don't want to overcomplicate this implementation with physics.
    I didn't know Renderer.bounds, that looks promissing for what I need!
    I probably can't rely 100% on that because I assume the final map will be a single mesh, these boxes I'm using are just placeholders, but I COULD just break problematic objects into a separate mesh, I think that might work.
    I will dig deeper on that, thanks for the suggestion! :D

    Unfortunately not flat haha
    But yeah, this is easily solvable with raycasts, but I wanted to avoid using that as this is kind of a "nice to have" feature in this game as there will be very little projectiles (contrary to XCOM where it's pretty much all projectile).

    Thank you everyone for the inputs, I really appreciate it :D
     
    MelvMay likes this.
  12. wideeyenow_unity

    wideeyenow_unity

    Joined:
    Oct 7, 2020
    Posts:
    728
    Just thought of it, kinda like what Kurt suggested, but if it's tile based, you could just use a form of A* pathfinding. And when it searches the next tile(in direction), and sees that it's impassable due to height or objects, then miss/shot failed.

    Can't speak so much to how more/less performant it is. But if you wanted to simply line of sight, that could be one way.
     
  13. Valasty

    Valasty

    Joined:
    Aug 22, 2013
    Posts:
    125
    Just to update on the results, Lerp works and it's quite simple... it still needs polishing but it's a good starting point.

    It still has issues with elevated objects but I will figure out a solution, should not be that hard (worst case scenario I think I can do Renderer.bounds suggested by Melv)


    float numberOfChecks = Vector3.Distance(rayOrigin, rayTarget) * 2.5f;
    for (float i = 1; i < numberOfChecks; i++){
    Vector3 rayPosition = Vector3.Lerp(rayOrigin, rayTarget, i / numberOfChecks);
    Tile tile = battle.map.GetTile(new Vector2Int((int)(rayPosition.x + 0.5f), (int)(rayPosition.z + 0.5f)));
    if (tile.transform.position.y > rayPosition.y){
    print("WALL COLLISION");
    break;
    }
    if (tile.character != null){
    print("CHAR COLLISION");
    break;
    }
    }


    upload_2023-8-18_13-47-50.png
    upload_2023-8-18_13-48-1.png
    upload_2023-8-18_13-48-23.png

    Thank you all for the inputs!
     
  14. wideeyenow_unity

    wideeyenow_unity

    Joined:
    Oct 7, 2020
    Posts:
    728
    That was a question I wanted to ask, suppose that character was on a tile height of say 4, and the enemy was on a tile next to it, but at a height of say 0, should the character be able to hit one tile away at a lower height?
     
  15. Valasty

    Valasty

    Joined:
    Aug 22, 2013
    Posts:
    125
    It works fine on higher tiles, what I meant by "elevated tiles" is the second situation below where I have 2 tiles occupying the same 2D space (X and Z, but different Ys).
    The line starts and ends around the characters neck, so as long as one's neck can see the other's even on height differences, then it should hit just fine.

    upload_2023-8-18_14-48-44.png upload_2023-8-18_14-49-48.png

    First case seeeeeeeems to hit but I guess I may need to add more checkpoints for more accuracy.
    Second case clearly doesn't hit (but its currently hitting because I haven't handled elevated tiles yet).
     
  16. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,662
    ^ ^ ^ ^ This suggestion is superior... here's why.

    I mentioned making colliders and perhaps raycasting. My suggestion has these drawbacks:

    If you were to balance your entire game based on certain scenarios of partial blocking being X% chance of hitting, then just update the art, which changed the colliders and now your game is either unplayable or super-easy, that would be bad.

    What WideEyed mentioned, using AStar on a grid, or at least using the grid to store intermediate unchanging values of "can I shoot through this or can I move through this" is actually superior, because it lets you play-balance without fear of a future art change breaking your game balance.
     
  17. mindfield44

    mindfield44

    Joined:
    Aug 18, 2023
    Posts:
    1
    The game your making looks cool how are you making the art?
     
  18. Valasty

    Valasty

    Joined:
    Aug 22, 2013
    Posts:
    125
    Appreciate that man, but there's no art, it's all placeholders I randomly found on Google lol (combined with standard Unity cubes for the map).

    I can't see Pathfinding working on this situation. I do use it to get skills and movements ranges, but it would be VERY HARD to do it for projectiles (unless I'm missing something). Just imagine how would pathfinding work on the below situation...
    C = character
    E = enemy
    T = empty
    |E|T|
    |E|E|
    |T|C|

    C is shooting on the uppermost E, the shot needs to hit as it must go through both middle Es. This gets very hard to figure out with pathfinding. Using lerp I can calculate the distance between the line and the center of the tile to figure out how far from the middle it is (and if far enough means it goes in between).
     
  19. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,662
    Sorry, lazy cut/paste on my part... I meant more the part about putting info in the tiles.

    Looking at your C E T example above, a projectile from C to the top T might consider that it is passing through one E tile.

    Drawing again from XCom there was some benefits from being high and shooting down on someone (provided they could be seen) and a detriment shooting up at someone from a lower location.
     
  20. Valasty

    Valasty

    Joined:
    Aug 22, 2013
    Posts:
    125
    C should shoot on E, not T. T indeed collides :p
     
  21. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,662
    Remember collisions can mean many things;

    collide on E? well, it wasn't E... there was a blocker
    miss on E? pass through!

    miss on T? well, target was smaller than you think
    collide on T? well, you hit it

    etc.
     
  22. wideeyenow_unity

    wideeyenow_unity

    Joined:
    Oct 7, 2020
    Posts:
    728
    As I mention in this post, which was pathfinding on tiles with small walls:
    https://forum.unity.com/threads/pathfinding-on-grid-with-wall.1469033/#post-9193775
    And the post just underneath that where I answer myself..

    It can very easily handle your situation. Each tile just needs simple variables in it(height = 4, isForest, etc...) and when handling the path start code, and it's moving directly towards towards the target, it can see "isForest" then do this.

    Unless you mean you don't know how to put info into tiles, or contain class references in order to pathfind with them?
     
  23. Valasty

    Valasty

    Joined:
    Aug 22, 2013
    Posts:
    125
    I know how, I have pathfinding working on movement already, I just don't see how it can help with the functionality I'm looking for. Here's the final version of the example I gave above, it's working great:
    upload_2023-8-18_19-1-34.png

    I just don't see how pathfinding would get past the 2 tiles containing enemies without colliding.
     
  24. wideeyenow_unity

    wideeyenow_unity

    Joined:
    Oct 7, 2020
    Posts:
    728
    I was just referring to "static blockages" before, but in that type of scenario, you would have your Enemy.cs reference and a simple check of "isTarget"(or tile.currentCharacter == targetEnemy).

    However, I do foresee an issue when it comes to a bit of longer distance, and one tile might only slightly be in the way, as the line/direction would technically select it during pathfinding. And looking at it, you would think it shouldn't hit it.. But other than this scenario, I don't see any other complications.
     
    Last edited: Aug 18, 2023