def startUp(self): assert len(self.names) == len(self.spawns) and len(self.spawns) == len(self.goals), "All player related arrays should be of equal size." # Initialise the goal colliders. self.goal_colliders = [] self.team_scores = [] self.locateBots() self.BOTS_PER_TEAM = math.ceil(len(self.robots) / len(self.names)) self.ball_centre = objectFactory(**{ 'collider': 'inherit', 'visual': { 'name': 'Circle', 'radius': 0.1 }, 'physics': True, 'key': 'IR-BALL', }) self.ball_centre.shape.sensor = True self.ball_centre.shape.collision_type = self.BALL_COLLISION_TYPE World.instance.registerObject(self.ball_centre) handler = World.instance.space.add_collision_handler(self.BALL_COLLISION_TYPE, self.GOAL_COLLISION_TYPE) def handle_collide(arbiter, space, data): a, b = arbiter.shapes if hasattr(a, 'goal_index'): self.goalScoredIn(a.goal_index) elif hasattr(b, 'goal_index'): self.goalScoredIn(b.goal_index) else: raise ValueError("Two objects with collision types used by soccer don't have a goal index.") return False handler.begin = handle_collide for x in range(len(self.names)): # Set up goal collider. pos = self.goals[x]['position'] del self.goals[x]['position'] obj = { 'collider': 'inherit', 'visual': self.goals[x], 'position': pos, 'physics': True, 'static': True, 'key': f'Goal-{x}', } self.goal_colliders.append(objectFactory(**obj)) self.goal_colliders[-1].shape.sensor = True self.goal_colliders[-1].shape.collision_type = self.GOAL_COLLISION_TYPE self.goal_colliders[-1].shape.goal_index = x World.instance.registerObject(self.goal_colliders[-1]) if self.show_goal_colliders: ScreenObjectManager.instance.registerVisual(self.goal_colliders[-1].visual, f'Soccer_DEBUG_collider-{len(self.goal_colliders)}') # Set up team scores self.team_scores.append(0) # Set up team name ScriptLoader.instance.object_map[f'name{x+1}Text'].text = self.names[x] self.updateScoreText() self.resetPositions() for robot in self.robots: robot.robot_class.onSpawn()
def generateGameColliders(self): self.ball_centre = objectFactory( **{ "collider": "inherit", "visual": { "name": "Circle", "radius": 0.1 }, "physics": True, "key": "IR-BALL", }) self.field_ball = ScriptLoader.instance.object_map[ "centreFieldBallDetector"] self.field = ScriptLoader.instance.object_map["centreField"] for x in range(len(self.spawns)): # Set up goal collider. self.goals[x]["zPos"] = 6 pos = self.goals[x]["position"] del self.goals[x]["position"] obj = { "collider": "inherit", "visual": self.goals[x], "position": pos, "physics": True, "static": True, "key": f"Goal-{x}", } self.goal_colliders.append(objectFactory(**obj)) if SoccerUIInteractor.instance.SHOW_GOAL_COLLIDERS: from ev3sim.visual.manager import ScreenObjectManager for i, collider in enumerate(self.goal_colliders): ScreenObjectManager.instance.registerVisual( collider.visual, f"Soccer_DEBUG_collider-{i}")
def loadElements(self, items): # Handle any programmatic color references. elements = [] from ev3sim.devices.base import initialise_device for item in items: assert "key" in item and "type" in item, f"Each item requires a key and type. {item}" if item["type"] == "visual": vis = visualFactory(**item) vis.key = item["key"] ScreenObjectManager.instance.registerVisual(vis, vis.key) self.object_map[item["key"]] = vis elements.append(vis) elif item["type"] == "object": devices = [] to_remove = [] for x in range(len(item.get("children", []))): if item["children"][x]["type"] == "device": devices.append(item["children"][x]) to_remove.append(x) for x in to_remove[::-1]: del item["children"][x] obj = objectFactory(**item) obj.key = item["key"] for index, device in enumerate(devices): # Instantiate the devices. initialise_device(device, obj, index) if item.get("physics", False): World.instance.registerObject(obj) ScreenObjectManager.instance.registerObject(obj, obj.key) self.object_map[obj.key] = obj elements.append(obj) return elements
def setUpBall(self): self.ball_centre = objectFactory( **{ "visual": {"name": "Image", "image_path": "custom/Square Dance/ui/flag.png", "scale": 1.3, "zPos": 3}, "physics": True, "key": "target", } ) self.ball_centre.shape.sensor = True self.ball_centre.shape.collision_type = self.BALL_COLLISION_TYPE self.ball_centre.shape.parent = self.ball_centre World.instance.registerObject(self.ball_centre) ScreenObjectManager.instance.registerObject(self.ball_centre, "target") handler = World.instance.space.add_collision_handler(self.BALL_COLLISION_TYPE, self.BOT_COLLISION_TYPE) saved_world_no = World.instance.spawn_no def handle_collide(arbiter, space, data): if World.instance.spawn_no != saved_world_no: return a, b = arbiter.shapes self.current_index += 1 if self.current_index < len(self.positions): if b.collision_type == self.BALL_COLLISION_TYPE: a, b = b, a a.parent.body.position = self.positions[self.current_index] a.parent.position = a.parent.body.position else: a.parent.body.position = [1000, 1000] a.parent.position = a.parent.body.position ScriptLoader.instance.object_map["positionText"].text = f"{self.current_index}/{len(self.positions)} Targets{'!' if self.current_index == len(self.positions) else '...'}" return False handler.begin = handle_collide
def spawnRobotFollows(self): self.bot_follows = [] for i in range(len(self.robots)): # Spawn the robot follow point collider. obj = objectFactory( **{ "collider": "inherit", "visual": { "name": "Circle", "radius": self.ROBOT_CENTRE_RADIUS, "fill": "#00ff00" if self.SHOW_ROBOT_COLLIDER else None, "stroke_width": 0, "sensorVisible": False, "zPos": 100, }, "physics": True, "key": f"Robot-{id}-follow", } ) obj.shape.filter = pymunk.ShapeFilter(categories=self.FOLLOW_POINT_CATEGORY) obj.shape.sensor = True obj.shape.collision_type = self.ROBOT_CENTRE_COLLISION_TYPE obj.shape._robot_index = i World.instance.registerObject(obj) if self.SHOW_ROBOT_COLLIDER: ScreenObjectManager.instance.registerObject(obj, obj.key) self.bot_follows.append(obj)
def _spawnFollowAtLocationWithIndicies(self, position, indicies): key = "Tile-follow-" + "-".join(list(map(str, indicies))) obj = objectFactory( **{ "collider": "inherit", "visual": { "name": "Circle", "radius": self.FOLLOW_POINT_RADIUS, "fill": self.follow_point_colour(indicies) if self.SHOW_FOLLOW_POINTS else None, "stroke_width": 0, "sensorVisible": False, "zPos": self.maxZpos + 0.2, }, "position": position, "physics": True, "static": True, "key": key, } ) obj.shape.filter = pymunk.ShapeFilter(categories=self.FOLLOW_POINT_CATEGORY) obj.shape.sensor = True obj.shape._follow_indexes = indicies obj.shape.collision_type = self.FOLLOW_POINT_COLLISION_TYPE World.instance.registerObject(obj) if self.SHOW_FOLLOW_POINTS: ScreenObjectManager.instance.registerObject(obj, obj.key) return obj
def onSpawn(self): super().onSpawn() # This is a rectangle which will contain the completion rects. bounding = self.rescue.tiles[self.index]["ui_spawned"].children[1] self.original_fill = bounding.visual.fill completion_rect_args = [ { "type": "object", "physics": False, "visual": { "name": "Rectangle", "width": bounding.visual.width, "height": bounding.visual.height / len(self.shortcuts), "fill": self.original_fill, "stroke": "#000000", "stroke_width": 0, "zPos": 5.5, }, "position": [0, ((len(self.shortcuts) - 1) / 2 - x) * bounding.visual.height / len(self.shortcuts)], } for x in range(len(self.shortcuts)) ] cur_length = len(bounding.children) self.completion_rects = [] for i, child in enumerate(completion_rect_args): child["key"] = bounding.key + f"-child-{i+cur_length}" bounding.children.append(objectFactory(**child)) self.completion_rects.append(bounding.children[-1]) ScreenObjectManager.instance.registerObject(bounding.children[-1], bounding.children[-1].key) bounding.children[-1].parent = bounding bounding.updateVisualProperties()
def loadMap(self, map_path): # load map custom_dir = find_abs_directory("workspace/custom/") full_path = join(custom_dir, "Arrow Maze", map_path) with open(full_path, "r") as f: conf = yaml.safe_load(f) # Despawn old stuff for arrow in self.arrows: ScreenObjectManager.instance.unregisterVisual(arrow.key) for child in arrow.children: ScreenObjectManager.instance.unregisterVisual(child.key) self.arrows = [] self.dimensions = conf["dimensions"] self.spawn = conf["spawn"] self.grid = conf["grid"] self.rotation = conf.get("rotation", 0) * math.pi / 180 # Spawn colours for x in range(self.dimensions[0]): for y in range(self.dimensions[1]): arrow_obj = { "name": "Image", "image_path": 'custom/Arrow Maze/ui/arrow.png', "hAlignment": "m", "vAlignment": "m", "scale": (self.width - self.margin) / 200, "zPos": 1, } children = [ { "visual": arrow_obj, } ] if self.grid[y][x] == "L": children[0]["rotation"] = math.pi elif self.grid[y][x] == "R": children[0]["rotation"] = 0 elif self.grid[y][x] == "U": children[0]["rotation"] = math.pi / 2 elif self.grid[y][x] == "D": children[0]["rotation"] = - math.pi / 2 else: children = [] c_obj = objectFactory( visual={ "name": "Rectangle", "width": self.width - self.margin, "height": self.width - self.margin, "fill": "#ffffff", "stroke_width": 0, "zPos": 0.5 }, children=children, key=f"sq-{x}-{y}", position=[ self.width * (x-self.offset[0]), -self.width * (y-self.offset[1]), ], ) ScreenObjectManager.instance.registerObject(c_obj, c_obj.key) self.arrows.append(c_obj)
def setUpBall(self): self.ball_centre = objectFactory( **{ "visual": { "name": "Image", "image_path": "custom/Search and Destroy/ui/flag.png", "scale": 1.3, "zPos": 3 }, "physics": True, "key": "target", }) self.ball_centre.shape.sensor = True self.ball_centre.shape.collision_type = self.BALL_COLLISION_TYPE World.instance.registerObject(self.ball_centre) ScreenObjectManager.instance.registerObject(self.ball_centre, "target") handler = World.instance.space.add_collision_handler( self.BALL_COLLISION_TYPE, self.BOT_COLLISION_TYPE) saved_world_no = World.instance.spawn_no def handle_collide(arbiter, space, data): if World.instance.spawn_no != saved_world_no: return a, b = arbiter.shapes self.accepted() return False handler.begin = handle_collide
def setUpGoal(self): self.goal = objectFactory( **{ "visual": { "name": "Rectangle", "width": 5, "height": 20, "fill": "#00ff00", "stroke_width": 0.1, "zPos": 3 }, "physics": True, "key": "target", "position": [57.5, -35] }) self.goal.shape.sensor = True self.goal.shape.collision_type = self.GOAL_COLLISION_TYPE World.instance.registerObject(self.goal) ScreenObjectManager.instance.registerObject(self.goal, "target")
def setUpGoal(self): self.alien = objectFactory( **{ "visual": {"name": "Image", "image_path": "custom/Social Distancing/ui/ship.png", "scale": 1, "zPos": 3}, "physics": True, "key": "target", } ) World.instance.registerObject(self.alien) ScreenObjectManager.instance.registerObject(self.alien, "target") self.distance_rect = visualFactory( **{ "name": "Rectangle", "width": 5, "height": 90, "fill": "#00ff00", "zPos": 3 } ) ScreenObjectManager.instance.registerVisual(self.distance_rect, "rect")
def startUp(self): assert len(self.names) == len(self.spawns) and len(self.spawns) == len( self.goals), "All player related arrays should be of equal size." # Initialise the goal colliders. self.goal_colliders = [] self.team_scores = [] self.locateBots() self.BOTS_PER_TEAM = math.ceil(len(self.robots) / len(self.names)) self.ball_centre = objectFactory( **{ "collider": "inherit", "visual": { "name": "Circle", "radius": 0.1 }, "physics": True, "key": "IR-BALL", }) self.ball_centre.shape.sensor = True self.ball_centre.shape.collision_type = self.BALL_COLLISION_TYPE World.instance.registerObject(self.ball_centre) handler = World.instance.space.add_collision_handler( self.BALL_COLLISION_TYPE, self.GOAL_COLLISION_TYPE) def handle_collide(arbiter, space, data): a, b = arbiter.shapes if hasattr(a, "goal_index"): self.goalScoredIn(a.goal_index) elif hasattr(b, "goal_index"): self.goalScoredIn(b.goal_index) else: raise ValueError( "Two objects with collision types used by soccer don't have a goal index." ) return False handler.begin = handle_collide # Initialise field collider for ball reset on white self.field_ball = ScriptLoader.instance.object_map[ "centreFieldBallDetector"] self.field_ball.shape.sensor = True self.field_ball.shape.collision_type = self.FIELD_BALL_COLLISION_TYPE if self.BALL_RESET_ON_WHITE: handler = World.instance.space.add_collision_handler( self.FIELD_BALL_COLLISION_TYPE, self.BALL_COLLISION_TYPE) def handle_separate_ball(arbiter, space, data): self.out_on_white_tick = self.BALL_RESET_WHITE_DELAY_SECONDS * ScriptLoader.instance.GAME_TICK_RATE return False def handle_collide_ball(arbiter, space, data): self.out_on_white_tick = 0 return False handler.separate = handle_separate_ball handler.begin = handle_collide_ball # Initialise field collider for out on white self.field = ScriptLoader.instance.object_map["centreField"] self.field.shape.sensor = True self.field.shape.collision_type = self.FIELD_COLLISION_TYPE if self.ENFORCE_OUT_ON_WHITE: handler = World.instance.space.add_collision_handler( self.FIELD_COLLISION_TYPE, self.BOT_COLLISION_TYPE) def handle_separate(arbiter, space, data): a, b = arbiter.shapes if hasattr(a, "bot_index"): self.penaliseBot(a.bot_index) elif hasattr(b, "bot_index"): self.penaliseBot(b.bot_index) else: raise ValueError( "Two objects with collision types used by soccer don't have a bot index." ) return False handler.separate = handle_separate for x in range(len(self.names)): # Set up goal collider. pos = self.goals[x]["position"] del self.goals[x]["position"] obj = { "collider": "inherit", "visual": self.goals[x], "position": pos, "physics": True, "static": True, "key": f"Goal-{x}", } self.goal_colliders.append(objectFactory(**obj)) self.goal_colliders[-1].shape.sensor = True self.goal_colliders[ -1].shape.collision_type = self.GOAL_COLLISION_TYPE self.goal_colliders[-1].shape.goal_index = x World.instance.registerObject(self.goal_colliders[-1]) if self.SHOW_GOAL_COLLIDERS: ScreenObjectManager.instance.registerVisual( self.goal_colliders[-1].visual, f"Soccer_DEBUG_collider-{len(self.goal_colliders)}") # Set up team scores self.team_scores.append(0) # Set up team name ScriptLoader.instance.object_map[ f"name{x+1}Text"].text = self.names[x] self.updateScoreText() self.resetPositions() for robot in self.robots: robot.robot_class.onSpawn()
def spawnPosition(self): self.setBotPos() for but in self.buttons: World.instance.unregisterObject(but) self.buttons = [] self.states = [random.randint(0, 2) for _ in range(3)] for x in range(3): c_key = f"movement_bot_colour-{x}" o_key = f"movement_bot-button-{x}" if c_key in ScreenObjectManager.instance.objects: ScreenObjectManager.instance.unregisterVisual(c_key) if o_key in ScreenObjectManager.instance.objects: ScreenObjectManager.instance.unregisterVisual(o_key) for y in range(3): if o_key + f"-{y}" in ScreenObjectManager.instance.objects: ScreenObjectManager.instance.unregisterVisual(o_key + f"-{y}") c_obj = visualFactory( name="Rectangle", width=5, height=5, position=[ self.INDICATOR_POSITIONS[x], -35, ], fill=self.COLOURS[self.states[x]], sensorVisible=True, stroke_width=0, zPos=0.5, ) ScreenObjectManager.instance.registerVisual(c_obj, c_key) buttonLight = visualFactory( name="Circle", radius=0.5, fill=self.COLOURS[self.states[x]], position=[ self.X_POSITIONS[x], self.Y_POSITIONS[self.states[x]], ], stroke_width=0, zPos=0.6, ) ScreenObjectManager.instance.registerVisual(buttonLight, o_key) for y in range(3): button = objectFactory(visual={ "name": "Rectangle", "width": 5, "height": 5, "fill": "#666666", "stroke_width": 0.1, "stroke": "#ffffff", "zPos": 0.5, }, position=[ self.X_POSITIONS[x], self.Y_POSITIONS[y], ], physics=True, key=o_key + f"-{y}") button.shape.sensor = True button.shape.collision_type = self.BALL_COLLISION_TYPE button.shape.movement_index = (x, y) self.buttons.append(button) World.instance.registerObject(button) ScreenObjectManager.instance.registerObject( button, o_key + f"-{y}") self.restartBots()
def loadMap(self, map_path): # load map custom_dir = find_abs_directory("workspace/custom/") full_path = join(custom_dir, "Memory Maze", map_path) with open(full_path, "r") as f: conf = yaml.safe_load(f) # Despawn old stuff for col in self.colours: ScreenObjectManager.instance.unregisterVisual(col.key) self.colours = [] for wall in self.walls: ScreenObjectManager.instance.unregisterVisual(wall.key) World.instance.unregisterObject(wall) self.walls = [] self.dimensions = conf["dimensions"] self.spawn = conf["spawn"] self.passcode = conf["passcode"] self.colour_map = conf["colours"] self.wall_map = conf["walls"] self.rotation = conf.get("rotation", 0) * math.pi / 180 # Spawn colours for x in range(self.dimensions[0]): for y in range(self.dimensions[1]): index = self.colour_map[y * self.dimensions[0] + x] if index == "_": continue c_key = f"movement_bot_colour-{x}-{y}" c_obj = visualFactory( name="Rectangle", width=self.width - self.margin, height=self.width - self.margin, position=[ self.width * (x - self.offset[0]), -self.width * (y - self.offset[1]), ], fill=self.COLOURS[index], sensorVisible=True, stroke_width=0, zPos=0.5, ) c_obj.key = c_key ScreenObjectManager.instance.registerVisual(c_obj, c_key) self.colours.append(c_obj) # Spawn walls # First, vertical for x in range(self.dimensions[0] + 1): for y in range(self.dimensions[1]): if self.wall_map[(y + 1) * self.dimensions[0] + y * (self.dimensions[0] + 1) + x] == "*": wall_key = f"movement_bot_wall-vert-{x}-{y}" wall_obj = objectFactory( visual={ "name": "Rectangle", "height": self.width, "width": 2, "stroke_width": 0, "fill": "#000000", "zPos": 0.7, }, position=[ (x - 0.5 - self.offset[0]) * self.width, -self.width * (y - self.offset[1]), ], rotation=0, physics=True, static=True, key=wall_key) self.walls.append(wall_obj) World.instance.registerObject(wall_obj) ScreenObjectManager.instance.registerObject( wall_obj, wall_key) # Next, horizontal for x in range(self.dimensions[0]): for y in range(self.dimensions[1] + 1): if self.wall_map[y * (2 * self.dimensions[0] + 1) + x] == "*": wall_key = f"movement_bot_wall-horizontal-{x}-{y}" wall_obj = objectFactory( visual={ "name": "Rectangle", "height": 2, "width": self.width, "stroke_width": 0, "fill": "#000000", "zPos": 0.7, }, position=[ (x - self.offset[0]) * self.width, -self.width * (y - self.offset[1] - 0.5), ], rotation=0, physics=True, static=True, key=wall_key) self.walls.append(wall_obj) World.instance.registerObject(wall_obj) ScreenObjectManager.instance.registerObject( wall_obj, wall_key)
def handleEvents(self): from ev3sim.simulation.loader import StateHandler, ScriptLoader events = list(pygame.event.get()) + self.unhandled_events self.unhandled_events = [] for event in events: if not StateHandler.instance.is_running: break self.screens[self.screen_stack[-1]].process_events(event) self.screens[self.screen_stack[-1]].handleEvent(event) if event.type == pygame.VIDEORESIZE: self.SCREEN_WIDTH, self.SCREEN_HEIGHT = event.size self._SCREEN_WIDTH_ACTUAL, self._SCREEN_HEIGHT_ACTUAL = self.SCREEN_WIDTH, self.SCREEN_HEIGHT # Preserve a 4:3 ratio. if self.SCREEN_WIDTH / self.SCREEN_HEIGHT < 4 / 3: self.SCREEN_HEIGHT = int(self.SCREEN_WIDTH * 3 / 4) else: self.SCREEN_WIDTH = int(self.SCREEN_HEIGHT * 4 / 3) self.screen = pygame.display.set_mode( (self._SCREEN_WIDTH_ACTUAL, self._SCREEN_HEIGHT_ACTUAL), pygame.RESIZABLE ) for key, menu in self.screens.items(): menu.setSize((self._SCREEN_WIDTH_ACTUAL, self._SCREEN_HEIGHT_ACTUAL)) for interactor in ScriptLoader.instance.active_scripts: if hasattr(interactor, "setSize"): interactor.setSize((self._SCREEN_WIDTH_ACTUAL, self._SCREEN_HEIGHT_ACTUAL)) for screen in self.screen_stack: if screen == self.SCREEN_SIM: for key in self.sorting_order: self.objects[key].calculatePoints() self.screens[screen].regenerateObjects() if event.type == pygame.QUIT: StateHandler.instance.is_running = False if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE: if len(self.screen_stack) == 1: StateHandler.instance.is_running = False else: self.popScreen() if event.type == EV3SIM_BOT_COMMAND and event.command_type == CommandSystem.TYPE_DRAW: try: possible_keys = [] for key in ScriptLoader.instance.object_map.keys(): if key.startswith(event.robot_id): possible_keys.append(key) if len(possible_keys) == 0: break possible_keys.sort(key=len) parent_bot = ScriptLoader.instance.object_map[possible_keys[0]] key = event.robot_id + "-" + event.payload["key"] to_remove = [] for x, child in enumerate(parent_bot.children): if child.key == key: to_remove.append(x) for x in to_remove[::-1]: del parent_bot.children[x] from ev3sim.objects.base import objectFactory obj = objectFactory( physics=False, visual=event.payload["obj"], position=event.payload["obj"].get("position", [0, 0]), rotation=event.payload["obj"].get("rotation", 0), key=key, ) if event.payload.get("on_bot", False): parent_bot.children.append(obj) obj.parent = parent_bot parent_bot.updateVisualProperties() life = event.payload.get("life", 3) self.registerVisual(obj.visual, key, kill_time=life, overwrite_key=True) except: pass return events
def startUp(self): super().startUp() self.posted = False self.ball = objectFactory( **{ "visual": {"name": "Circle", "radius": 2, "fill": "#000000", "zPos": 10}, "physics": True, "key": "falling_ball", "position": [0, 130], "friction": 0.75 } ) self.ball.shape.collision_type = self.BALL_COLLISION_TYPE World.instance.registerObject(self.ball) ScreenObjectManager.instance.registerObject(self.ball, self.ball.key) ended_cutoffs = [0] + self.CUTOFFS + [self.N_POINTS] sq_width = 200 / self.N_POINTS * 0.8 barricade_width = (200 - (sq_width * self.N_POINTS)) / self.N_POINTS for x in range(1, len(ended_cutoffs)): for y in range(ended_cutoffs[x-1], ended_cutoffs[x]): key = f"square_{y}" square = objectFactory( **{ "visual": {"name": "Rectangle", "width": sq_width, "height": 10, "fill": f"area_{x}_color", "zPos": 3}, "physics": True, "key": key, "position": [ 200 * (y + 0.5) / self.N_POINTS - 100, -70, ] } ) square.shape.sensor = True square.shape.collision_type = self.SQUARE_COLLISION_TYPE square.shape.sq_index = (x+1, y+1) World.instance.registerObject(square) ScreenObjectManager.instance.registerObject(square, key) num = visualFactory( **{ "name": "Text", "text": str(y+1), "font_style": "fonts/Poppins-Regular.ttf", "fill": "#000000", "font_size": 24, "position": [ 200 * (y + 0.5) / self.N_POINTS - 100, -70, ], "hAlignment": "m", "vAlignment": "m", "zPos": 5, "key": f"text_{y}", } ) ScreenObjectManager.instance.registerVisual(num, f"text_{y}") for y in range(self.N_POINTS + 1): key = f"barricade_{y}" barricade = objectFactory( **{ "visual": {"name": "Rectangle", "width": barricade_width, "height": 15, "fill": "#000000", "zPos": 3}, "physics": True, "static": True, "key": key, "position": [ y * 200 / self.N_POINTS - 100, -67.5, ] } ) World.instance.registerObject(barricade) ScreenObjectManager.instance.registerObject(barricade, key) for x in range(self.bumper_lines): more = x % 2 == 0 for y in range(self.double_spots // 2 + more): key = f"bumper_{x}_{y}" position = [ 200 / (self.double_spots + 1) * ((2*y+1) if more else (2*y+2)) - 100, -55 + (x+1) * self.bumper_y_distance, ] bumper = objectFactory( **{ "visual": {"name": "Circle", "radius": self.bumper_radius, "fill": "#000000", "zPos": 3}, "physics": True, "static": True, "key": key, "position": position, } ) World.instance.registerObject(bumper) ScreenObjectManager.instance.registerObject(bumper, key) ScriptLoader.instance.object_map["drop"].shape.sensor = True handler = World.instance.space.add_collision_handler(self.BALL_COLLISION_TYPE, self.SQUARE_COLLISION_TYPE) saved_world_no = World.instance.spawn_no def handle_collide(arbiter, space, data): if World.instance.spawn_no != saved_world_no: return a, b = arbiter.shapes if not hasattr(a, "sq_index"): a, b = b, a self.onFallen(a.sq_index) return False handler.begin = handle_collide