ゲーム作成に役立つオブジェクト指向6

はじめに

前回のInterpreterパターンは、
そのままではゲームに使うことはできません。
 
それは、
Nodeクラスに、キャラの行動パターンを記述しなければならない
からです。
 
それを回避する方法がVisitorパターンです。

Visitorパターンとは?

Visitorとは、「訪問者」のことです。
今回の例で言えば、敵オブジェクト(Enemy)が「訪問者」です。
そして、その受け入れ先が、構文解析クラス(Node以下)となります。
 
EnemyがNodeをぐるぐる回りながら、スクリプトの命令を実行する、
というようなイメージになります。
 

クラス図は?

ビジターパターン
前回との大きな違いは、

  • ITaskインターフェース、Enemyクラスの追加
  • NondeのparseメソッドのパラメータにEnemyを追加

ということになります。
 
ITaskというのは、タスクシステムの「タスク」ですね。
これを継承してEnemyクラスを実装します。
 
parseメソッドのパラメータにEnemyがあるのは、
Nodeの中をぐるぐる回るためですね。
 
さて、ほかにもクラスが増えていますが、
TopNode/RepeatingNodeは、「NonterminalNode」にあたり、
AttackingNode/MovingNode/ExplodingNodeは「TerminalNode」にあたります。
 

処理の流れは?

  1. Enemyクラスの生成
  2. Contextクラスを生成し、スクリプトを読み込ませる
  3. TopNodeクラスを生成し、parseメソッドにContext、thisポインタを渡す。

というのがNodeの入り口までの流れです。
 
そうしてNodeの中をぐるぐる回っているうちに、
「TerminalNode」に達します。
そうした場合には、パラメータのenemy.execScriptメソッドに、thisポインタを渡します。
 
これにより、Enemyが動き出すわけです。
 
なぜ、「TerminalNode」のthisポインタを渡すのかというと、
まず、

  • 処理の振り分けができる

ということがあります。
(まあ、ここらへんは好みの問題ではあります。
たとえば、「TerminalNode」を1つにして、if〜else if〜で分けるという手もあります)
 
さらに、
例えば、攻撃したりする場合には、
「どの攻撃方法で攻撃するか?」
という情報や、
 
移動する場合には、
「どこに移動するのか?」
という情報が必要になります。
 
それを自フィールドに詰めてあげれば、
Enemy.execSpript内でパラメータを覗くことができるようになるわけです。
 
 
ということで、Visitorパターンでしたー。

補足

Enemy.execScriptをprotected属性にしていますが、
これはJavaでは、
「同一パッケージであれば、protected属性を見ることができ、
異なるパッケージであれば、見ることができない」
という特性を利用しているものです。
 
つまり、EnemyとNodeは同一パッケージで実装することになります。
 
これに対して、C++ではそういった特性はないので、
publicにしてしまいましょう!
(スミマセン、、、C++で実装するいいアイデアが浮かびませんでした…(´Д`;

Pythonで画像処理8

輪郭抽出です。
大きめの画像でやると、そこそこキレイに抽出できますね。

import pygame
import math

_gSrc = None

pygame.init()
pygame.display.set_mode((150, 150), 0, 32)
pygame.display.set_caption("輪郭抽出")
_gScr = pygame.display.get_surface()
tBuf = pygame.image.load("kenmo.jpg").convert()

tblGray = []
# グレースケールテーブル作成
for j in range(tBuf.get_height()):
	w = []
	for i in range(tBuf.get_width()):
		colorA = tBuf.get_at((i, j))
		gray = (colorA[0] + colorA[1] + colorA[2]) / 3
		w.append(gray)
	tblGray.append(w)
# 輪郭抽出(グラディエント法)
for j in range(tBuf.get_height() - 1):
	for i in range(tBuf.get_width() - 1):
		# 微分、というか差分
		dx = tblGray[j][i+1] - tblGray[j][i]
		dy = tblGray[j+1][i] - tblGray[j][i]
		#tblGray[j][i] = math.sqrt(dx ** 2 + dy ** 2)
		tblGray[j][i] = abs(dx) + abs(dy) # 高速化

# 2値化
AMP = 5 # 出力レベル
BLACK = (0, 0, 0, 255)
WHITE = (255, 255, 255, 255)
for j in range(tBuf.get_height() - 1):
	for i in range(tBuf.get_width() - 1):
		if(tblGray[j][i] > AMP):
			tBuf.set_at((i, j), WHITE)
		else:
			tBuf.set_at((i, j), BLACK)

while True:
	_gScr.blit(tBuf, (0, 0), tBuf.get_rect())
	pygame.display.update()
	pygame.time.wait(10)

 
ノイズが多い場合は出力レベルを上げるとキレイになりますよー。