瀏覽代碼

ft: 初始化提交

master
SisMaker 4 年之前
當前提交
658adce1a9
共有 55 個文件被更改,包括 3917 次插入0 次删除
  1. +29
    -0
      .gitignore
  2. +21
    -0
      LICENSE
  3. +78
    -0
      README.md
  4. +21
    -0
      examples/action/action_add_rage.erl
  5. +20
    -0
      examples/action/action_become_attacking.erl
  6. +20
    -0
      examples/action/action_become_idle.erl
  7. +20
    -0
      examples/action/action_become_patrolling.erl
  8. +20
    -0
      examples/action/action_become_recovering.erl
  9. +60
    -0
      examples/action/action_collect_dest_path.erl
  10. +45
    -0
      examples/action/action_collect_path.erl
  11. +26
    -0
      examples/action/action_collect_patrol.erl
  12. +22
    -0
      examples/action/action_cost_power.erl
  13. +21
    -0
      examples/action/action_cost_rage.erl
  14. +20
    -0
      examples/action/action_del_dead_target.erl
  15. +21
    -0
      examples/action/action_died.erl
  16. +21
    -0
      examples/action/action_finish.erl
  17. +21
    -0
      examples/action/action_move_grid.erl
  18. +22
    -0
      examples/action/action_recover_power.erl
  19. +44
    -0
      examples/action/action_skill_attack.erl
  20. +24
    -0
      examples/condition/cond_is_attacking.erl
  21. +23
    -0
      examples/condition/cond_is_dest.erl
  22. +23
    -0
      examples/condition/cond_is_died.erl
  23. +24
    -0
      examples/condition/cond_is_idle.erl
  24. +24
    -0
      examples/condition/cond_is_patrolling.erl
  25. +24
    -0
      examples/condition/cond_is_power_full.erl
  26. +24
    -0
      examples/condition/cond_is_rage_full.erl
  27. +24
    -0
      examples/condition/cond_is_recovering.erl
  28. +28
    -0
      examples/condition/cond_is_target_died.erl
  29. +24
    -0
      examples/condition/cond_power_lt.erl
  30. +32
    -0
      examples/example.hrl
  31. +1786
    -0
      examples/example.json
  32. +59
    -0
      examples/game_dict.erl
  33. +127
    -0
      examples/game_process.erl
  34. +64
    -0
      include/behavior3.hrl
  35. +15
    -0
      rebar.config
  36. +22
    -0
      src/action/error.erl
  37. +22
    -0
      src/action/failer.erl
  38. +22
    -0
      src/action/runner.erl
  39. +22
    -0
      src/action/succeeder.erl
  40. +36
    -0
      src/action/wait.erl
  41. +43
    -0
      src/composite/mem_priority.erl
  42. +43
    -0
      src/composite/mem_sequence.erl
  43. +31
    -0
      src/composite/priority.erl
  44. +31
    -0
      src/composite/sequence.erl
  45. +137
    -0
      src/core/base_node.erl
  46. +215
    -0
      src/core/behavior_tree.erl
  47. +157
    -0
      src/core/blackboard.erl
  48. +31
    -0
      src/decorator/inverter.erl
  49. +43
    -0
      src/decorator/limiter.erl
  50. +39
    -0
      src/decorator/max_time.erl
  51. +44
    -0
      src/decorator/repeat_until_failure.erl
  52. +45
    -0
      src/decorator/repeat_until_success.erl
  53. +44
    -0
      src/decorator/repeater.erl
  54. +10
    -0
      src/eBhv3.app.src
  55. +3
    -0
      src/eBhv3.erl

+ 29
- 0
.gitignore 查看文件

