Skip to content

Display Feature

Bogdan T edited this page Feb 5, 2024 · 17 revisions

Structure

Here you will learn how the display feature is organized

DisplayRenderer Overview

The DisplayRenderer serves as the primary component in the drawing hierarchy. It is responsible for managing the rendering process of a DisplayCanvas, which is attached to the renderer. The DisplayRenderer not only handles the canvas but also manages the associated DisplayData and a list of DisplayTrigger objects.

Moreover, the DisplayRenderer implements the InjectablePlaceholderList, enabling the injection of placeholders for use within the canvas.

The DisplayRenderer offers versatility in its rendering contexts. It can be rendered in various environments, including the BaseScreen, in the HUD, or through manual invocation. Further details on these rendering contexts will be provided subsequently.

DisplayCanvas

The DisplayCanvas class, a subclass of DisplayElement, maintains a list of DisplayElements. It controls how the elements are displayed.

DisplayElement

The DisplayElement is the foundation of all elements that are displayed on screen.

It contains the base variables and methods

DisplayManager

The DisplayManager registers all display element templates, trigger templates, and actions (details on actions to be discussed later). This class is also the central hub for managing the HUD canvas and all active canvases linked to renderers. Future updates may transition from managing active canvases to focusing on renderers.

Element and Trigger Templates

Templates for elements and triggers are essentially pre-defined implementations without any data or the canvases, which are loaded from config objects with default data. To utilize these templates, you need first obtain a clone from the DisplayManager, then apply data from a configuration file using the updateVariables(Config conf) method. This process readies the element for use.

Resolution and Display Features

An important aspect of the display feature is its independence from Minecraft's scaleFactor and texture size limitations. This feature is designed to circumvent these restrictions. However, to manage resolution changes, the number of available resolutions (or window sizes) is restricted. These resolutions can be set in the game settings. Additionally, the system allows for the optimization of display element variables for specific resolutions, ensuring adaptability across different screen sizes.

More in-depth information on resolution handling will be provided in later sections.

First test

Now, after understanding the structure, you can practice a little bit and make the simpliest gui screen.

  • First, create the test.json in your resources(on the top, so, not in assets or any other folder)

And copy the json from here

Then, in your main class create the config object attached to the created file. If you dont know how, read the WIKI section about configs.

Once you've got a config object, you can compile and register canvas template:

//It basically clones and updates variables of the template elements that are specified in config
//This object can be used as a template with a default data.
DisplayElement element = getDisplayManager().getElementRegistry().compileCanvasTemplate(id,config); // for an id u can use test, or whatever you like
//Register to later access the template
getDisplayManager().getElementRegistry().registerTemplate(id, element);

Nice! Last step left. You need to draw it somewhere. The most simple way is to draw it on GuiScreen.

Lets override the MainMenu screen of the Minecraft:

@SubscribeEvent
public void onGuiOpen(GuiOpenEvent event){
    if(event.getGui() instanceof GuiMainMenu){
        event.setGui(
                new BaseScreen(this,
                        getDisplayManager().getElementRegistry().getDrawableCanvas("yourCanvasId") //returns the cloned canvas template, ready to use
                );
        );
    }
}

Don't forget to register the class where u put it in Forge EVENT_BUS:

MinecraftForge.EVENT_BUS.register(this)

Its ready to be tested! You should see the cute cat:)

Now, I highly suggest you to play around the settings.

Tip: Once you made changes to config you can press Shift+S to reload the currently active DisplayRenderer.

If it doesnt work, change the debug state of the AtumMod to true

The List of default elements and settings

Here, you can find the list of the settings you can use. But before we move to that, phew things require clarification.

  • There are the Base settings and Element settings. The difference between them is in what config section they can be setted up.

  • The Base settings are setuped in root section of an element. The Element settings are setuped in a "settings" subsection

  • Besides looking for settings options here, alternatively you can find them by looking at updateElementVariable() and updateBaseVariables() methods

  • Most of the settings support the placeholders.

Base settings

  • "template": "template-id-to-use"
  • "layer": "the-layer-to-use" //More info here
  • "posX": 1
  • "posY": 1
  • "width": 100
  • "height": 100
  • "fixRatio": true/false //Affects on different screen sizes //optional, default: false
  • "outline": {"color": "RGB", "size": 3 } //draws an outline around the element //optional
  • FOR CANVAS: "elements": { }

Element Image

  • "image": "write-path-to-resource"
  • "color": "RGB" //optional
  • "textureX": 0 OR the position in texture u want to draw //optional
  • "textureY": 0 OR the position in texture u want to draw //optional
  • "textureWidth": 0 OR the width of the part of texture u want to draw //optional
  • "textureHeight": 0 OR the height of the part of texture u want to draw //optional

Element Fill

  • 'color': "RGB"
  • 'opacity': "value from 1.0 to 0"

Element Text

  • "font": "font path" //to add supported fonts you need to add them to ur resources with the same path as here here, I'll later update this to allow more options to choose from
  • "fontSize": integer value, default is 25
  • "text": "your_text_here" //if you want to add colors, use minecraft color codes. Unfortunately, rgb colors not yet implemented here

Element Button

  • "image": "write-path-to-resource"
  • "brightness": 1.0;1.0;1.0 - more number, brighter it will be //kinda weird i know, it literally the color, from openGL, should be renamed
  • "brightness-onHover": 0.8.0.8,0.8
  • "brightness-onClick": 0.8.0.8,0.8
  • "textureX": 0 OR the position in texture u want to draw //optional
  • "textureY": 0 OR the position in texture u want to draw //optional
  • "textureWidth": 0 OR the width of the part of texture u want to draw //optional
  • "textureHeight": 0 OR the height of the part of texture u want to draw //optional
  • "actions-onPress"{ListOfActions}
  • "actions-onRelease"{ListOfActions}

