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作为过渡。
c 复制代码
    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
  • 过渡区域蛮难写的,都翻出了好久不用的笔
  • 是左右走向的效果,左右两个边界点是随机的
    • 如果左右有其它块,那就是指定的边界点。
  • 限制了最低高度和最高高度,但现在记录的时候感觉好像都用不到,只是在画下面图的时候比较连续...
  • 效果如下,生成了两行,可以看到每行都是连续的。
  • 下面分别是生成代码以及辅助代码。
c 复制代码
    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"的部分即可。
c 复制代码
    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;
    }
  • 下面是整合的柏林高度生成器。
c# 复制代码
    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()就是添加点的地方。
  • 结果如下图,可以看到,在持续生成的图像中,有四个点的位置明显不变。
相关推荐
wonder135798 分钟前
UGUI重建流程和优化
unity·游戏开发·ugui
Doc.S3 小时前
多无人机任务自定义(基于ZJU-FAST-Lab / EGO-Planner-v2)
游戏引擎·无人机·cocos2d
那个村的李富贵4 小时前
Unity打包Webgl后 本地运行测试
unity·webgl
nnsix5 小时前
Unity OpenXR开发HTC Vive Cosmos
unity·游戏引擎
nnsix6 小时前
Unity OpenXR,扳机键交互UI时,必须按下扳机才触发
unity·游戏引擎
nnsix6 小时前
Unity XR 编辑器VR设备模拟功能
unity·编辑器·xr
老朱佩琪!6 小时前
Unity访问者模式
unity·游戏引擎·访问者模式
不定时总结的那啥6 小时前
Unity实现点击Console消息自动选中预制体的方法
unity·游戏引擎
nnsix7 小时前
Unity OpenXR 关闭手柄的震动
unity·游戏引擎
CreasyChan7 小时前
Unity 中的反射使用详解
unity·c#·游戏引擎·游戏开发