ファイアボール
ここでは始点から終点へ向けて弾をまっすぐ飛ばす方法を説明します。1945では敵機が自機に向けてまっすぐ弾を飛ばす際に同じアルゴリズムを使っています。サンプルスクリプトでは、画面の中央(始点)からマウスでクリックした方向(終点)にファイアボールを飛ばします。マウスを押しっぱなしにして動かすとスクリーンショットのように乱れ撃ちできます。
サンプルスクリプト
#!/usr/bin/env python #coding:utf-8 import pygame from pygame.locals import * import math import sys SCR_RECT = Rect(0, 0, 640, 480) START = (320, 240) # ファイアボールの始点 def main(): pygame.init() screen = pygame.display.set_mode(SCR_RECT.size) pygame.display.set_caption(u"ファイアボール") # スプライトグループ all = pygame.sprite.RenderUpdates() Fireball.containers = all # スプライトの画像を登録 Fireball.image = load_image("fireball.png") clock = pygame.time.Clock() while True: clock.tick(60) screen.fill((0,0,0)) mouse_handler() all.update() all.draw(screen) pygame.display.update() for event in pygame.event.get(): if event.type == QUIT: pygame.quit() sys.exit() elif event.type == KEYDOWN and event.key == K_ESCAPE: pygame.quit() sys.exit() def mouse_handler(): """マウスイベントを検知してファイアボールを発射""" mouse_pressed = pygame.mouse.get_pressed() if mouse_pressed[0]: x, y = pygame.mouse.get_pos() Fireball(START, (x,y)) class Fireball(pygame.sprite.Sprite): """ファイアボール""" speed = 10 def __init__(self, start, target): pygame.sprite.Sprite.__init__(self, self.containers) self.rect = self.image.get_rect() # 始点と終点をセット self.start = start self.target = target self.rect.center = self.start # 終点の角度を計算 self.direction = math.atan2(target[1]-start[1], target[0]-start[0]) # 速度を計算 self.vx = math.cos(self.direction) * self.speed self.vy = math.sin(self.direction) * self.speed def update(self): self.rect.move_ip(self.vx, self.vy) # 画面外に出たらオブジェクトを破棄 if not SCR_RECT.contains(self.rect): self.kill() def load_image(filename, colorkey=None): try: image = pygame.image.load(filename) except pygame.error, message: print "Cannot load image:", filename raise SystemExit, message image = image.convert() if colorkey is not None: if colorkey is -1: colorkey = image.get_at((0,0)) image.set_colorkey(colorkey, RLEACCEL) return image if __name__ == "__main__": main()
始点と終点から角度を求める
画面中央の点からマウスでクリックした方向にファイボールを飛ばしたいので画面中央の点から見たマウスの位置を求める必要があります。上の図を見てください。始点(start)から終点(target)へ直線を引いたときの角度directionがファイアボールを飛ばす方向となります。始点の座標(画面中央の点)と終点の座標(マウス位置)からその間の角度を求めるにはmath.atan2()を使います。数学では逆正接 (arctangent) と呼ばれます。難しいことはともかく使い方は簡単です。
# 終点の角度を計算 self.direction = math.atan2(target[1]-start[1], target[0]-start[0]) print math.degrees(self.direction) # 角度を度で表示
第一引数には始点から終点のY座標の変化量、第二引数にはX座標の変化量を与えます。これで角度directionが求まります。 directionは-180度〜180度をラジアンという単位で表示した値です。Xの正の方向から半時計周りは負の角度、時計回りは正の角度となります。ラジアン単位をよく使われる度単位に変換するにはmath.degrees()が使えます。ソースコード中のdirectionの値を表示してみると角度がいくつか直感的につかめるので確かめてみてください。
角度から速度を求める
始点から終点への角度が求まりました。次は角度から速度を求めて見ます。ファイアボールの速度はspeed=10と定義されています。これをX方向の速度vxとY方向の速度vyに分解してやる必要があります。この分解はsin()とcos()を使います。
# 速度を計算
self.vx = math.cos(self.direction) * self.speed
self.vy = math.sin(self.direction) * self.speed
やっとX方向の速度vxとY方向の速度vyが求まりました。後は画像の移動と跳ね返り処理(2008/5/9)と同じです。ループが回るたびに距離に速度を足して移動させます。
self.rect.move_ip(self.vx, self.vy)
atan2()、degrees()、sin()、cos()といった関数はmathモジュールに含まれるのでmathをimportしておきます。mathは数学関係の関数がいろいろそろってます。