Мультитеррейны

Инструменты для разработки

Мультитеррейны

Сообщение foonk 12 окт 2010, 16:46

Всем привет!
Хочу поделиться полезным инструментом для создания мультитеррейнов непосредственно в Unity.

Синтаксис:
Используется javascript
import UnityEngine.GUILayout;
import UnityEditor.EditorGUILayout;

enum Direction {Across, Down}

class Stitch extends ScriptableWizard {
    static var across : int;
    static var down : int;
    static var tWidth : int;
    static var tHeight : int;
    static var totalTerrains : int;
    static var terrains : Object[];
    static var stitchWidth : int;
    static var message : String;
    static var terrainRes : int;
    static var lineTex : Texture2D;
   
    @MenuItem ("Terrain/Stitch...")
    static function CreateWizard () {
        if (lineTex == null) {    // across/down etc. defined here, so closing and re-opening wizard doesn't reset vars
            across = down = tWidth = tHeight = 2;
            stitchWidth = 10;
            SetNumberOfTerrains();
            lineTex = EditorGUIUtility.whiteTexture;
        }
        message = "";
        ScriptableWizard.DisplayWizard("Stitch Terrains", Stitch);
    }
   
    function OnGUI () {
        BeginHorizontal(Width(220));
            BeginVertical();
                BeginHorizontal(Width(190));
                    Label("Number of terrains across:");
                    across = Mathf.Max(IntField(across, Width(30)), 1);
                EndHorizontal();
                BeginHorizontal(Width(190));
                    Label("Number of terrains down:");
                    down = Mathf.Max(IntField(down, Width(30)), 1);
                EndHorizontal();
            EndVertical();
            BeginVertical();
                Space(12);
                if (Button("Apply")) {
                    tWidth = across;
                    tHeight = down;
                    SetNumberOfTerrains();
                }
            EndVertical();
        EndHorizontal();
       
        Space(16);
       
        var counter = 0;
        for (h = 0; h < tHeight; h++) {
            BeginHorizontal();
                Space(12);
                for (w = 0; w < tWidth; w++) {
                    terrains[counter] = ObjectField(terrains[counter++], Terrain, Width(112));
                    Space(5);
                }
            EndHorizontal();
            Space(10);
        }
        DrawGrid(Color.black, 1);
        DrawGrid(Color.white, 0);
       
        Space(10);
       
        BeginHorizontal();
            Label("Stitch width: " + stitchWidth, Width(90));
            stitchWidth = HorizontalSlider(stitchWidth, 1, 100);
        EndHorizontal();
       
        Space(2);

        Label(message);

        Space(2);
       
        BeginHorizontal();
            if (Button("Clear")) {
                SetNumberOfTerrains();
            }
            if (Button("Stitch")) {
                StitchTerrains();
            }
        EndHorizontal();
    }
   
    private function DrawGrid (color : Color, offset : int) {
        GUI.color = color;
        for (i = 0; i < tHeight+1; i++) {
            GUI.DrawTexture(Rect(5 + offset, 63 + offset + 28*i, 121*tWidth, 1), lineTex);
        }
        for (i = 0; i < tWidth+1; i++) {
            GUI.DrawTexture(Rect(5 + offset + 121*i, 63 + offset, 1, 28*tHeight + 1), lineTex);        
        }
    }
   
    static function SetNumberOfTerrains () {
        terrains = new Object[tWidth * tHeight];
        totalTerrains = tWidth * tHeight;
        message = "";
    }

