ここは、あんすこえむのHSP作品に関するサポートページ兼開発日誌Blogです。
2017/12/01 「Teritorion」がHSPプログラムコンテスト2017「審査員特別賞・kuni賞」を受賞しました。
2017/10/31 クリックゲーム「Teritorion」をHSPコンテスト2017HSPTV部門にエントリーしました。ID#1507
2016/12/01 「Temples Trick」がHSPプログラムコンテスト2016「審査員奨励賞・うすあじ賞」を受賞しました。
2016/10/31 パズルアクションゲーム「Temples Trick」をHSPコンテスト2016HSPTV部門にエントリーしました。ID#1323

続きを読む

賞状到着

出張から帰ったら大きなダンボール箱が1つ。コンテストの賞状、副賞、参加賞でした。
箱の大きさのわけは、Kuni賞副賞。
ドローンが入っていました。

専用コントローラとかは付いていなくて、タブレットにコントロール用のソフトウェアをインストールして動かすようです。
とりあえず、バッテリーの充電からやりますよ。

更新確認

更新版Ver1.25のTV配信が確認できました。


例によって、開発記録をもとに応募までの紆余曲折をメモ。
うーん、またしても最終週ギリギリでの作りこみ。
2017/10/07
 できるだけ操作が簡単(できればマウス操作のみ)という路線で、使えそうなネタを模索。
 隣接した同色パネルを領土としてグループ分割するサンプルコードを弄ってみる。
2017/10/09
 領土の面積計算を付与。
 パネル上のオブジェクトの個数とグループの大きさを比べて何かできないかなー。
 耐久重量以上のオブジェクトを載せると領土全体が水没してしまうとか。
2017/10/19
 ランダムにパネルを張り替えてみる。
 面積計算をもとに極小領域の生成を避けるようにしたら、周囲との地続きが生成されやすくなった。
 張替え時のパターン生成がある程度予想(もしくはギャンブル性のあるコントロール)ができそう。
 初期生成をこれに任せても、程よい繋がり方でパターン生成される。
2017/10/21
 試しに、直下の色と地続きの場所だけ動ける感じで、珠音を矢印キーで歩かせてみた。
 珠音が向いている方のグループを落としたり、載っているグループを張替えするのも面白そうだが、
 張り替えで敵ダメージにするとKacotte!と似すぎるかな。
 とりあえず「別々だった同色領土が、張り替えによって繋がる」を掘り下げてみる。
2017/10/26
 オブジェクトを載せて、複数が同色パネルで繋がったら何かさせることにする。
 当初の「できればマウス操作のみ」に回帰し、パネルをポイント&クリック指定という操作にする。
 パネルの張り替えアニメーション(簡便に回転縮小)とSEを導入。
 面積計算の使い道が極小領域回避のみでは地味→張替え可能なパネルの条件に面積を入れる。
 張り替えに必要な面積を増やすことで、難易度変化ができる。
2017/10/27〜29
 敵が存在しないので、GameOver条件としてクリック残量制とする。
 繋がり要素については「同種オブジェクトの+−が同一グループに載ったら消える」ルールを検証。
 +−のペアの識別は数字で対応づけ。
 ペアの数を増やすことで、難易度変化ができる。
 面構成をランダムにしつつも、ロジカルな思考が必要になるのは良い感じ。
 パネルを消した数がある程度溜まるとクリック残回復、+−消去でクリック残回復ボーナスか。
 →クリック残量制は、先(クリック残が足りるか不足するのか)が見えてしまうので消化不良気味。
  切迫感も不足している気がしたので、タイマー制に変更。
  設定上は、オブジェクトは時限爆弾的なものということにする。GameOver時の爆炎とSEを付与。
  パネルを消した数がある程度溜まるとタイマー残量回復。
  手持ちのテンプレートからTV対応、オープニング、ハイスコア表示の機能を入れる。
 →自動判定でオブジェクトを消すのは、タイムアップ間際での判定で違和感があった。
  (パネル張り替えモーションのせいで間に合わなかった、という感じになることがある)
  自動ではなく、ペアが一続きの同色パネルに載った状態で該当するパネルをクリック、に変更。
  クリックしたグループに載っているかをチェックすれば良いので、コードが減った。
  プレイヤーが+−繋がりの判断をミスしてクリックする可能性もあるが、それもゲームのうち。
 →最終ルール調整。爆弾がペアで増えるので面が進むと探すのが大変。→+−ペアはとりやめ。
  「全爆弾が同一グループに載った状態」での該当パネル張り替えを、面クリア条件にする。
  スピーディな操作で進行するクリックゲームとしては、爆弾全消去・即面クリアの方がわかり易い。
  爆弾個数を増やすのが1個ずつでよくなったので、さらにコード整理。
