Can I detect a projectile collision without using colliders?

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:

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?

You should have zero concern about using raycasts and colliders. Your game looks very simple CPU wise.

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.

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.

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

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:

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 :smile:

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: Unity - Scripting API: Bounds.IntersectRay

A simple way would be to ignore layers with other layers:
https://docs.unity3d.com/ScriptReference/Physics.IgnoreLayerCollision.html

Ohh, unless you mean you don’t want it going through trees, never mind*

The OP doesn’t want to author physics components.

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.”

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! :smile:

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 :smile:

1 Like

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.

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;
}
}



Thank you all for the inputs!

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?

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.

9232944--1289871--upload_2023-8-18_14-48-44.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).

^ ^ ^ ^ 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.

The game your making looks cool how are you making the art?

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).

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.

C should shoot on E, not T. T indeed collides :stuck_out_tongue: