• Advertisement
Sign in to follow this  

Unity Question regarding indexes to entities and scripts

This topic is 3944 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

From this thread.
Quote:
Original post by ApochPiQ
Quote:
Original post by Nitage In fact, I find reusing Ids a bit nasty - what happens if (say in an rts) a unit diedhalf an hour ago, but a script kept around a reference to it? Do you want to be able to tell the script that the unit in question no longer exists? Or would you prefer to hand it a reference to a totally different unit?
If this scenario ever occurs, you have serious design issues anyways, and likely some major bugs lurking under the surface. When the unit dies and becomes non-existent, every reference to it should be released. Your systems should be built in a way that makes it trivial to track all known references to a given resource or entity; as a logical consequence, it should be trivial to remove those references if an entity stops existing. If this is not a very simple process, chances are the design has some major weaknesses - most likely excessive coupling, probably some temporal coupling, and certainly a lot of unwritten and unverifiable assumptions.
This scares me because it's basically saying "Your program is going to have bugs, Ezbez!" I have a system similar to what was discussed in the linked thread. I have integer IDs for everything, and I create a new one by adding one to the last one that was made. No duplicates, no worries, eh? But now ApochPIQ tells me that it's fundamentally wrong and the very fabric of game-time is going to collapse on me one of these days because of it. Can anyone help me with this? I want a nice system, but I'm not sure how I can improve upon my current system. I guess some more details about my current system might be useful. I internally store Entities and expose them via EntityHandles to scripts. The EntityHandles don't actually ever give out an Entity, they wrap all function calls to the Entity between another layer. This layer checks that the Entity is still valid, if not, it does nothing and returns some generic nothingness to do no damage. Scripts only ever touch EntityHandles, and can store them. EntityHandles internally store indexes to the Entities.

Share this post


