今回はプログラミング言語の「Python」を使って作成しました。
下記の本の「3Dブロック崩しのサンプル」にボールの数やブロックのHPを増やして難易度が高くなるように改造しました。
■ プログラム
import traceback
try:
import sys
import random
from math import sin, cos, floor, radians
import pygame
from pygame.locals import QUIT, K_LEFT, K_RIGHT, KEYDOWN
#----------------------------------------------------------------------
# Pygame初期設定
#----------------------------------------------------------------------
pygame.init()
pygame.key.set_repeat(5, 5)
SURFACE = pygame.display.set_mode([600, 600])
FPSCLOCK = pygame.time.Clock()
#----------------------------------------------------------------------
# 3Dオブジェクトクラス
#----------------------------------------------------------------------
class Cube:
SPEED = 5
THETA = 270 + floor(random.randint(-10, 10))
HP = 10
""" Cube for blocks and paddle """
polygons = [
[2, 1, 5, 6], [0, 1, 2, 3], [4, 5, 1, 0],
[2, 6, 7, 3], [7, 6, 5, 4], [0, 3, 7, 4]
]
def __init__(self, x, y, z, w, h, d, color):
self.xpos = x
self.ypos = y
self.width = w
self.height = h
self.color = color
self.pos = []
self.vertices = [
{"x": x - w, "y": y - h, "z": z + d},
{"x": x - w, "y": y + h, "z": z + d},
{"x": x + w, "y": y + h, "z": z + d},
{"x": x + w, "y": y - h, "z": z + d},
{"x": x - w, "y": y - h, "z": z - d},
{"x": x - w, "y": y + h, "z": z - d},
{"x": x + w, "y": y + h, "z": z - d},
{"x": x + w, "y": y - h, "z": z - d},
]
def set_camera(self, rad_x, rad_y):
"update vertice positions depending on camera location"
self.pos.clear()
for vert in self.vertices:
p0x = vert["x"]
p0y = vert["y"]
p0z = vert["z"]
# rotate around X axis
p1x = p0x
p1y = p0y * cos(rad_x) - p0z * sin(rad_x)
p1z = p0y * sin(rad_x) + p0z * cos(rad_x)
# rotate around Y axis
p2x = p1x * cos(rad_y) + p1z * sin(rad_y)
p2y = p1y
p2z = -p1x * sin(rad_y) + p1z * cos(rad_y)
self.pos.append({"x": p2x, "y": p2y, "z": p2z})
def ishit(self, xpos, ypos):
"return if (x,y) hits the block"
return self.xpos-self.width < xpos < self.xpos+self.width \
and self.ypos-self.height < ypos < self.ypos+self.height
def translate(self, diffx, diffy):
"move the block"
self.xpos += diffx
self.ypos += diffy
for vert in self.vertices:
vert["x"] += diffx
vert["y"] += diffy
#----------------------------------------------------------------------
# 移動、衝突処理
#----------------------------------------------------------------------
def tick():
""" called periodically from the main loop """
global SPEED, THETA, BLOCKS, MESSAGE, BALLS
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
elif event.type == KEYDOWN:
if event.key == K_LEFT:
PADDLE.translate(-10, 0)
elif event.key == K_RIGHT:
PADDLE.translate(+10, 0)
if not MESSAGE is None:
return
# move the ball
for BALL in BALLS:
for block in BLOCKS:
if block.ishit(BALL.xpos, BALL.ypos) and not block == PADDLE:
block.HP -= 1
BALL.THETA = -BALL.THETA
diffx = cos(radians(BALL.THETA)) * BALL.SPEED
diffy = sin(radians(BALL.THETA)) * BALL.SPEED
BALL.translate(diffx, diffy)
# hit any blocks?
count = len(BLOCKS)
BLOCKS = [x for x in BLOCKS if x == PADDLE or x.HP>0 or not x.ishit(BALL.xpos, BALL.ypos)]
if len(BLOCKS) != count:
BALL.THETA = -BALL.THETA
# hit ceiling, wall or paddle?
if BALL.ypos > 800:
BALL.THETA = -BALL.THETA
SPEED = 10
if BALL.xpos < -250 or BALL.xpos > 250:
BALL.THETA = 180 - BALL.THETA
if PADDLE.ishit(BALL.xpos, BALL.ypos):
BALL.THETA = 90 + ((PADDLE.xpos - BALL.xpos) / PADDLE.width) * 80
BALLS = [x for x in BALLS if not x.ypos < -600]
if len(BALLS) == 0:
MESSAGE = MESS1
if len(BLOCKS) == 2:
MESSAGE = MESS0
# Rotate the Cube
rad_y = PADDLE.xpos / 1000
rad_x = 0.5 + BALL.ypos / 2000
BALL.set_camera(0.5, rad_y)
for block in BLOCKS:
block.set_camera(0.5, rad_y)
def paint():
"update the screen"
SURFACE.fill((0, 0, 0))
# Paint polygons
for block in BLOCKS:
for indices in block.polygons:
poly = []
for index in indices:
pos = block.pos[index]
zpos = pos["z"] + 500
xpos = pos["x"] * 500 / zpos + 300
ypos = -pos["y"] * 500 / zpos + 500
poly.append((xpos, ypos))
pygame.draw.lines(SURFACE, block.color, True, poly)
# Paint balls
for BALL in BALLS:
for indices in BALL.polygons:
poly = []
for index in indices:
pos = BALL.pos[index]
zpos = pos["z"] + 500
xpos = pos["x"] * 500 / zpos + 300
ypos = -pos["y"] * 500 / zpos + 500
poly.append((xpos, ypos))
pygame.draw.lines(SURFACE, BALL.color, True, poly)
if not MESSAGE is None:
SURFACE.blit(MESSAGE, (150, 400))
pygame.display.update()
FPS = 60
BLOCKS = []
BALLS = [Cube(random.randint(-200, 200), random.randint(200, 300), 0, 5, 5, 5, (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))) for i in range(20)]
PADDLE = Cube(0, 0, 0, 100, 10, 5, (255, 255, 255))
MESSAGE = None
MYFONT = pygame.font.SysFont(None, 80)
MESS0 = MYFONT.render("Cleared!!!", True, (255, 255, 0))
MESS1 = MYFONT.render("Game Over!", True, (255, 255, 0))
#----------------------------------------------------------------------
# メインルーチン
#----------------------------------------------------------------------
def main():
""" main routine """
colors = [(255, 0, 0), (255, 165, 0), (242, 242, 0),
(0, 128, 0), (128, 0, 128), (0, 0, 250)]
for ypos in range(0, len(colors)):
for xpos in range(-3, 4):
block = Cube(xpos * 70, ypos * 50 + 450, 0, 30, 10, 5, colors[ypos])
BLOCKS.append(block)
BLOCKS.append(PADDLE)
#----------------------------------------------------------------------
# 毎フレーム処理
#----------------------------------------------------------------------
while True:
tick()
#----------------------------------------------------------------------
# 描画処理
#----------------------------------------------------------------------
paint()
FPSCLOCK.tick(FPS)
if __name__ == '__main__':
main()
except Exception as e:
# 文字列を取得する format_exec()メソッド
print("エラー情報\n" + traceback.format_exc())
input()
■ 参考書
「Pythonゲームプログラミング 知っておきたい数学と物理の基本 (Future Coders(NextPublishing))」
■ ゲーム用ライブラリ
「Pygame」
■ プログラミング言語
「Python」