@ -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 |
@ -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. |
@ -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) |
@ -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}. |
@ -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}. |
@ -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}. |
@ -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}. |
@ -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}. |
@ -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. |
@ -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. | |||||
@ -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. |
@ -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}. |
@ -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}. |
@ -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}. |
@ -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)}}. |
@ -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}. |
@ -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}. |
@ -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}. |
@ -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. |
@ -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. |
@ -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. |
@ -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. |
@ -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. |
@ -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. |
@ -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. |
@ -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. |
@ -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. |
@ -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. |
@ -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. |
@ -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). |
@ -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}). |
@ -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. |
@ -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. |
@ -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 | |||||
]}. |
@ -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 | |||||
%%-------------------------------------------------------------------- |
@ -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 | |||||
%%-------------------------------------------------------------------- |
@ -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 | |||||
%%-------------------------------------------------------------------- |
@ -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 | |||||
%%-------------------------------------------------------------------- |
@ -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 | |||||
%%-------------------------------------------------------------------- |
@ -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}. |
@ -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}. |
@ -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}. |
@ -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}. |
@ -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/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. |
@ -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会转为下划线分割小写atom,value保持不变 | |||||
-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. |
@ -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). |
@ -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 | |||||
%%-------------------------------------------------------------------- |
@ -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 | |||||
%%-------------------------------------------------------------------- |
@ -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 | |||||
%%-------------------------------------------------------------------- |
@ -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}. |
@ -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}. |
@ -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}. |
@ -0,0 +1,10 @@ | |||||
{application, eBhv3, | |||||
[{description, "An OTP library"}, | |||||
{vsn, "0.1.0"}, | |||||
{registered, []}, | |||||
{applications, [kernel, stdlib]}, | |||||
{env,[]}, | |||||
{modules, []}, | |||||
{licenses, ["MIT"]}, | |||||
{links, []} | |||||
]}. |
@ -0,0 +1,3 @@ | |||||
-module(eBhv3). | |||||
-export([]). |