Unity记录5.6-地图-天空地面及地底的地形

本文介绍了在Unity中使用自定义算法生成天空、平原、地底三种不同地形的方法,考虑了地形生成的区域判断、方向概率以及周围环境对地形的影响。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

文章首发见博客:https://mwhls.top/4854.html
无图/格式错误/后续更新请见首发页。
更多更新请到mwhls.top查看
欢迎留言提问或批评建议,私信不回。

汇总:Unity 记录

摘要:天空,平原,地底的不同地形的生成。

地形-2023/9/11
  • 这部分代码断断续续写了好几天,不好计时,所以只标记最后的时间。
  • 三类测试地形,天空,平原,矿洞。
    • 地形有生成区域判断。
    • 地形周围,同类地形生成概率增加。
    • 地形的地势走向具有独特性。例如平原,绝大部分是东西走向,极小几率生成其它走向,并且没有悬空地形。
    • 地形决定了当前的主要方块是哪些。
  • 目前的判断条件
    • 生成高度影响当前地形。
    • 周围地形影响当前地形。
    • 当前地形决定当前地势。
    • 周围地势影响当前地势。
    • 当前地形决定当前的方块。
实现代码-2023/9/11
  • 核心部分,还有一个影响部分是direction的地势走向,但不重要。
    using System.Collections.Generic;
    using UnityEngine;
    using System.Linq;
    using MathNet.Numerics.LinearAlgebra;
    
    /*
     * ---------- type of terrain ----------
     * ---------- direction fluctuation
     * flat: 2
     * undulating: 1 3 7 9
     * steep: 4 6 14 16
     * suspended: 11 12 13 17 19
     * full: 5
     * empty: 0
     * ---------- ground
     * |    tag     |  flat  |  undulating  |  steep  |  suspended  |  full  |  empty  |
     * |------------|--------|--------------|---------|-------------|--------|---------|
     * |    flat    |  most  |     few      |   no    |     no      |  some  |   no    |
     * | undulating |  some  |     some     |   few   |     no      |  some  |   no    |
     * |   steep    |  some  |     some     |   some  |     few     |  some  |   no    |
     * |  suspended |  few   |     some     |   some  |     some    |  some  |   no    |
     *
     * ---------- underground
     * |    tag     |  flat  |  undulating  |  steep  |  suspended  |  full  |  empty  |
     * |------------|--------|--------------|---------|-------------|--------|---------|
     * |  spacious  |  most  |     few      |   some  |     few     |  few   |   some  |
     * |  roomy     |  some  |     some     |   some  |     some    |  some  |   some  |
     * |  narrow    |  few   |     some     |   few   |     some    |  most  |   few   |
     *
     * ---------- sky
     * |    tag     |  flat  |  undulating  |  steep  |  suspended  |  full  |  empty  |
     * |------------|--------|--------------|---------|-------------|--------|---------|
     * |    sky     |   no   |      no      |    no   |     no      |   no   |   most  |
     *
     */
    public struct TerrainType{
        public string[] tags;
        public string name;
        public float scale;
        public string[] dirs_avail;         // available directions
        public float[] dirs_prob;           // probability of each available directions
    }
    
    public struct TerrainInfo{
        public string ID;
        public string name;
        public string[] tags;
        public int[] h_avail;               // where can it generating, height means block offset, left close right open interval
        public string tile_surface;         // ID, surface tile
        public string tile_transition;      // ID, transition from terr1 to terr2, trans_tile is the surface_tile of terr2
        public string[] tiles_mineral;      // ID, which mineral can be generated
    }
    
    public class TilemapTerrain{
        List<TerrainType> _terrains_type = new();
        List<TerrainInfo> _terrains_info = new();
        public Dictionary<string, TerrainInfo> ID2TerrainInfo = new();
        public Dictionary<string[], TerrainType> tags2TerrainType = new();
    
        public TilemapTerrain() {
            _init_terrainsType();
            _init_terrainsInfo();
            _init_ID2TerrainInfo();
            _init_tags2TerrainType();
        }
    
        public TilemapBlock set_terrain_random(TilemapBlock block, TilemapAroundBlock around_blocks){
            List<string> terrains_avail = _get_available_terrains(block);
            float[] terrains_prob = _get_terrains_prob(terrains_avail, around_blocks);
            string terrain_ID = terrains_avail[_random_by_prob(terrains_prob)];
            block.terrain_ID = terrain_ID;
            return block;
        }
    
        public TilemapBlock set_terrainType_random(TilemapBlock block, TilemapAroundBlock around_blocks){
            List<TerrainType> terrainTypes_avail = _get_available_terrainTypes(block.terrain_ID);
            float[] terrainTypes_prob = _get_terrainTypes_prob(terrainTypes_avail, around_blocks);
            string[] terrainTypes_tags = terrainTypes_avail[_random_by_prob(terrainTypes_prob)].tags;
            block.terrainType_tags = terrainTypes_tags;
            return block;
        }
    
        List<TerrainType> _get_available_terrainTypes(string terrain_ID){
            TerrainInfo terrain = ID2TerrainInfo[terrain_ID];
            List<TerrainType> types = new();
            for (int i = 0; i < _terrains_type.Count; i++){
                if (_check_tagsIsSubset(terrain.tags, _terrains_type[i].tags))
                    types.Add(_terrains_type[i]);
            }
            return types;
        }
    
        float[] _get_terrainTypes_prob(List<TerrainType> types, TilemapAroundBlock around_blocks){
            // init
            float[] probs = new float[types.Count];
            for(int i = 0; i < types.Count; i++){
                probs[i] = 1;
                // increase the prob of exist tags
                if(around_blocks.up.isExist && _check_tagsIsSubset(around_blocks.up.terrainType_tags, types[i].tags))           probs[i] += types.Count;
                if(around_blocks.down.isExist && _check_tagsIsSubset(around_blocks.down.terrainType_tags, types[i].tags))       probs[i] += types.Count;
                if(around_blocks.left.isExist && _check_tagsIsSubset(around_blocks.left.terrainType_tags, types[i].tags))       probs[i] += types.Count;
                if(around_blocks.right.isExist && _check_tagsIsSubset(around_blocks.right.terrainType_tags, types[i].tags))     probs[i] += types.Count;
            }
            return probs;
        }
    
        bool _check_tagsIsSubset(string[] tags_sub, string[] tags_super){
            return tags_sub.All(item => tags_super.Contains(item));
        }
    
        bool _check_terrainAvailable(TilemapBlock block, TerrainInfo terrain){
            // height condition
            if (block.offsets.y < terrain.h_avail[0] && terrain.h_avail[0] != -999999999) return false;
            if (block.offsets.y >= terrain.h_avail[1] && terrain.h_avail[1] != 999999999) return false;
            // all pass
            return true;
        }
    
        List<string> _get_available_terrains(TilemapBlock block){
            List<string> availables = new ();
            for(int i = 0; i < _terrains_info.Count; i++)
                if (_check_terrainAvailable(block, _terrains_info[i]))
                    availables.Add(_terrains_info[i].ID);
            return availables;
        }
    
        float[] _get_terrains_prob(List<string> terrains, TilemapAroundBlock around_blocks){
            // init
            float[] probs = new float[terrains.Count];
            for(int i = 0; i < terrains.Count; i++){
                probs[i] = 1;
                // increase the prob of exist terrain
                if(around_blocks.up.isExist && terrains[i]==around_blocks.up.terrain_ID)          probs[i] += terrains.Count;
                if(around_blocks.down.isExist && terrains[i]==around_blocks.down.terrain_ID)      probs[i] += terrains.Count;
                if(around_blocks.left.isExist && terrains[i]==around_blocks.left.terrain_ID)      probs[i] += terrains.Count;
                if(around_blocks.right.isExist && terrains[i]==around_blocks.right.terrain_ID)    probs[i] += terrains.Count;
            }
            return probs;
        }
    
        // ---------- other ----------
    
        int _random_by_prob(float[] probs){
            float sum = probs.Sum();
            float target = Random.Range(0f, sum);
            for(int i = 0; i < probs.Length; i++){
                target -= probs[i];
                if(target <= 0) return i;
            }
            return probs.Length - 1;
        }
    
        // ---------- init ----------
        void _init_ID2TerrainInfo(){
            for(int i = 0; i < _terrains_info.Count; i++){
                ID2TerrainInfo.Add(_terrains_info[i].ID, _terrains_info[i]);
            }
        }
    
        void _init_tags2TerrainType(){
            for(int i = 0; i < _terrains_type.Count; i++){
                tags2TerrainType.Add(_terrains_type[i].tags, _terrains_type[i]);
            }
        }
    
        void _init_terrainsInfo(){
            _terrains_info.Add(new TerrainInfo{
                ID = "0", name = "plain", tags = new string[]{"flat", "ground"},
                h_avail = new int[]{-999999999, 999999999},
                tile_surface = "1",
            });
            _terrains_info.Add(new TerrainInfo{
                ID = "1", name = "sky", tags = new string[]{"sky"},
                h_avail = new int[]{-999999999, 999999999},
                tile_surface = "0",
            });
            _terrains_info.Add(new TerrainInfo{
                ID = "2", name = "underground gravel", tags = new string[]{"underground"},
                h_avail = new int[]{-999999999, 999999999},
                tile_surface = "3",
            });
        }
    
        void _init_terrainsType(){
            _terrains_type.Add(new TerrainType {
                tags = new string[] {"flat", "ground"},
                name = "flat ground", scale = 1f,
                dirs_avail = new string[] { "0", "1", "2",   "3", "4", "5",  "6", "7", "9",   "11", "12", "13",   "14", "16", "17",   "19" },
                dirs_prob = new float[] { 0.1f, 1, 50,   1, 0.1f, 5,   0.1f, 1, 1,   0.1f, 0.1f, 0.1f,   0.1f, 0.1f, 0.1f,   0.1f},
            });
            _terrains_type.Add(new TerrainType {
                tags = new string[] {"undulating", "ground"},
                name = "undulating ground", scale = 2f,
                dirs_avail = new string[] {"0", "1", "2",   "3", "4", "5",   "6", "7", "9",   "11", "12", "13",   "14", "16", "17",   "19" },
                dirs_prob = new float[] {0.1f, 10, 10,   10, 1, 10,   1, 10, 10,   0.1f, 0.1f, 0.1f,   1, 1, 0.1f,   0.1f },
            });
            _terrains_type.Add(new TerrainType {
                tags = new string[] {"steep", "ground"},
                name = "steep ground", scale = 4f,
                dirs_avail = new string[] {"0", "1", "2",   "3", "4", "5",   "6", "7", "9",   "11", "12", "13",   "14", "16", "17",   "19" },
                dirs_prob = new float[] {0.1f, 10, 10,   10, 10, 10,   10, 10, 10,   1, 1, 1,   10, 10, 1,   1 },
            });
            _terrains_type.Add(new TerrainType {
                tags = new string[] {"suspended", "ground"},
                name = "suspended ground", scale = 8f,
                dirs_avail = new string[] {"0", "1", "2",   "3", "4", "5",   "6", "7", "9",   "11", "12", "13",   "14", "16", "17",   "19" },
                dirs_prob = new float[] {0.1f, 10, 1,   10, 10, 10,   10, 10, 10,   10, 10, 10,   10, 10, 10,   10 },
            });
    
            _terrains_type.Add(new TerrainType {
                tags = new string[] {"spacious", "underground"},
                name = "spacious underground", 
                dirs_avail = new string[] {"0", "1", "2",   "3", "4", "5",   "6", "7", "9",   "11", "12", "13",   "14", "16", "17",   "19" },
                dirs_prob = new float[]   { 10, 1, 50,   1, 10, 1,   10, 1, 1,   1, 1, 1,   10, 10, 1,   1 },
            });
            _terrains_type.Add(new TerrainType {
                tags = new string[] {"roomy", "underground"},
                name = "roomy underground", 
                dirs_avail = new string[] {"0", "1", "2",   "3", "4", "5",   "6", "7", "9",   "11", "12", "13",   "14", "16", "17",   "19" },
                dirs_prob = new float[]   { 10, 10, 10,   10, 10, 10,   10, 10, 10,   10, 10, 10,   10, 10, 10,   10 },
            });
            _terrains_type.Add(new TerrainType {
                tags = new string[] {"narrow", "underground"},
                name = "narrow underground", 
                dirs_avail = new string[] {"0", "1", "2",   "3", "4", "5",   "6", "7", "9",   "11", "12", "13",   "14", "16", "17",   "19" },
                dirs_prob = new float[]   { 1, 10, 1,   10, 1, 50,   1, 10, 10,   10, 10, 10,   1, 1, 10,   10 },
            });
    
            _terrains_type.Add(new TerrainType {
                tags = new string[] {"sky"},
                name = "sky", scale = 1f,
                dirs_avail = new string[] {"0", "1", "2",   "3", "4", "5",   "6", "7", "9",   "11", "12", "13",   "14", "16", "17",   "19"},
                dirs_prob = new float[] {50, 0.1f, 0.1f,   0.1f, 0.1f, 0.1f,   0.1f, 0.1f, 0.1f,   0.1f, 0.1f, 0.1f,   0.1f, 0.1f, 0.1f, 0.1f},
            });
        }
    
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值