根據我在評論中提出的建議,下面是在pygame中創建和渲染9切片精靈的SliceSprite
類的實現。我還包括一個樣本來展示如何使用它。 它絕對是粗糙的邊緣(不會檢查無效的輸入,例如當您調整寬度小於定義的左側和右側切片大小的精靈時),但仍應該是一個有用的開始。此代碼已被更新和拋光以處理這些邊界情況,並且不會在@skrx在註釋中建議的每次繪製調用中重新創建九個子表面。
slicesprite.py
import pygame
class SliceSprite(pygame.sprite.Sprite):
"""
SliceSprite extends pygame.sprite.Sprite to allow for 9-slicing of its contents.
Slicing of its image property is set using a slicing tuple (left, right, top, bottom).
Values for (left, right, top, bottom) are distances from the image edges.
"""
width_error = ValueError("SliceSprite width cannot be less than (left + right) slicing")
height_error = ValueError("SliceSprite height cannot be less than (top + bottom) slicing")
def __init__(self, image, slicing=(0, 0, 0, 0)):
"""
Creates a SliceSprite object.
_sliced_image is generated in _generate_slices() only when _regenerate_slices is True.
This avoids recomputing the sliced image whenever each SliceSprite parameter is changed
unless absolutely necessary! Additionally, _rect does not have direct @property access
since updating properties of the rect would not be trigger _regenerate_slices.
Args:
image (pygame.Surface): the original surface to be sliced
slicing (tuple(left, right, top, bottom): the 9-slicing margins relative to image edges
"""
pygame.sprite.Sprite.__init__(self)
self._image = image
self._sliced_image = None
self._rect = self.image.get_rect()
self._slicing = slicing
self._regenerate_slices = True
@property
def image(self):
return self._image
@image.setter
def image(self, new_image):
self._image = new_image
self._regenerate_slices = True
@property
def width(self):
return self._rect.width
@width.setter
def width(self, new_width):
self._rect.width = new_width
self._regenerate_slices = True
@property
def height(self):
return self._rect.height
@height.setter
def height(self, new_height):
self._rect.height = new_height
self._regenerate_slices = True
@property
def x(self):
return self._rect.x
@x.setter
def x(self, new_x):
self._rect.x = new_x
self._regenerate_slices = True
@property
def y(self):
return self._rect.y
@y.setter
def y(self, new_y):
self._rect.y = new_y
self._regenerate_slices = True
@property
def slicing(self):
return self._slicing
@slicing.setter
def slicing(self, new_slicing=(0, 0, 0, 0)):
self._slicing = new_slicing
self._regenerate_slices = True
def get_rect(self):
return self._rect
def set_rect(self, new_rect):
self._rect = new_rect
self._regenerate_slices = True
def _generate_slices(self):
"""
Internal method required to generate _sliced_image property.
This first creates nine subsurfaces of the original image (corners, edges, and center).
Next, each subsurface is appropriately scaled using pygame.transform.smoothscale.
Finally, each subsurface is translated in "relative coordinates."
Raises appropriate errors if rect cannot fit the center of the original image.
"""
num_slices = 9
x, y, w, h = self._image.get_rect()
l, r, t, b = self._slicing
mw = w - l - r
mh = h - t - b
wr = w - r
hb = h - b
rect_data = [
(0, 0, l, t), (l, 0, mw, t), (wr, 0, r, t),
(0, t, l, mh), (l, t, mw, mh), (wr, t, r, mh),
(0, hb, l, b), (l, hb, mw, b), (wr, hb, r, b),
]
x, y, w, h = self._rect
mw = w - l - r
mh = h - t - b
if mw < 0: raise SliceSprite.width_error
if mh < 0: raise SliceSprite.height_error
scales = [
(l, t), (mw, t), (r, t),
(l, mh), (mw, mh), (r, mh),
(l, b), (mw, b), (r, b),
]
translations = [
(0, 0), (l, 0), (l + mw, 0),
(0, t), (l, t), (l + mw, t),
(0, t + mh), (l, t + mh), (l + mw, t + mh),
]
self._sliced_image = pygame.Surface((w, h))
for i in range(num_slices):
rect = pygame.rect.Rect(rect_data[i])
surf_slice = self.image.subsurface(rect)
stretched_slice = pygame.transform.smoothscale(surf_slice, scales[i])
self._sliced_image.blit(stretched_slice, translations[i])
def draw(self, surface):
"""
Draws the SliceSprite onto the desired surface.
Calls _generate_slices only at draw time only if necessary.
Note that the final translation occurs here in "absolute coordinates."
Args:
surface (pygame.Surface): the parent surface for blitting SliceSprite
"""
x, y, w, h, = self._rect
if self._regenerate_slices:
self._generate_slices()
self._regenerate_slices = False
surface.blit(self._sliced_image, (x, y))
實施例使用(main.py):
import pygame
from slicesprite import SliceSprite
if __name__ == "__main__":
pygame.init()
screen = pygame.display.set_mode((800, 600))
clock = pygame.time.Clock()
done = False
outer_points = [(0, 20), (20, 0), (80, 0), (100, 20), (100, 80), (80, 100), (20, 100), (0, 80)]
inner_points = [(10, 25), (25, 10), (75, 10), (90, 25), (90, 75), (75, 90), (25, 90), (10, 75)]
image = pygame.Surface((100, 100), pygame.SRCALPHA)
pygame.draw.polygon(image, (20, 100, 150), outer_points)
pygame.draw.polygon(image, (0, 60, 120), inner_points)
button = SliceSprite(image, slicing=(25, 25, 25, 25))
button.set_rect((50, 100, 500, 200))
#Alternate version if you hate using rects for some reason
#button.x = 50
#button.y = 100
#button.width = 500
#button.height = 200
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
screen.fill((0, 0, 0))
button.draw(screen)
pygame.display.flip()
clock.tick()
您正在尋找的方式是[9-slicing](https://docs.unity3d.com/Manual/9SliceSprites.html)。我認爲pygame中沒有任何內容可以直接處理9分片圖像。嘗試創建一個擴展pygame的'Sprite'類的類可能會很有趣!你打算通過多個「拆分」圖像,或者你想設置點來處理縮放? – CodeSurgeon
@CodeSurgeon呵呵,是的,我可能會做一個子類。我可能會拿出一個界限的圖像,然後創建9個圖像,然後進行切片。非常感謝這個名字,我無法弄清楚它應該被稱爲什麼。 –