Advertisement
mvaganov

GenerateMap.cs

Mar 5th, 2018
345
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 17.32 KB | None | 0 0
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4.  
  5. public class GenerateMap : MonoBehaviour {
  6.     [System.Serializable]
  7.     public class BuildingBlock
  8.     {
  9.         public string character;
  10.         public GameObject blockObject;
  11.     }
  12.     public BuildingBlock[] buildingBlocks;
  13.     private Vector3Int calculatedSize = Vector3Int.zero;
  14.     [TextArea(10,10)]
  15.     public string map =
  16.         "#######\n"+
  17.         ".....##\n"+
  18.         ".....#\n"+
  19.         "#\n"+
  20.         "#....#..........#\n"+
  21.         "###.###\n"+
  22.         "..#.###\n"+
  23.         ".\n"+
  24.         "####............#\n"+
  25.         "\n"+
  26.         "#.....###\n"+
  27.         "#.......#..###\n"+
  28.         "#.......#..#.#\n"+
  29.         "#..........###\n"+
  30.         "#...........#...#\n"+
  31.         "#...........#...#\n"+
  32.         "#..##############\n"+
  33.         "#..#............#\n"+
  34.         "#..#............#";
  35.     private char[,,] mutableData;
  36.     private GameObject[,,] allBlockObjects;
  37.     /// <summary>which index is dirty</summary>
  38.     private List<Vector3Int> dirtyList = new List<Vector3Int>();
  39.     public Vector3 cubeSize = new Vector3(3,3,3);
  40.     /// <summary>which index the user mouse is pointing at</summary>
  41.     public Vector3Int selectIndex;
  42.     /// <summary>which index is the creation point</summary>
  43.     public Vector3Int createIndex;
  44.     public string leftClickAdds = "#";
  45.     public bool rightClickRemoves = true;
  46.  
  47.  
  48.     [ContextMenu("Recalculate Bounds")]
  49.     void RecalculateBounds() {
  50.         // create the box collider, which will help position the generator in the game world
  51.         BoxCollider b = GetComponent<BoxCollider> ();
  52.         if (b == null) {
  53.             b = gameObject.AddComponent<BoxCollider> ();
  54.             b.isTrigger = true;
  55.             gameObject.layer = LayerMask.NameToLayer ("Ignore Raycast");;
  56.         }
  57.         calculatedSize = CalculateMapSize ();
  58.         Vector3 s = (Vector3)calculatedSize;
  59.         s.x = s.x * cubeSize.x;
  60.         s.y = s.z * cubeSize.y;
  61.         s.z = -calculatedSize.y * cubeSize.z;
  62.         b.size = s;
  63.         Vector3 offset = cubeSize / -2;
  64.         offset.z *= -1;
  65.         b.center = b.size / 2 + offset;
  66.         // ensure there is an entry for each character in the map
  67.         string letters = LettersUsedInMap ();
  68.         for (int i = 0; i < letters.Length; ++i) {
  69.             if (GetBlock (letters [i]) == null) {
  70.                 System.Array.Resize (ref buildingBlocks, buildingBlocks.Length + 1);
  71.                 BuildingBlock block = new BuildingBlock ();
  72.                 block.character = letters [i].ToString();
  73.                 buildingBlocks [buildingBlocks.Length - 1] = block;
  74.             }
  75.         }
  76.     }
  77.  
  78.     public char FindCharAtFromOriginalMap(Vector3Int index) {
  79.         if (calculatedSize == Vector3Int.zero) {
  80.             calculatedSize = CalculateMapSize ();
  81.         }
  82.         if (index.x >= 0 && index.y < calculatedSize.x &&
  83.             index.y >= 0 && index.y < calculatedSize.y &&
  84.             index.z >= 0 && index.y < calculatedSize.z) {
  85.             int row = 0, col = 0, dep = 0;
  86.             for (int i = 0; i < map.Length; i++) {
  87.                 char c = map [i];
  88.                 if (c == '\r') { continue; }
  89.                 else if (c == '\n') { if (col == 0) { dep++; row = 0; if (dep > index.z) { break; } } else { row++; } col = 0; }
  90.                 else {
  91.                     if (col == index.x && row == index.y && dep == index.z) { return c; }
  92.                 }
  93.             }
  94.         }
  95.         return (char)0;
  96.     }
  97.  
  98.     public char FindCharAt(Vector3Int index) { return mutableData [index.z, index.y, index.x];}
  99.  
  100.     public string LettersUsedInMap() {
  101.         string output = "";
  102.         for (int i = 0; i < map.Length; ++i) {
  103.             if (map [i] != '\n' && output.IndexOf (map [i]) < 0) {
  104.                 output += map [i];
  105.             }
  106.         }
  107.         return output;
  108.     }
  109.  
  110.     public string ToText(char emptyArea) {
  111.         System.Text.StringBuilder sb = new System.Text.StringBuilder ();
  112.         for (int dep = 0; dep < calculatedSize.z; dep++) {
  113.             for (int row = 0; row < calculatedSize.y; ++row) {
  114.                 int lastUsedIndex = -1;
  115.                 char c;
  116.                 for (int col = 0; col < calculatedSize.x; ++col) {
  117.                     c = mutableData [dep, row, col];
  118.                     if (c != (char)0 && c != emptyArea) {
  119.                         lastUsedIndex = col;
  120.                     }
  121.                 }
  122.                 if (lastUsedIndex < 0) {
  123.                     sb.Append (emptyArea);
  124.                 } else {
  125.                     for (int col = 0; col <= lastUsedIndex; ++col) {
  126.                         c = mutableData [dep, row, col];
  127.                         if (c == 0) {
  128.                             c = emptyArea;
  129.                         }
  130.                         sb.Append (c);
  131.                     }
  132.                 }
  133.                 sb.Append ("\n");
  134.             }
  135.             sb.Append ("\n");
  136.         }
  137.         return sb.ToString ();
  138.     }
  139.  
  140.     public GenerateMap ContainsRecursiveGenerator(List<GenerateMap> currentGenerators = null){
  141.         if (currentGenerators == null) {
  142.             currentGenerators = new List<GenerateMap> ();
  143.         }
  144.         if (currentGenerators.Contains (this)) {
  145.             return this;
  146.         }
  147.         for (int i = 0; i < buildingBlocks.Length; ++i) {
  148.             if (buildingBlocks [i].blockObject != null) {
  149.                 GenerateMap[] generators = buildingBlocks [i].blockObject.GetComponents<GenerateMap> ();
  150.                 if (generators != null) {
  151.                     for (int g = 0; g < generators.Length; ++g) {
  152.                         if (generators [g].ContainsRecursiveGenerator (currentGenerators)) {
  153.                             return generators [g];
  154.                         }
  155.                     }
  156.                 }
  157.             }
  158.         }
  159.         return null;
  160.     }
  161.  
  162.     // Use this for initialization
  163.     void Start () {
  164.         GenerateMap recursiveMap = ContainsRecursiveGenerator ();
  165.         if (recursiveMap != null) {
  166.             throw new System.Exception ("ERROR: about to infinite-recursively create objects with " + recursiveMap);
  167.         }
  168.         if (calculatedSize == Vector3Int.zero) {
  169.             calculatedSize = CalculateMapSize ();
  170.         }
  171.         SetMutableDataFrom (map);
  172.         RefreshMap (true);
  173.         Debug.Log(ToText ('.'));
  174.     }
  175.     void SetMutableDataFrom(string map) {
  176.         mutableData = new char[calculatedSize.z, calculatedSize.y, calculatedSize.x];
  177.         int row = 0, col = 0, dep = 0;
  178.         for (int index = 0; index < map.Length; index++) {
  179.             char c = map [index];
  180.             if (c == '\r') { continue; }
  181.             if (c == '\n') {
  182.                 if (col == 0) {
  183.                     dep++; row = 0;
  184.                 } else {
  185.                     row++;
  186.                 }
  187.                 col = 0;
  188.             } else {
  189.                 mutableData [dep, row, col] = c;
  190.                 col++;
  191.             }
  192.         }
  193.     }
  194.     public void DeleteAll() {
  195.         for (int dep = 0; dep < allBlockObjects.GetLength (0); dep++) {
  196.             for (int row = 0; row < allBlockObjects.GetLength (0); row++) {
  197.                 for (int col = 0; col < allBlockObjects.GetLength (1); col++) {
  198.                     Destroy (allBlockObjects [dep, row, col]);
  199.                     allBlockObjects [dep, row, col] = null;
  200.                 }
  201.             }
  202.         }
  203.     }
  204.     public BuildingBlock GetBlock(char c){
  205.         for (int b = 0; b < buildingBlocks.Length; ++b) {
  206.             BuildingBlock block = buildingBlocks [b];
  207.             if (block.character.Length > 0 && block.character [0] == c) {
  208.                 return block;
  209.             }
  210.         }
  211.         return null;
  212.     }
  213.     public GameObject RefreshBlockAt(Vector3Int i) {
  214.         char c = mutableData [i.z, i.y, i.x];
  215.         BuildingBlock block = GetBlock (c);
  216.         GameObject originalBlockObject = (block != null) ? block.blockObject : null;
  217.         GameObject newBlockObject = null;
  218.         if (allBlockObjects [i.z, i.y, i.x] != null) {
  219.             Destroy (allBlockObjects [i.z, i.y, i.x]);
  220.         }
  221.         if (originalBlockObject != null) {
  222.             Vector3 p = new Vector3 (i.x * cubeSize.x, i.z * cubeSize.y, -i.y * cubeSize.z);
  223.             p = transform.TransformPoint (p);
  224.             newBlockObject = Instantiate (originalBlockObject, p, transform.rotation) as GameObject;
  225.             newBlockObject.transform.SetParent (transform);
  226.         }
  227.         allBlockObjects [i.z, i.y, i.x] = newBlockObject;
  228.         return newBlockObject;
  229.     }
  230.     public bool IndexValid(Vector3Int index) {
  231.         return index.x >= 0 && index.x < calculatedSize.x
  232.             && index.y >= 0 && index.y < calculatedSize.y
  233.             && index.z >= 0 && index.z < calculatedSize.z;
  234.     }
  235.     public void SetBlock(Vector3Int index, char c) {
  236.         mutableData [index.z, index.y, index.x] = c;
  237.         dirtyList.Add (index);
  238.     }
  239.     public void RemoveBlock(Vector3Int index) { SetBlock (index, (char)0); }
  240.     public void RefreshMap(bool all) {
  241.         if (allBlockObjects == null) {
  242.             allBlockObjects = new GameObject[calculatedSize.z,calculatedSize.y,calculatedSize.x];
  243.         }
  244.         if (!all) {
  245.             for (int i = dirtyList.Count-1; i >= 0; i--) {
  246.                 RefreshBlockAt (dirtyList [i]);
  247.                 dirtyList.RemoveAt (i);
  248.             }
  249.         } else {
  250.             Vector3Int i = Vector3Int.zero;
  251.             for (i.z = 0; i.z < mutableData.GetLength (0); i.z++) {
  252.                 for (i.y = 0; i.y < mutableData.GetLength (1); i.y++) {
  253.                     for (i.x = 0; i.x < mutableData.GetLength (2); i.x++) {
  254.                         RefreshBlockAt (i);
  255.                     }
  256.                 }
  257.             }
  258.         }
  259.     }
  260.     /// <summary>read the map data and calculate the correct size based on text data</summary>
  261.     /// <returns>The map size.</returns>
  262.     public Vector3Int CalculateMapSize() {
  263.         int row = 0, col = 0, maxCol = 0, maxRow = 0, dep = 1;
  264.         for (int index = 0; index < map.Length; index++) {
  265.             char c = map [index];
  266.             if (c == '\r') { continue; }
  267.             if (c == '\n') {
  268.                 if (col == 0) {
  269.                     dep++; row = 0;
  270.                 } else {
  271.                     row++;
  272.                 }
  273.                 if (row > maxRow) {
  274.                     maxRow = row;
  275.                 }
  276.                 col = 0;
  277.             } else {
  278.                 col++;
  279.                 if (col > maxCol) {
  280.                     maxCol = col;
  281.                 }
  282.             }
  283.         }
  284.         return new Vector3Int (maxCol, maxRow, dep);
  285.     }
  286.  
  287.     void GetBoundingCorners(ref Vector3[] corners) {
  288.         if(corners == null || corners.Length != 8) { corners = new Vector3[8]; }
  289.         if (map == null || map == "" || enabled == false)
  290.             return;
  291.         if (calculatedSize == Vector3Int.zero) {
  292.             calculatedSize = CalculateMapSize ();
  293.         }
  294.         float w=(calculatedSize.x * cubeSize.x), h=(calculatedSize.y * cubeSize.y), d=(calculatedSize.z * cubeSize.z);
  295.         Vector3 offset = cubeSize / -2;
  296.         offset.z *= -1;
  297.         offset = transform.TransformVector (offset);
  298.         corners[0] = transform.position + offset; // lower top left
  299.         corners[1] = transform.position + transform.right * w + offset; // lower top right
  300.         corners[3] = transform.position - transform.forward * h + offset; // lower bottom left
  301.         corners[2] = corners[3] + transform.right * w; // lower bottom right
  302.         Vector3 u = transform.up * d;
  303.         for (int i = 0; i < 4; i++) {
  304.             corners [4 + i] = corners [i] + u;
  305.         }
  306.     }
  307.  
  308.     public static Vector3Int NOT_AN_INDEX = new Vector3Int (-65536, -65536, -65536);
  309.     /// <summary>Gets the index being pointed at by the given Camera+Mouse combo, at the given height.</summary>
  310.     /// <returns>The <see cref="UnityEngine.Vector3Int"/>, <see cref="GenerateMap.NOT_AN_INDEX"/> if nothing is hit.</returns>
  311.     /// <param name="cam">Cam.</param>
  312.     /// <param name="height">height of the plane.</param>
  313.     public Vector3Int GetIndexBeingPointedAtHeight(Camera cam, float height) {
  314.         Ray r = cam.ScreenPointToRay (Input.mousePosition);
  315.         Vector3 p = transform.position;
  316.         p.y += (height) * cubeSize.y;
  317.         Plane plane = new Plane (transform.up, p);
  318.         float whereHit = 0;
  319.         if (plane.Raycast (r, out whereHit)) {
  320.             Vector3 hit = r.origin + r.direction * whereHit;
  321.             return TranslateWorldToCoordinate (hit);
  322.         }
  323.         return NOT_AN_INDEX;
  324.     }
  325.     GameObject line_hit;
  326.     /// <summary>Gets the index being pointed at by the given Camera+Mouse combo</summary>
  327.     /// <returns>The <see cref="UnityEngine.Vector3Int"/>, <see cref="GenerateMap.NOT_AN_INDEX"/> if nothing is hit.</returns>
  328.     /// <param name="cam">Cam.</param>
  329.     /// <param name="face">returns which face is selected: {0, TOP, BOTTOM, FRONT, BACK, LEFT, RIGHT}</param>
  330.     public Vector3Int GetIndexBeingPointedAt(Camera cam, out int face) {
  331.         RaycastHit rh = new RaycastHit ();
  332.         if (Physics.Raycast (cam.ScreenPointToRay (Input.mousePosition), out rh)) {
  333.             Vector3 p = rh.point -= rh.normal * 0.125f;
  334.             Lines.Make (ref line_hit, rh.point, rh.point + rh.normal, Color.red);
  335.             Vector3Int index = TranslateWorldToCoordinate (p, out face);
  336.             if (face == 0) {
  337.                 float nx = Vector3.Dot (transform.right, rh.normal);
  338.                 float ny = Vector3.Dot (transform.up, rh.normal);
  339.                 float nz = Vector3.Dot (transform.forward, rh.normal);
  340.                 float anx = Mathf.Abs (nx);
  341.                 float any = Mathf.Abs (ny);
  342.                 float anz = Mathf.Abs (nz);
  343.                 if(anx > any && anx > anz) { face = nx < 0 ? LEFT : RIGHT; }
  344.                 if(any > anx && any > anz) { face = ny < 0 ? BOTTOM : TOP; }
  345.                 if(anz > any && anz > anx) { face = nz < 0 ? BACK : FRONT; }
  346.             }
  347.             return index;
  348.         }
  349.         face = 0;
  350.         return NOT_AN_INDEX;
  351.     }
  352.     public const int TOP = 1, BOTTOM = 2, FRONT = 4, BACK = 8, LEFT = 16, RIGHT = 32;
  353.     /// <summary>Translates the world coordinate to map coordinate space</summary>
  354.     /// <returns>The world to coordinate.</returns>
  355.     /// <param name="worldPosition">World position.</param>
  356.     /// <param name="face">returns which face is selected: {0, TOP, BOTTOM, FRONT, BACK, LEFT, RIGHT}</param>
  357.     /// <param name="allowMultipleFaces">If set to <c>true</c> allow multiple faces with binary |.</param>
  358.     public Vector3Int TranslateWorldToCoordinate(Vector3 worldPosition, out int face, bool allowMultipleFaces = false,
  359.         float faceThreshold = 0.45f) {
  360.         Vector3 hit = transform.InverseTransformPoint (worldPosition);
  361.         Vector3 index = new Vector3 (hit.x / cubeSize.x, hit.z / cubeSize.z, hit.y / cubeSize.y);
  362.         Vector3Int finalResult = new Vector3Int((int)Mathf.Round(index.x), (int)Mathf.Round(index.y), (int)Mathf.Round(index.z));
  363.         face = 0;
  364.         index.x -= (int)finalResult.x;
  365.         index.y -= (int)finalResult.y;
  366.         index.z -= (int)finalResult.z;
  367.         if(!allowMultipleFaces) {
  368.             int faceIndex = 0;
  369.             float[] faceWeight = new float[] {0,index.z,-index.z,index.y,-index.y,-index.x,index.x};
  370.             for (int i = 1; i < 7; ++i) { if (faceWeight [i] > faceWeight [faceIndex]) { faceIndex = i; } }
  371.             if (faceWeight [faceIndex] > faceThreshold) {
  372.                 face = 1 << (faceIndex - 1);
  373.             }
  374.         } else {
  375.             if(index.z > faceThreshold)  face += TOP;
  376.             if(index.z < -faceThreshold) face += BOTTOM;
  377.             if(index.y > faceThreshold)  face += FRONT;
  378.             if(index.y < -faceThreshold) face += BACK;
  379.             if(index.x > faceThreshold)  face += RIGHT;
  380.             if(index.x < -faceThreshold) face += LEFT;
  381.         }
  382.         finalResult.y *= -1;
  383.         return finalResult;
  384.     }
  385.     public Vector3Int TranslateWorldToCoordinate(Vector3 worldPosition) {
  386.         Vector3 hit = transform.InverseTransformPoint (worldPosition);
  387.         Vector3 index = new Vector3 (hit.x / cubeSize.x, hit.z / cubeSize.z, hit.y / cubeSize.y);
  388.         return new Vector3Int((int)Mathf.Round(index.x), (int)-Mathf.Round(index.y), (int)Mathf.Round(index.z));
  389.     }
  390.     public Vector3 TranslateCoordinateToWorld(Vector3Int index) {
  391.         Vector3 p = transform.position;
  392.         p += transform.right * index.x * cubeSize.x;
  393.         p += transform.forward * -index.y * cubeSize.z;
  394.         p += transform.up * index.z * cubeSize.y;
  395.         return p;
  396.     }
  397.     public Vector3Int AddFaceToIndex(Vector3Int index, int face) {
  398.         if((face & TOP) != 0)    index.z += 1;
  399.         if((face & BOTTOM) != 0) index.z -= 1;
  400.         if((face & FRONT) != 0)  index.y -= 1;
  401.         if((face & BACK) != 0)   index.y += 1;
  402.         if((face & RIGHT) != 0)  index.x += 1;
  403.         if((face & LEFT) != 0)   index.x -= 1;
  404.         return index;
  405.     }
  406.     public Vector3 TranslateCoordinateToWorld(Vector3Int index, int face) {
  407.         index = AddFaceToIndex (index, face);
  408.         Vector3 p = transform.position;
  409.         p += transform.right * index.x * cubeSize.x;
  410.         p += transform.forward * -index.y * cubeSize.z;
  411.         p += transform.up * index.z * cubeSize.y;
  412.         return p;
  413.     }
  414.     GameObject line_select, line_create;
  415.     public void HighlightFace(Vector3Int index, int face, ref GameObject line_select) {
  416.         Vector3 w = TranslateCoordinateToWorld (index), half = transform.TransformVector(cubeSize / 2);
  417.         Vector3 p = w + half; // upper top right
  418.         Vector3[] line = new Vector3[5];
  419.         Vector3 u = transform.up * cubeSize.y, r = transform.right * cubeSize.x, f = transform.forward * cubeSize.z;
  420.         switch(face) {
  421.         case TOP:
  422.             line [0] = p            ;
  423.             line [1] = p     - r    ;
  424.             line [2] = p     - r - f;
  425.             line [3] = p         - f; break;
  426.         case BOTTOM:
  427.             line [0] = p - u        ;
  428.             line [1] = p - u - r    ;
  429.             line [2] = p - u - r - f;
  430.             line [3] = p - u     - f; break;
  431.         case FRONT:
  432.             line [0] = p            ;
  433.             line [1] = p     - r    ;
  434.             line [2] = p - u - r    ;
  435.             line [3] = p - u        ; break;
  436.         case BACK:
  437.             line [0] = p         - f;
  438.             line [1] = p     - r - f;
  439.             line [2] = p - u - r - f;
  440.             line [3] = p - u     - f; break;
  441.         case RIGHT:
  442.             line [0] = p            ;
  443.             line [1] = p - u        ;
  444.             line [2] = p - u     - f;
  445.             line [3] = p         - f; break;
  446.         case LEFT:
  447.             line [0] = p     - r;
  448.             line [1] = p - u - r;
  449.             line [2] = p - u - r - f;
  450.             line [3] = p     - r - f; break;
  451.         }
  452.         line[4] = line[0];
  453.         Lines.Make (ref line_select, line, 5, Color.red);
  454.         line_select.SetActive (true);
  455.     }
  456.     void FixedUpdate() {
  457.         RefreshMap (false);
  458.     }
  459.     void Update () {
  460.         float dx = Input.GetAxis ("Mouse X"), dy = Input.GetAxis ("Mouse Y");
  461.         if (dx != 0 || dy != 0) { // if there is mouse motion
  462.             int face;
  463.             selectIndex = GetIndexBeingPointedAt(Camera.main, out face);
  464.             createIndex = AddFaceToIndex (selectIndex, face);
  465.             if (createIndex.z < 0) {
  466.                 createIndex = GetIndexBeingPointedAtHeight (Camera.main, -0.5f);
  467.                 face = 0;
  468.                 selectIndex = NOT_AN_INDEX;
  469.             }
  470.             Color ofBox = Color.grey;
  471.             if (IndexValid(createIndex)) {
  472.                 ofBox = Color.green;
  473.             }
  474.             if (face != 0) {
  475.                 if (rightClickRemoves) {
  476.                     HighlightFace (selectIndex, face, ref line_select);
  477.                 }
  478.                 if (leftClickAdds != "") {
  479.                     Lines.MakeBox (ref line_create, TranslateCoordinateToWorld (createIndex), cubeSize * 0.875f, transform.rotation, ofBox);
  480.                     line_create.SetActive (true);
  481.                 }
  482.             } else {
  483.                 if (leftClickAdds != "") {
  484.                     if (line_select != null) line_select.SetActive (false);
  485.                     Lines.MakeBox (ref line_create, TranslateCoordinateToWorld (selectIndex), cubeSize, transform.rotation, ofBox);
  486.                     line_create.SetActive (true);
  487.                 }
  488.             }
  489.         }
  490.         if (leftClickAdds != "" && Input.GetMouseButtonDown (0)) {
  491.             if (IndexValid (createIndex)) {
  492.                 SetBlock (createIndex, leftClickAdds[0]);
  493.                 Debug.Log (ToText ('.'));
  494.             }
  495.         }
  496.         if (rightClickRemoves && Input.GetMouseButtonDown (1)) {
  497.             if (IndexValid (selectIndex)) {
  498.                 RemoveBlock (selectIndex);
  499.                 Debug.Log (ToText ('.'));
  500.             }
  501.         }
  502.     }
  503. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement