Monday, January 16, 2017

Creating townsfolk schedules


    In order to make the game world livelier, the inhabitants of Gleaner Heights have to be on the move every day. Maybe they will leave home and go to the town square in the morning, then enjoy a meal at the Diner, then visit a friend, and so on, until nightfall approaches and they return home to rest. We've seen this in games before. The older Harvest Moon games did it. Modern AAA open-world RPGs do it. And now I'm doing it. Unsurprisingly, this section of game design is rife with challenges as well.

A few characters from our main cast. Sorry for the messy upscaling.
    I'll remind you that I am using Game Maker: Studio to develop the game so some technical jargon will refer to this specific application, but the gist of the article isn't confined by it, as always. Any software development tool supporting data structures can be used to implement the ideas discussed below.[*]People who write in programming languages utilizing classes and pointers are probably laughing so hard right now after having skimmed through this article that I can probably hear them from here. But this is Game Maker we're talking about, and we'll have to make do with whatever hand we've been dealt.

    So...we have a bunch of villagers (over thirty, actually). And we want to set up a daily schedule for each and every one or them, bearing in mind the following guidelines:
  1.     Townsfolk (or Non-Player Characters or NPCs) should have different schedules during certain days of the week. Also, their daily routines differ each season of the year.
  2.     Each NPC must have a clearly defined schedule, that is a list specifying where they should be at any given time. This means that if the mayor leaves home at 8:00 in the morning and he needs 10 game minutes to go to the general store, if at 8:05 you exit a building and you spawn at the town map near the general store, you should see the mayor exactly halfway there. Also, NPC schedules should be declared in a brief, yet human-readable way. Writing tons of borderline nonsensical -to your future self reading them- parameters for over 30 NPCs can cease to be fun very, very quickly.
  3.     NPCs must pause their route briefly when the player character is very close to them, in order to lend some attention to the player. Maybe we want to chat or give them an item. Of course we can do all that when an NPC is on the move, but chasing them around to do so isn't the smoothest option. In any case, they stop for a set amount of time, beyond which they resume their route.
  4.     NPC schedule infrastructure must be set up in such a way, that it will be possible for the game to alter a given day's schedule under special circumstances. For example, if it's snowing an NPC will stay home, if it's a festival day an NPC must be at the town square etc.
    So...my approach is the following:

  •     A list is indexed with all possible NPCs.
  •     Members of this list are lists indexed with all week days for each season.
  •     Members of that list is another list indexed with all schedule lines for that specific weekday and season (something like: Go there. Then go there. Then stay there.).
  •     Finally, members of that list is yet another list indexed with the very bare bones details for every schedule line, like what direction to be facing after arriving at the destination.
    Confused? You should be. It's a list within a list within a list within a list [Hans Zimmer music intensifies].

Listception.

    So for each NPC, the basic schedule declaration boils down to filling this nested structure. I have ended up setting up my parameters like this:

1:  addschedule(    list,   room_houseint_mayor_f1,         -1,                                 0,      7,      0,          199,    118,    c_down);;  
2:  addschedule(    list,   room_town,                      path_mayor_ownhouse_townsquare,     7,      -1,     0,          0,      0,      c_left);  
3:  addschedule(    list,   room_town,                      path_mayor_townsquare_generalstore, 9,      -1,     1,          0,      0,      c_down);
4:  addschedule(    list,   room_houseint_generalstore1,    -1,                                 -1,     12,     0,          237,    135,    c_down);
5:  addschedule(    list,   room_town,                      path_generic_generalstore_hotel,    12,     -1,     1,          0,      0,      c_down);   
etc. etc.

    Not too bad! You can see where an NPC will be and at what time. For example, the second line says that the mayor will spawn outside his home at 7:00 and follow the path to the town square. Even better, we don't have to explicitly declare the precise time of arrival at the destination (the -1 you see in the from and to columns do exactly this). The script does a second pass when all schedule lines are added for the day and deduces all those -1 judging by the length of the movement path, the NPC move speed or time data from the line below or above!

    Initially I set up all this and thought I was done, but forgot something: This is the bare bones schedule. What if we need to change it during a special day? We can't overwrite all those lists or else our default data will be lost!

    So essentially we have to create another nested structure similar to the above, but instead of holding info for every possible week day and season, this structure has info only for today-that is, the present in-game day, where we want to know at which spots to spawn the NPCs. Trying to copy info from a nested data structure to another nested and slightly different one requires some attention. I don't know if it's just me, but some times I handle these tasks with clarity, while other times I get confused and frustrated: In my initial writing of this code, I messed up the list names, making them to correspond to data trom the previous level. It's like having named as list_chicken the list that contains the egg data. The script worked, but this elusive discrepancy kept nagging me until I fully realized what I did wrong.

    So when a new day dawns, the game assigns a schedule to NPCs: If it's a "normal" day,  data is copied from the base structure. If it's a "special" day, another schedule is assigned (like in the image above), depending on the situation.

    The problem is that assigning schedules to each NPC is quite time consuming. For now, my approach is that not every single day of the week needs to be different. For the most part, townsfolk have the same daily routines except for weekends and usually another specific weekday. However this weekly routine is very different from season to season. All in all, for defining the weekly schedule for an NPC for all seasons, I need about 2 hours. For 30+ NPCs it's like defusing tens of small bombs: It's tiresome, you need to pay attention, it will soon become tedious, and a mistake can make people disappear. Plus, you'll have to verify that you entered all data correctly and that there are no "story" errors: Say you've set the carpenter to visit the doctor on Thursday mornings, but at that time you've set the doctor to be at the grocery. So...um, I guess the carpenter is all alone in an office that's not his, checking out the furniture? You can have things planned out and written down, but as the number of NPCs grows, blunders like the one above are quite likely to crop up.

