design pattern implementation

Hi there, I am facing an odd situation here. Lets say we have 5 objects:

1.object A
2.object B
3.object C
4.object D
5.master object

Let us classify a bit. A,B,C,D is called dummy.

If master object’s code run, then it processes all A,B,C,D objects; prepares data and sets data into some fields of A,B,C,D each of dummy objects. Now code within master object is pretty heavy. I think it is not a good idea to run master object’s code on every frame. Master object’s code will only run when A or B or C or D object need it to run. At any time more than one dummy objects may need master object’s code to run. Lets say A,B,C all three, need to run master object’s code on a certain frame. Now all of three, should not invoke master object’s code, only one(A or B or C. NOT A and B, or such!) dummy object will invoke master object’s code even though all three, need to run master object’s code.

How do I implement this, guys?

Edit:
Guys I think I need to elaborate my problem a bit further, I am feeling to have some sort of information gap. Basically master object is a gameobject, with which a nominator script is attached. All it does, are the following respectively:

1.get all gameobjects tagged as enemy.
2.process all necessary information of each enemy.
3.nominate action/actions for each enemy.
4.pack them as a container of states
5.Send the container to each enemy gameobject(aka set "stateContainerList" of each enemy)

Nominator will not run on every frame as it involves processing a lot of information for each enemy.
Now nominator will only nominates certain type of states,lets say action states.
Enemy AI first see if it needs those type of states now. If it does not,then nominator will not be invoked. If it does need, then it will invoke the code within nominator and nominator will process-pack-send data to every enemy. Now lets say enemy A needs actions, enemy B too needs actions. But nominator should be run only once on this frame. If we do not count this matter, then consider this situation:

Program counter is within enemy A gameobject’s code realm, then nominator will be called and necessary data are sent to every enemy gameobjects. With data from nominator and other factors,then enemy A can choose its behavior. Lets say immediately then, PC is within enemy B gameobject’s code realm, then again nominator will be called. But do we need it? We already called nominator and enemy B gameobject’s respective attached class already have the list(list of nominated states for enemy B) fully filled up. We are both wasting our resource and we are confusing the system with new calculated nominated values. This could posses huge security vulnerability which I do not want.

That is why this part is so important.

The asset structures are:

gameobjects:
1.master
2.enemyA
2.enemyB
3.enemyN

Classes:
1.nominator.cs---attached with master
2.coreAI.cs---attached with enemyA, enemyB.....enemyN

Inside coreAI.cs, there should be read/write both enabled property called say “listOfNominatedStates”.

responsibility:
nominator.cs will collect all necessary info of enemyA,B,C,D…N. For each enemy it will add necessary states to “listOfNominatedStates”.

coreAI.cs will decide final state from “listOfNominatedStates” and other info.

3 Answers

3

The most simple way is to store your current object as a static variable inside the A,B,C and D class (it’s the same class, right?)
So when one of your objects thinks he needs to call master’s code, he checks this variable and if it’s the same as the calling object.

...
public static MyLittleClass currentCallingObject;
public static List<MyLittleClass> allCurrentObjects
void Awake()
{
    if(allCurrentObjects == null)
    {
        allCurrentObjects = new List<MyLittleClass>();
    }
    allCurrentObjects.add(this);
    // We will randomly recalculate the object that would call the master's code
    currentCallingObject = allCurrentObjects[Random.Range(0, allCurrentObjects.Count - 1];
...
void needToCallFunction()
{
    if(needToCall && this == currentCallingObject)
    {
        master.callThisFunction();
    }
}
...

If all of the objects A,B,C and D need to call, only one of them will call the function

Interesting, I think now I'm starting to understand what you're up to) I'll edit the code right away, stay tuned

Check it out

I have updated my situation on original post, could you see a bit please?

If you talk about some kind of state update of the master object which should be done at max once per frame you can store Time.frameCount in a variable and ensure to only invoke the code when the last execution was not in the current frame.

// C#
// On the master object
int m_FrameCount = -1;
public void DoHeavyStuffOnlyOncePerFrame()
{
    if (Time.frameCount <= m_FrameCount)
        return;
    m_FrameCount = Time.frameCount;
    // Do your heavy stuff here
}

You have a quite abstract situation here, so it’s difficult to suggest other solutions since they would depend on who decides which object(s) need an “update” and when. Your question is too abstract to be answered in a straight way. Can you get a bit more concrete? :wink:

Also the event-flow isn’t really clear. First it sounds like A,B,C,D are passive objects and your master object actually get invoked “in some way”. But then you reversed the flow by stating that A or B or C or D should invoke some code on the master. That doesn’t really help to understand your situation.

x) forgot the most important line: m_FrameCount = Time.frameCount; I've edited my answer.

toying with Time.frameCount seems to be a good idea. I will post about its update tomorrow. Its 4.00AM here, gotta sleep.

Good answers from the other guys, however given the abstract scenario provided I will offer some thoughts that may be apply to your situation.

Is the master object’s algorithm really that heavy? Can you break it up so that A,B,C,D can call only those parts required at that time? Can you split the work across frames using coroutines?

Does it make sense to run the master object on consecutive frames? If not you could implement a cooldown feature.

float readyTime = Time.time;
float cooldownPeriod = 1.0f;
bool waiting = false;

public bool requestWork() {
    if (Time.time >= readytime) {
        readyTime = Time.time + cooldownPeriod;
        waiting = false; 
        doWork();
        return true;
    } else {
        waiting = true;  
        return false;
    }
}

void Update() {
    if (waiting)  {
        if (requestWork()) {
            waiting = false;
        }
    }
}

This would have the effect of not only ensuring that it is not called more than once per frame, it would also let you ensure it is not run for a defined cooldown period.


Update:

Ok, now we know more information about how you would the information to passed back and forth.

Pull:

If a method similar to this was placed in your Nominator class:

    IDictionary<CoreAI, AiState> stateMap = new Dictionary<CoreAI, AiState>();

    public AiState GetAiState(CoreAI client) {
        if (isUpdateNeeded()){
            UpdateStateMap();
        }
        return stateMap[client];
    }

Then the CoreAi’s can simply call this as required:

    aiState = nominator.GetAiState(this);

Push:

Alternatively you could place this in Nominator class:

    IDictionary<CoreAI, AiState> stateMap = new Dictionary<CoreAI, AiState>();

    public void UpdateAiStates() {
        if (!isUpdateNeeded()) return;
        UpdateStateMap();
        foreach (CoreAI coreAI in stateMap.Keys) {
            coreAI.PushState(stateMap[coreAI]);
        }
    }

And place this in CoreAi:

    private AiState aiState;

    public void PushState(AiState newState) {
        if (isNewAiStateNeeded()) {
            aiState = newState;
        }
    }

Thanks for appreciation @KellyThomas . But the main problem remains. Lets take this situation into count: enemy A "gameobject" has coreAI.cs attached with it, so has enemy B and C. All of those will call, aiState = nominator.GetAiState(this); So each one will invoke GetAiState(). And each one will check Boolean return type of "isUpdateNeeded()". Now lets we first encounter enemy A, it returns true and we updated states. Now when we are in enemy B, it must return false. Actually while we are at this frame, it must return false. On a single frame it can only be true once.

I agree this update only addresses the dispersal of information. The specific implementations of isUpdateNeeded(), UpdateStateMap() and isNewAiStateNeeded() would need to be undertaken by somebody with further knowledge of your project. Bunny83's answer provides a straightforward technique to detect if an update has already executed on the current frame, this would be a perfect fit for isUpdateNeeded().