2
\$\begingroup\$

Title could look vague, so here's what I actually mean.

Three groups of people are walking in the city. These are Guards, Thieves and Citizens. Something has happened in the city, Thief have stolen a pocket from a Citizen, the Guard walking around was close enough to see the crime, so now he's chasing Thief.

So here's the question, how that chain would work from execution perspective? We can imagine there is a some kind of StateCheckingObject placed in the world. When Thief steals a pocket, it's class (or Citizen's class ) would broadcast a message to the StateCheckingObject. After StateCheckingObject is aware of crime it would broadcast message to the Guards walking around making them aware of crime.

Well, broadcasting stuff doesn't look complex, that could be a simple event dispatcher. But how would Thieves, Citizens and Guards hold a reference to StateCheckingObject? And what StateCheckingObject should even look like? Should it somewhere in the level? Should other object access it when spawned with GetAllObjectOfClass(StateCheckingObject)? That could be a heavy task for system since iterating over all level objects is not very efficient.

The issue looks pretty similar to GTA where events are broadcaster to nearby police cars when crime is happening, but what would make a broadcast?

Edit: lilotop suggested a trigger box solution and I've realised I haven't explained the idea why I would want to make state checking system.

Some actions happening in the world should be stored somewhere. I.E. after Citizen got robbed StateChecker would get aware of it and store bool CitizenGotRobbed. That boolean would later be used in maybe some kind of dialogue adding new branch to guard dialogue.

\$\endgroup\$

5 Answers 5

3
\$\begingroup\$

Here's an idea.

When a citizen gets robbed, have the citizen store a report:

public class report{
Thief offender;
Time timeOfOccurrence;
Citizen victim;
Vector3 location;
Item[] stolenItems;
}

After the citizen gets robbed, have the citizen run around looking for a guard. It could be random running until the citizen finds a guard via Physics.OverlapSphere or any other similar method if you're not using Unity (to simulate the effect of not knowing where the guard is) or they could run to the nearest Guard Post where a guard is usually standing.

When the citizen finds a guard, it tags them and the guard and citizen move toward each other. Once within range, the citizen gives it's report to the guard. The guard then paths to report.location and looks around until it finds report.offender.

If the guard doesn't find report.offender within x amount of time after report.timeOfOccurrence, the guard gives up and goes back to his post. The report remains, but the guard no longer actively searches for report.offender.

When the guard finds report.offender, they chase him down and get back report.stolenItems, incarcerate report.offender based on the value of report.stolenItems, and go back to report.victim and give them back report.stolenItems. The report is then disposed of.

This of course is a very old-fashioned representation of law enforcement. In a more modern setting, a citizen could call in a report to a dispatch center who would then relay the report to a nearby guard.

In summary, I like to create classes that store information and exchange those class instances between scripts. Think of how packets of data are sent through the internet. The machines that send them might be different, but they can understand the data inside the packets, so communication is possible.

\$\endgroup\$
1
  • \$\begingroup\$ I'd also add a mechanic, that process of robbing a citizen has some chance of informing a Guard immediately (with all described info), if the guard's sight cone loosely includes the robbing location. \$\endgroup\$
    – Kromster
    Commented Feb 24, 2017 at 8:53
0
\$\begingroup\$

I would use triggers instead.

Imagine each guard has a circle or sphere collider spanning the size of this guard's detection area.

When a thief picks a pocket you turn on a collider on that thief object.

Both colliders are of course marked as triggers.

Now all you have to do is handle the OnTriggerEnter event in the guardian code.

\$\endgroup\$
1
  • \$\begingroup\$ That would indeed work well, but there are some reasons I want to see possible state checker implementation. You've helped me realise I've poorly composed the question. \$\endgroup\$ Commented Feb 23, 2017 at 18:56
0
\$\begingroup\$

I would do what lilotop suggested. Collision sphere is inexpensive, easy to visualize, and can be placed with an offset from the guard's head to mimic the difference in detection range based on the guard's facing direction. But if you have a good reason not to use any collision triggers, I would create one logical object (as opposed to a full game object) that all thieves register themselves to. When a thief steals, it raises a self-decaying flag in the registry, and the guards periodically check that registry and perform some filtering and prioritization (distance, LoS, time, etc) to decide whether they should go after the thief.

\$\endgroup\$
0
\$\begingroup\$

Achieving efficiency will vary by situation.

As lilotop mentioned, for purely radius-based or location-based checks, a collision filter with a detector and colliders can work well. Depending on the particulars of the physics engine in play, it may be more efficient to write a separate spatial query system. It may also be more efficient to invert the relationship, e.g. make the units like police have a large spherical shape and the events are then one-off collision queries into the spatial system.

If simple distance or area checks are invalid, a publish-subscribe (aka subscriber-observer) pattern may be best. In such a case, any agent that can see another agent would register itself as a visible observer, possibly with some filter flags. Events generated by an agent would then be forwarded to all registered observers.

An alternative to pubsub would be a combination of the spatial broadcasts and even filtering with vision checks. e.g., the guard within radius would get the crime event, but then might ignore it because a raycast form the guard to the perp fails. Whether this is more efficient will depend on the amount of caching and whether the vision tests are used for other systems. One might also broadcast the event in an area to find all possibly interested parties, then do vision tests to find out which ones could actually see the event, then register those that pass as subscribers for later events.

The simple most efficient solution if you have a low number of events and a high number of event chaser is likely to just use some kind of global-ish flag or list. When an event happens, push it into the list of flagged events. Officers would then scan this list of event on every tick, find the one that's closest that they care about, and select that as the active event of interest. This approach can also be combined with a spatial system if there's more than a handful of active events at any given time.

Yet another approach can involve generating flow fields or maps that guides AI. For instance, a crime might generate a flow map pushing all nearby law-enforcement units towards itself and civilian units away. The local pathing engine would use those overlays for moving units as appropriate, causing police is converge on the perp and civvies to run away.

A real game is probably going to use a large combination of many systems. There is no single fancy mega pattern that solves every use case efficiently.

\$\endgroup\$
0
\$\begingroup\$

Should other object access it when spawned with GetAllObjectOfClass(StateCheckingObject)?
=> No, you should have a list containing all StateCheckingObject in the beginning. Like this

List<StateCheckingObject> m_AllActors;

That could be a heavy task for system since iterating over all level objects is not very efficient.
=> You are right, for more optimization, you can cache all possible combination of object sets there is. Like,

List<StateCheckingObject> m_AllActors;
List<StateCheckingObject> m_AllPoliceActors;
List<StateCheckingObject> m_AllCitizenActors;
List<StateCheckingObject> m_AllRobberActors;
List<StateCheckingObject> m_FemaleRobberActors;
List<StateCheckingObject> m_MaleRobberActors;

It is not readability optimized, but it helps if you are having performance problem.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .