text: import sys, os
  sys.path.append(os.path.dirname(__file__))
  import pybullet
  
  import gym
  
  
  class Scene:
    "A base class for single- and multiplayer scenes"
  
    def __init__(self, bullet_client, gravity, timestep, frame_skip):
      self._p = bullet_client
      self.np_random, seed = gym.utils.seeding.np_random()
      self.timestep = timestep
      self.frame_skip = frame_skip
  
      self.dt = self.timestep * self.frame_skip
      self.cpp_world = World(self._p, gravity, timestep, frame_skip)
  
      self.test_window_still_open = True  # or never opened
      self.human_render_detected = False  # if user wants render("human"), we open test window
  
      self.multiplayer_robots = {}
  
    def test_window(self):
      "Call this function every frame, to see what's going on. Not necessary in learning."
      self.human_render_detected = True
      return self.test_window_still_open
  
    def actor_introduce(self, robot):
      "Usually after scene reset"
      if not self.multiplayer: return
      self.multiplayer_robots[robot.player_n] = robot
  
    def actor_is_active(self, robot):
      """
          Used by robots to see if they are free to exclusiveley put their HUD on the test window.
          Later can be used for click-focus robots.
          """
      return not self.multiplayer
  
    def episode_restart(self, bullet_client):
      "This function gets overridden by specific scene, to reset specific objects into their start positions"
      self.cpp_world.clean_everything()
      #self.cpp_world.test_window_history_reset()
  
    def global_step(self):
      """
          The idea is: apply motor torques for all robots, then call global_step(), then collect
          observations from robots using step() with the same action.
          """
      self.cpp_world.step(self.frame_skip)
  
  
  class SingleRobotEmptyScene(Scene):
    multiplayer = False  # this class is used "as is" for InvertedPendulum, Reacher
  
  
  class World:
  
    def __init__(self, bullet_client, gravity, timestep, frame_skip):
      self._p = bullet_client
      self.gravity = gravity
      self.timestep = timestep
      self.frame_skip = frame_skip
      self.numSolverIterations = 5
      self.clean_everything()
  
    def clean_everything(self):
      #p.resetSimulation()
      self._p.setGravity(0, 0, -self.gravity)
      self._p.setDefaultContactERP(0.9)
      #print("self.numSolverIterations=",self.numSolverIterations)
      self._p.setPhysicsEngineParameter(fixedTimeStep=self.timestep * self.frame_skip,
                                        numSolverIterations=self.numSolverIterations,
                                        numSubSteps=self.frame_skip)
  
    def step(self, frame_skip):
      self._p.stepSimulation()
  