游戏服务端AOI-九宫格法解析(附代码)

1-啥是AOI

AOI全称Area Of Interest,中文就是感兴趣的区域,个人理解就是玩家关注的并且可视的地图区域。

在RPG游戏中,玩家角色移动,攻击,放技能等操作都需要向其他玩家广播,但服务端由于性能原因,会避免往整张地图上的角色进行广播。为了把性能消耗降到最低,同时不影响玩家的游戏体验,就只会向玩家关注并感兴趣的区域,也就是AOI进行广播

2-AOI-网格法(九宫格)

1# 建立座标系

我们(0,0)座标为地址原点,再以16*8为一个格子,编号1,2…7,8共64个格子,建立座标系与一张16:8的地图。如下图:
在这里插入图片描述
假设玩家处在编号为20的格子的座标集中,那玩家的AOI(九宫格),就认为是玩家所在的格子和周围的8个格子,也就是上图的黄色部分

%% 获得当前座标所在格子的编号
get_cell(X, Y) ->
    Y div 8 * 8 + X div 16 + 1.

2# 广播原则(以玩家移动为例子)

  1. 当前座标和目标座标在同一格子,向以当前格子为中心的九宫格中的玩家发送"移动"广播。
  2. 当前座标和目标座标不在同一格子,设"以当前格子为中心的九宫格"为Old,“以目标格子为中心的九宫格"为New,向New和Old交集(New U Old)的区域的玩家广播"移动",向Old和New差集(Old - New)区域中的玩家广播"离开”,向New和Old差集(New - Old)区域中的玩家广播"进入"。下面用图举例三种情况:

在这里插入图片描述
假设玩家从"格子19的座标"移动到"格子20的座标",也就是九宫格从黄底区域变成了红字区域
因此:
在这里插入图片描述
向绿色区域的玩家发送"离开"广播,向蓝色区域的玩家发送"移动"广播,向黄色区域玩家发送"进入"广播

3-代码说明(erlang)

%% 获得当前座标所在格子的编号
get_cell(X, Y) ->
    Y div 8 * 8 + X div 16 + 1.

%% 执行判断
do_bool([]) -> ok;
do_bool([{Bool, Fun}|L]) ->
    case Bool of
        true -> Fun;
        false -> do_bool([{Bool, Fun}|L])
    end.

move_broadcast([X1,Y1], [X2,Y2]) ->
    OldCell = get_cell(X1, Y1),
    NewCell = get_cell(X2, Y2),
    PlayerList = get_all_player_in_map(),
    %% 1-先判断玩家原座标所在格子编号与目标格子所在的格子编号的位置关系
    %% 2-遍历PlayerList中Player的x和y,并定位其所在的格子编号。
    %% 3-判断其他Player的格子编号与玩家目标格子编号或原格子编号的位置关系,根据判断发送"进入(enter)"or"移动(move)"or"离开(leave)"三种广播
    if
        %% 1-在原格子内进行移动,向以原格子为中心的九宫格广播move信息
        OldCell == NewCell ->
            lists:foreach(fun(#player{x = TmpX, y = TmpY} = Player) ->
                TmpCell = get_cell(TmpX, TmpY),
                case lists:member(TmpCell, [NewCell,NewCell+1,NewCell-1,NewCell+8,NewCell-8,NewCell+7,NewCell-7,NewCell+9,NewCell-9]) of
                    true ->
                        send_msg(move, Player);
                    false ->
                        ok
                end
                          end, PlayerList);
        %% 2-向正右方的格子移动
        NewCell == OldCell+1 ->
            lists:foreach(fun(#player{x = TmpX, y = TmpY} = Player) ->
                TmpCell = get_cell(TmpX, TmpY),
                do_bool([
                    %% 向玩家新进入的九宫格区域的玩家发送enter广播
                    {lists:member(TmpCell, [NewCell+1,NewCell-7,NewCell+9]), send_msg(enter, Player)},
                    %% 向玩家离开的九宫格区域的玩家发送leave广播
                    {lists:member(TmpCell, [OldCell+1,OldCell-9,OldCell+7]), send_msg(leave, Player)},
                    %% 向新旧两个九宫格的公共区域玩家发送move广播
                    {lists:member(TmpCell, [OldCell,OldCell+1,OldCell-8,OldCell+9,OldCell-7,OldCell+9]), send_msg(move, Player)}
                ]) end, PlayerList);

        %% todo 此处省略 3-向左方格子移动

        %% 4-向正上格子移动
        NewCell == OldCell+1 ->
            lists:foreach(fun(#player{x = TmpX, y = TmpY} = Player) ->
                TmpCell = get_cell(TmpX, TmpY),
                do_bool([
                    %% 向玩家新进入的九宫格区域的玩家发送enter广播
                    {lists:member(TmpCell, [NewCell-7,NewCell-8,NewCell-9]), send_msg(enter, Player)},
                    %% 向玩家离开的九宫格区域的玩家发送leave广播
                    {lists:member(TmpCell, [OldCell+7,OldCell-8,OldCell+9]), send_msg(leave, Player)},
                    %% 向新旧两个九宫格的公共区域玩家发送move广播
                    {lists:member(TmpCell, [OldCell,OldCell+1,OldCell-1,OldCell-7,OldCell-8,OldCell-9]), send_msg(move, Player)}
                ]) end, PlayerList);

        %% todo 此处省略 5-向下方格子移动

        %% 6-向右下方的格子移动
        NewCell == OldCell+9 ->
            lists:foreach(fun(#player{x = TmpX, y = TmpY} = Player) ->
                TmpCell = get_cell(TmpX, TmpY),
                do_bool([
                    %% 向玩家新进入的九宫格区域的玩家发送enter广播
                    {lists:member(TmpCell, [NewCell+1,NewCell+7,NewCell+8,NewCell+9,NewCell-7]), send_msg(enter, Player)},
                    %% 向玩家离开的九宫格区域的玩家发送leave广播
                    {lists:member(TmpCell, [OldCell-1,OldCell-7,OldCell-8,OldCell-9,OldCell+7]), send_msg(leave, Player)},
                    %% 向新旧两个九宫格的公共区域玩家发送move广播
                    {lists:member(TmpCell, [OldCell,OldCell+1,OldCell+8,OldCell+9]), send_msg(move, Player)}
                ]) end, PlayerList);

        %% todo 此处省略 7-向右上 8-向左上 9-向左下
        true -> ok
    end.
        
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章