@ -0,0 +1,29 @@
.eunit
*.o
*.beam
*.plt
erl_crash.dump
.concrete/DEV_MODE
# rebar 2.x
.rebar
rel/example_project
ebin/*
deps
# rebar 3
.rebar3
_build/
_checkouts/
rebar.lock
# idea
.idea
*.iml
cmake-build*
CMakeLists.txt
# nif compile temp file
*.pdb
*.d
compile_commands.json

+ 21
- 0
LICENSE 查看文件

@ -0,0 +1,21 @@
The MIT License
Copyright (c) 2019-2020 alisdair sullivan <alisdairsullivan@yahoo.ca>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

+ 78
- 0
README.md 查看文件

@ -0,0 +1,78 @@
中文
=====
Behavior3的erlang支持库
快速开始
----
添加如下内容到**rebar.config**
{deps, [
...
{behavior3erl, "1.0.0"}
]}.
编译
----
$ rebar3 compile
使用
----
{TitleMaps, TreeMaps, TreeNodeMaps} = behavior_tree:load_tree_file("example.json"),
{ok, RootID} = behavior_tree:init_btree_by_title(<<"example_ai"/utf8>>, TitleMaps, TreeMaps, TreeNodeMaps),
{_BTStatus, _BTState1} = behavior_tree:execute(RootID, BTState = #{}).
更多
----
[behavior3editor](https://github.com/behavior3/behavior3editor)
[behavior3go](https://github.com/magicsea/behavior3go)
#### Behavior3使用系列文章:
[(一)行为树应用之行为树简介](http://note.youdao.com/s/77bGugj9)
[(二)行为树应用之组合节点](http://note.youdao.com/s/XiKlHPIr)
[(三)行为树应用之装饰节点](http://note.youdao.com/s/9Z6zI3YE)
[(四)行为树应用之自定义节点](http://note.youdao.com/s/AcRrY8ig)
[(五)行为树应用之加载行为树](http://note.youdao.com/s/DiqLf0ES)
[(六)行为树应用之节点执行](http://note.youdao.com/s/PI3Wic5D)
[(七)行为树应用之设计巡逻兵AI](http://note.youdao.com/s/HTCGTgAm)
[(八)行为树应用之设计丧尸AI](http://note.youdao.com/s/3wKFxcTw)
English
=====
Behavior3 by erlang library
Quickstart
----
add to **rebar.config**
{deps, [
...
{behavior3erl, "1.0.0"}
]}.
Build
----
$ rebar3 compile
Usage
----
{TitleMaps, TreeMaps, TreeNodeMaps} = behavior_tree:load_tree_file("example.json"),
{ok, RootID} = behavior_tree:init_btree_by_title(<<"example_ai"/utf8>>, TitleMaps, TreeMaps, TreeNodeMaps),
{_BTStatus, _BTState1} = behavior_tree:execute(RootID, BTState = #{}).
More
----
[behavior3editor](https://github.com/behavior3/behavior3editor)
[behavior3go](https://github.com/magicsea/behavior3go)

+ 21
- 0
examples/action/action_add_rage.erl 查看文件

@ -0,0 +1,21 @@
%%%-------------------------------------------------------------------
%%% @author DY
%%% @copyright (C) 2020, <COMPANY>
%%% @doc
%%%
%%% add_rage增加相应怒气100
%%% SUCCESS
%%% @end
%%% Created : 07. 10 2020 15:13
%%%-------------------------------------------------------------------
-module(action_add_rage).
-include("behavior3.hrl").
-include("example.hrl").
%% API
-export([tick/2]).
tick(#{properties := #{add_rage := AddRage}} = _BTree, #{cur_rage := CurRage} = State) ->
State1 = State#{cur_rage := min(CurRage + AddRage, ?MAX_RAGE)},
?INFO("~ts:怒气值增加~w点 总怒气~w", [maps:get(uid, State1), AddRage, maps:get(cur_rage, State1)]),
{?BT_SUCCESS, State1}.

+ 20
- 0
examples/action/action_become_attacking.erl 查看文件

@ -0,0 +1,20 @@
%%%-------------------------------------------------------------------
%%% @author DY
%%% @copyright (C) 2020, <COMPANY>
%%% @doc
%%%
%%% SUCCESS
%%% @end
%%% Created : 07. 10 2020 15:13
%%%-------------------------------------------------------------------
-module(action_become_attacking).
-include("behavior3.hrl").
-include("example.hrl").
%% API
-export([tick/2]).
tick(_BTree, State) ->
State1 = State#{cur_state := ?SET_STATE(?STATE_TYPE_ATTACKING)},
?INFO("~ts:变为攻击状态", [maps:get(uid, State1)]),
{?BT_SUCCESS, State1}.

+ 20
- 0
examples/action/action_become_idle.erl 查看文件

@ -0,0 +1,20 @@
%%%-------------------------------------------------------------------
%%% @author DY
%%% @copyright (C) 2020, <COMPANY>
%%% @doc
%%%
%%% SUCCESS
%%% @end
%%% Created : 07. 10 2020 15:13
%%%-------------------------------------------------------------------
-module(action_become_idle).
-include("behavior3.hrl").
-include("example.hrl").
%% API
-export([tick/2]).
tick(_BTree, State) ->
State1 = State#{cur_state := ?SET_STATE(?STATE_TYPE_IDLE)},
?INFO("~ts:变为空闲状态", [maps:get(uid, State1)]),
{?BT_SUCCESS, State1}.

+ 20
- 0
examples/action/action_become_patrolling.erl 查看文件

@ -0,0 +1,20 @@
%%%-------------------------------------------------------------------
%%% @author DY
%%% @copyright (C) 2020, <COMPANY>
%%% @doc
%%%
%%% SUCCESS
%%% @end
%%% Created : 07. 10 2020 15:13
%%%-------------------------------------------------------------------
-module(action_become_patrolling).
-include("behavior3.hrl").
-include("example.hrl").
%% API
-export([tick/2]).
tick(_BTree, State) ->
State1 = State#{cur_state := ?SET_STATE(?STATE_TYPE_PATROLLING)},
?INFO("~ts:变为巡逻状态", [maps:get(uid, State1)]),
{?BT_SUCCESS, State1}.

+ 20
- 0
examples/action/action_become_recovering.erl 查看文件

@ -0,0 +1,20 @@
%%%-------------------------------------------------------------------
%%% @author DY
%%% @copyright (C) 2020, <COMPANY>
%%% @doc
%%%
%%% SUCCESS
%%% @end
%%% Created : 07. 10 2020 15:13
%%%-------------------------------------------------------------------
-module(action_become_recovering).
-include("behavior3.hrl").
-include("example.hrl").
%% API
-export([tick/2]).
tick(_BTree, State) ->
State1 = State#{cur_state := ?SET_STATE(?STATE_TYPE_RECOVERING)},
?INFO("~ts:变为恢复状态", [maps:get(uid, State1)]),
{?BT_SUCCESS, State1}.

+ 60
- 0
examples/action/action_collect_dest_path.erl 查看文件

@ -0,0 +1,60 @@
%%%-------------------------------------------------------------------
%%% @author DY
%%% @copyright (C) 2020, <COMPANY>
%%% @doc
%%%
%%% SUCCESS
%%% @end
%%% Created : 07. 10 2020 15:13
%%%-------------------------------------------------------------------
-module(action_collect_dest_path).
-include("behavior3.hrl").
-include("example.hrl").
%% API
-export([tick/2]).
tick(_BTree, #{cur_grid := CurGrid, grid_list := GirdList} = State) ->
case GirdList of
[] ->
EndGrid = get_end_grid(CurGrid),
State1 = State#{grid_list := get_grid_path(CurGrid, EndGrid)},
?INFO("~ts:找到穿传说中的“大蒜”的位置,要尽快到达那里!", [maps:get(uid, State1)]),
{?BT_SUCCESS, State1};
_ ->
?INFO("~ts:天要黑了,要尽快到达那里!", [maps:get(uid, State)]),
{?BT_SUCCESS, State}
end.
get_end_grid(CurGrid) ->
EndGridList = [{0, 0}, {0, ?MAX_X}, {?MAX_Y, ?MAX_X}, {?MAX_Y, 0}],
{_, EndGrid} = hd(lists:sort([{calc_distance(CurGrid, Grid), Grid} || Grid <- EndGridList])),
EndGrid.
calc_distance({X1, Y1}, {X2, Y2}) ->
math:sqrt(math:pow(X1 - X2, 2) + math:pow(Y1 - Y2, 2)).
get_grid_path(CurGrid, EndGrid) ->
case EndGrid of
{0, 0} ->
get_grid_path(CurGrid, [EndGrid], 1, 1);
{0, ?MAX_X} ->
get_grid_path(CurGrid, [EndGrid], 1, -1);
{?MAX_Y, ?MAX_X} ->
get_grid_path(CurGrid, [EndGrid], -1, -1);
{?MAX_Y, 0} ->
get_grid_path(CurGrid, [EndGrid], -1, 1)
end.
get_grid_path(CurGrid, GridPath, XInc, YInc) ->
case GridPath of
[CurGrid | _] ->
GridPath;
[{X, Y} | _] ->
case Y + YInc of
Y1 when Y1 == 0 orelse Y1 == ?MAX_X ->
get_grid_path(CurGrid, [{X + XInc, Y1}, {X, Y1} | GridPath], XInc, bnot YInc + 1);
Y1 ->
get_grid_path(CurGrid, [{X, Y1} | GridPath], XInc, YInc)
end
end.

+ 45
- 0
examples/action/action_collect_path.erl 查看文件

@ -0,0 +1,45 @@
%%%-------------------------------------------------------------------
%%% @author DY
%%% @copyright (C) 2020, <COMPANY>
%%% @doc
%%%
%%% SUCCESS
%%% @end
%%% Created : 07. 10 2020 15:13
%%%-------------------------------------------------------------------
-module(action_collect_path).
-include("behavior3.hrl").
-include("example.hrl").
%% API
-export([tick/2, get_grid_path/1]).
tick(_BTree, #{cur_grid := CurGrid} = State) ->
State1 = State#{grid_list := get_grid_path(CurGrid)},
{?BT_SUCCESS, State1}.
get_grid_path(CurGrid) ->
List = [{1, 0}, {-1, 0}, {0, 1}, {0, -1}],
Max = length(List),
List1 = [E || {_, E} <- lists:sort([{?RAND(1, Max), E} || E <- List])],
case get_grid_path_1(List1, CurGrid, []) of
[] ->
get_grid_path(CurGrid);
Result ->
Result
end.
get_grid_path_1([{X, Y} | T], {CurX, CurY}, Result) ->
X1 = CurX + X,
Y1 = CurY + Y,
Grid = {X1, Y1},
case X1 >= 0 andalso X1 =< ?MAX_X andalso Y1 >= 0 andalso Y1 =< ?MAX_Y of
true ->
get_grid_path_1(T, Grid, [Grid | Result]);
false ->
[]
end;
get_grid_path_1([], _, Result) ->
Result.

+ 26
- 0
examples/action/action_collect_patrol.erl 查看文件

@ -0,0 +1,26 @@
%%%-------------------------------------------------------------------
%%% @author DY
%%% @copyright (C) 2020, <COMPANY>
%%% @doc
%%%
%%% SUCCESS
%%% FAILURE
%%% @end
%%% Created : 07. 10 2020 15:13
%%%-------------------------------------------------------------------
-module(action_collect_patrol).
-include("behavior3.hrl").
-include("example.hrl").
%% API
-export([tick/2]).
tick(_BTree, #{cur_grid := CurGrid, rival_list := RivalList} = State) ->
Fun = fun(UID) -> maps:get(type, game_dict:get_role_state(UID)) == ?HUMAN end,
case lists:filter(Fun, game_dict:get_map_data(CurGrid)) of
UIDList when UIDList /= [] ->
?INFO("~ts:嗅到了人类的味道,将要对[~ts]发起攻击", [maps:get(uid, State), lists:join(",", UIDList)]),
{?BT_SUCCESS, State#{rival_list := UIDList ++ RivalList}};
[] ->
{?BT_FAILURE, State}
end.

+ 22
- 0
examples/action/action_cost_power.erl 查看文件

@ -0,0 +1,22 @@
%%%-------------------------------------------------------------------
%%% @author DY
%%% @copyright (C) 2020, <COMPANY>
%%% @doc
%%%
%%% Misc结构中的atk_dmg字段的值
%%% SUCCESS
%%% @end
%%% Created : 07. 10 2020 15:13
%%%-------------------------------------------------------------------
-module(action_cost_power).
-include("behavior3.hrl").
-include("example.hrl").
%% API
-export([tick/2]).
tick(_BTree, State) ->
#{cur_power := CurPower, misc := #{atk_dmg := {AtkType, Dmg}} = Misc} = State,
State1 = State#{cur_power := max(CurPower - Dmg, 0), misc := maps:remove(atk_dmg, Misc)},
?INFO("~ts:受到~ts攻击,体力值减少~w点 剩余体力~w", [maps:get(uid, State1), AtkType, Dmg,maps:get(cur_power, State1)]),
{?BT_SUCCESS, State1}.

+ 21
- 0
examples/action/action_cost_rage.erl 查看文件

@ -0,0 +1,21 @@
%%%-------------------------------------------------------------------
%%% @author DY
%%% @copyright (C) 2020, <COMPANY>
%%% @doc
%%%
%%% cost_rage扣除相应怒气0
%%% SUCCESS
%%% @end
%%% Created : 07. 10 2020 15:13
%%%-------------------------------------------------------------------
-module(action_cost_rage).
-include("behavior3.hrl").
-include("example.hrl").
%% API
-export([tick/2]).
tick(#{properties := #{cost_rage := CostRage}} = _BTree, #{cur_rage := CurRage} = State) ->
State1 = State#{cur_rage := max(CurRage - CostRage, 0)},
?INFO("~ts:怒气值减少~w", [maps:get(uid, State1), CurRage - maps:get(cur_rage, State1)]),
{?BT_SUCCESS, State1}.

+ 20
- 0
examples/action/action_del_dead_target.erl 查看文件

@ -0,0 +1,20 @@
%%%-------------------------------------------------------------------
%%% @author DY
%%% @copyright (C) 2020, <COMPANY>
%%% @doc
%%%
%%% rival_list中第一个目标
%%% SUCCESS
%%% @end
%%% Created : 07. 10 2020 15:13
%%%-------------------------------------------------------------------
-module(action_del_dead_target).
-include("behavior3.hrl").
-include("example.hrl").
%% API
-export([tick/2]).
tick(_BTree, #{rival_list := [_ | T]} = State) ->
State1 = State#{rival_list := T},
{?BT_SUCCESS, State1}.

+ 21
- 0
examples/action/action_died.erl 查看文件

@ -0,0 +1,21 @@
%%%-------------------------------------------------------------------
%%% @author DY
%%% @copyright (C) 2020, <COMPANY>
%%% @doc
%%%
%%%
%%% SUCCESS
%%% @end
%%% Created : 07. 10 2020 15:13
%%%-------------------------------------------------------------------
-module(action_died).
-include("behavior3.hrl").
-include("example.hrl").
%% API
-export([tick/2]).
tick(_BTree, #{uid := UID} = State) ->
self() ! {died, UID},
?INFO("~ts 被消灭!", [UID]),
{?BT_SUCCESS, State#{cur_state := ?SET_STATE(?STATE_TYPE_DEAD)}}.

+ 21
- 0
examples/action/action_finish.erl 查看文件

@ -0,0 +1,21 @@
%%%-------------------------------------------------------------------
%%% @author DY
%%% @copyright (C) 2020, <COMPANY>
%%% @doc
%%% is_win属性打印结果
%%%
%%% SUCCESS
%%% @end
%%% Created : 07. 10 2020 15:13
%%%-------------------------------------------------------------------
-module(action_finish).
-include("behavior3.hrl").
-include("example.hrl").
%% API
-export([tick/2]).
tick(#{properties := #{is_win := IsWin}} = _BTree, #{uid := UID} = State) ->
self() ! finish,
?INFO("游戏结束,~ts~ts!", [UID, IsWin]),
{?BT_SUCCESS, State}.

+ 21
- 0
examples/action/action_move_grid.erl 查看文件

@ -0,0 +1,21 @@
%%%-------------------------------------------------------------------
%%% @author DY
%%% @copyright (C) 2020, <COMPANY>
%%% @doc
%%%
%%% SUCCESS
%%% @end
%%% Created : 07. 10 2020 15:13
%%%-------------------------------------------------------------------
-module(action_move_grid).
-include("behavior3.hrl").
-include("example.hrl").
%% API
-export([tick/2]).
tick(_BTree, #{uid := UID, cur_grid := OldGrid, grid_list := [Grid | T]} = State) ->
game_dict:update_map_date(OldGrid, Grid, UID),
State1 = State#{grid_list := T, cur_grid := Grid},
?INFO("~ts:移动到了~w格子", [UID, Grid]),
{?BT_SUCCESS, State1}.

+ 22
- 0
examples/action/action_recover_power.erl 查看文件

@ -0,0 +1,22 @@
%%%-------------------------------------------------------------------
%%% @author DY
%%% @copyright (C) 2020, <COMPANY>
%%% @doc
%%%
%%% Max - Min之间随机恢复几点体力
%%% SUCCESS
%%% @end
%%% Created : 07. 10 2020 15:13
%%%-------------------------------------------------------------------
-module(action_recover_power).
-include("behavior3.hrl").
-include("example.hrl").
%% API
-export([tick/2]).
tick(#{properties := #{max_power := MaxPower, min_power := MinPower}} = _BTree, #{cur_power := CurPower} = State) ->
RecoverPower = ?RAND(MinPower, MaxPower),
State1 = State#{cur_power := min(CurPower + RecoverPower, ?MAX_POWER)},
?INFO("~ts:体力值增加了~w点 当前体力:~w", [maps:get(uid, State1), RecoverPower, maps:get(cur_power, State1)]),
{?BT_SUCCESS, State1}.

+ 44
- 0
examples/action/action_skill_attack.erl 查看文件

@ -0,0 +1,44 @@
%%%-------------------------------------------------------------------
%%% @author DY
%%% @copyright (C) 2020, <COMPANY>
%%% @doc
%%%
%%% RAND(min_power ,max_power) * RAND(min_rate, max_rate)
%%% SUCCESS
%%% @end
%%% Created : 07. 10 2020 15:13
%%%-------------------------------------------------------------------
-module(action_skill_attack).
-include("behavior3.hrl").
-include("example.hrl").
%% API
-export([tick/2]).
tick(#{properties := Prop} = _BTree, #{uid := UID, rival_list := [RivalUID | _]} = State) ->
BTreeID = game_dict:get_passivity_btree_id(RivalUID),
RivalState = get_be_attacker_rival_state(Prop, UID, RivalUID),
{_, RivalState1} = behavior_tree:execute_sub_tree(BTreeID, RivalState),
game_dict:put_role_state(RivalUID, RivalState1),
{?BT_SUCCESS, State}.
get_be_attacker_rival_state(Prop, UID, RivalUID) ->
#{
skill_type := SkillType,
min_power := MinPower, max_power := MaxPower,
min_rate := MinRate, max_rate := MaxRate
} = Prop,
#{rival_list := RivalList} = RivalState = game_dict:get_role_state(RivalUID),
case lists:member(UID, RivalList) of
true ->
RivalList1 = RivalList;
false ->
RivalList1 = RivalList ++ [UID]
end,
RivalState1 = RivalState#{
rival_list := RivalList1,
misc := #{atk_dmg => {SkillType, ?RAND(MinPower, MaxPower) * ?RAND(MinRate, MaxRate)}}
},
game_dict:put_role_state(RivalUID, RivalState1),
?INFO("~ts:发动了~ts攻击", [UID, SkillType]),
RivalState1.

+ 24
- 0
examples/condition/cond_is_attacking.erl 查看文件

@ -0,0 +1,24 @@
%%%-------------------------------------------------------------------
%%% @author DY
%%% @copyright (C) 2020, <COMPANY>
%%% @doc
%%%
%%% SUCCESS
%%% FAILURE
%%% @end
%%% Created : 07. 10 2020 15:13
%%%-------------------------------------------------------------------
-module(cond_is_attacking).
-include("behavior3.hrl").
-include("example.hrl").
%% API
-export([tick/2]).
tick(_BTree, #{cur_state := CurState} = State) ->
case ?IS_STATE(?STATE_TYPE_ATTACKING, CurState) of
true ->
{?BT_SUCCESS, State};
false ->
{?BT_FAILURE, State}
end.

+ 23
- 0
examples/condition/cond_is_dest.erl 查看文件

@ -0,0 +1,23 @@
%%%-------------------------------------------------------------------
%%% @author DY
%%% @copyright (C) 2020, <COMPANY>
%%% @doc
%%%
%%% SUCCESS
%%% FAILURE
%%% @end
%%% Created : 07. 10 2020 15:18
%%%-------------------------------------------------------------------
-module(cond_is_dest).
-include("behavior3.hrl").
%% API
-export([tick/2]).
tick(_BTree, State) ->
case State of
#{grid_list := []} ->
{?BT_SUCCESS, State};
#{} ->
{?BT_FAILURE, State}
end.

+ 23
- 0
examples/condition/cond_is_died.erl 查看文件

@ -0,0 +1,23 @@
%%%-------------------------------------------------------------------
%%% @author DY
%%% @copyright (C) 2020, <COMPANY>
%%% @doc
%%%
%%% SUCCESS
%%% FAILURE
%%% @end
%%% Created : 07. 10 2020 15:26
%%%-------------------------------------------------------------------
-module(cond_is_died).
-include("behavior3.hrl").
%% API
-export([tick/2]).
tick(_BTree, #{cur_power := CurPower} = State) ->
case CurPower =< 0 of
true ->
{?BT_SUCCESS, State};
false ->
{?BT_FAILURE, State}
end.

+ 24
- 0
examples/condition/cond_is_idle.erl 查看文件

@ -0,0 +1,24 @@
%%%-------------------------------------------------------------------
%%% @author DY
%%% @copyright (C) 2020, <COMPANY>
%%% @doc
%%%
%%% SUCCESS
%%% FAILURE
%%% @end
%%% Created : 07. 10 2020 15:13
%%%-------------------------------------------------------------------
-module(cond_is_idle).
-include("behavior3.hrl").
-include("example.hrl").
%% API
-export([tick/2]).
tick(_BTree, #{cur_state := CurState} = State) ->
case ?IS_STATE(?STATE_TYPE_IDLE, CurState) of
true ->
{?BT_SUCCESS, State};
false ->
{?BT_FAILURE, State}
end.

+ 24
- 0
examples/condition/cond_is_patrolling.erl 查看文件

@ -0,0 +1,24 @@
%%%-------------------------------------------------------------------
%%% @author DY
%%% @copyright (C) 2020, <COMPANY>
%%% @doc
%%%
%%% SUCCESS
%%% FAILURE
%%% @end
%%% Created : 07. 10 2020 15:13
%%%-------------------------------------------------------------------
-module(cond_is_patrolling).
-include("behavior3.hrl").
-include("example.hrl").
%% API
-export([tick/2]).
tick(_BTree, #{cur_state := CurState} = State) ->
case ?IS_STATE(?STATE_TYPE_PATROLLING, CurState) of
true ->
{?BT_SUCCESS, State};
false ->
{?BT_FAILURE, State}
end.

+ 24
- 0
examples/condition/cond_is_power_full.erl 查看文件

@ -0,0 +1,24 @@
%%%-------------------------------------------------------------------
%%% @author DY
%%% @copyright (C) 2020, <COMPANY>
%%% @doc
%%%
%%% SUCCESS
%%% FAILURE
%%% @end
%%% Created : 07. 10 2020 15:13
%%%-------------------------------------------------------------------
-module(cond_is_power_full).
-include("behavior3.hrl").
-include("example.hrl").
%% API
-export([tick/2]).
tick(_BTree, #{cur_power := CurPower} = State) ->
case CurPower >= ?MAX_POWER of
true ->
{?BT_SUCCESS, State};
false ->
{?BT_FAILURE, State}
end.

+ 24
- 0
examples/condition/cond_is_rage_full.erl 查看文件

@ -0,0 +1,24 @@
%%%-------------------------------------------------------------------
%%% @author DY
%%% @copyright (C) 2020, <COMPANY>
%%% @doc
%%%
%%% SUCCESS
%%% FAILURE
%%% @end
%%% Created : 07. 10 2020 15:18
%%%-------------------------------------------------------------------
-module(cond_is_rage_full).
-include("behavior3.hrl").
-include("example.hrl").
%% API
-export([tick/2]).
tick(_BTree, #{cur_rage := CurRage} = State) ->
case CurRage >= ?MAX_RAGE of
true ->
{?BT_SUCCESS, State};
false ->
{?BT_FAILURE, State}
end.

+ 24
- 0
examples/condition/cond_is_recovering.erl 查看文件

@ -0,0 +1,24 @@
%%%-------------------------------------------------------------------
%%% @author DY
%%% @copyright (C) 2020, <COMPANY>
%%% @doc
%%%
%%% SUCCESS
%%% FAILURE
%%% @end
%%% Created : 07. 10 2020 15:13
%%%-------------------------------------------------------------------
-module(cond_is_recovering).
-include("behavior3.hrl").
-include("example.hrl").
%% API
-export([tick/2]).
tick(_BTree, #{cur_state := CurState} = State) ->
case ?IS_STATE(?STATE_TYPE_RECOVERING, CurState) of
true ->
{?BT_SUCCESS, State};
false ->
{?BT_FAILURE, State}
end.

+ 28
- 0
examples/condition/cond_is_target_died.erl 查看文件

@ -0,0 +1,28 @@
%%%-------------------------------------------------------------------
%%% @author DY
%%% @copyright (C) 2020, <COMPANY>
%%% @doc
%%%
%%% SUCCESS
%%% FAILURE
%%% @end
%%% Created : 07. 10 2020 15:20
%%%-------------------------------------------------------------------
-module(cond_is_target_died).
-include("behavior3.hrl").
-include("example.hrl").
%% API
-export([tick/2]).
tick(_BTree, State) ->
case State of
#{rival_list := [RivalUID | _]} ->
case game_dict:get_role_state(RivalUID) of
#{cur_state := CurState} when ?IS_STATE(?STATE_TYPE_DEAD, CurState) ->
{?BT_SUCCESS, State};
#{} ->
{?BT_FAILURE, State}
end;
#{} ->
{?BT_FAILURE, State}
end.

+ 24
- 0
examples/condition/cond_power_lt.erl 查看文件

@ -0,0 +1,24 @@
%%%-------------------------------------------------------------------
%%% @author DY
%%% @copyright (C) 2020, <COMPANY>
%%% @doc
%%%
%%% SUCCESS
%%% FAILURE
%%% @end
%%% Created : 07. 10 2020 15:20
%%%-------------------------------------------------------------------
-module(cond_power_lt).
-include("behavior3.hrl").
-include("example.hrl").
%% API
-export([tick/2]).
tick(#{properties := #{power := LTPower}} = _BTree, #{cur_power := CurPower} = State) ->
case CurPower < LTPower of
true ->
?INFO("~ts:当前体力低于~w点,必须要尽快休息恢复体力!", [maps:get(uid, State), LTPower]),
{?BT_SUCCESS, State};
false ->
{?BT_FAILURE, State}
end.

+ 32
- 0
examples/example.hrl 查看文件

@ -0,0 +1,32 @@
%%%-------------------------------------------------------------------
%%% @author DY
%%% @copyright (C) 2020, <COMPANY>
%%% @doc
%%%
%%% @end
%%% Created : 07. 10 2020 15:16
%%%-------------------------------------------------------------------
-type uid() :: binary().
-type grid() :: {X :: non_neg_integer(), Y :: non_neg_integer()}.
-define(INFO(Format, Args), io:format(Format ++ "~n", Args)).
-define(MAX_X, 5).
-define(MAX_Y, 5).
-define(HUMAN, 1).
-define(ZOMBIE, 2).
-define(RAND(Min, Max), (rand:uniform(Max - (Min - 1)) + (Min - 1))).
-define(STATE_TYPE_IDLE, 1).
-define(STATE_TYPE_ATTACKING, 2).
-define(STATE_TYPE_PATROLLING, 3).
-define(STATE_TYPE_RECOVERING, 4).
-define(STATE_TYPE_DEAD, 5).
-define(IS_STATE(StateType, CurState), (CurState band (1 bsl (StateType - 1)) > 0)).
-define(SET_STATE(StateType), (1 bsl (StateType - 1))).
-define(MAX_POWER, 100).
-define(MAX_RAGE, 100).

+ 1786
- 0
examples/example.json
文件差異過大導致無法顯示
查看文件


+ 59
- 0
examples/game_dict.erl 查看文件

@ -0,0 +1,59 @@
%%%-------------------------------------------------------------------
%%% @author DY
%%% @copyright (C) 2020, <COMPANY>
%%% @doc
%%%
%%% @end
%%% Created : 18. 10 2020 20:53
%%%-------------------------------------------------------------------
-module(game_dict).
-include("behavior3.hrl").
-include("example.hrl").
%% API
-export([
get_map_data/1, update_map_date/3,
get_initiative_btree_id/1, put_initiative_btree_id/2, erase_initiative_btree_id/1,
get_passivity_btree_id/1, put_passivity_btree_id/2, erase_passivity_btree_id/1,
get_role_state/1, put_role_state/2, erase_role_state/1
]).
-spec get_map_data(grid()) -> [uid()].
get_map_data(Grid) ->
case erlang:get(Grid) of
undefined ->
[];
L ->
L
end.
-spec update_map_date(grid(), grid(), uid()) -> ok.
update_map_date(OldGrid, NewGrid, UID) ->
erlang:put(OldGrid, lists:delete(UID, get_map_data(OldGrid))),
erlang:put(NewGrid, [UID | get_map_data(NewGrid)]),
ok.
-spec get_initiative_btree_id(uid()) -> bt_uid().
get_initiative_btree_id(UID) ->
erlang:get({initiative_btree_id, UID}).
put_initiative_btree_id(UID, BTreeID) ->
erlang:put({initiative_btree_id, UID}, BTreeID).
erase_initiative_btree_id(UID) ->
erlang:erase({initiative_btree_id, UID}).
-spec get_passivity_btree_id(uid()) -> bt_uid().
get_passivity_btree_id(UID) ->
erlang:get({passivity_btree_id, UID}).
put_passivity_btree_id(UID, BTreeID) ->
erlang:put({passivity_btree_id, UID}, BTreeID).
erase_passivity_btree_id(UID) ->
erlang:erase({passivity_btree_id, UID}).
-spec get_role_state(uid()) -> map().
get_role_state(UID) ->
erlang:get({role_state, UID}).
put_role_state(UID, State) ->
erlang:put({role_state, UID}, State).
erase_role_state(UID) ->
erlang:erase({role_state, UID}).

+ 127
- 0
examples/game_process.erl 查看文件

@ -0,0 +1,127 @@
%%%-------------------------------------------------------------------
%%% @author DY
%%% @copyright (C) 2020, <COMPANY>
%%% @doc
%%%
%%% @end
%%%-------------------------------------------------------------------
-module(game_process).
-behaviour(gen_server).
-include("example.hrl").
-export([start_link/0]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2]).
-define(SERVER, ?MODULE).
%%%===================================================================
%%% Spawning and gen_server implementation
%%%===================================================================
start_link() ->
gen_server:start_link(?MODULE, [], []).
init([]) ->
{TitleMaps, TreeMaps, TreeNodeMaps} = behavior_tree:load_tree_file("examples/example.json"),
ZombieList = [
%% <<"HUNTER"/utf8>>,
%% <<"BOMMER"/utf8>>,
%% <<"JOCKEY"/utf8>>,
%% <<"SOMKER"/utf8>>,
%% <<"SPITTER"/utf8>>,
%% <<"CHARGER"/utf8>>,
%% <<"TANK"/utf8>>,
<<"WITCH"/utf8>>
],
init_zombie(ZombieList, TitleMaps, TreeMaps, TreeNodeMaps),
UID = <<"凯恩"/utf8>>,
init_patrol(UID, TitleMaps, TreeMaps, TreeNodeMaps),
erlang:put(uid_list, ZombieList ++ [UID]),
self() ! run,
{ok, #{frame => 1}}.
handle_call(_Request, _From, State) ->
{reply, ok, State}.
handle_cast(_Request, State) ->
{noreply, State}.
handle_info(run, #{frame := Frame} = State) ->
try
?INFO("~w分钟", [Frame]),
Fun = fun(UID) -> run(UID) end,
lists:foreach(Fun, get(uid_list)),
erlang:send_after(1000, self(), run),
{noreply, State#{frame := Frame + 1}}
catch
Error:Reason:StackTrace ->
?INFO("Error:~w Reason:~w ~n StackTrace:~p", [Error, Reason, StackTrace]),
{stop, normal, State}
end;
handle_info(finish, State) ->
{stop, normal, State};
handle_info({died, UID}, State) ->
erlang:put(uid_list, lists:delete(UID, erlang:get(uid_list))),
{noreply, State};
handle_info(_Info, State) ->
{noreply, State}.
%%%===================================================================
%%% Internal functions
%%%===================================================================
new_state(UID, Type, CurGrid) ->
case Type of
?HUMAN ->
Power = ?MAX_POWER;
?ZOMBIE ->
Power = ?RAND(?MAX_POWER div 2, ?MAX_POWER)
end,
State = #{
uid => UID,
type => Type,
cur_state => ?STATE_TYPE_IDLE,
cur_power => Power,
cur_rage => 0,
grid_list => [],
cur_grid => CurGrid,
rival_list => [],
misc => #{}
},
?INFO("生成单位:~tp", [State]),
State.
init_zombie([UID | T], TitleMaps, TreeMaps, TreeNodeMaps) ->
{ok, InitiativeBTreeID} = behavior_tree:init_btree_by_title(<<"丧尸主动AI"/utf8>>, TitleMaps, TreeMaps, TreeNodeMaps),
{ok, PassivityBTreeID} = behavior_tree:init_btree_by_title(<<"丧尸被动AI"/utf8>>, TitleMaps, TreeMaps, TreeNodeMaps),
game_dict:put_initiative_btree_id(UID, InitiativeBTreeID),
game_dict:put_passivity_btree_id(UID, PassivityBTreeID),
Grid = {?RAND(0, ?MAX_X), ?RAND(0, ?MAX_Y)},
game_dict:put_role_state(UID, new_state(UID, ?ZOMBIE, Grid)),
init_zombie(T, TitleMaps, TreeMaps, TreeNodeMaps);
init_zombie([], _TitleMaps, _TreeMaps, _TreeNodeMaps) ->
ok.
init_patrol(UID, TitleMaps, TreeMaps, TreeNodeMaps) ->
{ok, InitiativeBTreeID} = behavior_tree:init_btree_by_title(<<"巡逻兵主动AI"/utf8>>, TitleMaps, TreeMaps, TreeNodeMaps),
{ok, PassivityBTreeID} = behavior_tree:init_btree_by_title(<<"巡逻兵被动AI"/utf8>>, TitleMaps, TreeMaps, TreeNodeMaps),
game_dict:put_initiative_btree_id(UID, InitiativeBTreeID),
game_dict:put_passivity_btree_id(UID, PassivityBTreeID),
Grid = {?RAND(0, ?MAX_X), ?RAND(0, ?MAX_Y)},
game_dict:put_role_state(UID, new_state(UID, ?HUMAN, Grid)).
run(UID) ->
BTreeID = game_dict:get_initiative_btree_id(UID),
#{cur_state := CurState} = State = game_dict:get_role_state(UID),
case ?IS_STATE(?STATE_TYPE_DEAD, CurState) of
true ->
ok;
false ->
{_, State1} = behavior_tree:execute(BTreeID, State),
game_dict:put_role_state(UID, State1)
end.

+ 64
- 0
include/behavior3.hrl 查看文件

@ -0,0 +1,64 @@
-ifndef(behavior3_h).
-define(behavior3_h, true).
-export_type([bt_uid/0, bt_status/0, bt_node_id/0, bt_state/0, properties/0, uninit_bt_node/0, tree_nodes/0, btree/0, bt_node/0]).
-type bt_uid() :: reference().
-define(BT_SUCCESS, 1).
-define(BT_FAILURE, 2).
-define(BT_RUNNING, 3).
-define(BT_ERROR, 4).
-type bt_status() :: ?BT_SUCCESS|?BT_FAILURE|?BT_RUNNING|?BT_ERROR.
-type bt_node_id() :: string().
-type bt_state() :: #{
'$global_maps' => map(),
'$local_maps' => map(),
term() => term()
}.
-type properties() :: #{atom() => term()}.
%%
-type uninit_bt_node() :: #{
id => bt_node_id(),
name => atom() | bt_node_id(),
category => atom(),
properties => properties(),
children => [bt_node_id()]
}.
-type tree_nodes() :: #{bt_node_id() => uninit_bt_node()}.
%%
-type btree() :: #{
id => bt_node_id(),
root => bt_node_id(),
properties => properties(),
tree_nodes => tree_nodes()
}.
%%
-type bt_node() :: #{
id := bt_uid(),
bt_node_id := bt_node_id(),
parent_id := bt_uid(),
name := atom(),
category := atom(),
properties := properties(),
children := [bt_uid()]
}.
-define(BI_MOD_LIST, [
error, failer, runner, succeeder, wait,
mem_priority, mem_sequence, priority, sequence,
inverter, limiter, max_time, repeat_until_failure, repeat_until_success, repeater
]).
-define(BT_LOG(Format), io:format("~w:~w:~w [debug] ~p: " ++ Format ++ "~n", tuple_to_list(time()) ++ [?MODULE])).
-define(BT_LOG(Format, Args), io:format("~w:~w:~w [debug] ~p: " ++ Format ++ "~n", tuple_to_list(time()) ++ [?MODULE | Args])).
-endif.

+ 15
- 0
rebar.config 查看文件

@ -0,0 +1,15 @@
{erl_opts, [debug_info, {i, "include"}
%% {d, show_bt_log}, %% log_switch
]}.
{deps, [
jsx
]}.
{xref_checks, [
undefined_function_calls
, undefined_functions
, locals_not_used
, deprecated_function_calls
, deprecated_functions
]}.

+ 22
- 0
src/action/error.erl 查看文件

@ -0,0 +1,22 @@
-module(error).
%%--------------------------------------------------------------------
%% include
%%--------------------------------------------------------------------
-include("behavior3.hrl").
%%--------------------------------------------------------------------
%% export API
%%--------------------------------------------------------------------
-export([tick/2]).
%%--------------------------------------------------------------------
%% API functions
%%--------------------------------------------------------------------
-spec tick(bt_node(), bt_state()) -> {bt_status(), bt_state()}.
tick(_BTNode, BTState) ->
{?BT_ERROR, BTState}.
%%--------------------------------------------------------------------
%% Internal functions
%%--------------------------------------------------------------------

+ 22
- 0
src/action/failer.erl 查看文件

@ -0,0 +1,22 @@
-module(failer).
%%--------------------------------------------------------------------
%% include
%%--------------------------------------------------------------------
-include("behavior3.hrl").
%%--------------------------------------------------------------------
%% export API
%%--------------------------------------------------------------------
-export([tick/2]).
%%--------------------------------------------------------------------
%% API functions
%%--------------------------------------------------------------------
-spec tick(bt_node(), bt_state()) -> {bt_status(), bt_state()}.
tick(_BTNode, BTState) ->
{?BT_FAILURE, BTState}.
%%--------------------------------------------------------------------
%% Internal functions
%%--------------------------------------------------------------------

+ 22
- 0
src/action/runner.erl 查看文件

@ -0,0 +1,22 @@
-module(runner).
%%--------------------------------------------------------------------
%% include
%%--------------------------------------------------------------------
-include("behavior3.hrl").
%%--------------------------------------------------------------------
%% export API
%%--------------------------------------------------------------------
-export([tick/2]).
%%--------------------------------------------------------------------
%% API functions
%%--------------------------------------------------------------------
-spec tick(bt_node(), bt_state()) -> {bt_status(), bt_state()}.
tick(_BTNode, BTState) ->
{?BT_RUNNING, BTState}.
%%--------------------------------------------------------------------
%% Internal functions
%%--------------------------------------------------------------------

+ 22
- 0
src/action/succeeder.erl 查看文件

@ -0,0 +1,22 @@
-module(succeeder).
%%--------------------------------------------------------------------
%% include
%%--------------------------------------------------------------------
-include("behavior3.hrl").
%%--------------------------------------------------------------------
%% export API
%%--------------------------------------------------------------------
-export([tick/2]).
%%--------------------------------------------------------------------
%% API functions
%%--------------------------------------------------------------------
-spec tick(bt_node(), bt_state()) -> {bt_status(), bt_state()}.
tick(_BTNode, BTState) ->
{?BT_SUCCESS, BTState}.
%%--------------------------------------------------------------------
%% Internal functions
%%--------------------------------------------------------------------

+ 36
- 0
src/action/wait.erl 查看文件

@ -0,0 +1,36 @@
-module(wait).
%%--------------------------------------------------------------------
%% include
%%--------------------------------------------------------------------
-include("behavior3.hrl").
%%--------------------------------------------------------------------
%% export API
%%--------------------------------------------------------------------
-export([open/2, tick/2, close/2]).
%%--------------------------------------------------------------------
%% API functions
%%--------------------------------------------------------------------
-spec open(bt_node(), bt_state()) -> bt_state().
open(#{id := ID}, BTState) ->
blackboard:set(start_time, erlang:system_time(millisecond), ID, BTState).
-spec tick(bt_node(), bt_state()) -> {bt_status(), bt_state()}.
tick(#{id := ID, properties := #{milliseconds := EndTime}} = _BTNode, BTState) ->
StartTime = blackboard:get(start_time, ID, BTState),
case erlang:system_time(millisecond) - StartTime > EndTime of
true ->
{?BT_SUCCESS, BTState};
false ->
{?BT_RUNNING, BTState}
end.
-spec close(bt_node(), bt_state()) -> bt_state().
close(#{id := ID} = _BTNode, BTState) ->
blackboard:remove(start_time, ID, BTState).
%%--------------------------------------------------------------------
%% Internal functions
%%--------------------------------------------------------------------

+ 43
- 0
src/composite/mem_priority.erl 查看文件

@ -0,0 +1,43 @@
-module(mem_priority).
%%--------------------------------------------------------------------
%% include
%%--------------------------------------------------------------------
-include("behavior3.hrl").
%%--------------------------------------------------------------------
%% export API
%%--------------------------------------------------------------------
-export([open/2, tick/2, close/2]).
%%--------------------------------------------------------------------
%% API functions
%%--------------------------------------------------------------------
-spec open(bt_node(), bt_state()) -> bt_state().
open(#{id := ID, children := Children} = _BTNode, BTState) ->
blackboard:set(running_children, Children, ID, BTState).
-spec tick(bt_node(), bt_state()) -> {bt_status(), bt_state()}.
tick(#{id := ID} = _BTNode, BTState) ->
RunningChildren = blackboard:get(running_children, ID, BTState),
tick_1(RunningChildren, ID, BTState).
-spec close(bt_node(), bt_state()) -> bt_state().
close(#{id := ID} = _BTNode, State) ->
blackboard:remove(running_children, ID, State).
%%--------------------------------------------------------------------
%% Internal functions
%%--------------------------------------------------------------------
tick_1([ChildID | T] = Children, ID, BTState) ->
case base_node:execute(ChildID, BTState) of
{?BT_FAILURE, BTState1} ->
tick_1(T, ID, BTState1);
{?BT_RUNNING, BTState1} ->
BTState2 = blackboard:set(running_children, Children, ID, BTState1),
{?BT_RUNNING, BTState2};
{BTStatus, BTState1} ->
{BTStatus, BTState1}
end;
tick_1([], _ID, BTState) ->
{?BT_FAILURE, BTState}.

+ 43
- 0
src/composite/mem_sequence.erl 查看文件

@ -0,0 +1,43 @@
-module(mem_sequence).
%%--------------------------------------------------------------------
%% include
%%--------------------------------------------------------------------
-include("behavior3.hrl").
%%--------------------------------------------------------------------
%% export API
%%--------------------------------------------------------------------
-export([open/2, tick/2, close/2]).
%%--------------------------------------------------------------------
%% API functions
%%--------------------------------------------------------------------
-spec open(bt_node(), bt_state()) -> bt_state().
open(#{id := ID, children := Children} = _BTNode, BTState) ->
blackboard:set(running_children, Children, ID, BTState).
-spec tick(bt_node(), bt_state()) -> {bt_status(), bt_state()}.
tick(#{id := ID} = _BTNode, BTState) ->
RunningChildren = blackboard:get(running_children, ID, BTState),
tick_1(RunningChildren, ID, BTState).
-spec close(bt_node(), bt_state()) -> bt_state().
close(#{id := ID} = _BTNode, BTState) ->
blackboard:remove(running_children, ID, BTState).
%%--------------------------------------------------------------------
%% Internal functions
%%--------------------------------------------------------------------
tick_1([ChildID | T] = Children, ID, BTState) ->
case base_node:execute(ChildID, BTState) of
{?BT_SUCCESS, BTState1} ->
tick_1(T, ID, BTState1);
{?BT_RUNNING, BTState1} ->
BTState2 = blackboard:set(running_children, Children, ID, BTState1),
{?BT_RUNNING, BTState2};
{BTStatus, BTState1} ->
{BTStatus, BTState1}
end;
tick_1([], _ID, BTState) ->
{?BT_SUCCESS, BTState}.

+ 31
- 0
src/composite/priority.erl 查看文件

@ -0,0 +1,31 @@
-module(priority).
%%--------------------------------------------------------------------
%% include
%%--------------------------------------------------------------------
-include("behavior3.hrl").
%%--------------------------------------------------------------------
%% export API
%%--------------------------------------------------------------------
-export([tick/2]).
%%--------------------------------------------------------------------
%% API functions
%%--------------------------------------------------------------------
-spec tick(bt_node(), bt_state()) -> {bt_status(), bt_state()}.
tick(#{children := Children} = _BTNode, BTState) ->
tick_1(Children, BTState).
%%--------------------------------------------------------------------
%% Internal functions
%%--------------------------------------------------------------------
tick_1([ChildID | T], BTState) ->
case base_node:execute(ChildID, BTState) of
{?BT_FAILURE, BTState1} ->
tick_1(T, BTState1);
{BTStatus, BTState1} ->
{BTStatus, BTState1}
end;
tick_1([], BTState) ->
{?BT_FAILURE, BTState}.

+ 31
- 0
src/composite/sequence.erl 查看文件

@ -0,0 +1,31 @@
-module(sequence).
%%--------------------------------------------------------------------
%% include
%%--------------------------------------------------------------------
-include("behavior3.hrl").
%%--------------------------------------------------------------------
%% export API
%%--------------------------------------------------------------------
-export([tick/2]).
%%--------------------------------------------------------------------
%% API functions
%%--------------------------------------------------------------------
-spec tick(bt_node(), bt_state()) -> {bt_status(), bt_state()}.
tick(#{children := Children} = _BTNode, BTState) ->
tick_1(Children, BTState).
%%--------------------------------------------------------------------
%% Internal functions
%%--------------------------------------------------------------------
tick_1([ChildID | T], BTState) ->
case base_node:execute(ChildID, BTState) of
{?BT_SUCCESS, BTState1} ->
tick_1(T, BTState1);
{BTStatus, BTState1} ->
{BTStatus, BTState1}
end;
tick_1([], BTState) ->
{?BT_SUCCESS, BTState}.

+ 137
- 0
src/core/base_node.erl 查看文件

@ -0,0 +1,137 @@
-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/2open函数
%%
-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/2close函数
-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.

+ 215
- 0
src/core/behavior_tree.erl 查看文件

@ -0,0 +1,215 @@
-module(behavior_tree).
-compile([inline, {inline_size, 100}]).
%%--------------------------------------------------------------------
%% include
%%--------------------------------------------------------------------
-include("behavior3.hrl").
%%--------------------------------------------------------------------
%% export API
%%--------------------------------------------------------------------
-export([
load_tree_file/1,
init_btree_by_title/4, init_btree/3,
execute/2, execute_child/2, close_btree_node/2, unload_btree/1
]).
%% Internal API
-export([get_btree_id_by_title/2, get_btree/2, get_btree_node/2]).
%%--------------------------------------------------------------------
%% API functions
%%--------------------------------------------------------------------
%% @doc
%%
-spec load_tree_file(file:name_all()) -> {#{string() => bt_node_id()}, #{bt_node_id() => btree()}, tree_nodes()}|{error, term()}.
load_tree_file(JsonConfig) ->
case file:read_file(JsonConfig) of
{ok, Json} ->
JsonTerm = jsx:decode(Json, [return_maps]),
parse_btree(JsonTerm);
{error, Reason} ->
?BT_LOG("Error:~w Reason:~w JsonConfig:~w", [error, Reason, JsonConfig]),
{error, Reason}
end.
%% @doc
%% id
-spec get_btree_id_by_title(string(), #{string() => bt_node_id()}) -> bt_node_id()|undefined.
get_btree_id_by_title(Title, TitleMaps) ->
case TitleMaps of
#{Title := ID} ->
ID;
#{} ->
undefined
end.
%% @doc
%%
-spec get_btree(bt_node_id(), #{bt_node_id() => btree()}) -> btree()|undefined.
get_btree(BTNodeID, TreeMaps) ->
case TreeMaps of
#{BTNodeID := BTree} ->
BTree;
#{} ->
undefined
end.
%% @doc
%%
-spec get_btree_node(bt_node_id(), tree_nodes()) -> uninit_bt_node()|undefined.
get_btree_node(BTNodeID, TreeNodeMaps) ->
case TreeNodeMaps of
#{BTNodeID := BTNode} ->
BTNode;
#{} ->
undefined
end.
%% @doc
%%
-spec init_btree_by_title(string(), #{string() => bt_node_id()}, #{bt_node_id() => btree()}, tree_nodes()) -> {ok, bt_uid()}|{error, term()}.
init_btree_by_title(Title, TitleMaps, TreeMaps, TreeNodeMaps) ->
BTNodeID = get_btree_id_by_title(Title, TitleMaps),
init_btree(BTNodeID, TreeMaps, TreeNodeMaps).
%% @doc
%%
-spec init_btree(bt_node_id(), #{bt_node_id() => btree()}, tree_nodes()) -> {ok, bt_uid()}|{error, term()}.
init_btree(BTNodeID, TreeMaps, TreeNodeMaps) ->
#{root := Root} = behavior_tree:get_btree(BTNodeID, TreeMaps),
#{id := NodeID} = behavior_tree:get_btree_node(Root, TreeNodeMaps),
base_node:do_init(undefined, NodeID, TreeMaps, TreeNodeMaps).
%% @doc
%%
-spec execute(bt_uid(), bt_state()) -> {bt_status(), bt_state()}.
execute(NodeID, BTState) ->
BTState1 = blackboard:init_maps_keys(BTState),
case base_node:execute(NodeID, BTState1) of
{?BT_RUNNING, BTState2} ->
{?BT_RUNNING, BTState2};
{BTStatus, BTState2} ->
BTState3 = blackboard:erase_btree(BTState2),
{BTStatus, BTState3}
end.
%% @doc
%%
-spec execute_child(bt_uid(), bt_state()) -> {bt_status(), bt_state()}.
execute_child(NodeID, BTState) ->
base_node:execute(NodeID, BTState).
%% @doc
%%
-spec close_btree_node(bt_uid(), bt_state()) -> bt_state().
close_btree_node(NodeID, BTState) ->
BTState1 = do_close_btree_node(NodeID, BTState),
blackboard:erase_btree(BTState1).
%% @doc
%%
-spec unload_btree(bt_uid()) -> ok.
unload_btree(NodeID) ->
do_unload_btree_node(NodeID).
%%--------------------------------------------------------------------
%% Internal functions
%%--------------------------------------------------------------------
%%
-spec parse_btree(map()) -> {#{string() => bt_node_id()}, #{bt_node_id() => btree()}, tree_nodes()}.
parse_btree(#{<<"trees">> := Trees}) ->
parse_btree_1(Trees, #{}, #{}, #{}).
parse_btree_1([Node | T], TitleMaps, TreeMaps, TreeNodeMaps) ->
#{<<"id">> := ID, <<"title">> := Title, <<"root">> := Root, <<"nodes">> := Nodes} = Node,
Iterator = maps:iterator(Nodes),
{TreeNodes, TreeNodeMaps1} = parse_btree_node(maps:next(Iterator), #{}, TreeNodeMaps),
BTree = #{
id => ID,
root => Root,
properties => parse_properties(Node),
tree_nodes => TreeNodes
},
parse_btree_1(T, TitleMaps#{Title => ID}, TreeMaps#{ID => BTree}, TreeNodeMaps1);
parse_btree_1([], TitleMaps, TreeMaps, TreeNodeMaps) ->
{TitleMaps, TreeMaps, TreeNodeMaps}.
%%
-spec parse_btree_node({bt_node_id(), map(), maps:iterator()}|none, tree_nodes(), tree_nodes()) -> {tree_nodes(), tree_nodes()}.
parse_btree_node({ID, Node, NextIterator}, TreeNodes, TreeNodeMaps) ->
#{<<"name">> := Name, <<"category">> := Category} = Node,
case Category of
<<"tree">> ->
Name1 = Name;
_ ->
Name1 = name_convert(Name)
end,
TreeNode = #{
id => ID,
name => Name1,
category => binary_to_atom(Category, utf8),
properties => parse_properties(Node),
children => parse_children(Node)
},
parse_btree_node(maps:next(NextIterator), TreeNodes#{ID => TreeNode}, TreeNodeMaps#{ID => TreeNode});
parse_btree_node(none, TreeNodes, TreeNodeMaps) ->
{TreeNodes, TreeNodeMaps}.
%%
%% key会转为下划线分割小写atomvalue保持不变
-spec parse_properties(map()) -> properties().
parse_properties(#{<<"properties">> := Properties} = _Node) ->
Fun = fun(K, V, Map) -> Map#{name_convert(K) => V} end,
maps:fold(Fun, #{}, Properties).
%%
-spec parse_children(map()) -> [bt_node_id()].
parse_children(#{<<"child">> := Child} = _Node) ->
[Child];
parse_children(#{<<"children">> := Children} = _Node) ->
Children;
parse_children(_Node) ->
[].
%%
%% PlayerID -> player_id Name -> name
-spec name_convert(unicode:chardata()) -> atom().
name_convert(Name) ->
case name_convert_1(Name) of
<<"_"/utf8, NewName/binary>> ->
binary_to_atom(NewName, utf8);
NewName ->
binary_to_atom(NewName, utf8)
end.
name_convert_1(<<C/utf8, Other/binary>>) when $A =< C, C =< $Z ->
Other1 = name_convert_1(Other),
<<"_"/utf8, (C + 32)/utf8, Other1/binary>>;
name_convert_1(<<C/utf8, Other/binary>>) ->
Other1 = name_convert_1(Other),
<<C/utf8, Other1/binary>>;
name_convert_1(<<>>) ->
<<>>.
%%
-spec do_close_btree_node(bt_uid(), bt_state()) -> bt_state().
do_close_btree_node(NodeID, BTState) ->
BTNode = blackboard:get_btree_node(NodeID),
base_node:do_close(BTNode, BTState).
%%
-spec do_unload_btree_node(bt_uid()) -> ok.
do_unload_btree_node(NodeID) ->
#{children := Children} = blackboard:get_btree_node(NodeID),
do_unload_btree_node_1(Children),
blackboard:erase_btree_node(NodeID),
ok.
do_unload_btree_node_1([NodeID | T]) ->
do_unload_btree_node(NodeID),
do_unload_btree_node_1(T);
do_unload_btree_node_1([]) ->
ok.

+ 157
- 0
src/core/blackboard.erl 查看文件

@ -0,0 +1,157 @@
-module(blackboard).
%%--------------------------------------------------------------------
%% include
%%--------------------------------------------------------------------
-include("behavior3.hrl").
%%--------------------------------------------------------------------
%% export API
%%--------------------------------------------------------------------
-export([
get_btree_node/1, set_btree_node/1, erase_btree_node/1,
set_global/3, set/4,
get_global/2, get_global/3, get/3, get/4,
remove/2, remove/3
]).
%% Internal API
-export([init_maps_keys/1, erase_node_local/2, erase_btree/1]).
%%--------------------------------------------------------------------
%% API functions
%%--------------------------------------------------------------------
%% @doc
%%
-spec get_btree_node(bt_uid()) -> bt_node()|undefined.
get_btree_node(NodeID) ->
get({btree_node, NodeID}).
%% @doc
%%
-spec set_btree_node(bt_node()) -> ok.
set_btree_node(#{id := NodeID} = BTNode) ->
put({btree_node, NodeID}, BTNode),
ok.
%% @doc
%%
-spec erase_btree_node(bt_uid()) -> bt_node().
erase_btree_node(NodeID) ->
erase({btree_node, NodeID}).
%% @doc
%%
-spec set_global(term(), term(), bt_state()) -> bt_state().
set_global(Key, Value, #{'$global_maps' := GlobalMaps} = BTState) ->
BTState#{'$global_maps' := GlobalMaps#{Key => Value}}.
%% @doc
%%
-spec set(term(), term(), bt_uid(), bt_state()) -> bt_state().
set(Key, Value, NodeID, #{'$local_maps' := LocalMaps} = BTState) ->
case LocalMaps of
#{NodeID := ValueMaps} ->
LocalMaps1 = LocalMaps#{NodeID := ValueMaps#{Key => Value}};
#{} ->
LocalMaps1 = LocalMaps#{NodeID => #{Key => Value}}
end,
BTState#{'$local_maps' := LocalMaps1}.
%% @doc
%%
-spec get_global(term(), bt_state()) -> term()|undefined.
get_global(Key, BTState) ->
case BTState of
#{'$global_maps' := #{Key := Value}} ->
Value;
#{} ->
undefined
end.
%% @doc
%% Default
-spec get_global(term(), term(), bt_state()) -> term().
get_global(Key, Default, BTState) ->
case BTState of
#{'$global_maps' := #{Key := Value}} ->
Value;
#{} ->
Default
end.
%% @doc
%%
-spec get(term(), bt_uid(), bt_state()) -> term()|undefined.
get(Key, NodeID, BTState) ->
case BTState of
#{'$local_maps' := #{NodeID := #{Key := Value}}} ->
Value;
#{} ->
undefined
end.
%% @doc
%% Default
-spec get(term(), bt_uid(), term(), bt_state()) -> term().
get(Key, NodeID, Default, BTState) ->
case BTState of
#{'$local_maps' := #{NodeID := #{Key := Value}}} ->
Value;
#{} ->
Default
end.
%% @doc
%%
-spec remove(term(), bt_state()) -> bt_state().
remove(Key, #{'$global_maps' := GlobalMaps} = BTState) ->
BTState#{'$global_maps' := maps:remove(Key, GlobalMaps)}.
%% @doc
%%
-spec remove(term(), bt_uid(), bt_state()) -> bt_state().
remove(Key, NodeID, #{'$local_maps' := LocalMaps} = BTState) ->
case LocalMaps of
#{NodeID := ValueMap} ->
LocalMaps1 = LocalMaps#{NodeID := maps:remove(Key, ValueMap)},
BTState#{'$local_maps' := LocalMaps1};
#{} ->
BTState
end.
%%--------------------------------------------------------------------
%% Internal functions
%%--------------------------------------------------------------------
%%
-spec init_maps_keys(bt_state()) -> bt_state().
init_maps_keys(BTState) ->
BTState1 = init_global(BTState),
init_local(BTState1).
init_global(BTState) ->
case maps:is_key('$global_maps', BTState) of
true ->
BTState;
false ->
BTState#{'$global_maps' => #{}}
end.
init_local(BTState) ->
case maps:is_key('$local_maps', BTState) of
true ->
BTState;
false ->
BTState#{'$local_maps' => #{}}
end.
%%
-spec erase_node_local(bt_uid(), bt_state()) -> bt_state().
erase_node_local(NodeID, #{'$local_maps' := LocalMaps} = BTState) ->
BTState#{'$local_maps' := maps:remove(NodeID, LocalMaps)}.
%%
-spec erase_btree(bt_state()) -> bt_state().
erase_btree(BTState) ->
BTState1 = maps:remove('$global_maps', BTState),
maps:remove('$local_maps', BTState1).

+ 31
- 0
src/decorator/inverter.erl 查看文件

@ -0,0 +1,31 @@
-module(inverter).
%%--------------------------------------------------------------------
%% include
%%--------------------------------------------------------------------
-include("behavior3.hrl").
%%--------------------------------------------------------------------
%% export API
%%--------------------------------------------------------------------
-export([tick/2]).
%%--------------------------------------------------------------------
%% API functions
%%--------------------------------------------------------------------
-spec tick(bt_node(), bt_state()) -> {bt_status(), bt_state()}.
tick(#{children := [ChildID]} = _BTNode, BTState) ->
case base_node:execute(ChildID, BTState) of
{?BT_SUCCESS, BTState1} ->
{?BT_FAILURE, BTState1};
{?BT_FAILURE, BTState1} ->
{?BT_SUCCESS, BTState1};
{BTStatus, BTState1} ->
{BTStatus, BTState1}
end;
tick(_BTNode, BTState) ->
{?BT_ERROR, BTState}.
%%--------------------------------------------------------------------
%% Internal functions
%%--------------------------------------------------------------------

+ 43
- 0
src/decorator/limiter.erl 查看文件

@ -0,0 +1,43 @@
-module(limiter).
%%--------------------------------------------------------------------
%% include
%%--------------------------------------------------------------------
-include("behavior3.hrl").
%%--------------------------------------------------------------------
%% export API
%%--------------------------------------------------------------------
-export([open/2, tick/2, close/2]).
%%--------------------------------------------------------------------
%% API functions
%%--------------------------------------------------------------------
-spec open(bt_node(), bt_state()) -> bt_state().
open(#{id := ID} = _BTNode, BTState) ->
blackboard:set(i, 0, ID, BTState).
-spec tick(bt_node(), bt_state()) -> {bt_status(), bt_state()}.
tick(#{id := ID, children := [ChildID], properties := #{max_loop := MaxLoop}} = _BTNode, BTState) ->
case blackboard:get(i, ID, BTState) of
I when I < MaxLoop ->
case base_node:execute(ChildID, BTState) of
{BTStatus, BTState1} when BTStatus =:= ?BT_SUCCESS orelse BTStatus =:= ?BT_FAILURE ->
BTState2 = blackboard:set(i, I + 1, ID, BTState1),
{BTStatus, BTState2};
{BTStatus, BTState1} ->
{BTStatus, BTState1}
end;
_I ->
{?BT_FAILURE, BTState}
end;
tick(_BTNode, BTState) ->
{?BT_ERROR, BTState}.
-spec close(bt_node(), bt_state()) -> bt_state().
close(#{id := ID} = _BTNode, BTState) ->
blackboard:remove(i, ID, BTState).
%%--------------------------------------------------------------------
%% Internal functions
%%--------------------------------------------------------------------

+ 39
- 0
src/decorator/max_time.erl 查看文件

@ -0,0 +1,39 @@
-module(max_time).
%%--------------------------------------------------------------------
%% include
%%--------------------------------------------------------------------
-include("behavior3.hrl").
%%--------------------------------------------------------------------
%% export API
%%--------------------------------------------------------------------
-export([open/2, tick/2, close/2]).
%%--------------------------------------------------------------------
%% API functions
%%--------------------------------------------------------------------
-spec open(bt_node(), bt_state()) -> bt_state().
open(#{id := ID} = _BTNode, BTState) ->
blackboard:set(start_time, erlang:system_time(millisecond), ID, BTState).
-spec tick(bt_node(), bt_state()) -> {bt_status(), bt_state()}.
tick(#{id := ID, children := [ChildID] , properties := #{max_time := MaxTime}} = _BTNode, BTState) ->
StartTime = blackboard:get(start_time, ID, BTState),
{BTStatus, BTState1} = base_node:execute(ChildID, BTState),
case MaxTime > erlang:system_time(millisecond) - StartTime of
true ->
{BTStatus, BTState1};
false ->
{?BT_FAILURE, BTState}
end;
tick(_BTNode, BTState) ->
{?BT_ERROR, BTState}.
-spec close(bt_node(), bt_state()) -> bt_state().
close(#{id := ID} = _BTNode, BTState) ->
blackboard:remove(start_time, ID, BTState).
%%--------------------------------------------------------------------
%% Internal functions
%%--------------------------------------------------------------------

+ 44
- 0
src/decorator/repeat_until_failure.erl 查看文件

@ -0,0 +1,44 @@
-module(repeat_until_failure).
%%--------------------------------------------------------------------
%% include
%%--------------------------------------------------------------------
-include("behavior3.hrl").
%%--------------------------------------------------------------------
%% export API
%%--------------------------------------------------------------------
-export([open/2, tick/2, close/2]).
%%--------------------------------------------------------------------
%% API functions
%%--------------------------------------------------------------------
-spec open(bt_node(), bt_state()) -> bt_state().
open(#{id := ID} = _BTNode, BTState) ->
blackboard:set(i, 0, ID, BTState).
-spec tick(bt_node(), bt_state()) -> {bt_status(), bt_state()}.
tick(#{id := ID, children := [ChildID], properties := #{max_loop := MaxLoop}} = _BTNode, BTState) ->
I = blackboard:get(i, ID, BTState),
{I1, BTStatus, BTState1} = tick_1(MaxLoop, I, ChildID, ?BT_ERROR, BTState),
BTState2 = blackboard:set(i, I1, ID, BTState1),
{BTStatus, BTState2};
tick(_BTNode, BTState) ->
{?BT_ERROR, BTState}.
-spec close(bt_node(), bt_state()) -> bt_state().
close(#{id := ID} = _BTNode, BTState) ->
blackboard:remove(i, ID, BTState).
%%--------------------------------------------------------------------
%% Internal functions
%%--------------------------------------------------------------------
tick_1(MaxLoop, I, ChildID, _BTStatus, BTState) when MaxLoop < 0 orelse I < MaxLoop ->
case base_node:execute(ChildID, BTState) of
{?BT_SUCCESS, BTState1} ->
tick_1(MaxLoop, I + 1, ChildID, ?BT_SUCCESS, BTState1);
{BTStatus, BTState1} ->
{I, BTStatus, BTState1}
end;
tick_1(_MaxLoop, I, _ChildID, BTStatus, BTState) ->
{I, BTStatus, BTState}.

+ 45
- 0
src/decorator/repeat_until_success.erl 查看文件

@ -0,0 +1,45 @@
-module(repeat_until_success).
%%--------------------------------------------------------------------
%% include
%%--------------------------------------------------------------------
-include("behavior3.hrl").
%%--------------------------------------------------------------------
%% export API
%%--------------------------------------------------------------------
-export([open/2, tick/2, close/2]).
%%--------------------------------------------------------------------
%% API functions
%%--------------------------------------------------------------------
-spec open(bt_node(), bt_state()) -> bt_state().
open(#{id := ID} = _BTNode, BTState) ->
blackboard:set(i, 0, ID, BTState).
-spec tick(bt_node(), bt_state()) -> {bt_status(), bt_state()}.
tick(#{id := ID, children := [ChildID], properties := #{max_loop := MaxLoop}} = _BTNode, BTState) ->
I = blackboard:get(i, ID, BTState),
{I1, BTStatus, BTState1} = tick_1(MaxLoop, I, ChildID, ?BT_ERROR, BTState),
BTState2 = blackboard:set(i, I1, ID, BTState1),
{BTStatus, BTState2};
tick(_BTNode, BTState) ->
{?BT_ERROR, BTState}.
-spec close(bt_node(), bt_state()) -> bt_state().
close(#{id := ID} = _BTNode, BTState) ->
blackboard:remove(i, ID, BTState).
%%--------------------------------------------------------------------
%% Internal functions
%%--------------------------------------------------------------------
tick_1(MaxLoop, I, ChildID, _BTStatus, BTState) when MaxLoop < 0 orelse I < MaxLoop ->
case base_node:execute(ChildID, BTState) of
{?BT_FAILURE, BTBTState1} ->
tick_1(MaxLoop, I + 1, ChildID, ?BT_FAILURE, BTBTState1);
{BTStatus, BTBTState1} ->
{I, BTStatus, BTBTState1}
end;
tick_1(_MaxLoop, I, _ChildID, BTStatus, BTState) ->
{I, BTStatus, BTState}.

+ 44
- 0
src/decorator/repeater.erl 查看文件

@ -0,0 +1,44 @@
-module(repeater).
%%--------------------------------------------------------------------
%% include
%%--------------------------------------------------------------------
-include("behavior3.hrl").
%%--------------------------------------------------------------------
%% export API
%%--------------------------------------------------------------------
-export([open/2, tick/2, close/2]).
%%--------------------------------------------------------------------
%% API functions
%%--------------------------------------------------------------------
-spec open(bt_node(), bt_state()) -> bt_state().
open(#{id := ID} = _BTNode, BTState) ->
blackboard:set(i, 0, ID, BTState).
-spec tick(bt_node(), bt_state()) -> {bt_status(), bt_state()}.
tick(#{id := ID, children := [ChildID], properties := #{max_loop := MaxLoop}} = _BTNode, BTState) ->
I = blackboard:get(i, ID, BTState),
{I1, BTStatus, BTState1} = tick_1(MaxLoop, I, ChildID, ?BT_SUCCESS, BTState),
BTState2 = blackboard:set(i, I1, ID, BTState1),
{BTStatus, BTState2};
tick(_BTNode, BTState) ->
{?BT_ERROR, BTState}.
-spec close(bt_node(), bt_state()) -> bt_state().
close(#{id := ID} = _BTNode, BTState) ->
blackboard:remove(i, ID, BTState).
%%--------------------------------------------------------------------
%% Internal functions
%%--------------------------------------------------------------------
tick_1(MaxLoop, I, ChildID, _BTStatus, BTState) when MaxLoop < 0 orelse I < MaxLoop ->
case base_node:execute(ChildID, BTState) of
{BTStatus, BTState1} when BTStatus =:= ?BT_SUCCESS; BTStatus =:= ?BT_FAILURE ->
tick_1(MaxLoop, I + 1, ChildID, BTStatus, BTState1);
{BTStatus, BTState1} ->
{I, BTStatus, BTState1}
end;
tick_1(_MaxLoop, I, _ChildID, BTStatus, BTState) ->
{I, BTStatus, BTState}.

+ 10
- 0
src/eBhv3.app.src 查看文件

@ -0,0 +1,10 @@
{application, eBhv3,
[{description, "An OTP library"},
{vsn, "0.1.0"},
{registered, []},
{applications, [kernel, stdlib]},
{env,[]},
{modules, []},
{licenses, ["MIT"]},
{links, []}
]}.

+ 3
- 0
src/eBhv3.erl 查看文件

@ -0,0 +1,3 @@
-module(eBhv3).
-export([]).

Loading…
取消
儲存