search
LoginSignup
7

More than 5 years have passed since last update.

posted at

updated at

HyperCard(HyperTalk)でFlappy Birdを作る

これは、マイナー言語 Advent Calendar 2014の記事であり、拙作記事「Flappy Birdを作る」シリーズ3作目でもあります。かつてのMacintoshに無償同梱されていたHyperCardを使ってFlappy Bird風ゲームを作りながら、スクリプト言語HyperTalkを紹介します。

HyperCard(HyperTalk)とは

HyperCardは"ハイパーテキストを実現した最初の商用ソフトウェア"です(HyperCard(Wikipedia))。HyperTalkはその上で動いたスクリプト言語です。

コンピューターの歴史上におけるHyperCardの存在意義、立ち位置についてはこの記事をどうぞ。実際の使用感について、あえて雑なたとえで説明すると、

PowerPoint + ペイントツール + スクリプト言語(HyperTalk)

といった感じです。PowerPointにおいてのスライドのように、1画面ごとの「カード」の連なりが1ファイル(「スタック」と呼ばれます)を構成しています。カードにはペイントツールで絵を描けますし、文字を表示する「フィールド」やクリックしたら別のカードに飛ばせる「ボタン」を配置することもできます。さらに、HyperTalkを使ってボタンに別のカードに遷移する以外の振る舞いを与えることもできます。

ゲーム開発環境としてのHyperCard

Wikipediaでも紹介されていますが、Cyan社のゲーム『MYST』はHyperCardで作られました。『MYST』は3Dグラフィックで作られた無人島を探索するアドベンチャーゲームです。発表当時の一般的なパソコンではリアルタイム・レンダリング不能な高精細な3D世界を、場面場面の止め絵を遷移させる手法で表現していました。Cyan社は80年代から同様の手法で『The Manhole』、『Cosmic Osmo and the Worlds Beyond the Mackerel』、『Spelunx』といった子供向けのアドベンチャーゲームを発表していました。これら作品群の、白黒ドットパターンを駆使したグラフィック表現には眼を見張るものがあります。(以下の動画だと、『Spelunx』が鮮明でわかりやすいと思います。)

場面遷移によるゲームブック的なアドベンチャーゲームは、HyperCardではスクリプトなしで制作できます。カードに場面場面の絵を描き、クリック可能な箇所に透明なボタンを配置します。ボタンにはほかのカードへ遷移をマウス操作のみで設定できます。

次に、Flappy Birdのような2Dゲームを作ることを考えてみます。アドベンチャーゲームとちがって、飛んだり跳ねたりするキャラクターが必要です。スプライトの代わりのゲームオブジェクトとして、ボタンが使用できます。ボタンには32x32ピクセルのアイコンを設定できます。アイコンはスクリプトで変更可能で、アニメーションさせることができます。スコアなどの表示にはフィールドが利用できます。カードに描いた絵をゲームの背景として利用できます。さらに、複数カード間でボタン・フィールド・背景を共有できる、「バックグラウンド」という概念も存在します。

hypercard_game.png

Flappy Birdを作る

本題に入ります。Flappy Birdゲームの構成の仕方については、拙作記事(Sprite KitでFlappy Birdを作る)をご参考ください。今回はHyperCardなので、鳥の代わりにスタックのアイコンを飛ばせてみます。以下のような仕上がりになりました:

2014-12-19 09_54_36.gif

ソース

ゲーム全体のスクリプトです。右下の「Play」ボタン内に仕込みます。クリックしたらゲームを開始し、ゲームオーバーになったら停止します。

on mouseUp
  -- initialization
  put 0 into vy
  put 1.5 into ay
  put -10 into impact
  put -8 into pillar_vx
  put 0 into count
  put false into mouse_down
  put 16 into interval
  put 5 into pmax
  put 2 into w
  put false into game_over
  repeat with i = 2 to pmax
    hide btn i
    set rect of btn i to 0,0,0,0
  end repeat
  put 0 into card field 1
  set loc of btn "stack" to 100,10

  repeat
    -- pillar
    if game_over is not true then
      if count mod interval is 0 then
        repeat with i = 2 to pmax
          if visible of btn i is false then
            show btn i
            if random(10) > 5 then
              set rect of btn i to 512,160,512+32,288
            else
              set rect of btn i to 512,0,512+32,128
            end if
            exit repeat
          end if
        end repeat
      end if
      repeat with i = 2 to pmax
        if visible of btn i is true then
          if item 1 of loc of btn i < -32 then
            hide btn i
            put card field 1 + 1 into card field 1
            play "boing" "a"
          else
            get loc of btn i
            put item 1 of it + pillar_vx into item 1 of it
            set loc of btn i to it
          end if
        end if
      end repeat
    end if

    -- user input
    if game_over is not true then
      if the mouse is down then
        if mouse_down is not true then
          put true into mouse_down
          put impact into vy
        end if
      else
        put false into mouse_down
      end if
    end if

    -- gravity
    put vy + ay into vy
    get loc of btn "stack"
    put item 2 of it + trunc(vy) into item 2 of it
    set loc of btn "stack" to it

    -- collision
    repeat with i = 1 to pmax
      put left of btn "stack" into l
      put top of btn "stack" into t
      put right of btn "stack" into r
      put bottom of btn "stack" into b
      repeat with j = 1 to 4
        if j is 1 then
          get l&","&t
        else if j is 2 then
          get r&","&t
        else if j is 3 then
          get r&","&b
        else if j is 4 then
          get l&","&b
        end if
        if it is within rect of btn i then
          if game_over then
            if i is 1 then
              exit mouseUp
            end if
          else
            play "boing"
            put true into game_over
            put 0 into vy
          end if
        end if
      end repeat
    end repeat

    wait w
    put (count + 1) mod 360 into count
  end repeat
