Creating a terminal system in UE4
Our team consists of two members:
My name is Sertac, I am involved in gameplay programming. I have been developing projects on the Unreal Engine for about 3.5 years. Although my post is called a “gameplay programmer,” I also like programming UIs. I have worked and continue to work on various projects published on Steam.
I also organize Unreal Engine meetings in Turkey. Over the past year, I have organized many events and continue to organize them. Along with these events, I am thinking of organizing workshops on game development in collaboration with universities and high schools. In Turkey, there are many people who want to engage in game development and I like to share my experience, because when you share information, it becomes more!
In addition, I am part of the Unreal Engine development community version 4.18. Of course, even a small part in the development of this amazing engine is a great pleasure.
My name is Kemal, I'm a 3D artist. For almost 10 years I have worked in the film and game industry. I usually do modeling and lighting. For the past four years, I have focused more on lighting. I have been using Unreal Engine for 3.5 years. During this time, I managed to work on various game projects and many short and full-length films. I am trying to combine the things that I learned in cinema and those discovered during the development of games. I share all this in the format of the course on my pages on Artstation and Youtube , helping other developers if possible.
I also try to answer questions asked during participation in seminars and webinars, as well as at events organized by universities.
about the project
In this article we will try to explain how the systems of terminals and puzzles work, which are the basic mechanics of the game TARTARUS. We believe that the article will be useful for other developers as a demonstration of what Unreal Engine 4 and the blueprint system are capable of. We will try to tell as much as possible about the problems that we had and about their solutions. But before moving on to terminal systems and puzzles, we would like to briefly talk about the project.
At its core, TARTARUS is a text-based first-person game. Your task is to save yourself and the ship with failed systems from falling onto the planet Neptune. The player uses the “terminal”, as well as mechanical tools, which we will discuss below. The game was released on November 22.
We designed and implemented a terminal system based on our own needs. But in essence it is applicable to any projects. Below we describe the process of its creation.
First of all, I want to talk about the logical execution of the process. If to explain in simple words, then it is formed on the basis of receiving and processing the player’s input. Especially for this, we created Widget Blueprint . The most important part here is that we had to make some changes in order to get player input. Otherwise, the OnKeyDown function would not be able to intercept keyboard keystrokes.
After making all the necessary changes and passing the focus to the interface, we can intercept the player’s input. We will use the following scheme:
The most important part here is getting the names of the keys pressed by the user using the Key Get Display Name function . Further actions are performed in subsequent steps of the process.
Now we can receive the player’s input and record the keys pressed by him. It is very important to break the task into parts in this way. We successfully solved the first part of the problem.
At further stages, we will process the input received and create a decision-making mechanism based on it.
To process input, we first need to integrate it. We pass each keystroke to a String variable created specifically for this .
We have integrated all input received from users. But at this stage we have a small problem. The Key Get Display Name function returns values of the form Space, Comma, Period, Hyphen , since it returns the names of the keys. Therefore, we need to subject these keys to the processing process and convert them to the values we need.
To do this, we created a function that, instead of the names of the pressed keys, returns the corresponding characters. Later we integrated it into a previously created scheme.
The next step will be the processing of the received teams based on the standards created by us and their impact on the gameplay. But since the standards will be different from the system we want to create, I will briefly describe the standards that we used for Tartarus.
In the terminals developed by us for Tartarus, there are several commands and their parameters set in the system. We recognize them and perform the necessary process based on the returned commands and parameters.
We execute these processes at the moment when users press the Enter key , that is, at the moment of confirmation of the entered command. We share user-confirmed teams based on our standards.
Some commands can be used without parameters. We determine this based on whether a space was entered in the entered command. Later we created a decision-making mechanism based on two situations.
As I said at the beginning of the article, the system described above can vary greatly depending on which system you want to create. Therefore, you will need to set your own standards.
In the next step, we will design the interface and make the system work. But first, I want to explain the logic of the system.
It is created in the form of two Widget Blueprint, which include the system interface and command lines. The first widget is the MainWidget , which contains background processes and design. The second widget is an ItemWidget, in which we wrote down the commands described above. Command lines, that is, ItemWidget , are located in the ScrollBox located in the MainWidget , and when you press Enter, a new version is added.
After the completion of the design phase, we need to process commands after you press Enter, and then add ScrollBox new ItemWidget .
In terms of logic, the system works this way. After this step, we will display our two-dimensional interface screen on the model in the scene.
We wanted the system we developed to be able to change in accordance with our requirements. Thanks to this, we would not have to invent every terminal anew, the creative process would become more convenient, and there would be much less problems. To begin with, we need a special “material”, and at the same time we need to choose it so that it matches our universe in the Lo-fi style.
The most important aspect for us was the sci-fi style. We wanted to create CRT screens to make terminal displays look bright, vibrant and nostalgic. We will explain how we managed to create them in several stages.
Similar to the “standard” material shown above, we used for all terminals. We wanted it to be as simple and straightforward as possible. Let's look at it in a little more detail.
First, we need to determine the “visibility” limit of our terminals. The “capture camera” located in the scene should constantly look at the surface and display the user input received from various points of the system hierarchy, while ensuring minimum delays and maximum speed. To achieve this, we tied it to the “Texture Target” section, which is one of the “camera” sections, creating the “Canvas Render Target”. Then we created the “Terminal” materials using this texture.
Thanks to this rather simple but effective method, we were able to answer many questions, for example, whether borders and restrictions of symbols should be visible on the screen. Later we completed the materials by adding parts.
Scratches on glass screens are implemented using the Roughness Map, as in the example below (image taken from Google).
Another important parameter is the maximization necessary to create the Scanline effect, which provides the illusion of scan lines. Thanks to this texture, we got a more pleasant image that allowed us to get rid of the monotony of the screen. It seems that we were quite able to convey a retro mood.
Drafts of the design can be prepared in any 2D editor. The area of brighter shades in the upper part is the place that creates the effect of scan lines that we invented. A little later we will consider the principle of its work.
As you can see, we use three main textures. Let's look at how and why we manage these materials. Of particular importance to us was controlling the screen size, especially for the Roughness and Scanline textures. They should not be either too large or too small. We need to solve the problem of simulating the “Ghosting” effect, which occurs when many lines are concentrated, for example, texts disappear with a delay with it. In addition, we do not want to touch the texture combined with the Roughness value. Therefore, we went along the path of dividing the task into two parts.
The part that is shown in the upper left corner is the part in which we control the size of the scratches on the terminal screens. We repeat the texture using the Multiply node, the Texture Coordinate node and the Scalar Parameter, thus achieving the desired effect. In the upper right corners, we repeat the Scanline texture and increase the number of lines using the same nodes. Using the Panner node, we adjust their frequency and position. Another important point is that the terminal screens are made of glass. But instead of the translucent properties or masks of materials that create the classic glass permeability effect, we wanted to use Opaque and provide brightness using the Roughness value. The diagram is shown below.
Finally, we tried to achieve the desired screen brightness using the Emissive value, again using ScalarParameter. The result obtained is somewhat reminiscent of the example that is shown in the Material Editor. If you look closely, you will notice scratches.
Finally, we made Material Instance from the created material so that when making changes we did not have to go back and we could work faster and more efficiently.
Above are the Material Instance parameters and the mesh to which it is applied. Below it is shown in a more understandable form.
One of the terminals used in the game and the use of materials.
Since all terminal screens in the game have a standard size, we did not have to make any changes or modifications in the future. Therefore, after preparing the materials, it remains for us to indicate which screens we see in which terminals, preserve the hierarchy of folders and add a puzzle interface to the screen, developing it and maintaining the same effect for certain events.
There were a lot of problems with developing the puzzle interface. It had to correspond to the narrative of the game, each time reminding us that we were on a spaceship, and at the same time look beautiful. This is not to say that all the steps we took were necessary. Below I will talk about them in more detail.
1) Determining the action / task in need of implementation / solution.
2) Reducing the action / task to its simplest form and adding steps that need to be completed / solved.
3) Development of an interface as a “concept” for an action / task in need of completion / solution.
4) Improvement (design and code)
1) An action / task in the game requiring fulfillment / solution is implemented by physical actions or using the terminal. In the case of physical actions (for example, opening the valves in the right order), they can be transferred quickly enough. Due to the modular design of the interaction system, similar situations arise very often (for example, the prompt “Press [E]”). But when the situation requires a solution on the terminal screen, our work becomes much more complicated and the same system is not applicable here.
2) For example, we need to connect to another section of the ship and open the door using the terminal. The player moves through the hierarchy of folders, which is quite simple to change for each terminal after understanding the commands, as well as how and where to use them. But everything changes when it comes to the puzzle screen. To make simple actions, for example, opening a door, complex, but understandable, and to turn them from one action into a sequence, we had to work hard. If we continue the example with the door, then the list of possible actions of the player is listed below.
A) Get a special program that opens the door, or the necessary information from the terminal, or from a physical object at the level.
B) Find a “special section” in which you can use this information or attach it using the command system.
C) After gaining access to a particular section, use the relevant information obtained earlier only for this section.
3) If we want the puzzle to be solved using the terminal screen, it is better to start by developing the concept of the interface. Thanks to this, we save time and can predict possible problems. At the same time, we can understand whether the puzzle fits the theme of the game. The biggest difficulty was placing the terminal screen within the screen and ensuring the visibility of all its parts. It should be both functional, visual, and provide sound feedback. In addition, we wanted some parts to be movable. Therefore, all the elements of the puzzles were separate parts and sometimes created in separate blocks. Below are some of the design concepts used in the game version and some parts of the puzzles.
4) The last is the stage of improvement. Here we thought about how we can improve the finished interface, while maintaining its understandability. Even though we sometimes did not want this, many elements had to be abandoned or changed.
Colors and Fonts
Tartarus terminals are “monochrome”. In other words, they consist of the same color values, or of the same color. The reason for this is because we wanted to create a simple and understandable design. In addition, it harmonized well with the overall atmosphere and theme of the game. The first reviews we received at gaming shows proved to us that we are moving in the right direction. Therefore, later we did not have to make many changes. In sections that deviate from the standard design (interactive areas), we decided to use more red and green. We tried to make the design more convenient and understandable, both consciously and unconsciously.
Another problem associated with the colors of the terminal was that the user can change the color of the text of the terminal using a special command. Players can do this with the Color team. At first we thought that no one would use it, but watching the “gameplay” videos we noticed that for many players the game works with non-standard colors. We wanted to leave this feature for two reasons. The first was that DOS users and people familiar with the command line were familiar with this and we wanted to revive their memories. The second reason was that thanks to the color change, the gameplay became a more personal process. To summarize briefly, we felt that no one would suffer from the monotony of the screens and a slight revival of colors. We believe that we made the right decision.
The next important issue was the fonts displayed on terminal screens. They should not be too thin or too bold because the terminal screens have curved surfaces like CRT monitors. We did not want the characters at the edges of the screen to go beyond it or become unreadable. They should have been consistent with the general theme of the game and the terminals. We chose a font that seemed appropriate, readable and bold, similar to the DOS and Commodore styles.
Another problem was the subsidence of the FPS in the game. We completed the prototyping phase of the terminals and after integrating them into the game, we noticed a decrease in FPS by an average of 30%. Thanks to the speed tracking tools, we found out that the Capture Camera is the cause of the problem. Tartarus is a game mainly related to the use of terminals. But at the same time, we must solve all other problems inside the ship, controlling the main characters. Therefore, when controlling a character, the capture camera is not required to capture every frame. We deactivated some parameters that allowed her to capture every possible frame. To do this, we selected the Capture Camera and deactivated the Capture Everyframe and Capture On Movement options in the Details panel.
We restore these settings when switching to terminal management.
Thus, we solved the problem with FPS. If in your game control is performed only through the terminals, then there is no reason to use such antiquity.
In addition, we have done everything to not execute unnecessary processes for Event Tick. Event Tick leads to a decrease in performance due to unnecessary processes, because it is performed in each frame.
Another problem was that in some situations, for example, when you press Alt + Tab, the terminals lose focus. This was not a problem if the player was on the menu or interacted with the mouse. But we did not want the player to use the mouse in a situation where he would use only the keyboard. In addition, some users might decide that this is a game bug. Therefore, we decided to use the following scheme. In this case, the user constantly focuses on the terminal until he moves away from it.
The Event On Focus Lost event is fired when the focus is lost, returning focus to the terminal. But the important point is that when the user wants to leave the terminal intentionally, then we set the Lost Focus variable to true. Even if this event is triggered in this case, the focus is not automatically transmitted to the terminals.
Thoughts in conclusion
The ideas presented above arose from our experience and the process of working on a project. I think this is a great example of what anyone can do with just a blueprint system and simple material settings. I hope the article was at least a little useful for you.