Unity记录5.1-地图-定点生成左右走向地图

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

汇总:Unity 记录

今天(2023/08/29)写的暴爽。

摘要:指定多个坐标,生成左右走向地图块。

初步设想-2023/08/20
  • 这部分想实现几个内容:
    • 指定坐标点a,生成随机地图块,a为地面交界处
    • 指定坐标点ab,生成随机地图块,ab为地面交界处
    • 指定坐标点abc,生成随机地图块,ab为地面,c为洞穴地面
指定坐标点a生成-2023/08/26-2023/08/27
  • 两个重点,首先是指定坐标点a为地面交界处,其次是指定坐标点a外无虚空。
    • 无虚空是为了避免下层区块判断上层区块的虚空位置。
  • 为了实现地面交界处,用了diff_h作为过渡。
  • 为了实现无虚空,用了一个min_h作为过渡。
    public List<List<string>> generate_1DTilemap_by_tile(Vector3Int tile_pos, float scale){
        Vector3Int Bsize = _game_configs.__block_size__;
        float scale_for_transition;
        int base_x = UnityEngine.Random.Range(0, 1000000);
        int perlin_h = Mathf.CeilToInt(Bsize.y * Mathf.PerlinNoise((tile_pos.x + base_x)/scale + 0.5f, 0.5f));
        int diff_h = tile_pos.y - perlin_h;
        int min_h = Mathf.CeilToInt(Bsize.y * 0.1f);
        List<List<string>> map_info = new List<List<string>>();
        for (int i = 0; i < Bsize.x; i++){
            // get perlin height
            perlin_h = Mathf.CeilToInt(Bsize.y * Mathf.PerlinNoise(((i + base_x)/scale + 0.5f), 0.5f));
            // gradient for transition from tile_pos to other pos.
            scale_for_transition = (Math.Abs(tile_pos.x - i)) / (Bsize.y + 0.5f) / 2f;
            perlin_h = perlin_h + Mathf.CeilToInt(diff_h * (1f - scale_for_transition));
            // constraint to postive number
            perlin_h = Mathf.Clamp(perlin_h, 0, perlin_h);
            // constraint to min height
            perlin_h = perlin_h + Mathf.CeilToInt(min_h * scale_for_transition);
            List<string> map_col = new List<string>();
            for (int j = 0; j < Bsize.y; j++){
                if (j < perlin_h)
                    map_col.Add("1");
                else
                    map_col.Add("0");
            }
            map_info.Add(map_col);
        }
        map_info.Reverse();
        return map_info;
    }
左右走向生成-2023/08/27-2023/08/29
  • 过渡区域蛮难写的,都翻出了好久不用的笔
  • 是左右走向的效果,左右两个边界点是随机的
    • 如果左右有其它块,那就是指定的边界点。
  • 限制了最低高度和最高高度,但现在记录的时候感觉好像都用不到,只是在画下面图的时候比较连续…
  • 效果如下,生成了两行,可以看到每行都是连续的。

Unity记录5.1-地图-定点生成左右走向地图_第1张图片

  • 下面分别是生成代码以及辅助代码。
    public BlockInfo generate_1DTilemap_plain(Vector3Int block_offsets, float scale, BlockInfo block_left, BlockInfo block_right){
        // init
        int perlin_h, left_x, left_y, left_perlin, right_x, right_y, right_perlin;
        // for convenience
        Vector3Int Bsize = _game_configs.__block_size__;
        // min_h also means boundBottom_h or boundTop_h
        int min_h = Mathf.CeilToInt(Bsize.y * 0.1f);
        int max_h = Bsize.y - min_h;
        // for record
        BlockInfo block_info = new BlockInfo();
        Vector3Int target_left_pos, target_right_pos;
        List<List<string>> map = new List<List<string>>();
        // get boundary target point
        if (block_left.block_type != "empty"){
            target_left_pos = new Vector3Int(0, block_left.bound_right.pos.y);
        } else {
            target_left_pos = new Vector3Int(0, UnityEngine.Random.Range(min_h, max_h));
        }
        if (block_right.block_type != "empty"){
            target_right_pos = new Vector3Int(Bsize.x - 1, block_right.bound_left.pos.y);
        } else {
            target_right_pos = new Vector3Int(Bsize.x - 1, UnityEngine.Random.Range(min_h, max_h));
        }
        // random for generating a different black in the same pos
        int base_x = UnityEngine.Random.Range(0, 1000000);
        // original perlin height in bound
        int perlin_left_h = Mathf.CeilToInt((max_h - min_h) * Mathf.PerlinNoise((target_left_pos.x + base_x)/scale, 0f)) + min_h;
        int perlin_right_h = Mathf.CeilToInt((max_h - min_h) * Mathf.PerlinNoise((target_right_pos.x + base_x)/scale, 0f)) + min_h;
        // for (int x = Bsize.x - 1; x >= 0; x--){
        for (int x = 0; x < Bsize.x; x++){
            // original perlin height in current pos
            perlin_h = Mathf.CeilToInt((max_h - min_h) * Mathf.PerlinNoise((x + base_x)/scale, 0f)) + min_h;
            // constraint "original perlin height" to "target height" by transition, and then constraint it to "min_h" and "max_h"
            left_x = target_left_pos.x;
            left_y = target_left_pos.y;
            left_perlin = perlin_left_h;
            right_x = target_right_pos.x;
            right_y = target_right_pos.y;
            right_perlin = perlin_right_h;
            perlin_h = constraint_target_h(x, perlin_h, left_x, left_y, left_perlin, right_x, right_y, right_perlin);
            perlin_h = constraint_min_h(x, perlin_h, left_x, right_x, min_h);
            perlin_h = constraint_max_h(x, perlin_h, left_x, right_x, max_h, min_h);
            // fill
            List<string> map_col = new List<string>();
            for (int y = 0; y < Bsize.y; y++){
                if (y < perlin_h)
                    map_col.Add("1");
                else
                    map_col.Add("0");
            }
            map.Insert(0, map_col);
        }
        // the code above is generate col, here to transpose
        map.Reverse();
        // record
        block_info.block_offset = block_offsets;
        block_info.map = map;
        block_info.block_type = "plain";
        block_info.bound_left = new TilePos();
        block_info.bound_left.ID = "0";
        block_info.bound_left.pos = target_left_pos;
        block_info.bound_right = new TilePos();
        block_info.bound_right.ID = "0";
        block_info.bound_right.pos = target_right_pos;
        return block_info;
    }
