System Core: Components¶
When we refer to the system functionality, the majority is implemented in BPC_Stats
, BPC_Attributes
and BPC_Leveling
. However, when we refer to the system core, that is the BPC_Stats
component,
because BPC_Attributes
and BPC_Leveling
can be considered as extensions to the main purpose of
the whole system, which is giving stats functionality to an actor.
Note
Each component work as isolated as they can, they just communicate the minimum to have as less dependencies between them as possible.
Lets say you want to use BPC_Attributes
and BPC_Stats
in an actor. When BPC_Attributes
gets one attribute modified, it will search if the owning actor has a BPC_Stats
component. If not,
it wont do anything related to the stats, but if it is found, it will tell the BPC_Stats
component
about that attribute modification (attributes can affect stats). For example if you get +1 point of
Strenght, you can set it to increase Attack Value by +15.
General Behaviour¶
In this section we will see that even though the components have different functionality, there are a few aspects that all of them share.
Settings Variables¶
Every component has a few variables to configure it. The names of the variables will vary a bit depending on the component, but the functionality is the same for all of them:
DataTable
: The datatable from which to extract the data.TableDataRow
: The name of the row data inDataTable
DebugEnabled?
: If true, the component will print messages on the screen showing possible errors and warnings when its initialized.
These settings are enough to setup the component, because all the data is in the assigned datatable,
so you only need to write the name of the row in TableDataRow
and the component will load the data
from there.
Widgets Refreshing¶
When communicating with widgets to send them data, all the components uses some of the interfaces showed in the Blueprints
section,
depending on the data it is going to send. Also, all components have a Widgets
variable, which contains the widgets that the component
is going to send data when some events happen.. There are also 2 common functions:
AddCustomWidgets
: Before any component is initialized, we need to pass it the widgets it has to refresh as said before, so this function just adds widgets to theWidgets
variable mentioned before. For example, theBPC_Stats
component inBP_DemoCharacter
will refresh theWB_DemoDebugStats
and its stats subwidgets, but it doesnt refresh the attributes or the current experience widgets. Its a simple way to link a component with the widgets you want.RefreshWidgets
: Its called when the component has loaded the neccessary data, either from saved before or by datatable, to refresh its widgets with all the data it has. For example, theBPC_Attributes
will refresh every attribute and the available attribute points.
Save / Load¶
All the components have built-in save / load functionality. They can save and load data either by GameInstance
or using a SaveGame
class.
Note
If you dont know about GameInstance
and SaveGame
classes, here you have a nice YouTube
video from Mathew Wadstein explaining the GameInstance
class and this other
link explains perfectly how the SaveGame
class work.
There are 4 functions used for this purpose:
CreateSaveData
: It gets the current component data and creates the proper struct (you can see it as a packet) with all the data, depending on the component:s_AttributesSaveData
,s_StatsSaveData
ors_LevelingSaveData
.SaveData
: It will send the data created byCreateSaveData
to the corresponding container that will store the data with an “identifier” of that data, given by theSaveName
input. The container will save the data with the value from theSaveName
variable, and it will be different depending on theToGameInstance
boolean input. If it is true, it will get the game’sGameInstance
object to send the data there. Remember that this class is persistent across levels, so if we go from one level to another, we can send the data to that object and retrieve it in the new level. If the boolean is false, then it will get the.sav
file on disk (if it doesnt exists it will create it) to save the data there.LoadData
: Exactly the inverse of theSaveData
, it will get the data that has the label given by the inputSaveName
assigned to it, also depending on the container it will ask for that data toGameInstance
or.sav
file. When it gets the data it will update the component variables with it, and it will also set the booleanHasLoadBeenSuccessful?
to true (false if no saved data was found). And if no data was found, what do we do? Here theLoadTableData
function comes to the rescue!LoadTableData
: It will be called in bothBPC_Stats
andBPC_Attributes
only when the component hasnt received saved data before. If so, the function will take the value ofTableDataRow
mentioned before to extract the data from the assigned datatable, just like default data. InBPC_Leveling
it will be called always but the value ofHasBeenLoadSuccessful?
will branch inside the function. Thats because we need to construct the needed experience for each level, because that data is not saved by theSaveData
function.
Note
Typically you would only save data to the .sav file disk when the user clicks on some menu button to save the current game state or if you have a menu button that closes the game, you can save there to the disk before closing the game. Anyway, it depends on how have you configured the saving and loading options in your game.
Consistency¶
Every component has a function called CheckConsistency
. This function will tell you if there is any unconsistency in the component settings.
For example if the variable DataTable
doenst have a valid value, or if the row assigned in TableDataRow
doesnt exist in the datatable if you
have assigned a valid value to it. It will check more consistency errors depending on the component, because each of them has its own consistency settings.
This function is also callable from the editor, that means that you can call it without running the game. To try it just go to the BP_DemoCharacter
blueprint, select one of the system components in the left side and you will see at the component details that you have a button with the same name as
the function, just click it.
Initialization Pipeline¶
At this point, you have seen a lot of functions and what they do, so the last thing to clarify is when some of them are called and in what order. Typically the initialization process is done in the event BeginPlay of your character or player controller. In the demo content, it has been implemented in the character. The steps would be the following ones:
- Get the component and call the function AddCustomWidgets and pass it an array of the widgets it has to refresh.
- Call the functin LoadData and give it a SaveName value, for example if you have a main menu and the user can give a name to the character, that would be a perfect value to put here.
- Call the Initialize event of the component.
Every component has an event called Initialize
. The pipeline of this event is the following one:
- Searches for other system components in the owning actor. For example
BPC_Leveling
will search forBPC_Attributes
andBPC_Stats
to perform some actions when the owning actor levels up. If the component hasnt found other components, nothing happens. - Calls the function
CheckConsistency
if the variableDebugEnabled?
is true, which will print some strings in the screen if there is any configuration error. - Calls
LoadTableData
ifHasLoadBeenSuccessful?
is false (with the special case of theBPC_Leveling
component mentioned before) to load the default data from the proper datatable. - Refresh all the widgets with the loaded data.
Tip
The previous process is the same for all the components.
Now that you have understood the main idea about how the components work, lets see the details of each component in the next sections.