Having NPCs return home at night.

Discussion in 'RPG Maker VX Ace' started by KingHazeel, Apr 15, 2019 at 4:56 AM.

  1. KingHazeel

    KingHazeel Veteran Veteran

    Messages:
    71
    Likes Received:
    1
    First Language:
    English
    Primarily Uses:
    RMVXA
    So when making my game, I decided to include a day/night system to add more immersion. And I figured there wasn't much point of a day/night system if NPCs stood around in the same area all day, so I added a "Nighttime" variable that updates on a certain hour. At this time, most NPCs outside will disappear and instead appear inside their homes.

    Currently, if you wander around outside and it turns to night, there's a brief fade to black. This acts as a "transition" to night, but really it's my way of trying to cover how awkwardly NPCs disappear when night arrives.

    I'm thinking "Wouldn't it be better if the NPCs actually walked over to their houses and then went inside?" The problem is how to implement it. With Galv's Move Route Extras script, I can easily have the NPCs simply make their way to their homes, regardless of where they are, the problem is setting the right switches to make it function properly. I've thought of two potential methods, but both have a glaring problem.

    Method 1: Have the NPC's move route take them home when "Nighttime" is on.

    Problem: This might work when it first turns to night, but say you visit the NPC in their home. When you go outside, you will see the NPC repeating their move route to head inside. That isn't right.

    Method 2: Have the NPC's move route occur during a certain hour. So say we have an NPC who is outside from hour 8-18. At hour 18, NPC starts a move route to their house. At hour 19, they should be inside and disappear outside.

    Problem: But what happens if something slows the NPC down? Or you come outside at 18:55, the NPC is on their move route home, and it turns to hour 19 before they can make it, causing them to abruptly disappear?

    Does anyone have any idea how this could be handled properly? Obviously it doesn't really matter what they do if the player isn't around to see it, but if the player is around they should be seen moving to their homes until they make it to the door or the player transitions somewhere. I'm open to using scripts if necessary.
     
    Last edited: Apr 15, 2019 at 5:01 AM
    #1
  2. Ebanyle

    Ebanyle Veteran Veteran

    Messages:
    94
    Likes Received:
    25
    Location:
    Brazil
    First Language:
    Portuguese
    Primarily Uses:
    RMVXA
    Does night time active a switch?
    You could do it like this:
    Insert a new page at each NPC marked as Parallel Process.
    This page will be marked with a certain switch condition, in this case, the nightime one.
    At the page, set the movement route of that NPC with the wait option. Below the move route, set a switch (or a specific variable value, if you don't too much switchs to spam, but I'm not sure if this will work since there's the probability of 2 events setting a value to the same switch at the same time)
    When that switch/variable is set accordingly, the NPC will be inside it's house.

    As for the slowing down, you could find a pathfinder script or something that improves NPC movement. I personally wouldn't mind for all of them to be home at an exact time, but you could put them to go to their houses sooner if you fear that they won't get in time. Actually, diversifying the NPC's time to go home would make it look more natural.
     
    #2
  3. KingHazeel

    KingHazeel Veteran Veteran

    Messages:
    71
    Likes Received:
    1
    First Language:
    English
    Primarily Uses:
    RMVXA
    Yeah, switch spam would be an issue if I had to set one for hundreds of NPCs. XD I'm not sure what the switch limit is either. With variables, I'm not sure how that would be set. I mean if an NPC came home and set the variable to 5, wouldn't NPCs with variables 1, 2, 3, and 4 also automatically come home?

    As for slowing down, I mainly meant distracts. I.e. events, player harasses them too much, etc.
     
    #3
  4. Ebanyle

    Ebanyle Veteran Veteran

    Messages:
    94
    Likes Received:
    25
    Location:
    Brazil
    First Language:
    Portuguese
    Primarily Uses:
    RMVXA
    iirc the max switch number is of 5000, maybe you can add more with a limit breaker script.

    And actually, about the variable, I thought about it too but the solution is to simply put in the conditions the number being "Equal to". It would only include the other numbers if it was "Equal or lesser than". As the NPC reaches the door you would only set a number in the variable. No adding, no sub, nothing more than set. The issue with that is the possibility of two NPCS reaching home at the same time, I don't know if the engine can handle two set values to the same variable at the same time or if it creates a very little delay to avoid that. I'll need to test that ingame

    For the obstacles, a pathfinder could easily solve that issue, HOWEVER most of them will make your game reach 1 fps if the map is too large, and since you mentioned hundred of NPCS, that would be a real problem.

    You could also search for Vlue's Eventing Fine Tune. It has a go_to function too (in his script it's called waypoint), you just need to set it on an event (no repeat) and it will go to the desired point. It can be a bit troubling though if you interrupt the movement (Don't know if it can be caused by regular eventing but when interrupting the route with an instant command script the character would try to complete the movement in the next set movement route)
    Besides that, I'm not sure if it avoids obstacles.
     
    #4
  5. Lioki_Popolion

    Lioki_Popolion Gao! Gao! Member

    Messages:
    28
    Likes Received:
    10
    Location:
    France
    First Language:
    French
    Primarily Uses:
    RMVXA
    I think that's a realism vs efficiency issue, I like the second option with specific hours you mentioned more and wouldn't mind the move routes breaking spacetime rules for the sake of playability. When it's 19:00 the npc should be inside either the outside npc route is done or not, you don't want the player to be waiting around for stubborn npcs.

    When you go inside after 19:00 both events could have self switch A activated, which means the npc would teleport inside even if their move route is not done but who cares. If you're not inside, the self switches can just activate after the move route. If you really need realism you'll have to store every npc coordinates so they can start the move route on their way but that's a lot of work for something so trivial.

    There's also a quick way to turn self switches on when the move route is done for both the outside and inside npc events with a single script call:
    Code:
    $game_self_switches[[OUTSIDEMAPID, OUTSIDEEVENTID, 'A']] = true
    $game_self_switches[[INSIDEMAPID, INSIDEEVENTID, 'A']] = true
     
    #5
    Heirukichi likes this.
  6. Heirukichi

    Heirukichi Veteran Veteran

    Messages:
    740
    Likes Received:
    217
    Location:
    Italy
    First Language:
    Italian
    Primarily Uses:
    RMVXA
    What @Lioki_Popolion said looks like the best solution in my opinion. You could also compromise by activating the move route for events in the map the player is currently in and trigger a generic switch for each other interior map. However, to do something easy to manage, you should use a script that interacts with the reserve_transfer method, otherwise you have to edit each transfer/door event in your game and add this. It is not a very efficient way to do it.

    However you don't have to store events coordinates, you can access them using
    Code:
    $game_map.events[id].x
    $game_map.events[id].y
    No need to use variables to store those values. You can check them in conditional branches using the script option (bottom part of the fourth page). Then you can use those coordinates to calculate the current distance from home for each event. To be more accurate, you move each event by 1 step toward home, calculate distance and activate that event self switch if that distance is equal to 0. When each event disappeared you trigger a normal switch that each event has in common and turn those self switches off.
     
    #6
  7. Lioki_Popolion

    Lioki_Popolion Gao! Gao! Member

    Messages:
    28
    Likes Received:
    10
    Location:
    France
    First Language:
    French
    Primarily Uses:
    RMVXA
    I think you still have to store x and y so you can put the event on the same spot on his way home and not reset its position when the player leaves the map and comes back during the move route. Maybe @Shaz 's Remember Event Position script can do the trick.
     
    #7
  8. Heirukichi

    Heirukichi Veteran Veteran

    Messages:
    740
    Likes Received:
    217
    Location:
    Italy
    First Language:
    Italian
    Primarily Uses:
    RMVXA
    Why would you? Once you leave a map the event is supposed to theoretically keep moving. It shouldn't be in the same spot. It could as well disappear.
     
    #8
    Lioki_Popolion likes this.
  9. Lioki_Popolion

    Lioki_Popolion Gao! Gao! Member

    Messages:
    28
    Likes Received:
    10
    Location:
    France
    First Language:
    French
    Primarily Uses:
    RMVXA
    You're right! It didn't occur to me the npc should keep walking if you move to another map, your solution is the way to go in terms of balancing practicality and realism. Realism really is overrated anyway ahah.
     
    #9
  10. gstv87

    gstv87 Veteran Veteran

    Messages:
    1,536
    Likes Received:
    593
    First Language:
    Spanish
    Primarily Uses:
    RMVXA
    no, without going and designing a full AI system.
    because if you were to stop the NPC for whatever reason, then the NPC *could* be potentially stopped indefinitely, past the threshold for it's change of behavior, and it would effectively be required to have two behaviors at the same time: "coming" and "going", but unable to take neither one, because it would be "stopped and dealing with action by the player".

    it's as easy to answer, as simply thinking about "why is people late, when they're late?"
    WHY is people late when they're late? -> because they can't get there, before dealing with this event here.
    time is linear, and you can't speed it up..... if you take too long for one action, you gonna be late for the next one.
    "late", as in "past the global time", which you can't change because it's always ticking.

    in order to avoid that, you'd have to move from timed events to conditioned events..... hence, AI.
     
    #10
  11. Heirukichi

    Heirukichi Veteran Veteran

    Messages:
    740
    Likes Received:
    217
    Location:
    Italy
    First Language:
    Italian
    Primarily Uses:
    RMVXA
    What's the problem if an events gets stuck? They should be moved using a parallel process anyway. As long as the player prevents them from moving, the event goes on and only moves those that have to be move. If the player wants to test events reaction and stands in their way until morning, they start moving in the opposite direction and everything is fine. I really don't see how an AI is required.

    From a software engineering standpoint it is definitely a bad design choice, as it requires a lot more effort and you get no real benefit from it. If you really want to move events when their path is blocked using something like Dijkstra's algorithm looks like a much better option to me.
    Code:
    Pseudo code:
    All events handled = true
    For each event do
      If event is on screen
        Get distance from home
        Move one step
        Get new distance
        If new distance == previous distance
          Use Dijkstra to calculate new path
        Endif
      Endif
      If new distance == 0 or event is out of screen
          Turn self switch on
      Else
        All events handled = false
      Endif
    End
    If all events handled
      Turn global switch on
      For each event do
        Turn self switch off
      End
    Endif
    You can still move other events if one has its path blocked, even without calculating a new path. You only have to remove the wait for completion option in the move route/each branch.
     
    Last edited: Apr 15, 2019 at 2:30 PM
    #11
  12. Restart

    Restart Villager Member

    Messages:
    26
    Likes Received:
    12
    First Language:
    English
    Primarily Uses:
    RMMV
    An easy compromise (and one commonly used) is to only code entrances and exits for NPCs on the player's current map.

    So if you're in the town square at 18:00, the NPCs all start walking towards an edge of the map, or other transition area. If Bob the Baker is standing just outside his house, you'll path him to that door instead.

    This only triggers once per day/night shift. Once the player loads a new map, the NPCs are set up in their appropriate day/night location (or, if they aren't there during nighttime, they're absent).

    The only time the player might see something funny is if Bob the Baker is onscreen walking towards his house at 18:01, then you go into his house and he's already there. But since a map transition is already an abstraction that teleports the player to a new location, having the npcs teleport to a new location on a map transition isn't going to be jarring.

    No need to track NPCs across different maps, and players don't have to worry about Bob's pathing getting hung up on a free-wandering cat somewhere or wait around for Bill the Blacksmith to finally get home so they can continue the questline. Even if Bill would realistically take two hours to go from his volcano blacksmith shop to his mountainside apartment complex, if you go into Bill's house at 18:01 he'll already be there, and if you are squatting in his house at 18:00 you'll see him come in.


    From a coding practicality standpoint, doing it this way means you can say when a NPC is supposed to be in a given map just by coding it in the event for that map, without having to change things elsewhere. Players don't have to track tiny differences in time for NPC schedules - he's HERE in the day, THERE at night, no need to wonder what's taking him so long.
     
    #12
  13. gstv87

    gstv87 Veteran Veteran

    Messages:
    1,536
    Likes Received:
    593
    First Language:
    Spanish
    Primarily Uses:
    RMVXA
    no, everything is not fine, because if the location of that event is tied to the execution of another sequence, then the player is able to interfere with the normal function of the game.
    that's exploitation of a vulnerability!
     
    #13
  14. Heirukichi

    Heirukichi Veteran Veteran

    Messages:
    740
    Likes Received:
    217
    Location:
    Italy
    First Language:
    Italian
    Primarily Uses:
    RMVXA
    I see your point, but calculating paths with Dijkstra's algorithm still solves the problem of an event being indefinitely stuck.

    On top of it, we - as game designers - already know which interactions happen in that map. It is much easier to create them so that a cutscene path never intersects any NPC going home rather than creating a whole AI system for that. The only unknown position is that of the player (if no instruction was given in the cutscene event itself).

    For everything else triggering a self switch if the event is involved in the cutscene is enough, this way it does not move until the cutscene ends. However, this does not change the algorithm used, it only adds one extra check to see if that particular event can be moved or not.

    Code:
    All events handled = true
    For each event do
      Next if cutscene self switch
      If event is on screen
        Get distance from home
        Move one step
        Get new distance
        If new distance == previous distance
          Use Dijkstra to calculate new path
        Endif
      Endif
      If new distance == 0 or event is out of screen
          Turn self switch on
      Else
        All events handled = false
      Endif
    End
    If all events handled
      Turn global switch on
      For each event do
        Turn self switch off
      End
    Endif
    The line "Next if cutscene self switch" is the only condition you need to not allow events involved in cutscenes to move.

    I definitely agree that transitions are already an abstraction and there is no need to bother too much about them. However, if you want more realism, this can be achieved by triggering the switch for events inside after each event outside has been handled OR after the player leaves an interior. This way if the player enters Bob's house when Bob is walking home, bob will not be there. But if the player leaves Bob's house, then the next time Bob will be inside, as if they met during the transition.

    It is still an abstraction but gives out a decent vibe. Of course, each house should have an event that triggers at some point to show the owner entering through the door and moving toward his/her final position. This, however, can be achieved using a single common event, as the algorithm to move events is the same for each house:
    Code:
    Unless final position reached
      Move one step
    Endif
    
    If you really want to be realistic you can add Dijkstra's algorithm here too.
    Code:
    Unless final position reached
      If next step possible
        Move one step
      Else
        Use Dajikstra's algorithm to calculate new path
      Endif
    Endif
     
    Last edited: Apr 16, 2019 at 12:02 AM
    #14
  15. gstv87

    gstv87 Veteran Veteran

    Messages:
    1,536
    Likes Received:
    593
    First Language:
    Spanish
    Primarily Uses:
    RMVXA
    every time you add a condition or a loop to any validation involving events, the game will lag depending on how many events you're handling.
    the game engine itself already validates events on a map through a loop.... if you nest another loop in that, you're creating a delay.

    also, you don't need to calculate new paths.... the path is one: the one that needs to be taken, for that event to reach it's *destination*.
    the problem here is, that the player interrupts the movement, and that the progression of the system is tied to the event's location, which depends on it's movement.
    concepts.

    what you need, is either to prevent the player from interrupting that movement (which would affect the interaction), or, disregard the check for action dependent on the event that's being interacted with, while the event is being interacted with.
    which, will potentially affect the development of the system on the long run, BUT, can also potentially be compensated by resetting those checks based on a global time control.
    basically: if you stop the event for a chat, and the event is supposed to be home waiting for another check, you send it home after the chat ends (regardless of time), you keep it home until the day ends, and you reset it the morning after.... enabling it to perform the routine of getting home again, provided the player doesn't stop it again.
    that pushes the problem further down the line, but since the player can't be everywhere at the same time, then there's always X-1 events running as intended, X being the total.
     
    #15
  16. Heirukichi

    Heirukichi Veteran Veteran

    Messages:
    740
    Likes Received:
    217
    Location:
    Italy
    First Language:
    Italian
    Primarily Uses:
    RMVXA
    This sentence might be very misleading for those who are not very experienced in the field of algorithms. It is misleading because it is true that if you nest loops you obtain a much worse complexity, but there is no nested loop here. Each time the engine scans events to check if they have something that should run, only a single event contains the loop required to scan them.

    Code:
    For each event
      Evaluate event
    End loop
    Evaluate event is usually a single operation or a fixed number of operations - in big O notation it is O(1). It is true, however, that if we have an event that scans each other event, that event has a different complexity. The complexity will be the same as the loop itself.

    If we use lc for the loop complexity, a for the average number of operations done for each event and n for the total number of events in that map, the overall complexity can be calculated like this:
    [​IMG]
    The summation actually performs a number of operations that is equal to n. I used (n - 1) to simplify calculations. If you keep reading, in the next step I explain how (n-1) can be considered n - and vice versa - if we are handling a large number of events.
    If you want to simplify that formula you can get rid of that last +1 as a single operation does not have a meaningful impact on the game.
    On top of it, the average value for a represents the number of operations performed for each event (check if a new page has to be activated, check if graphics have to be updated, check if commands have to be evaluated, check passability if player is moving, etc.) in the main engine loop. For this reason it is always greater than 1. For this reason, it is safe to say that (a + 1) ≈ a. The simplified formula for this would be like this:
    T(n) = O(a * (n - 1))

    If you put a loop in the main loop you obtain something like this:
    Code:
    For each event
      Evaluate event
      Run event loop if event is day/night system
    End loop
    This does not require extra operations on other events as the engine already checks each frame if the event commands have to be evaluated or nor. It only adds the required loop to a single event. From a complexity standpoint it adds a complexity equal to (n - 1) to the previous formula (in the worst case scenario).

    What you obtain is this:
    T(n) = O(a * (n - 1) + (n - 1)) = O((a + 1) * (n - 1))
    As we said, however, (a + 1) ≈ a. The same applies to (n - 1), which can be considered equal to n.

    The overall complexity you obtain is the following:
    T(n) = O(a*n)

    In big O notation, this means
    T(n) = a*n = O(n)

    If you develop an AI system and put that check directly in the engine you have still to calculate it, no matter what. Even if you perform one extra operation for each event you add a number of operations each frame that is equal to the total number of events. Not only is this irrelevant from a complexity standpoint (which is something theoretically calculated over a large number of events), it actually requires the very same amount of operations. The only thing you avoid is calling the eval method, but that doesn't really matter as ruby is an interpreted language and you are always evaluating strings, even when writing them in a script. It would be a different matter if you were to implement it using an external DLL written in C, but how many events can be in your screen (17 x 13 by default) each time? Is that really worth the effort?
    The optimal solution would be to let events that are outside of the screen instantly disappear, then store those who are visible in a variable (using an array) so that you only have to update those events. But while that reduces the number of events you have to handle, it does not reduce your overall complexity. In fact, checking if an event has to move or not means checking the value of a single boolean value - thus its complexity is T(n) = O(1). Even so, this still represents the optimal solution as the overall number of operations is reduced.
    Question: On a side note, would you - as the player - ever notice lag if you are standing still to prevent one event from reaching its destination?

    Answer: You probably would not, because you are standing still and everything else reached its destination so there is no image being updated. The only way to notice it when everything is immobile is to have a FPS counter, but even so it does not affect your game-play (since what you see is not changing at all).

    This does not mean you have to write something inefficient, it simply means that as long as you plan to stop everything by standing still and blocking an event path you are not affected by lag at all. You would only be affected when moving, but that means the event is free to move a step as well.


    Are you assuming that there is a single possible path connecting two different points on a plane? Because if you are not assuming that there is just one path - which would be what happens in the vast majority of situations - then there are many different ones. As long as at least one of them is not blocked you can take that one, and since there is only one player, you can only block one path. On top of it, even if you decide to move and keep blocking an event from reaching its house for any reason, once day comes, the destination for that event changes (since it is supposed to go back to its original location) so the path is no longer blocked. I cannot see how this can be vulnerable if the player decides to play table tennis with itself using an event as the ball.

    The former is the very same thing I said before: you activate a self switch when an event is involved in a cutscene/dialogue/whatever and it that very self switch is triggered you just do not update that event. The latter is solved by finding a new path if the player blocks the event. That way the event can take a different route or stand still until a route becomes possible.


    What I think is causing the misunderstanding here is that there is no need for two parallel process events: one to send events home and one to send them back. What you need is a single parallel process event and a number of destinations (those are based on the current time) that tells the event where those events are going. This cannot be messed up when an event is not able to move as your events will always move back when the day comes. And even if you spend your whole time blocking the path of any number of events, other events will always start going home at dusk and back to "work" at dawn.
     

    Attached Files:

    Last edited: Apr 16, 2019 at 7:06 AM
    #16
  17. gstv87

    gstv87 Veteran Veteran

    Messages:
    1,536
    Likes Received:
    593
    First Language:
    Spanish
    Primarily Uses:
    RMVXA
    YOUR loop is nested inside the engine's main loop!
    o_O
     
    #17
  18. Heirukichi

    Heirukichi Veteran Veteran

    Messages:
    740
    Likes Received:
    217
    Location:
    Italy
    First Language:
    Italian
    Primarily Uses:
    RMVXA
    @gstv87 The fact that it is inside the engine loop does not mean it is repeated for each iteration of the loop itself. It is only executed on a single iteration. Having it outside the main loop would result in the same number of iterations. As such, it is mathematically correct to consider it a simple addition. Do not let the fact that it runs inside another loop mislead you.

    Try counting the number of iterations and you will see what I mean.
    Code:
    a = [1, 2, 3, 4, 5]
    For each |e| in a
      e += 2
      If e == 7
        For each |i| in a
          e += i
        End Loop
      Endif
    End Loop
    The number of iterations is equal to 10 (5 + 5) which is far from 5*5 (nested loop). As a matter of fact it performs 2 operations on each element (sum and check) and 2 + 5 operations (sum and check + 5 sums) on the last one.

    You could write it this way and it would be the very same thing (not exactly the same though).
    Code:
    For each |e| in a
      e += 2
    End loop
    For each |e| in a
      a[a.length - 1] += e
    end
    This performs 5 iterations in the first cycle and another 5 iterations on the last cycle for a total of 10 iterations (same as the previous one). What is different is the check on the boolean value.

    In VX Ace, however, that check on the boolean value is already performed by the engine to see if an event command list has to be evaluated or not, so that would change literally nothing. As a matter of fact, since event triggering conditions are evaluated no matter what, not only does this have the same amount of iterations in VX Ace, but it also has the same amount of operations.

    Of course, it runs in the game (which is a loop per se) so you can consider it as a nested loop, but, if you consider what happens in each frame, it does not increase the current complexity. It increases the number of total operations, but as long as you add something you are increasing the number of total operations, and the number you add is the same you would add by putting it outside of the loop that checks events (see spoiler above).
     
    Last edited: Apr 16, 2019 at 12:00 PM
    #18
  19. KingHazeel

    KingHazeel Veteran Veteran

    Messages:
    71
    Likes Received:
    1
    First Language:
    English
    Primarily Uses:
    RMVXA
    Holy crap, this got complex since I last posted...

    See, this sounds like a great way to handle it--I just don't know how I would do it. I guess it would look something like this?

    Nighttime On (or hour triggered)
    No self switch.
    NPC starts moving home.

    if NPC reaches home, self switch D is turned on and they they disappear.

    if player transitions anywhere, self switch D is on and the NPC disappears outside.

    When nighttime is off (or hour triggered) and self switch D is on, self switch D is turned off. Then the NPC resumes their day schedule.

    The bolded is what stumps me. I can trigger the self switch outside of the NPC event, but I'm not sure how I would activate it whenever the player transitions.

    EDIT: I guess I could trigger the D switch for every single NPC with a schedule whenever the player opens a door or engages with a transition event...though I'm wondering if that's the best way to handle it.
     
    Last edited: Apr 17, 2019 at 6:50 AM
    #19
  20. Restart

    Restart Villager Member

    Messages:
    26
    Likes Received:
    12
    First Language:
    English
    Primarily Uses:
    RMMV
    Here's one way

    On each NPC

    Default: NPC is Invisible and uninteractable

    Self Switch A: Normal Conversations, default behaviors.

    Self Switch B: Path to edge of map, clear all switches when you reach it (becoming invisible and uninteractable)
    Self Switch C: Path to normal location, change to (A) when you reach it


    In addition, create a Timer Event on each map.

    Timer Event: On map load:
    Check to see which NPCs should be around right now. Set those to self switch A (so they appear). Leave the others in default state (so they're invisible and don't exist as far as the players know)

    Hour trigger: Check NPC schedule.

    If a NPC should ARRIVE at this hour: Teleport them to the edge of the map, then set switch C on that event. To the player, the NPC is arriving from offmap, going to his workspace, and everything is fine.

    If a NPC should DEPART at this hour: Set self switch B on that event. To the player, the NPC is now leaving to go offmap.
     
    #20

Share This Page