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

汇总: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_016_plain.gif

  • 下面分别是生成代码以及辅助代码。
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;
}
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_017_plain_multiple.gif

版权声明:
作者:MWHLS
链接:https://panwj.top/4844.html
来源:无镣之涯
文章版权归作者所有,未经允许请勿转载。

THE END
分享
二维码
打赏
< <上一篇
下一篇>>
文章目录
关闭
目 录