Celowin - パート1: 基本
前置き
この一連のレッスンの目的は、全くの初心者をプログラミングの世界に誘い、モジュールを作成するためにNWNのスクリプトをどのように使うかを教えることです。初めの 部分のレッスンはとても基本的なものになると思われるので、何かのプログラムを書いたことがある人は飛ばしても構わないでしょう。これらのレッスンの最終目的は、プログラムと聞いただけで身震いするような人々でも学べる場を提供することです。
これらのレッスンをフォーラムに掲示したり、印刷したり、修正を加えることは大いに構いません。しかし、その際には私が作成したということをどこかで触れて下さい。良い悪いを含めて、これらのレッスンについて何かコメントがあれば私に送って下さい。Celowin.
これらのレッスンは、オーロラツールセットをある程度触っている人を想定して書かれています。これらのレッスンにおいて解りにくい部分があるという意見が多数寄せられれば、必要な部分をさらに詳しく説明することも考えています。
イントロダクション
ツールセットを起動し、モジュールウィザードを使って新しいモジュールを作成して下さい。モジュール名は「テストモジュール」とします。新しいエリアを作成して下さい。名前は「テストエリア001」とします。サイズやタイルセットは自由に設定して下さい。このレッスンにおいては、さほど重要なことではありません。(配置物を簡単に把握するためにも、縦2、横2程度のサイズをお奨めします。)
「クリーチャーをペイント」ボタンを使って、NPCを配置して下さい。どんな外観でも構いません。そして、以下のことを行って下さい。
- 配置したNPC上で右クリックします。
- 表示されたメニューから「プロパティ」を選択します。
- NPCのタグを「SINGER」に変更します。
- 「スクリプト」タブを選択します。
- すべてのスロットにはデフォルトのスクリプトが表示されています。それぞれのスロットを選択し、スクリプト名を全て削除します。
- 「OnHeartbeat」スロットの側にある「編集」ボタンをクリックします。
- 以下のスクリプトを入力します。(一字一句が正確である必要があります。)
void main() { ClearAllActions(); ActionSpeakString("これは永遠に終わることのない歌です。"); ActionWait(1.5); ActionSpeakString("そう。ずっと歌い続けられるのです。友よ。"); ActionWait(1.5); ActionSpeakString("ある人々が、何の歌かも分からずにこの歌を歌い始めました。"); ActionWait(1.5); ActionSpeakString("そして、これからも彼らはこの歌を永遠に歌い続けるでしょう。ただそれだけの理由で...。"); }
上のスクリプトは、カットアンドペーストせずに実際に入力することをお勧めします。そうすることで、スクリプトを書く方法をより学ぶことができるでしょう。入力ミスをすれば、その一字一句がいかに重要な意味を持っているかを確認することができるでしょう。
- 「名前を付けて保存」をクリックし、「tm_singer_hb」という名前で保存します。
- スクリプト・エディタウィンドウの下の部分をチェックします。次のようになっていれば大丈夫です。0 Errors. 'tm_singer_hb' Compiled successfully. もしそうでなければ、タイプミスをしています。入力したスクリプトを再度チェックして下さい。
- 「終了」をクリックし、スクリプト・エディタを閉じます。
- クリーチャーのプロパティウィンドウの「OK」ボタンをクリックします。
- モジュールを保存して、ツールセットを終了します。
- NWNを起動して、「その他のモジュール」から今作成したモジュールを選択してプレイします。
- NPCがちょっとした歌を歌い続けることが確認できるでしょう。
解説
難しい部分に関しては、行ったことを全てステップ・バイ・ステップで説明したいと思います。みなさんが質問しそうなことを推測して、Q&A方式にしてみようと思います。
- なぜ「テストモジュール」という名前にしたのですか?
実際のところ、特に理由はありません。「その他のジュール」画面で簡単に見つけられるようにしたかっただけです。
- なぜ「テストエリア001」という名前にしたのですか? ツールセットで与えられたデフォルトの名前でも十分だったのでは?
今回のような簡単なモジュールでは、それで問題ないかもしれません。しかし、エクスポートを行う時のためにも、名前を変更する習慣をつけておいた方が良いと思います。もし、あなたが作成したモジュールから1つのエリアをエクスポートして、他のモジュールにインポートしたい場合、そのエリアの名前は特有の名前である必要があります。仮にみんなが 「エリア001」という名前にした場合、エリアのエクスポートとインポートにおいて問題が生じる可能性があるからです。
- なぜNPCのタグをSINGERにしたのですか? なぜ全て大文字なのですか?
いくつかの理由で、タグは全て大文字である必要があります。決してその理由に触れることはないかもしれませんが、そういう習慣をつけておいた方が良いということです。
また、配置したクリーチャーにはシンプルで覚えやすいタグを与えることをお勧めします。SINGER であれば、NPCがどんなNPCであるかは一目瞭然です。そして、もっと複雑なスクリプトでNPCを参照することが容易にできます。
タグは短くし、8文字以内にすることをお勧めします。
- なぜデフォルトのスクリプトを全て削除したのですか? なぜ元々それらが表示されていたのですか?
デフォルトのスクリプトには、まだ扱いたくないNPCの振る舞いがいくつか定義されているからです。例えば、オリジナルのNPCのままだと、最終的にプレイヤーを攻撃する結果になる可能性もあるからです。今回は、NPCにただ歌を歌ってもらいたくて、それ以外に何もさせないために他のスクリプトを削除しました。
後のレッスンにおいて、デフォルトのスクリプトの活用方法について触れるつもりです。
- なぜスクリプトを「OnHeartbeat」スロットに作成したのですか? いずれにしろ、なぜそんなに多くのスロットがあるのですか?
それぞれのスロットは、そこに書かれている特定のスクリプトが呼ばれるタイミングを定義しています。「OnHeartbeat」スロットは、そこに書かれているスクリプトを6秒毎に実行します。それで、NPCが6秒毎に作成した4行の歌を歌い続けるのです。
させたい行為に応じて、それぞれのスロットを使用します。しかし、「OnHeartbeat」スロットに設定するスクリプトは最小限に止めるべきです。なぜなら、多数のスクリプトが6秒毎に実行されると、コンピュータのパフォーマンスに問題が生じる可能性があるからです。
- void main()とは何ですか?
この関数はシンプルで、しかも、ほとんど全てのスクリプトで見ることができるので、多くの人がその存在を忘れがちです。このレッスンにおいて、この関数に関して十分な説明をしようと思いますが、後のレッスンまでに全てを説明できるかどうかは分かりません。
NWNのスクリプトは、「関数」を使って作成されます。関数はプログラムに何をすればいいかを伝えます。いくつかの関数は、あらかじめ作成されており、スクリプトに書くだけで使うことができます。しかし、スクリプトを作成する時には、オリジナルの関数を作成することもあります。このことを自らの関数を「セットアップする」、または、関数のシグネチャを定義すると呼ぶこともあります。
初めにvoidについて説明します。この部分は、関数からどんな種類の「答え」が返されるかを示しています。我々が配置したNPCは歌を歌うだけであり、何らかの「答え」を計算して返すわけではありません。したがって、voidは、その関数が実際に何も答えを返さないということを示しています。
mainは、そこがスクリプトのメイン部分であることを示しています。他の関数をスクリプトに書くこともできますが、スクリプトが実行されるとmain関数から処理が開始されます。
( )の間は、どんな種類の入力情報がスクリプトに与えられるかを示しています。今回のスクリプトには何の入力情報も必要ないので、( )の間は空欄になっています。しかし、入力情報がなくても( )は必ず必要です。
- { }はどういう意味ですか?
{ }は、スクリプト内のセクションをグルーピングするために使われます。今回の場合、書かれた内容は全てmain関数の一部であるということになります。
- なぜmain関数の全ての行が、';'で終わっているのですか?
基本的には、スクリプトにそこが行の終わりであることを伝えるためです。複雑なスクリプトを書くと、大抵1行では済まないでしょう。その場合は、複数の行に分けるのが好ましいと言えます。プログラムは、セミコロンが現れるまでは1行として扱います。
英語におけるピリオドにとても似ていると思われるかもしれません。(ピリオドの代わりにセミコロンが使われている理由は、ピリオドと小数点が混同されることによる混乱を避けるためです。)
- ClearAllActions()は何をするのですか?
これはちょっとややこしいです。なぜなら、今回のケースでは、この関数は実際には何も行わないからです。他の行為を行わないように、保険として書いてあるに過ぎません。
これについて説明しましょう。ClearAllActions()関数以外の他の行は、全てActionから始まる関数です。NPCは、前に行ったアクションが終わったことを確認してから次のアクションをすると思われるでしょう。
しかし、関数は「OnHeartbeat」スロットに割り当てられていて、6秒毎に呼ばれるということを思い出して下さい。NPCには7つのアクションをさせたいのですが、6秒間に4つのアクションしか終わらすことができなかったらどうなるでしょうか? まだ行っていない3つのアクションが残ることになり、そして再び7つのアクションをするように伝えられますので、結果的に10のアクションをしなければならないことになります。NPCは実行可能な4つのアクションを行い、さらに7つのアクションをするように伝えられます。そうすると、13のアクションをしなければならないことになります。NPCには、やるべきアクションが次から次へと残されていきます。結果的に、問題を引き起こすことになるでしょう。
この問題を解決する良い方法がありますが、ここではNPCに「やらなければならないことを全て忘れなさい」と言うのが一番簡単です。このためにClearAllActions()関数があるのです。
もし試したければ、スクリプトを編集して全てのActionWait()関数の中の数値を1.5から3.0に変えてみて下さい。NPCが、全ての歌を歌わないまま再び同じ歌を繰り返すのが確認できるでしょう。
ここまで説明しましたが、少し前に述べた部分にちょっと戻ってみます。ClearAllActions()関数は、main関数のように( )間が空欄ですので、この関数には何の入力情報がないことを示しています。
- main関数の他の行は何を意味しているのですか?
これらは全てNPCに何をすればいいかを伝える指令です。おおよそ見当はつくと思いますが、いずれにしろ少し説明したいと思います。
ActionSpeakString()関数は、NPCにあることを話させます。今までに扱ってきた関数とは違って、この関数は、実際の入力情報を必要とします。それは、NPCが話すテキストです。このテキストは「文字列」と呼ばれており、英語における会話テキストと同様にダブルクォーテーションで囲む必要があります。
ActionWait()関数は、NPCの動きを止め、与えられた数字に等しい秒数だけ何もさせないようにします。よって、ActionWait(1.5)は、NPCにそれぞれのテキストを話す間1.5秒待たせることを意味します。
- なぜスクリプトの名前をtm_singer_hbとしたのですか?
スクリプト名は自由に付けることができますが、覚えやすい名前を付けたいとします。その場合は、「標準化」が鍵になります。
- 「テストモジュール」を表すために、'tm'を使います。よって、テストモジュールで使用される全てのスクリプト名は、この2文字(tm)から始まるようにします。
- 'singer' は、このスクリプトが関連付けられたNPCを表しています。NPCのタグが全て大文字であっても、スクリプト名は全て小文字を用います。(注意:これがオブジェクトのタグを8文字以下にした方が良いと勧めた理由です。さもなければ、スクリプト名がとても長くなるに違いありません。)
- 'hb' は、このスクリプトがNPCの「OnHeartbeat」スロットに設定されているということを表しています。
- このレッスンはここまでですか?
この最初のレッスンだけでは、まだ完全に自分自身のスクリプトを作成できないことは良く分かっています。しかし、一気にやり過ぎるよりは、むしろやり足りなくて情報不足にフラストレーションを感じる位が良いと思っています。2、3日もらえれば、次のレッスンをお披露目することができるでしょう。
author: Celowin, editor: Iskander Merriman, JP team: katsu794
Send comments on this topic.