2017/10/30 V1.00
 爆弾画像はフレーム毎に書き換えるようにして、一部にタイマーゲージと同じ色を付けてみる。
 面クリア時ボーナスとしてタイマー全回復。
 全体把握と判断力が求められて、そこそこの戦略性と運要素。簡便な操作とスピード感もよさそう。
 BGM付与。タイトルはエスペラント後で領土を意味する単語から「Teritorion」に確定。
2017/10/31 V1.10
 難易度調整。コンテスト応募バージョン。

更新版準備

主に不具合改善と、難易度調整。
Teritorionはシンプルなルールと操作が身上なので、ゲーム要素の追加はしていません。


不具合改善:
 ハイスコア欄の文字縁取り調整。アナログRGBモニタだとかなり見難かったので。
 ゲーム開始のClick判定をWindowアクティブ時に限定。
 その他BUG修正。
難易度調整:
 タイマー減少が初期値(LV1)の1.5倍速になるポイントを、LV50からLV75付近に変更。
 固定だったタイマー小回復量をLVで増加させる。(LV50付近でLV1の1.5倍)
 クリア時、爆弾を落とす際に張り替えた色について、ゲージをMAXにする。
 (タイマー全快によって小回復が無駄になっていたので、即時小回復できるように。)
その他:
 爆弾落下モーションに回転を付与。爆破モーションを若干変更。


で、併せていろいろスリム化対応も行った結果、HSP3.5でのAXサイズが4599バイトになりました。
3.4だと更に減って4565バイトとなり、応募時からぴったり300バイト削減。

スリム化

次回の作品更新に向けて準備。
まずはスリム化。
今回はAXサイズの余裕がありサイズ削減を突き詰める必要はないのですが、ある程度やらないとつまらないので。


>この処理に入る前に、mapgとmapcはオール0クリアしています。
フレーム毎にそれぞれクリア(memset)をやっていたのですが、AXサイズ的に無駄に感じていました。
これを、mapcを倍のサイズにして、パネル面積カウンタとグループマップを連結してしまうことで圧縮。
グループマップ要素には常にdupしてからアクセスしており、オフセットしても8バイト増で済むのでこちらを後半。
面積の方は加算処理でどうしても配列参照しなければならないため、オフセット不要な前半という扱いにしました。
これにより、memsetとdimを一組削除して28バイト減らせたので、差し引き20バイトの削減。


グループ判定&面積計算のロジックにも手を入れます。

sffq=eep	; キュー先頭に開始位置の値を登録
repeat
	if( cnt>eeq ){ break }	; キューの末尾まで到達済みなら調査終了
	c0=sffq(cnt)
	if((mapf(c0)!=emapf)|mapg(c0)|mapd(c0)<grpchklim){}else{
		mapg(c0)=sffq; 該当セルのmapgにグループ番号(開始位置)をマークして調査済にする
		mapc(sffq)+	;	グループのパネル数をカウントアップ
		repeat 4	;上下左右のセルに範囲拡張する。※Size優先で無条件に拡張
			eeq+:sffq(eeq)=c0+du(cnt)	; 対象とする位置を計算して登録
		loop 
	}
loop

配列duに定義された上下左右を示す変化量を使っていますが、今回は縦横移動がなくdu利用はここだけでした。
duへの初期代入とrepeat 4などをやめることができれば、それなりにサイズが減るはずです。
また、ifの中でマップの配列参照は、他でも使っているdup後の変数が利用できればサイズが相当減ります。
というわけで以下のようになりました。

