Erlang 行爲模式gen_fsm狀態機 解析和案例

有限狀態機這名詞聽起來好像很高大上,其實本質上是對象(actor)在不同狀態下收到信息有不同的行爲(處理方式)和狀態轉換,有點類似設計模式中的狀態模式。

以一個簡單的遊戲場景爲案例,在rpg遊戲地圖中常常會出現一些怪物,怪物站在地圖裏的初始狀態是遊蕩狀態,如果玩家出現在他的實現範圍內,那麼他的狀態就會變成追擊狀態,離開怪物視野後又變爲遊蕩狀態,當人物打死怪物就會變成死亡,類似這種其狀態會因爲觸發事件而導致的狀態轉換就有限狀態機。

代碼如下所示

%%%-------------------------------------------------------------------
%%% @author zzh
%%% @copyright (C) 2019, <COMPANY>
%%% @doc
%%%
%%% @end
%%% Created : 21. 十一月 2019 20:38
%%%-------------------------------------------------------------------
-module(monster_fsm).
-author("zzh").
-behaviour(gen_fsm).


%% gen_fsm callbacks
-export([init/1,handle_event/3,  handle_sync_event/4, handle_info/3, terminate/3, code_change/4,
    wander/2,follow/2]).



%% API
-export([create/1,player_join/1,player_leave/1,defeat_mon/1]).

-record(state, {
    monster_id = 0,
    status = 0  %狀態 0 遊蕩 1追着玩家錘 2死亡
}).


%%%===================================================================
%%% API

create(MonsterId) ->    %啓動怪物進程
    PidName = monster_process_name(MonsterId),
    gen_fsm:start_link({local, PidName}, ?MODULE, [MonsterId], []),
    PidName.


%% 玩家進入視野
player_join(PidName) ->
    gen_fsm:send_event(PidName ,player_join).

%% 玩家離開視野
player_leave(PidName) ->
    gen_fsm:send_event(PidName, player_leave).

%玩家打敗怪物
defeat_mon(PidName)->
    gen_fsm:send_event(PidName,die).
%%%===================================================================





%%%===================================================================
%%% CallBack function

init([MonsterId])->
    io:format("monster is created,Id:~p ~n",[MonsterId]),
    State = #state{monster_id = MonsterId},
    {ok,wander,State}.  %初始化後爲遊蕩狀態

%0遊蕩狀態
wander(Event, State) ->
    case Event of
        player_join -> %% 玩家進入視野進入視野
            fuck_player(), %% 捶玩家
            NewState = State#state{status = 1},
            {next_state, follow, NewState}; %進入下一個狀態
        _ -> %如果這個狀態下還有其他事件 do something
            {next_state, wander, State}
    end.

%1追擊狀態
follow(Event,State) ->
    case Event of
        player_leave -> %% 玩家離開視野
            do_wander(), %% 遊蕩mou
            NewState = State#state{status = 0},
            {next_state, wander, NewState}; %進入下一個狀態
        die -> %被錘死
            die(),
            NewState = State#state{status = 2},
            {stop,normal,NewState}
    end.



handle_event(_Event, StateName, State) ->
    {next_state, StateName, State}.

handle_sync_event(_Event, _From, StateName, State) ->
    Reply = ok,
    {reply, Reply, StateName, State}.

handle_info(_Info, StateName, State) ->
    {next_state, StateName, State}.

terminate(Reason, StateName, State) ->
    io:format("process stop,Reason:~p ,StateName: ~p,State ~w ~n",[Reason,StateName,State]),
    ok.

code_change(_OldVsn, StateName, State, _Extra) ->
    {ok, StateName, State}.
%%%===================================================================




%% 捶玩家
fuck_player()->
    io:format("Face the wind!hasaki! ~n").

do_wander() ->
    io:format("monster stop moving, join wander...~n").

die()->
    io:format("why kill me W_W...~n").

%%返回怪物進程名稱 MonsterId爲1時返回mod_monster_1
monster_process_name(MonsterId) ->
    list_to_atom(lists:concat([mod_monster_, MonsterId])).


運行截圖

看完你大概會覺得這用gen_server實現也沒啥問題啊,是的,用gen_server實現也是沒問題的,當你點擊gen_fsm看源碼時你會發現2者有很多相似,你可以看成gen_fsm是由gen_server封裝(雖然erlang沒有封裝這種說話),state作爲抽象代碼中模式匹配的回調。。。2者相比,在一些典型的狀態轉換場景下用gen_fsm可讀性更好

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章