文章首发见博客: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()
就是添加点的地方。 - 结果如下图,可以看到,在持续生成的图像中,有四个点的位置明显不变。