lpoke sffp	; キュー書き込み用ポインタ初期化
repeat	; グループ判別処理
	if( cnt>sffp ){ break }	; キューの末尾まで到達済みなら調査終了
	gosub *posclceg	; 検索位置がeepにあることを利用しマップ値dup(条件判定での配列参照を軽減)
	if(((mapf(eeq)=emapf)*(emapf>=0)*(emapd>=eeo))>emapg){	; 検索位置の条件を判定
		emapg=eeq	; 該当セルのmapgにグループ番号(開始位置)をマークして調査済にする
		mapc(eeq)+	;	グループのパネル数をカウントアップ
		sffq(sffp)=eep-1,eep+1,eep-MAPW,eep+MAPW;上下左右に範囲拡張。※Size優先で無条件に行う
		sffp+4	; キュー書き込み用ポインタを進める。
	}
	eep=sffq(cnt)	;次ループに備えてキューからeepへ探索先を代入
loop

この処理を呼ぶ際はパラメータ定義として、eeo=パネル状態閾値,開始位置,開始位置 を行っておきます。
eep,eeqはそれぞれeeo.1,eeo.2に相当します。
では頭から解説。

lpoke sffp	; キュー書き込み用ポインタ初期化

現在の検索位置をeep、開始位置をeeqとして使用するので、キュー書き込み用ポインタは専用の変数sffpで管理。

	gosub *posclceg	; 検索位置がeepにあることを利用しマップ値dup(条件判定での配列参照を軽減)

各マップ要素をdupするサブルーチンを呼びます。posclcegは以下のようになっています。

*posclceg
	pos eex,eey
	dup emapg,mapc(eep+EMAPNUM)	; 現在位置のグループマップ
	dup emapf,mapf(eep)	; 現在位置のフロアマップ
	dup emapd,mapd(eep)	; 現在位置の状態マップ
	dup emapc,mapc(emapg)	; 現パネルが属するグループのパネル数(参照のみ)
	return

