Hey,
I think the complexity of this can vary depending on what you want to do. I’ve written 2 projects similar to what you’re attempting. Unfortunately I don’t have a good clean example project on hand, as my projects were fairly robust and I was still learning.
To answer your basic questions first. Yes, you can do this in BPs.
Really, I think the best thing you can do to learn is to learn about player controllers, characters, and putting variables on things just to start playing around and understanding how to get your characters to interact with things that can influence these variables.
That said, from my own experiences, I can give you a rough breakdown of what I went through during my learning process.
- To start, the first thing I wanted to do was create a global time system. I wanted age & time to play a factor for hunger, fatigue, death, ect. Originally I had this as object which sat in my level BP. I had some functions that would change time (I tracked smh:dmy) per tick. This was my first major muck up though, because in doing this I didn’t have a great way to communicate time data and time related functions to my player, their pet actor, or anywhere else, really. This also produced the problem of data persistence when transitioning between maps.
I’m starting with this point because I think the structure of the project is quite interlaced with solving this issue.
The solution that I ended up refactoring to was to move most of my data to some of the Game Mode classes. If you’re unfamiliar with these, it would be a good idea to read up on their uses. These are the classes you can set in your world settings, things like GameMode, GameState, PlayerState, ect.
Generally, I refer to this post to help me keep the functionality in mind. It kind of lays it out in a multiplayer sense, and to be clear there are specific inherited classes for multiplayer (they will be described as such). But the base classes can be used in single player with similar functionality. It’s mostly about global data persistence and game rules.
After moving my global clock and time-related functions into one of these classes, the ability to have player actions, maps, or anything really be able to manipulate time or get time became very easy. This also meant every map transition I didn’t need to save out and load the time data.
- This leads into the second part, which is of course the creature itself. The entire first part came down to 1 major thing, which is communication between classes. It’s not difficult to put some variables on a character and have some on collide function do something with those variables. But depending on the size of your project and how serious you want to be about it, it may not be the best way to handle it.
In my projects, the controller took player input and only applied it directly to the human character. The creature itself was an AI driven actor that was only influenced by the player character and followed the player character. But you never had direct control over it. At the same time, all my NPCs and enemies were the same creatures you could obtain. So I needed to think about how I could structure it in such a way that I could minimize code and structure duplication.
If you’re unfamiliar with programming, this may be getting too complex right out of the gate. But for my implementation, I chose to do this using data tables for default creature data. A base pawn class that contained the data structure and functions all creatures could use. Inherited classes from that for each specific implementation of a different creature (dragon, dog, ect.), which also utilized a row in the data table to set the default stats for that kind of creature. I extracted my creature’s data and put it in the custom PlayerState class so my creature’s stats could always persist through levels, ect.
From there it was figuring out where the best place was to put all the different functions that would ultimately end up changing something on my creature. Here are some examples:
- Maybe the “call pet to feed” function triggers when you press “x”, so the key press is exposed on the player controller which has a reference to the player character and the creature.
- It puts the player in a state of “waiting to feed pet” and it gives the pet a “move to” location which is the player.
- The pet will eventually move to the player character, and there will be an overlap event triggered
- Perhaps on the player character, when overlap is triggered, you check if it was the pet and what state you’re in. If you’re in “waiting to feed pet” state, you run the feed function which sits on your character class. It removes an item from your inventory, and then calls the creature’s “eat” function, sending in what you fed it, so it can adjust health or hunger or whatever accordingly in it’s data in the PlayerState object or wherever that data was being held
- At the end of the pet’s “eat” function, maybe it triggers another function on itself called “check evolve conditions”, which checks the current stats and applies it against some table of conditions to see if it matches enough to evolve into something new.
- If it does then you call the evolve function which gets the data table row of what you’re evolving into and if any stats are lower than the base it sets it to the base. otherwise maybe it adds 10% to each current stats. It changes out the model. Ect. Or since your data doesn’t sit on this class anyways, maybe that evolve function sits somewhere else and you can destroy the current actor, create a new one of the correct inherited class, and apply a mix of the defaults + the current data similar to above after that.
That may not be the best example, but I think that showcases that there’s a difference in the controller, physical actors, and data, and how it all can communicate between each other. Especially when learning, I don’t think there really is any right or wrong way to do it. Regardless of how you choose to do it, you will probably end up finding some case that causes you to refactor it - but that’s a great way to learn and increase your understanding of how all this stuff works together.
I hope this gives you an idea on what you can do to get started. I’m willing to keep discussing this more if you need any additional help!
Good luck!