... Challenge accepted!
It turned out to be quite a simple task, in the end. I wrote the export code as an editor extension, which would save the scene in my own binary format. This way, I can design a level just like I would when developing a "full" Unity3D game.
So, let's begin! First, create a new Unity3D project:
After you've created the project, create a new C# script. I called it "ExportScript.cs":
Now, the actual script! In its basic form, the export script is like this:
using UnityEditor; using UnityEngine; using System.Collections; using System.IO; // This class is responsible for exporting the scene to our own file format public class ExportScript : EditorWindow { [MenuItem("File/Export")] public static void ShowWindow() { EditorWindow.GetWindow(typeof(ExportScript)); } void OnGUI() { // Create a button that will start the export function GUILayout.Label("Export Level", EditorStyles.boldLabel); if(GUI.Button(new Rect(10, 50, 100, 32), "Export")) { // Start the export Export(); } } // Our output writer BinaryWriter writer; // The export function void Export() { // Create a new output file stream writer = new BinaryWriter(new FileStream("out.lvl", FileMode.Create)); // TODO: Write all the data we need (gameobjects, lights, components etc.) // All done, close the writer writer.Close(); } }And that's it! Now, what do we actually want to write to the file? It depends. In my own game project, I wanted to get textures, meshes, game objects, lights, colliders, joints, sounds, background music... Just about everything contained in the scenes. I will not show all the code for this as it would be too much, but the basic concept goes like this: First, write the basic information about the game objects, like name, location, orientation, and scale. Then, for each component you require, check if the game object has that component (say, a mesh filter), and if it does, write all the information available for that component.
It's really simple to do, although it takes some time to write all the component stuff down. When doing this, Unity3D's script reference helps a lot, and you can always check the inspector view to see if you've missed something. :)
Anyhoo, let's take a look at the final example. In this example, the script goes through all the game objects in the scene and saves their names and transforms, as well as checks for the mesh filter components.
void Export() { // Create a new output file stream writer = new BinaryWriter(new FileStream("out.lvl", FileMode.Create)); // Loop through the transforms in the scene, exporting only what's necessary foreach(Transform t in Object.FindObjectsOfType(typeof(Transform))) { Debug.Log("Exporting " + t.gameObject.name); WriteTransform(t); } // All done, close the writer writer.Close(); } // Writes the transform object void WriteTransform(Transform t) { // Write the basic transform components (location, orientation, scale) writer.Write(t.position.x); writer.Write(t.position.y); writer.Write(t.position.z); writer.Write(t.rotation.x); writer.Write(t.rotation.y); writer.Write(t.rotation.z); writer.Write(t.rotation.w); writer.Write(t.localScale.x); writer.Write(t.localScale.y); writer.Write(t.localScale.z); // Write the mesh renderer (cast / receive shadows, materials) WriteMeshRenderer(t.GetComponent<meshrenderer>()); // Write the actual mesh WriteMesh(t.GetComponent<meshfilter>()); } void WriteMeshRenderer(MeshRenderer mr) { if(mr != null) { // Mesh renderer found, write '1' writer.Write(1); // Write the mesh renderer properties writer.Write(mr.castShadows); writer.Write(mr.receiveShadows); // TODO: Write the materials } else { // No mesh renderer found, write '0' writer.Write(0); } } void writeMesh(MeshFilter mf) { if(mf != null) { writer.Write(1); Mesh mesh = mf.mesh; // TODO: Write the mesh data (vertices, indices, UVs etc.) } else { writer.Write(0); } }
Now, things to consider: You should keep track of all the assets you've written (meshes, materials, textures, sounds etc.), and only write them once. You can also create multiple files, for example save materials to one file, meshes to one and so on. A more sophisticated approach is to create atlas files where you have every asset you need neatly packed. You can also create texture atlases on-the-fly, create dummy scripts in Unity3D to have references about doors between scenes, set dummy objects for player spawns, and just about whatever you need! Remember, you can take full advantage of the tags, layers and such Unity3D has to offer. You can also write the output in JSON, do conversions between classes with reflection, you name it!
I hope you found this small tutorial useful, and as always, feel free to comment and ask questions. You can also make requests about future blog posts and tutorials. :)
This comment has been removed by a blog administrator.
ReplyDelete