The Querying the world recipe is a good proof of the potential of raycasting within a physics game environment. However, sometimes you might perform too many simultaneous queries within the same game loop iteration, so the CPU is not able to process them fast enough and makes a small freeze visible on the screen.
A solution would be to implement a queue of raycast queries, ordered by priority, with the particularity of having a limited time per frame to dispatch as much as it can, keeping the rest for future iterations. The higher the priority, the sooner it will be executed.
In order to keep things tidy, the code will be split into two files:
RayCastManager.java
to implement the deferred raycasterBox2DDefereedRaycasterSample.java
shows a practical usage case of the aforementioned classIf you do not feel comfortable with world queries, I strongly suggest you to go back and review the Querying the world recipe.
This recipe will not have too much visual interaction but a lot of log data so all the useful information will be printed in the console.
As the definition of the RayCastManager
class is written in the introduction to this recipe, we will just go to the class creation process:
private class RayCastRequest { final public int priority; final public Vector2 point1; final public Vector2 point2; final public RayCastCallback callback; public RayCastRequest(int priority, Vector2 point1, Vector2 point2, RayCastCallback callback) { this.priority = priority; this.point1 = point1; this.point2 = point2; this.callback = callback; } }
Field names should be self-explanatory for you. They are declared as final
to make those references immutable.
private final static float SECONDS_TO_NANO = 1000000000f; private float budgetTime; private World world; private PriorityQueue<RayCastRequest> requestQueue;
budgetTime
: This is the limit time (in seconds) for processing requests in each update tickSECONDS_TO_NANO
: This is a constant to perform conversionsrequestQueue
: This will store all the pending queriesWorld
and budgetTime
. The requestQueue
will be internally created with the help of a Comparator
class instance to order requests by priority:public RayCastManager(World world, float budgetTime) {
this.world = world;
this.budgetTime = budgetTime;
this.requestQueue = new PriorityQueue<RayCastRequest>(1, new Comparator<RayCastRequest>() {
public int compare(RayCastRequest r1, RayCastRequest r2) {
return r2.priority – r1.priority;
}
});
}
Please realize that the subtract order of elements in the compare
method is reversed to make the highest priority element the head of the queue.
public boolean addRequest(int priority, Vector2 point1, Vector2 point2, RayCastCallback callback) { return requestQueue.add(new RayCastRequest(priority, new Vector2(point1), new Vector2(point2), callback)); }
RayCastManager
must have a mechanism to start processing as many queries as budgetTime
allows:public void update() { long now = TimeUtils.nanoTime(); RayCastRequest rr = requestQueue.poll(); while(rr != null && TimeUtils.timeSinceNanos(now) < budgetTime * SECONDS_TO_NANO) { world.rayCast(rr.callback, rr.point1, rr.point2); rr = requestQueue.poll(); } }
This function basically gets the current value of the system time in nanoseconds and extracts the head value from the queue. Next, a loop starts ensuring that there are still some requests left to process and that budgetTime
is not spent. Then, the query is made and a new iteration starts with the next head value.
As you would have seen, the usage of RayCastManager
is very simple:
RayCastManager raycastManager;
RayCastManager
a maximum of 0.1 seconds per tick to process requests:raycastManager = new RayCastManager(world, 0.1f);
raycastManager.addRequest(priority, point1, point2, callback); …
render
function:raycastManager.update();
Too many words and code might move you away from the simplicity of this recipe. A good diagram will help:
3.128.205.21