Create ScriptableObject in Python when building ADX2

Introduction

In Unity both in Editor and at runtime I wanted to access various information about ADX2.

This article is probably a bit more difficult (lack of explanation).

If it's just the queue name, the previous article Introduction of useful scripts such as copying to Unity when building ADX2 Please also.

the term

About ScriptableObject

kan_kikuchi's page What is ScriptableObject? [Unity] [ScriptableObject]

ADX2 Sound middleware ADX2LE can be used for free (conditions apply)

ptyhon Here

Information for whom?

・ People who want to write code that generates ScriptableObject ・ People who want to use ADX2 xml output somehow ・ People who want to parse xml with python and generate c # code

Why did you make it?

Get information that is a little difficult to retrieve with the ADX2 standard method

There is information that can be obtained by the Acb function at the time of execution, and via the cs file output at the time of build. I wanted to manage only the necessary information in one place.

Information that cannot be obtained directly

--Queue comment information --A list of block names used by the queue --A list of AISAC control names used by the queue

I wanted to extract information that could be used for something.

("Something" here may be an in-house tool.)

What can i do?

After building with ADX2 When executed from the menu on Unity

image.png

Generate an .assets file that looks like this. It is made as many as the number of cue sheets.

Easy to use from the program

If you refer to this generated ScriptableObject from the Unity object, you can handle the ACB information of ADX2 in every situation. Also, since it is automatically updated at build time (menu operation is required *), information transmission mistakes can be reduced.

("Every scene" here refers to runtime, editor, build, etc.)

Utilization of comments

Also, comments and other "comments" can be added to each queue for those who are only touching with Unity. You can see it in the editor extension, so it seems good to use various comments

