x


C# Static class, works in unity, but not in the build

So, I've written a little engine for generated a tile based world map. I decided the generators would be static scripts, and I'd use a small mono-script to call the static class. But this probably isn't really descriptive enough so...

The script starts with a mono script, I called it spark

using UnityEngine;
using System.Collections;

public class Spark : MonoBehaviour {
    void Awake () {
       Debug.Log("Spark");
       MapManager.GenerateWorld();
    }

    void Start () {
       AvatarMapDrawer.DrawAvatarMap(MapManager.worldMap);
    }
}

So... MapManager is a static class that holds the map.

static public class MapManager {
    static public AvatarMap worldMap;  
    static MapManager () {
       Debug.Log("MapManager Constructed");
    }

    static public void GenerateWorld () {
       Debug.Log ("Generating World");
       WorldAvatarMapGenerator.Generate(ref worldMap);   
    }
}

WorldAvatarMapGenerator is another Static and it does the generation, but it's huge. Larger then the next block of code I included. The only place it could go wrong is in a static class assigning a mono-class variable.

AvatarMapDrawer is a static class that when given a map, instantiates a bunch of GameObject based on what's in the given map. My concern point, or where I think it's gone wrong are static non-mono c# scripts assigning variables (mentioned before), making use of instantiation for either prefabs or PrimitiveTypes.

The rest of this is just the code for AvatarMapDrawer. Don't bother with nit-picking syntax unless it really matters as this is problem I've had from project start (when the code was much smaller and neater XD) and haven't been able to find an answer.

using UnityEngine;
using System.Collections;

enum TileType {
    Plane,
    Cube
}

static public class AvatarMapDrawer {
    static public Material tundra;
    static public Material sahara;
    static public Material desert;

    static public Material snow;
    static public Material ideal;
    static public Material grassLand;

    static public Material ice;
    static public Material swamp;
    static public Material jungle

    static public Material river;
    static public Material ocean;

    static public GameObject plane;

    static int offset = 5;

    static public Vector3[] lakeVector;
    static public Vector3[] riverNorthVector;
    static public Vector3[] riverSouthVector;
    static public Vector3[] riverEastVector;
    static public Vector3[] riverWestVector;
    static public Vector3[] lightVegetationVector;
    static public Vector3[] heavyVegetationVector;
    static public Vector3[] elevationVector;

    static AvatarMap aM;

    static AvatarMapDrawer () {
       SetupAvatarMapMaterial(ref tundra, "Assets/Textures/tundra.jpg");
       SetupAvatarMapMaterial(ref sahara, "Assets/Textures/sahara.jpg");
       SetupAvatarMapMaterial(ref desert, "Assets/Textures/desert.jpg");

       SetupAvatarMapMaterial(ref snow, "Assets/Textures/snow.jpg");
       SetupAvatarMapMaterial(ref ideal, "Assets/Textures/ideal.jpg");
       SetupAvatarMapMaterial(ref grassLand, "Assets/Textures/grassland.jpg");

       SetupAvatarMapMaterial(ref ice, "Assets/Textures/ice.jpg");
       SetupAvatarMapMaterial(ref swamp, "Assets/Textures/swamp.jpg");
       SetupAvatarMapMaterial(ref jungle, "Assets/Textures/jungle.jpg");

       SetupAvatarMapMaterial(ref river, "Assets/Textures/river.jpg");
       SetupAvatarMapMaterial(ref ocean, "Assets/Textures/ocean.jpg");

       SetupAvatarMapModel(ref plane, "Assets/Prefabs/plane.prefab");

       lakeVector = new Vector3[1]{ 
         new Vector3(1,0,1) };
       riverNorthVector = new Vector3[1]{ 
         new Vector3(1,0,0) };
       riverSouthVector = new Vector3[3]{  
         new Vector3(1,0,2),
         new Vector3(1,0,3),
         new Vector3(1,0,4)};
       riverEastVector = new Vector3[3]{  
         new Vector3(2,0,1), 
         new Vector3(3,0,1),
         new Vector3(4,0,1) };
       riverWestVector = new Vector3[1]{ 
         new Vector3(0,0,1) };
       lightVegetationVector = new Vector3[8]{ 
         new Vector3(0,0,2),
         new Vector3(0,0,3), 
         new Vector3(4,0,2), 
         new Vector3(4,0,3),
         new Vector3(2,0,0),
         new Vector3(3,0,0), 
         new Vector3(2,0,4), 
         new Vector3(3,0,4) };
       heavyVegetationVector = new Vector3[4]{ 
         new Vector3(0,0,0), 
         new Vector3(4,0,0), 
         new Vector3(0,0,4), 
         new Vector3(4,0,4) };
       elevationVector = new Vector3[4]{ 
         new Vector3(2,0,2), 
         new Vector3(2,0,3),
         new Vector3(3,0,2),
         new Vector3(3,0,3) };
    }

