Packages

Behavior3 by erlang library

Current section

Files

Jump to
behavior3erl src core base_node.erl
Raw

src/core/base_node.erl

-module(base_node).
-compile([inline, {inline_size, 100}]).
%%--------------------------------------------------------------------
%% include
%%--------------------------------------------------------------------
-include("behavior3.hrl").
%%--------------------------------------------------------------------
%% export API
%%--------------------------------------------------------------------
-export([execute/2]).
%% Internal API
-export([do_init/4, do_open/2, do_tick/2, do_close/2]).
%% 节点初始化
-callback(init(bt_node()) -> bt_node()).
%% 开启节点
-callback(open(bt_node(), bt_state()) -> bt_state()).
%% 执行节点
-callback(tick(bt_node(), bt_state()) -> {bt_status(), bt_state()}).
%% 关闭节点
-callback(close(bt_node(), bt_state()) -> bt_state()).
-optional_callbacks([init/1, open/2, close/2]).
%%--------------------------------------------------------------------
%% API functions
%%--------------------------------------------------------------------
%% @doc 执行节点
-spec execute(bt_uid(), bt_state()) -> {bt_status(), bt_state()}.
execute(NodeID, BTState) ->
BTNode = blackboard:get_btree_node(NodeID),
BTState1 = do_open(BTNode, BTState),
{BTStatus, BTState2} = do_tick(BTNode, BTState1),
case BTStatus of
?BT_RUNNING ->
{?BT_RUNNING, BTState2};
BTStatus ->
BTState3 = do_close(BTNode, BTState2),
{BTStatus, BTState3}
end.
%%--------------------------------------------------------------------
%% Internal functions
%%--------------------------------------------------------------------
%% 初始化行为树
-spec do_init(bt_uid()|undefined, bt_node_id(), #{bt_node_id() => btree()}, tree_nodes()) -> {ok, bt_uid()}|{error, term()}.
do_init(ParentNodeID, BTNodeID, TreeMaps, TreeNodeMaps) ->
case behavior_tree:get_btree_node(BTNodeID, TreeNodeMaps) of
#{name := Name, category := tree} ->
#{root := Root} = behavior_tree:get_btree(Name, TreeMaps),
do_init(ParentNodeID, Root, TreeMaps, TreeNodeMaps);
#{name := Name, children := Children} = BTNode ->
case code:ensure_loaded(Name) of
{module, Mod} ->
case erlang:function_exported(Mod, init, 1) of
true ->
BTNode1 = Mod:init(BTNode);
false ->
BTNode1 = BTNode
end,
ID = make_ref(),
BTNode2 = BTNode1#{
id => ID,
bt_node_id => BTNodeID,
parent_id => ParentNodeID,
children => [do_init(ID, ChildBTNodeID, TreeMaps, TreeNodeMaps) || ChildBTNodeID <- Children]
},
blackboard:set_btree_node(BTNode2),
{ok, ID};
_ ->
{error, {btree_node_not_implement, Name}}
end
end.
%% 如果树节点没有开启,并且有open/2函数实现,则执行open函数,
%% 如果树节点已经开启,则跳过
-spec do_open(bt_node(), bt_state()) -> bt_state().
do_open(#{id := ID, parent_id := ParentID, name := Mod} = BTNode, BTState) ->
case blackboard:get('$is_open', ID, false, BTState) of
true ->
BTState;
false ->
case erlang:function_exported(Mod, open, 2) of
true ->
BTState1 = Mod:open(BTNode, BTState);
false ->
BTState1 = BTState
end,
BTState2 = blackboard:set('$is_open', true, ID, BTState1),
case is_reference(ParentID) of
true ->
Children = blackboard:get('$children', ParentID, [], BTState2),
blackboard:set('$children', [ID | Children], ParentID, BTState2);
false ->
BTState2
end
end.
%% 执行树节点tick函数
-spec do_tick(bt_node(), bt_state()) -> {bt_status(), bt_state()}.
do_tick(#{name := Mod} = BTNode, BTState) ->
Mod:tick(BTNode, BTState).
%% @doc 如果树节点有close/2函数实现,则执行close函数
-spec do_close(bt_node(), bt_state()) -> bt_state().
do_close(#{id := ID, name := Mod} = BTNode, BTState) ->
Children = blackboard:get('$children', ID, [], BTState),
BTState1 = do_close_1(Children, BTState),
case erlang:function_exported(Mod, close, 2) of
true ->
BTState2 = Mod:close(BTNode, BTState1);
false ->
BTState2 = BTState1
end,
BTState3 = blackboard:set('$is_open', false, ID, BTState2),
BTState4 = blackboard:set('$children', [], ID, BTState3),
blackboard:erase_node_local(ID, BTState4).
do_close_1([NodeID | T], BTState) ->
BTState1 = do_close_1(T, BTState),
case blackboard:get('$is_open', NodeID, false, BTState1) of
true ->
BTNode = blackboard:get_btree_node(NodeID),
do_close(BTNode, BTState1);
false ->
BTState1
end;
do_close_1([], BTState) ->
BTState.