Link to post
Share on other sites
Advertisement
When your entity dies, all references in the scripts should die too, so they should never have to manage a NULL handle. Else, you scripts might for example, leak memory quite badly (for example, accessing the entity one hour after its deletion. That's not really a good idea).

It is not recommended to silently discard NULL handle. At the very least, assert. It's like a memory violation, like accessing a dangling pointer. The exception is that you can obviously carry on the execution, during the development time. But in final builds, these should be ironed out.

If you are not confident with re-using Ids, that's fine (as long as you use a fast lookup / hash table), but consider accessing NULL handles to be a bad thing at least.

Share this post


Link to post
Share on other sites
I use references to pass around.

Since the code requires either serialized references or local objects, I use a single reference that stores the serialized id and smart pointer.

Then I overload the -> and * operators to make handling transparent.

Something like this:

template < class T >
class ObjectRef
{
public:
ObjectRef( ObjectPtr ptr )
: m_ptr(ptr)
, m_guid(ptr->guid())
{}

ObjectRef( ObjectGUID guid )
: m_ptr(NULL)
, m_guid(guid)
{}

T operator ->() {
{
if (!resolve() ) throw ...
return m_ptr;
}
private:
bool resolve()
{
if ( m_ptr.is_null() ) {
// do lookup to obtain instance
if ( ... not found ... )
return false;
else
m_ptr = ....
}
return true;
}

ObjectGUID m_guid;
ObjectPtr m_ptr;
}




The scripts are passed the ObjectRefs.


void invoke_action( EntityRef &ref )
{
... script( ref )
}




Here, I don't fuss much if object gets "destroyed" while script is holding a reference to it. Smart pointers make sure the object is still around, it's just disconnected from the event system.

And since all scriptable actions can only hold references for duration of function call, this overlap is minimal.

This isn't optimal, and I use it in networked game, so my requirements might be somewhat different, but they do the task.

When an object is destroyed, it's simply removed from global directory. Smart pointers take care of the rest.

In addition, it allows me to defer resolution of serialized references until they are needed - some might never need to be resolved.

Share this post


Link to post
Share on other sites
This reminds me of a minor bug in Warcraft 2. Summoned skeletons have a timed lifespan, after which they automatically die. A player can load a skeleton onto a transport ship and then wait for it to die. The slot on the transport will then appear empty. The next unit created will appear inside of the transport instead of where it was supposed to. Even units that shouldn't be allowed on the transport, like flying units, other ships, enemy units, and buildings. The bug wasn't that serious because summoned skeletons were fairly worthless and have a long lifespan, so it didn't happen in real games.

Just an example that shows this sort of bug has actually occured in commercial games.

Share this post


Link to post
Share on other sites
But how can I fix it? I'd like to be able to keep references in scripts for longer than the duration of one script. And I'm not sure how I'm supposed to go about handling the removal of all references. I could do that, but I'm not sure how that will work for scripts. For example, if Entity A gets a reference to B, then B dies. What should A do if it's handle to B disappears? Suddenly it'll be accessing a non-existant variable - the very thing I was trying to avoid when I created EntityHandles.

Edit: Though I don't really see how it takes care of the problem at hand, I really must say that I like your system Antheus. It seems like just the thing I was looking for to avoid having to do a lookup in an std::map every single function call.

Share this post


Link to post
Share on other sites
Essentially you have a requirement, which is to ensure that your references stay safe to use. Either that can be by magically removing all references to something unsafe, or it can be by making references to dead things just as safe as references to living things. The first way requires that each entity knows who refers to it (eg. observer pattern, smart pointers), but gives you the benefit of knowing that you never have null references.

Either way requires that you have a system in place for handling what happens to a script with a null reference. If your language uses exceptions, defining a standard exception for this purpose makes sense. If not, you have various choices - the 'generic nothingness' of the original post is one option, though perhaps not the easiest to debug when scripts misbehave.

In other words, I think ApochPiQ is perhaps overstating the problems. It's safe to keep dangling references if your references are safe to use, and if you don't recycle ids. If either of those are false, you need to get rid of references as soon as they become invalid. That's all.

Share this post


Link to post
Share on other sites
Okay, neither of those are false for me, so I'll just keep it how it is right now, but I'll throw an exception if it no longer exists and is accessed.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
  • Advertisement
  • Popular Tags

  • Advertisement
  • Popular Now

  • Similar Content

    • By 3dmodelerguy
      So I am building a turn based rogue-like (think CDDA). The game is going to have a very large map (up to 1000's x 1000's) however to alleviate most of that I obviously can't render everything so there will just be render a certain radius around the player and just load in and out data as the player moves.
      The next major system I am prototyping is making interactive tiles destructible and pretty much everything will be destructible besides basic landscape (cars, doors, windows, structures, etc. will be destructible)
      While I am only rendering a certain amount of tiles around the player, I want to keep the amount of colliders active at one time to be as small as possible for performance and currently the tilemap tool I use automatically merges colliders together.
      So instead of creating a separate colliders for each of these tiles and having the destructible behavior tied to that object (which my tilemap tool would allow me to do) I was thinking that I would store an array of all the X and Y locations for the interactive tilemap layer and let the tilemap manage the colliders. 
      Then when I hit a collider on the interactive tilemap layer, instead of of getting the behavior for how to deal with the destruction for that tile from that game object, I would pull it from the array I mentioned earlier based on the tile I attempt to interact with which I already have.
      Does this sound like a good approach? Any other recommendations would be welcomed.
    • By NDraskovic
      Hey guys,
      I have a really weird problem. I'm trying to get some data from a REST service. I'm using the following code:
       
      private void GetTheScores() { UnityWebRequest GetCommand = UnityWebRequest.Get(url); UnityWebRequestAsyncOperation operation = GetCommand.SendWebRequest(); if (!operation.webRequest.isNetworkError) { ResultsContainer rez = JsonUtility.FromJson<ResultsContainer>(operation.webRequest.downloadHandler.text); Debug.Log("Text: " + operation.webRequest.downloadHandler.text); } } The problem is that when I'm in Unity's editor, the request doesn't return anything (operation.webRequest.downloadHandler.text is empty, the Debug.Log command just prints "Text: "), but when I enter the debug mode and insert a breakpoint on that line, then it returns the text properly. Does anyone have an idea why is this happening?
      The real problem I'm trying to solve is that when I receive the text, I can't get the data from the JSON. The markup is really simple:
      [{"id":1,"name":"Player1"},{"id":2,"name":"Player2"}] and I have an object that should accept that data:
      [System.Serializable] public class ResultScript { public int id; public string name; } There is also a class that should accept the array of these objects (which the JSON is returning):
      [System.Serializable] public class ResultsContainer { public ResultScript[] results; } But when I run the code (in the debug mode, to get any result) I get an error: ArgumentException: JSON must represent an object type. I've googled it but none of the proposed solutions work for me.
      Also (regardless if I'm in the debug mode or not) when I try to do some string operations like removing or adding characters to the GET result, the functions return an empty string as a result
      Can you help me with any of these problems?
      Thank you
    • By nihitori
      The Emotional Music Vol. I pack focuses on beautiful and esoteric orchestral music, capable of creating truly emotive and intimate moods. It features detailed chamber strings, cello and piano as the main instruments, resulting in a subtle and elegant sound never before heard in video game royalty-free music assets.

      The pack includes 5 original tracks, as well as a total of 47 loops based on these tracks (long loops for simple use and short loops for custom / complex music layering).

      Unity Asset Store link: https://www.assetstore.unity3d.com/en/#!/content/107032
      Unreal Engine Marketplace link: https://www.unrealengine.com/marketplace/emotional-music-vol-i

      A 15 seconds preview of each main track is available on Soundcloud:
       
    • By RoKabium Games
      Another one of our new UI for #screenshotsaturday. This is the inventory screen for showing what animal fossils you have collected so far. #gamedev #indiedev #sama
  • Advertisement