New mobile Lineage 2 on Unreal Engine 4 we decided to compile a small selection of interesting remakes of games on this engine that deserve your attention.
Announcement of the remake of the legendary Final Fantasy 7 at E3 2015 was the highlight of that year for many people.
Subsequently, at PlayStation Experience 2015, a trailer was shown containing several seconds of gameplay shown for the first time with the participation of Cloud and Barret at the very beginning of the game.
To understand what place it occupies Final Fantasy 7 in the gaming industry, and how much people were waiting for this moment, just look at the reactions to the announcement of this remake.
22-year-old 3D environment artist Kimmo Kaunela from Finland has been working on the project for the past year. The Last Stop, inspired by the studio's creation - the game. Kimmo managed to create an amazing map showing what the game would look like if it were released on Unreal Engine 4.
The guy has an account on Deviant Art And own website, where you can see his latest work. This project, according to the author, taught him how to work with Unreal Engine 4.
Enthusiast Airam Hernandez shared a video earlier this year of an amateur remake of the first part, which he is working on alone.
Hernandez's work uses a modified version of the Unreal Engine 4 graphics engine. According to him, at first he only wanted to recreate the island of Shadow Moses using new technology so that gamers familiar with the original and newcomers could explore its attractions. However, after foreign media drew attention to the technical demo, Hernandez decided to take on a full-fledged remake of the stealth action game.
In his Facebook posts the author of the remade Metal Gear Solid did not specify when to wait final version. The video, as the introductory message suggests, is based on an early prototype of the remake.
Modder with nickname Logithx set out to make a remake of the cult shooter. An enthusiast is working on the embodiment of the dystopian City-17 from HL2 using Unreal Engine 4. The author cannot yet give an approximate completion date for the work - everything is still at an early stage.
Logithx spent a lot of time working on a remake of Half-Life 2 using the previous version of Epic's engine - Unreal Engine 3. However, when the company began distributing the powerful UE4, he decided to start work from scratch - and already on new technology.
“How so, you said Top 5 - where does the 6th point come from?” - you ask.
To hell with the rules! This is on Unreal Engine 4! Just look at these graphics! The author of the project to port this wonderful game to a new engine is Alexander Young.
Pong- the first game to show that games are serious. It's funny, but two rackets and a pixel ball were enough to create the first boom in arcade machines: slot machines with Pong were so popular that its clones soon flooded the market. Atari subsequently released a version of Pong for the Atari 2600 console, proving that home gaming systems have a right to life too. The success of Pong also became a signal for other gaming companies - for example, Konami quickly entered the arcade market with the game Maze.
This guide is designed to help developers improve performance in games made with Unreal Engine 4 (UE4). Here we will talk about tools that can be used both in the engine itself and outside of it, about the best approaches to using the editor, as well as about scripting that helps increase the frame rate and stability of the project.
The overall goal of this guide is to identify what causes performance problems and provide several methods to resolve them.
This guide was written using UE4 version 4.14.
Optimization improvements are measured using frames per second (also called frame rate or fps) and milliseconds per frame (ms).
The graph below shows the relationship between average framerate and milliseconds.
To find out ms at any fps, we simply find out the inverse of fps (i.e. take 1 and divide it by fps), and then multiply it by 1000.
1/FPS x 1000 = MS
Using milliseconds to describe performance improvements allows you to better measure the level of optimization required to achieve your target framerate.
Here are a couple of examples of increasing FPS by 20 frames per second:
Before we get started, let's look at three tools to understand what's going on under the hood of the engine. These are UE4 CPU Profiler, UE4 GPU Visualizer and Intel Graphics Performance Analyzers (Intel GPA).
UE4 CPU Profiler is a tool that is built into UE4 that allows you to track your in-game performance, whether it's a live game or just a saved snippet.
To find Profiler, in UE4 click on Window > Developer Tools > Session Frontend.
How to get to the Session Frontend window
In Session Frontend, select the Profiler tab.
Profiler in Unreal Engine
Now that you're in the Profiler window, select Play-In-Editor (PIE) and then select Data Preview and Live Preview to see the data being read from the game. To begin capturing data, press Data Capture, and to save the data for later viewing, press Data Capture.
Viewing processes via Profiler
In Profiler, every action and command is reflected in milliseconds. Each area can be examined for how it affects the project's frame rate.
UE4 GPU Visualizer determines how much computing resources are required for rendering passes, and also allows you to view in detail what is happening within a particular frame.
You can open GPU Visualizer through the developer console by entering “ProfileGPU” there.
ProfileGPU console command
After you enter the command, the GPU Visualizer window will appear. It shows how long passes take, as well as the approximate location of those passes in the scene.
View processes using GPU Visualizer
Just like in Profiler, by identifying the areas that take the longest to process, you'll know where to optimize.
Intel Graphics Performance Analyzers (Intel GPA) are a suite of analysis and optimization tools designed to help developers make their graphics projects perform better.
In this tutorial we will focus on two aspects of this suite: Analyze Application and Frame Analyzer. To get started, download GPA from the Intel Developer Zone. Once installed, compile your project with the Development setting (to select it, click on File > Package Project > Build Configuration > Development).
When the project is compiled, launch Graphics Monitor, click on Analyze Application, select the desired *.exe file in the Command Line field and click on the Run button to run it.
Next, the game will start - the same way it usually starts, however, in the upper left corner there will now be a menu with statistics. To expand it, click Ctrl+F1. If you press Ctrl+F1 once, several windows will appear with indicators measured in real time. If you press Ctrl+F1 again, a list of commands (plus the hotkeys you need to press to execute them) will appear with which you can experiment with the game while it is running.
Intel GPA menu in game
To make a frame for subsequent analysis in Frame Analyzer, you need to go into the game and do two additional actions.
First enable Toggle Draw Events. To do this, enter “ToggleDrawEvents” in the console.
Console command ToggleDrawEvents
When you enable this feature, the rendering commands coming from the engine will be given names. This will allow you to understand what's what when you look at the captured frame in Frame Analyzer.
Finally, save the frame by pressing the hotkeys Ctrl+Shift+C.
After saving the frame, launch Graphics Monitor, click on Graphics Frame Analyzer and select the frame you want to load. After saving is completed, the program will show all the information about the graphics available in the frame.
The abundance of data in Intel GPA seems overwhelming at first, so let's start with the largest pieces of information. In the upper right corner of the window, set both axes (X and Y) to GPU Duration - the result will be a graph of which rendering commands in this frame are the most resource-intensive.
In our example, i.e. in the frame with a desert landscape, it is clear that the most resource-intensive was the basic passage. Having selected the largest peak on the graph (that is, essentially the most resource-intensive rendering command), as well as the Highlighted item in the lower left preview window (called Render Target Preview), we see that the cause of the peak was the landscape (it is highlighted in pink ).
Next, going to the Process Tree List window (it is located above the preview window and shows a list of processes) to find the selected rendering command, we see that this landscape consists of 520,200 primitives, and the GPU has to process it (this is the GPU indicator Duration) takes 1.3185 milliseconds (ms).
Finding the most resource-intensive rendering command in a frame
Now that we know what caused the spike, we can start optimizing.
Firstly, the terrain can be rebuilt using the Manage mode of the UE4 terrain creation tool, which allows you to reduce the number of primitives to 129032, and GPU Duration to 0.8605 ms. Thus, the scene is optimized by 5%.
We see a decrease in GPU Duration
To reduce the resource "cost" of the landscape again, let's take a look at materials. Our terrain uses a 13-texture 4096 x 4096 (4K) material, resulting in a total of 212.5 MB of texture streaming.
View rendered textures in Intel GPA
By compressing all landscape textures to 2048 x 2048 (2K), we reduced GPU Duration to 0.801 ms and improved performance by an additional 6%.
As a result, reducing texture streaming for the landscape to 53.1 MB and reducing the number of primitives made the project faster. And all this at the cost of only a very small reduction in the visual quality of the landscape.
We see a reduction in GPU Duration achieved by reducing the texture size
Overall, by simply rebuilding the scene and changing the textures, we were able to achieve the following:
Deferred Rendering is the standard rendering method used in UE4. Using Deferred Rendering usually improves the image, but can also cause performance problems, especially in VR games and on weaker machines. In these cases, it makes more sense to switch to Forward Rendering.
For example, in the Reflection scene from the Epic store, you can see that there are some differences between the Deferred and Forward rendering methods.
Reflection scene rendered using Deferred method
Reflection scene rendered using Forward method
With forward rendering, reflections, lighting, and shadows suffer, but other visual elements do not change. As a result, productivity improves, but whether such sacrifices are necessary is, of course, up to you to decide.
If we take a look at the Intel GPA frame of this Deferred rendered scene, we see that the scene runs at 103.6 ms (9 fps), and a significant portion of this time is spent processing lighting and reflections.
Frame data from a Reflection scene rendered using Deferred on an Intel HD Graphics 530
And if we look at the frame rendered using the Forward method, we see that the “ms” indicator has improved from 103.6 to 44.0 (i.e. by 259%), and most of the time is spent on the base pass and post-processing. which can also be optimized.
Frame data from a Reflection scene rendered using Forward on an Intel HD Graphics 530
Static meshes in UE4 can consist of thousands or even hundreds of thousands of triangles - to show the most the smallest details, with which the 3D artist decorated his work. However, when the player is far from the model, he does not see these details, and the engine still processes these triangles. To solve this problem and thereby optimize the game, we can use so-called “levels of detail” (or simply LOD - from the English “level of detail”), so that these details are shown at a close distance, but not at a far distance.
In a standard pipeline, LODs are created by a 3D modeler during the creation of the model itself. Although this method allows you to control the end result, UE4 has a great tool built into it to automatically generate LODs.
To do this, select the desired model, go to the Details tab, and then to the LOD Settings item. There, find the Number of LODs item (i.e., the number of levels of detail) and enter the desired value there.
Automatic generation of levels of detail
Click Apply Changes. This will be a signal for the engine to generate several LODs, and the original model among them will be LOD0. The example below shows that when creating five LODs, the number of triangles in our static mesh decreases from 568 to 28 - this is a significant reduction in the load on the GPU.
Number of triangles and vertices, as well as screen size for each LOD
If we place this model on the stage, we will see how it will change as it moves away from the camera.
Visual demonstration of LODs shown according to screen size
Another feature of LODs is that each of them can use their own material. This allows us to further reduce the “cost” of the static mesh.
Materials assigned to each level of detail
For example, normal maps are widely used in the gaming industry. However, a problem that arises in VR games is that normal maps are not perfect because upon closer inspection the player sees that it is just a flat surface.
This problem can be solved using LODs. Because LOD0 is detailed to the point where small details like bolts and screws are visible, when the player looks at the object up close, the player experiences a more immersive experience. Since all these details are modeled, the normal map can be abandoned on the first LOD. When the player moves away from this object, the engine switches to another LOD, which has a normal map that reduces the detail of the model. When the player moves further away, the normal map can be removed, because it will become too small and simply not visible.
Every time a new object appears on the scene, it requires calling an additional render command on the graphics device. If this static mesh, then each copy of this mesh will require a separate call to the drawing command. One way to optimize this (i.e. the situation where the same static mesh is repeated multiple times in the scene) is to instantiate the static meshes and thereby reduce the number of render commands called.
For example, we have two spheres consisting of 200 octagonal meshes - one green and the other blue.
Sphere of static meshes and instance meshes
The green octahedrons are regular static meshes. This means that a separate set of rendering commands is used to generate each of these models.
Draw commands for 200 static meshes (maximum 569)
The blue octahedrons are instance meshes. This means that only one set of rendering commands was used to generate all these models.
Draw commands for 200 mesh instances (maximum 143)
Looking at both examples through GPU Visualizer, the basic pass for the green (with static meshes) sphere takes 4.30 ms, and for the blue (with instance meshes) it takes 3.11 ms. Thus, we optimize the scene by 27%.
One thing you need to know about instance meshes is that if such a mesh renders some part, it will also be rendered for all other “clones” of this mesh. That is, if one of the “clones” ends up outside the camera, our optimization potential is wasted. Therefore, we recommend making grid specimens in small piles - like a pile of stones, a pile of garbage bags, a mountain of boxes or modular buildings located at a distance.
If most mesh instances are off-screen, they are still rendered
If you are using static grids with LODs, consider hierarchical instance grids.
Sphere of hierarchical instance meshes with LODs
Like standard instance meshes, hierarchical instance meshes reduce the number of drawing commands, but also use LOD information.
A sphere of hierarchical instance meshes with LODs; close view
In the UE4 engine, Occlusion Culling is a system that allows you to make sure that objects that the player cannot see are not rendered. This allows you to reduce system requirements to the game, since the engine no longer has to draw absolutely all objects in absolutely all scenes and absolutely all frames.
Octagons scattered around the stage
To see occluded objects (they will be shown as transparent cubes with green edges), enter "r.VisualizeOccludedPrimitives 1" in the editor console. To disable this setting, enter “0” instead of “1”.
Edges of fenced nets; these edges became visible after using the command r.VisualizeOccludedPrimitives 1
Whether the mesh will be rendered or not depends on the so-called “bounding box”. Thanks to it, some objects may be invisible to the player, but visible to the camera - in this case, the engine decides to render these objects.
Viewing the boundaries of an object in the window for working with the object
If the mesh needs to be rendered before the player sees it - for example, to render idle animation, which is an animation of a character that activates when he stands still and does nothing; this could be head scratching, picking the ground with your foot, etc.) – then the size of the boundary cube can be increased. This can be done in the window for working with the object, in the Static Mesh Settings menu. Look for the Positive Bounds Extension and Negative Bounds Extension items there.
Set the scale for the object boundaries
The boundary cube of complex meshes and shapes always extends beyond those meshes, so the more empty space there is in the boundary cube, the more often these meshes will be rendered. Therefore, when working on a scene, it is important to know how the dimensions of the boundary cubes affect its performance.
Let's imagine a thought experiment where we create a 3D model and then export it to UE4. How should we approach creating a Colosseum-style arena?
Let's say a player stands in the center of the arena and looks around the huge Colosseum, trying to intimidate his opponents. When the player rotates the camera, its direction and angle will dictate what the engine needs to render. Since the Colosseum is a very important element of our game, we made it very detailed, but to save on drawing commands, it needs to be made from several objects.
But first, we must abandon the idea that the entire arena should be one big solid object. In this case, the number of triangles that will need to be rendered will correspond to the size of the entire arena - regardless of whether we are looking at its individual parts or not. How can we optimize this model?
Depends on several factors. Firstly, on what pieces the arena will be cut into, and secondly, on how the shape of these pieces will affect the size of the boundary cubes (which is important for Occlusion Culling). To make it easier, let's imagine that the player is using a camera with a 90-degree field of view.
Option one is “sliced pizza”. That is, we create 8 identical sharp pieces, the “noses” of which are directed towards the center of the arena. This method is simple, but it is not very suitable for Occlusion Culling, since in this case there will be a lot of overlap between the boundary cubes. If the player stands in the center and looks around, his camera will capture 3-4 cubes, i.e. most the engine will have to render half of the arena. In the worst case scenario, the player can stand with his back to the wall, look at the arena around him and thereby capture all 8 pieces of “pizza” in the frame. No optimization.
Option two is “tic-tac-toe”. Here we create 9 pieces. This is not the most traditional method, but it has the advantage that there is no overlap between the boundary cubes. As in the case of “pizza,” if the player stands in the center of the arena, he will capture 3-4 pieces in the frame. However, with his back to the wall, he will capture 6 out of 9 pieces in the frame, which, compared to “pizza,” provides some optimization.
The last option is “sliced apple” (1 central piece and 8 side pieces). This is the most common method for this thought experiment, and a very good one - there is overlap between the boundary cubes, but it is small. If the player stands in the center of the arena, he will capture 5-6 pieces in the frame, but unlike the first two options, in the worst case (with his back to the wall) the same 5-6 pieces will be rendered.
A thought experiment showing how a large model could be cut and how this would affect the boundary cubes and the overlaps between them
Although dynamic shadow cascades add to your game high level details, they can be very “expensive” in terms of performance - to play such a game without losing framerate, you will need a powerful PC.
Luckily, as the feature's name suggests, these shadows are created dynamically on a frame-by-frame basis. That is, we can create several options thanks to which the player can optimize his graphic settings.
The value in dynamic shadow cascades can be controlled dynamically. This can be done in several ways:
Drawing commands can be called even on completely transparent game objects. To avoid this, you need to configure the engine so that it stops rendering them.
To do this with blueprints, you need to use several different systems in UE4.
First, we create a set of parameters for materials (or simply MPC - from the English “material parameter collection”). Linear and vector parameters will be stored here, which can be linked to any material in the game. They can be used to modify these materials during the game - to create dynamic effects.
We create an MPC by clicking on the Content Browser tab on Add New > Materials & Textures > Material Parameter Collection.
Creation of MPC
Being in MPC, we can create, name and set default values for linear and vector parameters. In our case, we will need a linear parameter - we will call it Opacity (i.e. “transparency”) and with its help we will control the transparency of our material.
Set a linear parameter called Opacity
Searching for the Collection Parameter node in a material
Having created a node, we connect it with the Opacity parameter on the base material.
Setting up the Collection Parameter in the material
Having created the MPC and material, we go to the blueprint and configure it so that we can set and read values from the MPC. This is done using the Get/Set Scalar Parameter Value and Get/Set Vector Parameter Value nodes. Next, we go to these nodes, in the Collection item we select the set that we want to use (MPC), and in the Parameter Name item – the name of the parameter from this set.
For this example, we set the linear Opacity value to be a sine at game time - so we can see values ranging from "1" to "-1".
We set and read a linear parameter, and also use its value in a function
To determine whether an object is rendered or not, we create a new function called Set Visible Opacity. Input values it will have an Opacity parameter from MPC and a static mesh, and the output will be a Boolean value indicating whether the object is visible or not.
Next, we run a check to ensure that the value is slightly greater than “0” (in in this case, more than “0.05”). The check for "0" may work, but when getting closer to "0" the player will no longer be able to see the object, so we can simply turn it off before the value becomes "0". In addition, this allows you to create a buffer - in case of floating point errors, due to which the linear parameter will not be able to get an exact "0". For example, if the value is “0.0001”, this system will simply turn it off.
Next, we create a Branch node - if its output is True, then the visibility of the object (the upper Set Visibility node) will be given the value “true”, and if False, then the visibility of the object (the lower Set Visibility node) will be given “false”.
Set Visible Opacity function
If the scene blueprint uses an Event Tick node, these scripts will work even if no objects are visible on the screen. Typically this isn't a big deal, but the fewer blueprints that tick during each frame, the faster the scene will run.
Here are some situations where you can use this type of optimization:
As the most simple solution You can place the Was Recently Rendered node in front of the Event Tick. Thus, in order for our Event Tick to turn on/off, we do not need to connect special events and detectors to it. In addition, this system can still be independent of other processes occurring in the scene.
Managing an Event Tick Node Using a Render Check
This method can also be used for more complex problems. For example, if we have a process that runs depending on game time (for example, some glowing button that lights up and goes out every second), we can use the graph below:
The Emissive Value in the Material Collection is set in such a way that during rendering it will act as an absolute sine wave of game time
The graph above keeps track of how much game time has passed and then passes that value through an absolute sine plus one, resulting in a sine wave varying between the values "1" and "2".
The advantage of this method is that the button will blink according to the line in the graph above, regardless of whether the player is looking at the button or not (they can either spin in circles or stare at it). And all thanks to the value calculated based on the sine of the game time.
This also works with integer division remainders, but in this case the graph looks different.
You can check for rendering using the Was Recently Rendered node a little later. That is, if the object controlled by the Event Tick node has some tasks that need to be performed every frame, but you can make sure that these tasks are performed before the render check (see the graph below). The fewer nodes that are called with each “tick” of the Event Tick node, the better.
Using a Render Checker to Manage the Visual Fragments of a Blueprint
Another way to reduce the "cost" of a blueprint is to slow it down and allow the Event Tick node to "tick" only once during a certain interval. This interval is set using the Set Actor Tick Interval node.
Switching between intervals
In addition, the interval at which the Event Tick node “ticks” can be set in the Tick Interval item - it is located in the Details tab of the blueprint you are working on. Here the interval is specified in seconds.
Tick Interval item in the Details tab
This is convenient, for example, when you need to make a counter that fires every second.
Create a parallel counter that fires every second
As an example of how this type of optimization can reduce your average ms, let's take a look at the graph below:
Incredibly useful example of what not to do
Here we have a ForLoop node that counts from “0” to “10000”, and an integer Count is specified for it through the SET node. This graph is very resource-intensive and inefficient - so much so that the ms indicator for our scene is a whopping 53.49 ms.
Viewing Stat Unit's "cost" is an incredibly useful example.
Moving into Profiler, we understand why. This simple but extremely ineffective blueprint consumes 43 ms for each tick.
Viewing in Profiler the fragment responsible for triggering the Event Tick in each frame
But if you make this blueprint “tick” every second, then most of the time it will “eat” 0 ms. If we look at the average time (select some fragment of the timeline in the Graph View window) over three ticks, we will see that the average is 0.716 ms.
Viewing in Profiler the fragment responsible for triggering the Event Tick every second
Or let's take a more common case: let's say our blueprint has 1.4 ms, and if the scene runs at 60 fps, then it will take 84 ms to process this blueprint. But if you reduce the time during which the Event Tick node “ticks” on a blueprint, this will also reduce the total time spent on processing this blueprint.
When multiple models move at the same time, it looks very cool and can make for a very attractive visual style. True, in this case, a large load falls on the CPU, which is why FPS ultimately suffers. However, this can also be optimized by dividing the mass movement into several blueprints - thanks to multithreading and UE4’s ability to manage worker threads.
In this section, we will use a script that will dynamically move 1600 instanced spheres up/down along a modified sine curve.
Below is a simple script that creates a grid. We simply add the Instanced Static Mesh component, in the Details tab, select the mesh we will use, and then add the following nodes:
Script to create a simple grid
Having created the grid, we add this script to the Event Graph tab.
A few words about the Update Instance Transform node. If any of the instances are transformed, this change will not be shown until the Mark Render State Dirty item is marked "true". But this is a resource-intensive operation, because... the check goes through each grid. To save computing resources, especially if this node is launched several times per tick, you can make the grids updated at the end of the blueprint. In the script below, the Mark Render State Dirty item is marked as “true” only if two conditions are met - if ForLoop node costs Last Index and if the value in Index corresponds to Grid Size minus 1.
Dynamic Motion Blueprint for Static Instance Meshes
In addition, using an Actor type blueprint, a grid script and a dynamic motion event, we can create different grid options where 1600 grids will be displayed at the same time.
Multiple grille options with 1600 meshes
Running the scene, we will see lattice elements floating up and down.
Lattice of 1600 static grid instances dynamically moving up and down
However, the type of chunking affects the speed at which the scene runs.
First, if the lattice consists of 1600 individual fragments, then all 1600 blueprints will be processed in 16.86 ms (i.e., an average of 0.0105 ms per blueprint). That is, although the “price” of one blueprint is small, their total number slows down the system. The only thing that can be done here is to reduce the number of blueprints that fire with each tick. Another reason for the high load is the large number of individual meshes, which increases the number of both rendering and mesh transformation commands.
Secondly, if the grid consists of one fragment, which includes 1600 grids, then this option will be very well optimized in terms of drawing commands (because only one drawing command is required for the entire grid), but the “cost” of a blueprint, which is one “tick” will need to process 1600 grids, will be 19.63 ms.
But if you split the grid differently (into 4 fragments of 400 grids, 64 of 25 or 16 of 100), the result is more optimized - thanks to the reduced script processing time and the ability of UE4 to work with multi-threading. Thanks to the latter, UE4 can distribute the blueprint processing load across multiple worker threads, thereby effectively using all CPU cores.
If we look at the processing time of blueprints and how they are distributed among worker threads, we see the following:
Using the right data structures is a necessary element of any program, and this applies to game development as much as it applies to other software development. When programming in UE4, blueprints are used, and there are no data structures in the template array, which serves as the main container. They are created manually at a later stage of development - with the addition of functions and nodes found in UE4.
As an example of why and how data structure can be used in game development, let's imagine a shootmap game. One of the main mechanics of shootmaps is shooting at enemies, which generates thousands of bullets rushing across the screen. Since bullets eventually reach their targets (or fail to hit objects) and are destroyed, the game engine must clean up this debris, which can impact game performance and even reduce framerate. To deal with this problem, developers should provide in their design a so-called “object pool” - a set of objects (in this case, bullets) placed in an array/list and processed when the game starts - thanks to which developers can enable / disable bullets in any time. As a result, the engine is given only the job of creating bullets.
The most common method of using an object pool is to take the first not yet enabled bullet in the array/list, move it to the starting position, turn it on, and then turn it off when it flies off the screen or hits an enemy. The problem with this method is the time it takes for the script to run, i.e. in big "O". Because you're doing a lot of loops checking objects and looking for which one to turn off, when using 5000 objects it can take a lot of loops to find one object. For this method, the time will be represented as O(n), where “n” is the number of objects in the set.
Although O(n) is far from the worst algorithm. The closer we get to O(1) – i.e. to a fixed “cost”, independent of the size - the more effective the script will be and the faster the game will be. To pull this off with the object pool, we use a data structure called a "queue". Just like a real queue, this data structure takes the first object in the set, uses it, and then deletes it until it has used all the objects in the queue.
Using this "queue" for our object pool, we can take the front piece of the set, include it, then remove it and immediately put it in the back of the set. This will create an efficient loop in the script and reduce the script's running time to O(1). We can also add a check to this loop - if the deleted object was included, then the script takes it and, without creating a new object, places it at the end of the queue, increasing the size of the set, but without increasing the processing time of the script.
Below are some pictures that demonstrate how to use queues. They add various features to blueprints that make the code cleaner and more reusable.
Implementation of the queue::pop construct in the UE4 blueprint; removes an element from the front of the queue
Implementation of the queue::push construct in Blueprint UE4; inserts a new element at the end of the queue
Implementation of the queue::size construct in Blueprint UE4; reports the queue size
Implementation of the queue::front construct in UE4 Blueprint; returns a pointer to the first element in the queue
Implementation of the queue::back construct in UE4 blueprint; returns a pointer to the last element in the queue
Inserts the specified element into the specified queue location (with position check)
Implementation of the queue::swap construct in UE4 blueprint; forces two containers to exchange data (with position verification)
Below are some pictures that demonstrate how to use stacks. They add various features to blueprints that make the code cleaner and more reusable.
Implementation of stack::pop construct in UE4 blueprint; removes an element from the front end of the stack
Implementation of stack::push construct in UE4 blueprint; inserts a new element at the end of the stack
Implementation of stack::empty construct in UE4 blueprint; tells whether the stack is empty
Implementation of the stack::size construct in Blueprint UE4; reports the stack size
Returns a pointer to the last element on the stack
Inserts the specified element into the specified stack location (with position check)
The Unreal Engine has its roots back in 1998. Since then, many amazing games have been created. In this article we will talk about the 7 best of them. At the time the engine was created, it was used to create simple games with 2D view. Gradually, more modern 3D action games began to appear. The popularity of the engine was ensured by a unique operating system; it simultaneously has: a graphical and physical core, built-in artificial intelligence, as well as tools for managing file systems and networks. The main attraction was the availability of a ready-made environment for creating games - UnrealEd.
This game has an action-thriller genre. The plot is based on events that occurred 9 months after the famous war in Arkham City. Since the Joker is dead, Gotham begins to return to its usual rut of life, a safer time begins for its inhabitants, but the balance of power is lost when, one day, the inhabitants of Gotham throw themselves at each other in a cafe for no apparent reason.
After such a performance, Scarecrow appears on stage with the statement that he has only used 100 g of his new toxin and she intends to continue, increasing the dose every day. In this regard, residents are urgently evacuated from the city.
Main character– Batman, who tirelessly fights for the safety of his hometown and eradicates crime. During the game, numerous enemies, beautiful locations, and unique characters will appear. The game is distinguished by a careful approach to gameplay and therefore deserves to take 7th position among the best.
There are games that, without exaggeration, are known all over the world, Street Fighter is one of such games. The modern fifth part will fill the void left by the illogical release of the third and sixth parts. For those few who are not yet familiar with the game, it is a fighting game where the user fights with enemies. The basis of its popularity lies in the possibility of multiplayer, and playing with a friend always brings “live” emotions.
The game has been famous since time immemorial, due to the many characters with unique characteristics and combat abilities. Previously, there were high-class characters and users that were impossible to win against, but gradually the combat strategy and techniques changed and gained more balance.
The game focuses on the year 1964, when in a city called Wellington Wells(the name is fictitious) drugs were developed to increase joy, it’s called the “Joy” serum. It is based on clearing memories. The thing is that during the Second World War, residents resorted to using extremely bad things, which provoked a strong suppression of the spirit of the nation. The serum is designed to eliminate these memories and make people happier, and by force. Those residents who refuse to use “Joy” are called “Bores” and they are eradicated.
The main character is Arthur Hastings, an ordinary clerk who is engaged in censoring newspapers of “unfortunate” news from the past. While going through newspaper archives, he discovers his photo and younger brother on the cover. Memories creep into his head and they bring pain. In order to get to know himself, he refuses to drink “Joy”. After the effect ends, his colleagues recognize him as a “Bore” and begin to pursue him. This forces Arthur to flee to the dungeons.
The cult series of games once again inspires the user to immerse themselves in the world of "mess". In terms of gameplay, there are no significant differences from previous parts. The game remains an excellent shooter, where there are numerous enemies and shelters. There are really a lot of weapons, right down to a machine gun with a chainsaw as an under-barrel bayonet. Battles take place both with the Horde, although they are creepy, but alive, and with COG robots. All monsters are trying to devour the hero.
An interesting feature of the game is that a whirlwind of incredible strength may suddenly appear, which provokes a strong shaking of the entire surface of the Sulfur; no one and nothing can resist the force of nature. The player will be able to see houses soaring into the sky; surviving in such conditions will not be easy.
Mortal Kombat is in the top three best games on the Unreal Engine. It has a fighting game genre, which was discussed earlier using the example of Street Fighter V. There are many characters here, a countless number of strikes, but making them can be quite difficult, since the combinations are long and must be performed accurately. The popularity of the game is based on its excellent gameplay, spectacular battles and beautiful character models and terrain for fights.
The game even has a plot, although it is more of a formality, because the value of the game does not lie in this component. Fights with friends, which lead to the emergence of a competitive spirit, are the most important feature of the game. To be a winner, you need to memorize and practice combinations for strikes, and then neither your friend nor the computer will be able to resist.
BioShock is an interesting shooter that fascinates with its atmosphere and has some RPG elements, that is, you can upgrade your character’s abilities. The game takes the user back to 1912, the action takes place in the floating city of Columbia.
The user will play as private practice detective Booker DeWitt, who enters the city to search for Elizabeth. After the discovery of the girl, both heroes become prisoners of the war between the authorities and the rebels, the simple, working class.
Elizabeth's uniqueness is that she can create rifts between parallel worlds that reveal much about the city and the characters' pasts.
Stealth action is becoming quite a popular genre these days, but it's difficult to compete with Dishonored. Additionally, the game contains elements of the RPG genre.
The game takes you to a city that is completely mired in a plague epidemic - this is Dunwall. It is noticeable that the city is in many ways reminiscent of London in ancient times, approximately the Victorian era. The main character, Corvo Attano, wants to restore justice, because he was blamed for the murder of the empress. The lord escapes from prison and tries to establish a balance between good and evil forces.
Corvo does not neglect any ways to solve the situation, so he “moons” as a hired killer. The main task is to find and eliminate a specific character. Present in the game a large number of various weapons, but that’s not all, because the character has unique abilities.
The game has an interesting plot, it is non-linear. Almost every mission can be completed in many ways, in particular - you can make noise and kill numerous opponents or kill the enemy secretly so that no one notices.
Greetings, Habr! I would like to introduce you to relatively small project, which I made from scratch in about 150 hours (50 runs ~3 hours each) on Unreal Engine 4. I did the project in live only on streams once a week (it took a year in total), answering user questions along the way.
The project itself was not intended to be commercial. My goal was to show in practice the complexity of game development, namely problems such as:
At the end of the entire series of streams, we ended up with a playable prototype of a “survival” shooter. Those with a glass half full might even call it pre-alpha without a plot.
If you are interested in the details of the project, stream recordings, sources, etc., read on.
The entire project was implemented on a visual programming system called Blueprints. And of course, many specialists may call it childish; even a relatively large project can be easily developed on it. Moreover, this can be done relatively quickly, as we have already been able to prove.
I want to answer the question right away: " Why Blueprints and not C++?". Well, firstly, when I started the series, I almost didn’t know the pluses. Although I would still make such a single on a power supply. Secondly, power supplies are almost as good as the pluses in our case, but at the same time they provide a number of possibilities: They don’t allow you to do many errors are possible with advantages, you don’t have to be distracted between BP and C++, it’s easier to understand for beginners. And in our case they are not much slower, given the fact that almost all the logic is built on events.
We also managed to work a little on the graphics. Unfortunately, we didn’t have time to make assets, so we left some of them as dummies, some were made directly in the editor from primitives, and some content was borrowed from free Epic Games demos. Nevertheless, we managed to do some things ourselves, for example, a day and night system, post-processing for water and some materials for scene objects.
The plans for my streams also included problems that may arise during development. I specifically solved them live in order to not only show what young developers might encounter, but also how to debug their code, look for bugs and write their code so that the whole thing could be done twice as fast. Of course, I don’t have decades of experience in programming, and this affected the sometimes stupid mistakes I made. And I’m sure that many developers can challenge many points in the process of writing a game.
Naturally, this can hardly be called a full-fledged game, since there is no plot or goal in the game - only pure mechanics. However, I believe that the result is something to be proud of and that it fully reflects what the whole project was intended for.
List of everything we managed to implement in our game
Character
Object Inventory System
Equipment system
Contents in parts
Greetings, Habr! I would like to introduce you to a relatively small project that I made from scratch in about 150 hours (50 runs ~3 hours each) on Unreal Engine 4. I did the project live only on streams once a week (in total it took a year) , answering user questions along the way.
The project itself was not intended to be commercial. My goal was to show in practice the complexity of game development, namely problems such as:
At the end of the entire series of streams, we ended up with a playable prototype of a “survival” shooter. Those with a glass half full might even call it pre-alpha without a plot.
If you are interested in the details of the project, stream recordings, sources, etc., read on.
The entire project was implemented on a visual programming system called Blueprints. And of course, many specialists may call it childish; even a relatively large project can be easily developed on it. Moreover, this can be done relatively quickly, as we have already been able to prove.
I want to answer the question right away: " Why Blueprints and not C++?". Well, firstly, when I started the series, I almost didn’t know the pluses. Although I would still make such a single on a power supply. Secondly, power supplies are almost as good as the pluses in our case, but at the same time they provide a number of possibilities: They don’t allow you to do many errors are possible with advantages, you don’t have to be distracted between BP and C++, it’s easier to understand for beginners. And in our case they are not much slower, given the fact that almost all the logic is built on events.
We also managed to work a little on the graphics. Unfortunately, we didn’t have time to make assets, so we left some of them as dummies, some were made directly in the editor from primitives, and some content was borrowed from free Epic Games demos. Nevertheless, we managed to do some things ourselves, for example, a day and night system, post-processing for water and some materials for scene objects.
The plans for my streams also included problems that may arise during development. I specifically solved them live in order to not only show what young developers might encounter, but also how to debug their code, look for bugs and write their code so that the whole thing could be done twice as fast. Of course, I don’t have decades of experience in programming, and this affected the sometimes stupid mistakes I made. And I’m sure that many developers can challenge many points in the process of writing a game.
Naturally, this can hardly be called a full-fledged game, since there is no plot or goal in the game - only pure mechanics. However, I believe that the result is something to be proud of and that it fully reflects what the whole project was intended for.
List of everything we managed to implement in our game
Character
Object Inventory System
Equipment system
Contents in parts