The Mayor just went out of his house to do...erm...mayorly stuff.
    As a final note, Game Maker's path system can save you quite a lot of time. You want a character to follow a path from their house to the church? You add a new path, set the background to show the town area, add a bunch of points, and you're done!

    As soon as I'm done with the schedules, I'll do the weather effects and related schedule exceptions. Then, I'm thinking of adding the farm horse. Neeeigh! See you next post, and remember:

    If you get to run your code, before shouting "hooray"
    Delete this bunch of variables and use a damn array

10 comments:

  1. This was really interesting to read. I've not given much thought into games having schedules for NPCs but I guess it makes a lot of sense. Do you know what other games have this kind of mechanic? It's really interesting and I wanna explore it further.

    I look forward to your next blog post.
    -Callum

    ReplyDelete
    Replies
    1. Some newer Harvest Moon games also have detailed schedules, but I have no idea what's the engine behind it. Also, games like Oblivion and Skyrim have their NPCs go to places in town and doing stuff, but this is probably mixed with some AI.

      Delete
  2. As a beginner dev your assets look great. Did you make them yourself? What program do you use?
    Your posts are pretty cool also, keep them coming. :)

    ReplyDelete
    Replies
    1. All the assets are hand-made from sratch in drawing programs like Photoshop. It takes a lot of time man. Thanks for reading!

      Delete
  3. I'm also a beginner dev -- working on something right now, and curious how you personally handled an interesting issue I've found in scheduling.

    Specifically, if a character is given a schedule, and say that at 10 AM he is to walk from his house to another; let's say it takes an hour. Well, if you come from somewhere on the map to this town and it's 10:45 AM, you might want him to be 75% of the way along the path. But as he has just been loaded into the game's memory, he may either not move at all, or you can make him start walking right now.

    I was curious how you approached this issue, if at all. Did you just make the NPC persistent, and therefore never unloaded from the game's memory? Or did you have a clever solution?

    ReplyDelete
    Replies
    1. What programming language or game making software are you using?

      Delete
    2. Much like yourself, I am also using game maker.

      Delete
    3. I'm using paths for actor movement. Actors are not persistent objects, they are loaded when players enter town (or another room they're supposed to be in).

      The game checks the schedule of every actor and, if at the required time, the actor is supposed to be following a path, it places them along the path, at a percentage of its length like you said in your post. Then they start moving along the path with path_start().

      Delete
  4. Thank you, that makes a lot of sense. That's a smart solution, I hadn't considered something like that. Thank you for taking the time to explain it.

    I'd like to say that I really appreciate your blog, and your game concept. I'm a big fan of old 2D Harvest Moon games, and I think your idea sounds really interesting, and fun to play. I'll definitely be buying it whenever it gets done.

    I also respect you working on it alone, I'm working with a couple friends, I can't imagine having to work on all the programming and the assets on my own. We're working on an expansive 2D RPG after about half a year of brainstorming. In the past few months we've managed to stumble through building combat and dialogue engines.

    Thanks again, definitely going to be watching your blog.

    ReplyDelete
  5. I'm very new to GameMaker and my dream project involves a bit of stuff like this. Thank you for this wonderful post.

    ReplyDelete