Referencing the map from PlayerController
Now that I'm starting to write the game logic inside the Player Controller, I want a way to reference my map from it, so that I can have access to its tiles, and eventually pass it to my Player ship and enemy ships, to let them perform their own logic and know what tile they're on, etc.
I found 2 ways to achieve this goal, one using blueprints and the other from code, and I want to show you both:
Blueprints procedure:
Since Player Controller and MainMap are both Blueprint classes and I created the map by dragging it inside the level editor, I don't have a reference of it inside the code as I have, for example, for the Player ship (do you remember the p1 variable?).
There's an entity, though, that knows everything that is created inside the level, and it's the Level Blueprint, that we already met when creating a custom camera. The first part of the procedure is somewhat similar to that one, so it should sound familiar.
While we have the MainMap selected inside the editor, let's go to Blueprints > Open Level Blueprint and right-click on the graph editor.
One of the first options will be "Create a reference to MainMap":
click it, and a box displaying "MainMapBP (from persistent level)" will be created. This is the reference we need. Now we have to pass it to our Player Controller, and from there to our C++ code.
There's an entity, though, that knows everything that is created inside the level, and it's the Level Blueprint, that we already met when creating a custom camera. The first part of the procedure is somewhat similar to that one, so it should sound familiar.
While we have the MainMap selected inside the editor, let's go to Blueprints > Open Level Blueprint and right-click on the graph editor.
One of the first options will be "Create a reference to MainMap":
click it, and a box displaying "MainMapBP (from persistent level)" will be created. This is the reference we need. Now we have to pass it to our Player Controller, and from there to our C++ code.
First, let's open the PlayerController blueprint and create a variable called mainMapRef:
In the right "Details" tab, let's change its type to MainMapBP:
You'll see the new variable on the left tab. Don't forget to click on the eye icon on its right until it shows a green open eye. This means that the variable is public, and we can access it from the Level Blueprint.
In the right "Details" tab, let's change its type to MainMapBP:
You'll see the new variable on the left tab. Don't forget to click on the eye icon on its right until it shows a green open eye. This means that the variable is public, and we can access it from the Level Blueprint.
Next, let's move to C++ and create a method called SetMainMap() that accepts a MainMap parameter and sets a global variable called map. Here is the code to add in PlayerController.h:
and in PlayerController.cpp:
Compile the solution, and since we used the UFUNCTION directive we can access the method from Unreal Editor; so let's switch back to it and right-click to find the function Set Map Reference. We already have our MainMapRef variable, and we can pass it to the function by right-clicking again and typing "Get Main Map Ref".
I chained this procedure to the Begin Play event so that it is executed only once, at the game start. As shown below, I added a timer in between to delay the execution by 1 second after the Begin Play event because I had an error sometimes, probably because some objects weren't ready yet when trying to access them:
I chained this procedure to the Begin Play event so that it is executed only once, at the game start. As shown below, I added a timer in between to delay the execution by 1 second after the Begin Play event because I had an error sometimes, probably because some objects weren't ready yet when trying to access them:
We're almost done, now we only need to assign a value to the MainMapRef variable. Save the blueprint and go back to the Level Blueprint editor.
Here, we first need a reference to the Player Controller, and we can get it by searching "Get Player Controller". The resulting box has an index parameter and we can leave it to 0:
The result of this function returns a generic Player Controller, but we need our custom MyControllerBP, so we can access its methods. That's why we need a cast.
The result of this function returns a generic Player Controller, but we need our custom MyControllerBP, so we can access its methods. That's why we need a cast.
When we cast something, we tell the compiler "look, I know you gave me a generic type of Player Controller, but I can assure you this is a MyControllerBP, so please give me access to its custom methods." This is only possible if our custom class is derived from the generic one, and this is exactly what we did when we created the MyPlayerControllerBP class, so the compiler won't give us any error. Right-click and type "Cast To MyPlayerControllerBP" to have this box:
The last step is creating a "Set Main Map Ref" (type it after right-clicking, you may have to uncheck "Context Sensitive" to show it), and pass it our previously created "MainMapBP (from persistent level)" reference, and then connect everything as in the picture. Here is the complete chain:
That's it, simply enough, isn't it? Now we can just access our map by calling the map variable inside of our code.
Now, the second way to get to the same result...
Code procedure:
AActor class gives us the GetWorld() method that returns a pointer to the world in which the actor is moving. This object has a reference to every actor inside of it, and we can search them using an Actor Iterator.
As the name suggests, this object iterates through all the Actors of the specified type. This is the syntax:
TActorIterator<ActorType> IteratorName(WorldObject);
If we use AMainMap as ActorType, we can iterate through all the instances of MainMap inside of our level. We obviously know there's only one instance of it, but let's loop anyay. Here's the code I added to the BeginPlay() method of MyPlayerController:
Bam! the variable map is now a pointer to our current MainMap object.
Bam! the variable map is now a pointer to our current MainMap object.
We had the same result as before, by simply adding a few lines of code. That's why I prefer C++ to Blueprints!
I thought it would be useful to show both approaches: usually when I have the feeling that things are getting overcomplicated, I wonder if there's a faster, simpler way to do it and sometimes (like in this case), my search gives me a good result. So never stop at the first try, and remember that nothing is written in stone, don't be afraid to rework your code and workflow if you think it's not optimized. In the worst case, you'll just roll back to your previous version and have learned something new anyway.
Thanks for reading, and game you next time!
Commenti
Posta un commento