以前にPyGameをかんたんに使えるようにどうにかしようと作っていたモジュールがこれ。
import pygame,pygame.mixer,time
from pygame.locals import *
import os
class pyg:
def __init__(self,title=""):
pygame.mixer.pre_init(44100,-16,2,1024)
pygame.mixer.init()
pygame.init()
self._running = True
self.keys = [False] * 128
self.keyDownCnt = 0
_mode = pygame.HWSURFACE | pygame.DOUBLEBUF #| pygame.FULLSCREEN
self._real = (200,200)
info = pygame.display.Info()
screen_w = info.current_w
screen_h = info.current_h
#self._size = (screen_w,screen_h)
self._size = (400,400)
os.environ["SDL_VIDEO_CENTERED"] = "1"
self.screen = pygame.display.set_mode(self._size,_mode)
pygame.display.set_caption(title)
self.bg = pygame.Surface(self._real,pygame.HWSURFACE | pygame.SRCALPHA)
self.fg = pygame.Surface(self._real,pygame.HWSURFACE | pygame.SRCALPHA)
self.db = pygame.Surface(self._size,pygame.HWSURFACE | pygame.SRCALPHA)
def process( self,value=0.03 ):
st = time.time()
_procWait = True
while _procWait:
for event in pygame.event.get():
self.event = event
if event.type == pygame.QUIT:
self._running = False
if event.type == pygame.KEYDOWN:
key = event.key
if key < 128:
if self.keys[key]==False:
self.keyDownCnt += 1
self.keys[key] = True
else:
print(event)
if event.type == pygame.KEYUP:
key = event.key
if key < 128:
if self.keys[key]:
self.keyDownCnt -= 1
self.keys[key] = False
else:
pass
if time.time() - st > value:
self.bg.blit(self.fg,(0,0))
self.db = pygame.transform.scale(self.bg,self._size)
self.screen.blit(self.db,(0,0))
pygame.display.update()
self.bg.fill((0,0,0,0))
self.fg.fill((0,0,0,0))
self.screen.fill((0,0,0))
_procWait = False
def getPyg(self):
return pygame
def quit(self):
pygame.quit()
def imgload(self,path):
return pygame.image.load(path).convert_alpha()
def isKeyDown(self,key):
return self.keys[ord(key)]
def isKeyDownCode(self,key):
return self.keys[key]
def isAnyKeyDown(self):
return self.keyDownCnt != 0
def putFg(self,img,pos):
self.fg.blit(img,pos)
かんたんなのか。
パット見て微妙。
まあ、最終的には見なくても良い部分になるとこだから多少はね。
ただ、クラスにする必要があったのか。とか。
多分クラスにする必要がそもそも無いのでそこらへんから直してゆこうと思います。
そのまえにとりあえずこのpygを使ってPyGameで何か作るときはこうなるというテストプログラム。
#!/usr/bin/python3
import pyg
p = pyg.pyg("test")
imgPath="/usr/share/raspberrypi-artwork/launch.png"
img = p.imgload(imgPath)
pos = [0] * 2
pyg.pygame.mixer.music.load("../output.ogg")
pyg.pygame.mixer.music.play()
while p._running:
p.process()
if p.event.type == 4:
pos[0]=int(p.event.pos[0]/2-16)
pos[1]=int(p.event.pos[1]/2-16)
p.putFg(img,pos)
if p.isKeyDownCode(27):
p._running=False
if p.isKeyDown("a"):
print("true")
if p.isAnyKeyDown():
print("any")
p.quit()
ガチガチに隠蔽するとホント分かりづらくなるのでpygはゆるーくラップするのみって感じですね。
主にイベント処理とかフレームのウェイト処理とかがpygのお仕事。
地味にPyGameのサウンド関連のバッドノウハウも吸収している。
event.typeでマジックナンバーになっちゃってるとかが気になる。
ここらへんもどうにかしたい。
あとは、その界隈でputFgというイメージを表示するためのファンクションを呼んでいるのだけれどもここがひっかかった。
動いてなくても毎回こいつを、いくつあるかわからない画像を精査しながらputFgしてゆくのは少しつらい気がする。
スプライトのようにあつかえないだろうか。
脱クラス
ひとまずあまり意味のないクラスはやめようということで書き直した。
BG,FGなどと分けていたグラフィック関係もsurfaceということで一本化した。
特に分ける必要もないかなとか。
import pygame
from pygame.locals import *
import os, time
def getScreenSize():
info = pygame.display.Info()
return info.current_w, info.current_h
def setTitle(title):
pygame.display.set_caption(title)
def init(real=[128,64],disp=-1):
global _running, _keys, _keyDownCnt, _size, _event
global mixer, screen, surface, surfBuf
_running = True
_keys = [False] * 128
_keyDownCnt = 0
_event = 0
if disp == -1:
disp = real
_size = { "real":real, "display":disp }
mixer = pygame.mixer
mixer.pre_init( 44100, -16, 2, 1024 )
mixer.init()
pygame.init()
mode = pygame.HWSURFACE | pygame.DOUBLEBUF
os.environ["SDL_VIDEO_CENTERED"] = "1"
screen = pygame.display.set_mode(_size["display"], mode)
surface = pygame.Surface(_size["real"], mode)
surfBuf = pygame.Surface(_size["display"], mode)
def process( wait = 0.03 ):
global _keyDownCnt, _running, _keys, _event, screen
st = time.time()
_procWait = True
while _procWait:
for event in pygame.event.get():
_event = event
if event.type == pygame.QUIT:
_running = False
if event.type == pygame.KEYDOWN:
key = event.key
if key < 128:
if _keys[key] == False:
_keyDownCnt += 1
_keys[key] = True
if event.type == pygame.KEYUP:
key = event.key
if key < 128:
if _keys[key]:
_keyDownCnt -= 1
_keys[key] = False
if time.time() - st > wait:
surfBuf = pygame.transform.scale(surface, _size["display"])
screen.blit(surfBuf,(0,0))
pygame.display.update()
surface.fill((0,0,0,0))
screen.fill((0,0,0))
_procWait = False
def quit():
pygame.quit()
def imgload(path):
return pygame.image.load(path).convert_alpha()
def isKeyDown(key):
global _keys
return _keys[ord(key)]
def isKeyDownCode(key):
global _keys
return _keys[key]
def isAnyKeyDown():
global _keyDownCnt
return _keyDownCnt != 0
def putImg(img,pos):
global surface
surface.blit(img,pos)
使い方はだいたいいっしょ。
#!/usr/bin/python3
import pyg
pyg.init((128,64),(384,192))
pyg.setTitle("test")
imgPath="/usr/share/raspberrypi-artwork/launch.png"
img = pyg.imgload(imgPath)
pos = [0] * 2
while pyg._running:
pyg.process()
if pyg._event.type == 4:
pos[0] = int( pyg._event.pos[0]/3-16)
pos[1] = int( pyg._event.pos[1]/3-16)
pyg.putImg(img, pos)
if pyg.isKeyDownCode(27):
pyg._running = False
if pyg.isKeyDown("a"):
print("true")
if pyg.isAnyKeyDown():
print("any")
pyg.quit()
赤いとこが違うくらいで大体かわらない感じ。
クラスをやめたい理由にはメモリをより多く消費してしまうというのがあって、クラスを使わないといけないような処理じゃないならクラスは使わないほうがいい。泥臭いコードであれモジュールという形で普段は見なくて済むのでやばいコードもちょっと忘れてられる(ぉ
pyg.pyでハードウェアサーフェイスとかダブルバッファに関して外してテストしていたのだけどもどうもRaspberry Pi 2Bに関して言えばつけておいたほうがスムーズに動く感じがある。
環境によりけりと思うんだけど。Raspberry PiはSDLと相性がいいのかもしれない。よくわからないけど。
みりゃわかるだろう的な感じにコメントってつけてないんだけども。
あまりにも不親切なんだろうか。と思ったので親切にコメントをつけてみよう。
#!/usr/bin/python3
"""
pygをインポートするよ。
"""
import pyg
"""
pygを使えるようにする。これによってPyGameの機能が全部つかえるようになる。
引数は実際の画面サイズと表示するときのサイズ。表示するときのサイズは省略可能で省略すると1:1で表示される。
pyg.initはPyGameのミキサーの初期化や、毎回ランダムにウィンドウの位置が変わってしまうのを画面の中心に出すようにするとかいうバッドノウハウに関する部分も網羅してる。
"""
pyg.init((128,64),(384,192))
"""
ウィンドウのタイトルを設定。
initに含んでもいいかなと思ってる。あとフルスクリーンだとあまり意味がない。
現在フルスクリーンにするためのファンクションはpygには取り込んでいないけどね。
"""
pyg.setTitle("test")
"""
良い画像が無いかなと、絶対存在してるだろう画像ということでRaspbianのアプリケーションメニューのアイコンを読み込むことにした。PyGameが使えるRaspberry PiのRaspbianなら絶対この画像はあるだろうと。
pyg.imgloadで透過情報付きの画像データとしてimgに格納しているよ。
"""
imgPath="/usr/share/raspberrypi-artwork/launch.png"
img = pyg.imgload(imgPath)
"""
これは便宜上。画像の位置設定するための変数なんだけど仕方がなくここで初期化している感じかな。きもちわるいね。
"""
pos = [0] * 2
"""
ここからメインループ
pyg._runningがTrueなら無限ループになる。ウィンドウが閉じられたりプログラム中で_runningがFalseにされるとループを抜けてpyg.quit()が実行される。
pyg._event.type == 4 というのはマウスムーブ。マウスが動いたら座標を取得して画像の位置を決定するみたいな処理が書いてある。マジックナンバーになっているのはなぜなんだろう。pygame.MOUSEMOTIONでおきかえられる。
isKeyDownとisKeyDownCodeはキャラクタとアスキーコードでボタンが押されているかを判断するもの。
isAnyKeyDownは何かボタンが押されているかを判断するためのものだよ。
pyg.processが結構重要な処理で、このなかでウェイト処理しながらイベントの処理もしている。
pyg._event.typeと参照してpygの中では定義されていないイベントもフックできる。
待ち時間を変更するには pyg.process( 0.2 )みたいに。単位は秒。
"""
while pyg._running:
pyg.process()
if pyg._event.type == 4: #pygame.MOUSEMOTION
pos[0] = int( pyg._event.pos[0]/3-16)
pos[1] = int( pyg._event.pos[1]/3-16)
pyg.putImg(img, pos) #例のRPiのアイコンを指定位置に表示する
if pyg.isKeyDownCode(27): #キーコード27というのはESC。つまりESCで終了するっていうコード
pyg._running = False
if pyg.isKeyDown("a"): #aが押されていればコンソールにtrueと表示。
print("true")
if pyg.isAnyKeyDown(): #なんでも! キーが押されてたらanyと表示する。
print("any")
pyg.quit()
海外のソースなんか見てもらえればわかるけど、これくらい書かないとコメントっていみねーんです。
#表示
なんてコメントあってもなくても一緒なので。
でさ? みづらいじゃないですか。こうやってコメントついてると。
なのでわたしはあんまりコメント書かないんだよね。
ファンクションの名前みたらだいたいうっすら処理がわかる、引数もだいたい想像つく。
そういうのがいい設計だと思ってるんだよねー。
などと超脱線。
大したことやってないのはよくわかってもらえたと思う。
そして凄くかんたんに使えてるのもわかってもらえたんじゃないかな!