本書はTADS製作者用手引きの一部です。
Copyright (C) 1987 - 2000 by Michael J. Roberts. All rights reserved.

編集:N. K. Guytela design


第1章


サンプルゲーム

このセクションではある簡単な実例がどのように動くのかを説明することによって、TADSプログラム作成に関する概念の多くを紹介します。本章はゲームの全体構造を示すためにとても基本的なTADSプログラムから始め、次に言語の特徴をもっと見せるためにそれを拡張していきます。例題は非常に簡単なものですが、完全な、実際に動くTADSプログラムです。

私たちがサンプルゲームから出発するのは、それがTADSプログラムがいかに適合するのかという問題の概観をまとめて提供してくれるからです。言語の初歩を詳説している後のセクションを読む際に、その詳細が一つのゲームの全体構造の中でどこに当たるのか考えることは有用です。本章はその概観を提供する手助けになるはずです。


とても簡単なゲーム

私たちは、部屋が二つ、オブジェクトはないというこの上なく簡単なゲームから始めます(もっと簡単にするために部屋が一つしかないところから始めることもできたのですが、それではゲームを遊ぶ時になにもできません。部屋が二つあれば少なくともその間を移動することができます)。

このゲームを走らせてみたいなら、テキストエディタまたはワードプロセッサを使って下にあるプログラムテキストでファイルを作成してください。TADSコンパイラはASCIIファイルならそれを作ったエディタがなんであろうと読み込みます。

  /* これは注釈、Cにそっくり */ 
  #include <adv.t>     /* read generic adventure game "adv.t" */
  #include <std.t>     /* read standard underpinnings */
  startroom: room            /* the game always starts in "startroom" */
    sdesc = "Outside cave"    /* the "Short DESCription" of the room */
    ldesc = "You're standing in the bright sunlight just
    outside of a large, dark, foreboding cave, which
    lies to the north. "
    north = cave               /* the room called "cave" lies to the north */
  ;
  cave: room
    sdesc = "Cave"
    ldesc = "You're inside a dark and musty cave. Sunlight
    pours in from a passage to the south. "
    south = startroom
  ;

この例題を実行するためにしなくてはならないのは、TADSコンパイラを意味するtcでコンパイルし、それをTADSランタイムシステムを意味するtrで実行することだけです。もしあなたがサンプルゲームにsample.tという名前を付けたのなら、ほとんどのオペレーティングシステム上で次のようにタイプすればゲームをコンパイルできます:

  tc sample 

そして次のようにタイプして実行してください:

  tr sample 

これらの二つの命令― tctr ―は、テキストエディタとオペレーティング・システム・ユーティリティーを実行するために使う命令を別にすれば、あなたがゲーム作成を学ぶ上で必要になる唯一のオペレーティングシステム命令です。

それではサンプルゲームを1行ずつざっと見ていきましょう。

#include命令はあなたのプログラムにもう一つ別のソースファイルを挿入します。adv.tというファイルはほとんどのアドベンチャーに共通する基本的定義のセットです。あなたはたいていのアドベンチャーゲームにこれらの定義をほとんど変更せずに利用できるはずです。adv.tを自分のゲームに含めれば、「the」や動詞の巨大なセット(たとえば「take」「north」など)のような基本的語彙、そして、たくさんあるオブジェクトクラス(これらついてしばらく後にもっと詳しく述べます)のための定義に気を使わなくてすみます。

注意すべき重要な些事―テキストをコピー&ペーストでワードプロセッサやテキストエディタに移すときは、#includeコマンドの前にスペースがまったく入っていないことを絶対に確かめてください。#は行の最初の記号でなければいけません。

std.tを含む行も同じようなもので、それにはいくつかの付加的な標準定義が入っています。これらの定義を別々のファイルに置くのは、それらが通常できあがったゲームにおいて手直しされているからです。あなたは自分のゲームに磨きをかける最終段階で、通常std.tの定義を除去して独自のものを使いたいと思うことでしょう(たとえば、たぶんstd.tinit関数を自分のゲームにふさわしい前置きの言葉を表示してくれる独自のバージョンに入れ替えたいと思うでしょう)。しかし、std.tの定義はゲームにとりかかるには十分優れたものです。