end mouseUp

舞台・登場人物

今回はカード1枚のみを使用しました。画面下部のパターンで塗ってあるところが地面です。今回は、前進を表現する地面のスクロールは省略しました。その他、以下の部品を用意しました。

名前 種類 番号 役割
- ボタン 1 地面の衝突判定用
- ボタン 2
- ボタン 3
- ボタン 4
- ボタン 5
"Play" ボタン 6 ゲーム開始ボタン
"stack" ボタン 7
- フィールド 1 スコア表示

ボタンやフィールドは、新しく作られたものほど番号が大きくなります。番号が大きいほど前面に表示されるので、鳥は最大番号にしました。地面の衝突判定用の1番のボタンは透明です。2〜5の柱のボタンには土管の絵を割り当てたいですが、32x32ピクセル以上の画像は表示できないので(※)、妥協策としてただの四角いボタンにしました。

※ResEditというユーティリティを用いてスタックにPICTリソースを埋め込み、ボタンに貼り付けて表示する方法も裏ワザ的に存在します。(参考: HyperCardのスピードアップ)

シナリオ

ゲームループの概要をBASIC風に書くと、以下のようになります:

10 初期化
20 ループ開始
30 柱を作る
40 柱を動かす
50 柱が画面外に行ったら+1点
60 マウスクリックされたら鳥を跳ね上げる
70 鳥に重力をかける
80 衝突判定。ぶつかっていたらループを抜ける
90 20に戻る

処理の一部をかいつまんで説明します。

鳥に重力をかける

下記が鳥に重力を与える箇所です。HyperTalkの文の区切りは改行です。

    -- gravity
    put vy + ay into vy
    get loc of btn "stack"
    put item 2 of it + trunc(vy) into item 2 of it
    set loc of btn "stack" to it

1行目

"--"はコメント行を意味します。

2行目

HyperTalkは英語に近い文法と言われています。変数への代入には=演算子ではなく、put〜intoを使います。vyは鳥のY方向速度、ayはY方向重力加速度です。put vy + ay into vyで毎フレームの重力加速度加算をしています。日本語で書くと、vy+ayをvyに入れろといったところでしょうか。

3行目

さらに英語らしい要素として"it"があります。get loc of btn "stack"は何をしているのでしょうか。"loc"はボタンのプロパティである"location"の省略形、"btn"は"button"の省略形で、略さずに書けばget the location of button "stack"となります。"stack"という名前のボタンの位置を得よといったところですが、「得て」どうするのでしょうか。この文は、得た値を暗黙的に"it"という変数に代入します。続く2行で、このitを使ってボタンの変位を行います。

4,5行目

ボタンの位置(location)は、X座標とY座標のカンマ区切りデータで表されます。カンマ区切りデータは「アイテムリスト」と呼ばれ、各要素には"item"キーワードでアクセスできます。3行目で「得た」ボタンの位置情報はX座標,Y座標形式になっているので、item 2 of itでY座標を取り出せます。これにY方向速度を加算してさらにitに入れなおしています。座標は整数である必要があるのでtrunc()でキャストしています。5行目の"set"キーワードで、ボタンの位置を変更しています。

衝突判定

HyperCardには衝突判定に使える"is within"キーワードがあります。これは、2次元の点が矩形領域に入っているかどうかを判定します。ボタンのプロパティ"location"はボタン矩形領域の中央の座標です。鳥の中央の点だけで判定すると、鳥が壁にめり込んでしまってイマイチなので、ボタンの四つ角について判定処理を行います。

collision____.png

障害物(地面、柱)との衝突判定部分部分は以下です。

    repeat with i = 1 to pmax
      put left of btn "stack" into l
      put top of btn "stack" into t
      put right of btn "stack" into r
      put bottom of btn "stack" into b
      repeat with j = 1 to 4
        if j is 1 then
          get l&","&t
        else if j is 2 then
          get r&","&t
        else if j is 3 then
          get r&","&b
        else if j is 4 then
          get l&","&b
        end if
        if it is within rect of btn i then
          if game_over then
            if i is 1 then
              exit mouseUp
            end if
          else
            play "boing"
            put true into game_over
            put 0 into vy
          end if
        end if
      end repeat
    end repeat

あらかじめ鳥のボタンの四つ角の座標を変数に入れておき、repeatループで各障害物との判定を行います。

      put left of btn "stack" into l
      put top of btn "stack" into t
      put right of btn "stack" into r
      put bottom of btn "stack" into b

get l&","&tは、座標のカンマ区切りデータを作って"it"変数に代入しています。衝突を検知したら、"play"で音を鳴らしてゲームオーバー状態にします。"boing"はデフォルトで使用できるサウンドリソースです。第2引数に音階を与えてメロディを鳴らすことも可能です。このゲームでは点数が入る際にも使用しています:

play "boing" "a"

これは「A」の音、「ラ」でボインを鳴らします。

おわりに

やってみたら、わりとさくっとFlappy Birdが作れました。みなさんも、直観的にインタラクティブなコンテンツの創作ができるHyperCardでゲームなりなんなり作ってみましょう!実はまだAppleのサイトでHyperCardが入手できますから、古い実機かエミュレーターで動かすことができますよ!

・・・という話をするつもりでしたが、AppleサイトからHyperCard 2.2Lite-Jのリンクが消えていました。

HyperCard 2.2 Lite-Jをダウンロードしたいのですが・・・
現在でもダウンロードできるのでしょうか?

7 年位前まではダウンロード可能だった様ですが、今ではダウンロード出来なくなっていますね。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
7