``````c
    public struct BlockInfo{
        public string block_type;
        public Vector3Int block_offset;
        public TilePos bound_up;
        public TilePos bound_down;
        public TilePos bound_left;
        public TilePos bound_right;
        public List<List<string>> map;
    }
    
    int constraint_target_h(int x, int y, int a_x, int a_y, int a_perlin, int b_x, int b_y, int b_perlin){
        float diff_a = a_y - a_perlin;
        float diff_b = b_y - b_perlin;
        float distance = b_x - a_x;
        float transition_h = (diff_b - diff_a) / distance * (x - a_x) + diff_a;
        int result = y + Mathf.CeilToInt(transition_h);
        return result;
    }
    
    int constraint_min_h(int x, int y, int a_x, int b_x, int min_h){
        int min_distance = ((b_x-x) < (x-a_x)) ? b_x-x : x-a_x;
        // Above minimum height
        if (y >= min_h)
            return y;
        // Constraint function for min_h with slope 1, i.e., when min_distance > min_h, directly corrected to min_h
        y = (min_distance < min_h) ? min_distance : min_h;
        return y;
    }
    
    int constraint_max_h(int x, int y, int a_x, int b_x, int max_h, int topBound_h){
        int min_distance = ((b_x-x) < (x-a_x)) ? b_x-x : x-a_x;
        // Below maximum height
        if (y <= max_h)
            return y;
        // Constraint function for max_h with slope 1, i.e., when max_distance > bound_h, directly corrected to max_h
        y = (min_distance < topBound_h) ? max_h + topBound_h - min_distance : max_h;
        return y;
    }
