2021/10/02(土) [n年前の日記]
#2 [pygame] Pyagmeで画像を分割して描画
Python + Pygame で、スプライトシート画像を読み込んで、各パーツに分割してウインドウ内に描画したい。考えてみたら、今までそういう処理は試してなかったな…。さて、どうすれば…。
環境は、Windows10 x64 21H1 + Python 3.9.5 64bit + pygame 2.0.1。
使用画像は以下。
_spritesheet_number.png
環境は、Windows10 x64 21H1 + Python 3.9.5 64bit + pygame 2.0.1。
使用画像は以下。
_spritesheet_number.png
◎ blit()を使う方法。 :
Pygame は、Surface.blit() を使うことで、Surface から Surface へと画像を転送することができる。この blit() に pygame.Rect という矩形範囲を渡すことで、元画像の任意の範囲だけを転送、ということができるらしい。
これを使えばスプライトシート画像の一部だけを抜き出して描画できるんじゃないかな…。ということで、試してみた。
_02_blit_mn.py
pygame.Rect(x, y, width, height) で矩形範囲を指定できる。それを配列 rects に蓄えておいて…。スクリーンに相当する Surface、を格納している screen に blit() で描画する際に、area=rects[index] という形で渡して、「元画像の、この範囲だけを抜き出して描画せよ」的な処理をしている。
実行したら、こうなった。
元画像の一部だけを描画できている。
ということで、blit() に pygame.Rect を渡せば、目的は果たせると分かった。
ちなみに、上記のスクリプトでは pygame.Rect() を使ってるけど、その後ググってみたら、blit(img, (px, py), (x, y, w, h)) という書き方もできると知った。
処理速度で違いはあったりするのかな…。どうなんだろう。
これを使えばスプライトシート画像の一部だけを抜き出して描画できるんじゃないかな…。ということで、試してみた。
_02_blit_mn.py
import pygame import sys SCRW, SCRH = 320, 240 pygame.init() screen = pygame.display.set_mode((SCRW, SCRH), pygame.DOUBLEBUF) imgname = "spritesheet_number.png" img = pygame.image.load(imgname).convert_alpha() # make split area list rects = [] w, h = 96, 96 for y in range(8): for x in range(8): rects.append(pygame.Rect(x * w, y * h, w, h)) index = 0 running = True clock = pygame.time.Clock() # Main loop while running: # check event for ev in pygame.event.get(): if ev.type == pygame.QUIT: running = False if ev.type == pygame.KEYDOWN: if ev.key == pygame.K_ESCAPE or ev.key == pygame.K_q: # Push ESC or Q key running = False screen.fill((40, 60, 200)) # clear screen # draw image x = 160 - 96 / 2 y = 120 - 96 / 2 screen.blit(img, (x, y), area=rects[index]) index = (index + 1) % len(rects) pygame.display.flip() clock.tick_busy_loop(60) cap = "Image Split - %5.2f FPS" % (clock.get_fps()) pygame.display.set_caption(cap) pygame.quit() sys.exit()
pygame.Rect(x, y, width, height) で矩形範囲を指定できる。それを配列 rects に蓄えておいて…。スクリーンに相当する Surface、を格納している screen に blit() で描画する際に、area=rects[index] という形で渡して、「元画像の、この範囲だけを抜き出して描画せよ」的な処理をしている。
実行したら、こうなった。
元画像の一部だけを描画できている。
ということで、blit() に pygame.Rect を渡せば、目的は果たせると分かった。
ちなみに、上記のスクリプトでは pygame.Rect() を使ってるけど、その後ググってみたら、blit(img, (px, py), (x, y, w, h)) という書き方もできると知った。
# make split area list rects = [] w, h = 96, 96 for y in range(8): for x in range(8): rects.append((x * w, y * h, w, h)) # ... # draw image x = 160 - 96 / 2 y = 120 - 96 / 2 screen.blit(img, (x, y), rects[index])
処理速度で違いはあったりするのかな…。どうなんだろう。
◎ subsurface() を使う方法。 :
Pygame の Surface には、subsurface() というメソッドがあって、コレを使うと、元画像の一部を新たな Surface として得ることができるらしい。引数として、やはり pygame.Rect を渡す模様。
ということで試してみた。
_03_subsurface.py
Rect を配列に蓄える代わりに、subsurface を配列 imgs に蓄えている。また、screen に blit() で描画する際、元画像として subsurface を渡している。
実行したら、こうなった。
ということで、subsurface を使っても、元画像の一部を切り出した感じで描画できると分かった。
ただ、Surface のドキュメントを読むと、ハードウェア描画がどうのこうの、みたいなことが書いてあるような…。
_pygame.Surface - pygame v2.0.1.dev1 documentation
ディスプレイモードがハードウェア描画じゃない場合は、表示に使ってる Surface から subsurface を作ることもできる、と言ってるのかな…。逆に言うと、ハードウェア描画の場合は表示に使ってる Surface から subsurface を作れませんよ、ということだろうな…。
ということで試してみた。
_03_subsurface.py
import pygame import sys SCRW, SCRH = 320, 240 pygame.init() screen = pygame.display.set_mode((SCRW, SCRH), pygame.DOUBLEBUF) imgname = "spritesheet_number.png" img = pygame.image.load(imgname).convert_alpha() # make subsurface imgs = [] w, h = 96, 96 for y in range(8): for x in range(8): _rect = pygame.Rect(x * w, y * h, w, h) imgs.append(img.subsurface(_rect)) index = 0 running = True clock = pygame.time.Clock() # Main loop while running: # check event for ev in pygame.event.get(): if ev.type == pygame.QUIT: running = False if ev.type == pygame.KEYDOWN: if ev.key == pygame.K_ESCAPE or ev.key == pygame.K_q: # Push ESC or Q key running = False screen.fill((40, 60, 200)) # clear screen # draw image x = 160 - 96 / 2 y = 120 - 96 / 2 screen.blit(imgs[index], (x, y)) index = (index + 1) % len(imgs) pygame.display.flip() clock.tick_busy_loop(60) cap = "Image Split - %5.2f FPS" % (clock.get_fps()) pygame.display.set_caption(cap) pygame.quit() sys.exit()
Rect を配列に蓄える代わりに、subsurface を配列 imgs に蓄えている。また、screen に blit() で描画する際、元画像として subsurface を渡している。
実行したら、こうなった。
ということで、subsurface を使っても、元画像の一部を切り出した感じで描画できると分かった。
ただ、Surface のドキュメントを読むと、ハードウェア描画がどうのこうの、みたいなことが書いてあるような…。
_pygame.Surface - pygame v2.0.1.dev1 documentation
ディスプレイモードがハードウェア描画じゃない場合は、表示に使ってる Surface から subsurface を作ることもできる、と言ってるのかな…。逆に言うと、ハードウェア描画の場合は表示に使ってる Surface から subsurface を作れませんよ、ということだろうな…。
[ ツッコむ ]
以上です。