    static void SetupAvatarMapModel (ref GameObject gameObject, string modelPath) {
       gameObject = Resources.LoadAssetAtPath(modelPath, typeof(GameObject)) as GameObject;
    }

    static void SetupAvatarMapMaterial (ref Material material, string texturePath) {
       material = new Material(Shader.Find("Diffuse"));
       material.mainTexture = Resources.LoadAssetAtPath(texturePath, typeof(Texture2D)) as Texture2D;
    }

    static public GameObject DrawAvatarMap (AvatarMap avatarMap) {
       aM = avatarMap;
       GameObject avatarMapTransform;
       avatarMapTransform = new GameObject();
       avatarMapTransform.name = "CurrentMap";

       for (int i = 0; i < avatarMap.avatarMapTiles.GetLength(0); i++)
         for (int j = 0; j < avatarMap.avatarMapTiles.GetLength(1); j++) {
          GameObject tempTile = DrawAvatarMapTile (i, j);

          if ( tempTile == null )
              continue;

          tempTile.transform.position = new Vector3 (i * offset, 0, j * offset);
          tempTile.transform.parent = avatarMapTransform.transform;
         }

       aM = null;

       return avatarMapTransform;
    }

    static GameObject DrawAvatarMapTile (int x, int y) {
       GameObject avatarMapTile = null;

       avatarMapTile = DrawLandAvatarMapTile (x, y);
       if ( avatarMapTile != null )
         return avatarMapTile;

       avatarMapTile = DrawSeaAvatarMapTile (x, y);
       if ( avatarMapTile != null )
         return avatarMapTile;

       avatarMapTile = DrawSkyAvatarMapTile (x, y);
       if ( avatarMapTile != null )
         return avatarMapTile;

       return null;
    }

    static GameObject DrawLandAvatarMapTile (int x, int z) {
       LandAvatarMapTile landAvatarMapTile = aM.avatarMapTiles[x,z] as LandAvatarMapTile;
       if (landAvatarMapTile == null)
         return null;

       GameObject fullTile;
       float riverY = -0.25f;
       float normalY = 0.0f;
       float hillY = 0.5f;
       float mountainY = 1.00f;
       float y = 0.0f;
       Material tileMaterial;
       tileMaterial = GetBiomeMaterial(landAvatarMapTile);
       fullTile = new GameObject();

       GameObject subTile;

       y = normalY;

       //river for river in this tile
       if (landAvatarMapTile.river) {
         tileMaterial = river;
         //y = riverY;
       } else {
         tileMaterial = GetBiomeMaterial(landAvatarMapTile);
         y = normalY;
       }
       for (int i = 0; i < lakeVector.Length; i++) {
         subTile = DrawTile(fullTile, TileType.Plane, tileMaterial);
         subTile.transform.position = new Vector3(0.5f, y, 0.5f) + lakeVector[i];
       }
       //check for river in surrounding tiles
       //check for water to the north
       if (CanRecieveRiver(x, z - 1) && landAvatarMapTile.river) {
         tileMaterial = river;
         //y = riverY;
       } else {
         tileMaterial = GetBiomeMaterial(landAvatarMapTile);
         y = normalY;
       }

       for (int i = 0; i < riverNorthVector.Length; i++) {
         subTile = DrawTile(fullTile, TileType.Plane, tileMaterial);
         subTile.transform.position = new Vector3(0.5f, y, 0.5f) + riverNorthVector[i];
       }

       //check for water to the sout
       if (CanRecieveRiver(x, z + 1) && landAvatarMapTile.river) {
         tileMaterial = river;
         //y = riverY;
       } else {
         tileMaterial = GetBiomeMaterial(landAvatarMapTile);
         y = normalY;
       }

       for (int i = 0; i < riverSouthVector.Length; i++) {
         subTile = DrawTile(fullTile, TileType.Plane, tileMaterial);
         subTile.transform.position = new Vector3(0.5f, y, 0.5f) + riverSouthVector[i];
       }

       //check for water to the east
       if (CanRecieveRiver(x + 1, z) && landAvatarMapTile.river) {
         tileMaterial = river;
         //y = riverY;
       } else {
         tileMaterial = GetBiomeMaterial(landAvatarMapTile);
         y = normalY;
       }

       for (int i = 0; i < riverEastVector.Length; i++) {
         subTile = DrawTile(fullTile, TileType.Plane, tileMaterial);
         subTile.transform.position = new Vector3(0.5f, y, 0.5f) + riverEastVector[i];
       }

       //check for water to the west
       if (CanRecieveRiver(x - 1, z) && landAvatarMapTile.river) {
         tileMaterial = river;
         //y = riverY;
       } else {
         tileMaterial = GetBiomeMaterial(landAvatarMapTile);
         y = normalY;
       }

       for (int i = 0; i < riverWestVector.Length; i++) {
         subTile = DrawTile(fullTile, TileType.Plane, tileMaterial);
         subTile.transform.position = new Vector3(0.5f, y, 0.5f) + riverWestVector[i];
       }

       // all water checks done, no need to check tileMaterial any longer
       tileMaterial = GetBiomeMaterial(landAvatarMapTile);

       //check for raised terrian and mountains
       if (landAvatarMapTile.elevationLevel == ElevationLevel.Mountains)
         y = mountainY;
       else if (landAvatarMapTile.elevationLevel == ElevationLevel.Hills)
         y = hillY;
       else 
         y = normalY;

       for (int i = 0; i < elevationVector.Length; i++) {
         subTile = DrawTile(fullTile, TileType.Cube, tileMaterial);
         subTile.transform.position = new Vector3(0.5f, y, 0.5f) + elevationVector[i];
       }
       fullTile.name = "[" + x.ToString() + "," + z.ToString() + "]" + "LandTile";

       //place 
       y = normalY;

       for (int i = 0; i < lightVegetationVector.Length; i++) {
         subTile = DrawTile(fullTile, TileType.Plane, tileMaterial);
         subTile.transform.position = new Vector3(0.5f, y, 0.5f) + lightVegetationVector[i];
       }

       for (int i = 0; i < heavyVegetationVector.Length; i++) {
         subTile = DrawTile(fullTile, TileType.Plane, tileMaterial);
         subTile.transform.position = new Vector3(0.5f, y, 0.5f) + heavyVegetationVector[i];
       }

       return fullTile;
    }

