Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Multiplatform Runtime Level Editor

Discussion in 'Assets and Asset Store' started by FreebordMAD, Jun 10, 2014.

  1. FreebordMAD

    FreebordMAD

    Joined:
    Mar 15, 2013
    Posts:
    633
    I'm working on Undo/Redo right now.

    Here are my top 8 backlog entries (sorry for the format and typos, just copy pasted from OO Calc):
    Optimize (only Unity 5.2.2) 9500
    Terrain.TerrainData.SetHeights to Terrain.TerrainData.SetHeightsDelayLOD made a significant improvement.

    Select instance btn 9100
    - e.g. the start object is hard to find, it needs a select/show instance button

    Level Loading and Build Settings 9020
    - use strings instead if ids for level loading → possible to check if level is loadable
    - a debug message if trying to load a level that is already loaded

    Feature request Doc 9010
    I would like to see a simple step by step showing what prefabs need to be dragged into a scene and what settings need to be applied to add the MRLE functionality

    Feature request 2800
    Ability to change object materials (not just color). This feature is available in one of your competitor's assets: "Runtime Level Editor C Sharp."

    Feature requests 2700
    1.) System for changing global settings, such as lighting, shadows, fog, and ambient colors. Also available in the other package.
    2.) Foliage/grass/tree painting. Trying to place these individually seems very tedious.

    Precise Grid snapping 2650
    - add a checkbox for level objects if grid snapping is precise or not

    BUG: cam perps gizmo affected by light 2602
     
  2. dl_studios

    dl_studios

    Joined:
    Oct 14, 2012
    Posts:
    76
    @FreebordMAD I'm interested in using the terrain editing features for my game. Looking over the docs it appears that serialization is done as a complete level rather than a feature at a time--which makes sense. However, for my needs I'd like to use the terrain editing feature and save the terrain data independently. Does your asset by chance have anything to simplify that process? Do you have any suggestions on how to best do this?

    Thank you in advance.
     
  3. FreebordMAD

    FreebordMAD

    Joined:
    Mar 15, 2013
    Posts:
    633
    Thanks for your interest in my level editor (MRLE)! There are two ways to do it.
    1. As you can see in the demo (settings button->terrain editor demo). MRLE can work as a simple terrain editor, in this case only the terrain data is stored.
    2. You could also hack the save/load functions and remove all code that stores anything else but terrain data. If you decide to buy my asset, then I can send you the code (it will take me 5 seconds).

    p.s.: as a little motivation, I'm working on undo/redo functionality right now. As far as I know no other runtime terrain editor on the Asset Store has this feature ;)
     
  4. Der_Kevin

    Der_Kevin

    Joined:
    Jan 2, 2013
    Posts:
    517
    thats cool :)

    uhm, iam trying to find the script which is responsable of the appearance of the save/load/play canvas. so the RightMenu_Level. i actually want to have save/load in there all the time and not move away when i am in the object tab.
    is it okay to just drag RightMenu_Level_SaveBtn, RightMenu_Level_LoadBtn and RightMenu_Level_PlayBtn in from RightMenu_Level_BG in the RightNav_BG? or will this break anything

    also, what is the "AM_OBLIQUE_RIGHT_PIXEL_OFFSET" for? can not find it in the docs somehow
     
    Last edited: Nov 19, 2015
  5. FreebordMAD

    FreebordMAD

    Joined:
    Mar 15, 2013
    Posts:
    633
    If you want, I can send you the undo/redo patch via e-mail (it will take a while until the next patch will be released).

    Just move the buttons, it will work that way.
    What you were searching for is the uMyGUI_TabBox script attached to RightNav_BG object. This script handles the tab animation/selection.
    Check the target of the save and load buttons, it is the LE_GUIInterface script attached to the Canvas object. The methods called are OnLevelSaveBtn and OnLevelLoadBtn, see the docs here. The play button calls a method of the ExampleGame_Editor object and class (it's up to you to change this anyway).

    The right doc page is here. If something is unclear, please inform me about that. I will then change the docs.
    Also, this page could be interesting.
     
    Der_Kevin likes this.
  6. Der_Kevin

    Der_Kevin

    Joined:
    Jan 2, 2013
    Posts:
    517
    okay, cool thank you!
    that helped a lot!
    and thanks for the undo/redo offer. its not important at the moment, but if it gets urgent i will come back to that. thanks :)

    so, during working and resking the editor i found something pretty annoying. that you actually can not modify the ui and turning on different panels and then just press play. most of the time the editor is then broken because you activated a panel which shouldn't be active on startup. so you always have to remember which panels should be turned active on startup. thats just as minor improvement idea, nothing really bad that keeps me from working :)

    also, i think some slight camera damping would be nice so that zooming doesent feel so.. stiff?
    and also for the camera would be nice to have some boundaries. so that you can not move away so much from the map

    otherwise, super fun to mess around with the edior, great job. have to write a review later :)

    work in progress:
    leveleditor3.gif
     
    Last edited: Nov 19, 2015
    FaberVi and FreebordMAD like this.
  7. FreebordMAD

    FreebordMAD

    Joined:
    Mar 15, 2013
    Posts:
    633
    Could you give me an example here? The editor needs to know in which edit mode you are. For example, it has different bahaviour in the terrain and the object mode, but in which case would you use terrain functions while you are in object mode?

    I'll put it on the list.

    Nice to hear that! A review would be super awesome!

    Looks great, it is the first reskin that I see! ;)
     
    Last edited: Nov 19, 2015
  8. dl_studios

    dl_studios

    Joined:
    Oct 14, 2012
    Posts:
    76
    Sounds like its just what I needed and I bought it. Would you like me to send the order number so you can verify the purchase and send me the modified save function?
     
  9. FreebordMAD

    FreebordMAD

    Joined:
    Mar 15, 2013
    Posts:
    633
    I decided to post the changed script here anyway, therefore there is no need to send me the order number. What I have done is:
    1. remove all object related saving/loading
    2. remove all meta data related saving/loading

    Just replace the following functions of the LE_SaveLoad class (LE_SaveLoad.cs file) with the code bellow:

    SaveCurrentLevelDataToByteArray
    Code (CSharp):
    1.  
    2.         public static byte[] SaveCurrentLevelDataToByteArray(Texture2D[] p_terrainTextures)
    3.         {
    4.             using (MemoryStream byteStream = new MemoryStream())
    5.             {
    6.                 using (BinaryWriter stream = new BinaryWriter(byteStream))
    7.                 {
    8.                     // write version
    9.                     stream.Write(SERIALIZATION_VERSION);
    10.  
    11.                     // save terrain
    12.                     LE_GUI3dTerrain gui3dTerrain = Object.FindObjectOfType<LE_GUI3dTerrain>();
    13.                     Terrain terrain = gui3dTerrain!=null ? gui3dTerrain.TerrainInstance : null;
    14.                     stream.Write((int)(terrain!=null?1:0)); // write int (instead of bool for upward compatibility) to indicate if the level has a terrain
    15.                     if (terrain != null)
    16.                     {
    17.                         SaveTerrainHeightmap(stream, terrain.terrainData);
    18.                         SaveTerrainAlphamaps(stream, terrain.terrainData, p_terrainTextures);
    19.                     }
    20.  
    21.                     // return the resulting binary array
    22.                     stream.Flush();
    23.                     byteStream.Flush();
    24.                     return byteStream.ToArray();
    25.                 }
    26.             }
    27.         }
    SaveCurrentLevelMetaToByteArray
    Code (CSharp):
    1.  
    2.         public static byte[] SaveCurrentLevelMetaToByteArray(Texture2D p_levelIcon, KeyValuePair<string, string>[] p_metaData)
    3.         {
    4.             return new byte[0];
    5.         }
    LoadLevelDataFromByteArray
    Code (CSharp):
    1.  
    2.         public static LE_SaveLoadData LoadLevelDataFromByteArray(byte[] p_byteArray, int p_terrainLayer, Texture2D[] p_terrainTextures, Vector2[] p_terrainTextureSizes, Vector2[] p_terrainTextureOffsets)
    3.         {
    4.             if (!CheckParameters("LoadLevelDataFromByteArray", p_byteArray, p_terrainTextures, p_terrainTextureSizes, p_terrainTextureOffsets))
    5.             {
    6.                 return null;
    7.             }
    8.  
    9.             using (MemoryStream byteStream = new MemoryStream(p_byteArray))
    10.             {
    11.                 using (BinaryReader stream = new BinaryReader(byteStream))
    12.                 {
    13.                     byte version = stream.ReadByte();
    14.                     CheckVersion("LoadLevelDataFromByteArray", version);
    15.  
    16.                     // clear cached data of the streamed scene manager
    17.                     LS_LevelStreamingSceneManager.Instance.RemoveAllManagedObjects();
    18.                     // clear cached data of the 3d object gui if it exists
    19.                     LE_GUI3dObject gui3d = Object.FindObjectOfType<LE_GUI3dObject>();
    20.                     if (gui3d != null)
    21.                     {
    22.                         gui3d.ClearLevelData();
    23.                     }
    24.  
    25.                     // load terrain
    26.                     GameObject terrainGO = null;
    27.                     int terrainCount = stream.ReadInt32();
    28.                     if (terrainCount > 0)
    29.                     {
    30.                         TerrainData terrainData = LoadTerrainData(stream, p_terrainTextures, p_terrainTextureSizes, p_terrainTextureOffsets);
    31.                         terrainGO = LE_LogicTerrain.CreateOrRecycleTerrain(terrainData, p_terrainLayer);
    32.  
    33.                         // backwards compatibility loading (still only the first terrain is instantiated -> levels could break (see error message below))
    34.                         if (terrainCount > 1)
    35.                         {
    36.                             // read from stream, otherwise loading would break (only read no game object is created...)
    37.                             for (int i = 1; i < terrainCount; i++) { Object.Destroy(LoadTerrainData(stream, p_terrainTextures, p_terrainTextureSizes, p_terrainTextureOffsets)); }
    38.                             Debug.LogError("LE_SaveLoad: LoadLevelDataFromByteArray: you have multiple terrains in this level! " +
    39.                                 "This happened due to a bug in the older version of MR Level Editor! The new implementation will load only the first terrain. " +
    40.                                 "Please contact me if you have problems with this change: " +
    41.                                 "http://forum.unity3d.com/threads/multiplatform-runtime-level-editor-any-one-interested.250920/");
    42.                         }
    43.                     }
    44.                     else
    45.                     {
    46.                         // LE_LogicTerrain.DestroyOrResetTerrain(); // in v1.30
    47.                         DestroyOrResetTerrain(); // in v1.22
    48.                     }
    49.  
    50.                     return new LE_SaveLoadData(version, terrainGO, new LE_SaveLoadData.ObjectData[0]);
    51.                 }
    52.             }
    53.         }
    PeekLevelDataFromByteArray
    Code (CSharp):
    1.  
    2.         public static LE_SaveLoadDataPeek PeekLevelDataFromByteArray(byte[] p_byteArray, Texture2D[] p_terrainTextures, Vector2[] p_terrainTextureSizes, Vector2[] p_terrainTextureOffsets)
    3.         {
    4.             if (!CheckParameters("PeekLevelDataFromByteArray", p_byteArray, p_terrainTextures, p_terrainTextureSizes, p_terrainTextureOffsets))
    5.             {
    6.                 return null;
    7.             }
    8.          
    9.             using (MemoryStream byteStream = new MemoryStream(p_byteArray))
    10.             {
    11.                 using (BinaryReader stream = new BinaryReader(byteStream))
    12.                 {
    13.                     byte version = stream.ReadByte();
    14.                     CheckVersion("PeekLevelDataFromByteArray", version);
    15.  
    16.                     // load terrain
    17.                     TerrainData terrainData =  null;
    18.                     int terrainCount = stream.ReadInt32();
    19.                     if (terrainCount > 0)
    20.                     {
    21.                         terrainData = LoadTerrainData(stream, p_terrainTextures, p_terrainTextureSizes, p_terrainTextureOffsets);
    22.  
    23.                         // backwards compatibility loading (still only the first terrain is instantiated -> levels could break (see error message below))
    24.                         if (terrainCount > 1)
    25.                         {
    26.                             // read from stream, otherwise loading would break (only read no game object is created...)
    27.                             for (int i = 1; i < terrainCount; i++) { Object.Destroy(LoadTerrainData(stream, p_terrainTextures, p_terrainTextureSizes, p_terrainTextureOffsets)); }
    28.                             Debug.LogError("LE_SaveLoad: PeekLevelDataFromByteArray: you have multiple terrains in this level! " +
    29.                                            "This happened due to a bug in the older version of MR Level Editor! The new implementation will load only the first terrain. " +
    30.                                            "Please contact me if you have problems with this change: " +
    31.                                            "http://forum.unity3d.com/threads/multiplatform-runtime-level-editor-any-one-interested.250920/");
    32.                         }
    33.                     }
    34.  
    35.                     return new LE_SaveLoadDataPeek(version, terrainData, 0);
    36.                 }
    37.             }
    38.         }
    LoadLevelMetaFromByteArray
    Code (CSharp):
    1.  
    2.         public static LevelMetaData LoadLevelMetaFromByteArray(byte[] p_byteArray, bool p_isLevelIconLoaded)
    3.         {
    4.             return new LevelMetaData(SERIALIZATION_VERSION, null, new KeyValuePair<string, string>[0]);
    5.         }
     
    Last edited: Dec 2, 2015
  10. Der_Kevin

    Der_Kevin

    Joined:
    Jan 2, 2013
    Posts:
    517
    sure, for example if you activate the "RightMenu_Terrain_EditMode" or "Popup_Text_Root" and then press play, this object is active/visible. or deactivate the "RightNav_BG" then nothing works anymore :D
    or if you activate all panels they are suddenly all there. but yeah thats really a minor thing. after some time with the editor i know which panels should be active on startup :)

    ahjeah, I am also having problems to put down the "CPG_CameraPerspectiveGizmo(Clone)". so on the Y axis. dont know how i can move it down

    iam also trying to add the wall placment script that i posted before to the editor. so i set everything up. actually just cloned the "GridBaseCube" settings, named it 3D-test and added it to the object maps and its there, no problem.
    but when i drag my prefab with this script on it:

    Code (CSharp):
    1. #pragma strict
    2. private var trans:Transform;
    3. var wall_layer:LayerMask;
    4. var wall_prefabs:GameObject[];
    5. var wall_id:int=0;
    6. var current_prefab:GameObject;
    7. var current_pos:Vector3;
    8. var base_renderer:Renderer;
    9. var current_renderer:Renderer;
    10.  
    11.  
    12.  
    13. function Start ()
    14. {
    15.     trans=transform;  
    16.     var child_trans:Transform[]  = gameObject.GetComponentsInChildren.<Transform>() as Transform[];
    17.     var child_count:int=trans.childCount;
    18.    
    19.     if(child_count>1)//persistance manager created prefab in existing state rather than a new empty one
    20.     {
    21.    
    22.         for(var child : Transform in child_trans)
    23.         {
    24.            
    25.            
    26.             if(child.gameObject.name=="Cube" ||child.gameObject.name=="3D-test" ||child.gameObject.name== "3D-test(Clone)")
    27.             {
    28.                 //do nothing
    29.             }
    30.             else
    31.             {
    32.                 //Debug.Log(child.gameObject.name);
    33.                 Destroy(child.gameObject);
    34.             }
    35.         }
    36.        
    37.         current_pos=trans.position;
    38.         create_base_wall();
    39.     }
    40. }
    41.  
    42. function create_base_wall()
    43. {
    44.     current_prefab=Instantiate(wall_prefabs[0],trans.position,Quaternion.identity) as GameObject;
    45.     current_prefab.transform.parent=trans;
    46.     current_prefab.name="test";  
    47.     current_renderer=current_prefab.GetComponent(Renderer);
    48.     current_renderer.material.color =base_renderer.material.color;
    49.     check_walls(true);
    50. }
    51.  
    52. function Update()
    53. {
    54.     if(current_renderer.material.color !=base_renderer.material.color)
    55.     {
    56.         current_renderer.material.color =base_renderer.material.color;
    57.     }
    58.    
    59.     if(current_pos !=trans.position)
    60.     {
    61.         current_renderer.material.color =base_renderer.material.color;
    62.         current_pos=trans.position;
    63.         check_walls(true);
    64.     }
    65.     else
    66.     {
    67.         check_walls(false);
    68.     }
    69. }
    70.  
    71.  
    72. function check_walls(check_neighbors:boolean)
    73. {
    74.     var new_wall_id:int=0;
    75.     var hit:RaycastHit;
    76.     var ray_north:Ray =  Ray(trans.position+ Vector3.up * 2 + Vector3.forward *2, Vector3.down);
    77.     var ray_east:Ray =  Ray(trans.position+ Vector3.up * 2 + Vector3.right *2, Vector3.down);  
    78.     var ray_south:Ray =  Ray(trans.position+ Vector3.up * 2 + Vector3.back *2, Vector3.down);  
    79.     var ray_west:Ray =  Ray(trans.position+ Vector3.up * 2 + Vector3.left *2, Vector3.down);
    80.    
    81.     if (Physics.Raycast(ray_north, hit,10.0,wall_layer))
    82.     {
    83.        new_wall_id+=1;
    84.        Debug.DrawLine (ray_north.origin, hit.point);
    85.        if(check_neighbors)
    86.        {
    87.             hit.collider.gameObject.SendMessage("check_walls",false,SendMessageOptions.DontRequireReceiver);
    88.        }
    89.     }  
    90.    
    91.     if (Physics.Raycast(ray_east, hit,10.0,wall_layer))
    92.     {
    93.        new_wall_id+=2;
    94.        Debug.DrawLine (ray_east.origin, hit.point);
    95.         if(check_neighbors)
    96.        {
    97.             hit.collider.gameObject.SendMessage("check_walls",false,SendMessageOptions.DontRequireReceiver);
    98.        }
    99.     }  
    100.    
    101.     if (Physics.Raycast(ray_south, hit,10.0,wall_layer))
    102.     {
    103.        new_wall_id+=4;
    104.        Debug.DrawLine (ray_south.origin, hit.point);
    105.         if(check_neighbors)
    106.        {
    107.             hit.collider.gameObject.SendMessage("check_walls",false,SendMessageOptions.DontRequireReceiver);
    108.        }
    109.     }  
    110.    
    111.     if (Physics.Raycast(ray_west, hit,10.0,wall_layer))
    112.     {
    113.        new_wall_id+=8;
    114.        Debug.DrawLine (ray_west.origin, hit.point);
    115.         if(check_neighbors)
    116.        {
    117.             hit.collider.gameObject.SendMessage("check_walls",false,SendMessageOptions.DontRequireReceiver);
    118.        }
    119.     }  
    120.    
    121.     if(wall_id != new_wall_id)//only change mesh if we actually changed id number  
    122.     {
    123.         //Debug.Log("current wall id = " + wall_id  + ":new wall id = " + new_wall_id);      
    124.         wall_id=new_wall_id;      
    125.         change_wall(new_wall_id);  
    126.     }
    127. }
    128.  
    129.  
    130. function change_wall(id:int)
    131. {
    132.     if(current_prefab !=null)
    133.     {
    134.         Destroy(current_prefab.gameObject);
    135.     }
    136.    
    137.     var y_rot:int=0;
    138.     var prefab_index:int=0;
    139.    
    140.     switch(id)
    141.     {
    142.         case 0:
    143.             prefab_index=0;
    144.             y_rot=0;          
    145.         break;
    146.        
    147.         case 1:
    148.             prefab_index=1;
    149.             y_rot=0;
    150.         break;
    151.        
    152.         case 2:
    153.             prefab_index=1;
    154.             y_rot=90;
    155.         break;
    156.        
    157.         case 3:
    158.             prefab_index=3;
    159.             y_rot=90;
    160.         break;
    161.        
    162.         case 4:
    163.             prefab_index=1;
    164.             y_rot=180;
    165.         break;
    166.        
    167.         case 5:
    168.             prefab_index=2;
    169.             y_rot=0;
    170.         break;
    171.        
    172.         case 6:
    173.             prefab_index=3;
    174.             y_rot=180;
    175.         break;
    176.        
    177.         case 7:
    178.             prefab_index=4;
    179.             y_rot=180;
    180.         break;
    181.        
    182.         case 8:
    183.             prefab_index=1;
    184.             y_rot=270;
    185.         break;
    186.        
    187.         case 9:
    188.             prefab_index=3;
    189.             y_rot=0;
    190.         break;
    191.        
    192.         case 10:
    193.             prefab_index=2;
    194.             y_rot=90;
    195.         break;
    196.        
    197.         case 11:
    198.             prefab_index=4;
    199.             y_rot=90;
    200.         break;
    201.        
    202.         case 12:
    203.             prefab_index=3;
    204.             y_rot=270;
    205.         break;
    206.        
    207.         case 13:
    208.             prefab_index=4;
    209.             y_rot=0;
    210.         break;
    211.        
    212.         case 14:
    213.             prefab_index=4;
    214.             y_rot=270;
    215.         break;
    216.        
    217.         case 15:
    218.             prefab_index=5;
    219.             y_rot=0;
    220.         break;  
    221.     }
    222.    
    223.     current_prefab=Instantiate(wall_prefabs[prefab_index],trans.position,Quaternion.Euler(0.0,y_rot,0.0)) as GameObject;
    224.     current_prefab.transform.parent=trans;
    225.     current_prefab.name="test";  
    226.     current_renderer=current_prefab.GetComponent(Renderer);
    227.     current_renderer.material.color =base_renderer.material.color;
    228. }
    229. /*
    230. function OnDrawGizmos()
    231. {
    232.     Gizmos.color=Color(0.0,1.0,0.0,0.5);
    233.     Gizmos.DrawCube(transform.position + Vector3.up * 0.5,Vector3.one);  
    234. }*/
    this is whats happening:

    wallplacebug.gif

    it worked at the end because i deactivated the wall_check.js
    do you know why? thanks!
     
  11. FreebordMAD

    FreebordMAD

    Joined:
    Mar 15, 2013
    Posts:
    633
    I'm sorry, but I still don't exactly understand the problem. Obviously, if you go into the scene and activate Popup_Text_Root then you have hacked the logic and nothing will work, because you have an open popup without callbacks and buttons... If you want to trigger a popup then please use this code:
    Code (CSharp):
    1. ((uMyGUI_PopupText)uMyGUI_PopupManager.Instance.ShowPopup(POPUP_TEXT)).SetText("Game logic objects missing!", "Some error message").ShowButton("ok");
    Also, if you deactivate the RightNav_BG then you cannot navigate in the menus... I don't understand the problem here. From my point of view you describe how uGUI works, if you go into the scene and activate or deactivate objects, then things stop working. Could you please be more precise and tell me what I could do here to improve the MRLE?



    Use CPG_CameraPerspectiveGizmo.RelativeScreenPos property. You have to change this line of code in the LE_LevelEditorMain class:
    Code (CSharp):
    1. m_cameraPerspectiveGizmo.RelativeScreenPos = new Vector2(1f-relativeSize*0.5f/cam.aspect-rightMenuOffset, 1f-relativeSize*0.5f);
    I'm really sorry to say this, but I cannot help you here. Your script has almost 250 lines of code and most of them are not commented. It would take me hours to understand it. Please reduce the script to a minimal number of lines (max. 50), that cause this behaviour, then I can take a look.
     
  12. Der_Kevin

    Der_Kevin

    Joined:
    Jan 2, 2013
    Posts:
    517
    yes, true its a uGUI problem. not a MRLE problem. i usually avoid this behavior by setting the needed UI components active or not via script on startup. so that i can even activate all ui components but on startup only the needed ones are active. but again, not a big deal right now :)

    okay, can understand this. i think the line that is causing this weird behavior is between line 13 and and 70. because after line 70 comes code which is only checking if a new wall is around and which prefab should be used. but sure, i can shrink down the code and see what is going on in the update and start function :)
    thanks!
     
  13. FreebordMAD

    FreebordMAD

    Joined:
    Mar 15, 2013
    Posts:
    633
    Ok, I understand now what you were trying to say. I can do this for popups, disable all of them on startup. However, I think that disabling/enabling all elements of the UI would be a huge overkill... It is up to the user to modify the UI as he needs it. There would be over 200 elements that can be disabled and enabled, I think doing this in scripts would be chaotic and not maintainable.

    Code (CSharp):
    1. if(child.gameObject.name=="Cube" ||child.gameObject.name=="3D-test" ||child.gameObject.name== "3D-test(Clone)")
    2.             {
    3.                 //do nothing
    4.             }
    5.             else
    6.             {
    7.                 //Debug.Log(child.gameObject.name);
    8.                 Destroy(child.gameObject);
    9.             }
    Probably the code above is your problem. There you check for GameObject.name, but the name of each LE_Object is its resource path.
     
  14. Der_Kevin

    Der_Kevin

    Joined:
    Jan 2, 2013
    Posts:
    517
    yeah, something is wrong there. i think i found the mistake, thanks :)

    uhm, just another question :D
    just for convenience: can i turn off the "create terrain" part somehow? so that the whole dialog doesn't show up and on startup is just a 250x250x250 size map?
     
  15. FreebordMAD

    FreebordMAD

    Joined:
    Mar 15, 2013
    Posts:
    633
    What you want is a custom terrain. You can create it and save it to the scene. Use the built-in Unity Terrain Editor to make changes to it (set 250 size in the inspector). Then link it to the main MRLE class. Doc link, see Default/Template Custom Unity Terrain.
     
    Der_Kevin likes this.
  16. Der_Kevin

    Der_Kevin

    Joined:
    Jan 2, 2013
    Posts:
    517
    okay, so the create terrain panel is gone now which is okay, but iam having somehow trouble setting up a terrain prefab. i think thats more a unity problem than MLRE. but maybe you solved this before.
    I create a terrain in my scene, dragged is down as prefab and when i click it again it says "terrain asset missing". have you experienced this before?
     
  17. FreebordMAD

    FreebordMAD

    Joined:
    Mar 15, 2013
    Posts:
    633
    I really have to change the docs here, you are not the first who misunderstood this. Just leave the terrain in your scene. It does not need to be a prefab, you can link it directly in the scene. Take a look at the pure terrain editor demo.
     
  18. Der_Kevin

    Der_Kevin

    Joined:
    Jan 2, 2013
    Posts:
    517
    ahhh :D okay, thanks ;)
     
    FreebordMAD likes this.
  19. Der_Kevin

    Der_Kevin

    Joined:
    Jan 2, 2013
    Posts:
    517
    so, the only thing thats left is that i have to figure out how to RightMenu_Object first instead of the RightMenu_Terrain?

    and, i dont know if that comes from all my customization but, when you scroll down the list of objects with your mouse wheel the camera zooms with that. i gues this is a bug?

    edit:
    racetrackeditor.gif

    some progress i wanted to share :)
     
    Last edited: Nov 23, 2015
    FaberVi and FreebordMAD like this.
  20. FreebordMAD

    FreebordMAD

    Joined:
    Mar 15, 2013
    Posts:
    633
    You could trigger the event below in your UI implementation class on Start (set mode to 1).
    LE_GUIInterface(.EventHandlers).OnEditModeBtn
    See doc here.

    This is indeed a bug...

    LOOKS AWESOME ;)
    View attachment 163101
     
    Der_Kevin likes this.
  21. dl_studios

    dl_studios

    Joined:
    Oct 14, 2012
    Posts:
    76
    FreebordMAD likes this.
  22. Der_Kevin

    Der_Kevin

    Joined:
    Jan 2, 2013
    Posts:
    517
    hm. like this?
    Code (CSharp):
    1. void Start () {
    2.  
    3.         GameObject.Find("Canvas").GetComponent<LE_GUIInterface>().OnEditModeBtn();
    but that seams not to work somehow?
     
  23. FreebordMAD

    FreebordMAD

    Joined:
    Mar 15, 2013
    Posts:
    633
    You can use the LE_GUIInterface.Instance, this is much faster than using GameObject.Find. Like this:
    Code (CSharp):
    1. using LE_LevelEditor.UI;
    2.  
    3. ...
    4.  
    5. LE_GUIInterface.Instance.OnEditModeBtn(1);
    What do you mean with "seem not to work"? Was the problem that you did not provide the parameter "1"?
     
  24. Der_Kevin

    Der_Kevin

    Joined:
    Jan 2, 2013
    Posts:
    517
    hey, yeah, that it doesn't bring up the Object Editor on startup. otherwise i dont get any errors:

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using LE_LevelEditor.UI;
    4.  
    5. public class EditMode : MonoBehaviour {
    6.  
    7.     void Start () {
    8.  
    9.         LE_GUIInterface.Instance.OnEditModeBtn(1);
    10.     }
    11. }
    thanks!
     
  25. FreebordMAD

    FreebordMAD

    Joined:
    Mar 15, 2013
    Posts:
    633
    Sorry, I see the problem now... OnEditModeBtn changes the edit mode of the editor, but it does not affect the UI. To show the objects tab on start you need to change the Selected Index property of the uMyGUI_TabBox script attached to the RightNav_BG object to 1.
    Additionally, you need to use the script like above. However, the editor changes its mode on start too. Therefore, you should use a script like the example below and change the editor mode in the first update loop.
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using LE_LevelEditor.UI;
    4.  
    5. public class EditMode : MonoBehaviour
    6. {
    7.     bool m_isEditModeActivated = false;
    8.  
    9.     void LateUpdate()
    10.     {
    11.         if (!m_isEditModeActivated)
    12.         {
    13.             LE_GUIInterface.Instance.OnEditModeBtn(1);
    14.             m_isEditModeActivated = true;
    15.             enabled = false;
    16.         }
    17.     }
    18. }
    19.  
    [EDIT:] alternatively you can also set the Is Select Tab On Start property of the uMyGUI_TabBox script attached to the RightNav_BG object to false and enable the tabs by hand which you want to see active on start.
     
    Last edited: Nov 24, 2015
    Der_Kevin likes this.
  26. Der_Kevin

    Der_Kevin

    Joined:
    Jan 2, 2013
    Posts:
    517
    best solution :) works perfect! thank you :)
     
    FreebordMAD likes this.
  27. dl_studios

    dl_studios

    Joined:
    Oct 14, 2012
    Posts:
    76
    @FreebordMAD Been working on saving the terrain to byte array and loading it. I'm not sure what I'm doing wrong exactly. I placed the methods you gave me as described above. The byte array looks to be saving fine. When I load the terrain data byte array it has a length of 5, in other words something saved.

    I then call the below but nothing happens at all. No nulls, no warnings, nothing. Do you have any ideas as to what I'm doing wrong here? The Callback never gets called. Meaning I guess the levelEditorMain is never ready? I assume something isn't subscribed to the event that needs to be? I also tried calling LoadLevelDataFromBytesCallback(data) without the ExecuteWhenReady, but this too had no effect. I'm using the LE_GUI Interface_u GU Iimpl Terrain Only script. Thanks!

    levelEditorMain.ExecuteWhenReady(


    () =>
    {

    Debug.LogError("Loading terrain the length is " + terrain.levelDataByteArray.Length);
    levelEditorMain.GetLoadEvent().LoadLevelDataFromBytesCallback(terrain.levelDataByteArray);
    Debug.LogError("CallBack For Load got called");


    });
     
  28. FreebordMAD

    FreebordMAD

    Joined:
    Mar 15, 2013
    Posts:
    633
    This is strange because if your saved file has the length of 5 then there is no data in it, because it should be at least several KB...
    Could you please attach your saved file?
    [EDIT:] just tested on a fresh v1.30 version. It works in the demos. Did it work for you in the demo setup?
     
    Last edited: Dec 2, 2015
  29. FreebordMAD

    FreebordMAD

    Joined:
    Mar 15, 2013
    Posts:
    633
    The next version of the Multiplatform Runtime Level Editor is ready for download! Many of you people have asked for undo/redo and finally I have included it in the MRLE. Additionally, performance for terrain editing, especially on mobile, is highly improved when build with Unity 5.2.2 or higher! Another feature request was to change materials (@HakJak ) on runtime, if you need something like this, then you need to try the variations feature!
    Here is the change log:
    Patch v1.30:
    FEAT: undo/redo (terrain and object editing)
    FEAT: HUGE terrain height editing performance improvement (for Unity 5.2.2 or higher)
    FEAT: variations property: materials can be changed and child objects can be activated/deactivated on runtime
    FEAT: find button: instances of the selected prefab are selected and focused one after another
    FEAT: object are listed in groups, where each group demonstrates a certain editor feature
     
    HakJak and Der_Kevin like this.
  30. musou0

    musou0

    Joined:
    Dec 2, 2015
    Posts:
    16
    @FreebordMAD Hi, I just buy your editor. It's a great help to me, but I'm a new guy on programming, so I'm using the example scene to develop my game, and I got some problems to solve, could you teach me how to make the functions I need, or can you make an update to add these functions into the example scenes? Many thanks.

    First problem is, I want to make a main menu at the start of the game, but right now in build settings, the editor must have the index "0" and the game scene the index "1". If I add a main menu, then it must be the index "0", and the load and play functions will be broken.

    Second problem is, when I save the scene, I can't name it manually, right now it have only one name "level.txt". I want to name the scene by my self after I click the save button, so I can save multiple scenes using the editor.

    The last problem is about loading. After I can save multiple scenes, I will need to select the scene in the folder and load them, in the main menu and the editor.

    This picture shows all I need:
     
  31. CYBERDAD

    CYBERDAD

    Joined:
    Sep 11, 2012
    Posts:
    29
    Hi,

    Before I buy it, I would like to know if it is possible to save the scene as Assetbundle so that I am able to load the saved scene into another App?
    Thanks.
     
  32. FreebordMAD

    FreebordMAD

    Joined:
    Mar 15, 2013
    Posts:
    633
    Thanks for the purchase!

    Change the lines below in ExampleGame_Game and ExampleGame_DungeonGame classes:
    Code (CSharp):
    1. // original line
    2. Application.LoadLevel(0);
    3.  
    4. // change to
    5. Application.LoadLevel("LE_ExampleEditor");
    In the ExampleGame_Editor class you need to change the following lines:
    Code (CSharp):
    1.  
    2. // original line in OnFullLevelEditorExampleBtn method
    3. StartCoroutine(LatePlay(0));
    4. ... change '0' to the index of your level editor scene
    5.  
    6. // original line in OnPlayButtonClick method
    7. StartCoroutine(LatePlay(1));
    8. ... change '1' to the index of your game scene
    9.  
    Search for the LEVEL_FILE_NAME constant in the ExampleGame_Editor and the ExampleGame_Game classes. Replace the occurences with the level file name that you want to use. You could trigger a popup that would allow to select the level name when you need it. For the popup you could use the PopupManager (uMyGUI_PopupManager). You would need to create a new popup class that allows to enter strings.

    As described above. Search for LEVEL_FILE_NAME and replace it.

    This picture is awesome :D
     
  33. FreebordMAD

    FreebordMAD

    Joined:
    Mar 15, 2013
    Posts:
    633
    According to the Unity documentation building asset bundles is possible only from the Unity Editor. Therefore, it is not possible to build them at runtime, neither with the Multiplatform Runtime Level Editor, nor with any other runtime editor. To do this you would need a Windows or Mac OS server, that would run the Unity Editor for building asset bundles.
     
  34. Der_Kevin

    Der_Kevin

    Joined:
    Jan 2, 2013
    Posts:
    517
    This is not really related to your level editor since you are "just" using the unity terrain but...
    ... do you maybe know how i can achieve this look for the terrain?


    so. flat shaded. low poly etc.
    like in the game "astroneer" for example (http://astroneer.space/)
     
  35. FreebordMAD

    FreebordMAD

    Joined:
    Mar 15, 2013
    Posts:
    633
    I think you can achieve this with the Unity terrain. You need to use a very low heightmap resolution (HeightmapResolutionSizes) in combination with with a custom shader. How have you created this image?
    [EDIT:] here is some shader code: http://answers.unity3d.com/questions/798510/flat-shading.html
    [EDIT:]you could also contact this guy:

    [EDIT:]and you could also try to convert the terrain to a mesh (on runtime or to in editor obj):
    http://wiki.unity3d.com/index.php?title=TerrainObjExporter
     
    Last edited: Dec 2, 2015
  36. Der_Kevin

    Der_Kevin

    Joined:
    Jan 2, 2013
    Posts:
    517
    Hey, thanks for the answer
    i think i tried this already but that didn't looked like i wanted. gonna try it again later just to be sure
    //edit, tried it. it looks better. or lets say you have sharper edges now. but still not this vertex effect

    google image search :D

    i think that converts only meshes as far as i can see? but also gonna try it later. but then i would call theNoSharedVertices script every frame/every time i paint the terrain height?

    did it :) but he said "Yes, that's mine, pretty old :D It's based on mesh, not terrain. I plan to release a tutorial on that someday."

    wouldnt that slow down the terrain editor? i mean than i would need to convert the terrain to a mesh everytime i change something in the leveleditor, or?
     
    Last edited: Dec 2, 2015
  37. FreebordMAD

    FreebordMAD

    Joined:
    Mar 15, 2013
    Posts:
    633
    Yes in both of these solutions you would need to generate a mesh from your terrain. That were just some suggestions, there are even more solutions on the Asset Store. It wouldn't slow down your application that much if you would update the mesh terrain only when editing is finished (mouse down -> edit -> mouse up -> update terrain). However, it would be hacky... Using a Unity Terrain to create a mesh terrain is just not the right way to do it. There are some mesh terrain engines on the Asset Store, I think you should test all of them and then go with the one that works best for you.

    You could also write a complex shader. This shader could read the normals from a texture instead of using those of the terrain. For example, you could save a normal for each vertex in a texture that has the same resolution as the terrain heightmap. In the vertex shader you could calculate which normal to take from the texture by looking at the world position of the vertex. Then you would pass the normal from the texture to the fragment shader instead of the original vertex normal. You would need to generate the texture on the CPU and update it after every terrain change (, but this is not too costly in means of performance). I think, this could be done with 200 lines of C# code and 100 lines of Cg code.
     
    Der_Kevin likes this.
  38. Der_Kevin

    Der_Kevin

    Joined:
    Jan 2, 2013
    Posts:
    517
    Cool, thanks. I think i can go on with these informations. I think the terrain shader will be the best solution for me personal.

    But can you name one or two mesh terrain engine assets? Iam somehow having trouble finding one.
    Thanks!
     
  39. dl_studios

    dl_studios

    Joined:
    Oct 14, 2012
    Posts:
    76
    @FreebordMAD Thanks for the reply. We tried the demo scene and it worked fine, so we realized we where using your system incorrectly. We've got it worked out and the terrain is now saving.

    We have a couple more questions.

    1. Can your system work with Relief Terrain Pack?

    2. Or if not RTP can the terrain use normal maps? It looks like in the LE_TerrainTextureConfig it only has a slot for textures nothing for normal map. Or does it look automatically for a normal map that has a certain naming convention?

    3. A feature we would love to see is the ability to stamp trees and grass like Unity has in its terrain editor and be able to save and load from its own byte array like you do with the terrain data. Are there any plans to do this?

    Thank you for the great support!
     
  40. FreebordMAD

    FreebordMAD

    Joined:
    Mar 15, 2013
    Posts:
    633
    https://www.assetstore.unity3d.com/en/#!/content/9043
    https://www.assetstore.unity3d.com/en/#!/content/8131
    https://www.assetstore.unity3d.com/en/#!/content/9180
    You have to search for voxel terrain. This is how you call mesh terrains. In contrast to heightmap terrains (like the Unity solution) you can build caves with voxel terrains. Heightmap terrains have always exactly one terrain vertex with a fixed Y position on a certain X,Z position and voxel terrains can have multiple vertecies with different Y positions at the same X,Z position.
     
    Der_Kevin likes this.
  41. FreebordMAD

    FreebordMAD

    Joined:
    Mar 15, 2013
    Posts:
    633
    I'm glad this works now. Could you please tell me what was wrong? Maybe I can improve the demos or the documentation. The best thing would be if it would be possible to print a warning in the Unity Editor if an incorrect setup is detected.

    Reading the description of Relief Terrain Pack I could not see if it uses the built-in Unity terrain. If it does, then it is compatible. Unity does not care if the terrain is edited by the Multiplatform Runtime Level Editor or by the Unity Terrain Editor, because both solutions use exactly the same methods from the TerrainData class. Also, if you run into any trouble with this, I will be happy to help you out.

    Indeed, you would need to extend LE_TerrainTextureConfig. However, this should not get too complex. Simply add a normal map property to the class and search for all references in the project, there should not be too many of them. I will put normal maps on the ToDo list.

    At the moment I have not implemented these features, because I think that players could be overchallenged with that. The main design concept of the MRLE is to be simple and intuitive. There are already 4 menus for terrain editing (height, set height, smooth, paint). If there would be more, then it could be just too much for non developers. Having said this, adding trees and detail placement is on the list, but the priority is not high... However, I know someone who has already implemented these features and I will ask him if I can give you his contact.

    You're welcome :)
     
  42. musou0

    musou0

    Joined:
    Dec 2, 2015
    Posts:
    16
    @FreebordMAD Hi, thanks for the reply. Now I can understand your reply about my first problem, and I'm working on the second. I try to make a popup with a text field (to input the name) and a button (save button). But when I finished, I found that the access to the save data folder is denied, and the console says:

    "UnauthorizedAccessException: Access to the path 'C:/Users/MYCOM/AppData/LocalLow/DefaultCompany/CharSys_Mecanim_F1/' is denied.
    System.IO.FileStream..ctor (System.String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, Boolean anonymous, FileOptions options) (at /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/System.IO/FileStream.cs:259)"

    Could you help me with this? Many thanks.
    This script is what I write:
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using UnityEngine .UI;
    4. using LE_LevelEditor .Example ;
    5. using LE_LevelEditor.Core;
    6. using LE_LevelEditor .UI ;
    7.  
    8. public class NameTheSaveData : MonoBehaviour {
    9.    
    10.     public InputField Saveinput0;//this input field let the user can give his map a name;
    11.     public Button Save0;//click to save the map;
    12.     public string Savename0;
    13.     public GameObject  Nonamepopup0;//if there is no name, then this popup will show and says that you must give the map a name;
    14.     public ExampleGame_Editor  Levelobject;
    15.     public LE_GUIInterface Saveevent0;
    16.  
    17.  
    18.     // Update is called once per frame
    19.     void Update () {
    20.  
    21.  
    22.     if (Save0) {
    23.  
    24.             if (Saveinput0.text == null )
    25.             {
    26.                 Nonamepopup0.SetActive (true );
    27.             }
    28.             else {
    29.                 Levelobject .LEVEL_FILE_NAME = Saveinput0 .text ;
    30.                 Saveevent0 .OnLevelSaveBtn ();
    31.             }
    32.         }
    33.     }
    34. }
     
  43. FreebordMAD

    FreebordMAD

    Joined:
    Mar 15, 2013
    Posts:
    633
    There is no file name after CharSys_Mecanim_F1/ it looks like there is a bug in your code and LEVEL_FILE_NAME ends up being empty.
     
  44. CYBERDAD

    CYBERDAD

    Joined:
    Sep 11, 2012
    Posts:
    29
    Hi,

    Thanks for your reply. Can your asset be used for example like ... a user modifying the terrain/scene via android (mobile) and save it on the server (PHP/MySQL) for later use ? I am wondering if I can use it to make a (not multi-user) online game where users can create their own scene or world and save it. When they return back the modified scene will display automatically. Is this possible with your asset ?
     
  45. FreebordMAD

    FreebordMAD

    Joined:
    Mar 15, 2013
    Posts:
    633
    Sure, this is exactly the purpose of this asset. Check my game Mad Snowboarding. You will see that people can play the same levels on Android, iOS, Windows Phone, Windows and Linux. Players can create levels on any platform and load them again on any other platform. Mad Snowboarding has more than 700 levels made by the players. The levels are uploaded to a server and stored as text files (you could also use MySQL here).
     
  46. CYBERDAD

    CYBERDAD

    Joined:
    Sep 11, 2012
    Posts:
    29
    That's excellent !
    I will now definitely buy your asset. Do you have an example on how the scene's can be saved as a text file on a server and on how to load them automatically on load ? Thanks !!
     
  47. CYBERDAD

    CYBERDAD

    Joined:
    Sep 11, 2012
    Posts:
    29
    Small question -> It is also possible to upload an assetbundle into a scene (when new assets are needed via menu) instead of every time deploying new versions of the application ? or does your asset foresee have another way of updating assets ?
     
  48. FreebordMAD

    FreebordMAD

    Joined:
    Mar 15, 2013
    Posts:
    633
    :)

    The example implementation saves a text file.The example game implementation automatically loads this game file.
    I can send you some PHP code examples showing how to receive files and store them. You could download the files by simply using the WWW class and providing an URL to the example implementation (ExampleGame_LoadSave class).
    Take a look at this post: http://forum.unity3d.com/threads/mu...any-one-interested.250920/page-4#post-2233288

    There is no asset bundle support in the MRLE yet. Therefore, you would need to deploy an update to the game or you would need to implement the Asset Bundle feature yourself.
     
  49. Centripetal

    Centripetal

    Joined:
    May 31, 2013
    Posts:
    96
    I've added functions to the LE_TerrainManager for painting trees and details (see below). After adding these you'll have to add code to the SetIsCursorAction function in GUI_3dTerrain script as well to call these functions. I also added new Terrain edit modes in the ETerrainEditMode enumeration.

    Code (CSharp):
    1.  
    2.         public void PaintTrees(int treePrototypeIndex, float delta, float relativeBrushSize, Vector2 relativeLocalLocation)
    3.         {
    4.             int numTrees = (int)Mathf.Clamp(relativeBrushSize * 0.5f * m_terrain.terrainData.detailResolution, 1f, 10f);
    5.  
    6.             Vector3 treePosition = new Vector3(relativeLocalLocation.y, 0f, relativeLocalLocation.x);
    7.  
    8.             int count = m_terrain.terrainData.treeInstanceCount;
    9.  
    10.             for (int i = 0; i < numTrees; i++)
    11.             {
    12.                 Vector3 newTreePosition = treePosition + new Vector3(Random.Range(-relativeBrushSize, relativeBrushSize) * 0.3f, 0f, Random.Range(-relativeBrushSize, relativeBrushSize) * 0.3f);
    13.  
    14.                 bool collision = false;
    15.  
    16.                 for (int c = 0; c < count; c++)
    17.                 {
    18.                     Vector3 a = m_terrain.terrainData.GetTreeInstance(c).position; a.y = 0f;
    19.                     if (Vector3.Distance(newTreePosition, a) < treeDensity)
    20.                     {
    21.                         collision = true;
    22.                         break;
    23.                     }
    24.                 }
    25.  
    26.                 if (!collision)
    27.                 {
    28.                     TreeInstance newTreeInstance = new TreeInstance();
    29.  
    30.                     if (treePrototypeIndex >= 0)
    31.                     {
    32.                         newTreeInstance.prototypeIndex = treePrototypeIndex;
    33.                     }
    34.                     else
    35.                     {
    36.                         newTreeInstance.prototypeIndex = (int)(Random.Range(0, treePrototypeCount - 0.001f));
    37.                     }
    38.  
    39.                     newTreeInstance.heightScale = 0.4f + Random.value * 0.6f;
    40.                     newTreeInstance.widthScale = 0.4f + Random.value * 0.6f;
    41.                     newTreeInstance.color = Color.white * 0.8f + (Color.yellow * (Random.value * 0.2f));
    42.                     newTreeInstance.position = newTreePosition;
    43.                     newTreeInstance.lightmapColor = Color.white;
    44.  
    45.                     ++count;
    46.                     m_terrain.AddTreeInstance(newTreeInstance);
    47.                 }
    48.             }
    49.         }
    50.  
    51.         public void EraseTrees(int treePrototypeIndex, float relativeBrushSize, Vector2 relativeLocalLocation)
    52.         {
    53.             TreeInstance[] treeInstances = m_terrain.terrainData.treeInstances;
    54.             List<TreeInstance> newTreeInstances = new List<TreeInstance>();
    55.  
    56.             Vector3 treePosition = new Vector3(p_relativeLocalLocation.y, 0f, relativeLocalLocation.x);
    57.             float threshold = relativeBrushSize * 0.3f + treeDensity;
    58.  
    59.             foreach (var treeInstance in treeInstances)
    60.             {
    61.                 a = treePosition; a.y = 0f;
    62.                 b = treeInstance.position; b.y = 0f;
    63.  
    64.                 if (Vector3.Distance(a, b) > threshold && (treePrototypeIndex == -1 || treePrototypeIndex == treeInstance.prototypeIndex))
    65.                 {
    66.                     newTreeInstances.Add(treeInstance);
    67.                 }
    68.             }
    69.  
    70.             // apply the changed trees
    71.             m_terrain.terrainData.treeInstances = newTreeInstances.ToArray();
    72.             m_terrain.Flush();
    73.         }
    74.  
    75.         public void PaintDetail(int detailPrototypeIndex, int detailDensity, Texture2D alphaBrushTexture, float relativeBrushSize, Vector2 relativeLocalLocation)
    76.         {
    77.             int[][,] detailLayer;
    78.             int minX, maxX, minY, maxY;
    79.             float relBrushMinX, relBrushMinY, detailLayerMaxX, detailLayerMaxY;
    80.  
    81.             detailLayerMaxX = m_terrain.terrainData.detailWidth - 1;
    82.             detailLayerMaxY = m_terrain.terrainData.detailHeight - 1;
    83.  
    84.             GetAffectedAreaInternal(alphaBrushTexture, relativeBrushSize, relativeLocalLocation, detailLayerMaxX, detailLayerMaxY,
    85.                                     out minX, out maxX, out minY, out maxY, out relBrushMinX, out relBrushMinY);
    86.  
    87.             // get the current alphamaps array
    88.             if (detailPrototypeIndex >= 0)
    89.             {
    90.                 detailLayer = new int[1][,];
    91.                 detailLayer[0] = m_terrain.terrainData.GetDetailLayer(minY, minX, maxY - minY + 1, maxX - minX + 1, detailPrototypeIndex);
    92.             }
    93.             else
    94.             {
    95.                 detailLayer = new int[detailPrototypeCount][,];
    96.                 int y = minY;
    97.                 int x = minX;
    98.                 int h = maxY - minY + 1;
    99.                 int w = maxX - minX + 1;
    100.                 for (int i = 0; i < detailPrototypeCount; i++)
    101.                 {
    102.                     detailLayer[i] = m_terrain.terrainData.GetDetailLayer(y, x, h, w, i);
    103.                 }
    104.             }
    105.  
    106.             // apply alpha change to every affected alpha map entry
    107.             // according to given p_delta and the alpha value in alphaBrushTexture
    108.             // alpha is always changed towards p_targetValue
    109.             int iterateToX = Mathf.Min(maxX - minX, detailLayer[0].GetLength(0) - 1);
    110.             int iterateToY = Mathf.Min(maxY - minY, detailLayer[0].GetLength(1) - 1);
    111.             float brushValue, detailValue;
    112.  
    113.             for (int indexX = 0; indexX <= iterateToX; indexX++)
    114.             {
    115.                 for (int indexY = 0; indexY <= iterateToY; indexY++)
    116.                 {
    117.                     if (detailPrototypeIndex >= 0)
    118.                     {
    119.                         detailValue = detailLayer[0][indexX, indexY];
    120.  
    121.                         if (Mathf.Abs(detailValue - detailDensity) > 0.0001f)
    122.                         {
    123.                             brushValue = alphaBrushTexture.GetPixelBilinear(
    124.                                 (((indexY + minY) / detailLayerMaxY) - relBrushMinY) / relativeBrushSize,
    125.                                 (((indexX + minX) / detailLayerMaxX) - relBrushMinX) / relativeBrushSize).a * 10f;
    126.  
    127.                             // apply change in the selected layer
    128.                             if (brushValue > 2f)
    129.                             {
    130.                                 detailLayer[0][indexX, indexY] = detailDensity;
    131.                             }
    132.                         }
    133.                     }
    134.                     else // Erase details
    135.                     {
    136.                         brushValue = alphaBrushTexture.GetPixelBilinear(
    137.                            (((indexY + minY) / detailLayerMaxY) - relBrushMinY) / relativeBrushSize,
    138.                            (((indexX + minX) / detailLayerMaxX) - relBrushMinX) / relativeBrushSize).a * 10f;
    139.  
    140.                         // apply change in the selected layer
    141.                         if (brushValue > 2f)
    142.                         {
    143.                             for (int i = 0; i < detailPrototypeCount; i++)
    144.                             {
    145.                                 detailLayer[i][indexX, indexY] = 0;
    146.                             }
    147.                         }
    148.                     }
    149.                 }
    150.             }
    151.  
    152.             // apply the changed alpha maps
    153.             if (detailPrototypeIndex >= 0)
    154.             {
    155.                 m_terrain.terrainData.SetDetailLayer(minY, minX, detailPrototypeIndex, detailLayer[0]);
    156.             }
    157.             else
    158.             {
    159.                 for (int i = 0; i < detailPrototypeCount; i++)
    160.                 {
    161.                     m_terrain.terrainData.SetDetailLayer(minY, minX, i, detailLayer[i]);
    162.                 }
    163.             }
    164.         }
    Note that if you have over 2000 trees on the terrain the height painting becomes really slow. You can change the call to TerrainData.SetHeights function to TerrainData.SetHeightsDelayLOD which delays updating the tree and detail data. You'll need to call Terrain.ApplyDelayedHeightmapModification() once the user releases the mouse button or touch.

    If you start making heavy changes to the code, you will need to keep your own version separate as they will likely conflict with updates with the MPLE asset.
     
    Last edited: Dec 3, 2015
    Ony and Arkade like this.
  50. Centripetal

    Centripetal

    Joined:
    May 31, 2013
    Posts:
    96
    See my post above.