startroom: roomと書いてある行は「startroom」という部屋をこれから定義するつもりだとコンパイラに告げるものです。今や、roomはTADSにとってけっして特別なものではありません。しかし、あなたが導入したadv.troomがなんなのか定義しています。roomは私たちが言及したオブジェクトクラスの一つなのです。次の行はこの部屋に対応するsdescを定義します。sdescとは short description(短い説明)のことで、ある部屋について、普通、プレーヤーがその部屋に入る度に表示されます。ldescはlong descriptionです。普通、プレーヤーがその部屋に初めて入ったときに表示されます、また、プレーヤーが「look」とタイプすることによっても表示されます。最後に、caveという別の部屋へはプレーヤーがstartroomにいる時に「north」とタイプすれば行けることが、northの定義に書かれています。

若干の用語解説:startroomcaveオブジェクトであり、roomというクラスに属しています。そして、sdescldescnorthその他同様のものはそれぞれが属するオブジェクトのプロパティーです。TADSプログラミングの背景として、オブジェクトというのはstartroomのように定義された名前を持つ実在物のことです。各オブジェクトはクラスを持っていて、クラスはそのオブジェクトがどういうふうにふるまうのか、そしてそれがどんな種類のデータを受け入れるのかを定義します。私たちの用法は少し厳密ではなく、プレーヤーの立場から見た「オブジェクト」も、プレーヤーが操作できるゲームに出てくるなんらかのものに言及するため使うことに注意してください。実際、プレーヤーがオブジェクトだと考える各アイテムは本当に一個のTADSオブジェクト(実際は時々複数)によって表現されています。しかし、TADSプログラムは部屋のようにプレーヤーが直接操作しないたくさんのオブジェクトを含むことになります。

もしあなたがプログラミング言語に精通しているなら、上のプログラムが全体的にオブジェクトの定義の姿をしていることに気付いたでしょう。また、プログラムがどこから実行され出すのか不思議に思われたかもしれません。その答はこのプログラムはまさに始まりがない、ということです。

TADSはあなたがこれまでに遭遇したものとは異なるプログラミングのスタイルです。この新しいスタイルは慣れるまで少し時間がかかるかもしれませんが、アドベンチャーゲームを書く上でそれがきわめて強力で作業を非常に簡素にすることにやがて気付くでしょう。ほとんどのプログラミング言語は「手続き型」で、コンピュータが順番に実行する手順の連なりを指定するわけです。一方TADSは、もっと「叙述的」であり、システムに対してオブジェクトを説明することになります。TADSプログラムには普通、手続きに関する部分があり、そこで手順が順番に実行されますが、プログラム全体には初めも終わりもありません。

TADSのプログラムが手続き型ではない理由は、プレーヤーが常にゲームを管理しているということです。ゲームが最初に開始されたとき、システムはプログラム中にある若干の手続的コードを呼び出し、あなたがプレーヤーに見てほしい前置きの文章を表示します。それからシステムはプレーヤーからの命令を待ちます。その命令に基づいて、システムはあなたが定義したオブジェクトをその動作についてあなたが宣言した通りに操作します。あなたはプレーヤーがタイプするところについて心配する必要はありません。あなたのオブジェクトがどのようにふるまうのか、どのように別のものと相互作用するのかを特定するだけでいいのです。


ゲームにアイテムを追加する

それでは、二つの部屋の間を行ったり来たりする以外になにかできるよう、ゲームにプレーヤーが操作できるアイテムをいくつか加えてみましょう。固い金色のどくろとそれを載せる台座を加えることにしましょう。

  pedestal: surface, fixeditem
    sdesc = "pedestal"
    noun = 'pedestal'
    location = cave
  ;
  goldSkull: item
    sdesc = "gold skull"
    noun = 'skull' 'head'
    adjective = 'gold'
    location = pedestal
  ;

ここで私たちは二つのオブジェクト、pedestal(台座)と goldSkull(金色のどくろ)を定義しました。

pedestalsurfacefixeditemという二つのクラスに属します。つまり両方のクラスのアトリビュートを持っているということです。衝突があった場合、クラスのリストの最初にあるsurfaceが優先されます。surfaceクラスのオブジェクトはその上に置かれる他のオブジェクトを持つことができます。fixeditemクラスのオブジェクトは運ぶことができません。注意してほしいのは、surfaceクラスには標準のldescプロパティーがあって、それがその表面上にあるオブジェクトを列挙するということです。

goldSkullは、特別なプロパティーを持たないオブジェクトのための一般的なクラスであるitemクラスに属します。