    static GameObject DrawSeaAvatarMapTile (int x, int z) {
       SeaAvatarMapTile seaAvatarMapTile = aM.avatarMapTiles[x,z] as SeaAvatarMapTile;
       if (seaAvatarMapTile == null)
         return null;

       float y = -0.25f;
       GameObject fullTile;
       fullTile = new GameObject();

       for (int i = 0; i < 5; i ++)
         for (int j = 0; j < 5; j++) {
          GameObject subTile = DrawTile(fullTile, TileType.Plane, ocean);
          if (subTile != null)
              subTile.transform.position = new Vector3(i + 0.5f, 0.0f /*y*/, j + 0.5f);
         }
       fullTile.name = "[" + x.ToString() + "," + z.ToString() + "]" + "SeaTile";
       return fullTile;
    }

    static GameObject DrawSkyAvatarMapTile (int x, int z) {
       SkyAvatarMapTile skyAvatarMapTile = aM.avatarMapTiles[x,z] as SkyAvatarMapTile;
       if (skyAvatarMapTile == null)
         return null;

       GameObject fullTile;
       fullTile = new GameObject();

       fullTile.name = "[" + x.ToString() + "," + z.ToString() + "]" + "SkyTile";

       return fullTile;
    }

    static GameObject DrawTile(GameObject tileParent, TileType tileType, Material material){
       GameObject tile;

       if (tileType == TileType.Plane) {
         tile = (GameObject)Object.Instantiate(plane);
         tile.transform.FindChild("plane").renderer.material = material;
       } else {
         tile = GameObject.CreatePrimitive(PrimitiveType.Cube);
         tile.renderer.material = material;
       }
       if(tileParent != null)
         tile.transform.parent = tileParent.transform;
       Component.Destroy(tile.collider);
       return tile;
    }

    static bool CanRecieveRiver(int x, int y) {
       if (aM.avatarMapTiles[x,y] as LandAvatarMapTile != null) {
         LandAvatarMapTile landAvatarMapTile = aM.avatarMapTiles[x,y] as LandAvatarMapTile;
         if (landAvatarMapTile.river)
          return true;
       }

       if (aM.avatarMapTiles[x,y] as SkyAvatarMapTile != null)
         return true;
       if (aM.avatarMapTiles[x,y] as SeaAvatarMapTile != null)
         return true;
       return false;
    }

