HSPでゲームを作るときのルーティーンを紹介(実際にブロック崩しを作りながら解説します) Part1

要旨
・stop命令で止まらずに、常にループを回し続けるタイプのゲームの作り方を紹介。
・毎ループ動かすために、どういう処理を書けばよいかを考える必要がある。
・命令群を機能別にまとめるためにgosub命令を使うとよい。

 こんにちは!
 土曜日担当(?)の、ななななななな と申します!

 もうすぐ夏休みですね。つい一ヶ月前に中間考査があったにも関わらず、すぐ目前に期末考査が迫っています。

 期末考査後のクールダウン期間でもあり、勉強の総復習としての期間としても重宝される、夏休みという特別な日々。

 そんな夏休みを目の前にして、この男、ななななななな とかいうふざけた名前で活動していやがるこいつは……

「夏休みの予定は一切ありません!」

 よく聞く例として、友達とプールに行くとか、みんなで友達の家に集まって宿題をやるとか、オンライン通話をしながらネットゲームをやったりするかと思います。
 リアルが充実しているという点である意味”リア充”な生活も、こいつにとっては、”我関せず”!

 …宿題をして、適当に何かアニメ見て終わります………。

 …さてさて、気を取り直して、本題に入りましょう!

 前回は……前置きだけで終わってしまってスンマセンでした。挨拶として書きたいことが山々で……。

 お題は何でしたっけ……? …あっ、そうそう、ブロック崩しでしたね!

 stop命令で止まらずに、常にループをぐるぐる回る系のゲームを作る例として、ブロック崩しを作るという企画でした。

 期末考査前の短い期間ですが、速攻で書き上げてきましたよ!

 それでは、ブロック崩しを作るために、まずはHSPの付属のスクリプトエディタを開いてみましょう。

 HSPを持ってないよって方は、「HSP プログラミング」とでも検索して、エディターをダウンロード&インストールしてくださいな。

 あと、今回のこのシリーズでは、HSPの文法や命令はある程度は知っているものとしますが、いざとなれば付属のエディタ上でF1キーを押すことでリファレンスを出すことができますし、最悪、ググればなんとかなります。一応、解説も困らないくらいには付けようかなとも思っております。

 エディターを開いたら、早速、ゲームプロジェクトとして新しくそれ専用のディレクトリ(フォルダ)を作成して、そこに名前を付けて保存しておきましょう。
 そのフォルダは、マイドキュメントの中でも構いませんし、デスクトップ上に置いてしまっても構いません。ファイル名は適当に、「main.hsp」とでもしておきましょうか。

 なぜ開いた瞬間すぐに保存するのかといいますと、一つは、パソコンがフリーズするなどして、データが吹き飛んだときにも安心することができるからです。

 パソコン上で何か制作をするときに一番怖いのが、これまでの作業が無くなってしまうことなんですよね。

 だから、絵を描く人なら常識かと思いますが、変更を加えるごとに逐一セーブを施すことで、バックアップを取るよりは数段安心感は劣りますが、万が一パソコンからブルースクリーンが表示されたとしても、その直前のセーブからやり直すことができます。
(どうして「絵を描く人なら」知ってるのかって? メディバンペイント公式のメディバンペイント使い方講座にそう書いてあったんだもん)

 まあ、プログラム書いてる間にそんなこと滅多に起こらないんですけどね。あり得るとしたらノーパソの充電が切れるときとか、wait命令ナシで無限ループを書いたスクリプトを実行してしまったときぐらいですかね…。

 あと、もう一つの理由が、新しくフォルダを作って、まっさらのフォルダを見てからスタートすることで、心機一転、制作のモチベーションが上がるからです。
 ……真っ白の紙を見て、今から作るぞ〜、ってなったときに興奮するのって僕だけですかね……。

 とにかく、変更を加える度に保存をかけておく癖を付けておくと、もしものときのために困らなくなります。
 スクリプトエディタの仕様的に、保存しなくてもスクリプトを実行することができるからこそ、身に付けておきたい癖ですね。

1.メイン画面に入る前

 名前を付けて保存ができたら、早速命令を打ち込んでいきましょう。
 最初に書くスクリプトはこうです。

