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.
kan_kikuchi's page What is ScriptableObject? [Unity] [ScriptableObject]
ADX2 Sound middleware ADX2LE can be used for free (conditions apply)
ptyhon Here
・ 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
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.
--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.)
After building with ADX2 When executed from the menu on Unity
Generate an .assets file that looks like this. It is made as many as the number of cue sheets.
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.)
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 ...)
Use python to parse xml. Select the necessary information with python and Generate the code that creates the ScriptableObject.
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
--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.)
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 ();
}
}
}
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");
}
}
}
}
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.
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