In this issue:
My view on good games, development and business
Unreal Engine Blueprint nuances like functions, macros and macro libraries
Unreal Engine networking and multiplayer basics
A Good Game, Good Game Development and Game Business - How I See This Equation
I have been involved in games and game business for a long time, although I generally do not make a fuss about it. I have a very broad view of games because I have been involved in tabletop RPGs, board games, strategy games, miniature games, and video games. Also, I have made lots of research in games and their history which is a really fascinating subject itself. In many ways, it has been wonderful to see how games have risen from obscure fringe culture to mainstream entertainment - and in many ways, it has not been so wonderful.
In general, I personally think a good game is something that gives lots of possibilities for a player and does not restrict a player's imagination. In other words, it empowers a player to do things that even the designer of the game has not thought of. Although the game has a clear focus that game designer has built, the good game does not restrict player into just that "cardboard box", but offers a stepping stone to much more - into an area where the player himself will extend the universe that designer has built. In a way, I see a good game as a set of prebuilt stories and a set of tools to build something else on top of that. Even better, if a player can play the game in a totally different way the designer though and still have lots of fun.
Unfortunately, it seems that current trends on commercial and "respected" game design are totally opposite. What current game media and development scene tries to force-feed on budding game designer emphasizes much more restrictive approach, where the game is seen as a fixed set of blocks which player can organize only in a certain way, nothing else is allowed. You often hear designers say "you have to lock the features of the game" while they try to minimize the "core game" into a neat loop. If someone tries to put extra features into the game, they are quick to shout "feature creep!". It is with great sadness that I look these current games - especially mobile games - that are ridiculously stupid offering nothing for your imagination because everything has been "chewed" so much that there is no flavor left. They are just dumb time wasters.
It is interesting to note, that during this evolution - or perhaps degeneration - of games, also the game development has evolved into the big-time game business. While during the '80s and '90s games were usually developed and produced by moderately small studios - sometimes only by one or two developers - we are now living in a world where many games are produced in factory-like environments, where the so-called pipeline may involve 100 - 200 developers, and most of them have specialized in a very narrow field of expertise.
In order to support that kind of development, the games have to make money, lots of money. That has led to thoroughly analyzed and planned monetization of games. And here is the link to the current status of game design preferences. In order to support a thoroughly planned monetization - like IAPs, loot boxes, skins, etc - we have to know exactly how the game is played. There is no room for player imagination or extending the play beyond the boundaries of a fixed set of player actions. Only then are we able to know where to put all those monetization tricks, and how they should operate in order to maximize the profits. The way I see it, many modern games are just like those one-armed bandits, nothing more, nothing less in spirit.
All that compact monetization turns the scales and favors business-side of game publishing. As long as you concentrate on the games, you are free to express yourself in any way you like. But when the business comes into the picture you will gradually lose that freedom. It does not matter whether you are a lead game designer or even a CEO, you are not free because you are restricted and enslaved by the business rules. Whatever you do, whatever decisions you make, have to serve the business goals.
Of course, you may argue that you have to make money if you want to eat. That is true, of course, but I believe that you should dip your foot into the business sea only a little - enough that you make a living, without sacrificing too much of your true goals to make good games as just games. Nowadays it seems that the success in the gaming industry - especially here in Finland - is measured only by how many hundred million euros your game earned this quarter. Artistic or technical quality is irrelevant, only money matters.
On my point of view, if you make a living with your games, it is enough to call it a success. I think there is an alternative for an indie game developer, but it means living outside the scene, a very harsh life defined by your own rules. It just depends on one thing - do you focus on games or do you focus on business. But everyone is free to make their own choice, I am no judge of that.
UE Blueprints in depth: Graphs, Functions, Macros and Macro Libraries
I cannot even count how many times I have heard people say "Blueprints are not programming", "You cannot make a proper game with Blueprints", "Blueprints are bad for performance" or "You cannot make a whole game with Blueprints only". All of that is just hogwash, and express only a symptom of an attitude problem - elitism, prejudice or total ignorance - in the claimant.
Blueprints are as much programming like C++, C#, Python, Java or any other programming language. They are just tools to achieve the goal, and like any tool, they do not define its quality, but it is your skill in using them that defines whether the outcome is good or bad. Programming with C++ or C# does not make your program superior. You can program horrible code with C++ if you cannot use it and do not understand the principles of programming. You can make horrible code with Blueprints, but you can also make the code as good as any with any other programming language. Using Blueprints does not make you any less programmer, you are just using different tool - but you have to learn to use that tool.
Unfortunately, many developers do not bother learning all the good aspects of Blueprint programming and restrict to a very simplistic set of methods, thus making clumsy programs. You can make whole games - even multiplayer games - with just Blueprints, although there are certain special areas which are impossible to do (currently) with Blueprints. Also, when you do things right, the performance of Blueprints is not that far behind C++. Actually, in most situations, there is no need to use C++. But eventually, you will encounter a project where C++ is the best choice.
Benefits of Blueprint
Compared to C++ programming, Blueprints have many advantages, especially for small teams. Blueprints are relatively easy to learn and understand regardless of your role. Even a non-programmer can easily visualize the flow of logic. Also, since they are built in Unreal Engine itself, there is no need to install and configure any extra tools or editors.
Prototyping with Blueprints is amazingly fast. A competent Blueprint programmer implements functionalities for several actors in just minutes. You can prototype a whole game idea in a day. Actually, it took me about a day's amount of work (in total) to make ROT-8 prototype with movement and some game logic. But keep in mind that I really mean just prototyping a game idea, not a full game.
In small teams, Blueprints really shine, since everyone is able to participate in the development of game mechanics and features. If you want to test an idea, you can do it yourself. If it doesn't work you probably lost only minutes. If it works, then you or someone else can fledge it out later, and integrate it with other game logic.
Blueprints are deeply integrated into everything in Unreal Engine, they are not some 3rd party tool. Same Blueprint logic works in actors and game mechanics, animations are done as Blueprints, and even levels have their own level Blueprints. Materials for models are defined in very similar node based blueprints, although they are not exactly the same.
Structure of Program and Blueprints
If you want to be a good Blueprint programmer, then it is necessary to understand the structure and hierarchy of Blueprints. There are lots of similarities with C++, presentation is just different. I assume that you know the basics of Blueprints and have used them a little, at least. I am not going to repeat the basics from UE documentation, but give a quick and condensed view of building blocks I consider important.
As an example, we can imagine a game that is done with Blueprints only. It means that you will have several different Blueprints representing different classes, actors and game mechanics, like level Blueprints. Just like in C++, it would be absurd to cram everything into just one actor class. Instead, you will chop everything into clear, well-defined pieces, that work together. Blueprint programming has all those mechanisms that are familiar from other programming languages. You have a class inheritance, interfaces, structs, enums, etc.
But even within a Blueprint, you have to organize your code as different kinds of graphs. Although the compiled code does not care about the beautiful aspects of the code, organizing the code helps you and your team's work process. It is easier to maintain, modify and develop further when everything is properly organized. It will also make your Blueprints more modularized which helps code re-use and makes component interactions less complicated.
Construction Script
Construction Script is a graph that is called immediately after the actor is spawned and its components have been set up. If you are experienced in other programming languages, you may think that it is like a constructor of a class in C++ or C#. Well, yes it is, but in some ways there are differences. First of all, each Blueprint may have only one Construction Script. Second, Construction Scripts have no arguments as such.
If you want to set some variables in the Blueprint with some value (like you would as a constructor argument value) when the instance of that Blueprint class is initialized, then you have to expose that variable in the settings of that variable. However, exposing a variable does not need constructor - it works even if you do not implement anything in the Construction Script.
Construction Script is called only once. If the actor is in the scene when the level is loaded, Construction Script is called immediately. Otherwise, it is called when an actor is spawned in the game. After that, it is not called again, except when an actor is destroyed and spawned again. Although Construction Script is a similar graph as Event Graph, it has some limitations. There are several nodes that you cannot use in Construction Script, and due to the timing of things, some values and references may not have been properly set up yet. Usually, Construction Script is used to spawn and attach components and other setups.
Event Graph
Event Graphs are the main program of every actor. A Blueprint can contain several Event Graphs, as many as needed. Although you can use just one, it is a good idea to use several Event Graphs and organize your mechanics and features into groups.
As the name implies, an Event Graph handles different events and actions that affect and control the Blueprint actor. Event Graph is like a canvas where you put your game logic and behavior using in-built Events, Custom Events, and Input nodes.
Event Begin Play is called automatically when the play begins for the instance of an actor, and everything in that event is processed before the Tick is activated for that actor. Then Event Tick starts to run, and it is called every frame unless it is disabled. Besides these two, there are several other in-built Events that are called if the actor hits something, overlaps other actors, etc.
Additionally, you can create your own Custom Events, and name them appropriately. Any Event may have a number of inputs, just like functions in other programming languages. But - this is a crucial difference - Events do not have any outputs, meaning that they do not return any value. So, if you need to have a return value, then you cannot use an event.
It is a good idea to organize these events into separate Event Graphs. For example, you may put Begin Play logic in one, especially there are complicated mechanics. For Pawns and Characters, it is natural to put all Input Events (that come from Player Controller) on separate Event Graph. Hits and overlap events in another and then your custom events grouped by their type.
Functions
As I said above, Events do not return any value. They have other limitations too, like the one that they are run within the scope of the whole Event Graph. If you have used other programming languages, you will immediately understand that there are cases where this just does not work. That is why Blueprints have Functions, too.
Blueprint functions work in the same way as in any other programming language. Functions and events have a lot in common, but the functions have two advantages. They can return a number of values, and they have their own scope. Functions take any number of inputs and they can return a number of values. It is a common and good practice to use functions for simple processes that you use repeatedly in your program. It is even better to practice to make a function very simple and general, and then you can combine and chain functions to other functions and so on.
Since functions have their own scope, they may have their own variables that are initiated when a function is called and disposed of when the function has been processed through. Unlike Events and Custom Events in the Event Graph, these local variables exist only within the function itself. A function can use the same variables as in Event Graphs, too. Also, each function call is a separate instance, with separate local variables, just like in C++ and C#.
On the other hand, events can only use global variables - and there is only one instance of each - which means that if an event is called several times within a frame (actually they are not run simultaneously, but one call at a time), and that function uses a value of a variable and then modifies its value, then it will use that modified value on the second function call and so on.
Collapsed Graphs, Macros, and Functions
When your Blueprints grow it may happen that organizing everything with just several Event Graphs and Functions is not enough. In this case, you can use collapse several linked nodes, and create a Collapsed Graph, Macro or even a Function. Basically, all of them are quite similar, since they have a number of inputs and outputs, but they have some differences, and understanding those differences will clarify when to use which one.
A Collapsed Graph is just a cosmetic way to make a Blueprint more clear to read. You mark several linked nodes, collapse them, and then they will be shown as just one node that you can name any way you like. You can always "zoom in" that node, look what is inside it and modify it.
However, you cannot actually reuse that collapsed graph unless you physically copy everything in it to some other place in the Blueprint. If you want to re-use that node structure several times in your Blueprint then you have to collapse it into a Macro or a Function. The function works exactly like described above, it does not matter whether you collapse an existing node structure into a function or start doing it from scratch.
Macro is almost like a Function except it does not have local variables. Macro takes a number of input channels, and it has several output channels. What makes Macros interesting, is that you can implement Macro as an execution branch. Macro has one Exec input, but it may have several Exec outputs. However, the usefulness of a Macro is limited by the fact that it does not have local variables like a Function (it operates within Blueprint scope), and you cannot call it from other Blueprints (unlike Custom Events and Functions that may be called from other Blueprints if you have a reference available).
Macro Library
Macros described above may be used only within the Blueprint they were created. Other Blueprints cannot use them unless you copy them into another Blueprint. But there is another way. In the Content Browser, you can create an asset called Macro Library. It is a container Blueprint that will hold a collection of Macros, and they are then usable in all Blueprints in your project.
You create Macros in the Macro Library just like any other Macro. Then you can add them as nodes in other Blueprints, just like any other node. It will show input and output pins. At this point, it is important to understand, that there are limitations what kind of Macros you should put into a Macro Library. There is no point to put there anything that modifies Blueprint variables. First of all, such variables may not even be present in the Blueprint itself, and even if it were, you could create unwanted side-effects. Basically, you should put only such structures that take some input, and return something based on that input, or branch execution.
Other tips
To make your Blueprints more organized and readable, you should also group your variables, macros, and functions. This is done by defining their Category. You can create your own categories just by typing their name in the Category field. After that, it will appear in the drop-down list.
Commenting your Blueprints makes them easier to understand. You can select a group of nodes, right-click, and then select Create Comment From Selection. You can color code these comments, based on their purpose in the execution.
When you create Custom Events, Macros and Functions think very carefully when you write anything that modifies the values of Blueprint variables. If you don't, there is a possibility that you create an unwanted side-effect, which may be hard to pinpoint later. Remember, that you cannot know in what order CPU will process all Blueprints, function calls and events. You can modify the Tick group of a Blueprint, but otherwise, there is no way to tell.
When you use all these programming elements shown above, your Blueprint based game will be easy to understand, modify and maintain, especially if you also use comments. It will be as elegant as any C++ based project, and even non-programmers may understand its execution logic (at least the chance is better).
Unreal Engine Networking and Multiplayer Basics
It is quite interesting how totally different views some developers have on multiplayer games and networking. Many think that it is extremely difficult to develop and understand - comparable to magic, occult knowledge, and alien technology. Then there those who think that any beginner can create a multiplayer Battle Royale or MMORPG in a month just by himself. Both views are quite ridiculous.
In reality, network programming and multiplayer is a bit complex, but otherwise, it is just programming. All it needs is a solid understanding of how server and client architecture works, and how data is synchronized between server and all clients. Of course, there are different cases. Even a single player game may contain some network features. There may be global leaderboards and other features (like statistics collection) that demand simple networking between the game and the global server. This is a very light case of network programming where data packets are transferred only occasionally.
True multiplayer needs constant data flow between clients and server. Server controls what happens in the game, and then synchronizes the state of the game to all clients. It is quite easy to understand, that controlling a multiplayer game with only 2-8 players is totally different than having 2000 players simultaneously in the same game world. MMO games and large game services need much more complicated server architecture than a simple game with a couple of players. In a simple multiplayer game, one of the clients can act as the server, but with thousands of players, you need clusters of servers, databases, and other networking solutions.
If you want to understand and learn to develop multiplayer games, then you should understand how the principles of a single player game work, first. Multiplayer expands those principles and adds some more. If you have used - or at least tried to use - Unity for multiplayer and networking (like I have), you have noticed that it is very tedious to develop. If you want to learn multiplayer development on Unreal Engine, then you will have a pleasant surprise. Unreal Engine has many networking and multiplayer features in-built, although you still have done some implementation yourself, of course.
Replication
Unreal Engine has a networking feature called Replication. It does all synchronization of necessary objects under the hood. You just define an actor or its component as replicated, and Unreal Engine does synchronize it between server and clients. So that's all? You just click and define objects, put a replicated checkmark on, and you have a multiplayer game? Not really. But when you implement your game mechanics in a certain way, the synchronization is done for you by UE itself.
It is really important to understand, that replication is not a miracle gizmo that does all the work for you. If you do not implement all the custom events and functions in appropriate actors in a certain way, then the things will not happen the way they are supposed to happen. I will clarify the basics of UE multiplayer structure, but the best way is to learn by doing. I will continue this subject in future newsletters, and dig even deeper since this is a really big subject.
Unreal Engine uses server-client architecture, where the server is authoritative. Everything goes through the server, which checks and validates data, gives an appropriate response, and only then things happen on clients. Whenever you give input for your character, e.g. press the Jump button, that input is sent to the server, which should check whether the character is allowed to jump, and if it is, what happens. If it really is allowed to jump, then it will jump both on the server and every client where it is replicated.
Multiplayer and Blueprints
There is one common misconception, which usually stems from prejudice and ignorance. I have often heard a claim that you cannot make a multiplayer game with Blueprints only (well, I often hear the claim "you cannot make a game with Blueprints only"). This just simply is not true. But this depends a lot on the type of the game. Blueprints have all the necessary nodes and mechanisms to make a multiplayer game. You can actually make a simple multiplayer demo in a couple of hours with lobby, server hosting, sessions, etc.
However, there are cases where C++ is the only way, especially if some really complex networking mechanism is needed. One such is Remote Procedure Calls (RPC). Although Custom Events in a Blueprint can be defined as replicated, you cannot do the same for Blueprint functions. Blueprints can use functions that have been defined replicated in C++, but currently, Blueprints do not allow you to define a function as replicated.
When Blueprints are just fine for a multiplayer game that supports 2-4 players, I would not recommend it for a game with 100 or more players. But in any case, it is also a question of your skills in the overall optimization of game performance.
Components of a Multiplayer Game
First, you should understand about UE replication that it works from server to client, not vice versa. Any changes made on the client instance of an actor (whether replicated or not) stays on the client. Even if you spawn something on a client, it will exist only there, and other clients do not even see it. If you want to make a change on a replicated actor or spawn a new replicated actor to the game, then it has to be done on server. When such a change is done on server for a replicated actor, UE handles the rest and synchronizes that change (or new actor) to every relevant client.
As you may have guessed from above, the replicated actors and components have an instance on the server and every client, but the server controls everything. It is easy to visualize in a way that the replicated actor is created on the server and then copied to all clients because it co-exists on all of them. Even the player pawn or character is handled this way. But there are other components, and some of them exist only on the server, and some only on the client.
What makes Unreal Engine multiplayer game development fascinating is the fact, that the components of a game are just the same as in a single player game. They are just implemented in a way that makes synchronization possible with the UE replication mechanisms. Just like a single player game, also a multiplayer game has GameInstance, GameMode, GameState, PlayerController, PlayerState and Pawn (or Character), HUD and Widgets, some of them multiple times.
In a multiplayer game, it is important to understand the concept of the owner or owning client. Many nodes and actors need this information. For example, when certain actors or widgets are spawned, you should also define who owns it. This differentiates how things are handled on the server and all clients. Although an actor coexists (as a replicated actor) on different clients, there are actions that are processed only on owning client instance.
GameInstance
GameInstance is created when the engine is started and exists until the game is shut down. Each client and server has its own unique GameInstance. GameInstance is always there and it can be used to store data, that needs to persist even when a player transfers from one Level to another. Only owning client can access its GameInstance, meaning that not even the server can access client's GameInstance.
PlayerController
Each client has one PlayerController. That PlayerController exists both on the server and owning client, but it is not replicated on other clients. In a way, you can think that PlayerController is an abstract representation of the player, and through it, he controls his Pawn or Character which is the real manifestation of the player in the game. PlayerController is the first class that the client owns.
PlayerController links players input to game mechanics that happen on the server. Although most of the real input mechanics are implemented in Pawn, it could be implemented in PlayerController. Input is always passed through PlayerController, and if it is not used in it, then it will be passed to other actors, like Pawn. Since there could be a different kind of Pawns in the game, you could implement in PlayerController those inputs that are common to all of the Pawns, and then implement type specific inputs on each Pawn.
HUD and Widgets
Each player has his own view of the game, meaning that HUD and UMG Widgets exist only on owning client, and other clients have no access to it.
GameMode
There is only one instance of GameMode and exists on the server. If any other client tries to get access it, a null pointer is returned. As its name implies, GameMode is used to define the game and its rules, and store any information that clients should not know or need not to know. But do not make the mistake, that you try to put all your game mechanics into game mode since it really is accessible on the server only. Any mechanism that is needed for clients directly should be put on other classes.
GameMode has many built-in functions, but you may override them and insert your own logic on top of them. There are also built-in Events, and one really important is Event OnPostLogin, which is called every time someone joins the game. There are also mechanisms for starting and ending matches and other nodes to control the flow of the game.
GameState
Just like GameMode, there is only one GameState, and it is replicated to every client. As such it is used to keep up-to-date data about the game in general - like scores - and basically, anything that every client should have access to. It also contains a list of all connected Players (actually PlayerStates)
PlayerState
Each player client has its own PlayerState which is replicated to all other clients. Usually, PlayerState is used to store data about the player. Since it is replicated, some variables could also be replicated, so that other players have access to it. This is really handy for certain features since the client does not have to ask that information from the server. For example, if your HUD should show a hovering name widget above every player, you can make player name variable replicated, and UI widget can access it directly.
Pawn
PlayerState and PlayerController are linked into a Pawn or Character. Actually, PlayerController possesses a Pawn and controls its actions. PlayerController may change the Pawn and possess another Pawn. Pawns and Characters are replicated and exist on the server and all clients, but they do not have to have the same features on everyone. For example, in a first-person game the Character may have only hands shown on owning client, but a full animated body on all other clients.
Since Pawn may change during play, you have to think carefully what data should be stored in it. As long as PlayerController possesses a Pawn, you have access to it directly. When you switch it to another Pawn, the situation changes, depending on whether it is destroyed or not, and do you keep a reference to it or not. PlayerController and PlayerState will persist, but only until a new level is loaded for owning client. Ultimately, only GameInstance persists throughout the game from one level to another.
Actor Replication
Remember that you can make your own custom versions of these base classes by inheriting them. You could do a lot with just those classes, but every game needs more classes, your own custom Actors. For them, you have to decide whether to replicate them and how. Some classes are defined automatically as replicated, but it is not necessary for everyone. Also, you have to decide what parts of an actor are replicated and how.
Remember that replicated actor is not really replicated to all clients unless it is created on the server. Anything spawned on the client stays on the client only. But you can fine tune the replication for Blueprint components and variables. You can make a variable replicated or not, depending on whether it is necessary.
Blueprint variable replication has one extra option called RepNotify. Variable with this definition has a special function, which is called whenever the value of the variable is changed. This happens server and clients. So the variable is not only synchronized, but you can trigger some other functionalities, too.
Final Words
All this should give you a condensed breakout of UE networking system. I recommend that you also consult the UE documentation. I will explain more details on UE networking in my next newsletter and show some examples. My plan is to make a simple demo game, and use an upcoming Moon asset pack from Kimmo Kaunela as an environment, as shown in the images above. This will be a lengthy subject which probably spans over several newsletters, assuming that readers are interested.
I have several other subjects prepared, too, so if this does not interest anyone, I can easily switch to mathematical details of machine learning. Anyway, in the future I will show more details about my AI projects, there is some Planetrism and ROT-8 stuff, advanced techniques to work with Git, etc.