csharp
Unity per project compiler options, -unsafe
by Dave Buchhofer on Nov.28, 2011, under csharp, Unity3D
I had to integrate a small set of third party code that calls down to some win32 dllimport bits, to do this I needed to be able to work with some pointers. which in c# need to be declared in an unsafe function, so that it knows to be unmanaged.
Slightly awkward, but it turns out that there is an -unsafe compiler option that needs to be turned on in unity/mono, and that you can do this very easily on a per project basis by adding a file ‘smcs.rsp’ to the root of your assets dir, and in there you can add the text ‘-unsafe’ to add this to the compiler commandline.
Reference threads: Setting Compiler Options, Unsafe code in Unity, How To: Set Project-Wide #pragma Directives with JavaScript
Other apparent uses stated in the threads are setting project wide defines and pragmas
Fun with Generics and Lists in C#
by Dave Buchhofer on May.19, 2011, under .net, csharp, Scripting, Unity
So I’ve been doing a lot of List juggling lately and have run into a few tidbits that I wish I had learned a few months ago. I figure that is always a good cue to write them down for future reference and for any others who are digging into this C# world headfirst also. this is all sort of an exansion to try and get a concrete example of mike_mac’s lamda/delegates/callback articles
Say I have a custom class that I’m using to store a structured set of data, this time we’ll play with “ArmDefs”. ArmDef’s just hold a little string and int based data and a list of ArmDef’s as children, and a second list of GameObjects that contains all of the models for this section of arm for use to serialize to disk, but thats more a story for another time.
Overall this is used to build up a hierarchy that holds the “contents” of a runtime modular rig, and handles the Saving/Loading/Reloading.
To work with this I needed to set up a pile of recursive functions to edit the tree at runtime, since I don’t know the depth of the hierarchy, and rebuild the system as needed.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | [System.Serializable] //Inheriting from ISerializable gives us //an easy access to Serialization to disk public class ArmDef : ISerializable { public string Name; public List<ArmDef> Children = new List<ArmDef>(); public ArmDef(string iFile) { this.Name= iFile; this.Children = new List<ArmDef>(); } public void AddChild(ArmDef child) { Children.Add(child); //just wrapping the Add so that we can do "extra" //stuff here if we want, like add links to parents etc } } |
not really building these thru scripts out in the wild but just to build something to work with here.
1 2 3 4 5 6 7 8 9 10 | public ArmDef config = new ArmDef("Base Level"); //add a couple children config.AddChild(new ArmDef("Level2a")); config.AddChild(new ArmDef("Level2b")); config.AddChild(new ArmDef("Level2c")); //add a couple grandchildren config.Children[1].AddChild(new ArmDef("Level3a")); config.Children[1].AddChild(new ArmDef("Level3b")); |
ok thats all the boring stuff for now! here is a Generic Recursive function that walks down a tree and does ‘Action<>‘ to all children of ‘parent’. The key here is that the Action<> pretty much rocks. !
1 2 3 4 5 6 7 8 9 10 11 | public void HandleArmDef(Action<ArmDef> item, ArmDef parent) { if (parent.Children.Count > 0) { foreach (ArmDeft in parent.Children) { HandleArmDef(item, ti); } } item(parent); } |
Now the fun begins when you start to chain this stuff together to “act” on all children.
we can define that “Action
This will go through the hierarchy and remove all children with the Name of “Level3a”.
1 2 | string toDelete = "Level3a"; HandleArmDef(item => { item.Children = item.Children.Where(child => child.Name != toDelete).ToList(); }, config); |
In another example, i wanted to search the hierarchy and see if there was a certain value anywhere in the hierarchy. easiest way i found was to just quickly build a flattened list and use the Linq “Any” function to see if the string was found anywhere in the hierarchy.
1 2 3 | List<string> flatList = new List<string>(); HandleArmDef(item => { flatList.Add(item.Name ); }, parent); bool isCurrentlyFlat = flatList.Any(x => x.Contains("Flat")); |
in another case, i wanted to insert a new child into the hierarchy as a child of a specific object.
1 2 3 4 5 6 7 8 9 10 11 12 13 | void AddChildAddon(ArmDef parent, string addAsChildOf, string newChild) { //for each object 'item' down the hierarchy //if the item.Name contains our addAsChildOf string //we'll add the newChild as a child to the item. HandleArmDef(item => { if (item.Name.Contains(addAsChildOf)) { item.Children.Add(new ArmDef(newChild)); } }, parent); } |
there’s plenty more possibilities of things to do here if I’ve explained well enough!
More info on Linq by example
Unity: Toying with Scriptable objects and Custom Editors
by Dave Buchhofer on May.18, 2011, under .net, csharp, Scripting, Unity
Expanding on the previous post about Scriptable Objects as an easy way to save/edit some data at runtime, here’s a small extension looking at an example for a simple custom editor that appears when you select the Scriptable Object we built previously in the Project view.
Here’s what you get clicking on the file previously:

And the result with the custom editor:

Now, this isn’t the sexiest of examples, but hopefully it helps someone along the way!
First a small addition to the data class, lets add a constructor to it, since our input values are going to be basically the same every time (Mimic ‘this’ camera), we’ll add a secondary constructor for the class that just takes a unity Camera as input and records the values for us.
We’ll use the same ‘CameraLocationHolder.cs’ from below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | //File: CameraLocation.cs using UnityEngine; using System.Collections; //the Serializable attribute is key to what makes the custom class visible in the editor ! [System.Serializable] public class CameraLocation { public string name; public bool isOrtho; public float orthoSize; public float fov; public Vector3 position; public Quaternion rotation; //base constructor that builds an empty CamLoc public CameraLocation() { name = string.Empty; isOrtho = false; orthoSize = -1; fov = -1; position = Vector3.zero; rotation = Quaternion.identity; } //build a CamLoc from a supplied camera public CameraLocation(Camera c) { if (c) { name = "New Camera"; isOrtho = c.isOrthoGraphic; orthoSize = c.orthographicSize; fov = c.fov; position = c.transform.position; rotation = c.transform.rotation; } } } |
And we replace the ‘CameraListboxWizard’ that we previously used to edit the Scriptable Object with this (very similar) ‘Custom Editor’
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 | //File: CameraLocationEditor.cs -- Place in an Editor directory under the unity project using UnityEngine; using UnityEditor; //this limits the editor to running on object that have type CameraLocationHolder [CustomEditor(typeof(CameraLocationHolder))] public class CameraLocationEditor : Editor { //This will just be a shortcut to the target, ex: the object you clicked on. private CameraLocationHolder db; void Awake() { db=(CameraLocationHolder)target; } public override void OnInspectorGUI() { GUILayout.BeginVertical(); if (GUILayout.Button("Add current Camera.main position", GUILayout.Height(50))) { Camera m = Camera.main; if(m) { //Add the new camera to the list db.content.Add(new CameraLocation(m)); //tell the unity editor that the list has changed and needs to be serialized EditorUtility.SetDirty(target); } } GUILayout.Space(5); DisplayCurrentCamList(); GUILayout.Space(5); GUILayout.EndVertical(); } void DisplayCurrentCamList() { if (db.content.Count == 0) return; //For each of the cameralocations in the saved scriptable object db for (int i = 0; i < db.content.Count; i++ ) { EditorGUILayout.BeginHorizontal(); //A text field to rename the camera db.content[i].name = GUILayout.TextField(db.content[i].name, GUILayout.Width(200)); //a few simple buttons to juggle the array order if (GUILayout.Button("UP")) { if (i > 0) { CameraLocation item = db.content[i]; db.content.RemoveAt(i); db.content.Insert(i - 1, item); } } if (GUILayout.Button("DN")) { if (i < db.content.Count) { CameraLocation item = db.content[i]; db.content.RemoveAt(i); db.content.Insert(i + 1, item); } } if (GUILayout.Button("Remove")) { db.content.Remove(db.content[i]); } //sets the maincamera to be at the selected cameralocation if (GUILayout.Button("MoveTo")) { MoveToView(db.content[i]); } //updates the cameralocation to match up with the Camera.main (Usable at runtime or at edit time) if (GUILayout.Button("UPDATE")) { UpdateView(db.content[i]); } EditorUtility.SetDirty(target); EditorGUILayout.EndHorizontal(); } } void MoveToView(CameraLocation cam) { Camera MainCamera = Camera.mainCamera; Quaternion currentRotation = cam.rotation; //if we had an ortho cam, lets just set it there before we start moving. MainCamera.orthographicSize = cam.orthoSize; MainCamera.isOrthoGraphic = cam.isOrtho; //a parent check incase we have a 'fpswalker' type script setup also if (MainCamera.transform.parent) { //move the parent object MainCamera.transform.parent.position = cam.position; Vector3 ang = currentRotation.eulerAngles; //rotate the parent object on the Y axis MainCamera.transform.parent.rotation = Quaternion.Euler(0, ang.y , 0); //rotate the child camera on the X and Z axis MainCamera.transform.localRotation = Quaternion.Euler(ang.x , 0, ang.z ); } else // there is no FPSWalker parent, so just move the camera itself { MainCamera.transform.position = cam.position; MainCamera.transform.rotation = currentRotation; } MainCamera.fov = cam.fov; } void UpdateView(CameraLocation cam) { Camera MainCamera = Camera.mainCamera; cam.isOrtho = Camera.main.isOrthoGraphic; cam.orthoSize = Camera.main.orthographicSize; cam.position = Camera.main.transform.position; cam.rotation = Camera.main.transform.rotation; cam.fov = MainCamera.fov; } } |
Now having built that up, we can simply click on the asset file that we had created previously and see the editor in the Inspector panel, making it easier to handle multiple lists.
Unity: toying with Scriptable Objects
by Dave Buchhofer on Oct.19, 2010, under Architecture, csharp, Scripting, Unity3D
In architecture there is often a need to keep track of and have handy many different viewpoints of a model at a time. with my early unity experiments, I made the mistake of creating a Camera for every Viewpoint that I wanted to have available in my scene. this was faulty for several reasons, not the least of which was that every ‘Camera’ object in a scene is actually rendering everything in the scene at once. Each camera has a few fun/useful options in the Clear Flags that makes this useful for things like rendering UI overlays with 3d objects and other assorted multipass ideas, but thats a whole ‘nother story.
Next attempt was to just import a large list of dummy objects in from 3dsmax that would represent camera orientations and allow the end user to select from these dummys and align the camera with the dummy objects using some UI bits in unity. This worked fine, but there is the base problem that the user cannot modify/edit these objects easily and have the changes save across play sessions.
This is where Scriptable Objects started to come in to play. (Taken partly from the unity Character Demo where they were explaining some of the joys of AssetBundles if you wish to look up more in depth examples.) this may not be the most useful demo of Scriptable objects, but this is a case study for when you are trying to store Runtime data for use at editortime.
There are several parts: First and probably the only important part is the Holder, this is really just a list of ‘Somethings’ that we save to disk. in our case, CameraLocations.
//CameraLocationHolder.cs using System.Collections.Generic; using UnityEngine; public class CameraLocationHolder : ScriptableObject { public List<CameraLocation> content; public CameraLocationHolder(List<CameraLocation> content) { this.content = content; } }
the above empty list consists of ‘CameraLocation’s which is just a simple class I used to store a few variables that are needed to re-create a Viewpoint. Camera locations is a Serializable Class (Serializable in this case means that its viewable in the inspector in Unity the same way you would view for instance a Vector3..) and it has some default values in there of pulling the maincameras location/rotation and field of view.
1 2 3 4 5 6 7 8 9 10 11 12 | //CameraLocation.cs using UnityEngine; using System.Collections; [System.Serializable] public class CameraLocation { public string name = "New Camera"; public float fov = Camera.main.fov; public Vector3 position = Camera.main.transform.position; public Quaternion rotation = Camera.main.transform.rotation; } |
Now, we create an asset in the editor where we will store our list of CameraLocation’s, this can be placed anywhere, its a small file so i tend to just store it in Resources for easy finding later.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | //CreateCameraLocationAsset.cs using UnityEngine; using UnityEditor; using System; using System.Collections.Generic; public class CreateCameraLocationAsset { [MenuItem("Custom/Cameras/Create camera location holder")] public static void CreateMyAsset() { List<CameraLocation> content = new List<CameraLocation>(); CameraLocationHolder asset = new CameraLocationHolder(content); //scriptable object AssetDatabase.CreateAsset(asset, "CameraLocationDatabase"); AssetDatabase.SaveAssets(); EditorUtility.FocusProjectWindow(); Selection.activeObject = asset; } } |
Now you have an ‘Asset’ where you can store/load/erase/whatever in a list of CameraLocations at runtime, and the results will still be available at Editor time, and at future Runs of the application.
For instance, you can browse around your scene as you would normally with your FlyingCameraScriptOfDoom, and occasionally call a script to add your current camera.main viewpoint to the list.
or accessing it as a runtime resource for use by a normal GUI pulldown:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | //Excerpts from CameraListBox.cs //declaring a list of stuff public List<CameraLocation> camList = new List<CameraLocation>(); //Declare our list of stuff //.... //somewhere in Start() we load the list from the DB Object o = Resources.Load("CameraLocationDatabase", typeof(CameraLocationHolder)); CameraLocationHolder CameraLocationDB = (CameraLocationHolder)o; //fill our list of stuff, with the content from the Holder in the asset camList = CameraLocationDB.content; //.... //and somewhere in our OnGUI() GUILayout.BeginVertical(GUILayout.Width(120)); for (int i = 0; i < camList.Count; i++) { if (GUILayout.Button(camList[i].name)) { SelectedListItem = i;//Set the index for our currrently selected item CameraLocation cam = camList[SelectedListItem]; StartCoroutine(SmoothMoveCamLoc(cam.position, cam.rotation, cam.fov, duration)); } } GUILayout.EndVertical(); //the SmoothMoveCamLoc just lerps between the current positions/rotations/fov's //and the desired ones that were stored in the CameraLocation. //although mine has a few other random logic items in there to decide //which style of camera i have currently and deal with any parents/targets/initializing/etc //needed if we move the camera. |
For instance, editing the list by an Editor script, that builds a list for you to Add/Remove/Rename/and Re-Order the list, because thats oddly hard to do with the built in object interface? Maybe this is where a custom inspector should be for the .asset type? hmm! things to play with in the future.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 | //CameraListboxWizard.cs using UnityEngine; using UnityEditor; using System.Collections.Generic; public class CameraListboxWizard : ScriptableWizard { public string DBLocation = "Assets/Resources/CameraLocationDatabase.asset"; private CameraLocation camLocation; static private Object dbaseAsset; static private CameraLocationHolder db; [MenuItem("Custom/Cameras/CameraListBox - Database Helper")] static void DoSet() { ScriptableWizard.DisplayWizard("Add view to CameraListBox", typeof(CameraListboxWizard), "DONE"); } void OnWizardUpdate() { helpString = "This will control the Camera Location asset database\n, and allow you to 'save' the Camera.main's position\n while in the player mode\n"; UpdateMyAssetLocation(); } void AddExistingCameras() { int count = Camera.allCameras.Length; for (int i = 0; i < count; ++i) { //except for the main camera, which we'll use to transition between the others if (Camera.allCameras[i] != Camera.main) { camLocation = new CameraLocation(); camLocation.name = Camera.allCameras[i].name; camLocation.fov = Camera.allCameras[i].fov; camLocation.position = Camera.allCameras[i].transform.position; camLocation.rotation = Camera.allCameras[i].transform.rotation; AddCamera(camLocation); } } } void OnWizardCreate() { Debug.Log("done"); } void AddCamera(CameraLocation cam) { db.content.Add(cam); EditorUtility.SetDirty(dbaseAsset); } void OnGUI() { EditorGUILayout.BeginVertical(); EditorGUILayout.BeginHorizontal(); GUILayout.Label(name, GUILayout.MaxWidth(0)); DBLocation = EditorGUILayout.TextField(DBLocation); if (GUILayout.Button("Browse")) { DBLocation = EditorUtility.OpenFilePanel(name, DBLocation, "asset"); UpdateMyAssetLocation(); } EditorGUILayout.EndHorizontal(); if (GUILayout.Button("Update Database Location")) { UpdateMyAssetLocation(); } EditorGUILayout.Space(); if (GUILayout.Button("Add current Camera.main position", GUILayout.Height(50))) { camLocation = new CameraLocation(); AddCamera(camLocation); } EditorGUILayout.Space(); DisplayCurrentCamList(); EditorGUILayout.Space(); if (GUILayout.Button("Add all existing 'Cameras' to the list\n this can 'import' max camera locations", GUILayout.Height(50))) { AddExistingCameras(); } EditorGUILayout.EndVertical(); } void DisplayCurrentCamList() { for (int i = 0; i < db.content.Count; i++ ) { EditorGUILayout.BeginHorizontal(); db.content[i].name = GUILayout.TextField(db.content[i].name, GUILayout.Width(200)); if (GUILayout.Button("UP")) { if (i > 0) { CameraLocation item = db.content[i]; db.content.RemoveAt(i); db.content.Insert(i - 1, item); } } if (GUILayout.Button("DN")) { if (i < db.content.Count) { CameraLocation item = db.content[i]; db.content.RemoveAt(i); db.content.Insert(i + 1, item); } } if (GUILayout.Button("Remove")) { db.content.Remove(db.content[i]); } //db.SetDirty(); EditorUtility.SetDirty(dbaseAsset); EditorGUILayout.EndHorizontal(); } } public void UpdateMyAssetLocation() { dbaseAsset = AssetDatabase.LoadAssetAtPath(DBLocation, typeof(CameraLocationHolder)); CameraLocationHolder CameraLocationDB = (CameraLocationHolder)dbaseAsset; db = CameraLocationDB; } public void CreateMyAsset() { List<CameraLocation> content = new List<CameraLocation>(); CameraLocationHolder asset = new CameraLocationHolder(content); //scriptable object AssetDatabase.CreateAsset(asset, "CameraLocationDatabase"); AssetDatabase.SaveAssets(); EditorUtility.FocusProjectWindow(); Selection.activeObject = asset; } } |
Interactive Lab Demo
by Dave Buchhofer on Feb.10, 2010, under Architecture, csharp, Interactive, Unity3D
Here is another Architectural interactive test project. This time, a laboratory setup, a tighter interior space, set up without any real lighting (1 direct light + ambient colors + Ambient Occlusion currently) created with the goal of customizing the unity asset pipeline to be a little more friendly for architectural work.
First, the Project, roughly 6mb, clicking on the screenshot will open the project in a new window. its another quickly evolving work in progress, Warning: No consideration has been taken currently for speed on older hardware, ~100fps on an 8800GT was the target.

Some findings and random ramblings on arch and interactive 3d:
- unlike with general gaming, with architecture you are in most cases going to already have a model provided in some form or another. odds are, the model is going to be pretty horrific, as arch models tend to be built for the purpose of either:
- A: Viz… the Viz model will require a lot of cleanup in terms of material work, and stripping down a lot of the high poly fluff required for rendering.
- B: Design Development… the DD model is normally plagued more by bad modeling conventions, no naming, poor materials, materials not fully assigned, or mangled geometry due to being stretched and squished for days on end to keep up with a design.
- you will probably still want to break up the file by some logical layer type system, by default this will land you with a unique material for each material of each fbx file
- a small AssetPostProcessor script that works on the OnMaterialAssign function can make the process of sharing materials across multiple FBX files much easier to handle, with the added bonus of being able to easily progressively build up a library of reusable realtime materials
//file: MaterialsPostProcessor.cs //goal: Share a library of materials across different assets based on the name of material supplied in the FBX file // Dave Buchhofer - 02.10.2010 using UnityEngine; using UnityEditor; using System.Collections; public class MaterialsPostProcessor : AssetPostprocessor { Material OnAssignMaterialModel (Material material, Renderer renderer) { //The path where you keep your "Standard" library of materials string StandardMatPath = "Assets/DMGStandardMaterials/" + material.name + ".mat"; //the path where you want to keep your "temp" materials, that weren't found above //So you have a logical place to look for materials that need editing and tweaking for realtime work. string TemporaryMatPath = "Assets/Mesh/Materials/" + material.name + ".mat"; //Check for it in the standard if (AssetDatabase.LoadAssetAtPath(StandardMatPath, typeof(Material))) { Debug.Log("FOUND: " + StandardMatPath); return (Material)AssetDatabase.LoadAssetAtPath(StandardMatPath, typeof(Material)); } //Else check to see if we've already built one in the temp path if (AssetDatabase.LoadAssetAtPath(TemporaryMatPath, typeof(Material))) { Debug.Log("FOUND temp: " + TemporaryMatPath); return (Material)AssetDatabase.LoadAssetAtPath(TemporaryMatPath, typeof(Material)); } //Or create it? material.shader = Shader.Find("Diffuse"); AssetDatabase.CreateAsset(material, TemporaryMatPath); Debug.Log("CREATED temp: " + TemporaryMatPath); return material; } }
also check out the previous arch experiment a few posts down: Basketball Arena