Example for actions: "actions-onRelease": { "1": "open_link@https://google.com" }

Element Progress Bar

  • "color-filled": "RGB"
  • "color-empty": "RGB"
  • "progress-expression": "your expression" //Supports placeholders

You can find all the elements with the description here

Digging Deeper

Here we will cover the advanced features you may use.

Display Actions

The DisplayAction is integral to the display feature. It is primarily utilized within triggers and specialized elements like buttons to execute actions that influence elements, the client, or DisplayData.

In most cases, you will be able to specify the list of actions:

{

    "action-id1": "open_link@https://google.com",
    "action-id2"....
    ....
}

So, the string you need to write has to be with the following pattern: "action_id@arguments" OR if action do not require args, then just "action_id"

You can test it out by creating the button element and specifying "actions-onPress" variable

The list of default actions can be found here

Display Triggers

The DisplayTrigger functions as a listener for specific events or actions initiated by the player. Upon detecting a specified event, it executes a predefined list of DisplayActions

It supports filters, which define what data has to be in a trigger to activate. These filters allow for precise control over when a DisplayTrigger should respond to an event and execute its associated actions.

Example:

{
"template": "canvas",

"triggers": {

    "notifySound": {
      "template": "data_changed",
      "filters": {
        "data_id": "notification",
        "change_type": "SET_TEMPORARY"
      },
      "actions": {
        "1": "open_link@https://google.com"  //if filters match the data received on trigger performs this action
      }
    }

}

}

It has to be added in a base settings of a canvas attached to a renderer.

Right now there is only data_changed trigger available by default. I plan to add more in next updates.

The list of available triggers can be found here

Display Data

The DisplayData is responsible for dynamically changing data that can be used in triggers and display elements. Also, the data can be used in config setup. For that, you need to write the following placeholder: "%data_{data_id}%".

The ways to set the data

  • Basic data - it is the data which u just set and it stays untill changed/removed

  • Temporary data - data with a lifetime. It can be also queued

  • Default data - it is used together with temporary, to set the data back to a default once temporary value has expired

In config:

{
  "template": "canvas",
  "default_data": {
    "your_data_id": "anything"  //on canvas activated, will set this data value
  }

}
  • State of display element (enabled/disabled) - the same as basic data, but it affects on the state of the elements. Basically, it is the following data pattern: "element_enabled${element_id}"

You can change the data via the actions:

  • 'set_data'
  • 'set_multiple_data'
  • 'remove_data'
  • 'remove_multiple_data'
  • 'clear_data'

More info

Resolution Optimizer

It is a great tool to use if you want to optimize your canvas for different window sizes. Its usage is pretty simple:

{
  "template": "canvas",
  "elements": {
    "progressBar_health": {
      "settings": {
        "color-filled": "#880808",
        "color-empty": "#4c4c4c",
        "progress-expression": "(%player_health%/%player_health_max%) * 100"

      },
      "outline":{
        "color": "#000000",
        "size": 1
      },
      "fixRatio": true,
      "template": "progress_bar",
      "layer": "MIDDLE",
      "posX": 1620,
      "posY": 35,
      "width": 270,
      "height": 19

   }

  },
  "width": 1920,
  "height": 1080,
  "resolution_optimization": {
    "RES_1024x728": {
      "elements": {
        "progressBar_health": {
          "posX": 1520,
          "posY": 45,
          "width": 370,
          "height": 27
        }
       }
      }
    "RES_1280x720": {
      "elements": {
        "progressBar_health": {
          "posX": 1020,
          "posY": 45,
          "width": 370,
          "height": 27
        }
      }
    }
       
  }
}

The list of supported resolutions here.

So, you can change the position and sizes of elements, additionally, some element types support optimization of their variables. The ElementText is one of them. You can change its font type and size

How to work with API

Here I will only explain the behaviour of major methods and what you may use for a convenience.

I highly suggest you to check the default implementations as good examples.

Both Display Elements and Display Triggers are automatically added to the EVENT_BUS of a forge.

Create elements

Once you created the class, you need to make it extend the BaseElement and add an annotation @RegisterDisplayElement with 'templateId' specified.

The annotation says the DisplayManager to register this class as an element. It will be done during the FMLPreInitialization event.

About cloning and updating variables:

If you have mutable objects in your class, then make a copy of them in onClone() method, to not to have issues with the same objects used in multiple element instances.

For updateElementVariables you can obtain the data from Config, which is a 'settings' subsection in configuration.

Drawing

for coordinates and width, heght use the methods getX(), getY(), getWidth(), getHeight(). They are calculated each tick to ignore an annoying minecraft scaleFactor and match the resolution optimization.

If you want to get original coordinates, which has been set in settings, use: getOriginX(), getOriginY() etc.

You can use RenderUtils to draw basic shapes with OpenGL

Resolution optimization

If you want to make your variables optimizable for specific resolution in config, check this. Choose the class you need in 'variables' folder to match the type of your variable. Then, place it instead of the current variable type and add an annotation: @RegisterOptimizedVariable

Thats it! You dont need to do anything more, the cloning and updating of variable will be done automatically

Create actions

Pretty similar to the way you work with elements, but only one method to implement. Dont forget to add an annotation: @RegisterDisplayAction

Create triggers

annotation: @RegisterDisplayTrigger class to use as a parent: BaseTrigger

Cloning and updating variables is the same as for the elements

For the filters, make sure to check the length of the arguments you've got

Clone this wiki locally