collinsmith / riiablo

Diablo II remade using Java and LibGDX
http://riiablo.com
Apache License 2.0
895 stars 101 forks source link

Make entity angles consistent #48

Closed collinsmith closed 5 years ago

collinsmith commented 5 years ago

Entity angles are currently configured as relative to pixel location. This is becoming an issue and I think it's about time to change this behavior to map the angle to the in-game map grid instead (0=east, pi=west, pi/2=north, -pi/2=south). I think this will simplify some of the common operations (e.g., look at an entity).

See Entity#lookAt(Entity) or Entity#lookAt(float,float) for just how convoluted the current implementation is -- i.e., every time you want to look at a position it needs to be converted to the pixel location and then mapped to the angle from there.

The primary reason why this change should be looked into is that Missile entities require a vector target and the method to calculate this correctly is crazier than the methods I mentioned above to do something that should be fairly simple. I will need to look at some of the other Missile travel functions as well to see some of the other patterns (e.g., blessed hammer travels in a spiral, is this relative to in-game map grid?).

In other words, I think the current implementation is really the corner case and the default implementation should be relative to map grid.

collinsmith commented 5 years ago

2a0ed7d40a0a7e0c26c937562640f080b8d5137e added an interactive tool to test new direction utility class which will work using map-coordinates. The tool needs to be adjusted to fit map plane since it currently uses pixel-based Cartesian coordinates using the center as the origin and mouse as offset (meaning the results are gibberish compared to the game unless you can keep track of the actual map-relative axis in your head). The new direction utils should be just about ready to merge pending a bit more testing, however I need to look over and see how pluggable this change will be since it contains significant changes to how all angles work in-game.

On desktop, this API should be easier to work with since you can select a specific tile to move to, so finding the vector is easy (and thus the direction is easy), however controller and android are a bit different since you're not so much selecting a tile as much as you're saying "walk in this direction". This section of the code needs to change anyways, since the current implementation is placeholder anyways (pathfinding is really sticky on android with the joystick). I'm going to research and see if I can find some algorithm that can intelligently select a tile -- and this is more of a pathfinding issue, but the algorithm should handle moving around walls/through doors smoothly. Lastly, this will require some changes to how the angle is selected via the joystick angle since these angles no longer map the same values -- I think this might be resolved by keeping the existing code in some form to convert the angles, but this might require some adjustments (interpret joystick as player-relative on isometric plane and grab that angle instead of joystick angle, and this could be how isometric games naturally handle this anyways).


I just wanted to note here also that com.riiablo.entity.Direction/DirectionUtils contains a ton of probably needless optimization just as an exercise for fun. I haven't actually benchmarked it to see if it has any tangible effect on the runtime performance. I did end up inlining the float arrays because there is no reason not to since they don't need to be generated at compile time, and I think the compiler might be able to perform optimizations if the values are constants known at compile time.


I did a benchmark just for fun using the below code and it is showing a performance benefit (~50ms vs 5ms -- extremely negligible). I understand it's not a perfect benchmark, but it's interesting to see any difference at all.

final int N = 10000000;
final float t = MathUtils.PI * 0.75f;
long start = TimeUtils.millis();
for (int i = 0; i < N; i++) {
  Arrays.binarySearch(DirectionUtils.RADIANS_32M, t);
}
System.out.println(TimeUtils.millis() - start + " ms");

start = TimeUtils.millis();
for (int i = 0; i < N; i++) {
  DirectionUtils.radiansToDirection(t, 32);
}
System.out.println(TimeUtils.millis() - start + " ms");
collinsmith commented 5 years ago

After some deliberation and a bit of research, I might port the optimizations to com.riiablo.entity.Direction and scrap the new implementation (or archive it). Resolving the correct angle is more difficult than I had hoped and may be unnecessary since the pixel location of the entities are required anyways, I'm going to implement an optimization where Entity#act(float) saves the pixel (x,y) as a GridPoint2 field within Entity (possibly Vector2 if I think the extra precision might help with interpolation). This is already used extensively and calculated multiple times per frame.

So the current plan is to handle all collision detection using map coordinates. Cursor over and direction will be handled through pixel coordinates. Entity#lookAt(float,float) and Entity#lookAt(Entity) will then become much more simple.

As a side note -- I need to look into the case where an entity looks at another entity before that entity has acted and moved, so the entity looks at the entities previous location. This is already happening with the current implementation. It's not noticeable with players since they update first, so monster entities look at their updated location, and I haven't noticed the problem, but it should be there in cases with a fast moving monster.

collinsmith commented 5 years ago

Resolved by 4947d8a8a9ca94b1edad31252475e6d8ade5d1f2.