重构-2023/08/29
  • 我把tilemap相关的拆分成了几个文件,以及本节中的代码也以更通用的形式重构了,方便后续的其它方式,下面放一下核心代码。
  • 下面是地图生成的代码,在我目前的设想中,只用修改"custom init"的部分即可。
    BlockInfo _generate_1DTilemap_plain(Vector3Int block_offsets, float scale, _BlockAround block_around){
        // ---------- custom init ----------
        Vector3Int BSize = _game_configs.__block_size__;    // for convenience
        string block_type = "plain";
        int min_h = Mathf.CeilToInt(BSize.y * 0.1f);        // min_h also means boundBottom_h or boundTop_h
        int max_h = BSize.y - min_h;
        Vector3Int target_left_pos, target_right_pos;       // for record
    
        // get boundary target point
        if (block_around.left.type != "empty"){
            target_left_pos = new Vector3Int(0, block_around.left.bounds.right.pos.y);
        } else {
            target_left_pos = new Vector3Int(0, Random.Range(min_h, max_h));
        }
        if (block_around.right.type != "empty"){
            target_right_pos = new Vector3Int(BSize.x - 1, block_around.right.bounds.left.pos.y);
        } else {
            target_right_pos = new Vector3Int(BSize.x - 1, Random.Range(min_h, max_h));
        }
    
        BoundTiles bounds = new() {
            up = new(),
            down = new(),
            left = new(){ID="0", pos=target_left_pos},
            right = new(){ID="0", pos=target_right_pos}
        };
    
        // ---------- init ----------
        int perlin;
        List<List<string>> map = new();  // for record
        _HeightGenerator height_limiter = new(scale, min_h, max_h);
        height_limiter.Add(target_left_pos);
        height_limiter.Add(new(10, 10));
        height_limiter.Add(new(20, 20));
        height_limiter.Add(new(30, 30));
        height_limiter.Add(new(40, 40));
        height_limiter.Add(target_right_pos);
    
        // ---------- fill map ----------
        for (int x = 0; x < BSize.x; x++){
            perlin = height_limiter.get_height(x);
            // fill
            List<string> map_col = new();
            for (int y = 0; y < BSize.y; y++){
                if (y < perlin)
                    map_col.Add("1");
                else
                    map_col.Add("0");
            }
            map.Insert(0, map_col);
        }
        // the code above is generate col, here to transpose
        map.Reverse();
    
        // ---------- record ----------
        BlockInfo block_info = new() {
            type = block_type,
            offsets = block_offsets,
            map = map,
            bounds = bounds
        };
        return block_info;
    }
  • 下面是整合的柏林高度生成器。
    class _HeightGenerator{
        struct _Tile{
            public int x;       // target x
            public int y;       // target y
            public int perlin;  // perlin result which need to transit to target y
        }
        List<_Tile> _tiles = new();
        float _scale;
        int _min_h, _max_h;
        int _base_x = Random.Range(0, 1000000);  // for generating with difference each times
    
        public _HeightGenerator(float scale, int min_h, int max_h){
            _scale = scale;
            _min_h = min_h;
            _max_h = max_h;
        }
    
        public void Add(Vector3Int tile_pos){
            int perlin = _get_perlin(tile_pos.x);
            _Tile tile = new(){x=tile_pos.x, y=tile_pos.y, perlin=perlin};
            _tiles.Add(tile);
        }
    
        public int get_height(int x){
            _Tile tile_left = new();
            _Tile tile_right = new();
            for (int i=1; i<_tiles.Count; i++){
                tile_right = _tiles[i];
                if (tile_right.x >= x ){
                    tile_left = _tiles[i-1];
                    break;
                }
            }
            int perlin = _get_height(tile_left, tile_right, x);
            return perlin;
        }
    
        int _get_perlin(int x){
            int perlin = Mathf.CeilToInt((_max_h - _min_h) * Mathf.PerlinNoise((x + _base_x)/_scale, 0f)) + _min_h;
            return perlin;
        }
    
        int _get_height(_Tile left, _Tile right, int x){
            int perlin = _get_perlin(x);
            perlin = _limit_target_h(x, perlin, left.x, left.y, left.perlin, right.x, right.y, right.perlin);
            perlin = _limit_min_h(x, perlin, left.x, right.x);
            perlin = _limit_max_h(x, perlin, left.x, right.x);
            return perlin;
        }
    
        // ---------- limit format ----------
        int _limit_target_h(int x, int y, int a_x, int a_y, int a_perlin, int b_x, int b_y, int b_perlin){
            float diff_a = a_y - a_perlin;
            float diff_b = b_y - b_perlin;
            float distance = b_x - a_x;
            float transition_h = (diff_b - diff_a) / distance * (x - a_x) + diff_a;
            int result = y + Mathf.CeilToInt(transition_h);
            return result;
        }
    
        int _limit_min_h(int x, int y, int a_x, int b_x){
            int min_distance = ((b_x-x) < (x-a_x)) ? b_x-x : x-a_x;
            // Above minimum height
            if (y >= _min_h)
                return y;
            // limit function for min_h with slope 1, i.e., when min_distance > min_h, directly corrected to min_h
            y = (min_distance < _min_h) ? min_distance : _min_h;
            return y;
        }
    
        int _limit_max_h(int x, int y, int a_x, int b_x){
            int min_distance = ((b_x-x) < (x-a_x)) ? b_x-x : x-a_x;
            // Below maximum height
            if (y <= _max_h)
                return y;
            // limit function for max_h with slope 1, i.e., when max_distance > bound_h, directly corrected to max_h
            y = (min_distance < _min_h) ? _max_h + _min_h - min_distance : _max_h;
            return y;
        }
    }
指定多个点生成-2023/08/29
  • 即在上面左右走向的基础上,在中心加了多个点,多个点间是连续的。
  • 重构的代码展示的是多个点的,中间"init"的几行height_limiter.Add()就是添加点的地方。
  • 结果如下图,可以看到,在持续生成的图像中,有四个点的位置明显不变。

Unity记录5.1-地图-定点生成左右走向地图_第2张图片

你可能感兴趣的:(Unity,unity,游戏引擎)