Unreal definitely has a problem with level-of-detail in their documentation.
If I had to summarize, I’d say programming 101 boils down to four bullet points:
This is the obvious one: the if 'if this then that" part. Most people get this intuitively, and up to a point Blueprint helps with that – it looks kind of like a flowchart, so you can follow along a line and see “if this number is larger than 1, go here; if not, go there”
The slightly less obvious part of logic as flow control. Not everything is an ‘if then’ – often you want to make something repeatedly. That’s usually known as looping – for example, you might “loop over” a list of email address and send a spam message to everyone on the list. Or, you might loop over the list, check to see if they have bought something from your store in the last six months, and only then send them a coupon. In both cases your ‘flowchart’ now has a loop inside it. In blueprint that is represented by a foreach node.
This is the stuff you’re working on – a list of numbers, an unreal object with properties, and so on. You’ve basically got many different ways of representing the “real” world data is how you present that to your program . Your logic operates on your data , using it to make decisions and either create new data or change existing data.
This all seems pretty obvious and in some ways it is. However much of the real work in programming is about finding the right way to turn the problem set into data. For example if you were writing a program to deal with customer data you’d need to store addresses in a way which handles zip codes and street address in the US, but districts and postal zones in Japan. If you making a decision about where to put effects in an unreal scene, you need data which lets you distinguish between good locations and bad ones – for example, you don’t want to spawn your effects under the floor or inside walls.
Logic and data go hand in hand: if the logic and the data work well together, your program (or blueprint) will be short and clear. If they don’t match up well the logic usually ends up being super complicated – which makes it very buggy. You’re always looking for way to organize your data that makes the logic clearer.
Sometimes you’ll break a problem up into stages where you collect data (say, looking at all the characters in your game and bucketing them into ‘allies’ and ‘enemies’) then acting on that data in a second step ( “if the numbers of enemies is 2 times or more larger than the number of allies, run away”) . More often than not there are several steps along the way, but it’s good to break a problem down into simple steps with simple answers that you can understand easily. The most common source of problems in any programming is that people get overwhelmed by the complexity of trying to do everything at once.
A critical aspect of data is what programmers call “type” – that’s a term of art for that means ‘here’s a bunch of data which always has these properties’. Type is how you know that an object name contains letters and not numbers, or that an unreal light is a light (with properties for color and intensity etc) and not a vehicle or a physics collision mesh. Programs usually rely on type information to keep things organized; in blueprint that is why you can’t connect anything to just anything else – when you run a wire into a “multiply” node that wire has to represent some numbers.
It’s very obvious if you look at blueprint examples that even a fairly simple bit of logic and is quickly hard to follow. Say you want to figure out where to shoot that missile – you need to:
- check your ammo supply
- If you’re out of ammo, play the ‘empty magazine’ sound and exit
- get your own position
- find the right enemy
- Did we find any enemy? If not, exit
- get the enemies position
- check to see if there’s something in the way
- if there is, go back to step #2 and try a different enemy
- create a missile object
- set the missiles destination using the position in #6
- play the fire sound
- deduct one from our ammo supply
- done! Now exit
That flow is complex enough – but most of my bullet point are not unreal built-in abilities. Each one probably takes several individual operations – my 13 logical steps might take 50 or 100 individual bits of blueprint, which would be both overwhelming to look at and also very hard to change or upgrade later.
To handle that, it’s a good idea to break up each of these logical tasks into what programmers call ‘functions’ or ‘methods’. In blueprint that’s a single block that has some inputs and outputs . The inputs are the data that you need to do the job and the outputs are any new data that you want to pass along to some other function down the road.
Functions are useful for lots of reasons – they help you organize the layout of what you’re doing to make it easy to read and follow, which just as important as the actual technical inner workings of what you are doing; you’ll spend way more time re-reading and trying to understand your code than you ever spend creating it!. But functions also help you spot what kind of data you need in different parts of what you are doing. One to the classic issues any programmer faces is managing data (see above!) – if you can’t figure out what inputs and outputs you need for a function that’s a sure sign you don’t quite grasp the real problem yet.
Sometimes there are functions without inputs or without outputs (never without both, however!) A no-input function just does gives you some data back – for example, maybe it generates a random message text. A no-output function usually does something to existing objects - maybe it adds an item to a list, or sets a property on some Unreal actor.
Which gets us to the last big concept: state. State is the current status of all the data in a program. That might be something really simple – in a little program that grabs information from an http server the ‘state’ might just be the address of the server, the name of a file on disk you want to dump the information to, and maybe an optional stored password that you might have given the program to access the website. In something big – like a character controller in Unreal – the state is all of the data that defines the situation – everything from the current game mode to the character’s position in the world to the whether or not the character has a health bar over his head.
John Carmack – the guy behind Quake and Doom – once said in his understated way that “all bugs come from the state of the program not being quite what you think it is”. Once you leave the realm of the trivial example, everything is about managing state. Some of that is simple stuff: When you spawn an enemy, you manage it’s ‘state’ by positioning it in a good place, by making sure it has health and ammunition, by turning on its AI, and so on. But if you make some object that has to live for a while – say, you attach a blueprint of your own design to an object to make it rotate in place – then you have to make sure you understand the state of both the Unreal object you’re using and any data you are hanging on to. For example, you might want your thing to rotate slowly at first and then speed up – you’ll need to save the current speed somewhere and then keep track of it. You might have a function that adds or removes speed from the rotation … so what happens if the rotation speed gets set below zero? Does it just stop? Does it rotate in the other direction? Are you dividing a number by the speed – since math does not allow division by zero, that will be a problem.
Because confusion about state is the root of most bugs, you need to pay attention to both the data and the functions that use the data to make sure your state is as simple as possible. The more things you’re trying to manage, the harder life becomes. So making programs or blueprints is usually an iterative process of subdividing what you’re doing into small, simple chunks without a lot of state. At each level of what you’re doing you want to be able to see and understand the parts you’re looking at – once you are having trouble, it’s probably time to see if you can partition the problem a little bit.
A lot of the time the right way to break up the problem is to create a little sub-universe of data and functions that work on that data. Programmers usually call that a ‘class’ or a ‘struct’ – basically it’s a way of grouping some information and some functions that work on that information together. Classes are an organizational tool – you can do most of what you need to do without them – but they are a big help in keeping the chaos at bay.
There’s obviously a lot more: every environment – whether it’s a blueprint or a script running in Maya , or a huge monster program with 3 million lines of code like Unreal – has its own special rules and gotchas. But basically you need break things down to the four basic concepts. You’ve got logic that decides what to do and data that your logic uses. Typically you bundle that logic up into functions that do one job at a time to keep from going crazy. The sum total of all your data and the way your functions use that data is your state – changing any part of the state changes the behavior of the whole system.
Hope that helps…