Have you watched the blueprint youtube video’s on the Epic channel? That tells you most of things you need to know, I will admit though that when getting variables/functions from other objects they don’t explain it very well as they like to use the quick and easy shortcut of adding it to the level blueprint which I personally think is bad practice, that should only be used for level specific stuff. The tutorials do teach a lot of bad habits in regards to blueprints, it’s easy to see why they’ve done the tutorials in this way so they concentrate on the actual specifics of the tutorial rather than explaining why you’re doing something like you’re doing. If you don’t already know what you’re doing then obviously you’re going to follow the bad practices in those videos as that’s how you’re being taught.
Unfortunately blueprints are still programming no matter what the PR line is so if you don’t have a basic grasp of object orientated programming then it can be very confusing, especially in regards to things like getting variables from other objects.
I’ve managed to do everything I wanted in blueprints without having to resort to any C++ programming yet, this includes full space flight movement and AI so trust me it is possible and not that difficult once you understand a few basic concepts.
So I’ll try and explain a few basics in regards to communicating between objects with a practical example:
One of the most common usages of getting variables and data from other objects is during runtime i.e. in game you want to store an enemy object as a ‘target’ variable so you can then do various things like for instance, get the targets location in the world. This obviously can only be done whilst the game is running so you have to do something to find and store that object in game. There’s various methods of doing this, it really depends on the situation but for this example we’re going to use a trace.
So I’ve setup two blueprints that derive from class ‘Actor’:
Then place them into the level:
In the ‘Player’ blueprint I’ve created a couple of variables that we are going to use to store the same enemy actor but each in a slightly different way, this is to show you an example of ‘casting’, it’ll make sense in a minute
TargetActor - this has a variable type of ‘Actor’
TargetActorCast - VERY IMPORTANT has a variable type of ‘Enemy_C’. Basically it’s the ‘Enemy’ blueprint class we created a minute ago but when setting it as a variable type UE4 adds that suffix of ‘_C’
The ‘Enemy’ blueprint has just the one variable called ‘MyInt’ which is set as variable type ‘Int’, the default value is ‘69’.
So now the basics are setup lets do some exciting stuff…
Now just before I go any further I’m going to point out another bad practice from the tutorial video’s, they always show most things being done from the ‘Event graph’. This basically means you end up with a cluttered confusing blueprint that steadily grows in size and confusion. Bad idea, you’re better off using functions/macro’s/function libraries/ that hold the actual logic and then you just call these nodes in the event graph itself via events. You’ll see what I mean in a minute
So lets create a trace function in the Player blueprint to store that Enemy.
First off create a ‘Vector’ variable called ‘EndTraceLocation’, this is basically used for the ‘end point’ of the trace away from the players position, it’s just easier to amend this variable rather than going into the function and changing it everytime. I’ve also made it ‘editable’ so you can change the value of it within the editor once you’ve placed an instance of it in the level.
You may also notice that I’m putting all the functions and variables into ‘groups’, once your list of variables/functions starts growing it’s going to get very confusing very quickly so best to group stuff to keep it neat and tidy.
Now we create a function in the ‘Player’ called ‘FindEnemy’.
-Click on the entry node ‘FindEnemy’, in the ‘details’ panel in the bottom left of the window add a new ‘output’ and set the variable type as ‘Actor’. I’ve also called the variable name ‘Actor’ just to confuse you. This will then add an ‘Exit node’ in your graph with a return type of ‘Actor’.
Basically this function does a trace which returns the first actor object it comes into contact with.
This as you may have worked out will return any actor in the world (that is in layer ‘visible’)as everything in the game world is an ‘actor’ which isn’t really what we want in a real game situation, you can narrow down what the trace will pick up but that’s a whole other topic that we won’t get into now, just bear in mind when doing this for real you’d want to set up traces to work more efficiently than this by using ‘object types’ and ‘collision channels’.
Before we run our first test go into the ‘defaults’ tab of the ‘Player’ blueprint. Under the heading ‘Input’ set the ‘Auto Receive Input’ as ‘Player 0’. This is just an input hack so we can run input against this blueprint without creating controller class’s and all that sort of stuff. Useful for testing stuff but you don’t want to use this in game really
Back into the ‘Event graph’ of the ‘Player’ blueprint, create an event for key ‘F’. Drag your new function ‘FindEnemy’ into the event graph and hook up your ‘F’ key event. You see how now you’ve put it in that function your event graph is only two easily readable nodes rather than an absolute mess of wires and rubbish all over the place
I’ve also attached a print string after the ‘FindEnemy’ node so you can see what the value is printing on the screen.
I’ve done this test in a blank project so my actual player is just the floating first person camera, might be easier to use this for the test if you’re not doing so already. Anyway so lets hit ‘play’, move your player/camera so you can see both the ‘Player’ and ‘Enemy’ blueprints at the same time, then hit the ‘F’ key.
Now I honestly cocked this up on purpose (honestly) to show you that the trace will return any old object as we’re tracing by actor, see how the print screen is printing the value ‘floor’ as that is the first actor the trace hit.
So move the Player and Enemy blueprints a bit higher on the Z axis so the trace won’t hit the floor and run the test again, press F and see what the value printed is now (if like me you’ve been in an idiot and positioned the Player blueprint further down the X axis than the Enemy you’ll have to set your ‘EndTraceLocation’ to a minus value to get the trace to go in the right direction)
There we go it’s returning the ‘Enemy_C_1’ object now which is the unique name for that instance in the game, so if we store that value in our ‘TargetActor’ variable in our ‘Player’ blueprint we could then use that variable to get specific data from that particular instance of the ‘Enemy’ blueprint.
So let’s do that, open up the ‘Player’ blueprint and add a ‘set’ node for the variable ‘TargetActor’ and hook it up to the ‘Actor’ pin on the ‘FindEnemy’ node in the event graph.
I’m going to kill two birds with one screenshot here…in regards to getting the debugging information on the blueprint to look at a specific instance, whilst the game is running just choose the instance you want from the drop down box I’ve highlighted called ‘Debug Filter’. As there’s only 1 instance in this example you’ll probably find that’s been chosen automatically anyway.
So now you have stored a reference to that particular instance in the game you can now access it’s variables and functions!
but…and here’s where ‘casting’ comes into play, because your variable type is an ‘Actor’, it can only access the variables and functions from the class ‘Actor’, not any custom variables or functions that you put into the ‘Enemy’ blueprint itself. This is your object orientated programming, the Actor class is the parent class, Enemy is a child class of Actor, so Enemy has all the variables and functions passed down to it from it’s parent, so if your variable type is ‘Actor’ then it can only access the variables/functions on the parent level.
Lets do a practical example to demonstrate the above waffle:
In the ‘Player’ event graph place a ‘get’ node for both ‘TargetActor’ and ‘TargetActorCast’.
Click and drag from the pin on the ‘TargetActor’ node and type "location’ into the search bar (make sure you have ‘context sensitive’ ticked).
You should see a list like my screenshot where under heading ‘transformation’ you’ll see nodes like ‘Get Actor Location’.
Now do the same for the node ‘TargetActorCast’, type in ‘location’ again and you’ll see the same list of available nodes. This is because class ‘Actor’ has these variables/functions so therefore the child class ‘TargetActorCast’ has them also.
Now…to turn it around…click and drag the pin from the ‘TargetActorCast’ node and type in ‘MyInt’. You’ll see the options to ‘get’ or ‘set’ the variable ‘MyInt’ that you created in the ‘Enemy’ blueprint at the beginning of this massive waffle. So there you go, you can easily access variables from another object.
Now try the same with the ‘TargetActor’ node and you’ll see that it can’t find that variable ‘MyInt’…because it doesn’t exist in ‘Actor’.
Hope this is making sense now
So therefore sometimes you might want to just store a reference to the instance in game as ‘Actor’ as you only need to access the variables from the parent ‘Actor’ class. Sometimes you’ll want to store the instance as the child class itself so you can access the variables and functions you’ve created within that class. This is when you do a cast.
So back in the event graph of ‘Player’. Drag the ‘Actor’ pin from the ‘FindEnemy’ node and type ‘Enemy’. You should get a node saying “CastToEnemy”. Whack that in there, from that cast node you then set the ‘TargetActorCast’ to store the Enemy. So that cast is basically checking that the Actor class is the sub class ‘Enemy’. For instance you could create another blueprint derived from Actor, call it something different, run the same as above and get a reference to it with a trace. You will be able to store it in your ‘TargetActor’ variable but when you run the ‘CastToEnemy’ it will fail because it’s not variable type ‘Enemy’.
One last practical example using what we’ve already done, lets run a function from the ‘Enemy’ blueprint but we’ll call it from the ‘Player’ blueprint. I bet you’re struggling to contain your excitement by this point…
Open up the ‘Enemy’ blueprint and create a new function called ‘PrintMyBitchUp’ (you can tell I’ve lost the will to take this seriously anymore). This function will just print the value of the ‘MyInt’ variable. I’ve also created a string variable that will get added to the print log to make the log a bit more meaningful on the screen rather than just a number.
Go back to the event graph of the ‘Player’ blueprint. Add a new event for the key ‘G’ and then put a ‘get’ node for the variable ‘TargetActorCast’. Click and drag the pin from the ‘TargetActorCast’ node and type ‘Print’. You should see the function ‘PrintMyBitchUp’ available in the list! Click on that to add the node and then connect that up to your ‘G’ event.
Nearly finished don’t worry…
Play the game, now before you hit the ‘F’ key to fire off the trace, press ‘G’ first, see how nothing happens? This is because currently the ‘TargetActorCast’ is ‘none’ because you havn’t stored a reference in there yet. So hit the ‘F’ key to fire the trace to get and set the ‘TargetActorCast’, now press the ‘G’ key. You should get a print log showing that the function from ‘Enemy’ has been ran by being called from the ‘Player’ blueprint!
Hope that all made sense and hopefully helped you understand accessing variables and functions between blueprints. Any questions feel free to ask and I’ll attempt further waffling.