1. 先说下环境:
a.我们是slg大地图寻路,地图尺寸是10001000.
b.2D地图,没有高度的概念.
c.忽略人物模型大小,默认只要路径联通即能通过.
d.只做静态障碍的寻路(比如资源点,山脉河流等),不做动态障碍的寻路(玩家主城,怪物等)
2. 在说下大致思路:(请先看图)
a.将地图切分成5050等大小的400个大格子(绿色的方格子,黑色的线条是山脉,河流等等障碍物)
b.每个大格子根据情况,配置可以通过的路点(红色的空心点),预先计算好路点之间的距离.
注意,不需要计算每个路点到其他所有路点之间的距离,值需要计算他附近几个连同的路点即可(主要是考虑到内存问题,如果忽略内存问题,此种方式可以大大提高寻路效率).
服务器端只负责寻找出经过了那些路点,至于路点之间怎么走,是客户端的配置文件规定的.
c. 假设需要从A点到F点,那么需要计算A点L1路点之间的具体路径,这部分使用A*寻路,
路点L1到L7之间,也是用A*寻路,这部分就只是寻路大格子.
L7到F之间,也使用A*寻路即可.
总结一下,最坏的情况下采用三段寻路,两种A*,首位段采用具体坐标点的A*寻路,中间段采用A*大格子路点寻路.
其他情况都是这种情况的子集,比如C到D,就是只有首段寻路,C到E是首位两段寻路.
3.上代码了
area_search.erl模块
%%%===================模块描述(开始)=====================
%% 寻路
%%%===================模块描述(结束)=====================
-module(area_search).
%%%=======================STATEMENT====================
-time("2018/8/24 10:45").
%%%=======================EXPORT=======================
-export([init_obstacles/0, search/4, a_star/4]).
%%%=======================INCLUDE======================
-include("../include/area_search.hrl").
%%%=======================RECORD=======================
%%%=======================DEFINE=======================
-define(PATH_LAYER, path_layer).
%%%=================EXPORTED FUNCTIONS=================
%% ----------------------------------------------------
%% 初始化各个区块的障碍点
%% ----------------------------------------------------
init_obstacles() ->
{BMinX, BMaxX, BMinY, BMAxY} = map_lib:map_border(0),%获取地图的边界值:{X最小值,X最大值,Y最小值,Y最大值}
area_search_lib:load_obstacles(BMinY div ?AREA_SIZE, BMinX div ?AREA_SIZE, BMaxX div ?AREA_SIZE, BMinY div ?AREA_SIZE, BMAxY div ?AREA_SIZE),
area_search_lib:load_connect_mesh(),
area_search_lib:load_mesh().
%% ----------------------------------------------------
%% 寻路
%% ----------------------------------------------------
search(FromX, FromY, TargetX, TargetY) ->
{StartPath1, MidPath1, EndPath1} = case area_search_lib:check_layer(FromX, FromY, TargetX, TargetY) of
0 ->%同区块,只计算当前区块的寻路
{a_star(FromX, FromY, TargetX, TargetY), [], []};
{{EnterX, EnterY}, {ExitX, ExitY}} ->%私有连接点
{_, Mesh} = zm_config:get(?CFG_AREA_SEARCH, mesh),
MidPath = area_mesh:search(Mesh, {EnterX, EnterY}, {ExitX, ExitY}),%中间段的路径
{a_star(FromX, FromY, EnterX, EnterY), MidPath, a_star(ExitX, ExitY, TargetX, TargetY)};
{EnterX, EnterY} when is_integer(EnterX) ->%通用连接点
{a_star(FromX, FromY, EnterX, EnterY), [], a_star(EnterX, EnterY, TargetX, TargetY)}
end,
area_search_lib:init_path({FromX, FromY}, {TargetX, TargetY}, StartPath1, MidPath1, EndPath1).
%%%===================LOCAL FUNCTIONS==================
%% ----------------------------------------------------
%% 点位A*寻路
%% ----------------------------------------------------
a_star(FromX, FromY, TargetX, TargetY) ->
erase(),%%清除该线程的数据字段数据
case area_search_lib:near_points(FromX, FromY) of
[] ->
"no_path";
Subs ->
[begin
H = area_search_lib:get_h(PointX, PointY, TargetX, TargetY),
F = H + Weight,
Point = area_search_lib:init_point(PointX, PointY, FromX, FromY, F, H),
area_search_lib:put_open({PointX, PointY}, F, Point)
end || {{PointX, PointY}, Weight} <- Subs],
put({FromX, FromY}, area_search_lib:init_point(FromX, FromY, -1, -1)),
[{FromX, FromY} | a_star_(TargetX, TargetY)]
end.
a_star_(TargetX, TargetY) ->
case area_search_lib:get_opens() of
[] ->
"point_not_connect";
Opens ->
FPoint = get({element(1, hd(Opens)), ?KEY_OPEN}),
{FX, FY} = FPoint#point.point,
case area_search_lib:check(TargetX, TargetY, FPoint, area_search_lib:near_points(FX, FY)) of
{'end', Point} ->
area_search_lib:back(Point, [{TargetX, TargetY}]);
'null' ->
case length(area_search_lib:get_opens()) > 0 of
true ->
put({FX, FY}, FPoint),
area_search_lib:del_open({FX, FY}),
a_star_(TargetX, TargetY);
_ ->
area_search_lib:back(FPoint, [{TargetX, TargetY}])
end
end
end.
area_mesh.erl模块
```java
%%%===================模块描述(开始)=====================
%% 网格寻路
%%%===================模块描述(结束)=====================
-module(area_mesh).
%%%=======================STATEMENT====================
-copyright('youkia,www.youkia.net').
-author('lb,lb@youkia.net').
-time("2020/3/25 16:47").
%%%=======================EXPORT=======================
-export([new/1, insert_vertex/3, add_edge/4, search/3, get_vertex/3]).
%%%=======================INCLUDE======================
-include("../include/area_search.hrl").
%%%=======================RECORD=======================
%%%=======================DEFINE=======================
%%%=================EXPORTED FUNCTIONS=================
%% ----------------------------------------------------
%% 新建网格
%% ----------------------------------------------------
new(Type) ->
ets:new(?MODULE, [Type, {read_concurrency, true}]).
%% ----------------------------------------------------
%% 更新顶点
%% ----------------------------------------------------
insert_vertex(Ets, Vertex, V) ->
ets:insert(Ets, {Vertex, V}).
%% ----------------------------------------------------
%% 获取顶点的值
%% ----------------------------------------------------
get_vertex(Ets, Vertex, Default) ->
case ets:lookup(Ets, Vertex) of
[] ->
Default;
[{_, V}] ->
V
end.
%% ----------------------------------------------------
%% 新增边
%% ----------------------------------------------------
add_edge(Ets, V1, V2, Weight) ->
case ets:lookup(Ets, V1) of
[] ->
ets:insert(Ets, {V1, [{V2, Weight}]});
[{_, L}] ->
ets:insert(Ets, {V1, [{V2, Weight} | lists:keydelete(V2, 1, L)]})
end.
%% ----------------------------------------------------
%% 网格寻路
%% ----------------------------------------------------
search(Ets, {FromX, FromY}, {TargetX, TargetY}) ->
erase(),%%清除该线程的数据字段数据
case area_search_lib:connect_point(Ets, {FromX, FromY}) of
[] ->
"mesh_no_path";
Subs ->
[begin
H = area_search_lib:get_h(PointX, PointY, TargetX, TargetY),
F = H + Weight,
area_search_lib:put_open({PointX, PointY}, F, area_search_lib:init_point(PointX, PointY, FromX, FromY, F, H))
end || {{PointX, PointY}, Weight} <- Subs],
put({FromX, FromY}, area_search_lib:init_point(FromX, FromY, -1, -1)),
[{FromX, FromY} | search_(Ets, TargetX, TargetY)]
end.
%%%===================LOCAL FUNCTIONS==================
%% ----------------------------------------------------
%% 最短路径
%% ----------------------------------------------------
search_(Ets, TargetX, TargetY) ->
FPoint = get({element(1, hd(area_search_lib:get_opens())), ?KEY_OPEN}),
{FX, FY} = FPoint#point.point,
case area_search_lib:check(TargetX, TargetY, FPoint, area_search_lib:connect_point(Ets, {FX, FY})) of
{'end', Point} ->
area_search_lib:back(Point, [{TargetX, TargetY}]);
'null' ->
case length(area_search_lib:get_opens()) > 0 of
true ->
put({FX, FY}, FPoint),
area_search_lib:del_open({FX, FY}),
search_(Ets, TargetX, TargetY);
_ ->
'no_path_connect'
end
end.
area_search_lib.erl模块
%%%===================模块描述(开始)=====================
%% 区域寻路辅助方法
%%%===================模块描述(结束)=====================
-module(area_search_lib).
%%%=======================STATEMENT====================
-copyright('youkia,www.youkia.net').
-author('lb,lb@youkia.net').
-time("2019/8/1 16:34").
%%%=======================EXPORT=======================
-export([near_points/2, init_point/4, init_point/6, get_h/4, check_layer/4, distance/4, distance/2, init_path/5]).
-export([load_connect_mesh/0, load_mesh/0, load_obstacles/5, ets_name/2, get_layer/2]).
-export([put_open/3, get_opens/0, del_open/1, check/4, back/2, connect_point/2, xy_to_point/2]).
%%%=======================INCLUDE======================
-include("../include/area_search.hrl").
%%%=======================RECORD=======================
%%%=======================DEFINE=======================
%%%=================EXPORTED FUNCTIONS=================
%% ----------------------------------------------------
%% 加载每个区块的障碍点
%% ----------------------------------------------------
load_obstacles(StartY, X, MaxX, Y, MaxY) when Y >= MaxY ->
if
X >= MaxX ->
Fun = fun
({Point, _}) ->
PX = Point bsr 16,
PY = Point band ((1 bsl 16) - 1),
ets:insert(area_search_lib:ets_name(PX, PY), {{PX, PY}, 1})
end,
lists:foreach(Fun, zm_config:get(map_path_obstacle_0));
true ->
load_obstacles(StartY, X + 1, MaxX, StartY, MaxY)
end;
load_obstacles(StartY, X, MaxX, Y, MaxY) ->
Ets = ets:new(ets_name(X * ?AREA_SIZE, Y * ?AREA_SIZE), [named_table, {read_concurrency, true}, public]),
ets:insert(Ets, {?BORDER, {X * ?AREA_SIZE, (X + 1) * ?AREA_SIZE, Y * ?AREA_SIZE, (Y + 1) * ?AREA_SIZE}}),%写入区块的边界值
load_obstacles(StartY, X, MaxX, Y + 1, MaxY).
%% ----------------------------------------------------
%% 加载连通图
%% ----------------------------------------------------
load_connect_mesh() ->
%%连通矩阵配置文件
case file:open(?FILE_ARRAY, [read]) of
{ok, IO} ->
Fun = fun
({Y, IO1}) ->
case file:read_line(IO1) of
{ok, Line} ->
zm_config:set(?CFG_AREA_SEARCH, {{Y, connect}, list_to_tuple([list_to_integer(S) || S <- string:tokens(hd(string:tokens(Line, "\n")), ",")])}),
{Y + 1, IO1};
Other ->
{break, Other}
end
end,
z_lib:while(Fun, {0, IO});
Error ->
z_log:warn(?MODULE, ?FUNCTION_NAME, "_read_file_error", [Error])
end,
%%连通类型对应的连同点
case file:open(?FILE_ENTER, [read]) of
{ok, EnterIO} ->
Fun2 = fun
(EnterIO1) ->
case file:read_line(EnterIO1) of
{ok, Line} ->
[StrType, StrEnter] = string:tokens(Line, ":"),
zm_config:set(?CFG_AREA_SEARCH, {{list_to_integer(StrType), ?ENTER},
lists:sort([list_to_integer(S) || S <- string:tokens(hd(string:tokens(StrEnter, "\n")), ",")])}),
EnterIO1;
Other ->
{break, Other}
end
end,
z_lib:while(Fun2, EnterIO);
Error2 ->
z_log:warn(?MODULE, ?FUNCTION_NAME, "enter_read_file_error", [Error2])
end.
%% ----------------------------------------------------
%% 加载路点网格
%% ----------------------------------------------------
load_mesh() ->
Mesh = area_mesh:new(protected),
Fun2 = fun
({Point, Weight}) ->
Band = (1 bsl 16) - 1,
X1 = Point bsr (16 * 3),
Y1 = (Point bsr (16 * 2)) band Band,
X2 = (Point bsr (16 * 1)) band Band,
Y2 = Point band Band,
Ets = area_search_lib:ets_name(X1, Y1),
area_mesh:add_edge(Mesh, {X1, Y1}, {X2, Y2}, Weight),
area_mesh:add_edge(Mesh, {X2, Y2}, {X1, Y1}, Weight),
[begin
case ets:lookup(Ets, ?ENTER) of
[] ->
ets:insert(Ets, {?ENTER, [{X, Y}]});
[{_, L}] ->
ets:insert(Ets, {?ENTER, [{X, Y} | lists:delete({X, Y}, L)]})
end
end || {X, Y} <- [{X1, Y1}, {X2, Y2}]]
end,
lists:foreach(Fun2, zm_config:get(map_path_0)),
zm_config:set(?CFG_AREA_SEARCH, {mesh, Mesh}).%找个地方缓存网格
%% ----------------------------------------------------
%% 初始化路径
%% ----------------------------------------------------
init_path({FromX, FromY}, {ToX, ToY}, StartPath, MidPath, EndPath) ->
{BinStart, D1} = encode_points(StartPath),
{BinEnd, D2} = encode_points(EndPath),
MidPath1 = [xy_to_point(X, Y) || {X, Y} <- MidPath],
pro_map:init_path(xy_to_point(FromX, FromY), xy_to_point(ToX, ToY), BinStart, BinEnd, MidPath1, D1 + D2).
%% ----------------------------------------------------
%% xy转坐标点
%% ----------------------------------------------------
xy_to_point(X, Y) ->
(X bsl 16) bor Y.
%% ----------------------------------------------------
%% 路径回溯
%% ----------------------------------------------------
back(Point, Result) ->
if
Point#point.parent =:= {-1, -1} ->
Result;
true ->
Parent = get(Point#point.parent),
back(Parent, [Point#point.point | Result])
end.
%% ----------------------------------------------------
%% 寻路检查
%% ----------------------------------------------------
check(_, _, _, []) ->
'null';
check(TargetX, TargetY, Parent, [{{TargetX, TargetY}, _} | _]) ->
{'end', Parent};
check(TargetX, TargetY, Parent, [{{X, Y}, Weight} | SubPoints]) ->
case get({X, Y}) of
undefined ->
{ParentX, ParentY} = Parent#point.point,
case get({{X, Y}, ?KEY_OPEN}) of
undefined ->%%不在,计算GHF,存入开启列表中
H = area_search_lib:get_h(X, Y, TargetX, TargetY),
if
H == 0 ->
{'end', Parent};
true ->%存入open列表中
F = Parent#point.f + Weight + H,
put_open({X, Y}, F, area_search_lib:init_point(X, Y, ParentX, ParentY, F, H)),
check(TargetX, TargetY, Parent, SubPoints)
end;
OldPoint ->%%在开启列表中,检查以当前节点为父节点,F是否更优
H = area_search_lib:get_h(X, Y, TargetX, TargetY),
if
H == 0 ->
{'end', Parent};
H < OldPoint#point.h ->
F = H + Parent#point.f + Weight,
put_open({X, Y}, F, OldPoint#point{f = F, h = H, parent = {ParentX, ParentY}}),
check(TargetX, TargetY, Parent, SubPoints);
true ->
check(TargetX, TargetY, Parent, SubPoints)
end
end;
_ ->%%关闭列表中忽略
check(TargetX, TargetY, Parent, SubPoints)
end.
%% ----------------------------------------------------
%%获取连接的点
%%==>[{point,weight},...]
%% ----------------------------------------------------
connect_point(Ets, Point) ->
case ets:lookup(Ets, Point) of
[] ->
[];
[{_, L}] ->
L
end.
%% ----------------------------------------------------
%%保存开放列表
%% ----------------------------------------------------
put_open(XY, F, Point) ->
case put({XY, ?KEY_OPEN}, Point) of
undefined ->
put(?KEY_OPEN_LIST, lists:keysort(2, [{XY, F} | get_opens()]));
_ ->
put(?KEY_OPEN_LIST, lists:keysort(2, [{XY, F} | lists:keydelete(XY, 1, get_opens())]))
end.
%% ----------------------------------------------------
%%获取开放列表(F值排序)
%% ----------------------------------------------------
get_opens() ->
case get(?KEY_OPEN_LIST) of
undefined ->
[];
L ->
L
end.
%% ----------------------------------------------------
%%从开放列表中删除坐标
%% ----------------------------------------------------
del_open(XY) ->
case erase({XY, ?KEY_OPEN}) of
undefined ->
ok;
_ ->
put(?KEY_OPEN_LIST, lists:keydelete(XY, 1, get_opens()))
end.
%% ----------------------------------------------------
%% 障碍ets表
%% ----------------------------------------------------
ets_name(X, Y) ->
list_to_atom(lists:concat(["path_obstacles_", X div ?AREA_SIZE, "_", Y div ?AREA_SIZE])).
%% ----------------------------------------------------
%% 连通层级检查
%% ==>0(直接A*)|{X,Y}(通用连同点)|{{X,Y},{X,Y}}(私有连同点)
%% ----------------------------------------------------
check_layer(FromX, FromY, ToX, ToY) ->
FromLayer = get_layer(FromX, FromY),
ToLayer = get_layer(ToX, ToY),
if
FromLayer andalso ToLayer ->%连通层级相同,直接A*寻路
0;
true ->%连通层级不同,大格子寻路
{_, FromEnters} = zm_config:get(?CFG_AREA_SEARCH, {FromLayer, ?ENTER}),
{_, ToEnters} = zm_config:get(?CFG_AREA_SEARCH, {ToLayer, ?ENTER}),
select_enter({FromX, FromY}, {ToX, ToY}, [point_to_xy(Point) || Point <- FromEnters], [point_to_xy(Point) || Point <- ToEnters])
end.
%% ----------------------------------------------------
%% 计算h值
%% ----------------------------------------------------
get_h(X, Y, X, TargetY) ->%同一X坐标
abs(Y - TargetY);
get_h(X, Y, TargetX, Y) ->%同一Y坐标
abs(X - TargetX);
get_h(X, Y, TargetX, TargetY) ->%勾股定理算H值
distance(X, Y, TargetX, TargetY).
%% ----------------------------------------------------
%% 获得一个节点的所有可用子节点及其权重
%% ==>[{{X,Y},Weight},....]
%% ----------------------------------------------------
near_points(X, Y) ->
Ets = area_search_lib:ets_name(X, Y),
[{_, {XMin, XMax, YMin, YMax}}] = ets:lookup(Ets, ?BORDER),
%得出当前坐标点周围八个点的坐标以及权重
Points = [{X1 + X, Y1 + Y, W} || {X1, Y1, W} <- ?AROUND],
near_points_(Ets, Points, XMin, XMax, YMin, YMax, []).
near_points_(_, [], _, _, _, _, Result) ->
Result;
near_points_(Ets, [{X, Y, Weight} | Points], XMin, XMax, YMin, YMax, Result) ->
case ets:lookup(Ets, {X, Y}) of
[] ->%非障碍点
if
X < XMin orelse X >= XMax orelse Y < YMin orelse Y >= YMax ->
near_points_(Ets, Points, XMin, XMax, YMin, YMax, Result);
true ->
near_points_(Ets, Points, XMin, XMax, YMin, YMax, [{{X, Y}, Weight} | Result])
end;
_ ->
near_points_(Ets, Points, XMin, XMax, YMin, YMax, Result)
end.
%% ----------------------------------------------------
%% 初始化一个坐标点
%% ----------------------------------------------------
init_point(X, Y, ParentX, ParentY) ->
#point{point = {X, Y},
f = 0,
h = 0,
parent = {ParentX, ParentY}
}.
%% ----------------------------------------------------
%% 初始化一个坐标点
%% ----------------------------------------------------
init_point(X, Y, ParentX, ParentY, F, H) ->
#point{point = {X, Y},
f = F,
h = H,
parent = {ParentX, ParentY}
}.
%%%===================LOCAL FUNCTIONS==================
%% ----------------------------------------------------
%% 获得层级
%% ----------------------------------------------------
get_layer(X, Y) ->
{_, Array} = zm_config:get(?CFG_AREA_SEARCH, {Y, connect}),
element(X + 1, Array).
%% ----------------------------------------------------
%% 距离计算
%% ----------------------------------------------------
distance({SX, XY}, {EX, EY}) ->
distance(SX, XY, EX, EY).
distance(SX, XY, EX, EY) ->
XV = abs(SX - EX),
YV = abs(XY - EY),
trunc(math:sqrt(XV * XV + YV * YV) * 15).
%% ----------------------------------------------------
%% 获得两个邻接大格子的出入口
%% ----------------------------------------------------
select_enter(From, To, List1, List2) ->
case near_enter(From, To, List1, List2, [], [], []) of
{FromDistance, ToDistance, []} ->
{element(1, hd(lists:keysort(2, FromDistance))), element(1, hd(lists:keysort(2, ToDistance)))};
{_, _, CommonDistance} ->
element(1, hd(lists:keysort(2, CommonDistance)))
end.
%%获得两个邻接大格子的出入口
near_enter(_, _, [], [], FromDistance, ToDistance, CommonDistance) ->
{FromDistance, ToDistance, CommonDistance};
near_enter(From, To, [E | FromEnters], [E | ToEnter], FromDistance, ToDistance, CommonDistance) ->%有相同的入口
near_enter(From, To, FromEnters, ToEnter, FromDistance, ToDistance, [{E, area_search_lib:distance(From, E) + area_search_lib:distance(To, E)} | CommonDistance]);
near_enter(From, To, [FE | FromEnters], [TE | ToEnter], FromDistance, ToDistance, CommonDistance) ->%有相同的入口
if
FE < TE ->
near_enter(From, To, FromEnters, [TE | ToEnter], [{FE, area_search_lib:distance(From, FE) + area_search_lib:distance(To, FE)} | FromDistance],
ToDistance, CommonDistance);
true ->
near_enter(From, To, [FE | FromEnters], ToEnter, FromDistance,
[{TE, area_search_lib:distance(From, TE) + area_search_lib:distance(To, TE)} | ToDistance], CommonDistance)
end;
near_enter(From, To, [], Enter, FromDistance, ToDistance, CommonDistance) ->
R = [{E, area_search_lib:distance(From, E) + area_search_lib:distance(To, E)} || E <- Enter],
{FromDistance, R ++ ToDistance, CommonDistance};
near_enter(From, To, Enter, [], FromDistance, ToDistance, CommonDistance) ->
R = [{E, area_search_lib:distance(From, E) + area_search_lib:distance(To, E)} || E <- Enter],
{R ++ FromDistance, ToDistance, CommonDistance}.
%将点位压缩成二进制
encode_points([]) ->
{<<>>, 0};
encode_points([{X1, Y1} | Points]) ->
encode_points({X1, Y1}, Points).
encode_points({X, Y}, [{X1, Y1} | Points]) ->
{Direction, Weight} = direction(X1 - X, Y1 - Y),
encode_points({X1, Y1}, Points, Direction, 1, <<>>, Weight).
encode_points(_, [], Direction, Times, Bin, SumWeight) ->
{init_bin(Direction, Times, Bin), trunc(SumWeight)};
encode_points({X, Y}, [{X1, Y1} | Points], Direction, Times, Bin, SumWeight) ->
{Direction1, Weight} = direction(X1 - X, Y1 - Y),
if
Direction1 =:= Direction ->
encode_points({X1, Y1}, Points, Direction, Times + 1, Bin, SumWeight + Weight);
true ->
encode_points({X1, Y1}, Points, Direction1, 1, init_bin(Direction, Times, Bin), SumWeight + Weight)
end.
%%[{-1, -1, 14}, {0, -1, 10}, {1, -1, 14}, {-1, 0, 10}, {1, 0, 10}, {-1, 1, 14}, {0, 1, 10}, {1, 1, 14}]
direction(XR, YR) when XR =:= -1 andalso YR =:= -1 ->%左下方
{0, 14};
direction(XR, YR) when XR =:= 0 andalso YR =:= -1 ->%正下方
{1, 10};
direction(XR, YR) when XR =:= 1 andalso YR =:= -1 ->%右下方
{2, 14};
direction(XR, YR) when XR =:= -1 andalso YR =:= 0 ->%正左方
{3, 10};
direction(XR, YR) when XR =:= 1 andalso YR =:= 0 ->%正右方
{4, 10};
direction(XR, YR) when XR =:= -1 andalso YR =:= 1 ->%右上方
{5, 14};
direction(XR, YR) when XR =:= 0 andalso YR =:= 1 ->%正上方
{6, 10};
direction(XR, YR) when XR =:= 1 andalso YR =:= 1 ->%右上方
{7, 17}.
init_bin(D, T, Bin) ->
if
T > 31 ->
init_bin(D, T - 31, <<Bin/binary, <<31:5, D:3>>/binary>>);
true ->
<<Bin/binary, <<T:5, D:3>>/binary>>
end.
point_to_xy(Point) ->
{Point bsr 16,
Point band 65535}.