これらのオブジェクトはプレーヤーが直接操作できるので、プレーヤーはそれらを指示するための言葉が必要です。これはnoun(名詞)およびadjective(形容詞)プロパティーが定義するところです。プレーヤーが操作できるすべてのオブジェクトは少なくとも一つのnounを持っていなければいけません。goldSkullは二つの名詞を持っていることに注意してください。二つの名詞は単に一つの空白を挟んで並べられています。オブジェクトは形容詞を持つこともできます。形容詞は同一の名詞を持つオブジェクトを区別するのに役立ちます。しかし、それ以外では任意のものです。良いゲームはオブジェクトの説明に使うあらゆる言葉を認識するものですから、もしあなたがどくろを「gold skull」と説明するなら、プレーヤーが「take the gold skull」と入れたときにそれを理解すべきです。

これはややこしい話になります。注意してほしいのですが、ldescとその他のプロパティーは文字列に" "を使いますが、nounadjectiveプロパティーは' 'です。ボキャブラリー・ワード(名詞、形容詞、動詞前置詞冠詞)は常にシングル・クオテーションを使います。それ以外のものはほとんどすべてダブル・クオテーションを使います。その違いは、ダブル引用符で挟まれた文字列が評価される度に即座に表示されるのに対して、シングル引用符で挟まれた文字列は内部で操作される文字データであるということです。ダブル引用符で挟まれた文字列は便宜上自動的に表示されます。というのは、テキストアドベンチャー中の文字列は大概それ以上の処理なしに表示されるからです。(ダブル引用符はキーボード上で独立した記号で、単なる二つのシングル引用符ではないので注意してください)

これら二つのオブジェクトはもう一つlocationという別のプロパティーを持っています。これは単に定義の対象となっているオブジェクトを包含しているオブジェクトを明示します。pedestalの場合、それを含んでいるオブジェクトはcaveというroomです。一方、goldSkullpedestalの上にあるので、そのlocationはpedestalです。(このレベルではシステムが他のオブジェクトの内部にあるオブジェクトと他のオブジェクトの上にあるオブジェクトを区別しないことに注意してください。つまり、一つのオブジェクトはsurfacecontainerの両方であることはできないのです)


何かをするアイテムを作る

ゲームはまだかなり甘口です。なにしろ謎がありません。そこで、小さな謎を取り入れてみましょう。金色のどくろはただ放置されているのではなく、そこにそれを置いた何者かが、それを台座から持ち上げると作動する罠を仕掛けていたのだ、ということにしましょう。これを実装するには、goldSkullオブジェクトにメソッドを追加する必要があります。メソッドは実行対象となるコードを含む特種なプロパティーであり、CやPascalにおける関数にとても近いものです。メソッドが加わった新しいgoldSkullは次のようになります:

  goldSkull: item
    sdesc = "gold skull"
    noun = 'skull' 'head'
    adjective = 'gold'
    location = pedestal
    doTake( actor ) =
    {
      "As you lift the skull, a volley of poisonous
      arrows is shot from the walls! You try to dodge
      the arrows, but they take you by surprise!";
      die();
    }
  ;

doTake(「direct objecttake」を表わす)メソッドは 引数としてそのオブジェクトを取ろうとするキャラクターを必要とします(私たちはこのゲームにおいてプレーヤーの外にはまだ登場人物をまったく設けていませんので、actorはプレーヤーのキャラクターになり、Meという名前になるでしょう)。ここで、私たちはそれを単にメッセージを表示し(このメッセージはダブル引用符でくくられているので、条件を満たすと即座に表示されます)、次にdieという特別な関数を呼び出すように定義しました。(dieに続くかっこはdieを関数として呼びたいということをTADSに伝えるための印です)

私たちはdoTakeという名称をただ漠然と選んだのではないことに注意してください。goldSkullオブジェクトの中のdoTakeメソッドは、プレーヤーが「take」命令を直接目的語のgoldSkullを備えてタイプしたときにTADSによって呼び出されます。プレーヤーがタイプする各動詞は、そのオブジェクトまたはコマンドの中で指名されたオブジェクト内の特定のメソッドに対するシステムによる呼び出しを引き起こします。これらのメソッドの名称についてはこのマニュアルの後の方で詳しく説明しています。