    static Material GetBiomeMaterial (LandAvatarMapTile landAvatarMapTile) {
       if (landAvatarMapTile.saturationLevel == SaturationLevel.Dry && landAvatarMapTile.temperatureLevel == TemperatureLevel.Hot)
         return desert;
       if (landAvatarMapTile.saturationLevel == SaturationLevel.Dry && landAvatarMapTile.temperatureLevel == TemperatureLevel.Moderate)
         return sahara;
       if (landAvatarMapTile.saturationLevel == SaturationLevel.Dry && landAvatarMapTile.temperatureLevel == TemperatureLevel.Cold)
         return tundra;

       if (landAvatarMapTile.saturationLevel == SaturationLevel.Good && landAvatarMapTile.temperatureLevel == TemperatureLevel.Hot)
         return grassLand;
       if (landAvatarMapTile.saturationLevel == SaturationLevel.Good && landAvatarMapTile.temperatureLevel == TemperatureLevel.Cold)
         return snow;

       if (landAvatarMapTile.saturationLevel == SaturationLevel.Wet && landAvatarMapTile.temperatureLevel == TemperatureLevel.Hot)
         return jungle;
       if (landAvatarMapTile.saturationLevel == SaturationLevel.Wet && landAvatarMapTile.temperatureLevel == TemperatureLevel.Moderate)
         return swamp;
       if (landAvatarMapTile.saturationLevel == SaturationLevel.Wet && landAvatarMapTile.temperatureLevel == TemperatureLevel.Cold)
         return ice;
       return ideal;
    }
}

SAMPLE FROM THE DEVELOPER

http://imageshack.us/photo/my-images/62/ectwmsample1.jpg/

http://imageshack.us/photo/my-images/708/ectwmsample2.jpg/

http://imageshack.us/photo/my-images/849/ectwmsample3.jpg/

SAMPLE FROM THE BUILD

http://imageshack.us/photo/my-images/11/ectwmsamplefail.jpg/

more ▼

asked Oct 11 '11 at 02:22 PM

Magarthryx gravatar image

Magarthryx
-4 1 1 1

Getting any error messages in your build? I'm afraid a massive code-dump isn't likely to get you any help- not many people will read through that!

Oct 11 '11 at 02:24 PM syclamoth

Yep, i also just had a short look through the code and had almost given up but the extentions(.jpg / .prefab) in your ressources paths was suspicious. Resources.Load only takes the assetname without extention. Then it was quite obvious since the path didn't even include a Resources folder ;)

Oct 11 '11 at 04:25 PM Bunny83
(comments are locked)
10|3000 characters needed characters left

2 answers: sort voted first

The answer is quite simple:

Resources.LoadAssetAtPath only works in the editor (like stated in the docs).

Beside the fact that Resources.LoadAssetAtPath is an editor function you can't use the Resources class to load an arbitrary asset. Unity includes only those assets into a build that are referenced by a scene which also have to be included in the build settings. To be able to load assets by name you have to place them below a Ressources folder. Assets in a Ressources folder are always included in a build and therefore can be loaded with Resources.Load.

ps. i'd like to know why the heck are you using static classes? ;) It's like you throwing all advantages of OOP in the trash. Is there any reason why you use them instead of implementing those function at the corresponding classes or at least as singleton?

more ▼

answered Oct 11 '11 at 04:18 PM

Bunny83 gravatar image

Bunny83
46.9k 12 50 210

Yeah, the guys I'm working with said get over static, just go OOP.

Thanks, will get everything change out of static and Resources.Load.

Oct 11 '11 at 04:40 PM Magarthryx

Not to nit pick, but the difference between a static class and a singleton is purely syntax, they both have global state and neither is a proper object in terms of OO. The syntax of the singleton is simpler and it should be used for that reason.

If you're interested in OO there are some really great talks at google: http://www.youtube.com/watch?v=-FRm3VPhseI

This is purely academic since Unity forces you to have a lot of global state in the system anyway.

Oct 11 '11 at 09:48 PM _Petroz
(comments are locked)
10|3000 characters needed characters left

TL;DR: Don't put ANY code which uses other classes in static constructors

Because you cannot rely on the order in which the static class constructors are called.

It WILL differ between different builds, and even between different runs of your program. Use appropriate event handlers for initialization.

Also, if you have a lot of code that uses other static classes extensively in static constructors, you might even end up with

  • crashing player
  • infinite loops
  • uninitialized memory

And lots of other fun things. So, just don't do it and make a proper initialization sequence (e.g. on the "Play!" button handler - you do have a menu, do you?)

more ▼

answered Oct 11 '11 at 08:43 PM

wizzard0 gravatar image

wizzard0
1 1

(comments are locked)
10|3000 characters needed characters left
Your answer
toggle preview:

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this question

By Email:

Once you sign in you will be able to subscribe for any updates here

By RSS:

Answers

Answers and Comments

Topics:

x684
x284

asked: Oct 11 '11 at 02:22 PM

Seen: 3345 times

Last Updated: Oct 11 '11 at 09:48 PM