There are generally two ways of creating minimaps. One way is to let an artist draw a representation of the map, as shown in the following screenshot. This usually ends up beautifully as it gives considerable freedom to the artist when it comes to style. The method is not that viable during development when scenes might be changing a lot, or for games with procedural content where the end result is not known beforehand.
In those cases, taking a snapshot of the actual scene can be very helpful. The resulting image can then be run through various filters (or shaders during rendering) to get a less raw look.
In this recipe, we'll achieve this by creating a new ViewPort
port, and FrameBuffer
to store a snapshot of a camera. Finally, we'll create NiftyImage
out of it and display it as a GUI element.
We're going to start by creating a Util
class to handle the rendering of our minimap. This will consist of the following 15 steps:
MinimapUtil
.createMiniMap
, with the following declaration:public static void createMiniMap(final SimpleApplication app, final Spatial scene, int width, int height)
offScreenCamera
with the same width and height that were supplied to the method.1
and 1000
in depth, -width
to width
, and -height
to height
, as shown in the following code:offScreenCamera.setParallelProjection(true); offScreenCamera.setFrustum(1, 1000, -width, width, height, -height);
offScreenCamera.setLocation(new Vector3f(0, 100f, 0)); offScreenCamera.setRotation(new Quaternion().fromAngles(new float[]{FastMath.HALF_PI,FastMath.PI,0}));
ViewPort
by calling the application's RenderManager
and its createPreView
method using offScreenCamera
:final ViewPort offScreenView = app.getRenderManager().createPreView(scene.getName() + "_View", offScreenCamera); offScreenView.setClearFlags(true, true, true); offScreenView.setBackgroundColor(ColorRGBA.DarkGray.mult(ColorRGBA.Blue).mult(0.3f));
Texture2D
class to store the data in, so we create a class called offScreenTexture
with the same width and height as before and set MinFilter
to Trilinear
:final Texture2D offScreenTexture = new Texture2D(width, height, Image.Format.RGB8); offScreenTexture.setMinFilter(Texture.MinFilter.Trilinear);
FrameBuffer
class is needed as a medium for the data, so we create one with the same width and height, and 1
sample, as shown in the following code:FrameBuffer offScreenBuffer = new FrameBuffer(width, height, 1);
DepthBuffer
to be Image.Format.Depth
and offScreenTexture
to be ColorTexture
:offScreenBuffer.setDepthBuffer(Image.Format.Depth); offScreenBuffer.setColorTexture(offScreenTexture);
outPutFrameBuffer
of offScreenView
to be offScreenBuffer
:offScreenView.setOutputFrameBuffer(offScreenBuffer);
Light
class to it.offScreenView
:offScreenView.attachScene(scene);
AssetManager
with the following line:((DesktopAssetManager)app.getAssetManager()).addToCache( new TextureKey(scene.getName()+"_mini.png", true), offScreenTexture);
renderManager
and renderViewPort
methods:app.getRenderManager().renderViewPort(offScreenView, 0);
removePreview
to discard offScreeenView
:app.getRenderManager().removePreView(offScreenView);
With the Util
class done, we can create a screen Controller
class. Perform the following additional six steps to do this:
GameScreenController
that extends NiftyController
.createMinimap
that takes a scene as the input.createMinimap
method should do is call MiniMapUtil.createMinimap
.NiftyImage
with the nifty.createImage
method.screen.findElementByName("minimap").getRenderer(ImageRenderer.class).setImage(image);
minimap
to a screen that uses GameScreenController
as the controller.Offscreen rendering is just what it sounds like. We render something in a view that is not related to the main view that the player sees. To do this, we set up a new viewport and camera. It's not possible to render something directly to a texture, which is why FrameBuffer
is used as the medium.
Once the texture object is created and added to the asset manager, it's possible to keep changing it if we would like to at a later stage. It's even possible to have a live view of the scene in the minimap, although this would probably cost unnecessary resources. In this case, we remove the view as soon as we've rendered it once.
The example is limited in some sense, like it expects that there is a correlation between the size of the scene and the size of the minimap.
Nifty uses its own image format, NiftyImage
, so we need to convert the image we saved; however, Nifty's createImage
will automatically find the texture in the asset manager based on the name (key).
Usually, on a minimap, players will want some kind of indication about their (and others) whereabouts. Let's implement that in the minimap we just created:
minimap
element in our screen a bit. We set childLayout
to absolute
and add another panel inside it called playerIcon
with a small width and height.Element
field called playerIcon
to the GameScreenController
and use findElementByName
in the bind
method to set it.updatePlayerPosition
with two integers, x
and y
, as the input.setConstraintX
and setConstraintY
on the playerIcon
element to set the position. Those methods take SizeValue
as the input, and we supply the x
and y
values with the "px"
definition.layoutElements()
on the minimap
element to make it update its child elements.For other things, such as visible enemies, we can use the builder interface to create them as and when we need them and then use markForRemoval
to remove them when they're not needed anymore. An example of this process can be seen in the Handling a game message queue recipe.
18.191.176.194