First SimulationΒΆ
This page creates one dynamic rigid body, steps PhysX, and synchronizes its visual representation.
self.standard_materials = self.create_standard_materials()
self.timing = self.configure_timing(
ke.SimulationTimingConfig(
render_hz=60.0,
physics_hz=120.0,
fixed_update_hz=60.0,
)
)
self.set_simulation_hotkeys_enabled(enabled=True)
self.world: ke.sim.KangSimWorld = ke.sim.KangSimWorld(
num_envs=1,
sim_dt=self.timing.physics_dt,
add_ground=True,
)
self.visual: ke.visual.sim.SimWorldVisualizer = ke.visual.sim.SimWorldVisualizer(
app=self,
world=self.world,
)
ball_xml = package_asset_path("objects", "ball.xml")
ball_data: ke.asset.ArticulationDesc = self.world.load_mjcf(
mjcf_path=ball_xml,
)
self.ball: ke.sim.SimRigid = self.world.add_rigid(
data=ball_data,
env_id=0,
obj_id=0,
name="ball",
pos=[0.0, 0.0, 1.8],
density=600.0,
)
self.visual.add(
sim_handle=self.ball,
mjcf_path=ball_xml,
material=self.standard_materials.common,
)
Advance at the fixed simulation rate and display the latest state once per rendered frame:
def fixed_update(self, fixed_dt):
self.world.advance(fixed_dt)
def pre_render(self):
self.visual.sync()
Run the complete example:
python ./python/examples/sim_world_minimal.py --width 1280 --height 720
Complete source: sim_world_minimal.py
1"""Minimal KangSimWorld example.
2
3Inspired by Newton's basic shape examples: create a small simulation world,
4spawn one rigid body, step the world, and sync it to the viewer.
5"""
6
7from __future__ import annotations
8
9import argparse
10from pathlib import Path
11
12import kangengine as ke
13from kangengine import imgui, keys
14
15
16def package_asset_path(*parts: str) -> str:
17 return str(Path(ke.__file__).resolve().parent / "assets" / Path(*parts))
18
19
20class MinimalSimWorldApp(ke.App):
21 def setup(self):
22 self.spawn_pos = [0.0, 0.0, 1.8]
23 self.timing = self.configure_timing(
24 ke.SimulationTimingConfig(
25 render_hz=60.0,
26 physics_hz=120.0,
27 fixed_update_hz=60.0,
28 )
29 )
30 self.set_simulation_hotkeys_enabled(True)
31
32 self.standard_materials = self.create_standard_materials()
33 self.add_ground()
34 self.set_camera_view([3.0, -4.0, 2.2], [0.0, 0.0, 0.7])
35
36 self.world = ke.sim.KangSimWorld(
37 num_envs=1,
38 sim_dt=self.timing.physics_dt,
39 add_ground=True,
40 )
41 self.visual = ke.visual.sim.SimWorldVisualizer(self, self.world)
42
43 self.ball_xml = package_asset_path("objects", "ball.xml")
44 ball_data = self.world.load_mjcf(self.ball_xml)
45 self.ball = self.world.add_rigid(
46 ball_data,
47 env_id=0,
48 obj_id=0,
49 name="ball",
50 pos=self.spawn_pos,
51 density=600.0,
52 )
53 self.ball_visual = self.visual.add(
54 self.ball,
55 self.ball_xml,
56 path="/ball",
57 material=self.standard_materials.common,
58 color=[0.95, 0.2, 0.12, 1.0],
59 )
60
61 self._reset()
62 print("Minimal KangSimWorld example: one dynamic rigid body")
63 self.check_error()
64
65 def _reset(self):
66 self.ball.set_root_state(
67 None,
68 self.spawn_pos,
69 [0.0, 0.0, 0.0, 1.0],
70 linear_velocity=[0.0, 0.0, 0.0],
71 angular_velocity=[0.0, 0.0, 0.0],
72 )
73 self.world.step(substeps=0, apply_commands=False)
74 self.visual.sync()
75
76 def pre_update(self):
77 if self.was_key_pressed(keys.R):
78 self._reset()
79
80 def fixed_update(self, fixed_dt):
81 self.world.advance(fixed_dt)
82
83 def pre_render(self):
84 self.visual.sync()
85 self.check_error()
86
87 def render(self):
88 pos = self.ball.get_root_pos()
89
90 imgui.begin("Minimal Sim World")
91 imgui.text("KangSimWorld + SimWorldVisualizer")
92 imgui.text("Enter: play/pause Space: pause/step R: reset")
93 imgui.separator()
94 state = "paused" if self.is_simulation_paused() else "running"
95 imgui.text(f"State: {state}")
96 imgui.text(f"Ball root: {pos[0]: .2f}, {pos[1]: .2f}, {pos[2]: .2f}")
97 imgui.end()
98
99 def cleanup(self):
100 if hasattr(self, "world"):
101 self.world.release()
102
103
104def parse_args():
105 parser = argparse.ArgumentParser()
106 parser.add_argument("--width", type=int, default=1920)
107 parser.add_argument("--height", type=int, default=1080)
108 return parser.parse_args()
109
110
111def main():
112 args = parse_args()
113 app = MinimalSimWorldApp()
114 app.initialize(args.width, args.height, False, ke.UpAxis.Z)
115 app.start()
116
117
118if __name__ == "__main__":
119 main()
Expected result: the ball falls onto the ground. Enter toggles play/pause,
Space pauses or advances one step, and R resets it.

Next: Fixed Timestep and Rendering or Simulation.