├── .gitattributes
├── Behaviour Tree Friends.yyp
├── builded.md
├── example.md
├── example_running.gif
├── example_tree.jpg
├── nodes.md
├── objects
├── o_bt_tester
│ ├── Create_0.gml
│ └── Step_0.gml
├── o_enemy
│ ├── Create_0.gml
│ ├── Draw_0.gml
│ ├── Step_0.gml
│ └── o_enemy.yy
└── o_mouse_follower
│ ├── Create_0.gml
│ ├── Draw_0.gml
│ ├── Step_0.gml
│ └── o_mouse_follower.yy
├── options
├── amazonfire
│ └── options_amazonfire.yy
├── android
│ └── options_android.yy
├── html5
│ └── options_html5.yy
├── ios
│ └── options_ios.yy
├── linux
│ └── options_linux.yy
├── mac
│ └── options_mac.yy
├── main
│ └── options_main.yy
├── tvos
│ └── options_tvos.yy
└── windows
│ └── options_windows.yy
├── readme.md
├── readme_logo.png
├── rooms
└── Room1
│ └── Room1.yy
├── scripts
├── BehaviourTree
│ ├── BehaviourTree.gml
│ └── BehaviourTree.yy
├── __BTConfig
│ ├── __BTConfig.gml
│ └── __BTConfig.yy
└── behavior_chase
│ ├── BT_test.yy
│ ├── behavior_chase.gml
│ └── behavior_chase.yy
└── sprites
├── Sprite1
├── Sprite1.yy
├── d47c3ffb-aa12-4455-8cb7-22cd1be52178.png
└── layers
│ └── d47c3ffb-aa12-4455-8cb7-22cd1be52178
│ └── 04284397-b0cd-4c59-8518-3ed983155443.png
└── Sprite2
├── 9c751507-4992-43c3-9dde-795606e8c68d.png
├── Sprite2.yy
└── layers
└── 9c751507-4992-43c3-9dde-795606e8c68d
└── 4fdad78a-d4c5-4cdc-8b18-9a23df9ac50a.png
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Re-classify .yy files (for GMS 2 projects)
2 | *.yy linguist-language=GML
3 |
--------------------------------------------------------------------------------
/Behaviour Tree Friends.yyp:
--------------------------------------------------------------------------------
1 | {
2 | "resources": [
3 | {"id":{"name":"behavior_chase","path":"scripts/behavior_chase/behavior_chase.yy",},"order":3,},
4 | {"id":{"name":"Sprite1","path":"sprites/Sprite1/Sprite1.yy",},"order":4,},
5 | {"id":{"name":"o_enemy","path":"objects/o_enemy/o_enemy.yy",},"order":2,},
6 | {"id":{"name":"Sprite2","path":"sprites/Sprite2/Sprite2.yy",},"order":5,},
7 | {"id":{"name":"BehaviourTree","path":"scripts/BehaviourTree/BehaviourTree.yy",},"order":2,},
8 | {"id":{"name":"o_mouse_follower","path":"objects/o_mouse_follower/o_mouse_follower.yy",},"order":1,},
9 | {"id":{"name":"__BTConfig","path":"scripts/__BTConfig/__BTConfig.yy",},"order":0,},
10 | {"id":{"name":"Room1","path":"rooms/Room1/Room1.yy",},"order":0,},
11 | ],
12 | "Options": [
13 | {"name":"Amazon Fire","path":"options/amazonfire/options_amazonfire.yy",},
14 | {"name":"Android","path":"options/android/options_android.yy",},
15 | {"name":"HTML5","path":"options/html5/options_html5.yy",},
16 | {"name":"iOS","path":"options/ios/options_ios.yy",},
17 | {"name":"Linux","path":"options/linux/options_linux.yy",},
18 | {"name":"macOS","path":"options/mac/options_mac.yy",},
19 | {"name":"Main","path":"options/main/options_main.yy",},
20 | {"name":"tvOS","path":"options/tvos/options_tvos.yy",},
21 | {"name":"Windows","path":"options/windows/options_windows.yy",},
22 | ],
23 | "isDnDProject": false,
24 | "isEcma": false,
25 | "tutorialPath": "",
26 | "configs": {
27 | "name": "Default",
28 | "children": [],
29 | },
30 | "RoomOrderNodes": [
31 | {"roomId":{"name":"Room1","path":"rooms/Room1/Room1.yy",},},
32 | ],
33 | "Folders": [
34 | {"folderPath":"folders/GMLBehaviorTree.yy","order":11,"resourceVersion":"1.0","name":"GMLBehaviorTree","tags":[],"resourceType":"GMFolder",},
35 | {"folderPath":"folders/example.yy","order":12,"resourceVersion":"1.0","name":"example","tags":[],"resourceType":"GMFolder",},
36 | ],
37 | "AudioGroups": [
38 | {"targets":-1,"resourceVersion":"1.3","name":"audiogroup_default","resourceType":"GMAudioGroup",},
39 | ],
40 | "TextureGroups": [
41 | {"isScaled":true,"autocrop":true,"border":2,"mipsToGenerate":0,"groupParent":null,"targets":-1,"resourceVersion":"1.3","name":"Default","resourceType":"GMTextureGroup",},
42 | ],
43 | "IncludedFiles": [],
44 | "MetaData": {
45 | "IDEVersion": "2.3.3.570",
46 | },
47 | "resourceVersion": "1.4",
48 | "name": "Behaviour Tree Friends",
49 | "tags": [],
50 | "resourceType": "GMProject",
51 | }
--------------------------------------------------------------------------------
/builded.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | # GML Behavior Tree
8 |
9 | The classic Behavior Tree to GMS 2.3+
10 |
11 | ## How its builded
12 |
13 | Our implementation is builded all above the same BTreeNode struct:
14 |
15 | ### Variables:
16 |
17 | | Name | Description |
18 | |------------------|------------------------------------|
19 | | name | The node name |
20 | | status | The Current Status of the node |
21 | | visited | If it already been visited or not |
22 | | children | The node children |
23 | | children_arr_len | The actual children array length |
24 | | black_board_ref | The reference to blackboard struct |
25 |
26 | ### Methods:
27 |
28 | | Name | Description |
29 | |------------------------|----------------------------------------------------------------------------|
30 | | Init() | Call this for some extra configs when visiting the node for the first time |
31 | | Process() | Called in every step of the game. Need to return one of the BTStates enum |
32 | | ChildAdd(BTreeNode) | Used to add a child to the node |
33 | | NodeProcess(BTreeNode) | Process other nodes |
34 |
--------------------------------------------------------------------------------
/example.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | # GML Behavior Tree
8 |
9 | The classic Behavior Tree to GMS 2.3+
10 |
11 |
12 | ## How to use
13 |
14 | You can see this example with the projects file.
15 | We choose no enter in game maker details like objects, etc.
16 | it's just the Behavior tree use.
17 |
18 | This example implements this tree:
19 |
20 | 
21 |
22 | > before mount your behavior tree try figure out about it and make a diagram like that
23 |
24 | First of all we create our Leaf nodes inside a script file
25 |
26 | ``` gml
27 | behavior_chase.gml
28 |
29 | /// @param obj_ref
30 | /// @param range
31 | function TaskInstanceInRange(obj, range) : BTreeLeaf() constructor {
32 | name = "Task Instance In Range";
33 | object_find = obj;
34 | range_limit = range;
35 |
36 | instance_ref = noone;
37 |
38 | /// @override
39 | static Init = function(){
40 | instance_ref = instance_find(object_find, 0);
41 | }
42 |
43 | /// @override
44 | static Process = function(){
45 | if(instance_exists(instance_ref) && point_distance(black_board_ref.user.x, black_board_ref.user.y, instance_ref.x, instance_ref.y) <= range_limit)
46 | return BTStates.Success;
47 | else
48 | return BTStates.Failure;
49 | }
50 | }
51 | ```
52 |
53 | > remember: the process need to return a BTStates state
54 |
55 | See that in the Init method we start the behavior variables while in Process method we run the behavior. The tree general data stay in the blackboard.
56 | Notice that the behavior can receive some parameters to promote reuses
57 |
58 | After the TaskInstanceInRange is ready we create the other behaviors:
59 |
60 |
61 | Click here to see some code
62 |
63 | ``` gml
64 | behavior_chase.gml
65 |
66 | ...
67 | /// @param speed
68 | /// @param timer_secs
69 | function TaskPatrolSimple(speed, timer_secs) : BTreeLeaf() constructor {
70 | name = "Taks Patrol Simple";
71 | patrol_speed = speed;
72 |
73 | patrol_spd_x = 0;
74 | patrol_spd_y = 0;
75 |
76 | patrol_direction = 0;
77 | patrol_timer_max = timer_secs * room_speed;
78 | patrol_timer = 0;
79 |
80 | /// @override
81 | static Process = function(){
82 | if(--patrol_timer <= 0){
83 | patrol_direction = irandom(360);
84 | patrol_spd_x = lengthdir_x(patrol_speed, patrol_direction);
85 | patrol_spd_y = lengthdir_y(patrol_speed, patrol_direction);
86 | patrol_timer = patrol_timer_max;
87 | }
88 |
89 | black_board_ref.user.x += patrol_spd_x;
90 | black_board_ref.user.y += patrol_spd_y;
91 | return BTStates.Success;
92 | }
93 | }
94 |
95 | /// @param instance_chase
96 | /// @param speed_chase
97 | /// @param distance_max
98 | /// @param distance_min
99 | function TaskChaseInstance(instance_chase, speed_chase, distance_max, distance_min) : BTreeLeaf() constructor{
100 | name = "Task Chase Instance";
101 |
102 | chase_speed = speed_chase;
103 | instance_to_chase = instance_chase;
104 | distance_maximun_to_stop_chase = distance_max;
105 | distance_minimun_to_stop_chase = distance_min;
106 |
107 | ///@override
108 | static Process = function(){
109 | if(instance_exists(instance_to_chase)){
110 |
111 | // Check Stop chasing
112 | var _dist = point_distance(black_board_ref.user.x, black_board_ref.user.y, instance_to_chase.x, instance_to_chase.y);
113 | if(_dist <= distance_minimun_to_stop_chase)
114 | return BTStates.Success;
115 | else if (_dist >= distance_maximun_to_stop_chase)
116 | return BTStates.Failure;
117 | else {
118 | // Moving towards chasing
119 | var _dir = point_direction(black_board_ref.user.x, black_board_ref.user.y, instance_to_chase.x, instance_to_chase.y);
120 | black_board_ref.user.x += lengthdir_x(chase_speed, _dir);
121 | black_board_ref.user.y += lengthdir_y(chase_speed, _dir);
122 |
123 | return BTStates.Running;
124 | }
125 | }
126 | else
127 | return BTStates.Failure
128 |
129 | }
130 |
131 | }
132 |
133 | /// @param instance_target
134 | /// @param secs_between_hits
135 | function TaskMeleeHitTarget(instance_target, secs_preparation) : BTreeLeaf() constructor{
136 | name = "Task Melee Hit on Target";
137 |
138 | target_hit = instance_target;
139 | time_preparation_max = secs_preparation * room_speed;
140 | time_preparation = 0;
141 |
142 | /// @override
143 | static Process = function(){
144 | if(!instance_exists(target_hit)) return BTStates.Failure;
145 | if(++time_preparation >= time_preparation_max){
146 | time_preparation = 0;
147 | target_hit.life -= 10;
148 | return BTStates.Success;
149 | }
150 | else
151 | return BTStates.Running;
152 | }
153 | }
154 | ```
155 |
156 |
157 | With our behaviors done we can finally make our tree based on the diagram.
158 |
159 | To mount the tree we first instantiate the nodes
160 |
161 | ``` gml
162 | o_enemy -> create
163 |
164 | /// @desc bt Start
165 | bt_root = new BTreeRoot(id);
166 |
167 | // ---
168 | var _selector_root = new BTreeSelector();
169 | var _sequence_chase = new BTreeSequence();
170 | var _chase_in_range = new TaskInstanceInRange(o_mouse_follower, 200);
171 | var _chase_behave = new TaskChaseInstance(o_mouse_follower, 2.5, 400, 64);
172 | var _succeder = new BTreeSucceeder();
173 | var _hit = new TaskMeleeHitTarget(o_mouse_follower, 1.5);
174 | ```
175 |
176 | Notice that the BTreeRoot node receives the object id as parameter. With this the blackboard.user will point directly to our instance.
177 |
178 | The other nodes receive some configurations like range and speed.
179 |
180 | With all our nodes created, we can mount our tree just like the diagram using the node.addChild() method:
181 |
182 | ``` gml
183 | o_enemy -> create
184 |
185 | ...
186 | bt_root.ChildAdd(_selector_root);
187 |
188 | _sequence_chase.ChildAdd(_chase_in_range);
189 | _sequence_chase.ChildAdd(_chase_behave);
190 | _sequence_chase.ChildAdd(_succeder);
191 | _succeder.ChildAdd(_hit);
192 |
193 | _selector_root.ChildAdd(_sequence_chase);
194 | _selector_root.ChildAdd(_patrol);
195 | ```
196 |
197 | With our tree ready we just need to initialize it with
198 |
199 | ``` gml
200 | o_enemy -> create
201 |
202 | ...
203 | bt_root.Init();
204 | ```
205 |
206 | And to properly process the tree add this on the step event
207 |
208 | ``` gml
209 | o_enemy -> step
210 |
211 | bt_root.Process();
212 | ```
213 |
214 | And this is the final result
215 |
216 | 
217 |
218 | ## References
219 |
220 | - https://en.wikipedia.org/wiki/Behavior_tree_(artificial_intelligence,_robotics_and_control)
221 | - https://www.gamasutra.com/blogs/ChrisSimpson/20140717/221339/Behavior_trees_for_AI_How_they_work.php
222 |
--------------------------------------------------------------------------------
/example_running.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vitorestevam/GML-Behavior-Tree/49ae7e350fad426700e59fe7718aa3397a1a410a/example_running.gif
--------------------------------------------------------------------------------
/example_tree.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vitorestevam/GML-Behavior-Tree/49ae7e350fad426700e59fe7718aa3397a1a410a/example_tree.jpg
--------------------------------------------------------------------------------
/nodes.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | # GML Behavior Tree
8 |
9 | The classic Behavior Tree to GMS 2.3+
10 |
11 | ## How our nodes works
12 |
13 |
14 | ### Composites
15 |
16 | A composite is a node that can have more than one child. They will process one or more of them children.
17 |
18 | | Name | Description |
19 | |------------------|------------------------------------|
20 | | Sequence | Process each child and when that return Success Process the next one|
21 | | Selector | Process each child, but only process the next if this actual returns Failure|
22 |
23 |
24 | ### Decorators
25 |
26 | A decorator is a node that can have only one child. They will process this child and transform the received status
27 |
28 | | Name | Description |
29 | |------------------|------------------------------------|
30 | | Succeeder | Process the child and always return Success|
31 | | Inverter | Process the child and invert the received Status|
32 |
33 | ### Leafs
34 |
35 | A leaf node is a node that will process the behavior. This need to return one of the BTStates enum.
36 |
37 | You can see details about how to create behaviors in [example.md](./example.md)
38 |
39 | ### Root
40 |
41 | The Root node is a custom node that will start our tree. Is responsible by startup the tree, init our Blackboard and some other configs.
42 |
43 | ## References
44 |
45 | - https://www.gamasutra.com/blogs/ChrisSimpson/20140717/221339/Behavior_trees_for_AI_How_they_work.php
46 |
--------------------------------------------------------------------------------
/objects/o_bt_tester/Create_0.gml:
--------------------------------------------------------------------------------
1 | b_tree = new BTreeRoot(id);
2 |
3 | b_tree.ChildAdd(find_and_chase_bt());
4 |
5 | b_tree.Init();
--------------------------------------------------------------------------------
/objects/o_bt_tester/Step_0.gml:
--------------------------------------------------------------------------------
1 |
2 | b_tree.Process();
--------------------------------------------------------------------------------
/objects/o_enemy/Create_0.gml:
--------------------------------------------------------------------------------
1 | /// @desc bt Start
2 | bt_root = new BTreeRoot(id);
3 |
4 | // ---
5 | var _selector_root = new BTreeSelector();
6 | var _sequence_chase = new BTreeSequence();
7 | var _chase_in_range = new TaskInstanceInRange(o_mouse_follower, 200);
8 | var _chase_behave = new TaskChaseInstance(o_mouse_follower, 2.5, 400, 64);
9 | var _succeder = new BTreeSucceeder();
10 | var _hit = new TaskMeleeHitTarget(o_mouse_follower, 1.5);
11 | var _patrol = new TaskPatrolSimple(1, 2);
12 |
13 | bt_root.ChildAdd(_selector_root);
14 |
15 | _sequence_chase.ChildAdd(_chase_in_range);
16 | _sequence_chase.ChildAdd(_chase_behave);
17 | _sequence_chase.ChildAdd(_succeder);
18 | _succeder.ChildAdd(_hit);
19 |
20 | _selector_root.ChildAdd(_sequence_chase);
21 | _selector_root.ChildAdd(_patrol);
22 |
23 |
24 | bt_root.Init();
25 |
--------------------------------------------------------------------------------
/objects/o_enemy/Draw_0.gml:
--------------------------------------------------------------------------------
1 | /// @description
2 | draw_self();
3 | draw_circle(x, y, 200, true);
--------------------------------------------------------------------------------
/objects/o_enemy/Step_0.gml:
--------------------------------------------------------------------------------
1 | /// @desc BT Process call
2 | bt_root.Process();
--------------------------------------------------------------------------------
/objects/o_enemy/o_enemy.yy:
--------------------------------------------------------------------------------
1 | {
2 | "spriteId": {
3 | "name": "Sprite1",
4 | "path": "sprites/Sprite1/Sprite1.yy",
5 | },
6 | "solid": false,
7 | "visible": true,
8 | "spriteMaskId": null,
9 | "persistent": false,
10 | "parentObjectId": null,
11 | "physicsObject": false,
12 | "physicsSensor": false,
13 | "physicsShape": 1,
14 | "physicsGroup": 1,
15 | "physicsDensity": 0.5,
16 | "physicsRestitution": 0.1,
17 | "physicsLinearDamping": 0.1,
18 | "physicsAngularDamping": 0.1,
19 | "physicsFriction": 0.2,
20 | "physicsStartAwake": true,
21 | "physicsKinematic": false,
22 | "physicsShapePoints": [],
23 | "eventList": [
24 | {"isDnD":false,"eventNum":0,"eventType":0,"collisionObjectId":null,"resourceVersion":"1.0","name":"","tags":[],"resourceType":"GMEvent",},
25 | {"isDnD":false,"eventNum":0,"eventType":3,"collisionObjectId":null,"resourceVersion":"1.0","name":"","tags":[],"resourceType":"GMEvent",},
26 | {"isDnD":false,"eventNum":0,"eventType":8,"collisionObjectId":null,"resourceVersion":"1.0","name":"","tags":[],"resourceType":"GMEvent",},
27 | ],
28 | "properties": [],
29 | "overriddenProperties": [],
30 | "parent": {
31 | "name": "example",
32 | "path": "folders/example.yy",
33 | },
34 | "resourceVersion": "1.0",
35 | "name": "o_enemy",
36 | "tags": [],
37 | "resourceType": "GMObject",
38 | }
--------------------------------------------------------------------------------
/objects/o_mouse_follower/Create_0.gml:
--------------------------------------------------------------------------------
1 | /// @description
2 | life = 100;
3 |
4 |
--------------------------------------------------------------------------------
/objects/o_mouse_follower/Draw_0.gml:
--------------------------------------------------------------------------------
1 | /// @description Health
2 | draw_self();
3 | draw_healthbar( bbox_left, bbox_top - 32,
4 | bbox_right, bbox_top - 10,
5 | life, c_black, c_red, c_green,
6 | 0, true, true);
7 |
--------------------------------------------------------------------------------
/objects/o_mouse_follower/Step_0.gml:
--------------------------------------------------------------------------------
1 | /// @desc Mouse follow With lerp
2 | x = lerp(x, mouse_x,0.05)
3 | y = lerp(y, mouse_y,0.05)
--------------------------------------------------------------------------------
/objects/o_mouse_follower/o_mouse_follower.yy:
--------------------------------------------------------------------------------
1 | {
2 | "spriteId": {
3 | "name": "Sprite2",
4 | "path": "sprites/Sprite2/Sprite2.yy",
5 | },
6 | "solid": false,
7 | "visible": true,
8 | "spriteMaskId": null,
9 | "persistent": false,
10 | "parentObjectId": null,
11 | "physicsObject": false,
12 | "physicsSensor": false,
13 | "physicsShape": 1,
14 | "physicsGroup": 1,
15 | "physicsDensity": 0.5,
16 | "physicsRestitution": 0.1,
17 | "physicsLinearDamping": 0.1,
18 | "physicsAngularDamping": 0.1,
19 | "physicsFriction": 0.2,
20 | "physicsStartAwake": true,
21 | "physicsKinematic": false,
22 | "physicsShapePoints": [],
23 | "eventList": [
24 | {"isDnD":false,"eventNum":0,"eventType":3,"collisionObjectId":null,"resourceVersion":"1.0","name":"","tags":[],"resourceType":"GMEvent",},
25 | {"isDnD":false,"eventNum":0,"eventType":0,"collisionObjectId":null,"resourceVersion":"1.0","name":"","tags":[],"resourceType":"GMEvent",},
26 | {"isDnD":false,"eventNum":0,"eventType":8,"collisionObjectId":null,"resourceVersion":"1.0","name":"","tags":[],"resourceType":"GMEvent",},
27 | ],
28 | "properties": [
29 | {"varType":3,"value":"True","rangeEnabled":false,"rangeMin":0.0,"rangeMax":10.0,"listItems":[],"multiselect":false,"filters":[],"resourceVersion":"1.0","name":"follow","tags":[],"resourceType":"GMObjectProperty",},
30 | ],
31 | "overriddenProperties": [],
32 | "parent": {
33 | "name": "example",
34 | "path": "folders/example.yy",
35 | },
36 | "resourceVersion": "1.0",
37 | "name": "o_mouse_follower",
38 | "tags": [],
39 | "resourceType": "GMObject",
40 | }
--------------------------------------------------------------------------------
/options/amazonfire/options_amazonfire.yy:
--------------------------------------------------------------------------------
1 | {
2 | "option_amazonfire_sync_android": false,
3 | "option_amazonfire_display_name": "Created with GameMaker Studio 2",
4 | "option_amazonfire_version": "1.0.0.0",
5 | "option_amazonfire_tools_from_version": false,
6 | "option_amazonfire_build_tools": "",
7 | "option_amazonfire_support_lib": "",
8 | "option_amazonfire_target_sdk": "",
9 | "option_amazonfire_minimum_sdk": "",
10 | "option_amazonfire_compile_sdk": "",
11 | "option_amazonfire_package_domain": "com",
12 | "option_amazonfire_package_company": "company",
13 | "option_amazonfire_package_product": "game",
14 | "option_amazonfire_orient_portrait": true,
15 | "option_amazonfire_orient_portrait_flipped": true,
16 | "option_amazonfire_orient_landscape": true,
17 | "option_amazonfire_orient_landscape_flipped": true,
18 | "option_amazonfire_gamepad_support": true,
19 | "option_amazonfire_lint": false,
20 | "option_amazonfire_install_location": 0,
21 | "option_amazonfire_sleep_margin": 4,
22 | "option_amazonfire_splash_screens_landscape": "${base_options_dir}/amazonfire/splash/landscape.png",
23 | "option_amazonfire_splash_screens_portrait": "${base_options_dir}/amazonfire/splash/portrait.png",
24 | "option_amazonfire_splash_time": 0,
25 | "option_amazonfire_launchscreen_fill": 0,
26 | "option_amazonfire_splashscreen_background_colour": 255,
27 | "option_amazonfire_tv_banner": "${base_options_dir}/amazonfire/tv_banner.png",
28 | "option_amazonfire_interpolate_pixels": false,
29 | "option_amazonfire_screen_depth": 0,
30 | "option_amazonfire_scale": 0,
31 | "option_amazonfire_texture_page": "2048x2048",
32 | "option_amazonfire_icon_ldpi": "${base_options_dir}/android/icons/ldpi.png",
33 | "option_amazonfire_icon_mdpi": "${base_options_dir}/android/icons/mdpi.png",
34 | "option_amazonfire_icon_hdpi": "${base_options_dir}/android/icons/hdpi.png",
35 | "option_amazonfire_icon_xhdpi": "${base_options_dir}/android/icons/xhdpi.png",
36 | "option_amazonfire_icon_xxhdpi": "${base_options_dir}/android/icons/xxhdpi.png",
37 | "option_amazonfire_icon_xxxhdpi": "${base_options_dir}/android/icons/xxxhdpi.png",
38 | "option_amazonfire_permission_write_external_storage": false,
39 | "option_amazonfire_permission_read_phone_state": false,
40 | "option_amazonfire_permission_network_state": false,
41 | "option_amazonfire_permission_internet": true,
42 | "option_amazonfire_permission_bluetooth": true,
43 | "option_amazonfire_permission_record_audio": false,
44 | "option_amazonfire_application_tag_inject": "",
45 | "resourceVersion": "1.0",
46 | "name": "Amazon Fire",
47 | "tags": [],
48 | "resourceType": "GMAmazonFireOptions",
49 | }
--------------------------------------------------------------------------------
/options/android/options_android.yy:
--------------------------------------------------------------------------------
1 | {
2 | "option_android_sync_amazon": false,
3 | "option_android_display_name": "Created with GameMaker Studio 2",
4 | "option_android_version": "1.0.0.0",
5 | "option_android_tools_from_version": false,
6 | "option_android_build_tools": "",
7 | "option_android_support_lib": "",
8 | "option_android_target_sdk": "",
9 | "option_android_minimum_sdk": "",
10 | "option_android_compile_sdk": "",
11 | "option_android_package_domain": "com",
12 | "option_android_package_company": "company",
13 | "option_android_package_product": "game",
14 | "option_android_arch_armv7": true,
15 | "option_android_arch_x86": false,
16 | "option_android_arch_arm64": false,
17 | "option_android_arch_x86_64": false,
18 | "option_android_orient_portrait": true,
19 | "option_android_orient_portrait_flipped": true,
20 | "option_android_orient_landscape": true,
21 | "option_android_orient_landscape_flipped": true,
22 | "option_android_gamepad_support": true,
23 | "option_android_lint": false,
24 | "option_android_install_location": 0,
25 | "option_android_sleep_margin": 4,
26 | "option_android_splash_screens_landscape": "${base_options_dir}/android/splash/landscape.png",
27 | "option_android_splash_screens_portrait": "${base_options_dir}/android/splash/portrait.png",
28 | "option_android_splash_time": 0,
29 | "option_android_launchscreen_fill": 0,
30 | "option_android_splashscreen_background_colour": 255,
31 | "option_android_tv_banner": "${base_options_dir}/android/tv_banner.png",
32 | "option_android_interpolate_pixels": false,
33 | "option_android_screen_depth": 0,
34 | "option_android_device_support": 0,
35 | "option_android_scale": 0,
36 | "option_android_texture_page": "2048x2048",
37 | "option_android_icon_ldpi": "${base_options_dir}/android/icons/ldpi.png",
38 | "option_android_icon_mdpi": "${base_options_dir}/android/icons/mdpi.png",
39 | "option_android_icon_hdpi": "${base_options_dir}/android/icons/hdpi.png",
40 | "option_android_icon_xhdpi": "${base_options_dir}/android/icons/xhdpi.png",
41 | "option_android_icon_xxhdpi": "${base_options_dir}/android/icons/xxhdpi.png",
42 | "option_android_icon_xxxhdpi": "${base_options_dir}/android/icons/xxxhdpi.png",
43 | "option_android_icon_adaptive_generate": false,
44 | "option_android_icon_adaptive_ldpi": "${base_options_dir}/android/icons_adaptive/ldpi.png",
45 | "option_android_icon_adaptive_mdpi": "${base_options_dir}/android/icons_adaptive/mdpi.png",
46 | "option_android_icon_adaptive_hdpi": "${base_options_dir}/android/icons_adaptive/hdpi.png",
47 | "option_android_icon_adaptive_xhdpi": "${base_options_dir}/android/icons_adaptive/xhdpi.png",
48 | "option_android_icon_adaptive_xxhdpi": "${base_options_dir}/android/icons_adaptive/xxhdpi.png",
49 | "option_android_icon_adaptive_xxxhdpi": "${base_options_dir}/android/icons_adaptive/xxxhdpi.png",
50 | "option_android_icon_adaptivebg_ldpi": "${base_options_dir}/android/icons_adaptivebg/ldpi.png",
51 | "option_android_icon_adaptivebg_mdpi": "${base_options_dir}/android/icons_adaptivebg/mdpi.png",
52 | "option_android_icon_adaptivebg_hdpi": "${base_options_dir}/android/icons_adaptivebg/hdpi.png",
53 | "option_android_icon_adaptivebg_xhdpi": "${base_options_dir}/android/icons_adaptivebg/xhdpi.png",
54 | "option_android_icon_adaptivebg_xxhdpi": "${base_options_dir}/android/icons_adaptivebg/xxhdpi.png",
55 | "option_android_icon_adaptivebg_xxxhdpi": "${base_options_dir}/android/icons_adaptivebg/xxxhdpi.png",
56 | "option_android_use_facebook": false,
57 | "option_android_facebook_id": "",
58 | "option_android_facebook_app_display_name": "",
59 | "option_android_google_cloud_saving": false,
60 | "option_android_google_services_app_id": "",
61 | "option_android_permission_write_external_storage": false,
62 | "option_android_permission_read_phone_state": false,
63 | "option_android_permission_network_state": false,
64 | "option_android_permission_internet": true,
65 | "option_android_permission_bluetooth": true,
66 | "option_android_permission_record_audio": false,
67 | "option_android_application_tag_inject": "",
68 | "option_android_google_apk_expansion": false,
69 | "option_android_google_dynamic_asset_delivery": false,
70 | "option_android_google_licensing_public_key": "",
71 | "option_android_tv_isgame": true,
72 | "resourceVersion": "1.0",
73 | "name": "Android",
74 | "tags": [],
75 | "resourceType": "GMAndroidOptions",
76 | }
--------------------------------------------------------------------------------
/options/html5/options_html5.yy:
--------------------------------------------------------------------------------
1 | {
2 | "option_html5_browser_title": "Created with GameMaker Studio 2",
3 | "option_html5_version": "1.0.0.0",
4 | "option_html5_foldername": "html5game",
5 | "option_html5_outputname": "index.html",
6 | "option_html5_splash_png": "${base_options_dir}/html5/splash.png",
7 | "option_html5_usesplash": false,
8 | "option_html5_outputdebugtoconsole": true,
9 | "option_html5_display_cursor": true,
10 | "option_html5_localrunalert": true,
11 | "option_html5_index": "",
12 | "option_html5_loadingbar": "",
13 | "option_html5_jsprepend": "",
14 | "option_html5_icon": "${base_options_dir}/html5/fav.ico",
15 | "option_html5_allow_fullscreen": true,
16 | "option_html5_interpolate_pixels": true,
17 | "option_html5_centregame": false,
18 | "option_html5_usebuiltinparticles": true,
19 | "option_html5_usebuiltinfont": true,
20 | "option_html5_webgl": 2,
21 | "option_html5_scale": 0,
22 | "option_html5_texture_page": "2048x2048",
23 | "option_html5_use_facebook": false,
24 | "option_html5_facebook_id": "",
25 | "option_html5_facebook_app_display_name": "",
26 | "option_html5_flurry_enable": false,
27 | "option_html5_flurry_id": "",
28 | "option_html5_google_analytics_enable": false,
29 | "option_html5_google_tracking_id": "",
30 | "resourceVersion": "1.0",
31 | "name": "HTML5",
32 | "tags": [],
33 | "resourceType": "GMHtml5Options",
34 | }
--------------------------------------------------------------------------------
/options/ios/options_ios.yy:
--------------------------------------------------------------------------------
1 | {
2 | "option_ios_display_name": "Created with GameMaker Studio 2",
3 | "option_ios_bundle_name": "com.company.game",
4 | "option_ios_version": "1.0.0.0",
5 | "option_ios_output_dir": "~/gamemakerstudio2",
6 | "option_ios_team_id": "",
7 | "option_ios_orientation_portrait": true,
8 | "option_ios_orientation_portrait_flipped": true,
9 | "option_ios_orientation_landscape": true,
10 | "option_ios_orientation_landscape_flipped": true,
11 | "option_ios_devices": 2,
12 | "option_ios_defer_home_indicator": false,
13 | "option_ios_icon_iphone_app_120": "${base_options_dir}/ios/icons/app/iphone_120.png",
14 | "option_ios_icon_iphone_app_180": "${base_options_dir}/ios/icons/app/iphone_180.png",
15 | "option_ios_icon_ipad_app_76": "${base_options_dir}/ios/icons/app/ipad_76.png",
16 | "option_ios_icon_ipad_app_152": "${base_options_dir}/ios/icons/app/ipad_152.png",
17 | "option_ios_icon_ipad_pro_app_167": "${base_options_dir}/ios/icons/app/ipad_pro_167.png",
18 | "option_ios_icon_iphone_notification_40": "${base_options_dir}/ios/icons/notification/iphone_40.png",
19 | "option_ios_icon_iphone_notification_60": "${base_options_dir}/ios/icons/notification/iphone_60.png",
20 | "option_ios_icon_ipad_notification_20": "${base_options_dir}/ios/icons/notification/ipad_20.png",
21 | "option_ios_icon_ipad_notification_40": "${base_options_dir}/ios/icons/notification/ipad_40.png",
22 | "option_ios_icon_iphone_spotlight_80": "${base_options_dir}/ios/icons/spotlight/iphone_80.png",
23 | "option_ios_icon_iphone_spotlight_120": "${base_options_dir}/ios/icons/spotlight/iphone_120.png",
24 | "option_ios_icon_ipad_spotlight_40": "${base_options_dir}/ios/icons/spotlight/ipad_40.png",
25 | "option_ios_icon_ipad_spotlight_80": "${base_options_dir}/ios/icons/spotlight/ipad_80.png",
26 | "option_ios_icon_iphone_settings_58": "${base_options_dir}/ios/icons/settings/iphone_58.png",
27 | "option_ios_icon_iphone_settings_87": "${base_options_dir}/ios/icons/settings/iphone_87.png",
28 | "option_ios_icon_ipad_settings_29": "${base_options_dir}/ios/icons/settings/ipad_29.png",
29 | "option_ios_icon_ipad_settings_58": "${base_options_dir}/ios/icons/settings/ipad_58.png",
30 | "option_ios_icon_itunes_artwork_1024": "${base_options_dir}/ios/icons/itunes/itunes_1024.png",
31 | "option_ios_splashscreen_background_colour": 255,
32 | "option_ios_launchscreen_image": "${base_options_dir}/ios/splash/launchscreen.png",
33 | "option_ios_launchscreen_image_landscape": "${base_options_dir}/ios/splash/launchscreen-landscape.png",
34 | "option_ios_launchscreen_fill": 0,
35 | "option_ios_interpolate_pixels": false,
36 | "option_ios_half_ipad1_textures": false,
37 | "option_ios_scale": 0,
38 | "option_ios_texture_page": "2048x2048",
39 | "option_ios_use_facebook": false,
40 | "option_ios_facebook_id": "",
41 | "option_ios_facebook_app_display_name": "",
42 | "option_ios_push_notifications": false,
43 | "option_ios_apple_sign_in": false,
44 | "option_ios_podfile_path": "${options_dir}/ios/Podfile",
45 | "option_ios_podfile_lock_path": "${options_dir}/ios/Podfile.lock",
46 | "resourceVersion": "1.3",
47 | "name": "iOS",
48 | "tags": [],
49 | "resourceType": "GMiOSOptions",
50 | }
--------------------------------------------------------------------------------
/options/linux/options_linux.yy:
--------------------------------------------------------------------------------
1 | {
2 | "option_linux_display_name": "Created with GameMaker Studio 2",
3 | "option_linux_version": "1.0.0.0",
4 | "option_linux_maintainer_email": "",
5 | "option_linux_homepage": "http://www.yoyogames.com",
6 | "option_linux_short_desc": "",
7 | "option_linux_long_desc": "",
8 | "option_linux_splash_screen": "${base_options_dir}/linux/splash/splash.png",
9 | "option_linux_display_splash": false,
10 | "option_linux_icon": "${base_options_dir}/linux/icons/64.png",
11 | "option_linux_start_fullscreen": false,
12 | "option_linux_allow_fullscreen": false,
13 | "option_linux_interpolate_pixels": true,
14 | "option_linux_display_cursor": true,
15 | "option_linux_sync": false,
16 | "option_linux_resize_window": false,
17 | "option_linux_scale": 0,
18 | "option_linux_texture_page": "2048x2048",
19 | "option_linux_enable_steam": false,
20 | "option_linux_disable_sandbox": false,
21 | "resourceVersion": "1.0",
22 | "name": "Linux",
23 | "tags": [],
24 | "resourceType": "GMLinuxOptions",
25 | }
--------------------------------------------------------------------------------
/options/mac/options_mac.yy:
--------------------------------------------------------------------------------
1 | {
2 | "option_mac_display_name": "Created with GameMaker Studio 2",
3 | "option_mac_app_id": "com.company.game",
4 | "option_mac_version": "1.0.0.0",
5 | "option_mac_output_dir": "~/gamemakerstudio2",
6 | "option_mac_team_id": "",
7 | "option_mac_signing_identity": "Developer ID Application:",
8 | "option_mac_copyright": "",
9 | "option_mac_splash_png": "${base_options_dir}/mac/splash/splash.png",
10 | "option_mac_icon_png": "${base_options_dir}/mac/icons/1024.png",
11 | "option_mac_installer_background_png": "${base_options_dir}/mac/splash/installer_background.png",
12 | "option_mac_menu_dock": false,
13 | "option_mac_display_cursor": true,
14 | "option_mac_start_fullscreen": false,
15 | "option_mac_allow_fullscreen": false,
16 | "option_mac_interpolate_pixels": true,
17 | "option_mac_vsync": false,
18 | "option_mac_resize_window": false,
19 | "option_mac_enable_retina": false,
20 | "option_mac_scale": 0,
21 | "option_mac_texture_page": "2048x2048",
22 | "option_mac_build_app_store": false,
23 | "option_mac_allow_incoming_network": false,
24 | "option_mac_allow_outgoing_network": false,
25 | "option_mac_app_category": "Games",
26 | "option_mac_enable_steam": false,
27 | "option_mac_disable_sandbox": false,
28 | "option_mac_apple_sign_in": false,
29 | "resourceVersion": "1.0",
30 | "name": "macOS",
31 | "tags": [],
32 | "resourceType": "GMMacOptions",
33 | }
--------------------------------------------------------------------------------
/options/main/options_main.yy:
--------------------------------------------------------------------------------
1 | {
2 | "option_gameguid": "ef4643ad-26d1-4c0a-ba5d-03c7e9c77b8b",
3 | "option_gameid": "0",
4 | "option_game_speed": 60,
5 | "option_mips_for_3d_textures": false,
6 | "option_draw_colour": 4294967295,
7 | "option_window_colour": 255,
8 | "option_steam_app_id": "0",
9 | "option_sci_usesci": false,
10 | "option_author": "",
11 | "option_lastchanged": "",
12 | "option_spine_licence": false,
13 | "resourceVersion": "1.2",
14 | "name": "Main",
15 | "tags": [],
16 | "resourceType": "GMMainOptions",
17 | }
--------------------------------------------------------------------------------
/options/tvos/options_tvos.yy:
--------------------------------------------------------------------------------
1 | {
2 | "option_tvos_display_name": "Made in GameMaker Studio 2",
3 | "option_tvos_bundle_name": "com.company.game",
4 | "option_tvos_version": "1.0.0.0",
5 | "option_tvos_output_dir": "~/GameMakerStudio2/tvOS",
6 | "option_tvos_team_id": "",
7 | "option_tvos_icon_400": "${base_options_dir}/tvos/icons/400.png",
8 | "option_tvos_icon_400_2x": "${base_options_dir}/tvos/icons/400_2x.png",
9 | "option_tvos_icon_1280": "${base_options_dir}/tvos/icons/1280.png",
10 | "option_tvos_topshelf": "${base_options_dir}/tvos/topshelf/topshelf.png",
11 | "option_tvos_topshelf_2x": "${base_options_dir}/tvos/topshelf/topshelf_2x.png",
12 | "option_tvos_topshelf_wide": "${base_options_dir}/tvos/topshelf/topshelf_wide.png",
13 | "option_tvos_topshelf_wide_2x": "${base_options_dir}/tvos/topshelf/topshelf_wide_2x.png",
14 | "option_tvos_splashscreen": "${base_options_dir}/tvos/splash/splash.png",
15 | "option_tvos_splashscreen_2x": "${base_options_dir}/tvos/splash/splash_2x.png",
16 | "option_tvos_splash_time": 0,
17 | "option_tvos_interpolate_pixels": true,
18 | "option_tvos_scale": 0,
19 | "option_tvos_texture_page": "2048x2048",
20 | "option_tvos_display_cursor": false,
21 | "option_tvos_push_notifications": false,
22 | "option_tvos_apple_sign_in": false,
23 | "option_tvos_podfile_path": "${options_dir}\\tvos\\Podfile",
24 | "option_tvos_podfile_lock_path": "${options_dir}\\tvos\\Podfile.lock",
25 | "resourceVersion": "1.3",
26 | "name": "tvOS",
27 | "tags": [],
28 | "resourceType": "GMtvOSOptions",
29 | }
--------------------------------------------------------------------------------
/options/windows/options_windows.yy:
--------------------------------------------------------------------------------
1 | {
2 | "option_windows_display_name": "Created with GameMaker Studio 2",
3 | "option_windows_executable_name": "${project_name}.exe",
4 | "option_windows_version": "1.0.0.0",
5 | "option_windows_company_info": "YoYo Games Ltd",
6 | "option_windows_product_info": "Created with GameMaker Studio 2",
7 | "option_windows_copyright_info": "",
8 | "option_windows_description_info": "A GameMaker Studio 2 Game",
9 | "option_windows_display_cursor": true,
10 | "option_windows_icon": "${base_options_dir}/windows/icons/icon.ico",
11 | "option_windows_save_location": 0,
12 | "option_windows_splash_screen": "${base_options_dir}/windows/splash/splash.png",
13 | "option_windows_use_splash": false,
14 | "option_windows_start_fullscreen": false,
15 | "option_windows_allow_fullscreen_switching": false,
16 | "option_windows_interpolate_pixels": false,
17 | "option_windows_vsync": false,
18 | "option_windows_resize_window": false,
19 | "option_windows_borderless": false,
20 | "option_windows_scale": 0,
21 | "option_windows_copy_exe_to_dest": false,
22 | "option_windows_sleep_margin": 10,
23 | "option_windows_texture_page": "2048x2048",
24 | "option_windows_installer_finished": "${base_options_dir}/windows/installer/finished.bmp",
25 | "option_windows_installer_header": "${base_options_dir}/windows/installer/header.bmp",
26 | "option_windows_license": "${base_options_dir}/windows/installer/license.txt",
27 | "option_windows_nsis_file": "${base_options_dir}/windows/installer/nsis_script.nsi",
28 | "option_windows_enable_steam": false,
29 | "option_windows_disable_sandbox": false,
30 | "option_windows_steam_use_alternative_launcher": false,
31 | "option_windows_use_x64": false,
32 | "resourceVersion": "1.1",
33 | "name": "Windows",
34 | "tags": [],
35 | "resourceType": "GMWindowsOptions",
36 | }
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | # GML Behavior Tree
8 |
9 | The classic Behavior Tree to GMS 2.3+
10 |
11 | > if you don't know what you're doing here or need to learn what is a Behavior Tree, you can see it on our [references](#references)
12 |
13 | ## How to use
14 | ``` gml
15 | //create
16 | function TaskChangeColor() : BTreeLeaf() constructor{
17 | name = "TaskChangeColor";
18 |
19 | /// @override
20 | static Process = function(){
21 | black_board_ref.user.image_blend = c_yellow
22 | return BTStates.Success;
23 | }
24 | }
25 |
26 | bt_root = new BTreeRoot(id);
27 | var change_color = new TaskChangeColor()
28 | bt_root.ChildAdd(change_color)
29 |
30 | bt_root.Init();
31 |
32 | //step
33 | bt_root.Process();
34 | ```
35 |
36 | ## More details on
37 |
38 | - [Usage example](./example.md)
39 | - [Nodes docs](./nodes.md)
40 | - [How its builded](./builded.md)
41 |
42 | ## Contributors
43 |
44 |
45 |
46 |
47 |
48 | ## References
49 |
50 | - https://en.wikipedia.org/wiki/Behavior_tree_(artificial_intelligence,_robotics_and_control)
51 | - https://www.gamasutra.com/blogs/ChrisSimpson/20140717/221339/Behavior_trees_for_AI_How_they_work.php
52 |
53 | And a especial thank you to [squircledev](https://github.com/squircledev) who shared some drafts with us :)
54 |
--------------------------------------------------------------------------------
/readme_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vitorestevam/GML-Behavior-Tree/49ae7e350fad426700e59fe7718aa3397a1a410a/readme_logo.png
--------------------------------------------------------------------------------
/rooms/Room1/Room1.yy:
--------------------------------------------------------------------------------
1 | {
2 | "isDnd": false,
3 | "volume": 1.0,
4 | "parentRoom": null,
5 | "views": [
6 | {"inherit":false,"visible":false,"xview":0,"yview":0,"wview":1366,"hview":768,"xport":0,"yport":0,"wport":1366,"hport":768,"hborder":32,"vborder":32,"hspeed":-1,"vspeed":-1,"objectId":null,},
7 | {"inherit":false,"visible":false,"xview":0,"yview":0,"wview":1366,"hview":768,"xport":0,"yport":0,"wport":1366,"hport":768,"hborder":32,"vborder":32,"hspeed":-1,"vspeed":-1,"objectId":null,},
8 | {"inherit":false,"visible":false,"xview":0,"yview":0,"wview":1366,"hview":768,"xport":0,"yport":0,"wport":1366,"hport":768,"hborder":32,"vborder":32,"hspeed":-1,"vspeed":-1,"objectId":null,},
9 | {"inherit":false,"visible":false,"xview":0,"yview":0,"wview":1366,"hview":768,"xport":0,"yport":0,"wport":1366,"hport":768,"hborder":32,"vborder":32,"hspeed":-1,"vspeed":-1,"objectId":null,},
10 | {"inherit":false,"visible":false,"xview":0,"yview":0,"wview":1366,"hview":768,"xport":0,"yport":0,"wport":1366,"hport":768,"hborder":32,"vborder":32,"hspeed":-1,"vspeed":-1,"objectId":null,},
11 | {"inherit":false,"visible":false,"xview":0,"yview":0,"wview":1366,"hview":768,"xport":0,"yport":0,"wport":1366,"hport":768,"hborder":32,"vborder":32,"hspeed":-1,"vspeed":-1,"objectId":null,},
12 | {"inherit":false,"visible":false,"xview":0,"yview":0,"wview":1366,"hview":768,"xport":0,"yport":0,"wport":1366,"hport":768,"hborder":32,"vborder":32,"hspeed":-1,"vspeed":-1,"objectId":null,},
13 | {"inherit":false,"visible":false,"xview":0,"yview":0,"wview":1366,"hview":768,"xport":0,"yport":0,"wport":1366,"hport":768,"hborder":32,"vborder":32,"hspeed":-1,"vspeed":-1,"objectId":null,},
14 | ],
15 | "layers": [
16 | {"instances":[
17 | {"properties":[],"isDnd":false,"objectId":{"name":"o_mouse_follower","path":"objects/o_mouse_follower/o_mouse_follower.yy",},"inheritCode":false,"hasCreationCode":false,"colour":4294967295,"rotation":0.0,"scaleX":1.0,"scaleY":1.0,"imageIndex":0,"imageSpeed":1.0,"inheritedItemId":null,"frozen":false,"ignore":false,"inheritItemSettings":false,"x":160.0,"y":384.0,"resourceVersion":"1.0","name":"inst_21BFD710","tags":[],"resourceType":"GMRInstance",},
18 | {"properties":[],"isDnd":false,"objectId":{"name":"o_enemy","path":"objects/o_enemy/o_enemy.yy",},"inheritCode":false,"hasCreationCode":false,"colour":4294967295,"rotation":0.0,"scaleX":1.0,"scaleY":1.0,"imageIndex":0,"imageSpeed":1.0,"inheritedItemId":null,"frozen":false,"ignore":false,"inheritItemSettings":false,"x":1120.0,"y":384.0,"resourceVersion":"1.0","name":"inst_710534F9","tags":[],"resourceType":"GMRInstance",},
19 | ],"visible":true,"depth":0,"userdefinedDepth":false,"inheritLayerDepth":false,"inheritLayerSettings":false,"gridX":32,"gridY":32,"layers":[],"hierarchyFrozen":false,"resourceVersion":"1.0","name":"Instances","tags":[],"resourceType":"GMRInstanceLayer",},
20 | {"spriteId":null,"colour":4278190080,"x":0,"y":0,"htiled":false,"vtiled":false,"hspeed":0.0,"vspeed":0.0,"stretch":false,"animationFPS":15.0,"animationSpeedType":0,"userdefinedAnimFPS":false,"visible":true,"depth":100,"userdefinedDepth":false,"inheritLayerDepth":false,"inheritLayerSettings":false,"gridX":32,"gridY":32,"layers":[],"hierarchyFrozen":false,"resourceVersion":"1.0","name":"Background","tags":[],"resourceType":"GMRBackgroundLayer",},
21 | ],
22 | "inheritLayers": false,
23 | "creationCodeFile": "",
24 | "inheritCode": false,
25 | "instanceCreationOrder": [
26 | {"name":"inst_21BFD710","path":"rooms/Room1/Room1.yy",},
27 | {"name":"inst_710534F9","path":"rooms/Room1/Room1.yy",},
28 | ],
29 | "inheritCreationOrder": false,
30 | "sequenceId": null,
31 | "roomSettings": {
32 | "inheritRoomSettings": false,
33 | "Width": 1366,
34 | "Height": 768,
35 | "persistent": false,
36 | },
37 | "viewSettings": {
38 | "inheritViewSettings": false,
39 | "enableViews": false,
40 | "clearViewBackground": false,
41 | "clearDisplayBuffer": true,
42 | },
43 | "physicsSettings": {
44 | "inheritPhysicsSettings": false,
45 | "PhysicsWorld": false,
46 | "PhysicsWorldGravityX": 0.0,
47 | "PhysicsWorldGravityY": 10.0,
48 | "PhysicsWorldPixToMetres": 0.1,
49 | },
50 | "parent": {
51 | "name": "example",
52 | "path": "folders/example.yy",
53 | },
54 | "resourceVersion": "1.0",
55 | "name": "Room1",
56 | "tags": [],
57 | "resourceType": "GMRoom",
58 | }
--------------------------------------------------------------------------------
/scripts/BehaviourTree/BehaviourTree.gml:
--------------------------------------------------------------------------------
1 | /*
2 | The classic Behavior Tree to GMS 2.3+
3 | if you don't know what you're doing here or need to learn what is a Behavior Tree,
4 | you can see it on our references
5 |
6 | https://github.com/VitorEstevam/GML-Behaviour-Tree
7 | Mantained by @vitorstvm and @jalesjefferson
8 | */
9 |
10 | enum BTStates
11 | {
12 | Running,
13 | Success,
14 | Failure,
15 | Off,
16 | }
17 |
18 | ///@abstract
19 | function BTreeNode() constructor{
20 | name = "BT_TREE_NODE_BASE";
21 | status = BTStates.Running;
22 | visited = false;
23 |
24 | children = [];
25 | children_arr_len = 0;
26 | black_board_ref = noone;
27 |
28 | static Init = function(){}
29 |
30 | static Process = function(){
31 | return BTStates.Success;
32 | }
33 |
34 | static ChildAdd = function(_child){
35 | array_push(children, _child);
36 | ++children_arr_len;
37 | }
38 |
39 | static NodeProcess = function(_node){
40 |
41 | if(_node.visited == false){ // Initial configure
42 | _node.black_board_ref = black_board_ref;
43 | _node.visited = true;
44 | _node.Init();
45 | }
46 |
47 | var _status = _node.Process(); // Returning State
48 |
49 | if(_status == BTStates.Running and black_board_ref.running_node == noone){
50 | black_board_ref.running_node = _node
51 | }
52 | else if( _status != BTStates.Running and black_board_ref.running_node != noone){
53 | black_board_ref.running_node = noone;
54 | }
55 |
56 | return _status
57 | }
58 |
59 | }
60 |
61 | ///@abstract
62 | function BTreeComposite() : BTreeNode() constructor{}
63 |
64 | ///@abstract
65 | function BTreeLeaf() : BTreeNode() constructor{}
66 |
67 | ///@abstract
68 | function BTreeDecorator() : BTreeNode() constructor{
69 | /// @overwrite
70 | static ChildAdd = function(child_node){
71 | children[0] = child_node;
72 | children_arr_len = 1;
73 | }
74 | }
75 |
76 | /// @param inst_id - Expects an instance id.
77 | function BTreeRoot(inst_id): BTreeNode() constructor{
78 | name = "BT_ROOT";
79 | status = BTStates.Off;
80 | array_push(children, noone);
81 |
82 | black_board = {
83 | user : inst_id,
84 | root_reference : other,
85 | running_node: noone,
86 | };
87 |
88 | black_board_ref = black_board;
89 |
90 | /// @override
91 | static Init = function(){
92 | status = BTStates.Running;
93 | }
94 |
95 | /// @override
96 | static Process = function(){
97 | if(black_board.running_node != noone)
98 | NodeProcess(black_board.running_node);
99 |
100 | else if(children[0] != noone){
101 | if(status == BTStates.Running)
102 | NodeProcess(children[0]);
103 | }
104 | }
105 |
106 | /// @override
107 | /// @param child_node - Expects a BTreeNode.
108 | static ChildAdd = function(child_node){
109 | children[0] = child_node;
110 | children_arr_len = 1;
111 | }
112 | }
113 |
114 | function BTreeSequence() : BTreeComposite() constructor{
115 | name = "BT_SEQUENCE";
116 |
117 | /// @override
118 | static Process = function(){
119 | var _i = 0;
120 | repeat(children_arr_len){
121 | if(children[_i].status == BTStates.Running){
122 | switch( NodeProcess(children[_i])){
123 | case BTStates.Running: return BTStates.Running;
124 | case BTStates.Failure: return BTStates.Failure;
125 | }
126 | }
127 |
128 | ++_i;
129 | }
130 |
131 | return BTStates.Success;
132 | }
133 | }
134 |
135 | function BTreeSelector() : BTreeComposite() constructor{
136 | name = "BT_SELECTOR";
137 |
138 | /// @override
139 | static Process = function(){
140 | var _i = 0;
141 | repeat(children_arr_len){
142 | if(children[_i].status == BTStates.Running){
143 | switch(NodeProcess(children[_i])){
144 | case BTStates.Running: return BTStates.Running;
145 | case BTStates.Success: return BTStates.Success;
146 | }
147 | }
148 |
149 | ++_i;
150 | }
151 |
152 | return BTStates.Failure;
153 | }
154 | }
155 |
156 | function BTreeInverter() : BTreeDecorator() constructor{
157 | name = "BT_Inverter";
158 |
159 | /// @override
160 | static Process = function(){
161 | var _state = NodeProcess(children[0]);
162 | switch(_state){
163 | case BTStates.Failure: return BTStates.Success;
164 | case BTStates.Success: return BTStates.Failure;
165 | default: return _state;
166 | }
167 | }
168 | }
169 |
170 | function BTreeSucceeder() : BTreeDecorator() constructor{
171 | name = "BT_Succeeder";
172 |
173 | /// @override
174 | static Process = function(){
175 | var _state = NodeProcess(children[0]);
176 | return BTStates.Success;
177 | }
178 | }
179 |
180 |
181 |
182 |
183 |
--------------------------------------------------------------------------------
/scripts/BehaviourTree/BehaviourTree.yy:
--------------------------------------------------------------------------------
1 | {
2 | "isDnD": false,
3 | "isCompatibility": false,
4 | "parent": {
5 | "name": "GMLBehaviorTree",
6 | "path": "folders/GMLBehaviorTree.yy",
7 | },
8 | "resourceVersion": "1.0",
9 | "name": "BehaviourTree",
10 | "tags": [],
11 | "resourceType": "GMScript",
12 | }
--------------------------------------------------------------------------------
/scripts/__BTConfig/__BTConfig.gml:
--------------------------------------------------------------------------------
1 | #macro BTree_version "1.0.0"
2 |
3 | show_debug_message("GMLBehaviorTree version "+BTree_version)
--------------------------------------------------------------------------------
/scripts/__BTConfig/__BTConfig.yy:
--------------------------------------------------------------------------------
1 | {
2 | "isDnD": false,
3 | "isCompatibility": false,
4 | "parent": {
5 | "name": "GMLBehaviorTree",
6 | "path": "folders/GMLBehaviorTree.yy",
7 | },
8 | "resourceVersion": "1.0",
9 | "name": "__BTConfig",
10 | "tags": [],
11 | "resourceType": "GMScript",
12 | }
--------------------------------------------------------------------------------
/scripts/behavior_chase/BT_test.yy:
--------------------------------------------------------------------------------
1 | {
2 | "isDnD": false,
3 | "isCompatibility": false,
4 | "parent": {
5 | "name": "Scripts",
6 | "path": "folders/Scripts.yy",
7 | },
8 | "resourceVersion": "1.0",
9 | "name": "BT_test",
10 | "tags": [],
11 | "resourceType": "GMScript",
12 | }
--------------------------------------------------------------------------------
/scripts/behavior_chase/behavior_chase.gml:
--------------------------------------------------------------------------------
1 |
2 | /// @param obj_ref
3 | /// @param range
4 | function TaskInstanceInRange(obj, range) : BTreeLeaf() constructor {
5 | name = "Task Instance In Range";
6 | object_find = obj;
7 | range_limit = range;
8 |
9 | instance_ref = noone;
10 |
11 | /// @override
12 | static Init = function(){
13 | instance_ref = instance_find(object_find, 0);
14 | }
15 |
16 | /// @override
17 | static Process = function(){
18 | if(instance_exists(instance_ref) && point_distance(black_board_ref.user.x, black_board_ref.user.y, instance_ref.x, instance_ref.y) <= range_limit)
19 | return BTStates.Success;
20 | else
21 | return BTStates.Failure;
22 | }
23 | }
24 |
25 | /// @param speed
26 | /// @param timer_secs
27 | function TaskPatrolSimple(speed, timer_secs) : BTreeLeaf() constructor {
28 | name = "Taks Patrol Simple";
29 | patrol_speed = speed;
30 |
31 | patrol_spd_x = 0;
32 | patrol_spd_y = 0;
33 |
34 | patrol_direction = 0;
35 | patrol_timer_max = timer_secs * room_speed;
36 | patrol_timer = 0;
37 |
38 | /// @override
39 | static Process = function(){
40 | if(--patrol_timer <= 0){
41 | patrol_direction = irandom(360);
42 | patrol_spd_x = lengthdir_x(patrol_speed, patrol_direction);
43 | patrol_spd_y = lengthdir_y(patrol_speed, patrol_direction);
44 | patrol_timer = patrol_timer_max;
45 | }
46 |
47 | black_board_ref.user.x += patrol_spd_x;
48 | black_board_ref.user.y += patrol_spd_y;
49 | return BTStates.Success;
50 | }
51 | }
52 |
53 | /// @param instance_chase
54 | /// @param speed_chase
55 | /// @param distance_max
56 | /// @param distance_min
57 | function TaskChaseInstance(instance_chase, speed_chase, distance_max, distance_min) : BTreeLeaf() constructor{
58 | name = "Task Chase Instance";
59 |
60 | chase_speed = speed_chase;
61 | instance_to_chase = instance_chase;
62 | distance_maximun_to_stop_chase = distance_max;
63 | distance_minimun_to_stop_chase = distance_min;
64 |
65 | ///@override
66 | static Process = function(){
67 | if(instance_exists(instance_to_chase)){
68 |
69 | // Check Stop chasing
70 | var _dist = point_distance(black_board_ref.user.x, black_board_ref.user.y, instance_to_chase.x, instance_to_chase.y);
71 | if(_dist <= distance_minimun_to_stop_chase)
72 | return BTStates.Success;
73 | else if (_dist >= distance_maximun_to_stop_chase)
74 | return BTStates.Failure;
75 | else {
76 | // Moving towards chasing
77 | var _dir = point_direction(black_board_ref.user.x, black_board_ref.user.y, instance_to_chase.x, instance_to_chase.y);
78 | black_board_ref.user.x += lengthdir_x(chase_speed, _dir);
79 | black_board_ref.user.y += lengthdir_y(chase_speed, _dir);
80 |
81 | return BTStates.Running;
82 | }
83 | }
84 | else
85 | return BTStates.Failure
86 |
87 | }
88 |
89 | }
90 |
91 | /// @param instance_target
92 | /// @param secs_between_hits
93 | function TaskMeleeHitTarget(instance_target, secs_preparation) : BTreeLeaf() constructor{
94 | name = "Task Melee Hit on Target";
95 |
96 | target_hit = instance_target;
97 | time_preparation_max = secs_preparation * room_speed;
98 | time_preparation = 0;
99 |
100 | /// @override
101 | static Process = function(){
102 | if(!instance_exists(target_hit)) return BTStates.Failure;
103 | if(++time_preparation >= time_preparation_max){
104 | time_preparation = 0;
105 | target_hit.life -= 10;
106 | return BTStates.Success;
107 | }
108 | else
109 | return BTStates.Running;
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/scripts/behavior_chase/behavior_chase.yy:
--------------------------------------------------------------------------------
1 | {
2 | "isDnD": false,
3 | "isCompatibility": false,
4 | "parent": {
5 | "name": "example",
6 | "path": "folders/example.yy",
7 | },
8 | "resourceVersion": "1.0",
9 | "name": "behavior_chase",
10 | "tags": [],
11 | "resourceType": "GMScript",
12 | }
--------------------------------------------------------------------------------
/sprites/Sprite1/Sprite1.yy:
--------------------------------------------------------------------------------
1 | {
2 | "bboxMode": 0,
3 | "collisionKind": 1,
4 | "type": 0,
5 | "origin": 4,
6 | "preMultiplyAlpha": false,
7 | "edgeFiltering": false,
8 | "collisionTolerance": 0,
9 | "swfPrecision": 2.525,
10 | "bbox_left": 0,
11 | "bbox_right": 63,
12 | "bbox_top": 0,
13 | "bbox_bottom": 63,
14 | "HTile": false,
15 | "VTile": false,
16 | "For3D": false,
17 | "width": 64,
18 | "height": 64,
19 | "textureGroupId": {
20 | "name": "Default",
21 | "path": "texturegroups/Default",
22 | },
23 | "swatchColours": null,
24 | "gridX": 0,
25 | "gridY": 0,
26 | "frames": [
27 | {"compositeImage":{"FrameId":{"name":"d47c3ffb-aa12-4455-8cb7-22cd1be52178","path":"sprites/Sprite1/Sprite1.yy",},"LayerId":null,"resourceVersion":"1.0","name":"","tags":[],"resourceType":"GMSpriteBitmap",},"images":[
28 | {"FrameId":{"name":"d47c3ffb-aa12-4455-8cb7-22cd1be52178","path":"sprites/Sprite1/Sprite1.yy",},"LayerId":{"name":"04284397-b0cd-4c59-8518-3ed983155443","path":"sprites/Sprite1/Sprite1.yy",},"resourceVersion":"1.0","name":"","tags":[],"resourceType":"GMSpriteBitmap",},
29 | ],"parent":{"name":"Sprite1","path":"sprites/Sprite1/Sprite1.yy",},"resourceVersion":"1.0","name":"d47c3ffb-aa12-4455-8cb7-22cd1be52178","tags":[],"resourceType":"GMSpriteFrame",},
30 | ],
31 | "sequence": {
32 | "spriteId": {"name":"Sprite1","path":"sprites/Sprite1/Sprite1.yy",},
33 | "timeUnits": 1,
34 | "playback": 1,
35 | "playbackSpeed": 30.0,
36 | "playbackSpeedType": 0,
37 | "autoRecord": true,
38 | "volume": 1.0,
39 | "length": 1.0,
40 | "events": {"Keyframes":[],"resourceVersion":"1.0","resourceType":"KeyframeStore",},
41 | "moments": {"Keyframes":[],"resourceVersion":"1.0","resourceType":"KeyframeStore",},
42 | "tracks": [
43 | {"name":"frames","spriteId":null,"keyframes":{"Keyframes":[
44 | {"id":"73222d53-7cb6-4681-ae3b-ba7ad31a08df","Key":0.0,"Length":1.0,"Stretch":false,"Disabled":false,"IsCreationKey":false,"Channels":{"0":{"Id":{"name":"d47c3ffb-aa12-4455-8cb7-22cd1be52178","path":"sprites/Sprite1/Sprite1.yy",},"resourceVersion":"1.0","resourceType":"SpriteFrameKeyframe",},},"resourceVersion":"1.0","resourceType":"Keyframe",},
45 | ],"resourceVersion":"1.0","resourceType":"KeyframeStore",},"trackColour":0,"inheritsTrackColour":true,"builtinName":0,"traits":0,"interpolation":1,"tracks":[],"events":[],"modifiers":[],"isCreationTrack":false,"resourceVersion":"1.0","tags":[],"resourceType":"GMSpriteFramesTrack",},
46 | ],
47 | "visibleRange": null,
48 | "lockOrigin": false,
49 | "showBackdrop": true,
50 | "showBackdropImage": false,
51 | "backdropImagePath": "",
52 | "backdropImageOpacity": 0.5,
53 | "backdropWidth": 1366,
54 | "backdropHeight": 768,
55 | "backdropXOffset": 0.0,
56 | "backdropYOffset": 0.0,
57 | "xorigin": 32,
58 | "yorigin": 32,
59 | "eventToFunction": {},
60 | "eventStubScript": null,
61 | "parent": {"name":"Sprite1","path":"sprites/Sprite1/Sprite1.yy",},
62 | "resourceVersion": "1.3",
63 | "name": "Sprite1",
64 | "tags": [],
65 | "resourceType": "GMSequence",
66 | },
67 | "layers": [
68 | {"visible":true,"isLocked":false,"blendMode":0,"opacity":100.0,"displayName":"default","resourceVersion":"1.0","name":"04284397-b0cd-4c59-8518-3ed983155443","tags":[],"resourceType":"GMImageLayer",},
69 | ],
70 | "nineSlice": null,
71 | "parent": {
72 | "name": "example",
73 | "path": "folders/example.yy",
74 | },
75 | "resourceVersion": "1.0",
76 | "name": "Sprite1",
77 | "tags": [],
78 | "resourceType": "GMSprite",
79 | }
--------------------------------------------------------------------------------
/sprites/Sprite1/d47c3ffb-aa12-4455-8cb7-22cd1be52178.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vitorestevam/GML-Behavior-Tree/49ae7e350fad426700e59fe7718aa3397a1a410a/sprites/Sprite1/d47c3ffb-aa12-4455-8cb7-22cd1be52178.png
--------------------------------------------------------------------------------
/sprites/Sprite1/layers/d47c3ffb-aa12-4455-8cb7-22cd1be52178/04284397-b0cd-4c59-8518-3ed983155443.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vitorestevam/GML-Behavior-Tree/49ae7e350fad426700e59fe7718aa3397a1a410a/sprites/Sprite1/layers/d47c3ffb-aa12-4455-8cb7-22cd1be52178/04284397-b0cd-4c59-8518-3ed983155443.png
--------------------------------------------------------------------------------
/sprites/Sprite2/9c751507-4992-43c3-9dde-795606e8c68d.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vitorestevam/GML-Behavior-Tree/49ae7e350fad426700e59fe7718aa3397a1a410a/sprites/Sprite2/9c751507-4992-43c3-9dde-795606e8c68d.png
--------------------------------------------------------------------------------
/sprites/Sprite2/Sprite2.yy:
--------------------------------------------------------------------------------
1 | {
2 | "bboxMode": 0,
3 | "collisionKind": 1,
4 | "type": 0,
5 | "origin": 4,
6 | "preMultiplyAlpha": false,
7 | "edgeFiltering": false,
8 | "collisionTolerance": 0,
9 | "swfPrecision": 2.525,
10 | "bbox_left": 0,
11 | "bbox_right": 63,
12 | "bbox_top": 0,
13 | "bbox_bottom": 63,
14 | "HTile": false,
15 | "VTile": false,
16 | "For3D": false,
17 | "width": 64,
18 | "height": 64,
19 | "textureGroupId": {
20 | "name": "Default",
21 | "path": "texturegroups/Default",
22 | },
23 | "swatchColours": null,
24 | "gridX": 0,
25 | "gridY": 0,
26 | "frames": [
27 | {"compositeImage":{"FrameId":{"name":"9c751507-4992-43c3-9dde-795606e8c68d","path":"sprites/Sprite2/Sprite2.yy",},"LayerId":null,"resourceVersion":"1.0","name":"","tags":[],"resourceType":"GMSpriteBitmap",},"images":[
28 | {"FrameId":{"name":"9c751507-4992-43c3-9dde-795606e8c68d","path":"sprites/Sprite2/Sprite2.yy",},"LayerId":{"name":"4fdad78a-d4c5-4cdc-8b18-9a23df9ac50a","path":"sprites/Sprite2/Sprite2.yy",},"resourceVersion":"1.0","name":"","tags":[],"resourceType":"GMSpriteBitmap",},
29 | ],"parent":{"name":"Sprite2","path":"sprites/Sprite2/Sprite2.yy",},"resourceVersion":"1.0","name":"9c751507-4992-43c3-9dde-795606e8c68d","tags":[],"resourceType":"GMSpriteFrame",},
30 | ],
31 | "sequence": {
32 | "spriteId": {"name":"Sprite2","path":"sprites/Sprite2/Sprite2.yy",},
33 | "timeUnits": 1,
34 | "playback": 1,
35 | "playbackSpeed": 30.0,
36 | "playbackSpeedType": 0,
37 | "autoRecord": true,
38 | "volume": 1.0,
39 | "length": 1.0,
40 | "events": {"Keyframes":[],"resourceVersion":"1.0","resourceType":"KeyframeStore",},
41 | "moments": {"Keyframes":[],"resourceVersion":"1.0","resourceType":"KeyframeStore",},
42 | "tracks": [
43 | {"name":"frames","spriteId":null,"keyframes":{"Keyframes":[
44 | {"id":"1684f6c8-91ac-4171-a9c7-fa74fc53b85a","Key":0.0,"Length":1.0,"Stretch":false,"Disabled":false,"IsCreationKey":false,"Channels":{"0":{"Id":{"name":"9c751507-4992-43c3-9dde-795606e8c68d","path":"sprites/Sprite2/Sprite2.yy",},"resourceVersion":"1.0","resourceType":"SpriteFrameKeyframe",},},"resourceVersion":"1.0","resourceType":"Keyframe",},
45 | ],"resourceVersion":"1.0","resourceType":"KeyframeStore",},"trackColour":0,"inheritsTrackColour":true,"builtinName":0,"traits":0,"interpolation":1,"tracks":[],"events":[],"modifiers":[],"isCreationTrack":false,"resourceVersion":"1.0","tags":[],"resourceType":"GMSpriteFramesTrack",},
46 | ],
47 | "visibleRange": null,
48 | "lockOrigin": false,
49 | "showBackdrop": true,
50 | "showBackdropImage": false,
51 | "backdropImagePath": "",
52 | "backdropImageOpacity": 0.5,
53 | "backdropWidth": 1366,
54 | "backdropHeight": 768,
55 | "backdropXOffset": 0.0,
56 | "backdropYOffset": 0.0,
57 | "xorigin": 32,
58 | "yorigin": 32,
59 | "eventToFunction": {},
60 | "eventStubScript": null,
61 | "parent": {"name":"Sprite2","path":"sprites/Sprite2/Sprite2.yy",},
62 | "resourceVersion": "1.3",
63 | "name": "Sprite2",
64 | "tags": [],
65 | "resourceType": "GMSequence",
66 | },
67 | "layers": [
68 | {"visible":true,"isLocked":false,"blendMode":0,"opacity":100.0,"displayName":"default","resourceVersion":"1.0","name":"4fdad78a-d4c5-4cdc-8b18-9a23df9ac50a","tags":[],"resourceType":"GMImageLayer",},
69 | ],
70 | "nineSlice": null,
71 | "parent": {
72 | "name": "example",
73 | "path": "folders/example.yy",
74 | },
75 | "resourceVersion": "1.0",
76 | "name": "Sprite2",
77 | "tags": [],
78 | "resourceType": "GMSprite",
79 | }
--------------------------------------------------------------------------------
/sprites/Sprite2/layers/9c751507-4992-43c3-9dde-795606e8c68d/4fdad78a-d4c5-4cdc-8b18-9a23df9ac50a.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vitorestevam/GML-Behavior-Tree/49ae7e350fad426700e59fe7718aa3397a1a410a/sprites/Sprite2/layers/9c751507-4992-43c3-9dde-795606e8c68d/4fdad78a-d4c5-4cdc-8b18-9a23df9ac50a.png
--------------------------------------------------------------------------------