>>
#packopt name "ブロック崩し"
title "ブロック崩し"
;randomize
*thitle
mes "サンプル"
button "はじめる",*prepare
stop
*prepare
cls
mes "準備中"
stop
>>

 (最後に書いたmes命令とstop命令は後で消します。stop命令は、ここで終わりだとわかりやすくするために付けただけですので、実際に模写するときは無くても構いません。HSP3の仕様では、スクリプトの最後まで進んでも、最後にstop命令があるかのようにウィンドウが残り続けます。)

 解説していきます。

 まず、1行目の#packopt命令ですが、この命令の後ろにnameを書いてから半角ダブルクォーテーションで囲んだ文字列を書いておくことで、実行ファイル(HSPのエディタが無くてもソフトとして動作する形式)に変換した際に、この名前で出力されることになります。今回なら「ブロック崩し.exe」ですね。

 実は、特にこれを指定することのメリットがあるわけでもなく、実行ファイルを作成してからそのファイルの名前を変えることで十分対処することができるのですが、後からファイルの名前を変えるのは、個人的にちょっと違和感がありますし、スクリプトの頭にこれから作るゲームの名前を打っておくことで、他のゲームを作っているときに飽きて、このゲームに移ったときに、このゲームの頭に切り替えることができるのです。

 3行目にrandomize命令がコメントアウトされて残っておりますが、これは、デバッグのために乱数を固定するためなのと、リリースするときにrandomize命令を抜かさないようにするためです。
 今回は、ブロック崩しですので、乱数は不要だとは思いますが、もし乱数が出てきたときには、リリース直前くらいにコメントアウトを外せばいいですし、乱数が出てこなかったら出てこなかったで、randomize命令を消せばよろしいかと。

 ラベル*thitleですが、ここは、タイトル画面を記述する場所にしています。

 ちなみに、名前の由来は、本当は名前を*titleにしたいけど、title命令が、HSP標準で既に存在しているので、使うことができない。ならば、ということで「h抜き」ならぬ「h入れ」をした次第です。
 …なんでhを入れたのかは分からん。これを決めたときの俺に聞いてくれ。

 タイトル画面の演出に凝る方もいますが、ぶっちゃけ、それはあくまでも飾りで、大事なのはゲーム本編ですので、始めはそんなに凝らなくても構わないと思います。

 むしろ、「よし、作るかー!」となっている状態でタイトル画面の演出に固執してしまうと、だんだんとゲーム本編を作るモチベーションが失せてくると思います。

 ですから、とりあえず最初はmesbuttonstopで十分です。改造するなら、後から改造しましょう。

 あと、各命令の意味が分からないのであれば、リファレンスを御覧ください。ここでは基本的な命令は解説しません。英単語だって、辞書で実際に引いた方が、その単語のニュアンスがなんとなく分かる気がして、頭に残るかと思います。

 次に、*prepare内で、ゲームのメイン画面に移る前の、変数の宣言や画像の読み込みをしていきます。

 とりあえず、使いそうな変数は今のうちに書いておきましょう。

>>
*prepare
cls
buffer 1:picload "data\\ball.bmp"
/*↑ボールの画像を読み込んでいます。
バックスラッシュが2つ並んでいるのは、HSPの仕様で、バックスラッシュが2つ並んだらバックスラッシュ一つ分として認識するようになっているからです。
バックスラッシュ(もしくは「円記号」)は、広く一般に、一階層下のフォルダですよ〜、と区切るときに使われる文字です。併せて覚えておきましょう。*/

ball_x = 0//ボールの座標。ボールは点ではなく、大きさを持っていますが、今回はボールの左上の座標を表すこととします。
ball_y = 0//ボールの大きさは、今回16px*16pxで作っています。
ball_dx = 0//ボールの1フレーム当たりに移動するx方向の距離。
ball_dy = 0//dxとかいう表記は、数学の微分の単元で出てくると思います。
//微分は読んで字の如く、細かく分けることを意味します。語弊込みで表現すると、1フレーム当たりにどれだけ増えるか、減るかを一般的に考えることです。

bar_x = 120//バーの左端のx座標。
bar_y = 400//バーは基本、左右にしか動かないものが多いので、y座標は定数で構わないと思いますが、y座標の値を変えるときに面倒なので、変数としておいておきましょう。
bar_length = 100//バーの長さ。当たり判定を記述するときに使う。

Block_line = 8//ブロックの左右に並べる数。
Block_row = 6//ブロックの上下に並べる数。
The_number_of_blocks = Block_line * Block_row//大文字から始めることで定数感が出る。※個人の感想です。

dim block_x,The_number_of_blocks//ブロックのx座標、y座標。
dim block_y,The_number_of_blocks//今回、ブロックの座標は配列で管理します。
dim block_exist,The_number_of_blocks
/*block_exist:ブロックが存在しているかどうか。1で存在、0で消失とします。
配列変数は初期化されたばかりの状態では、全て値は0になっているので、下のループで全ての要素に1を代入しています。*/

