早速
C:\Program Files (x86)\CYBERFRONT\Sid Meier's Civilization 4(J)\Beyond the Sword(J)\Assets\Python\Pyhelpers.py
1を開いて、
getUnitList()
というメソッドを見ていきます…
## Sid Meier's Civilization 4
## Copyright Firaxis Games 2005
from CvPythonExtensions import *
#import Info as PyInfo
import CvUtil
gc = CyGlobalContext()
class PyPlayer:
# ...(中略)...
def getUnitList(self):
' UnitList - All of the players alive units '
lUnit = []
(loopUnit, iter) = self.player.firstUnit(false)
while( loopUnit ):
if ( not loopUnit.isDead() ): #is the unit alive and valid?
lUnit.append(loopUnit) #add unit instance to list
(loopUnit, iter) = self.player.nextUnit(iter, false)
return lUnit
最初に空のリストを作って、lUnit = []
最後にそのリストを返しています。return lUnit
その間に、変数lUnit
にCyUnit
のインスタンスを詰め込んでいきます。
まずCyPlayer
のメソッドfirstUnit()
を呼び出しています。
109. TUPLE firstUnit (BOOL bRev)
引数のBoolは「逆順に調べるか?」を表します。
ここにTrue
を指定すると逆順に調べることになりますが、
ここではfalse
で順に調べています。
このメソッドが返すのは、「最初のユニットのCyUnit
インスタンス」と「イテレータ」の
2つが入った「タプル」です。
「タプル」はリストとすごく似ていますが、1点、
タプルの中身は代入によっては変更できない、ということが異なります。
listA = [1,5,3] # リスト変数
tupleB = (1,5,3) # タプル変数
listA[1] = 2 # 1番目を指定して書き換えができる
# tupleB[1] = 2 # タプルではできない、エラー
print listA
print tupleB
ai, aj, ak = listA # ばらして代入できる
bi, bj, bk = tupleB # タプルでもできる(タプルの中身を変更しているわけではないのでOK)
print "ai = %d, aj = %d, ak = %d" % (ai, aj, ak) # フォーマット文字列の変数指定にタプルが利用できる
print "bi = %d, bj = %d, bk = %d" % (bi, bj, bk) # (bi, bj, bk)は新しく作ったタプルでありもとのタプルとは別物であることに注意
PyPlayer.getUnitList()
の定義に目を戻すと、
(loopUnit, iter) = self.player.firstUnit(false)
とばらして代入しています。
これでloopUnit
は最初のユニットのインスタンス、
iter
は「イテレータ」という謎の存在が入っていることになります。
while文は繰り返しの一種です。
while (条件式):
(ブロック)
で「条件式が真の間」ブロックを何度でも繰り返し実行します。
当然そのままだといつまでも繰り返しが終わらないので(無限ループと呼びます)、
ブロック中で条件式に使っている変数を変更する必要があります。
例えば:
a = 10
while a > 0:
print "a = %d" % a
a = a - 1 # aを1減らす。これがあることでそのうち条件を満たさなくなる
PyPlayer.getUnitList()
の定義では、
while( loopUnit ):
となっています。
変数をそのまま突っ込むことで、loopUnit
が
False
0
""
()
・空のリスト[]
・空のディクショナリ{}
None
のどれかに該当するまでブロックを繰り返すことになります。
条件式において「偽」以外はすべて「真」でしたから、
これらのどれにも該当しない値を取る間は「真」になりますね。
whileブロックの中を見ていきます。
>>>>>>>>>>
while( loopUnit ):
if ( not loopUnit.isDead() ): #is the unit alive and valid?
lUnit.append(loopUnit) #add unit instance to list
(loopUnit, iter) = self.player.nextUnit(iter, false)
<<<<<<<<<<
if( not loopUnit.isDead() ):
とあります。
not
はTrue
とFalse
を逆にするのでした。
「「loopUnit
が致死ダメージを負っているなら」の逆ならば」なので、
「loopUnit
が致死ダメージを負っていないなら」になります。
もしそうならば、lUnit.append(loopUnit)
で
インスタンスloopUnit
をリストlUnit
に追加しています。
ここまで、loopUnit
は最初のユニットのインスタンスでした。
ここで、loopUnit
が次のユニットのインスタンスになってくれたら、
繰り返しによって『もう一度loopUnit
の生死判定→生きてたら追加→次のユニットへ』
をずーっと繰り返していけば生きているユニットを全員リストに追加することができそうです。
その『次のユニットを取得する』をやっているのが、
(loopUnit, iter) = self.player.nextUnit(iter, false)
です。やっていることはつまり「イテレータ」を渡して、(falseは逆順にしないという意味でした)
「次のユニット」と「更新された新しいイテレータ」を受け取るということです。
おおざっぱに、「イテレータ」というのは、
「いま何番目くらいのユニットを数えていたか覚えておいてくれるもの」
という認識でいいでしょう。
次のユニットを取得すると同時にイテレータの持つ位置情報も更新することで
次に呼び出したときにはさらにその次のユニットが取得できるのですね。
最終的に、nextUnit()
はユニットを最後まで数え終わると
次のユニットとしてNone
を返します。loopUnit
の中身がNone
になりますから、
while( loopUnit ):
を満たさなくなってループが終了します。
CyPlayer
のメソッドだけから全ユニットのリストを取得するのは
このようにすこし難しいですが、処理をまとめて提供してくれる便利クラスも
同時に提供されているので、ありがたく使わせてもらいましょう。
パッケージ版の場合。Steam版は C:Program Files (x86)\Steam\SteamApps\common\Sid Meier's Civilization IV Beyond the Sword\Beyond the Sword\Assets\Python\Pyhelpers.py ↩︎