7
\$\begingroup\$

my player has a gun with 3 bullets. If he runs out of ammo the scene should reset itself. I don't want to load the scene again, some components should reset themselves by using an interface.

So my observer class keeps track of the bullets and when there is nothing "active" anymore it should reset the scene.

When a bullet goes inactive it starts calling the observer.

public class Observer : MonoBehaviour
{
    [SerializeField]
    private Projectile[] projectiles; // my 3 bullets

    public void ResetScene()
    {
        for (int i = 0; i < projectiles.Length; i++)
        {
            Projectile currentProjectile = projectiles[i];

            if (currentProjectile.IsActive) // Are any bullets left?
            {
                return; // Leave this method
            }
        }

        MonoBehaviour[] sceneObjects = FindObjectsOfType<MonoBehaviour>(); // find all objects in the scene

        for (int i = 0; i < sceneObjects.Length; i++)
        {
            MonoBehaviour currentObj = sceneObjects[i];
            IRewindable currentComponent = currentObj.GetComponent<IRewindable>(); // try to get the interface component

            if (currentComponent != null) // does the gameobject implement this interface?
            {
                currentComponent.Rewind(); // make it rewind itself
            }
        }
    }
}

I think the code below the first loop might not be the best. Is there a more optimized way of finding all interface components and call their Rewind method?

\$\endgroup\$
3
\$\begingroup\$

In the first loop I think, you could do:

if (projectiles.Any(p => p.IsActive))
{
  return;
}

In the second you could do something like:

foreach (var currentComponent in sceneObjects.Select(so => so.GetComponent<IRewindable>()).Where(r => r != null))
{
  currentComponent.Rewind();
}

But be aware, that using linq is not necessarily the most optimized approach (performance wise) , so you'll have to do some tests.

\$\endgroup\$
  • 1
    \$\begingroup\$ yes this is very good. I'm just looking for a way where I don't have to loop all Monobehaviours .. but I think there is no \$\endgroup\$ – Question3r Apr 22 '18 at 10:40
  • \$\begingroup\$ I'd expect the linq operation to be wildly overshadowed by the GetComponent()call, and the FindObjectsOfType(), but yeah, measure and test. \$\endgroup\$ – Yann Apr 22 '18 at 13:28
  • \$\begingroup\$ hmm the code works fine, I don't think any errors could come up \$\endgroup\$ – Question3r Apr 22 '18 at 13:37
3
\$\begingroup\$

Since it's a game and in games code usually should run pretty fast I'm rather sceptical about your bullet solution.

currentProjectile.IsActive

You are checking each bullet whether it's still active... a counter of active bullets would be much more efficient.

\$\endgroup\$
3
\$\begingroup\$

One different approach would be have your IRewindable register to your Observer class and unregister when it is destroyed. That would eliminate your (slow) FindObjectsOfType<GameObject>().

To provide some pseudocode that definitely won't compile, if for no other reason that you can't have a constructor in an interface.

interface IRewindable
{
    public IRewindable()
    {
         Observer.RegisterRewindable(this);
    }

    public ~IRewindable()
    {
         Observer.UnregisterRewindable(this);
    }

    public void Rewind();
}

public class Observer() : MonoBehaviour
{
    private static HashSet<IRewindable> m_ToRewind = new HashSet<IRewindable>();
    public static void RegisterRewindable(IRewindable rewind)
    {
         if(rewind)
         {
             m_ToRewind.Add(rewind);
         }
    }

    public static void UnregisterRewindable(IRewindable rewind)
    {
         if(rewind)
         {
             m_ToRewind.Remove(rewind);
         }
    }

    public void ResetScene()
    {
        foreach(IRewindable toRewind in m_ToRewind)
        {
            if(toRewind) //Unneeded really, but best to be safe IMO
            {
                toRewind.Rewind();
            }
        }
    }
}

There are definitely improvements to be made to this as it's intentionally very barebones to just illustrate the pattern, but something to work from. It entirely side-steps the slow FindObjectsOfType and also the slow GetComponent at the cost of a teeny bit more memory usage.

I chose a HashSet assuming that you wanted uniqueness, but on reflection since it's just being used as a List, that would also do if you don't care about being unique.

\$\endgroup\$
2
\$\begingroup\$

Lambda is one of the impressive ways I agree upon , but I recommend to use Parallel.Each in case of loops in second loop. That will help you optimize the list traversal time.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.