Forum rules - please read before posting.

Copy-Paste multiple Actions as JSON?

I digged about how to link Ink from Inkle to AC.
So far, may be a nice way would be to be able to make an external JSON "adapter" (being able to read JSON from AC, adapt Ink and paste back to create a list of linked actions).

So first step would be to be able to copy and paste ActionList as JSON.
I don't want to hack AC but here's a preview :)
image

Do you think this would be valuable for AC? (I do)

Comments

  • edited May 2018
    For something like that to be included, it would have to be robust enough to not cause knock-on problems to occur.  I suspect that the dependency of e.g. scene object references etc would cause issues.

    Apart from the Inspector changes, I don't think there'd be another need for any hacking to be involved.  Aside from the convenience, it'd be possible to do via another script.

    Here's a test script for you to test out:
    http://pasteall.org/962253/csharp

    It'll make a new "Test" menu item in the "Adventure Creator" top toolbar.  It only works for scene-based ActionLists, but replace with ActionList with ActionListAsset
    and it should work for assets instead.
  • Fantastic!
    Thank you for providing such a great and detailed answer!
    I really appreciate :)

    I didn't know how to "piggy back" the menu and you provide a great solution.
    I replaced ActionList with ActionListAsset thinking that it will collect all actions present in the asset, but it only gives the root level and points to instanceID for actions:


    But, I can see actions are present in the .asset file.
    • messageText field is the most important for Ink.
    • constantID would be great to fill in based on Unity constantID assignment (prior to Paste).
    Since we can "Add/Insert" a new action, I guess we could "insert using JSON" multiple actions?

    I guess skipActionActual with fileID are out of scope and it means we'll have to wire actions within Unity (or else?). On forums, this fileID/instanceID seems to be a pain to develop external tools.


  • I just tried with EditorJsonUtility.ToJson which results in a slightly different result:
    image
    It's level 0 of the asset. No sub-levels (actions).

    It looks like array is the problem. Both for copying and pasting.
    I tried to crawl the forum without luck.

    Which leads to questions:
    What is expected when clicking "Add new Action"?
    Could we create multiple actions at once?
    Filling them with Json array content?

  •  Ok, I spent the whole day on this.
    I'm slow, partially because I suck at coding (hence me buying AC) and partially because JsonUtility sucks too (big time).

    In the end I added JSON .NET from asset store (Newtonsoft.Json) because it serializes "everything" then I tried:
    dataString = JsonConvert.SerializeObject(actionList);

    But when I try to get the JSON, I get an error about ActionSpeech (within AC?):
    UnassignedReferenceException: The variable headClip of ActionSpeech has not been assigned.
    You probably need to assign the headClip variable of the ActionSpeech script in the inspector.
    UnityEngine.AnimationClip.get_events () (at C:/buildslave/unity/build/artifacts/generated/bindings_old/common/Animation/AnimationsBindings.gen.cs:154)
    (wrapper dynamic-method) UnityEngine.AnimationClip.Getevents (object) <IL 0x00006, 0x00073>
    Newtonsoft.Json.Serialization.DynamicValueProvider.GetValue (object) <IL 0x00025, 0x000ed>
    Rethrow as JsonSerializationException: Error getting value from 'events' on 'UnityEngine.AnimationClip'.
    Newtonsoft.Json.Serialization.DynamicValueProvider.GetValue (System.Object target)
    Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.CalculatePropertyValues (Newtonsoft.Json.JsonWriter writer, System.Object value, Newtonsoft.Json.Serialization.JsonContainerContract contract, Newtonsoft.Json.Serialization.JsonProperty member, Newtonsoft.Json.Serialization.JsonProperty property, Newtonsoft.Json.Serialization.JsonContract& memberContract, System.Object& memberValue)
    Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject (Newtonsoft.Json.JsonWriter writer, System.Object value, Newtonsoft.Json.Serialization.JsonObjectContract contract, Newtonsoft.Json.Serialization.JsonProperty member, Newtonsoft.Json.Serialization.JsonContainerContract collectionContract, Newtonsoft.Json.Serialization.JsonProperty containerProperty)
    Rethrow as TargetInvocationException: Exception has been thrown by the target of an invocation.
    System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Reflection/MonoMethod.cs:232)
    System.Reflection.MethodBase.Invoke (System.Object obj, System.Object[] parameters) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Reflection/MethodBase.cs:115)
    UnityEditor.HostView.Invoke (System.String methodName, System.Object obj) (at C:/buildslave/unity/build/Editor/Mono/HostView.cs:291)
    UnityEditor.HostView.Invoke (System.String methodName) (at C:/buildslave/unity/build/Editor/Mono/HostView.cs:284)
    UnityEditor.HostView.InvokeOnGUI (Rect onGUIPosition) (at C:/buildslave/unity/build/Editor/Mono/HostView.cs:251)
    What's up with ActionSpeech?

    (Or may be another solution would be to foreach each action, but it doesn't recognize ActionList as a valid type.
    To help, most useful posts I found were: https://stackoverflow.com/questions/36239705/serialize-and-deserialize-json-and-json-array-in-unity/...
    and https://blog.csdn.net/xdestiny110/article/details/79596953 (end of the page))

    I can't do more but wait for your yoda's vision on it :)

  • My own testing found no issue with a scene-based ActionSpeech, but I didn't try assets yet.

    My mistake about assets being as simple - ActionListAssets actually hide their Actions sub-assets within them, so in hindsight I don't know how possible it can be.  I shall to some further testing, however.

    Copy/pasting individual Actions as Json data is risky, because of their inderdependency.  You could likely make an "insert after", but don't expect them to connect with each other all as intended.  AC's ActionListEditorWindow does a lot of heavy lifting to have each inserted Action connect automatically.  But if you want to give it a go, it's really just a case of modifying the internal "actions" List.

    A few other thoughts:

    1) Since scene-based ActionLists seem to be OK, bear in mind that ActionLists can be converted between scene-based and asset files via the cog icon at the top of their Inspector.

    2) If you're mainly interested in transferring speech lines, it might be easiest to have your "dialogue sequence" as a separate ActionList, and then call upon from a separate one with ActionList: Run - that way you can leave other Actions that you don't care about editing in Ink out of the process.

    3) I'm fairly sure Dialogue System, which has a robust AC integration, has Ink/Json support - though you'll need to contact the developer for confirmation.  It would be worth looking into as an alternative.
  • Thank you,
    1:30AM, you're passionate!

    "My own testing found no issue with a scene-based ActionSpeech"
    Do you mean you get the actions details (not fileID or instanceID) in the JSON string?
    I get the same error about headClip.

    Sideline, I had to re-import the package, I had error:
    Assets/AdventureCreator/Scripts/ActionList/ActionListAsset.cs(31,15): error CS0101: The namespace `AC' already contains a definition for `ActionList'

    File has been modified ("public class ActionListAsset : ScriptableObject"  to     "public class ActionList : ScriptableObject") but I didn't.

    "it's really just a case of modifying the internal "actions" List"
    How?
    "You could likely make an "insert after""
    How?

    1) yes. I focused on asset to avoid breaking embedded actions (I made a copy of asset file)
    2) totally!
    3) Out of budget and limited import. I prefer JSON to really see the limitations.




  • Ok, dataString = JsonUtility.ToJson(actionList.actions[1]); throws the right thing.
    So it's a question of wrapping (and unwrapping) the nested action inside the actionList.
    I don't get how to use ISerializationCallbackReceivers to parse/serialize on the fly so I'm going the ugly regex way on the string:
    dataString = Regex.Replace(JsonUtility.ToJson(actionList),"\"actions.+?],","");
                dataString = Regex.Replace(dataString,"}", ",\"actions\":[");
                var i = 0;
                    foreach (var item in actionList.actions)
                {
                    item.id = i;
                    dataString = dataString + JsonUtility.ToJson(item) + ",";
                    i++;
                };
                dataString = Regex.Replace(dataString, "(.+),$", "$1]}");

    which is a valid (complete?) JSON string to work on.
    But how to "paste" this as actions? Would it work?

    What's the best approach to serialize/deserialize these nested actions?



  • Here's the (too) verbose result:
    image

  • > "(paste as is) would it work?"
    No it doesn't:
    NullReferenceException: Object reference not set to an instance of an object AC.ActionsManager.GetActionTypeIndex (AC.Action _action) (at Assets/AdventureCreator/Scripts/Managers/ActionsManager.cs:614) AC.ActionListAssetEditor.OnInspectorGUI () (at Assets/AdventureCreator/Scripts/ActionList/Editor/ActionListAssetEditor.cs:111) UnityEditor.InspectorWindow.DrawEditor (UnityEditor.Editor[] editors, Int32 editorIndex, Boolean rebuildOptimizedGUIBlock, System.Boolean& showImportedObjectBarNext, UnityEngine.Rect& importedObjectBarRect) (at C:/buildslave/unity/build/Editor/Mono/Inspector/InspectorWindow.cs:1295) UnityEngine.GUIUtility:ProcessEvent(Int32, IntPtr)
    • 1:30AM, you're passionate!
    I'm afraid the post time is local to the viewed, not the author.

    • Do you mean you get the actions details (not fileID or instanceID) in the JSON string?
    I was mistaken - I was just getting the instanceID.  I didn't pick up on this because if the ActionList being read is still present in the scene, then deserializing the Json data somehow re-links to its Actions.  So, if you refresh Unity or delete the original ActionList, that data is then gone.

    • What's the best approach to serialize/deserialize these nested actions?
    This is pretty complicated, because of the nature of Actions: subclassed ScriptableObjects living as child assets within a larget asset.  No Action in an ActionList is actually an instance of the "Action" class - only a subclass, e.g. "ActionPause" (for Engine: Wait).

    What you essentially need to do is store each Action as an individual Json string, and also record its class type so that you can then create the correct class type before serialization.  To also save e.g. the properties of the ActionListAsset itself, that too will need to be in a separate Json string.

    In the specific case of ActionList asset files, you also need to use Unity's AssetDatabase methods to import each new Action as a separate asset, and merge it with the ActionListAsset itself.

    Here's an example script.  Unlike the last one, this is a component that goes on an empty GameObject in your scene.  When you run the scene, however, you'll see two buttons in the top-left corner to copy/paste ActionListAsset data.  Because it's in the Inspector, it will also display the recorded Json data neatly in the component.

    http://pasteall.org/964800/csharp

    • How?
    A List type can be modified using the .Add, .Insert .InsertAt methods.  This isn't a coding forum, but you can find plenty of info on them on e.g. https://msdn.microsoft.com/en-us/library/6sh2ey19(v=vs.110).aspx

    The above script shows how an entire ActionList asset can be serialized/deserialized, but it should be possible to take the same principle and apply it to individual Actions, however.  See if the script works first, though.

  • You really help me, I appreciate and left a dithyrambic review :) Thank you.
    If I can help, let me know.

    I understand more about coding mechanic (I still don't get why it has to be playing though), it's not my cup of coffee.
    I would love something visual tool (flow diagrams, blocks, ...) to grasp the what-where-when-why of C# (if you know any, I'm all ears, I tried quick diagramming in VS but it's limited to class hierarchy).
    I learn code better with blocky (you see all possibilities of a class or function at once). AFAIK, It doesn't exist for C#.

    I tried Dialogue System demo but didn't find a tutorial about linking ink->DS->AC to work together (AC oriented).
    I suss DS will come with limitations about ink because it's not the primary goal of DS (almost a competition), hence me trying to get JSON format and start from here (I guess I can adapt JSON export format from ink (JS) for my need, maybe I'm wrong).

    I understand how to write the json strings and class names to a file but I'm scratching my head about parsing correctly all this back to a Unity ScriptableObject ("from Json" is re-using the formatted object). I'll try and may come back to ask help on this detail :)

    It may interest others to extend and share this WIP and we could collectively get some nice external scripting tool for narrative. It's not my call to make/sell/support a Unity Asset or plugin though (future reader, please don't contact me for this).

    In addition, if you feel this post is turning to competing another solution (future AC, DS, ...), feel free to delete it.


  • The script only works when the game is running because it's a regular Unity component - not an Editor window.  It can be made into an Editor window, but this is a quick-and-easy way of testing the functionality beforehand.  Did you find it working?

    The author of Dialogue System, Tony, is very approachable - I'm sure he can advise you on how appropriate using DS/Ink/AC all together are.
  • "Did you find it working?"
    Yes, it does work when JSON object is "exported" (to memory) then "re-imported" using existing actionJsonData frame.

    Importing a JSON file from scratch is another story (parse className from within the file, create an empty frame from ... nowhere(?)), there are details I don't get yet.
    It's close, but it's far :) And I'd like to focus on my game.
    May be Dialogue is the way to go. I'll ask Tony.
  • I'm coming back on my topic to focus on a single Action in an ActionList Asset (not Inkle this time but simply any AC action in an existing ActionList Asset (ALA) or ActionList).
    How could we update (not create) an existing action with JSON at runtime ... in a build?
    So not using AssetDatabase or other Editor functions.

    I don't want to go the Assetbundle way for ALA, because if ALA is in a bigger bundle (Scene, GO, ...), a version update can break everything (including the ALA import in previous sold builds) when updating Unity (see comment https://blogs.unity3d.com/2017/04/12/asset-bundles-vs-resources-a-memory-showdown/#comment-349176). Technically it's not a bug (since it's called a new feature or an improvement of shaders), but it remains a dev's problem in the long run. Rather find another way before it happens :)
    Or could we have (dummy) "Action Asset" (the same way as ALA) that we add to an ActionList or ALA and we update with JSON?
    AFAIK, it's not a job for Custom Action since we cannot inject a script (hopefully on the security side!).


  • Since you're looking to update Unity asset files at runtime, whether that's possible or not is really a question for Unity's technical team.

    I should think, though, that you can omit the UnityEditor calls (which save the changes) and have it still work - so long as you run this operation each time the game is launched (since changes won't be saved).

    Because of the inter-dependency of Actions, though (what connects to what, etc), it would be best to update an entire list if you can.  Having an ActionList: Run Action re-route to the ActionList you want to update would allow you to have a separate list with just one Action that you can update.

    The only key difference between your final script and this one, then, would be in the way that the commands are called.  A quick tweak can move the key lines into their own public functions:


    That then means you can call e.g. the JSonToActionList method from a separate script when ready.  The UnityEditor function calls are also hidden behind UNITY_EDITOR preprocessors, so they should be ignored when running in a build.
  • An Actionlist Asset of one action is self contained! Thanks for pointing that, I forgot to check.

    "That then means you can call e.g. the JSonToActionList method from a separate script when ready.  The UnityEditor function calls are also hidden behind UNITY_EDITOR preprocessors, so they should be ignored when running in a build."
    I added the UNITY_EDITOR conditions but then it can't use AssetDatabase

    How to write the JSON to the asset at runtime in a build?


    • How to write the JSON to the asset at runtime in a build?
    Like I said - I don't know if it's possible, and it's a question for Unity.  But the code I posted is how it would be done if it is.

    As I also explained - AssetDatabase won't/can't be called, which means any changes you make won't be saved upon restarting the game, which means you'll have to make such changes each time the game begins.
  • Hmmm, it's a dead end I think.
    I know how to add a new OBJ from file at runtime but not how to write an action on the fly before executing it. I definitely need it and will have to search for another solution.
    Sometimes, time gives the answer, if you find a way to write into an ActionList at runtime ...
  • I was plain wrong because I mixed the Asset+Editor case with the Action+Runtime case.
    You provided the reply and I didn't see it. My bad, sorry.
Sign In or Register to comment.

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Welcome to the official forum for Adventure Creator.