• Register
Post news Report RSS Netherguild and the Imprecise Hovering

-Or, "how I fixed hovering over tiles without using colliders." A technical tale from my last update about stubbornness and creative solutions!

Posted by on

Netherguild and the Imprecise Hovering


-Or, "how I fixed hovering over tiles without using colliders." A technical tale from my last update about stubbornness and creative solutions!


Preface


Characters sitting around a bonfire


Netherguild is the turn based strategy game I've been solo developing for the last year and a half. As you can see, it usually sports an isometric camera-

Voxel knight standing on a hill

If you look at the cave floor, you'll notice it's "3D"- it has small, perlin-noise based hills and ditches-
(I talk more about the inspiration behind it in this article: Indiedb.com)

And, since the beginning of my development of Netherguild, I vowed to never use Unity's physics system-be it colliders, rigidbodys, everything- Why? I thought it'd be a fun challenge to try and abstract everything into tiles and only simulate physics using animations or particle effects, also I figured not using this mostly-unnecessary feature will improve performance and reduce the final file size (due to Unity not adding un-included features into the build. I'll be frank here and say I don't know if it would also do that with physics, but I hope so).

All of that culminated into one big problem when it comes to raycasting and letting players precisely select the tile they wish to select-


Problem



Imprecise hovering


Incosistant initial hover


With an absence of colliders, I defaulted into raycasting on a plane. And while that is a great solution in the perfect, clean world of 2D- which the early prototype did use- it really didn't fit the rugged, rough world of 3D tiles.

Isometric Tiles As Plane

Some tiles are poking above the plane and some are below it-

As you can imagine, pretending the floor is uniform when it really isn't is bound to make you fall, and it did make navigation uncomfortable, imprecise and downright bad as soon as you were navigating a more steep area of Netherguild's randomly generated mine level (with bigger differences between tile heights).

Tiles that *should* have obstructed other tiles weren't getting picked up correctly- this method was just imprecise with 3D, and made hovering feel unresponsive.
And, during this update, when @Hallgrim pointed out to me that hovering was inaccurate (in addition to other people before him), I had to face my fears and solve this problem.
But, how?


I mean, I can cast a ray, but how can I know where it should stop? How do I know which length it should be? Will I.... have to use colliders and physics, systems that are just overkill and unnecessary for my game?!?

Dramatic Cat


Solution


My first direction of attack was considering "how can I make a more abstract version of colliders?"- I used this approach for checking mouse-hovering on buildings in the overworld for my game, pretty much just checking if the mouse is within their "fake rects"-

FakeRects


But, am I going to create a big tile manager script that checks if a tile is hiding another tile? how the hell would that even work and be as precise as it should be?

No... There must be a better way!

...

And then, I figured out a new approach-

In Netherguild, what decides a tile's height is a "Heightmap"- currently, it's just perlin noise with an added dictionary for exceptions, like the elevator. Somewhere along the generation process, once a tile is spawned/instantiated it's assigned a tile and height according to this class.

perlinNoise

So... why not cast a ray from the camera in the direction of the mouse on the screen, and proceed along that ray until we're below the tile? As soon as we're below a tile, that's probably the tile we're hovering over!

InitialSketch


But uhh... if I want this to be precise, this is going to be a lot of segments and checks. For example, if I check for every 0.1 units of the tile, in an average distance of 10 it'd usually mean a hundred checks. Per frame. And what if in future maps/levels there'd be even more distance between the camera and the floor? am I going to check 500 times per frame? Hell naw-

There must be a better way!

Enter... math.

Instead of checking for 100 times per frame, which just feels wrong (even if it'll work with modern CPUs) why not get as much precision if not more with 11 checks?
And it works a bit like this:

SketchBetter

I start with a certain length of the ray (10 units). Every time I check along the ray, if the point is above the tile, move the ray forward. If it's below the tile, move the ray upwards. Then, I divide the check length by two (5 units, 2.5 units, 1.25 units).

Doing this ends up giving me a lot of precision when it comes to the exact length of the ray.

WorkingHover


Perfect! Except...

CrazyTiles

Oh no. When I hover "between tiles", at times the hover would flip out and quickly toggle between two tiles. If earlier the it felt unresponsive, now it feels hyper-responsive, and not in a good way.

Solution 2.0


Luckily, this is an easy fix. And! We get to abstract tiles, in a way.

SketchFinal

All I ended up doing, is taking the final "mouse position", and checking for how far away from the center of the tile it is. All there is to it is rounding the X and Y coordinates and subtracting to check the vector magnitude. If it's too far away from the center, the imaginary "collider" isn't hit.

FinalHoverGif

There we go! Works like a charm.


If you enjoyed this weird technical article, why not join Netherguild's discord server and say hello?

Thanks for your time and hope you have a great day (:

Comments
Guest
Guest

How do you check if a given point is below or above a tile? If you know the tiles position, then surely, you can get the distance and raycast exactly on the first try?

Reply Good karma Bad karma0 votes
DavidCodeAndArt Author
DavidCodeAndArt

But! I don't know the tiles position. My only tool for figuring out the position of the currently hovered-over tile is a ray I can cast out of my camera in the mouse direction. That ray is going to travel above some tiles and below some tiles, if I were to check each point along the ray infinitely.

So for each point I check on the ray, I compare the points "height" with the height of the tile it's currently either above or below (a.k.a the rounded points "height" on the perlin noise map)

Does that make sense? By the way, thank you for your interest Guest (:

Reply Good karma+1 vote
Guest
Guest

There are much easier ways to do this... You could have googled like 4 of them.

Reply Good karma Bad karma0 votes
DavidCodeAndArt Author
DavidCodeAndArt

Hey, if you find any other ways to do this without colliders/physics I'll be more than happy to check them out :D

Reply Good karma+1 vote
Guest
Guest

Unless I am missing something, you could algebraically solve it with Ray-Plane intersection and a bounding box check.

The centre position of each tile is (x,y,z) with y being the Perlin noise height. If the tiles are laid out in the x-z plane then (0,1,0) is the normal vector for all the tile planes.
The Ray-Plane intersection equation (easily found online) gives you the intersection point on the tile plane. The intersection point needs to be checked against the tile's bounding box as a plane stretches infinitely. If multiple tiles can contain the ray the one with the largest height must be the one that is hit first.

hope that helps. :)

Reply Good karma Bad karma0 votes
DavidCodeAndArt Author
DavidCodeAndArt

Great solution! :D

I was thinking about using planes before I came up with this solution, but I was worried it would mean I'd have to check against every tile's plane, or that somehow rotating the camera would ruin it (which it won't). Thinking back on it now, I could've reduced the number of tiles I check bounds with by using the ray's position.

While I'm going to stick with my solution for now, I still really appreciate the insightful comment! (:

Reply Good karma+1 vote
Post a comment
Sign in or join with:

Only registered members can share their thoughts. So come on! Join the community today (totally free - or sign in with your social account on the right) and join in the conversation.

News
Related Games
Netherguild
Netherguild Turn Based Strategy
Related Engines
Unity
Unity Commercial
Related Groups
Linux Gamers
Linux Gamers Fans & Clans with 2,970 members
PC Gamers
PC Gamers Hardware & Tech with 743 members
Unity3d Discussion
Unity3d Discussion Educational with 25 members