key = 0//主にキー判定をするときに使う変数として使いますが、一時変数としても使用します。
tmp = 0//経験上、一時変数は2つ用意すれば便利だという結論に至りました。異論は認めます。

//ブロックの座標をセットする。配列の番号は左上から下に行って、6つセットしたら1つ右に動いていくように、下では記述しています。
repeat Block_line
    tmp = cnt
    repeat Block_row
    key = Block_row*tmp+cnt//何番目の変数を操作するかの式は、3回繰り返し書かれるので、一時変数にまとめました。
        block_x(key) = 80*(tmp+1)//横の長さは80pxです。左右に80pxずつ間隔を開けてみましたが、普通のブロック崩しの左右は埋まっているらしいですね。
        block_y(key) = 50 + cnt*40//縦の長さは40pxです。上に50pxの間隔を開けています。ちなみに、下端のy座標は290です。
        block_exist(key) = 1
    loop
loop

screen 0//描画先ウィンドウをID0のウィンドウに戻しつつ、念の為初期化。
//ここでscreen 0か、gsel 0を入れておかないと、上のbuffer命令で描画先が変わっているので、メインウィンドウに描画されなくなります。
*main
stop
>>

 上のスクリプトの意味は、コメント内で記述しています。
 (文章にすると余計に尺が取られることが判りましたので……orz)

 画像についてですが、ボールの画像は、このスクリプトが置かれている場所から下にあるdataフォルダの中に入れています。
 今回はBGMも効果音も入れませんので、dataフォルダ内がすっからかんになると思いますが、今回は一応、一つの例として紹介していますので、dataフォルダ内に一つしか入っていなくてもあまり気にしないで下さい。
 ちなみに、ボールの画像の大きさはコメント内で言及した通り、16px×16pxです。ペイント等で適当に書いておいてください。

2.メインループ内の構造

 では、次に、メインループを*prepareの下に書いていきますが、今回は骨組みだけ書いて終わります。

>>
*main
repeat//筆者はrepeatでループを回す派です。
redraw 0
gosub *key_judge
gosub *culculate
gosub *draw
gosub *isEnd
redraw 1
await 1000/60 //60fps
loop
>>

 *mainの中身ですが、非常に簡潔にまとめています。
 ごちゃごちゃ書くならサブルーチン内でやってくれ、ってことです。

 *mainの解説はこれくらいしか無いですね……。

 そろそろPart1はお開きにしましょう。
 次回から実際に、*main内のループから飛ぶサブルーチンの中身をそれぞれ記述していきたいと思っております。

 それぞれのサブルーチンについて、これからの予定を書いておきます。
 来週の投稿までに自分で書いてみても面白いかもしれません。

 まず最初に*key_judgeでキー入力を受け取ります。
 キー入力によってバーが動きますが、アイデアによっては、特定のキーを押すと、特殊技能を発動させることができるようにしてもいいかもしれませんね。
 バーの移動は、ここですることにします。

 次に、*culculateで計算をします。
 ラベル名が非常にざっくりとしていますが、具体的に、*culculate内では次のような動きをする予定です。

・ボールをdx,dy分移動させる。
・ボールとバーとの当たり判定を行なう。
・ボールとブロックとの当たr(ry →当たっていれば該当するblock_existを0にする。

 *drawでは、その名の通り、描画を行ないます。
 背景やボール、ブロック、バーを描画します。

 *isEndでは、ブロックが全て崩されたかを判定します。今回は、ブロックが全て崩れたらクリアにしましょうか。
 ……あれ? ブロックの残り数を記録しておいた方が楽だな……。あとで変数を作ろうと思いますが、それはまた次回のお話。

 改めて今回の要旨を書いておきますね。

要旨
・stop命令で止まらずに、常にループを回し続けるタイプのゲームの作り方を紹介。
・毎ループ動かすために、どういう処理を書けばよいかを考える必要がある。
・命令群を機能別にまとめるためにgosub命令を使うとよい。

 今回の記事はここまでです。
 やっぱり分量が思ったより多くなるなぁ……。

 文章をまとめる練習でもしようかな。

 それでは、また! HAVE A NICE DAY!
 今日は 2021年7月10日 土曜日! そういえば、今日はうちのお母さんの誕生日だ! 祝わなきゃ!
 というわけで、なななななななでした!

コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です