For example, "Temporary data" or "Update date" (ADX2-> One-way comment to Unity, so it's not bidirectional ...)

Parse xml with python

Use python to parse xml. Select the necessary information with python and Generate the code that creates the ScriptableObject.

About AISAC

I'd like to know the AISAC control name, but when referring to the global AISAC, it is not output to XML, so As a naming convention Name @ control name Adopted

image.png

ADX2 build settings in AtomCraft

--Check "acb_info" XML output --Turn off layered output

I'm calling python in post process (The file names are slightly different here, but they are called in the same way.)

image.png

ScriptableObject class

If you add the necessary information, it should be convenient to use the information from XML in Unity.

 ADXAcbData.cs


using System;
using System.Collections.Generic;
using UnityEngine;

namespace MyDearest {
	[CreateAssetMenu (menuName = "Sound/ADX Acb Data")]
	public class ADXAcbData
		: ScriptableObject
	{
		[Serializable]
		public class CueData {
			public string Name = "";
			public List<string> BlockNames = new List<string> ();
			public List<string> AisacNames = new List<string> ();
			public string Comment = "";
			public string UserData = "";
		}
		public string Name = "";
		public List<CueData> Cues = new List<CueData> ();

		private List<string> _cueNames = new List<string> ();
		/// <summary>
		///Returns a list of queue names
		/// </summary>
		/// <returns></returns>
		public string[] CueNames () {
			if (_cueNames.Count > 0) return _cueNames.ToArray ();
			foreach (CueData cuedata in Cues) {
				_cueNames.Add (cuedata.Name);
			}
			return _cueNames.ToArray ();
		}
	}
}

Code that creates a ScriptableObject

Since it will be longer by the number of data, only one queue is omitted here.

AcbDataCreators.cs


using UnityEngine;
using UnityEditor;
namespace MyDearest {
	public static class AcbDataCreator {
		[MenuItem ("Sound/CreateAcbData")]
		private static void Create () {
			{
				ADXAcbData acb = ScriptableObject.CreateInstance<ADXAcbData> ();
				acb.Name = "BGM";
				{
					ADXAcbData.CueData cueData = new ADXAcbData.CueData ();
					cueData.Name = "Chronos";
					acb.Cues.Add (cueData);
				}
				AssetDatabase.CreateAsset (acb, "Assets/MyDearest/Chronos/Data/Sound/BGM.asset");
			}
		}
	}
}

Generate Unity CS file with python

How to create a ScriptableObject in Unity

I wish I could create a ScriptableObject directly from python, but I didn't know a little about Unity's YAML, so I am making the source code of the menu to make ScriptObject with unity.

It's a little hard to see because the code is generated at the same time as the analysis, but it's easy to understand what you're doing.

Please correct the path name as appropriate.

adx2xml_to_str_cs.py


#print("Create ScriptableObject from ADX2 xml output C#Generate code")

import xml.etree.ElementTree as ET
import os

g_currentCueName = ""  #Queue name
g_currentCueSheetName = "" #Cue sheet name


def writeHeader(outstr):
    outstr += "using UnityEngine;\n"
    outstr += "using UnityEditor;\n"
    outstr += "namespace MyDearest {\n"
    outstr += "	public static class AcbDataCreator {\n"
    outstr += "		[MenuItem (\"Window/MDSound/CreateAcbData\")]\n"
    outstr += "		private static void Create () {\n"

    return outstr

def writeFooter(assetoutpath,outstr):
    outstr += "\t\t\t\t\tacb.Cues.Add (cueData);\n"
    outstr += "\t\t\t\t}\n"
    outstr += "\t\t\t\tEditorUtility.SetDirty (acb);\n"
    outstr += "\t\t\t}\n"
    outstr += "\t\t}\n"
    outstr += "\t}\n"
    outstr += "}\n"

    return outstr
        
def printOrcaName(nest,child,xmlpath,outpath,assetoutpath,outstr):
    global g_currentCueName,g_currentCueSheetName
    nestspacestr = ""
    for i in range(nest):
        nestspacestr +=" "
    
    if(child.get("OrcaType") == "CriMw.CriAtomCraft.AcCore.AcOoCueSheet"):        
        print("Cue sheet name" + child.get("OrcaName"))   #Cue sheet

        if(g_currentCueSheetName != "" and g_currentCueSheetName != child.get("OrcaName")):   #Only when the cue sheet changes
            outstr += "\t\t\t\t\tacb.Cues.Add (cueData);\n"
            outstr += "\t\t\t\t}\n"
            outstr += "\t\t\t\tEditorUtility.SetDirty (acb);\n"
            outstr += "\t\t\t}\n"


        g_currentCueSheetName = child.get("OrcaName")
        g_currentCueName = ""
        outstr += "\t\t\t{\n"        
        outstr += "\t\t\t\tADXAcbData acb = (ADXAcbData)AssetDatabase.LoadAssetAtPath (\"" + assetoutpath + g_currentCueSheetName + ".asset\", typeof (ADXAcbData));\n"
        outstr += "\t\t\t\tif (acb == null) {\n"
        outstr += "\t\t\t\t        acb = ScriptableObject.CreateInstance<ADXAcbData> ();	//Make when not\n"
        outstr += "\t\t\t\t        AssetDatabase.CreateAsset (acb, \"" + assetoutpath + g_currentCueSheetName + ".asset\");\n"
        outstr += "\t\t\t\t        acb = (ADXAcbData)AssetDatabase.LoadAssetAtPath (\"" + assetoutpath + g_currentCueSheetName + ".asset\", typeof (ADXAcbData));\n"
        outstr += "\t\t\t\t}\n"
        outstr += "\t\t\t\tacb.Cues.Clear ();\n"
        outstr += "\t\t\t\tacb.Name = \"" + g_currentCueSheetName + "\";\n"
    if(child.get("OrcaType") == "CriMw.CriAtomCraft.AcCore.AcOoCueFolder"):
        print(nestspacestr + "Queue folder name" + child.get("OrcaName"))
    if(child.get("OrcaType") == "CriMw.CriAtomCraft.AcCore.AcOoCueSynthCue"): #queue
        print(nestspacestr + "Queue name" + child.get("OrcaName"))

        if(g_currentCueName != "" and g_currentCueName != child.get("OrcaName")):   #Close when the queue changes
            outstr += "					acb.Cues.Add (cueData);\n"
            outstr += "				}\n"

        g_currentCueName = child.get("OrcaName")
        outstr += "				{\n"
        outstr += "					ADXAcbData.CueData cueData = new ADXAcbData.CueData ();\n"
        outstr += "					cueData.Name = \"" + g_currentCueName + "\";\n"
        
        if 'UserData' in child.attrib:
            outstr += "					cueData.UserData = @\"" + child.get("UserData") + "\";\n"
        if 'Comment' in child.attrib:
            outstr += "					cueData.Comment = @\"" + child.get("Comment") + "\";\n"
            
    if(child.get("OrcaType") == "CriMw.CriAtomCraft.AcCore.AcOoAisac"):
        print(nestspacestr + "AISAC control name" + os.path.basename(child.get("AisacControl")))
        outstr += "					cueData.AisacNames.Add (\"" + os.path.basename(child.get("AisacControl")) + "\");\n"
    if(child.get("OrcaType") == "CriMw.CriAtomCraft.AcCore.AcOoAisacLink"):
        print(nestspacestr + "AISAC control name" + os.path.basename(child.get("LinkAisac")).split('@')[1])  # Distance@With a naming convention like Distance@Behind the control name((Because LinkAisac does not have AisacControl)
        outstr += "					cueData.AisacNames.Add (\"" + os.path.basename(child.get("LinkAisac")).split('@')[1] + "\");\n"
    if(child.get("OrcaType") == "CriMw.CriAtomCraft.AcCore.AcOoBlock"):
        print(nestspacestr + "Block name" + child.get("OrcaName"))
        outstr += "					cueData.BlockNames.Add (\"" + child.get("OrcaName") + "\");\n"

    return outstr

#Parse XML to generate scriptable object generation code
def conv(xmlpaths,outpath,assetoutpath):
    outstr = "";    
    outstr = writeHeader(outstr)

    for xmlpath in xmlpaths:
        tree = ET.parse(xmlpath) 
        root = tree.getroot()
        for child in root:
            for child1 in child:
                for child2 in child1:
                    for child3 in child2:
                        outstr = printOrcaName(0,child3,xmlpath,outpath,assetoutpath,outstr)
                        for child4 in child3:
                            outstr = printOrcaName(1,child4,xmlpath,outpath,assetoutpath,outstr)
                            for child5 in child4:
                                outstr = printOrcaName(2,child5,xmlpath,outpath,assetoutpath,outstr)
                                for child6 in child5:
                                    outstr = printOrcaName(3,child6,xmlpath,outpath,assetoutpath,outstr)
                                    for child7 in child6:
                                        outstr = printOrcaName(4,child7,xmlpath,outpath,assetoutpath,outstr)


    outstr = writeFooter(assetoutpath,outstr)
    
    print(outstr)
    with open(outpath,"w",encoding="utf-8") as f:
        f.write(outstr)

#Build output destination (no layered output)
adx2outputpath = "C:/MyDearest/CraftData/OculusAdxTest/PC/"
#XML list to be analyzed
cuesheetXmlNames = [adx2outputpath + "BGM_acb_info.xml",
                 adx2outputpath + "SE_acb_info.xml",
                 adx2outputpath + "VOICE_acb_info.xml"]

#Specify the location of the actual conversion and generated cs file and the location of the assets generated by the generated file
conv(cuesheetXmlNames,
     "C:/MyDearest/github/Chronos/Assets/MyDearest/Sound/Editor/AcbDataCreator.cs",
     "Assets/MyDearest/Chronos/Data/Sound/")

I'm sorry Recursive parsing of xml is a bit disappointing & messy code, but please add it according to the complexity of the data.

in conclusion

As an application It's easy to parse xml and create a text file, so It may also be used for checking purposes, version control, and cooperation with ci tools.

The code here is not supported, but for example

--Queue folder hierarchy information (administrative path in Craft) --Arrangement time on the sequence --Beat information --Block length

I think I can take it out. However, if you blindly take out all of them, it will be huge and difficult to handle & data will be bloated. I think it's good to be able to retrieve only the necessary information.

It may be used in the future for purposes such as replacing data at build time or deleting debug information (although I haven't tried it yet).

Also, it may be possible to upload information to the cloud at the time of building, check for updates from there, and automatically generate it.

Recommended Posts

Create ScriptableObject in Python when building ADX2
Create SpatiaLite in Python
Create a function in Python
Create a dictionary in Python
Create gif video in Python
Attention when os.mkdir in Python
Precautions when using pit in Python
Error when building mac python environment
Create a DI Container in Python
Behavior when listing in Python heapq
Create a binary file in Python
Create Gmail in Python without API
Create Python project documentation in Sphinx
Create a Kubernetes Operator in Python
Create a random string in Python
When using regular expressions in Python
When writing a program in Python
Create and read messagepacks in Python
When specifying multiple keys in python sort
Create your own Linux commands in Python
Precautions when pickling a function in python
[LLDB] Create your own command in Python
Create a simple GUI app in Python
Create a JSON object mapper in Python
Create Qt designer parts in Python (PyQt)
When looking at memory usage in Python 3
[GPS] Create a kml file in Python
[Tips] Easy-to-read writing when connecting functions in Python
Quadtree in Python --2
When codec can't decode byte appears in python
Python in optimization
Metaprogramming in Python
Create a Vim + Python test environment in 1 minute
Python 3.3 in Anaconda
Geocoding in python
SendKeys in Python
Create a GIF file using Pillow in Python
Meta-analysis in Python
Building an environment that uses Python in Eclipse
Unittest in python
I want to create a window in Python
Create a standard normal distribution graph in Python
When I try matplotlib in Python, it says'cairo.Context'
How to create a JSON file in Python
Automatically create word and excel reports in python
Epoch in Python
Create a virtual environment with conda in Python
Discord in Python
Sudoku in Python
DCI in Python
quicksort in python
Precautions when dealing with control structures in Python 2.6
nCr in python
N-Gram in Python
Note on encoding when LANG = C in Python
Programming in python
Plink in Python
Constant in python
Create an image with characters in python (Japanese)
Character encoding when dealing with files in Python 3
Think about building a Python 3 environment in a Mac environment