posなどグループ判定には不要なものありますが、別の処理の兼ね合いで必要な準備処理としてまとめています。
EMAPNUMはmapcの後半をグループマップとして使用するためのオフセット。

	if(((mapf(eeq)=emapf)*(emapf>=0)*(emapd>=eeo))>emapg){	; 検索位置の条件を判定

現在の検索場所が壁なら処理をしないように、emapf>=0をここに追加しています。
これによって、呼び出し側でやっていた開始位置が壁の場合を除外するためのifを廃止できました。
また、修正前にはelseで処理をしていましたが、emapgの評価方法を工夫したことで、真の場合の処理にしています。

		emapg=eeq	; 該当セルのmapgにグループ番号(開始位置)をマークして調査済にする
		mapc(eeq)+	;	グループのパネル数をカウントアップ

初期位置はeeqで示されているので、そのまま使います。

		sffq(sffp)=eep-1,eep+1,eep-MAPW,eep+MAPW;上下左右に範囲拡張。※Size優先で無条件に行う
		sffp+4	; キュー書き込み用ポインタを進める。

上下左右の4箇所分を一度に登録してからポインタに4を加算。これによってduが廃止可能になりました。

	eep=sffq(cnt)	;次ループに備えてキューからeepへ探索先を代入

eepはループ1回目は処理前に登録された開始位置、2回目以降はキューから取り出した位置情報を順次使います。
これらの対応を行うことで、50バイト近く削減できました。

蛇足事項追記2

・色別ゲージ
パネルを張り替えると5本のパネル色に応じたゲージが貯まり、それぞれMaxになる毎にタイマー回復します。
2,3本うまくギリギリまで貯めてから面クリアしておけば、次LVでのリカバリがしやすくなります。
上位LVに進むほど戦略的に余裕をもたせるべきですが、あせってしまうとこれがなかなか難しい…。


・LVアップ時の難易度変化
わかりやすい変化として、6LVまたは7LVあがる毎に、爆弾が1つ増えます。
また、20で割り切れるLVへ達する毎に、消せない狭いパネルの上限面積が3枚→4枚→5枚と増えていきます。
(このとき爆弾の出現数を一時的に減らしています。)
タイマーゲージの減り方も、LVがあがる毎に僅かに速くなっています。


・パネル出現
パネル状態値マップの配列をゲーム開始時に0クリアしており、すべてのパネルが一斉に出現します。
状態値=0が色決めタイミング、1→32までフレーム毎にカウントアップされ32で生成済み。
生成済みの色つきパネルのみが張り替え対象に選択可能です。
ゲームエリアの外周のグレー部分もパネルになっています。(フロアマップ値=-1)
連結判別からは除外しているため、グループ番号は0固定で面積も0枚扱いとなります。
しかし、パネル生成/描画のロジックは色付パネルと同じなので、ゲーム開始時には出現モーションが発生します。

領域判別と面積計算

連結領域の判別や面積の計算をどのように処理しているかについて解説。
マップ上の任意の位置を起点に、連結している同色パネルに同一グループ番号を割り当ててパネル数も調べます。
具体的なコードは以下の通り。

sffq=eep	; キュー先頭に開始位置の値を登録
repeat
	if( cnt>eeq ){ break }	; キューの末尾まで到達済みなら調査終了
	c0=sffq(cnt)
	if((mapf(c0)!=emapf)|mapg(c0)|mapd(c0)<grpchklim){}else{
		mapg(c0)=sffq; 該当セルのmapgにグループ番号(開始位置)をマークして調査済にする
		mapc(sffq)+	;	グループのパネル数をカウントアップ
		repeat 4	;上下左右のセルに範囲拡張する。※Size優先で無条件に拡張
			eeq+:sffq(eeq)=c0+du(cnt)	; 対象とする位置を計算して登録
		loop 
	}
loop

マップ配列は例によって一次元で持っています。
それぞれの役割は、
・mapf →壁、パネル各色の区別を保持
・mapg →連結領域のグループ番号を格納。同じ番号なら連結された同一グループの一員。
・mapd →個々のパネルの状態 -32〜-1:消失中、0:床色登録タイミング、1〜31:生成中、32:生成済
・mapc →グループのパネル数(面積)。要素番号がグループ番号である配列要素だけ値を保持。
この処理に入る前に、mapgとmapcはオール0クリアしています。
調査対象の位置情報は、配列sffqでキューとして管理しています。
キューのポインタeeqは、eep.1相当としてDUPされており、この処理に入る前にeep=開始位置,0 で開始位置指定と同時にクリアしています。
ちなみに、面積検査のときは左上から右下の各パネル番号を順次開始位置として処理し、張替え範囲探索の場合はクリックしたパネルを開始位置として処理します。
では各コードの詳細について。

sffq=eep	; キュー先頭に開始位置の値を登録

ループ内では、sffqから調査対象を取り出して処理していますので、調査開始位置をキューの先頭に入れておきます。

if( cnt>eeq ){ break }	; キューの末尾まで到達済みなら調査終了

無限ループの体裁になっているので、調査対象の残りが無くなったことをもってBreakします。

c0=sffq(cnt)

キューから調査対処の位置情報を取り出します。

if((mapf(c0)!=emapf)|mapg(c0)|mapd(c0)<grpchklim){}else{<処理>}

調査対処の各種マップ値を判定し、
 調査開始位置の色(emapfに代入済)と異なる色、グループ値が0以外、パネル状態が閾値grpchklim未満、
のどれにも当てはまらない場合に<処理>を実行します。
素直に書くなら

		if((mapf(c0)==emapf)&(mapg(c0)==0)&(mapd(c0)>=grpchklim)){<処理>}

ですが、先のようにすると2バイト削減になります。(今回は圧縮はほどほどでよいのですが、習性には抗えず…)
grpchklimは、パネル生成やり直しや狭小領域の判定では-100を使い、mapdのとりうる値によらず連結対象としますが、クリックした張り替え範囲を判別する際は、32を使って生成済のパネルのみを連結対象としています。
(クリック時に生成済以外のパネルを含めると、生成中のパネルを跨ぐような張り替えが起きて違和感があるので)

mapg(c0)=sffq; 該当セルのmapgにグループ番号(開始位置)をマークして調査済にする

グループ番号には、調査開始位置の値を使います。
それを該当パネルのグループマップに設定することで、所属グループ確定&調査済にしています。
(これにより、マップクリアするまでは、グループ番号が確定済のパネルを開始位置とした判別処理は、素通りになります。)

mapc(sffq)+	;	グループのパネル数をカウントアップ

パネル数は、調査開始位置(グループ番号が示す)の要素にのみ格納しています。

repeat 4	;上下左右のセルに範囲拡張する。※Size優先で無条件に拡張
	eeq+:sffq(eeq)=c0+du(cnt)	; 対象とする位置を計算して登録
loop 

調査対象のパネルの周囲(上下左右)4カ所について、調査対象とするようにキューに追加登録します。
調査済のところも含まれることがありますが、上のifで除外されるので構わず登録。


壁以外のすべてのパネルを起点としてこの判別処理を繰り返していくと、パネルの領域判別と面積計算が出来上がります。