どうしてgoldSkullの初めの定義ではdoTakeメソッドが要らなかったんだろうとあなたは思われたかもしれません。あるいはあるオブジェクトについてdoTakeが定義されていない場合、システムは自動的になにをすべきか分かるのだろうと推測したかもしれません。実を言うと、すべてのオブジェクトにdoTakeメソッドが必要で、システムがそれに関する状況を自ずと知ることもありません。ですが、現実にはgoldSkullのような若干の例外を除いて、すべてのオブジェクトが同じdoTakeを持っていますので、ゲーム中のすべてのオブジェクトについてdoTakeメソッドをタイプするのはきわめて退屈な話です。その代わり、私たちは「継承」というものを使用します。goldSkullitemクラスの一員として定義することによって、goldSkullは、それが独自に行う定義に加えて、itemのための定義のすべてを「継承する」旨をTADSに伝えることになります。プログラムの始まりで導入されるadv.tに登場するitemクラスは、doTakeメソッドを定義しているので、itemとして定義されるものはどんなものでもその定義を継承します。しかし、itemgoldSkullの両方で定義されているものがあった場合、この例題中のdoTakeがそれにあたりますが、goldSkullの中の定義を優先し、継承されたメソッドは「却下」することになります。

実のところ、私たちはここでそんなに良い謎は設けていません。なにしろ、死なずに金色のどくろを取る方法がないのですから。そこで、洞穴の床の上に石を一つ置いてみることにしましょう:

  smallRock: item
    sdesc = "small rock"
    noun = 'rock'
    adjective = 'small'
    location = cave
  ;

では、goldSkulldoTakeメソッドを変えましょう。

  doTake( actor ) =
  {
    if ( self.location<>pedestal or        /* am I off the pedestal? */
    smallRock.location=pedestal )           /* or is the rock there? */
      pass doTake;                            /* yes - take as usual */
    else                                  /* no - the trap goes off! */
    {
      "As you lift the skull, a volley
      of poisonous arrows is shot from
      the walls! You try to dodge the
      arrows, but they take you by surprise!";
      die();
    }
  }

この新しいdoTakeは初めにそのオブジェクトが取り上げられているかどうか調べますが(selfという特別なオブジェクトはdoTakeメッセージが最初に送られて来るオブジェクトです)、この場合それは金色のどくろであり、それがすでに台座から離れているかという条件です。条件通りなら、私たちは何も起こしたくないので、doTakeメッセージを送信します。小石が台座にある場合もメッセージを送信します。メッセージを送信すると、私たちが親クラス(この場合item)から継承したdoTakeメソッドが呼び起こされます。こうすれば特別な状況下でのみ、あるメソッドを却下できますし、それ以外の時は通常の処理を図れます。この二つの条件のうちの一つを満たさないと、前と同じく毒矢が一斉に放たれます。

そういうわけで、この罠の解決方法はどくろを取る前に石を台座に置き、それによって台座をごまかし、どくろがまだそこにのっかっているように思わせるというものです。

本章によってTADSのプログラムの姿がどういうものか多少分かっていただけたはずです。私たちは後のセクションで、言語の細部を扱い、adv.tで定義されている全クラスを踏破します。TADSソフトウェアにはもっと複雑なサンプルゲームのソースコードが含まれていることを覚えておいてください。この手引きの残りを読み進める過程で、言語の構文の実例としてそういう見本を参照することが役に立つことを分かっていただけることでしょう。また、サンプルゲームの一部があなた自身が作るゲームのオブジェクトの原型を提供してくれることにもたぶん気づくでしょう。それから、第6章は実現の過程を幾分詳しく述べると同時に、巨大なサンプルゲームの組み立て方を見せてくれます。あなたがもうちょっとTADSに詳しくなったらたぶん第6章を見たいと思うでしょう。

この基本的な見本のゲームの拡張バージョンについては、The Golden Skullを一瞥してください。これはHTML-TADSのデモゲームであり、出発点としてこの章のサンプルコードを使用しています。




実例は常に教訓よりも効き目がある。
SAMUEL JOHNSON, Rasselas (1759)


序文 目次 第2章

楽天モバイル[UNLIMITが今なら1円] ECナビでポインと Yahoo 楽天 LINEがデータ消費ゼロで月額500円〜!


無料ホームページ 無料のクレジットカード 海外格安航空券 解約手数料0円【あしたでんき】 海外旅行保険が無料! 海外ホテル