Over the last few days we've been working on solidifying the weapon select experience, and in the meantime we ran into an unexpected bug that led me (@barryrowe) down a bit of a refactoring rabbit hole.
The bug had to do with converting touch events to world units properly. This is a common issue as its often better to work in "World Units" instead of pixels so that you can better manage units relative to your game world rather than whatever device might happen to be running the game. Even better, being a common practice, most engines and libraries provide a quick way to do this via your camera object(s). LibGDX is no exception and the Camera API provides both project() and unproject() methods
//Based on working in a Screen of 640x960 pixels //Our desired width and Height in world units private static final float WORLD_WIDTH = 20f; private static final float WORDL_HEIGHT = 30f; private Vector3 screenPoint = new Vector3(320f, 480f, 0f); private Vector3 worldPoint = new Vector3(10f, 15f, 0f); Camera cam = new OrthographicCamera(WORLD_WIDTH, WORLD_HEIGHT); //World to Screen
cam.project(worldPoint); //worldPoint is updated to have values [320f, 480f, 0f] //Screen to World cam.unproject(screenPoint); //screenPoint is updated to have values [10f, 15f, 0f]
This is pretty straight forward, and so we were passing in the camera to our System(s) that needed to handle user input, and we just use camera.unproject() to get the world units, and from there find what was touched. Right? Well, we made the mistake of thinking this was all we would ever need to do, and didn't account for the viewport (Read up on libgdx viewports if you're interested). It turns out that if the viewport comes into play for resizing (we are using a FitViewport currently), the camera doesn't necessarily know about this adjustment when calling unproject(). Instead, we really needed to have access to the viewport object itself, and use Viewport.unproject(), otherwise we would end up with world units that don't match up to the adjusted viewport and we found that on odd device screens tapping to select a weapon was just completely broken.
The bigger problem this uncovered, was that we had written the kitten2d RenderingSystem to create the camera for the game. Looking back, this shouldn't have been the job of the rendering system. The RenderingSystem definitely needs a reference to the camera, but it shouldn't be responsible for creating it and exposing said camera to other systems/events that need it. Now in the future, it's probably the right call to make cameras an Entity with a CameraComponent, but for now we just moved this responsibility up to the Game implementation itself. We then make the Game implement IGameProcessor which can then expose the camera and viewport objects. We're free to pass the IGameProcessor reference to our Screens (which can provide the camera, viewport, gameProcessor, whatever is needed, on down to the systems that screen adds to its Ashley Engine.
You can see the bulk of this refactor in this commit (b4fbfb6a5). Feel free to laugh at how many cameras were being generated for some reason...it's kind of embarrassing.
Anyway, now that we knocked out this big adjustment, we're ready to start knocking out the final sets of features before the #kentuckyfriedpixels deadline.
Hopefully we finish strong and produce something at least a few people find fun.