1
\$\begingroup\$

I have the following Actor and Scene classes :

public class Actor
{
    string name;
    vec3   position;
    quat   rotation;

    public void Update()
    {
        ...
    }

    public void Render()
    {
        ...
    }
}

public class Scene
{
    List<Actor> actors = new List<Actor>();

    public void OnUpdate()
    {
        for (int i = 0; i < actors.Count; i++) {
            actors[i].Update();
        }
    }

    public void OnRender()
    {
        for (int i = 0; i < actors.Count; i++) {
            actors[i].Render();
        }
    }

    public void DestroyActor(Actor act)
    {
        actors.Remove(act);
    }
}

The DestroyActor method will remove a specified actor from the list, and stop it from updating/rendering, but it does not destroy it. Other actors still have a reference to that object, for example, the camera has reference to the player actor to chase him:

public class Camera
{
    Actor player;

    public void Update()
    {
        if (player == null) {
            // select nearest NPC and chase him
        } else {
            // chase player
        }
    }
}

I can change the classes and make them communicate via handles, for example, but that means I have to make the Actor class internal, instead of public, and include methods such as ActorExists(int hdl), GetActorPosition(int hdl) and GetActorRotation(int hdl). This makes me wonder what the difference is between C# and a low level language, like C.

In the Unity game engine, a call to MonoBehaviour.Destroy(player) will completely destroy the object, and set all references of that object to null. I know that they use the embed mono, and have more control over how the garbage collect works, but I wonder how do others work around these kind of problems in C#?

\$\endgroup\$
5
  • \$\begingroup\$ Why not just make object inactive? gameObject.SetActive(false) \$\endgroup\$ – Lufi Mar 17 '14 at 10:12
  • \$\begingroup\$ this is not a unity question, MonoBehaviour.Destroy(player); was just an example for what i want to achieve in my own game engine ... \$\endgroup\$ – amin.v Mar 17 '14 at 10:49
  • \$\begingroup\$ If it is not a Unity question - I would not tag it with the Unity tag. \$\endgroup\$ – Tom 'Blue' Piddock Mar 17 '14 at 11:15
  • \$\begingroup\$ Possible duplicate of stackoverflow.com/questions/19476730/how-to-delete-object \$\endgroup\$ – SpartanDonut Mar 17 '14 at 14:08
  • \$\begingroup\$ Specifically I would look at this answer - I think you may be trying to accomplish something at isn't meant to be accomplished in the context of C# \$\endgroup\$ – SpartanDonut Mar 17 '14 at 14:08
1
\$\begingroup\$

Initial idea based off your Actor structure:

You are basically just wanting a character to be null, if you have removed it from the list that updates and renders it why not just set act=null; to handle resource disposing in DestroyActor() and track the resources themselves as part of your overall design. Otherwise these items will not be handled correctly and cause issues longer down the line. MonoBehaviours are handled behind the scenes and have a lot of code behind the simple Destroy() function, so it's not as simple as a one line solution:

public void DestroyActor(Actor act)
{
    actors.Remove(act);
    // Call the classes destroy to handle any resources to dispose manually.
    act.Destroy();
    act = null;
}

Garbage collection will cleanup any handled items so create a Destroy() or Dispose() function in your Actor class and clean up any resources used, GC will pick it up.

To expand you can keep references to your Camera within your Actor and break the references from there. Creating and calling a method such as AttachedCamera.Detach(); could be easier to implement and then when removing the actor you would do:

public void DestroyActor(Actor act)
{
    actors.Remove(act);
    // Call on the referencing resources to detach from the actor.
    act.DetachResources();
}
...
//In Actor
public void DetachResources(){
    // Tell camera to detach
    attachedCamera.Detach();

    // Manage any other actors attached to this Actor
    foreach(Actor act in attachedActors){
        actor.Detach(this);
    }
}
...
//In Camera
public void Detach(){
    // Remove reference to actor
    _attachedActor = null;
}

Alternative (I would choose this):

Use events and subscribing to listen when your actor is removed from the scene and get your attachable actors to then detach from it.

public class Actor
{
    string name;
    vec3   position;
    quat   rotation;

    public delegate void RemoveEventHandler();    
    public event RemoveEventHandler Remove;

    protected virtual void OnRemove() 
    {
        Remove();
    }

    public void Update()
    {
        ...
    }

    public void Render()
    {
        ...
    }
}

Listen to this event in your Camera:

public class Camera
{
    Actor player;

    // If player removed from scene, remove reference.
    private void ActorRemoved(){
        player.OnRemove -= new Actor.RemoveEventHandler(ActorRemoved);
        player = null;
    }

    public void Update()
    {
        // This is null if the above event is fired.
        if (player == null) {
            // Select nearest NPC and chase him.
            player = NearestNPC();
            player.OnRemove += new Actor.RemoveEventHandler(ActorRemoved);
        } else {
            // Chase player.
        }
    }
}

This then requires no tracking on the side of your player or NPC's and then you can assign the event to any necessary linked classes.

\$\endgroup\$
10
  • \$\begingroup\$ Thanks, but act = null; just set that single reference to null, not all references (the one in Camera class for example), Destroy()/Dispose() methods also make sense when you have unmanaged resources, Actor is a pure C# class, what would i do in those methods ? \$\endgroup\$ – amin.v Mar 17 '14 at 11:42
  • \$\begingroup\$ The act = null; is not the important part, it's the act.Destroy;. You can implement the IDisposable interface to help with any unmanaged resources. You should potentially link this to anything that tracks it, Camera, Scene etc, and use Dispose to break those references. \$\endgroup\$ – Tom 'Blue' Piddock Mar 17 '14 at 11:56
  • \$\begingroup\$ Just edited to make it clearer. \$\endgroup\$ – Tom 'Blue' Piddock Mar 17 '14 at 12:02
  • \$\begingroup\$ Sorry but the answer is completely wrong, reason : the actor doesnt know who has reference to it, maintaining a list (attachedActors) is not possible and also lead to other problems, any other object (not derived from Actor) can hold reference to an actor object, how would you guaranty that it has Detach method ? \$\endgroup\$ – amin.v Mar 17 '14 at 12:44
  • \$\begingroup\$ Create an interface IDetachable to the actors that can be detached. Then in the foreach() you can check if they match that interface and detach them. \$\endgroup\$ – Tom 'Blue' Piddock Mar 17 '14 at 12:57

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.