def init_camera(self, feature_dimensions, map_size, camera_width_world_units): """Initialize the camera (especially for feature_units). This is called in the constructor and may be called repeatedly after `Features` is constructed, since it deals with rescaling coordinates and not changing environment/action specs. Args: feature_dimensions: See the documentation in `AgentInterfaceFormat`. map_size: The size of the map in world units. camera_width_world_units: See the documentation in `AgentInterfaceFormat`. Raises: ValueError: If map_size or camera_width_world_units are falsey (which should mainly happen if called by the constructor). """ if not map_size or not camera_width_world_units: raise ValueError( "Either pass the game_info with raw enabled, or map_size and " "camera_width_world_units in order to use feature_units or camera" "position.") map_size = point.Point.build(map_size) self._world_to_world_tl = transform.Linear(point.Point(1, -1), point.Point(0, map_size.y)) self._world_tl_to_world_camera_rel = transform.Linear( offset=-map_size / 4) world_camera_rel_to_feature_screen = transform.Linear( feature_dimensions.screen / camera_width_world_units, feature_dimensions.screen / 2) self._world_to_feature_screen_px = transform.Chain( self._world_to_world_tl, self._world_tl_to_world_camera_rel, world_camera_rel_to_feature_screen, transform.PixelToCoord())
def update_transformations(self): # Create transformations self._world_to_minimap = transform.Linear(point.Point(1, -1), point.Point(0, self._map_size.y)) max_map_dim = self._map_size.max_dim() self._minimap_to_fl_minimap = transform.Linear( self._minimap_size / max_map_dim) self._world_to_fl_minimap = transform.Chain( self._world_to_minimap, self._minimap_to_fl_minimap, transform.Floor()) # Flip and zoom to the camera area. Update the offset as the camera moves. # Get camera corner cam_x = -1 * (self._camera_pos['x'] - (self._camera_width / 2)) cam_y = 1 * (self._camera_pos['y'] + (self._camera_width / 2)) cam_pos = point.Point(x = cam_x, y = cam_y) self._reorient_world = transform.Linear(point.Point(1, -1), offset=cam_pos) self._world_to_screen = transform.Linear(point.Point(1, 1), point.Point(0, 0)) #self._world_to_screen = transform.Linear(point.Point(1, -1), # point.Point(0, self._map_size.y)) self._screen_to_fl_screen = transform.Linear( self._screen_size / self._camera_width) self._world_to_fl_screen = transform.Chain( self._reorient_world, self._world_to_screen, self._screen_to_fl_screen, transform.PixelToCoord())
def _world_tl_to_minimap_px(raw_unit): # TODO configurable resolution minimap_px = point.Point(64.0, 64.0) map_size = point.Point(88.0, 96.0) pos_transform = transform.Chain( transform.Linear(minimap_px / map_size.max_dim()), transform.PixelToCoord()) screen_pos = pos_transform.fwd_pt(point.Point(raw_unit.x, raw_unit.y)) return screen_pos.x, screen_pos.y
def _init_camera(self): self._world_to_world_tl = transform.Linear( point.Point(1, -1), point.Point(0, self._map_size.y)) self._world_tl_to_world_camera_rel = transform.Linear( offset=-self._map_size / 4) self._world_to_feature_screen_px = transform.Chain( self._world_to_world_tl, self._world_tl_to_world_camera_rel, transform.Linear( (self._screen_size_px / self._camera_width_world_units), self._screen_size_px / 2), transform.PixelToCoord())
def _init_camera(self, feature_dimensions, map_size, camera_width_world_units): """Initialize the feature_units camera.""" if not map_size or not camera_width_world_units: raise ValueError( "Either pass the game_info with raw enabled, or map_size and " "camera_width_world_units in order to use feature_units.") map_size = point.Point.build(map_size) self._world_to_world_tl = transform.Linear(point.Point(1, -1), point.Point(0, map_size.y)) self._world_tl_to_world_camera_rel = transform.Linear( offset=-map_size / 4) world_camera_rel_to_feature_screen = transform.Linear( feature_dimensions.screen / camera_width_world_units, feature_dimensions.screen / 2) self._world_to_feature_screen_px = transform.Chain( self._world_to_world_tl, self._world_tl_to_world_camera_rel, world_camera_rel_to_feature_screen, transform.PixelToCoord())
def init_window(self): """Initialize the pygame window and lay out the surfaces.""" if os.name == "nt": # Enable DPI awareness on Windows to give the correct window size. ctypes.windll.user32.SetProcessDPIAware() # pytype: disable=module-attr pygame.init() if self._render_rgb and self._rgb_screen_px: main_screen_px = self._rgb_screen_px else: main_screen_px = self._feature_screen_px window_size_ratio = main_screen_px if self._feature_screen_px and self._render_feature_grid: # Want a roughly square grid of feature layers, each being roughly square. num_feature_layers = (len(features.SCREEN_FEATURES) + len(features.MINIMAP_FEATURES)) feature_cols = math.ceil(math.sqrt(num_feature_layers)) feature_rows = math.ceil(num_feature_layers / feature_cols) features_layout = point.Point(feature_cols, feature_rows * 1.05) # make room for titles # Scale features_layout to main_screen_px height so we know its width. features_aspect_ratio = (features_layout * main_screen_px.y / features_layout.y) window_size_ratio += point.Point(features_aspect_ratio.x, 0) window_size_px = window_size_ratio.scale_max_size( _get_max_window_size()).ceil() # Create the actual window surface. This should only be blitted to from one # of the sub-surfaces defined below. self._window = pygame.display.set_mode(window_size_px, 0, 32) pygame.display.set_caption("Starcraft Viewer") # The sub-surfaces that the various draw functions will draw to. self._surfaces = [] def add_surface(surf_type, surf_loc, world_to_surf, world_to_obs, draw_fn): """Add a surface. Drawn in order and intersect in reverse order.""" sub_surf = self._window.subsurface( pygame.Rect(surf_loc.tl, surf_loc.size)) self._surfaces.append(_Surface( sub_surf, surf_type, surf_loc, world_to_surf, world_to_obs, draw_fn)) self._scale = window_size_px.y // 30 self._font_small = pygame.font.Font(None, int(self._scale * 0.5)) self._font_large = pygame.font.Font(None, self._scale) def check_eq(a, b): """Used to run unit tests on the transforms.""" assert (a - b).len() < 0.0001, "%s != %s" % (a, b) # World has origin at bl, world_tl has origin at tl. self._world_to_world_tl = transform.Linear( point.Point(1, -1), point.Point(0, self._map_size.y)) check_eq(self._world_to_world_tl.fwd_pt(point.Point(0, 0)), point.Point(0, self._map_size.y)) check_eq(self._world_to_world_tl.fwd_pt(point.Point(5, 10)), point.Point(5, self._map_size.y - 10)) # Move the point to be relative to the camera. This gets updated per frame. self._world_tl_to_world_camera_rel = transform.Linear( offset=-self._map_size / 4) check_eq(self._world_tl_to_world_camera_rel.fwd_pt(self._map_size / 4), point.Point(0, 0)) check_eq( self._world_tl_to_world_camera_rel.fwd_pt( (self._map_size / 4) + point.Point(5, 10)), point.Point(5, 10)) if self._feature_screen_px: # Feature layer locations in continuous space. feature_world_per_pixel = (self._feature_screen_px / self._feature_camera_width_world_units) world_camera_rel_to_feature_screen = transform.Linear( feature_world_per_pixel, self._feature_screen_px / 2) check_eq(world_camera_rel_to_feature_screen.fwd_pt(point.Point(0, 0)), self._feature_screen_px / 2) check_eq( world_camera_rel_to_feature_screen.fwd_pt( point.Point(-0.5, -0.5) * self._feature_camera_width_world_units), point.Point(0, 0)) self._world_to_feature_screen = transform.Chain( self._world_to_world_tl, self._world_tl_to_world_camera_rel, world_camera_rel_to_feature_screen) self._world_to_feature_screen_px = transform.Chain( self._world_to_feature_screen, transform.PixelToCoord()) world_tl_to_feature_minimap = transform.Linear( self._feature_minimap_px / self._map_size.max_dim()) check_eq(world_tl_to_feature_minimap.fwd_pt(point.Point(0, 0)), point.Point(0, 0)) check_eq(world_tl_to_feature_minimap.fwd_pt(self._map_size), self._map_size.scale_max_size(self._feature_minimap_px)) self._world_to_feature_minimap = transform.Chain( self._world_to_world_tl, world_tl_to_feature_minimap) self._world_to_feature_minimap_px = transform.Chain( self._world_to_feature_minimap, transform.PixelToCoord()) if self._rgb_screen_px: # RGB pixel locations in continuous space. # TODO(tewalds): Use a real 3d projection instead of orthogonal. rgb_world_per_pixel = (self._rgb_screen_px / 24) world_camera_rel_to_rgb_screen = transform.Linear( rgb_world_per_pixel, self._rgb_screen_px / 2) check_eq(world_camera_rel_to_rgb_screen.fwd_pt(point.Point(0, 0)), self._rgb_screen_px / 2) check_eq( world_camera_rel_to_rgb_screen.fwd_pt( point.Point(-0.5, -0.5) * 24), point.Point(0, 0)) self._world_to_rgb_screen = transform.Chain( self._world_to_world_tl, self._world_tl_to_world_camera_rel, world_camera_rel_to_rgb_screen) self._world_to_rgb_screen_px = transform.Chain( self._world_to_rgb_screen, transform.PixelToCoord()) world_tl_to_rgb_minimap = transform.Linear( self._rgb_minimap_px / self._map_size.max_dim()) check_eq(world_tl_to_rgb_minimap.fwd_pt(point.Point(0, 0)), point.Point(0, 0)) check_eq(world_tl_to_rgb_minimap.fwd_pt(self._map_size), self._map_size.scale_max_size(self._rgb_minimap_px)) self._world_to_rgb_minimap = transform.Chain( self._world_to_world_tl, world_tl_to_rgb_minimap) self._world_to_rgb_minimap_px = transform.Chain( self._world_to_rgb_minimap, transform.PixelToCoord()) # Renderable space for the screen. screen_size_px = main_screen_px.scale_max_size(window_size_px) minimap_size_px = self._map_size.scale_max_size(screen_size_px / 4) minimap_offset = point.Point(0, (screen_size_px.y - minimap_size_px.y)) if self._render_rgb: rgb_screen_to_main_screen = transform.Linear( screen_size_px / self._rgb_screen_px) add_surface(SurfType.RGB | SurfType.SCREEN, point.Rect(point.origin, screen_size_px), transform.Chain( # surf self._world_to_rgb_screen, rgb_screen_to_main_screen), self._world_to_rgb_screen_px, self.draw_screen) rgb_minimap_to_main_minimap = transform.Linear( minimap_size_px / self._rgb_minimap_px) add_surface(SurfType.RGB | SurfType.MINIMAP, point.Rect(minimap_offset, minimap_offset + minimap_size_px), transform.Chain( # surf self._world_to_rgb_minimap, rgb_minimap_to_main_minimap), self._world_to_rgb_minimap_px, self.draw_mini_map) else: feature_screen_to_main_screen = transform.Linear( screen_size_px / self._feature_screen_px) add_surface(SurfType.FEATURE | SurfType.SCREEN, point.Rect(point.origin, screen_size_px), transform.Chain( # surf self._world_to_feature_screen, feature_screen_to_main_screen), self._world_to_feature_screen_px, self.draw_screen) feature_minimap_to_main_minimap = transform.Linear( minimap_size_px / self._feature_minimap_px) add_surface(SurfType.FEATURE | SurfType.MINIMAP, point.Rect(minimap_offset, minimap_offset + minimap_size_px), transform.Chain( # surf self._world_to_feature_minimap, feature_minimap_to_main_minimap), self._world_to_feature_minimap_px, self.draw_mini_map) if self._feature_screen_px and self._render_feature_grid: # Add the feature layers features_loc = point.Point(screen_size_px.x, 0) feature_pane = self._window.subsurface( pygame.Rect(features_loc, window_size_px - features_loc)) feature_pane.fill(colors.white / 2) feature_pane_size = point.Point(*feature_pane.get_size()) feature_grid_size = feature_pane_size / point.Point(feature_cols, feature_rows) feature_layer_area = self._feature_screen_px.scale_max_size( feature_grid_size) feature_layer_padding = feature_layer_area // 20 feature_layer_size = feature_layer_area - feature_layer_padding * 2 feature_font_size = int(feature_grid_size.y * 0.09) feature_font = pygame.font.Font(None, feature_font_size) feature_counter = itertools.count() def add_feature_layer(feature, surf_type, world_to_surf, world_to_obs): """Add a feature layer surface.""" i = next(feature_counter) grid_offset = point.Point(i % feature_cols, i // feature_cols) * feature_grid_size text = feature_font.render(feature.full_name, True, colors.white) rect = text.get_rect() rect.center = grid_offset + point.Point(feature_grid_size.x / 2, feature_font_size) feature_pane.blit(text, rect) surf_loc = (features_loc + grid_offset + feature_layer_padding + point.Point(0, feature_font_size)) add_surface(surf_type, point.Rect(surf_loc, surf_loc + feature_layer_size), world_to_surf, world_to_obs, lambda surf: self.draw_feature_layer(surf, feature)) # Add the minimap feature layers feature_minimap_to_feature_minimap_surf = transform.Linear( feature_layer_size / self._feature_minimap_px) world_to_feature_minimap_surf = transform.Chain( self._world_to_feature_minimap, feature_minimap_to_feature_minimap_surf) for feature in features.MINIMAP_FEATURES: add_feature_layer(feature, SurfType.FEATURE | SurfType.MINIMAP, world_to_feature_minimap_surf, self._world_to_feature_minimap_px) # Add the screen feature layers feature_screen_to_feature_screen_surf = transform.Linear( feature_layer_size / self._feature_screen_px) world_to_feature_screen_surf = transform.Chain( self._world_to_feature_screen, feature_screen_to_feature_screen_surf) for feature in features.SCREEN_FEATURES: add_feature_layer(feature, SurfType.FEATURE | SurfType.SCREEN, world_to_feature_screen_surf, self._world_to_feature_screen_px) # Add the help screen help_size = point.Point( (max(len(s) for s, _ in self.shortcuts) + max(len(s) for _, s in self.shortcuts)) * 0.4 + 4, len(self.shortcuts) + 3) * self._scale help_rect = point.Rect(window_size_px / 2 - help_size / 2, window_size_px / 2 + help_size / 2) add_surface(SurfType.CHROME, help_rect, None, None, self.draw_help) # Arbitrarily set the initial camera to the center of the map. self._update_camera(self._map_size / 2)