Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Passing Python Kwargs to Godot Envrionment #206

Merged
merged 9 commits into from
Oct 21, 2024
43 changes: 43 additions & 0 deletions docs/CUSTOM_ENV.md
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,49 @@ https://user-images.githubusercontent.com/7275864/209363084-f91b2fcb-2042-494c-9

https://user-images.githubusercontent.com/7275864/209363098-a6bee0a6-dc85-4b8d-b69a-d747bcf39635.mp4

### Custom environments and additional arguments:

> OBSERVATION
>
> The following example focuses on how to change the **starting_level** using additional command line arguments. \
> You can also use command line arguments to:
> * change the **game_difficulty**
> * change the **starting_character**
> * load a custom **game_state**
> * update any other variable before the game starts

Let's say you have a Godot Environment with multiple levels and want to set the **starting_level** before the simulation starts. All you need to do is to pass **starting_level** as an argument, when instantiating a `StableBaselinesGodotEnv` along with the value you want to pass to Godot and you're good to go. Here's an example in python:

```python
from godot_rl.wrappers.stable_baselines_wrapper import StableBaselinesGodotEnv

# we set the starting_level as RainbowRoad
env = StableBaselinesGodotEnv(
env_path=PATH_TO_ENVIRONMENT,
port=11008,
env_seed=42,
speedup=1,
starting_level="RainbowRoad"
)
```
After running the script you'll see in the console something like:
```bask
getting command line arguments
--port=11008
--env_seed=42
--speedup=1
--starting_level=RainbowRoad
```
Which means that those variables got to the Godot Environment successfully. However, your environment needs to support handling those environments, prior to it being built.

You can access the environment variables though the **godot_rl_agents_plugin/sync.gd** script. You'll see there's a `args` variable that stores all the command line arguments your environment received from the python script. You can access **starting_level** by doing:
```godot
func get_starting_level():
return agrs.get("starting_level", None)
```

Then, you can use the returned value to set the starting level before the game begins.

## There’s more!

We have only scratched the surface of what can be achieved with Godot RL Agents, the library includes custom sensors and cameras to enrich the information available to the agent. Take a look at the [examples](https://github.com/edbeeching/godot_rl_agents_examples) to find out more!
Expand Down
27 changes: 25 additions & 2 deletions godot_rl/core/godot_env.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ def __init__(
action_repeat: Optional[int] = None,
speedup: Optional[int] = None,
convert_action_space: bool = False,
**kwargs,
):
"""
Initialize a new instance of GodotEnv
Expand All @@ -51,7 +52,16 @@ def __init__(
env_path = self._set_platform_suffix(env_path)

self.check_platform(env_path)
self._launch_env(env_path, port, show_window, framerate, seed, action_repeat, speedup)
self._launch_env(
env_path,
port,
show_window,
framerate,
seed,
action_repeat,
speedup,
**kwargs,
)
else:
print("No game binary has been provided, please press PLAY in the Godot editor")

Expand Down Expand Up @@ -277,7 +287,17 @@ def _close(self):
print("exit was not clean, using atexit to close env")
self.close()

def _launch_env(self, env_path, port, show_window, framerate, seed, action_repeat, speedup):
def _launch_env(
self,
env_path,
port,
show_window,
framerate,
seed,
action_repeat,
speedup,
**kwargs,
):
# --fixed-fps {framerate}
path = convert_macos_path(env_path) if platform == "darwin" else env_path

Expand All @@ -291,6 +311,9 @@ def _launch_env(self, env_path, port, show_window, framerate, seed, action_repea
launch_cmd += f" --action_repeat={action_repeat}"
if speedup is not None:
launch_cmd += f" --speedup={speedup}"
if len(kwargs) > 0:
for key, value in kwargs.items():
launch_cmd += f" --{key}={value}"

launch_cmd = launch_cmd.split(" ")
self.proc = subprocess.Popen(
Expand Down
20 changes: 14 additions & 6 deletions godot_rl/wrappers/petting_zoo_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def env(render_mode=None):
class GDRLPettingZooEnv(ParallelEnv):
metadata = {"render_modes": ["human"], "name": "GDRLPettingZooEnv"}

def __init__(self, port=GodotEnv.DEFAULT_PORT, show_window=True, seed=0, config: Dict = {}):
def __init__(self, port=GodotEnv.DEFAULT_PORT, show_window=True, seed=0, config: Dict = None):
"""
The init method takes in environment arguments and should define the following attributes:
- possible_agents
Expand All @@ -38,15 +38,23 @@ def __init__(self, port=GodotEnv.DEFAULT_PORT, show_window=True, seed=0, config:

These attributes should not be changed after initialization.
"""
config = config or {} # initialize config as empty dict if None
extra_arguments = {
key: value
for key, value in config.items()
if key not in ["env_path", "show_window", "action_repeat", "speedup", "seed", "port"]
}

# Initialize the Godot Env which we will wrap
self.godot_env = GodotEnv(
env_path=config.get("env_path"),
show_window=config.get("show_window"),
action_repeat=config.get("action_repeat"),
speedup=config.get("speedup"),
env_path=config["env_path"],
show_window=show_window,
action_repeat=config["action_repeat"],
speedup=config["speedup"],
convert_action_space=False,
seed=seed,
port=port,
**extra_arguments,
)

self.render_mode = None # Controlled by the env
Expand Down Expand Up @@ -125,7 +133,7 @@ def step(self, actions):
# Godot env have done = true. For agents that received no actions, we will set zeros instead for
# compatibility.
godot_actions = [
actions[agent] if agent in actions else np.zeros_like(self.action_spaces[agent_idx].sample())
(actions[agent] if agent in actions else np.zeros_like(self.action_spaces[agent_idx].sample()))
for agent_idx, agent in enumerate(self.agents)
]

Expand Down
15 changes: 9 additions & 6 deletions godot_rl/wrappers/ray_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,22 @@


class RayVectorGodotEnv(VectorEnv):
def __init__(
self,
port=10008,
seed=0,
config=None,
) -> None:
def __init__(self, port=10008, seed=0, config=None) -> None:
config = config or {} # initialize config as empty dict if None
extra_arguments = {
key: value
for key, value in config.items()
if key not in ["env_path", "show_window", "action_repeat", "speedup", "seed", "port"]
}

self._env = GodotEnv(
env_path=config["env_path"],
port=port,
seed=seed,
show_window=config["show_window"],
action_repeat=config["action_repeat"],
speedup=config["speedup"],
**extra_arguments,
)
super().__init__(
observation_space=self._env.observation_space,
Expand Down
Loading