def compileSVASheet(sheet, color): color = tuple([255-c for c in color]) width = sheet.get_width() height = sheet.get_height() / 3 colorSurf = Surface((width, height)) colorSurf.fill(color) colorSurf.blit(sheet, (0, 0), None, BLEND_MULT) # Now create a white surface so we can invert the Saturation map result = Surface((width, height), SRCALPHA) result.fill((255, 255, 255)) result.blit(colorSurf, (0, 0), None, BLEND_RGB_SUB) # Multiply this with the Value Map result.blit(sheet, (0, -height), None, BLEND_MULT) # copy the alpha mask from the spritesheet alpha = Surface((width, height)) alpha.blit(sheet, (0, -2 * height)) #convert the (nearly done) Surface to per pixel alpha result.convert_alpha() # Use Numpy here numWhite = surfarray.pixels_alpha( result ) numAlpha = surfarray.pixels3d( alpha ) numWhite[ :, : ] = numAlpha[ :, :, 0 ] return result.copy()
def smart_convert( original: pygame.Surface, colorkey: Optional[ColorLike], pixelalpha: bool, ) -> pygame.Surface: """ Return new pygame Surface with optimal pixel/data format This method does several interactive_tests on a surface to determine the optimal flags and pixel format for each tile surface. Parameters: original: tile surface to inspect colorkey: optional colorkey for the tileset image pixelalpha: if true, prefer per-pixel alpha surfaces Returns: new tile surface """ # tiled set a colorkey if colorkey: tile = original.convert() tile.set_colorkey(colorkey, pygame.RLEACCEL) # TODO: if there is a colorkey, count the colorkey pixels to determine if RLEACCEL should be used # no colorkey, so use a mask to determine if there are transparent pixels else: tile_size = original.get_size() threshold = 254 # the default try: # count the number of pixels in the tile that are not transparent px = pygame.mask.from_surface(original, threshold).count() except: # pygame_sdl2 will fail because the mask module is not included # in this case, just convert_alpha and return it return original.convert_alpha() # there are no transparent pixels in the image if px == tile_size[0] * tile_size[1]: tile = original.convert() # there are transparent pixels, and set for perpixel alpha elif pixelalpha: tile = original.convert_alpha() # there are transparent pixels, and we won't handle them else: tile = original.convert() return tile
def render(self, text, antialias, forecolor, backcolor=(0, 0, 0, 255)): size = self.size(text) img = NSImage.alloc().initWithSize_(size) img.lockFocus() NSString.drawAtPoint_withAttributes_( text, (0.0, 0.0), { NSFontAttributeName: self._font, NSUnderlineStyleAttributeName: self._isUnderline and 1.0 or None, NSBackgroundColorAttributeName: backcolor and _getColor(backcolor) or None, NSForegroundColorAttributeName: _getColor(forecolor), }) rep = NSBitmapImageRep.alloc().initWithFocusedViewRect_( ((0.0, 0.0), size)) img.unlockFocus() if rep.samplesPerPixel() == 4: s = Surface(size, SRCALPHA | SWSURFACE, 32, [-1 << 24, 0xff << 16, 0xff << 8, 0xff]) a = Numeric.reshape( Numeric.fromstring(rep.bitmapData(), typecode=Numeric.Int32), (size[1], size[0])) blit_array(s, Numeric.swapaxes(a, 0, 1)) return s.convert_alpha()
def work_text(self, timer=None, size=1): color = (255, 255, 255) string = self.disp_score(timer) background = (0, 0, 0, 0.4 * 255) font_size = 15 * size font = py.font.SysFont("monospace", int(font_size), bold=True) line_width = 25 lines = string.split('/ ') labels = [] for line in lines: render = font.render(line, 1, color) labels.append(render) width = max([f.get_width() for f in labels]) height = 0 ys = [0] for f in labels: height += f.get_height() + 3 ys += [height] surface = Surface((width, height), SRCALPHA, 32) surface = surface.convert_alpha() if background != None: surface.fill(background) y = 0 for label in labels: surface.blit(label, (0, ys[y])) y += 1 return surface
def __init__(self, relative_rect: pygame.Rect, image_surface: pygame.Surface, manager: IUIManagerInterface, container: Union[IContainerLikeInterface, None] = None, parent_element: UIElement = None, object_id: Union[str, None] = None, anchors: Dict[str, str] = None): super().__init__(relative_rect, manager, container, starting_height=1, layer_thickness=1, anchors=anchors) self._create_valid_ids(container=container, parent_element=parent_element, object_id=object_id, element_id='image') self.original_image = None image_surface = image_surface.convert_alpha( ) # GUI images must support an alpha channel if (image_surface.get_width() != self.rect.width or image_surface.get_height() != self.rect.height): self.original_image = image_surface self.set_image( pygame.transform.smoothscale(self.original_image, self.rect.size)) else: self.set_image(image_surface)
def set_alpha_from_intensity(surface, alpha_factor, decay_mode, color): if not HAS_PIL: raise Exception("PIL was not found on this machine.") if not HAS_NUMPY: raise Exception("NumPy was not found on this machine.") rect = surface.get_rect() newsurf = Surface(rect.size, SRCALPHA, depth=surface.get_bitsize()) newsurf = newsurf.convert_alpha() newsurf.blit(surface, (0, 0)) arrayrgb = surfarray.pixels3d(newsurf) arraya = surfarray.pixels_alpha(newsurf) bulk_color = tuple(color) for x in range(rect.left, rect.right): for y in range(rect.top, rect.bottom): color = tuple(arrayrgb[x][y]) light = square_color_norm(color) alpha = float(light)/MAX_NORM * 255 arrayrgb[x][y][0] = bulk_color[0] arrayrgb[x][y][1] = bulk_color[1] arrayrgb[x][y][2] = bulk_color[2] if decay_mode == "linear": actual_alpha = int(255 - alpha) elif decay_mode == "exponential": tuning_factor = 1.03 actual_alpha = int(255*tuning_factor**-alpha) ## elif decay_mode == "quadratic": ## pass else: raise Exception("decay_mode not recognized: " + decay_mode) actual_alpha *= alpha_factor arraya[x][y] = actual_alpha return newsurf
def make_new_transparent_surface(surf: Surface): """ make a new transparent surface with the same size as the passed in one """ surf = Surface((surf.get_width(), surf.get_height()), SRCALPHA, 32) surf = surf.convert_alpha() return surf
def set_alpha_from_intensity(surface, alpha_factor, decay_mode, color): if not HAS_PIL: raise Exception("PIL was not found on this machine.") if not HAS_NUMPY: raise Exception("NumPy was not found on this machine.") rect = surface.get_rect() newsurf = Surface(rect.size, SRCALPHA, depth=surface.get_bitsize()) newsurf = newsurf.convert_alpha() newsurf.blit(surface, (0, 0)) arrayrgb = surfarray.pixels3d(newsurf) arraya = surfarray.pixels_alpha(newsurf) bulk_color = tuple(color) for x in range(rect.left, rect.right): for y in range(rect.top, rect.bottom): color = tuple(arrayrgb[x][y]) light = square_color_norm(color) alpha = float(light) / MAX_NORM * 255 arrayrgb[x][y][0] = bulk_color[0] arrayrgb[x][y][1] = bulk_color[1] arrayrgb[x][y][2] = bulk_color[2] if decay_mode == "linear": actual_alpha = int(255 - alpha) elif decay_mode == "exponential": tuning_factor = 1.03 actual_alpha = int(255 * tuning_factor**-alpha) ## elif decay_mode == "quadratic": ## pass else: raise Exception("decay_mode not recognized: " + decay_mode) actual_alpha *= alpha_factor arraya[x][y] = actual_alpha return newsurf
def create_blank_base_image(self): # Give the agent a larger Surface (by sqrt(2)) to work with since it may rotate. blank_base_image = Surface( (self.rect.w * Agent.SQRT_2, self.rect.h * Agent.SQRT_2)) # This was important, but I don't remember why. blank_base_image = blank_base_image.convert_alpha() blank_base_image.fill((0, 0, 0, 0)) return blank_base_image
def __init__(self): """Initialize the core variables.""" self.change_to = "" self.running = True surface = Surface([960, 1024], SRCALPHA, 32) self.windows = {"main": surface.convert_alpha()} self.musics = None
def create_base(self): surf = Surface((64, 32), pygame.SRCALPHA, 32) pygame.draw.rect(surf, self.color, Rect(0, 8, 44, 16)) pygame.draw.polygon(surf, self.color, [(44, 0), (64, 16), (44, 32)]) # noinspection PyArgumentList self.base_image = surf.convert_alpha() self.drawable = Drawable(self.base_image.copy(), self.position, True, True)
def create_blank_base_image(self): # Give the agent a larger Surface (by sqrt(2)) to work with since it may rotate. blank_base_image = Surface( (self.rect.w * Agent.SQRT_2, self.rect.h * Agent.SQRT_2)) # This sets the rectangle to be transparent. # Otherwise it would be black and would cover nearby agents. # Even though it's a method of Surface, it can also take a Surface parameter. # If the Surface parameter is not given, PyCharm complains. # noinspection PyArgumentList blank_base_image = blank_base_image.convert_alpha() blank_base_image.fill((0, 0, 0, 0)) return blank_base_image
def CreateClassesSurface(self, classes): print('CreateClassesSurface') if classes: timeSurfaceText = ConvertMilitaryToStd(classes[0]['LEC']['Start']) else: timeSurfaceText = 'No More Classes Today' timeSurfaceFont = font.Font(*self.timeSlotFont) timeSurface = timeSurfaceFont.render(timeSurfaceText, True, (51, 51, 51)) classSurfaceHeight = (self.height - (timeSurface.get_rect().height * 2) - ((self.scheduleDisplay_numberOfClasses - 1) * self.classSurface_heightBuffer) ) / self.scheduleDisplay_numberOfClasses classesSurfaceHeight = timeSurface.get_rect().height + ( len(classes) * (classSurfaceHeight + self.classSurface_heightBuffer)) classesSurface = Surface( (self.width - (self.classSurface_widthBuffer * 2), classesSurfaceHeight), SRCALPHA, 32) classesSurface.blit(timeSurface, (self.classSurface_widthBuffer, 0)) for i, meeting in enumerate(classes): nextClass = self.CreateClassSurface( meeting, self.classSurface_bgColors[i % 2], self.width - (2 * self.classSurface_widthBuffer), classSurfaceHeight) classesSurface.blit(nextClass, (0, timeSurface.get_rect().height + (nextClass.get_rect().height + self.classSurface_heightBuffer) * i)) classesSurface.convert_alpha() if timeSurfaceText != 'No More Classes Today': currentTime = datetime.now() timeSlot = datetime.combine( currentTime.date(), datetime.strptime(timeSurfaceText, '%I:%M %p').time()) timeSlot = timeSlot + timedelta(minutes=self.timeSlotDisplayBuffer) self.classesSurfacesAndTimes.append((classesSurface, timeSlot)) return classesSurface
class Cell(sprite.Sprite): def __init__(self, x, y, r=BG[0], g=BG[1], b=BG[2], a=BG[3]): sprite.Sprite.__init__(self) self.posx = x self.posy = y self.color = (r, g, b, a) self.image = None self.rect = None def set(self, w, h): self.image = Surface([w, h]) self.image.convert_alpha() self.rect = self.image.get_rect() self.rect.topleft = (self.posx, self.posy) def fill(self, r, g, b, a, color=(-1, -1, -1)): if color != (-1, -1, -1): self.color = (r, g, b, a) else: self.color = color def update(self): self.image.fill(self.color)
def pad(source: Surface, margins: tuple, background_color=None) -> Surface: ''' Pads the source Surface by margins, a quintuple on the form (top, right, bottom, left), or a Margins object. ''' margins = Margins(*margins) source_rect = source.get_rect() padded_rect = Rect(source_rect) padded_rect.w += margins.left + margins.right padded_rect.h += margins.top + margins.bottom padded_surface = Surface(padded_rect.size, pygame.SRCALPHA, 32) padded_surface = padded_surface.convert_alpha() if background_color: padded_surface.fill(background_color) padded_surface.blit(source, (margins.left, margins.top)) return padded_surface
def get_image(self, x: int, y: int, width: int, height: int, alpha: bool = False) -> Surface: """ Extracts sprite of given point (x, y) (left, top) and width and height. Alpha boolean keyword argument for converting the sprite in alpha or non-alpha. """ image = Surface((width, height)) image.blit(self.spritesheet, (0, 0), (x, y, width, height)) image.set_colorkey((0, 0, 0)) image.set_alpha(255) if alpha: return image.convert_alpha() return image.convert()
def render(self, text, antialias, forecolor, backcolor=(0,0,0,255)): size = self.size(text) img = NSImage.alloc().initWithSize_(size) img.lockFocus() NSString.drawAtPoint_withAttributes_(text, (0.0, 0.0), { NSFontAttributeName: self._font, NSUnderlineStyleAttributeName: self._isUnderline and 1.0 or None, NSBackgroundColorAttributeName: backcolor and _getColor(backcolor) or None, NSForegroundColorAttributeName: _getColor(forecolor), }) rep = NSBitmapImageRep.alloc().initWithFocusedViewRect_(((0.0, 0.0), size)) img.unlockFocus() if rep.samplesPerPixel() == 4: s = Surface(size, SRCALPHA|SWSURFACE, 32, [-1<<24,0xff<<16,0xff<<8,0xff]) a = Numeric.reshape(Numeric.fromstring(rep.bitmapData(), typecode=Numeric.Int32), (size[1], size[0])) blit_array(s, Numeric.swapaxes(a,0,1)) return s.convert_alpha()
def tick(self, dt): if self.engine.gameMgr.game_status == 'IN_GAME' or self.engine.gameMgr.game_status == 'ENTRY_ANIMATION': if self.show_fps: self.engine.gfxMgr.uiScreen.blit( self.engine.gfxMgr.update_fps(), (10, 0)) if self.show_grounded: self.engine.gfxMgr.uiScreen.blit( self.engine.gfxMgr.update_is_grounded(), (300, 0)) if self.show_jumps_left: self.engine.gfxMgr.uiScreen.blit( self.engine.gfxMgr.update_show_jumps(), (600, 0)) if self.show_bullet_count: self.engine.gfxMgr.uiScreen.blit( self.engine.gfxMgr.update_bullet_count(), (900, 0)) if self.show_platforms_rendered: self.engine.gfxMgr.uiScreen.blit( self.engine.gfxMgr.update_platforms_rendered(), (10, 50)) if self.show_total_enemy_count: self.engine.gfxMgr.uiScreen.blit( self.engine.gfxMgr.update_enemy_count(), (300, 50)) if self.show_total_enemy_rendered: self.engine.gfxMgr.uiScreen.blit( self.engine.gfxMgr.update_enemy_rendered_count(), (600, 50)) if self.show_game_status: self.engine.gfxMgr.uiScreen.blit( self.engine.gfxMgr.update_game_status(), (900, 50)) self.engine.gfxMgr.uiScreen.blit( self.engine.gfxMgr.update_player_lives_left(), (10, 100)) elif self.engine.gameMgr.game_status == 'MENU': transparent_background = Surface( (self.engine.config.window_size[0], self.engine.config.window_size[1])) transparent_background = transparent_background.convert_alpha() transparent_background.fill((0, 0, 0, 60)) self.engine.gfxMgr.uiScreen.blit(transparent_background, (0, 0)) for button in self.main_menu_buttons: button.draw()
def get_label(self, string, break_char="", *, width=None, height=None, scale=1, color=(255, 255, 0), background=None): """ Generates text in a given area, wrapped at :break_char: Returns PyGame surface """ # Scaling if width: width = int(width * (scale**-1)) if height: height = int(height * (scale**-1)) # Get labels labels = self.get_lines(string, break_char, width, scale, color) # Generate blank surface if not width: width = max([f.get_width() for f in labels]) if not height: height = sum([f.get_height() + 2 for f in labels]) surface = Surface((width, height), SRCALPHA, 32) surface = surface.convert_alpha() if background: surface.fill(background) # Add lines y = 0 for label in labels: surface.blit(label, (0, y)) y += label.get_height() + 2 return surface
class Creature(Sprite): ''' Class represents a ''' def __init__(self, x=0, y=0, theta=0): self.y = x self.y = y self.theta = theta self.v = 1 self.energy = 5000 + abs(50 * randn()) self.brain = createEmpty(10, 10) self.width = CREATURE_WIDTH self.height = CREATURE_HEIGHT self.senses = [] self.base_image = Surface((self.width, self.height), pygame.SRCALPHA, 32) self.base_image = self.base_image.convert_alpha() self.brain_data = None self.rect = (0, 0, self.width, self.height) self._render() self.image = pygame.transform.rotate(self.base_image, theta) ''' updates the creature for the next round ''' def tick(self): self.brain.elapseTime(10, [(randrange(1, 1000) * 1.0)/ 1000 for _ in xrange(10)]) self.y -= math.sin(deg2rad(self.theta)) * self.v self.y += math.cos(deg2rad(self.theta)) * self.v self.theta += (random.randrange(60)) - 30 self.image = pygame.transform.rotate(self.base_image, self.theta) ''' rendering for the basic, ugly creature ''' def _render(self): pygame.draw.circle(self.base_image, pygame.Color(0, 128, 64), (self.width/2, self.height/2), self.width/2, self.width/2) s = sqrt(2) * 12 pygame.draw.rect(self.base_image, pygame.Color(0, 128, 64), (round(12.5 - s/2), round(12.5 - s/2), s, s)) pygame.draw.line(self.base_image, pygame.Color(255, 10, 10), (12, 12), (24, 12), 1)
def add_image(self, name=DEFAULT, img_surface: pygame.Surface = None, rotate_point=None): self._img_dict[name] = (img_surface.convert_alpha(), rotate_point)
def load_pygame(filename): """ load a tiled TMX map for use with pygame """ from pygame import Surface import pygame, os tiledmap = load_tmx(filename) # cache will find duplicate tiles to reduce memory usage # mostly this is a problem in the blank areas of a tilemap cache = {} # just a precaution to make sure tileset images are added in the correct order for firstgid, t in sorted([(t.firstgid, t) for t in tiledmap.tilesets]): path = os.path.join(os.path.dirname(tiledmap.filename), t.source) image = pygame.image.load(path) w, h = image.get_rect().size tile_size = (t.tilewidth, t.tileheight) # some tileset images may be slightly larger than the tiles area # ie: may include a banner, copyright, etc. this compensates for that for y in range(0, int(h / t.tileheight) * t.tileheight, t.tileheight): for x in range(0, int(w / t.tilewidth) * t.tilewidth, t.tilewidth): # somewhat handle transparency, though colorkey handling is not tested if t.trans is None: tile = Surface(tile_size, pygame.SRCALPHA) else: tile = Surface(tile_size) # blit the smaller portion onto a new image from the larger one tile.blit(image, (0, 0), ((x, y), tile_size)) # make a unique id for this image, not sure if this is the best way, but it works key = pygame.image.tostring(tile, "RGBA") # make sure we don't have a duplicate tile try: tile = cache[key] except KeyError: if t.trans is None: tile = tile.convert_alpha() else: tile = tile.convert() tile.set_colorkey(t.trans) # update the cache cache[key] = tile tiledmap.images.append(tile) # correctly handle transformed tiles. currently flipped tiles # work by creating a new gid for the flipped tile and changing the gid # in the layer to the new gid. for layer in tiledmap.tilelayers: for x, y, gid, trans in layer.flipped_tiles: fx = trans & FLIP_X == FLIP_X fy = trans & FLIP_Y == FLIP_Y tile = pygame.transform.flip(tiledmap.images[gid], fx, fy) tiledmap.images.append(tile) # change the original gid in the layer data to the new gid layer.data[y][x] = len(tiledmap.images) - 1 del layer.flipped_tiles del cache return tiledmap
class GroundSurface: def __init__(self, **kwargs): scale = 1.0 if "mode" in kwargs: mode = kwargs["mode"] if mode == "auto": surface = Surface( (WIDTH, HEIGHT)) # pygame.display.set_mode((WIDTH, HEIGHT)) self.rect = surface.get_rect() elif mode == "copy": surface = kwargs["surface"] if type(surface) is GroundSurface: surface = surface.surface self.rect = surface.get_rect() elif mode == "custom": if "x" not in kwargs: kwargs["x"] = 0 if "y" not in kwargs: kwargs["y"] = 0 surface = Surface((int(kwargs["w"]), int(kwargs["h"]))) rect = surface.get_rect() rect.left = kwargs["x"] rect.top = kwargs["y"] if "scale" in kwargs: scale = kwargs["scale"] self.rect = rect else: # default: surface = Surface((WIDTH, HEIGHT)) WriteLog.error(__name__, "GroundSurface错误,提供的mode不存在") self.rect = surface.get_rect() else: surface = Surface((WIDTH, HEIGHT)) WriteLog.error(__name__, "GroundSurface错误,未提供mode参数") self.rect = surface.get_rect() self.block_size = BLOCK_UNIT self.w = self.rect.w self.h = self.rect.h self.surface = surface self.scale = scale self.parent = None self.layer = 0 self.children = [] self.group = Group() # 当前画布占用情况 无记忆 不考虑缝隙等复杂情况 指定的是一个“可绘制矩形范围” self.curpos = {"left": 0, "top": 0, "right": 0, "bottom": 0, "mid": 0} self.priority = 0 if "priority" in kwargs: self.priority = kwargs["priority"] self.active = True # 增加子画布 这类画布可以是别的独立画布 也可以给出参数创建 def add_child(self, *args): if len(args) == 1: # 插入别的画布到自适应的左上角 这样做由于对齐问题只能插一行 并且导致自适应混乱 多行需要手动重定位curpos ground_surface = args[0] rect = ground_surface.rect rect.left = self.curpos['left'] rect.top = self.curpos['top'] self.curpos['left'] += rect.w elif len(args) == 2: # 自适应画布 贴边或者定在中心 第一个指定类型 第二个指定大小 ground_surface = self.create_adaptive_surface(*args) elif len(args) == 3: # 插入别的画布到指定坐标 无视碰撞 ground_surface = args[0] ground_surface.rect.left = args[1] ground_surface.rect.top = args[2] else: # 用Ground方式创建画布 ground_surface = GroundSurface(*args) ground_surface.layer = self.layer + 1 ground_surface.parent = self ground_surface.scale *= self.scale self.children.append(ground_surface) self.children.sort(key=lambda it: it.priority, reverse=False) return ground_surface # 自适应矩形 def create_adaptive_surface(self, type, value): if type not in self.curpos: WriteLog.error(__name__, "错误的ground类型") return w = self.rect.w h = self.rect.h t = self.curpos["top"] r = self.curpos["right"] l = self.curpos["left"] b = self.curpos["bottom"] op = { "left": Rect(l, t, value, h - t - b), "top": Rect(l, t, w - l - r, value), "right": Rect(w - value - r, t, value, h - t - b), "bottom": Rect(l, h - value - b, w - l - r, value), "mid": Rect(max(l, int((w - value) / 2)), max(r, int((h - value) / 2)), min(w - l - r, value), min(h - t - b, value)) } rect = op[type] ground_surface = GroundSurface(mode="copy", surface=Surface([rect.w, rect.h])) ground_surface.rect.left = rect.left ground_surface.rect.top = rect.top self.curpos[type] += value return ground_surface # 填充一个surface到画布上,以变形/重复等方式 fill_rect指定填充范围 默认全图 def fill_surface(self, surface: Surface, mode="scale", fill_rect=None): rect = surface.get_rect() if fill_rect is None: fill_rect = self.rect rect.left = 0 rect.top = 0 else: rect.left = fill_rect.left rect.top = fill_rect.top if mode == "scale": self.surface.blit(scale(surface, (fill_rect.w, fill_rect.h)), fill_rect) elif mode == "repeat": while rect.bottom <= fill_rect.bottom: while rect.right <= fill_rect.right: self.surface.blit(surface, rect) rect.left += rect.w rect.left = 0 rect.top += rect.h # 填充一个sprite到画布上 这个Sprite会被添加到当前画布的精灵组 def add_sprite(self, sprite, mode="normal", fill_rect=None): if fill_rect is not None: if mode == "scale": # 这个对精灵基本没用 放弃吧 sprite.image = scale(sprite.image, (fill_rect.w, fill_rect.h)) sprite.rect.left = fill_rect.left sprite.rect.top = fill_rect.top self.group.add(sprite) # 重定位 - 画布的内部逻辑坐标转换为外部坐标(父类可视坐标系) def relocate(self, *args): x, y = self.trans_locate(*args) x += self.rect.left y += self.rect.top return x, y # 重新设置surface大小 其他设置不变(如果增大会向右下扩张) def resize(self, w, h): self.surface = Surface((w, h)) self.rect.w = w self.rect.h = h # 需要被实现 逻辑坐标到物理坐标的转换: def trans_locate(self, *args): """ 逻辑转物理,默认为top left :param args: :arg[3]: "up":top centerx "down": bottom centerx exmaple 1: map.trans_locate(12,12,'down') # 获取坐标 然后在该位置绘制敌人 example 2: event.move(map.trans_locate(12,12,'down')) # 移动事件到12,12位置 :return: """ x, y = args[0], args[1] if len(args) > 2: if args[2] == "up": return int((x + 0.5) * self.block_size), y * self.block_size elif args[2] == "down": return int( (x + 0.5) * self.block_size), (y + 1) * self.block_size return x * self.block_size, y * self.block_size # 刷新函数: 可以根据变化层级刷新部分内容而不是全部一起刷新 # !不同画布不能有不同的刷新率,刷新率以最快为准,控制动画频率在sprite的update中自行控制 def flush(self, screen=None): self.group.update(pygame.time.get_ticks()) self.group.draw(self.surface) self.children.sort(key=lambda it: it.priority) # tempSurface = Surface() for c in self.children: if c.active: c.flush(screen=self.surface) if screen is not None: screen.blit(self.surface.convert_alpha(self.surface), self.rect) # 填充纯色(debug使用) def fill(self, arg): self.surface.fill(arg) # draw_text 在画布上绘制文字 # 接受GroundSurface(画板),text(需要显示的文字),size(文字大小),color(文字颜色),x,y(xy相对坐标) # mode(模式,默认为画布相对方格坐标,如果mode="px"那么将为画布相对像素坐标) def draw_text(self, text, size, color, x, y, mode=None): font_name = global_var.get_value("font_name") font = pygame.font.Font(font_name, size) text_surface = font.render(text, True, color) text_rect = text_surface.get_rect() if mode == "px": text_rect.left = x text_rect.top = y else: text_rect.left = x * BLOCK_UNIT text_rect.top = y * BLOCK_UNIT self.surface.blit(text_surface, text_rect) # draw_bulk_text 在画布上绘制大量文字 # 接受GroundSurface(画板),content(需要显示的内容),size(文字大小) # mode(模式,默认为画布相对方格坐标,如果mode="px"那么将为画布相对像素坐标) ''' content = [ { "x": x, "y": y, "text": text, "color": color } ] ''' def draw_bulk_text(self, content, size, mode=None): font_name = global_var.get_value("font_name") font = pygame.font.Font(font_name, size) for text_obj in content: text_surface = font.render(text_obj["text"], True, text_obj["color"]) text_rect = text_surface.get_rect() if mode == "px": text_rect.left = text_obj["x"] text_rect.top = text_obj["y"] else: text_rect.left = text_obj["x"] * BLOCK_UNIT text_rect.top = text_obj["y"] * BLOCK_UNIT self.surface.blit(text_surface, text_rect) # draw_stroke_text 在画布上绘制描边文字 # 接受GroundSurface(画板),content(需要显示的内容),size(文字大小),color(文字颜色),x,y(xy相对坐标) # mode(模式,默认为画布相对方格坐标,如果mode="px"那么将为画布相对像素坐标) def draw_stroke_text(self, text, size, text_color, stroke_color, x, y, mode=None): content = [] coord = [(x + 1, y + 1), (x + 1, y - 1), (x - 1, y + 1), (x - 1, y - 1), (x, y)] for i in range(len(coord)): text_obj = {} text_obj["x"] = coord[i][0] text_obj["y"] = coord[i][1] text_obj["text"] = text if i < len(coord) - 1: text_obj["color"] = stroke_color else: text_obj["color"] = text_color content.append(text_obj) self.draw_bulk_text(content, size, mode) # draw_bulk_stroke_text 在画布上绘制大量描边文字 # 接受GroundSurface(画板),text(需要显示的文字),size(文字大小),text_color(文字颜色),stroke_color(描边颜色) # mode(模式,默认为画布相对方格坐标,如果mode="px"那么将为画布相对像素坐标) ''' content = [ { "x": x, "y": y, "text": text, } ] ''' def draw_bulk_stroke_text(self, content, size, mode=None): result_content = [] for i in content: x = i["x"] y = i["y"] coord = [(x + 1, y + 1), (x + 1, y - 1), (x - 1, y + 1), (x - 1, y - 1), (x, y)] for j in range(len(coord)): text_obj = {} text_obj["x"] = coord[j][0] text_obj["y"] = coord[j][1] text_obj["text"] = i["text"] if j < len(coord) - 1: text_obj["color"] = i["stroke_color"] else: text_obj["color"] = i["text_color"] result_content.append(text_obj) self.draw_bulk_text(result_content, size, mode) # draw_lines 在画布上绘制(一条或多条)线段 # 接受points(端点数组,格式[(x, y)]),width(线条宽度),color(线条颜色) # mode(模式,默认为画布相对方格坐标,如果mode="px"那么将为画布相对像素坐标) # 例子:draw_lines([(1,1),(1,100),(100,1),(1,1)], 5, WHITE) -> 一个线条宽度为5px的白色三角形 def draw_lines(self, points, width, color, mode=None): if mode == "px": pygame.draw.lines(self.surface, color, False, points, width) else: block_points = [] for item in points: block_points.append( [item[0] * BLOCK_UNIT, item[1] * BLOCK_UNIT]) pygame.draw.lines(self.surface, color, False, block_points, width) # draw_rect 在画布上绘制矩形 # start_pos(矩形左上角的坐标),格式[(x, y)]),end_pos(矩形右下角的坐标) # width(线条宽度),color(线条颜色) # mode(模式,默认为画布相对方格坐标,如果mode="px"那么将为画布相对像素坐标) def draw_rect(self, start_pos, end_pos, width, color, mode=None): if mode == "px": Rect = (start_pos[0], start_pos[1], end_pos[0] - start_pos[0], end_pos[1] - start_pos[1]) else: Rect = (start_pos[0] * BLOCK_UNIT, start_pos[1] * BLOCK_UNIT, end_pos[0] * BLOCK_UNIT - start_pos[0] * BLOCK_UNIT, end_pos[1] * BLOCK_UNIT - start_pos[1] * BLOCK_UNIT) pygame.draw.rect(self.surface, color, Rect, width) # draw_icon 提供一个可以调用Sprite的接口 def draw_icon(self, map_element, x, y): px, py = self.trans_locate(0, 0) rect = Rect(px, py, self.block_size, self.block_size) # sprite的显示需要接通group name = str(map_element) ret = get_resource(name) px, py = self.trans_locate(x, y, "down") rect.centerx = px rect.bottom = py if type(ret) is tuple: # 属于精灵 (注意:此时不能直接导入精灵,因为先有map后有精灵) img = ret[0] img_rect = ret[1] # 以资源本体大小显示 用以支持超过32*32的图像 img_rect.topleft = rect.topleft sp = list(ret[2]) self.add_sprite(EventSprite(name, img, sp), fill_rect=img_rect) elif ret is not None: self.fill_surface(ret, fill_rect=rect)
def __create_surface(size: Tuple[int, int]) -> Surface: """Returns a new Surface instance.""" surface = Surface(size, pygame.SRCALPHA, 32) surface = surface.convert_alpha(surface) return surface
class Game(object): def __init__(self): self.is_running = True self.grid_position = (0, 0) self.scrolling = (0, 0) self.zoom = ZOOM self.scroll_left = 0 self.scroll_up = 0 self.clock = time.Clock() init() self.screen = display.set_mode(RESOLUTION_DEFAULT) self.background = Surface(self.screen.get_size()) self.background.fill(COLOR_WHITE) self.background = self.background.convert() self.make_grid() def make_grid(self): self.grid = Surface( (self.zoom * MAP_SIZE + 1, self.zoom * MAP_SIZE + 1)) self.grid.fill(COLOR_WHITE) draw_grid(self.grid, COLOR_BLACK, self.zoom, 20, 20) self.grid = self.grid.convert_alpha() self.draw() def draw(self): self.grid_position = (self.grid_position[0] + self.scrolling[0], self.grid_position[1] + self.scrolling[1]) self.screen.blit(self.background, POSITION_ZERO) self.screen.blit(self.grid, self.grid_position) display.flip() def start(self): while self.is_running: self.clock.tick(FPS) for e in event.get(): # handle_events_debug(e, self) handle_events_quit(e, self) self.handle_grid_scroll(e) self.handle_grid_zoom(e) self.draw() def handle_grid_scroll(self, event): if event.type == KEYDOWN: if event.key == K_RIGHT: self.scrolling = (-SCROLL_SPEED, self.scrolling[1]) if event.key == K_LEFT: self.scrolling = (SCROLL_SPEED, self.scrolling[1]) if event.key == K_UP: self.scrolling = (self.scrolling[0], SCROLL_SPEED) if event.key == K_DOWN: self.scrolling = (self.scrolling[0], -SCROLL_SPEED) elif event.type == KEYUP: if event.key == K_RIGHT: self.scrolling = (0, self.scrolling[1]) if event.key == K_LEFT: self.scrolling = (0, self.scrolling[1]) if event.key == K_UP: self.scrolling = (self.scrolling[0], 0) if event.key == K_DOWN: self.scrolling = (self.scrolling[0], 0) def handle_grid_zoom(self, event): if event.type == KEYDOWN: if event.key == K_EQUALS: self.zoom = self.zoom + 10 self.make_grid() if event.key == K_MINUS: if self.zoom > 10: self.zoom = self.zoom - 10 self.make_grid()
def get_image(self, component): surf = Surface((400, 400), SRCALPHA) surf = surf.convert_alpha() return self.blit_on(component, surf)
class hud: def __init__(self, statusBarWidth=100, hpBarHeight=25, spBarHeight=10, screenWidth=1024, screenHeight=768, HPBarMaxColor=(50,20,20), SPBarMaxColor=(100,100,100), HPBarCurrentColor=(155,20,20), SPBarCurrentColor=(200,200,200), eventWindowColor = (20,20,20,200), nameMargin = 10): self.screenWidth = screenWidth self.screenHeight = screenHeight self.statusBarWidth = statusBarWidth self.HPBarMaxB1Dim = Rect(0,0,self.statusBarWidth,hpBarHeight) self.SPBarMaxB1Dim = Rect(0,hpBarHeight,self.statusBarWidth,spBarHeight) self.HPBarMaxB2Dim = Rect(self.screenWidth-self.statusBarWidth,0,self.statusBarWidth,hpBarHeight) self.SPBarMaxB2Dim = Rect(self.screenWidth-self.statusBarWidth,hpBarHeight,self.statusBarWidth,spBarHeight) self.HPBarCurrentB1Dim = Rect(0,0,self.statusBarWidth,hpBarHeight) self.SPBarCurrentB1Dim = Rect(0,hpBarHeight,self.statusBarWidth,spBarHeight) self.HPBarCurrentB2Dim = Rect(self.screenWidth-self.statusBarWidth,0,self.statusBarWidth,hpBarHeight) self.SPBarCurrentB2Dim = Rect(self.screenWidth-self.statusBarWidth,hpBarHeight,self.statusBarWidth,spBarHeight) self.HPBarMaxColor = HPBarMaxColor self.SPBarMaxColor = SPBarMaxColor self.HPBarCurrentColor = HPBarCurrentColor self.SPBarCurrentColor = SPBarCurrentColor self.nameMargin = nameMargin #self.timeDim = Rect(x,y,Width,Height) self.eventWindowDim = Rect(0,self.screenHeight/4*3,self.screenWidth,self.screenHeight) self.eventWindowSurface = Surface((int(self.screenWidth),int(self.screenHeight/4)), flags=SRCALPHA) self.eventWindowSurface.fill(eventWindowColor) self.eventWindowSurface.convert_alpha() self.eventWindowTextPos = (10,0) self.events = [] self.timeDim = None self.eventWindowColor = eventWindowColor #Color(eventWindowColor[0],eventWindowColor[1],eventWindowColor[2],eventWindowColor[3],) self.nameFont = font.SysFont("Arial",12) self.timeFont = font.SysFont("Arial",20) self.roundFont = font.SysFont("Arial",12) self.eventWindowFont = font.SysFont("Arial",12) def updateHPBars(self, B1HPpercent, B2HPpercent): B1HPCurrentWidth = B1HPpercent * self.statusBarWidth B2HPCurrentWidth = B2HPpercent * self.statusBarWidth self.HPBarCurrentB1Dim.left = self.statusBarWidth-B1HPCurrentWidth self.HPBarCurrentB1Dim.width = B1HPCurrentWidth self.HPBarCurrentB2Dim.width = B2HPCurrentWidth def updateSPBars(self, B1SPpercent, B2SPpercent): B1SPCurrentWidth = B1SPpercent * self.statusBarWidth B2SPCurrentWidth = B2SPpercent * self.statusBarWidth self.SPBarCurrentB1Dim.left = self.statusBarWidth-B1SPCurrentWidth self.SPBarCurrentB1Dim.width = B1SPCurrentWidth self.SPBarCurrentB2Dim.width = B2SPCurrentWidth
def loop_display(self, clicked, hit, miss): gameTime, endGame = self.timerData if not gameTime and self.timer: gameTime = -1 # Display bg self.screen.blit(self.img_background, (0, 0)) # Display holes for position in self.holes: self.screen.blit(self.img_hole, position) # Display moles for mole in self.moles: holes = [f for f in self.holes if f not in self.used_holes] mole_display = mole.do_display(holes, self.score.level, not endGame) # If new/old hole given if len(mole_display) > 1: if mole_display[1] == 0: # New hole self.used_holes.append(mole_display[2]) else: # Old hole if mole_display[2] in self.used_holes: self.used_holes.remove(mole_display[2]) # If should display if mole_display[0]: # Get pos and display pos = mole.get_hole_pos(not endGame) self.screen.blit(mole.image, pos) # Hammer thisHammer = transform.rotate(self.img_mallet.copy(), (Constants.MALLETROTHIT if clicked else Constants.MALLETROTNORM)) hammer_x, hammer_y = mole.get_hammer_pos() hammer_x -= thisHammer.get_width() / 5 hammer_y -= thisHammer.get_height() / 4 self.screen.blit(thisHammer, (hammer_x, hammer_y)) # Fade screen if not started or has ended if self.timer and (endGame or gameTime == -1): overlay = Surface((Constants.GAMEWIDTH, Constants.GAMEHEIGHT), SRCALPHA, 32) overlay = overlay.convert_alpha() overlay.fill((100, 100, 100, 0.9 * 255)) self.screen.blit(overlay, (0, 0)) # Debug data for readout debug_data = {} if Constants.DEBUGMODE: debug_data = { "DEBUG": True, "FPS": int(self.clock.get_fps()), "MOLES": "{}/{}".format(Constants.MOLECOUNT, Constants.HOLEROWS * Constants.HOLECOLUMNS), "KEYS": "E[H]R[M]T[M0]Y[M+5]U[M-5]I[H0]O[H+5]P[H-5]" } # Display data readout data = self.score.label(timer=gameTime, debug=debug_data, size=(1.5 if endGame else 1)) self.screen.blit(data, (5, 5)) # Display hit/miss indicators if not endGame: # Hit indicator if hit: self.show_hit = time.get_ticks() if self.show_hit > 0 and time.get_ticks( ) - self.show_hit <= Constants.MOLEHITHUD: hit_label = self.text.get_label("BONK!", scale=3, color=(255, 50, 0)) hit_x = (Constants.GAMEWIDTH - hit_label.get_width()) / 2 hit_y = (Constants.GAMEHEIGHT - hit_label.get_height()) / 2 self.screen.blit(hit_label, (hit_x, hit_y)) else: self.show_hit = 0 # Miss indicator if miss: self.show_miss = time.get_ticks() if self.show_miss > 0 and time.get_ticks( ) - self.show_miss <= Constants.MOLEMISSHUD: miss_label = self.text.get_label("Miss!", scale=2, color=(0, 150, 255)) miss_x = (Constants.GAMEWIDTH - miss_label.get_width()) / 2 miss_y = (Constants.GAMEHEIGHT + miss_label.get_height()) / 2 self.screen.blit(miss_label, (miss_x, miss_y)) else: self.show_miss = 0 # Click to start indicator if self.timer and gameTime == -1: timer_label = self.text.get_label("Click to begin...", scale=2, color=(0, 255, 255)) timer_x = (Constants.GAMEWIDTH - timer_label.get_width()) / 2 timer_y = (Constants.GAMEHEIGHT - timer_label.get_height()) / 2 self.screen.blit(timer_label, (timer_x, timer_y)) # Time's up indicator if self.timer and endGame: timer_label_1 = self.text.get_label("Time's up!", scale=3, color=(0, 150, 255)) timer_label_2 = self.text.get_label("Press space to restart...", scale=2, color=(0, 150, 255)) timer_x_1 = (Constants.GAMEWIDTH - timer_label_1.get_width()) / 2 timer_x_2 = (Constants.GAMEWIDTH - timer_label_2.get_width()) / 2 timer_y_1 = (Constants.GAMEHEIGHT / 2) - timer_label_1.get_height() timer_y_2 = (Constants.GAMEHEIGHT / 2) self.screen.blit(timer_label_1, (timer_x_1, timer_y_1)) self.screen.blit(timer_label_2, (timer_x_2, timer_y_2))
def set_texture_atlas(self, texture: pygame.Surface): self.cached_texture_atlas = texture.convert_alpha() self.on_texture_changed()
def loop_display(self, clicked, hit, miss): gameTime, endGame = self.timerData if not gameTime and self.timer: gameTime = -1 # Display bg self.screen.blit(self.img_background, (0, 0)) # Display holes #有一个防止栈空 for i in range(1, Constants.HOLEROWS): for j in range(Constants.HOLECOLUMNS): if (self.vis[i][j] == False): self.screen.blit(self.diamond[j % Constants.HOLECOLUMNS], self.holes[j][i]) flag = False for i in range(1, Constants.HOLEROWS): for j in range(Constants.HOLECOLUMNS): if (self.change[i][j] > 0): flag = True self.screen.blit( self.diamond_remove[j % Constants.HOLECOLUMNS], self.holes[j][i]) self.change[i][j] -= 1 if (flag): if (self.turn % 2): self.screen.blit(self.imagea, (0 + self.moveset, 200 + self.moveset)) else: self.screen.blit(self.imageb, (250 - self.moveset, 200 - self.moveset)) self.moveset += 0.3 thisHammer = transform.rotate( self.img_mallet.copy(), (Constants.MALLETROTHIT if clicked else Constants.MALLETROTNORM)) hammer_x, hammer_y = mouse.get_pos() hammer_x -= thisHammer.get_width() / 5 hammer_y -= thisHammer.get_height() / 4 self.screen.blit(thisHammer, (hammer_x, hammer_y)) # Fade screen if not started or has ended if self.timer and (endGame or gameTime == -1): overlay = Surface((Constants.GAMEWIDTH, Constants.GAMEHEIGHT), SRCALPHA, 32) overlay = overlay.convert_alpha() #rgb, opacity overlay.fill((100, 100, 100, 0.8 * 255)) self.screen.blit(overlay, (0, 0)) # Debug data for readout debug_data = {} if Constants.DEBUGMODE: debug_data = { "DEBUG": True, "FPS": int(self.clock.get_fps()), "MOLES": "{}/{}".format(Constants.MOLECOUNT, Constants.HOLEROWS * Constants.HOLECOLUMNS), "KEYS": "E[H]R[M]T[M0]Y[M+5]U[M-5]I[H0]O[H+5]P[H-5]" } # Display data readout data = self.score.label(timer=gameTime, turn=self.turn, debug=debug_data, size=(1.5 if endGame else 1.5)) self.screen.blit(data, (5, 5)) # Display hit/miss indicators if not endGame: # Hit indicator if self.a: self.show_hit = time.get_ticks() self.a = False if self.show_hit > 0 and time.get_ticks( ) - self.show_hit <= Constants.MOLEHITHUD: hit_label = self.text.get_label("PLAYER 1 DONE", scale=3, color=(255, 50, 0)) hit_x = (Constants.GAMEWIDTH - hit_label.get_width()) / 2 hit_y = (Constants.GAMEHEIGHT - hit_label.get_height()) / 2 self.screen.blit(hit_label, (hit_x, hit_y)) else: self.show_hit = 0 # Miss indicator if self.b: self.show_miss = time.get_ticks() self.b = False if self.show_miss > 0 and time.get_ticks( ) - self.show_miss <= Constants.MOLEMISSHUD: miss_label = self.text.get_label("PLAYER 2 DONE", scale=2, color=(0, 150, 255)) miss_x = (Constants.GAMEWIDTH - miss_label.get_width()) / 2 miss_y = (Constants.GAMEHEIGHT + miss_label.get_height()) / 2 self.screen.blit(miss_label, (miss_x, miss_y)) else: self.show_miss = 0 # Click to start indicator if self.timer and gameTime == -1: timer_label = self.text.get_label("Click to begin...", scale=2, color=(0, 255, 255)) timer_x = (Constants.GAMEWIDTH - timer_label.get_width()) / 2 timer_y = (Constants.GAMEHEIGHT - timer_label.get_height()) / 2 self.screen.blit(timer_label, (timer_x, timer_y)) # Time's up indicator if endGame: winner = "" if (self.time_out and self.turn % 2): winner = "YOU PLAY BETTER THAN CXK" elif (self.time_out and self.turn % 2 == 0): winner = "YOU PALY AS GOOD AS CXK" elif (self.tot_stone == 0): if (self.turn % 2): winner = "YOU PLAY BETTER THAN CXK" else: winner = "YOU PALY AS GOOD AS CXK" timer_label_1 = self.text.get_label(winner, scale=2, color=(0, 150, 255)) timer_label_2 = self.text.get_label("Press space to restart...", scale=2, color=(0, 150, 255)) if (self.win_show == False): self.win_show = True else: if (winner == "YOU PLAY BETTER THAN CXK"): self.screen.blit(self.imagea, (150, 70)) else: self.screen.blit(self.imageb2, (150, 70)) if (self.victory_play == False): self.victory_play = True pygame.mixer.music.load("assets/cxkwin.wav") pygame.mixer.music.play() timer_x_1 = (Constants.GAMEWIDTH - timer_label_1.get_width()) / 2 timer_x_2 = (Constants.GAMEWIDTH - timer_label_2.get_width()) / 2 timer_y_1 = (Constants.GAMEHEIGHT / 2) - timer_label_1.get_height() timer_y_2 = (Constants.GAMEHEIGHT / 2) self.screen.blit(timer_label_1, (timer_x_1, timer_y_1 + 70)) self.screen.blit(timer_label_2, (timer_x_2, timer_y_2 + 70)) if (self.back_on == False): self.screen.blit(self.i5, (120, 600)) else: self.screen.blit(self.i51, (120, 600))
def create_surfaces(self): self.hole = Surface((self.rect.w, self.rect.h), SRCALPHA) circle(self.hole, (0, 0, 0), self.rect.center, self.rect.centerx) circle(self.hole, (150, 75, 0), self.rect.center, self.rect.centerx, 3) self.sprite = Surface.convert_alpha(scale(load("face.jpg"), (self.sprite_rect.w, self.sprite_rect.w)))
def simple_alpha_frame(size, color=constants.BRIGHT, alpha=200): surface = Surface(size, flags=SRCALPHA) color = gac(color, alpha) surface.fill(color) return surface.convert_alpha()
class Level(object): """ Main Level class, handles most of the gameplay """ def __init__(self, screen, draw_offset, control_set, player_color, finish_game): """ Init with... way too many parameters :param screen: Main PyGame surface to draw the objects/UI on :param draw_offset: Drawing offset used in multiplayer :param control_set: Key-set used to control the Paddle :param player_color: Color of the player's Paddle :param finish_game: Function passed to the constructor, triggered on the game end :return: """ self.screen = screen self.draw_offset = draw_offset self.control_set = control_set self.prev_input_x = 0 self.level_number = 1 self.block_count = 0 self.player = Player("Derp") self.paddle = Paddle(LEVEL_WIDTH/2 - Paddle.WIDTH/2, LEVEL_HEIGHT - 40, player_color, parent=self, owner=self.player) self.ball = Ball(self.paddle.rect.x + Paddle.WIDTH/2 - Ball.RADIUS, self.paddle.rect.y - Ball.RADIUS * 2) self.items = None self.blocks = [] self.blocks_surface = None self.blocks_surface_dirty = True self.entities = [] self.font = Assets.font self.score_label = None self.lives_label = None self.level_label = None self.load_level(self.level_number) self.finish_game = finish_game def handle_input(self, events): """ Handles incoming input events :param events: input events from the main app :return: """ BONUS_SPAWN_X = 305 BONUS_SPAWN_Y = 200 for event in events: if event.type == KEYDOWN: if event.key == self.control_set[0]: if self.paddle.vx != 0: self.prev_input_x = 1 self.paddle.vx = -1 elif event.key == self.control_set[2]: if self.paddle.vx != 0: self.prev_input_x = -1 self.paddle.vx = 1 # elif event.key == K_SPACE: # self.paddle.change_size(self.paddle.rect.width+10) elif event.key == K_1: # self.add_entity(ItemExpand(BONUS_SPAWN_X, BONUS_SPAWN_Y)) self.spawn_item(BONUS_SPAWN_X, BONUS_SPAWN_Y, 1) elif event.key == K_2: # self.add_entity(ItemLaserGun(BONUS_SPAWN_X, BONUS_SPAWN_Y)) self.spawn_item(BONUS_SPAWN_X, BONUS_SPAWN_Y, 2) elif event.key == K_3: # self.add_entity(ItemShrink(BONUS_SPAWN_X, BONUS_SPAWN_Y)) self.spawn_item(BONUS_SPAWN_X, BONUS_SPAWN_Y, 3) elif event.key == K_4: # self.add_entity(ItemPaddleNano(BONUS_SPAWN_X, BONUS_SPAWN_Y)) self.spawn_item(BONUS_SPAWN_X, BONUS_SPAWN_Y, 4) elif event.key == K_5: # self.add_entity(ItemLife(BONUS_SPAWN_X, BONUS_SPAWN_Y)) self.spawn_item(BONUS_SPAWN_X, BONUS_SPAWN_Y, 5) elif event.key == K_6: # self.add_entity(ItemLife(BONUS_SPAWN_X, BONUS_SPAWN_Y)) self.spawn_item(BONUS_SPAWN_X, BONUS_SPAWN_Y, 6) elif event.type == KEYUP: if event.key == self.control_set[0]: if self.prev_input_x < 0: self.prev_input_x = 0 elif self.prev_input_x > 0: self.paddle.vx = 1 self.prev_input_x = 0 else: self.paddle.vx = 0 elif event.key == self.control_set[2]: if self.prev_input_x > 0: self.prev_input_x = 0 elif self.prev_input_x < 0: self.paddle.vx = -1 self.prev_input_x = 0 else: self.paddle.vx = 0 elif event.key == self.control_set[1]: if self.ball.docked: self.ball.docked = False self.ball.vx = 1 self.ball.vy = -1 else: self.paddle.use_attachment() elif event.key == K_EQUALS: self.start_level(self.level_number + 1) def start_level(self, new_level_num): """ Used to start level, checks the level bounds :param new_level_num: :return: """ self.ball.docked = True self.ball.dead = False if new_level_num > get_level_count(): self.finish_game() else: if self.level_number < new_level_num: self.player.score += self.player.lives * 500 else: self.player.score = 0 self.load_level(new_level_num) self.paddle.change_size(PADDLE_WIDTHS[PADDLE_DEFAULT_WIDTH_INDEX]) self.paddle.attachments = [] self.paddle.rect.x = LEVEL_WIDTH/2 - self.paddle.rect.width/2 self.paddle.rect.y = LEVEL_HEIGHT - 40 self.player.lives = 3 self.blocks_surface = Surface((LEVEL_WIDTH, LEVEL_HEIGHT)) self.blocks_surface_dirty = True def load_level(self, new_level_num): """ Parses level from the Character array that is provided by the LevelLoader class :param new_level_num: Number of the level, used to construct the filename, compute the next level number, and viewed on the UI :return: """ loaded_level = LevelLoader.load(LEVELDIR + str(new_level_num).zfill(2) + ".lvl") level = loaded_level[0] self.items = loaded_level[1] self.level_number = new_level_num self.blocks = [] self.entities = [] self.block_count = 0 for y in xrange(0, BLOCK_NUM_HEIGHT): self.blocks.append([None, ] * BLOCK_NUM_WIDTH) for x in xrange(0, BLOCK_NUM_WIDTH): if level[y][x] == 'i': self.blocks[y][x] = BlockIndestructible(PLAYFIELD_PADDING[0] + x * Block.WIDTH, PLAYFIELD_PADDING[1] + y * Block.HEIGHT) elif level[y][x] == 'm': self.blocks[y][x] = BlockMultiHit(PLAYFIELD_PADDING[0] + x * Block.WIDTH, PLAYFIELD_PADDING[1] + y * Block.HEIGHT) self.block_count += 1 elif level[y][x] == 'e': self.blocks[y][x] = BlockExplosive(PLAYFIELD_PADDING[0] + x * Block.WIDTH, PLAYFIELD_PADDING[1] + y * Block.HEIGHT) self.block_count += 1 elif level[y][x] != '0': self.blocks[y][x] = Block(PLAYFIELD_PADDING[0] + x * Block.WIDTH, PLAYFIELD_PADDING[1] + y * Block.HEIGHT, int(level[y][x]) - 1) self.block_count += 1 def add_entity(self, entity): """ Utility function to add new entities to the level. Later, might search for dead entities to replace them instead of expanding the list indefinitely :param entity: :return: """ self.entities.append(entity) def spawn_item(self, x, y, item): """ Function used to spawn items from blocks :param x: x coordinate of the play-field :param y: y coordinate of the play-field :param item: item to spawn, leave empty to make it randomly selected :return: """ if item == 1: self.add_entity(ItemLife(x, y)) elif item == 2: self.add_entity(ItemExpand(x, y)) elif item == 3: self.add_entity(ItemShrink(x, y)) elif item == 4: self.add_entity(ItemLaserGun(x, y)) elif item == 5: self.add_entity(ItemPaddleNano(x, y)) elif item == 6: pass elif randint(0, 35) == 0: item_type = randint(0, 4) if item_type == 0: dropped_item = ItemLife(x, y) elif item_type == 1: dropped_item = ItemExpand(x, y) elif item_type == 2: dropped_item = ItemShrink(x, y) elif item_type == 3: dropped_item = ItemLaserGun(x, y) else: dropped_item = ItemPaddleNano(x, y) self.add_entity(dropped_item) def block_destruction(self, block, item, func): """ Decides what to do with the block, based on the block type (sigh), and performs appropriate action :param block: Block to operate on :param item: If the block has a hard-assigned item in level, it will be spawned :param func: Function of the block that returns its points-value :return: """ return_v = func() if isinstance(block, BlockExplosive): rect = block.rect self.entities.append(Explosion(rect.x + rect.width/2 - Explosion.WIDTH/2, rect.y + rect.height/2 - Explosion.HEIGHT/2)) if block.dead: self.player.add_points(return_v) self.block_count -= 1 if not isinstance(block, BlockExplosive): self.spawn_item(block.rect.x, block.rect.y, item) self.blocks_surface_dirty = True def draw(self): """ Method called each frame to (re)draw the objects and UI :return: """ self.screen.blit(Assets.background, (self.draw_offset[0], self.draw_offset[1])) self.screen.blit(Assets.border, (self.draw_offset[0], self.draw_offset[1])) self.screen.blit(Assets.border, (self.draw_offset[0] + LEVEL_WIDTH - PLAYFIELD_PADDING[0], self.draw_offset[1])) if self.blocks_surface_dirty: self.blocks_surface = Surface((LEVEL_WIDTH, LEVEL_HEIGHT), SRCALPHA, 32) self.blocks_surface = self.blocks_surface.convert_alpha() self.blocks_surface_dirty = False for row in self.blocks: for block in row: if block is not None and not block.dead: block.draw(self.blocks_surface) self.screen.blit(self.blocks_surface, self.draw_offset) self.paddle.draw(self.screen, self.draw_offset) if not self.ball.dead: self.ball.draw(self.screen, self.draw_offset) # draw entities for entity in self.entities: if not entity.dead: entity.draw(self.screen, self.draw_offset) # draw upper bar draw.rect(self.screen, (0, 0, 0), (self.draw_offset[0] + PLAYFIELD_PADDING[0], self.draw_offset[1], LEVEL_WIDTH - PLAYFIELD_PADDING[0] * 2, PLAYFIELD_PADDING[1])) self.screen.blit(self.score_label, (self.draw_offset[0] + PLAYFIELD_PADDING[0] + 10, self.draw_offset[1])) self.screen.blit(self.lives_label, (self.draw_offset[0] + PLAYFIELD_PADDING[0] + 150, self.draw_offset[1])) self.screen.blit(self.level_label, (self.draw_offset[0] + LEVEL_WIDTH - 100, self.draw_offset[1])) def update(self): """ Method called each frame, to update the state of entities based on input events and previous state of the game :return: """ if self.block_count <= 0: self.start_level(self.level_number + 1) elif self.player.lives <= 0: self.start_level(self.level_number) self.paddle.update() if self.ball.docked and not self.ball.dead: self.ball.rect.x = self.paddle.rect.x + self.paddle.rect.width/2 - self.ball.radius self.ball.rect.y = self.paddle.rect.y - self.ball.radius * 2 elif self.player.lives > 0: self.ball.update() for entity in self.entities: if not entity.dead: entity.update() self.check_collision() self.score_label = self.font.render("SCORE: " + str(self.player.score), 1, (255, 255, 255)) self.lives_label = self.font.render("LIVES: " + str(self.player.lives), 1, (255, 255, 255)) self.level_label = self.font.render("LEVEL " + str(self.level_number), 1, (255, 255, 255)) def check_collision(self): """ Called after input handling and movement of the object, to check and solve collisions :return: """ # ball vs paddle if self.ball.rect.y < self.paddle.rect.y and \ sprite.collide_rect(self.paddle, self.ball): self.ball.vy = -1 # ball.vy # ball vs bottom if not self.ball.dead and self.ball.rect.y + self.ball.radius * 2 > LEVEL_HEIGHT: self.player.lives -= 1 if self.player.lives < 1: self.ball.dead = True else: self.ball.rect.x = self.paddle.rect.x + self.paddle.rect.width/2 - self.ball.radius self.ball.rect.y = self.paddle.rect.y - self.ball.radius * 2 self.ball.docked = True self.paddle.change_size(PADDLE_WIDTHS[PADDLE_DEFAULT_WIDTH_INDEX]) self.paddle.attachments = [] # ball vs blocks coll_num = [0, 0, 0] coll_num_val = (4, 2, 1) ball_grid_x = (self.ball.rect.x - PLAYFIELD_PADDING[0] + self.ball.radius) / Block.WIDTH ball_grid_y = (self.ball.rect.y - PLAYFIELD_PADDING[1] + self.ball.radius) / Block.HEIGHT for y in range(ball_grid_y - 1, ball_grid_y + 2): for x in range(ball_grid_x - 1, ball_grid_x + 2): if 0 <= x < BLOCK_NUM_WIDTH and 0 <= y < BLOCK_NUM_HEIGHT: if self.blocks[y][x] is not None and not self.blocks[y][x].dead and \ sprite.collide_rect(self.blocks[y][x], self.ball): self.block_destruction(self.blocks[y][x], self.items[y][x], self.blocks[y][x].on_collide) coll_num[y - ball_grid_y + 1] += coll_num_val[x - ball_grid_x + 1] self.ball.on_collide(coll_num) # entities for entity in self.entities: if not entity.dead: # paddle vs items if isinstance(entity, Item) and sprite.collide_rect(self.paddle, entity): entity.on_collect(self.paddle) entity.dead = True # self.player.lives += 1 # explosion vs blocks elif isinstance(entity, Explosion) and entity.state > 0: entity_block_x = (entity.rect.x - PLAYFIELD_PADDING[0] + Explosion.WIDTH/2) / Block.WIDTH entity_block_y = (entity.rect.y - PLAYFIELD_PADDING[1] + Explosion.HEIGHT/2) / Block.HEIGHT for y in xrange(entity_block_y - 1, entity_block_y + 2): for x in xrange(entity_block_x - 1, entity_block_x + 2): if 0 <= x < BLOCK_NUM_WIDTH and 0 <= y < BLOCK_NUM_HEIGHT: if self.blocks[y][x] is not None and not self.blocks[y][x].dead: self.block_destruction(self.blocks[y][x], self.items[y][x], self.blocks[y][x].kill) elif isinstance(entity, Projectile): entity_block_x = (entity.rect.x - PLAYFIELD_PADDING[0] + entity.rect.width/2) / Block.WIDTH entity_block_y = (entity.rect.y - PLAYFIELD_PADDING[1] + entity.rect.height/2) / Block.HEIGHT for y in xrange(entity_block_y - 1, entity_block_y + 2): for x in xrange(entity_block_x - 1, entity_block_x + 2): if 0 <= x < BLOCK_NUM_WIDTH and 0 <= y < BLOCK_NUM_HEIGHT: if self.blocks[y][x] is not None and not self.blocks[y][x].dead \ and sprite.collide_rect(self.blocks[y][x], entity): self.block_destruction(self.blocks[y][x], self.items[y][x], self.blocks[y][x].kill) entity.on_collide()