    static function StitchTerrains () {
        for (t in terrains) {
            if (t == null) {
                message = "All terrain slots must have a terrain assigned";
                return;
            }
        }
   
        terrainRes = terrains[0].terrainData.heightmapWidth;
        if (terrains[0].terrainData.heightmapHeight != terrainRes) {
            message = "Heightmap width and height must be the same";
            return;
        }
       
        for (t in terrains) {
            if (t.terrainData.heightmapWidth != terrainRes || t.terrainData.heightmapHeight != terrainRes) {
                message = "All heightmaps must be the same resolution";
                return;
            }
        }
       
        if (tWidth == 1 && tHeight == 1) {
            message = "Only one terrain specified...nothing to stitch";
            return;
        }
       
        for (t in terrains) {
            Undo.RegisterUndo(t.terrainData, "Stitch");
        }

        stitchWidth = Mathf.Clamp(stitchWidth, 1, (terrainRes-1)/2);
        var counter = 0;
        var total = tHeight*(tWidth-1) + (tHeight-1)*tWidth;
   
        for (h = 0; h < tHeight; h++) {
            for (w = 0; w < tWidth-1; w++) {
                EditorUtility.DisplayProgressBar("Stitching...", "", Mathf.InverseLerp(0, total, ++counter));
                BlendData (terrains[h*tWidth + w], terrains[h*tWidth + w + 1], Direction.Across);
            }
        }
        for (h = 0; h < tHeight-1; h++) {
            for (w = 0; w < tWidth; w++) {
                EditorUtility.DisplayProgressBar("Stitching...", "", Mathf.InverseLerp(0, total, ++counter));
                BlendData (terrains[h*tWidth + w], terrains[(h+1)*tWidth + w], Direction.Down);
            }
        }
       
        message = "Terrains stitched successfully";
        EditorUtility.ClearProgressBar();
    }
   
    static function BlendData (terrain1 : Terrain, terrain2 : Terrain, thisDirection : Direction) {
        var heightmapData = terrain1.terrainData.GetHeights(0, 0, terrainRes, terrainRes);
        var heightmapData2 = terrain2.terrainData.GetHeights(0, 0, terrainRes, terrainRes);
        var pos = terrainRes-1;
       
        if (thisDirection == Direction.Across) {
            for (i = 0; i < terrainRes; i++) {
                for (j = 1; j < stitchWidth; j++) {
                    var mix = Mathf.Lerp(heightmapData[i, pos-j], heightmapData2[i, j], .5);
                    if (j == 1) {
                        heightmapData[i, pos] = mix;
                        heightmapData2[i, 0] = mix;
                    }
                    var t = Mathf.SmoothStep(0.0, 1.0, Mathf.InverseLerp(1, stitchWidth-1, j));
                    heightmapData[i, pos-j] = Mathf.Lerp(mix, heightmapData[i, pos-j], t);
                    heightmapData2[i, j]    = Mathf.Lerp(mix, heightmapData2[i, j], t);
                }
            }
        }
        else {
            for (i = 0; i < terrainRes; i++) {
                for (j = 1; j < stitchWidth; j++) {
                    mix = Mathf.Lerp(heightmapData2[pos-j, i], heightmapData[j, i], .5);
                    if (j == 1) {
                        heightmapData2[pos, i] = mix;
                        heightmapData[0, i] = mix;
                    }
                    t = Mathf.SmoothStep(0.0, 1.0, Mathf.InverseLerp(1, stitchWidth-1, j));
                    heightmapData2[pos-j, i] = Mathf.Lerp(mix, heightmapData2[pos-j, i], t);
                    heightmapData[j, i]      = Mathf.Lerp(mix, heightmapData[j, i], t);
                }
            }
        }
       
        terrain1.terrainData.SetHeights(0, 0, heightmapData);
        terrain2.terrainData.SetHeights(0, 0, heightmapData2);
    }
}


Способ применения: создаем, к примеру, 4 террейна, настраиваем высоты, расставляем в порядке:
1 2
3 4
Открываем Terrain - Stitch, перетаскиваем террейны в том же порядке в окно Stitch Terrains и жмем Stitch, вуаля :)
— 2010-й год. В Ладу Калину поставили первый Глонасс-приемник размером с утюг. В то же время Эппл выпускает плеер размером меньше спичечного коробка с цветным дисплеем и тачскрином.
Добавить foonk09 в Skype
Аватара пользователя
foonk
UNITрон
 
Сообщения: 302
Зарегистрирован: 16 май 2009, 20:25
Откуда: Москва
  • ICQ

Вернуться в Инструментарий

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 2