Using canvas to highlight objects on screen

Hey everyone - since some people emailed asking where my old blog posts were, I rescued them from web archive and posting them here. Hope you find them useful!

Using canvas to highlight objects on screen (UE4 Blueprint)


This article is to explain how I used the canvas blueprint class in UE4 to render a screen mask, for the purposes of highlighting objects on screen in Garden Wars.

When I gave early versions of the game to people for testing, it was apparent that they didn’t know what they were meant to touch/click to use a particular power. So I thought it would be great if the screen could be darkened, except for the valid clickable objects, in any situation.

To do this I would use a “Canvas Render Target 2D” overlaid on the screen that defaulted to black. Onto this canvas, I would render “holes” for the objects I want to see.

So first, I created a child blueprint of the CanvasRenderTarget2D, and set it to 512x512 (enough resolution to show blurred dark areas without noticeable pixelation at any screen size). I also unticked “HDR” to try and reduce the memory used.

Canvas Render Target 2D Properties

After that, I set up a material that would sample this render target. This material gets applied to a quad that is placed in the world. The material is set to Translucent/Unlit, with “disable depth test” ticked, so that it draws in front of everything:

Simple material used in the world to act as a “screen overlay”

Note: the canvas render target gets created at startup, so once it is created I apply it to the material via Blueprint.

Now, you could set it up to render almost any object to the canvas if you had the time (you’d probably have to spawn copies of things off-screen and use a RenderToTexture actor to get the silhouette of the object), but the only things I ever wanted to highlight in my case were the hexagon tiles on-screen, which are always the same size and never move. So that simplified things a little.

I added a function in the canvas class to loop through all of the tiles in the world on startup and convert their world location to a renderable location on the canvas. This took a fair amount of fiddling and magic numbers… but in the end I had an array of canvas positions for each tile, pre-filled:

The functions in the canvas class, that calculate the positions & size of my tiles

So now all I had to do was keep a list of which tiles needed drawing on the canvas. I made an event that I could call, specifying the tile and whether it should be enabled or not. I also made an event to reset it all to black again:

These events in the canvas class define which tiles will be drawn

Lastly, I made the actual drawing logic. So whenever an update is requested on this canvas, it draws each enabled tile, using a set texture (a blurred hexagon for my case). The blend mode is set to modulated, so that overlapping draws didn’t look weird. Also note that I am only using the green channel of this render target - this is because the background of a canvas render target is full green by default, so it just made things easier if full green translated to fully black in the final material:

This event draws the blurred hexagon to the render target at the right spot

So that was the canvas done. From here I made a shared function called “Focus on Tiles” that took an array of tiles to focus on. This function would tell the canvas which tiles to render, and then update it only once for performance reasons (Note: I used events so that in multiplayer it would only change on the correct player’s machine):

This function tells the canvas what tiles it wants to highlight

Now the only thing left to do was to fade the darkening in and out nicely. To do this, I simply added an “Opacity” parameter to the material. The game logic then uses a Timeline node to smoothly fade the opacity parameter in and out when the darkening is needed!

Timeline node used to smoothly fade the mask in and out

There is a bit of detail left out of this article so if you have any specific questions about any part, please feel free to ask!