Creating Custom Behavior Trees
This document explains how to create your own behavior trees for AI agents in the Kane vs Abel Mortal Kombat II environment.
Introduction to Behavior Trees
Behavior Trees (BTs) are a hierarchical structure of nodes that control decision making for AI agents. They offer several advantages:
- Modular, reusable behavior components
- Visual representation of decision logic
- Readable and maintainable AI designs
- Effective for implementing complex game AI
Behavior Tree Structure
A behavior tree is defined in YAML format and consists of different node types arranged in a hierarchical structure:
node:
type: Selector # Root node type
name: "Root" # Node name for debugging
children: # Child nodes
- type: Sequence
name: "Attack Sequence"
children:
# More nodes...
Node Types
Selector Node
Executes children from left to right until one succeeds. If all fail, the selector fails.
type: Selector
name: "Choose Attack"
children:
- type: Action
name: "High Attack"
properties:
action_id: A
frames_needed: 3
- type: Action
name: "Low Attack"
properties:
action_id: DOWN_A
frames_needed: 3
Sequence Node
Executes children from left to right until all succeed or one fails. If any fail, the sequence fails.
type: Sequence
name: "Jump and Attack"
children:
- type: Action
name: "Jump"
properties:
action_id: UP
frames_needed: 2
- type: Action
name: "Attack"
properties:
action_id: A
frames_needed: 1
Condition Node
Tests a condition and returns success or failure based on the result.
type: Condition
name: "Is Enemy Close"
properties:
condition: "is_close_to_enemy" # Must match a function in ConditionsProvider
Action Node
Performs a game action for a specified number of frames.
type: Action
name: "Block"
properties:
action_id: B # Must match an action in action_map
frames_needed: 5 # Number of frames to hold the action
Creating Your BT File
File Structure
Create a YAML file with a single root node:
node:
type: Selector
name: "Fighter Behavior"
children:
# Add your behavior tree nodes here
Available Conditions
The following conditions are available from ConditionsProvider
:
Condition Name | Function | Description |
---|---|---|
is_enemy_to_the_right |
Returns true when the enemy is to the right of the player | Used for directional awareness |
is_enemy_to_the_left |
Returns true when the enemy is to the left of the player | Used for directional awareness |
is_close_to_enemy |
Returns true when the enemy is within 50 units | Used for close-range actions |
is_long_range_enemy |
Returns true when the enemy is beyond 50 units | Used for ranged attacks |
is_medium_range_enemy |
Returns true when the enemy is between 50-120 units | Good for jump-in attacks |
Available Actions
Actions are defined in env_config.yaml and mapped to names in the ActionGenerator. Common actions include:
Action Name | Description | Button Combination |
---|---|---|
NEUTRAL |
No buttons pressed | None |
LEFT |
Move left | LEFT |
RIGHT |
Move right | RIGHT |
UP |
Jump | UP |
DOWN |
Crouch | DOWN |
A |
A button | A |
B |
B button | B |
LEFT_DOWN |
Crouch + move left | LEFT+DOWN |
RIGHT_DOWN |
Crouch + move right | RIGHT+DOWN |
UP_A |
Jump + A | UP+A |
DOWN_B |
Crouch + B | DOWN+B |
Complete Example: Aggressive Fighter
node:
type: Selector
name: "Aggressive Fighter"
children:
# Close Range Attack
- type: Sequence
name: "Close Range Attack"
children:
- type: Condition
name: "Enemy is Close"
properties:
condition: "is_close_to_enemy"
- type: Selector
name: "Choose Close Attack"
children:
- type: Action
name: "Leg Sweep"
properties:
action_id: DOWN_B
frames_needed: 3
- type: Action
name: "Low Punch"
properties:
action_id: DOWN_A
frames_needed: 3
# Medium Range Jump-In
- type: Sequence
name: "Jump Attack Approach"
children:
- type: Condition
name: "Enemy is Medium Range"
properties:
condition: "is_medium_range_enemy"
- type: Selector
name: "Jump Direction"
children:
- type: Sequence
name: "Jump Right Attack"
children:
- type: Condition
name: "Enemy to Right"
properties:
condition: "is_enemy_to_the_right"
- type: Action
name: "Jump Forward Attack"
properties:
action_id: RIGHT_UP_A
frames_needed: 5
- type: Sequence
name: "Jump Left Attack"
children:
- type: Condition
name: "Enemy to Left"
properties:
condition: "is_enemy_to_the_left"
- type: Action
name: "Jump Forward Attack"
properties:
action_id: LEFT_UP_A
frames_needed: 5
# Long Range Fireball
- type: Sequence
name: "Long Range Attack"
children:
- type: Condition
name: "Enemy is Far"
properties:
condition: "is_long_range_enemy"
- type: Action
name: "Fireball Special"
properties:
action_id: RIGHT_UP_B
frames_needed: 7
# Approach - Movement
- type: Selector
name: "Movement"
children:
- type: Sequence
name: "Move Right"
children:
- type: Condition
name: "Enemy to Right"
properties:
condition: "is_enemy_to_the_right"
- type: Action
name: "Move Right"
properties:
action_id: RIGHT
frames_needed: 3
- type: Sequence
name: "Move Left"
children:
- type: Condition
name: "Enemy to Left"
properties:
condition: "is_enemy_to_the_left"
- type: Action
name: "Move Left"
properties:
action_id: LEFT
frames_needed: 3
Best Practices
- Prioritize actions properly: Place the most important behaviors at the top of selectors
- Balance reactivity: Don't set
frames_needed
too high or your agent will be unresponsive - Test incrementally: Start with a simple BT and gradually add complexity
- Use meaningful node names: They help when debugging behaviors
- Consider opponent position: Always check enemy position before directional actions
- Add fallback behaviors: Ensure your tree has default actions if no conditions match
Debugging BTs
- Check action names: Ensure they match exactly with keys in
action_map
- Verify conditions: Make sure condition names match functions in
ConditionsProvider
- Start simple: Begin with a few nodes and gradually expand
- Test against stationary opponents first to verify behavior
- Watch for indentation errors in YAML that may break the tree structure
Using Your Custom BT
Run the arena with your custom BT file:
python arena.py --p1-type bt --p1-bt-file path/to/your_bt.yaml
Or for player 2:
python arena.py --p2-type bt --p2-bt-file path/to/your_bt.yaml
Further Customization
To add custom conditions:
1. Add your function to the ConditionsProvider
class in conditions.py
2. Use your new condition name in the BT YAML file