Pygameラッパライブラリ

Atomic Bomb 0.1

Pygameを使いやすくするためのラッパライブラリを作ってみました。
 
Pygame自体が優れたライブラリなので、ラッパする必要なないのですが…。
しかし、なんというか、逆に使いにくくなっているかもしれません(´▽`;
 
 
提供する機能は、マウス入力と、単純な図形の描画機能のラッパです。
あと、シーン遷移クラスを用意しています。
 
使い方についてはmain.pyのサンプルを見ることになります。(手抜きだ…(´Д`;
 
<pygamelib.py>

# PygameLib
# Pygameを使いやすくしたライブラリです。
import pygame
import pygraphics
import pyinput

class PygameLib:
	def __init__(self):
		self.sceneManager = None    # シーンマネージャ
		self.width = 0              # 画面サイズ(横)
		self.height = 0             # 画面サイズ(縦)
		self.clearColor = (0, 0, 0) # 消去色
		self.flgExit = False        # ゲーム終了フラグ
		self.wait = 10              # 更新ウェイト
		
		self.graphics = pygraphics.PyGraphics()
		self.input = pyinput.PyInput()
	# シーンマネージャ登録
	def registSceneManager(self, mng):
		self.sceneManager = mng
	# 初期化処理
	def initialize(self, width, height):
		pygame.init()
		self.width = width
		self.height = height
		pygame.display.set_mode((width, height), 0, 32)
		self._screen = pygame.display.get_surface()
		
		for scene in self.sceneManager.sceneDict.values():
			scene.create(self, self.graphics, self.input)
	# ゲームループ
	def run(self):
		while True:
			if(self.flgExit): return
			for e in pygame.event.get():
				if(e.type == pygame.QUIT): return
			pygame.display.update()
			pygame.time.wait(self.wait)
			self._screen.fill(self.clearColor)
			# 入力情報更新
			self.input.reflesh()
			# シーン実行
			self.getScene().update()
	# シーン遷移
	def changeScene(self, key):
		self.sceneManager.changeScene(key)
	# シーン取得
	def getScene(self):
		return self.sceneManager.scene

 
<pygraphics.py>

# 描画関連のラッパークラスです
import pygame
from util import *
import math

class PyGraphics:
	# 文字列の描画
	def drawMessage(self, pos, message, size=25, fColor=(255, 255, 255, 255), bColor=None):
		font = pygame.font.SysFont("", size)
		if(bColor == None):
			pygame.display.get_surface().blit(font.render(message, 0, fColor), pos.getTuble())
		else:
			pygame.display.get_surface().blit(font.render(message, 0, fColor, bColor), pos.getTuble())
	# 線分描画
	def drawLine(self, pos1, pos2, color=(0, 0, 255, 255)):
		pygame.draw.line(pygame.display.get_surface(), color, pos1.getTuble(), pos2.getTuble())
	# 円描画
	def drawCircle(self, pos, radius, color=(0, 0, 255, 255)):
		pygame.draw.circle(pygame.display.get_surface(), color, pos.getTuble(), radius)
	# 矩形描画
	def drawRect(rect, color=(0, 0, 255, 255)):
		pygame.draw.rect(pygame.display.get_surface(), color, rect)

 
<pyinput.py>

# 入力関連のラッパークラスです
import pygame
from util import *

class PyInput:
	def __init__(self):
		self.pushDict = {}
		self.pressDict = {}
		self.pressDictPrev = {}
		self.mouse = Vec2D()
	def reflesh(self):
		self.refleshMouse()
		self.refleshPress()
		self.refleshPush()
	def refleshMouse(self):
		self.mouse.x, self.mouse.y = pygame.mouse.get_pos()
	def refleshPush(self):
		for key in self.pressDict.keys():
			if(self.pressDict[key] and not(self.pressDictPrev[key])):
				if(self.pushDict.has_key(key)):
					del self.pushDict[key]
				self.pushDict[key] = True
			else:
				self.pushDict[key] = False
	def refleshPress(self):
		for item in self.pressDict.iteritems():
			self.pressDictPrev[item[0]] = item[1]
		mouseL, mouseM, mouseR = pygame.mouse.get_pressed()
		self.pressDict["M_L"] = mouseL
		self.pressDict["M_M"] = mouseM
		self.pressDict["M_R"] = mouseR
	# そのフレーム内に押したかどうか
	def isPush(self, strKey):
		if(self.pushDict.has_key(strKey)):
			return self.pushDict[strKey]
		else:
			return False
	# 押しているかどうか
	def isPress(self, strKey):
		if(self.pressDict.has_key(strKey)):
			return self.pressDict[strKey]
		else:
			return False

 
<scene.py>

# シーン抽象クラス
class IScene:
	def create(self, p, g, i):
		self.p = p
		self.g = g
		self.i = i
	def initialze(self):
		raise "Cannot instance."
	def update(self):
		raise "Cannot instance."

# シーン管理クラス
class SceneManager:
	def __init__(self):
		self.sceneDict = {}
		self.scene = None
	def regist(self, key, scene):
		self.sceneDict[key] = scene
	def getScene(self):
		return self.scene
	def changeScene(self, key):
		if(self.sceneDict.has_key(key)):
			self.scene = self.sceneDict[key]
		else:
			raise "Non-Existance Key :%s" % (key)

 
<util.py>

import math

# 2次元ベクトル
class Vec2D:
	def __init__(self, x=0, y=0):
		self.x = x
		self.y = y
	def __add__(self, pos):
		return Vec2D(self.x + pos.x, self.y + pos.y)
	def __iadd__(self, pos):
		return Vec2D(self.x + pos.x, self.y + pos.y)
	def __sub__(self, pos):
		return Vec2D(self.x - pos.x, self.y - pos.y)
	def __mul__(self, vec):
		return self.x*vec.x + self.y*vec.y
	def __eq__(self, vec):
		return (self.x==vec.x and self.y==vec.y)
	def nomalize(self):
		d = math.sqrt(self.x ** 2 + self.y ** 2) * 1.0
		if(d == 0): d = 1
		return Vec2D(self.x / d, self.y / d)
	def cross(self, scalar):
		return Vec2D(self.x*scalar, self.y*scalar)
	def lengthSq(self):
		return self.x*self.x + self.y*self.y
	def distance(self, vec):
		return math.sqrt(pow(self.x - vec.x, 2) + pow(self.y - vec.y, 2))
	def distanceSq(self, vec):
		return pow(self.x - vec.x, 2) + pow(self.y - vec.y, 2)
	def getOrthogonalVector(self):
		return Vec2D(self.y, -self.x)
	def reverse(self):
		return Vec2D(self.x*-1, self.y*-1)
	def getTuble(self):
		return (self.x, self.y)
	def debug(self):
		print "(x,y)=(%f,%f)" % (self.x, self.y)

# タスク基底クラス
class Task:
	def __init__(self, pos=Vec2D(0,0), mov=Vec2D(0,0), age=-99):
		self.pos = pos
		self.mov = mov
		self.age = age
		self.alive = True
	def update(self):
		self.pos += self.mov
		self.age -= 1
		if(self.age == 0):
			return False
		return True
	def debug(self):
		print "p(x,y)=(%d,%d) m(x,y)=(%d,%d) a=%d" % (self.pos.x, self.pos.y, self.mov.x, self.mov.y, self.alive)

 
<main.py>

import pygamelib
import scene
import math
import random
from util import *

# タイトルシーン
class SceneTitle(scene.IScene):
	def __init__(self):
		self.timer = 0
	def initialize(self):
		self.timer = 0
	def update(self):
		posX = random.randint(-1, 1)
		posY = random.randint(-1, 1)
		self.g.drawMessage(Vec2D(200+posX, 100+posY), "Atomic Bomb ver 0.1", 30)
		self.timer += 2
		if(self.timer > 360):
			self.timer -= 360
		alpha = 255*abs(math.sin(math.radians(self.timer)))
		self.g.drawMessage(
			Vec2D(240, 200),
			"Push Left Click!",
			25,
			(255, 255, 255, 255),
			(alpha, 0, 0, alpha))
		if(self.i.isPush("M_L")):
			self.p.changeScene(1)
			self.p.getScene().initialize()

# 爆弾タスク
class Bomb(Task):
	def __init__(self, pos):
		self.r = 1
		self.pos = pos
		self.color = (0, 255, 0, 255)
	def Update(self):
		self.r += 1
		if(self.r > 300):
			self.r = 300
		color = self.r*255/100.0
		if(color > 255): color = 255
		self.color = (color, 255-color, 0, 255)
		
		if(self.r > 100):
			return False # ドカン
		return True

# メインシーン
class SceneMain(scene.IScene):
	def __init__(self):
		self.score = 0
		self.bombList = []
		self.effectList = []
		self.gameover = False
	def initialize(self):
		self.score = 0
		self.bombList = []
		self.effectList = []
		self.gameover = False
		self.p.wait = 100
	def update(self):
		posX = random.randint(-5, 5)
		posY = random.randint(-5, 5)
		size = random.randint(30, 32)
		self.g.drawMessage(Vec2D(200+posX, 100+posY), "Atomic Bomb ver 0.1", size)
		
		if(random.randint(0, 30) == 0):
			if(len(self.bombList) < 64):
				# 爆弾登場
				bomb = Bomb(Vec2D(random.randint(100, 540), random.randint(100, 380)))
				self.bombList.append(bomb)
		# 爆弾
		tmpList = []
		emergency = False
		for bomb in self.bombList:
			if(not(self.gameover)):
				if(self.i.isPush("M_L")):
					if(self.i.mouse.distance(bomb.pos) < bomb.r):
						# 爆破
						self.score += 10 # スコア
						self.p.wait /= 2 # タイマを早める
						if(self.p.wait < 1): self.p.wait = 1
						for i in range(10):
							effect = Task(
								Vec2D(bomb.pos.x, bomb.pos.y),
								Vec2D(random.randint(-5, 5), random.randint(-5, 5)),
								30)
							self.effectList.append(effect)
						continue
			if(not(bomb.Update())):
				self.gameover = True
			if(bomb.r > 50):
				emergency = True
			# 爆弾描画
			self.g.drawCircle(bomb.pos, bomb.r, bomb.color)
			tmpList.append(bomb)
		self.bombList = tmpList
		if(emergency and not(self.gameover)):
			self.g.drawMessage(Vec2D(240, 150), "Emergency!!!", 25, (255, 0, 0, 0))

		# 爆破
		tmpList = []
		for effect in self.effectList:
			if(effect.update()):
				effect.mov.y += 0.1
				alpha = math.sin(math.radians(effect.age * 90 / 20)) * 255
				self.g.drawCircle(effect.pos, 5, (alpha, alpha, 0, alpha))
				tmpList.append(effect)
		self.effectList = tmpList
		# ゲームオーバー
		if(self.gameover):
			self.g.drawMessage(Vec2D(250, 200), "GAME OVER")
			self.g.drawMessage(
				Vec2D(240, 250),
				"Push Right Click",
				25,
				(255, 255, 255, 255))
			if(self.i.isPush("M_R")):
				self.p.changeScene(0)
				self.p.getScene().initialize()
		# スコア
		self.g.drawMessage(Vec2D(0, 0), "SCORE:%d" % (self.score))
def main():
	# シーン登録
	mng = scene.SceneManager()
	sTitle = SceneTitle()
	sMain = SceneMain()
	mng.regist(0, sTitle)
	mng.regist(1, sMain)
	mng.changeScene(0)
	pgl = pygamelib.PygameLib()
	pgl.registSceneManager(mng)
	
	# 初期化
	pgl.initialize(640, 480)
	
	# 実行
	pgl.run()

if(__name__ == "__main__"):
	main()