【第1章】車両で遊ぼう
第7回 パンタグラフを上げ下げしよう
その2 4つのキーでパンタグラフを上げ下げしよう
少し用語を覚えましょう。
【使用するVRMNX命令】編成の命令:GetCar(車両位置)
車両位置は0が1両目になります。戻り値に車両オブジェクトのインスタンスを返します。
編成「TRAIN_11」の車両2両目「CAR-2」で、SetEventKeyDownを使って'keydown'イベントに書き込めばいいなと思った方は今までのことをちゃんと覚えている方ですね。
#CARID=2
import vrmapi
def vrmcarevent_2(obj,ev,param):
if ev == 'init':
dummy = 1
elif ev == 'broadcast':
dummy = 1
elif ev == 'dooropen':
dummy = 1
elif ev == 'doorclose':
dummy = 1
ところが車両には'keydown'イベントがありません。どうしましょう。答えは、
この車両2両目「CAR-2」は編成「TRAIN_11」に所属していますから、編成「TRAIN_11」の'keydown'イベントで車両2両目「CAR-2」を呼び出してあげれば良いのです。
【やってみよう】「親子で学ぼう用レイアウト(2初期状態).vrmnx」を開き、編成「TRAIN_11」にスクリプトをこのように書こう。書くのが大変ならコピペしよう。完成品は「親子で学ぼう用レイアウト(7-2).vrmnx」です。
#OBJID=11
import vrmapi
def vrmevent_11(obj,ev,param):
if ev == 'init':
car = obj.GetCar(1)
car.SetPantograph(0,False)
car.SetPantograph(1,False)
obj.SetEventKeyDown('A',102)
obj.SetEventKeyDown('S',103)
obj.SetEventKeyDown('D',104)
obj.SetEventKeyDown('F',105)
(中略)
elif ev == 'keydown':
car = obj.GetCar(1)
if param['eventUID'] == 102:
car.SetPantograph(0,True)
car.SetPantograph(1,False)
if param['eventUID'] == 103:
car.SetPantograph(0,False)
car.SetPantograph(1,True)
if param['eventUID'] == 104:
car.SetPantograph(0,True)
car.SetPantograph(1,True)
if param['eventUID'] == 105:
car.SetPantograph(0,False)
car.SetPantograph(1,False)
【解説】car = obj.GetCar(1)は、自分に所属する車両の2両目を変数carに車両オブジェクトのインスタンスとして代入しなさいという命令です。さぁここでオブジェクト指向の用語を覚えましょう。
VRMNX専用拡張機能となるvrmapiモジュールには、「こういうものが編成だよ」、「こういうものが車両だよ」ということを色々と定義をしている設計図があります。この設計図を「クラス」と呼びます。そして今回実際に編成「TRAIN_11」の車両2両目「CAR-2」を指定することで「クラス」から実体となる車両の「インスタンス」が生み出されます。これらのモノをオブジェクトと呼び、こういう考え方をするプログラミング言語を「オブジェクト指向プログラミング言語」と呼びます。Pythonは代表的なオブジェクト指向プログラミング言語の1つです。
前回の車両でパンタグラフを操作した時はobj.SetPantographと自分(これもインスタンス)の命令を実行していましたが、今回はcar.SetPantographと自分に所属する車両2両目のインスタンスの命令を実行しています。そして'keydown'イベントに関してはobj.SetEventKeyDownと自分の'keydown'イベントを呼び出しています。この使い分けがポイントです。
さてもう1つ注目したい点は、'init'イベントと'keydown'イベントの両方で「car = obj.GetCar(1)」が書かれているという点です。'init'イベントで1回だけ実行すれば良さそうに思いますが、そうはなりません。'init'イベント内の変数carはローカル変数で'init'イベント内でしか通用しないのです。これをスコープ(有効範囲)と言います。'init'イベントでcarを宣言しても'keydown'イベントではスコープがないのです。ですから同じことを実行しています。これに対して1回宣言するだけでOKな変数もあります。それがグローバル変数です。
完成品は「親子で学ぼう用レイアウト(7-2global).vrmnx」です。
#OBJID=11
import vrmapi
car = vrmapi.LAYOUT().GetCar(2)
def vrmevent_11(obj,ev,param):
if ev == 'init':
car.SetPantograph(0,False)
car.SetPantograph(1,False)
obj.SetEventKeyDown('A',102)
obj.SetEventKeyDown('S',103)
obj.SetEventKeyDown('D',104)
obj.SetEventKeyDown('F',105)
(中略)
elif ev == 'keydown':
if param['eventUID'] == 102:
car.SetPantograph(0,True)
car.SetPantograph(1,False)
if param['eventUID'] == 103:
car.SetPantograph(0,False)
car.SetPantograph(1,True)
if param['eventUID'] == 104:
car.SetPantograph(0,True)
car.SetPantograph(1,True)
if param['eventUID'] == 105:
car.SetPantograph(0,False)
car.SetPantograph(1,False)
これがグローバル変数を使用した場合の書き方です。defより前で宣言しています。よって関数内のobjが使えませんから編成の命令:GetCar(車両位置)は使えません。そこでレイアウトの関数:GetCar(車両ID番号)を使っています。一見するとこちらでも良いように思えますが実際はよろしくありません。
まず1つにレイアウトの関数:GetCar(車両ID番号)を使っているため汎用性がありません。編成の命令:GetCar(車両位置)なら自分の編成内の何両目というのを指定する形ですから分かりやすいですが、レイアウトの関数:GetCar(車両ID番号)レイアウト全体内にある車両の全ての中から固有の番号を指定する必要があるのでイチイチ調べる必要があり面倒です。そしてもう1つがスコープです。グローバル変数がこのオブジェクト内だけで通用するのなら良いのですが、実際はレイアウト全体で通用してしまいます。よって、他のオブジェクトが同じ名前の変数を使っていると混乱してちゃんと動作しなくなってしまいます。ですのでグローバル変数は必要な時以外はなるべく使わないことが推奨されています。ということで、こちらのスクリプトは使わないようにしましょう。
【追記】実はここに入れればいいんですけどね
#OBJID=11
import vrmapi
def vrmevent_11(obj,ev,param):
car = obj.GetCar(1)
if ev == 'init':
ここならvrmevent_11内で通用するのでローカル変数のままいけます。スコープとグローバル変数の話をしたくて強引に書いてしまいましたが、ここでもいけるよということも1つ重要な点なので追記しました。
【実践】それでは「運転」を押してビューワーを起動させましょう。A、S、D、Fキーを押してそれぞれが違う動きをしていれば成功です(^^)/
←戻る 目次 進む→