遊戲服務端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.
        
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章