DSS 2020/09/15 00:00

[ティラノスクリプト]選択肢をキーボード操作で選びたいっ!

Javascript初心者が試行錯誤した結果ですが、ティラノスクリプト使ってる方で参考程度になればお使いください。

概要

ティラノスクリプトは基本の操作がマウスありきになっています。デフォルト設定だとキーボードでの入力は「スペース:メッセージの表示/非表示」、「Enter:次のメッセージに進む」、「Ctrl:メッセージをスキップ」くらいしか設定されていません。
キーボード操作をメインにしてるプレイヤーは選択肢が表示される度にマウスを持って選択肢を選んでクリックする必要があったりと、ゲームを進めるのに無精がしにくいのはプレイヤーとしては面倒だったりします。(主に私が。
スクリプトは少々面倒になりますが、キーボードでできることを増やすと先を急ぎたいプレイヤーとしてはありがたいものです(個人的な感想です。)!
今回はキー操作を強化することを試してみました。

やりたいこと

縦もしくは横に並んでいる選択肢が表示されている状態で上下矢印もしくは左右矢印で選択項目の変更、Enterキーで確定できるようにしたい。
何よりシナリオを書く時点で実装は楽にしたい!(あまり特別なオマジナイはしたくないっ!

前口上

data/system以下のキーコンフィグを使うやり方もあったのですが、今回は直接JavascriptでHTML要素にtabindexを追加して選択肢のフォーカスを切り換える方法にしました。ので、今回はキーコンフィグは使っていません。

あと、事前準備として、選択肢の各要素についてCSS上で:focusと:hoverの設定をできるだけ目立つようにしておいてください。
目立つようにしないと選択肢が選ばれているか視覚的に気付きません。

.selectLoop:focus, .selectLoop:hover {
border: solid 5px #ff9900;
border-radius: .5em;
margin: -5px
}

やり方

ソースは以下の通りです。

function keySet() {
	if($(".selectLoop").length>=1){
		//タブor矢印キーでフォーカス移動の選択肢
		$(".selectLoop").attr("tabindex","0");
		$(".selectLoop").first().addClass("loopFirst");
		$(".selectLoop").last().addClass("loopLast");
		$(".selectLoop").on("keydown", function(e){
			if(e.keyCode === 13) {
				$(this).trigger("click");
			}else if(e.keyCode === 38 || e.keyCode === 37 || (e.keyCode === 9 && e.shiftKey)){
				//↑、←、Shift+tabキーのいずれかの処理
				if($(":focus").hasClass("loopFirst")){
					//最初の選択肢だったら、最後にループ
					event.preventDefault();
					$(".loopLast").focus();
				}else if(e.keyCode === 38 || e.keyCode === 37){
					$(":focus").prev().focus();
				}
			}else if(e.keyCode === 40 || e.keyCode === 39 || (e.keyCode === 9 && !e.shiftKey)) {
				//↓、→、tabキーいずれかの処理
				if($(":focus").hasClass("loopLast")){
					//最後の選択肢だったら、最初にループ
					event.preventDefault();
					$(".loopFirst").focus();
				}else if(e.keyCode === 40 || e.keyCode === 39){
					$(":focus").next().focus();
				}
			}
		});
		$(".selectLoop").on("mouseenter", function() {
			$(this).trigger("focus");
		});
		$(".selectLoop").first().focus();
	}
}

いつも通りですがマクロ化しておくと便利です。

[macro name="keySet"]
	[iscript]
		keySet();
	[endscript]
[endmacro]

このスクリプトのポイントは以下の通りです。

  • キー操作対象は[glink][button]両方で利用できるようにしたい。([link]は使うつもりもないので、対象外にしています。
  • キーの操作対象は画面上のすべての選択肢ではなく、都度設定できるようにしたい。(キーボード操作の対象となる選択肢は物語の進行に関連したものだけにしたいため。Fixed buttonとかは選択肢のループに入ってこないでっ!
  • 画面に選択肢が表示されたら、第一候補が自動的に選択されている状態にしたい。しかも選択の変更は矢印キーで操作できるようにしたい!(え?tabキーでもできちゃうの?なら、それでも同じように動いてほしいわ。

使い方

1) ティラノスクリプトのdata/othersに新しいフォルダを作り、その中に新しいjsファイルを作成する。 例)data/others/original/test.jsなど
2) テキストエディタなどでtest.jsを開き、先述のコードを追記。保存して閉じてください。
3) data/scenario/first.ksに以下のタグを追記。
 必ずタイトル画面など別のksファイルにジャンプする前に実行できるところに記述してください。(ついでに先述のマクロの定義も下記のタグの直下に追加しちゃいましょう。

; 外部JSの読み込み(./data/others/original/test.js)
[loadjs storage='original/test.js']

4) あとはシナリオファイルを記述する中で、[glink][button]を使った選択肢を記述するときにname="selectLoop"をタグ内に追加して、選択肢の記述の直後に先ほど定義したマクロ[keySet]を記述すれば対象の選択肢が自動的にキーボード操作対象になります。

以上です!ラクチンですね。それなりに汎用性あると思うので結構満足。(イベントハンドラで選択肢以外の要素を汚すことないし。

既存の不具合というか未解決課題

残念ながら完全無欠の仕様にはなっていません。

  • マウスで画面の違うところをクリックすると、フォーカスがずれてキー入力を受け付けてくれなくなってしまいます。tabキーを一度押すか、マウスをオーバーラップすると戻ってくるんですけど、この機能ってどうやったらエミュレートできるんだろう?
  • Fixed buttonが混ざるとそれにはフォーカスがうまく移動しません。具体例としては、ティラノスクリプトのダウンロードした時に起動できるサンプルゲームのタイトル画面。「はじめから」「CGモード」「回想モード」はフォーカスしますが、roleで指定してFixed buttonになっている「つづきから」「コンフィグ」にはフォーカスが移動しません。一旦Fixed buttonにならないように通常buttonとして配置して、target先にてrole対象へジャンプする処理をすることで回避できます。

解説

では、一つずつ解説です。
前提として、[glink][button]はどちらもnameパラメータを持っており、ゲーム画面描画の時に対象のhtml要素のclassにnameで設定した文字列が記述されるのがティラノスクリプトの仕様です。なので、「[glink][button]で利用可能にすること」と、「都度対象の選択肢を設定できるようにすること」は、class名を利用して解決しました。
javascriptの処理の方ですが、$(".selectLoop")で、先ほどname="selectLoop"に設定した選択肢すべてを取得しています。

  • Tabキーを押したり矢印キーで選択肢を順番に移動できるようにする機能自体はtabindexの機能を用いています。そのため、対象になる要素にtabindex=0を一律で設定しています。
$(".selectLoop").attr("tabindex","0");
  • そのままでは最初と最後の選択肢でループしないので、そのための事前準備をします。それぞれの要素に最初と最後が分かるように新しいclass名を追加します。
$(".selectLoop").first().addClass("loopFirst");
$(".selectLoop").last().addClass("loopLast");
  • この行以降は各要素にキーが押されたときの動作を登録していきます。
$(".selectLoop").on("keydown", function(e){
  • Enterキーが押されたときにフォーカスされている選択肢がクリックされたと認識させます。
$(".selectLoop").on("keydown", function(e) {
	if(e.keyCode === 13) $(this).trigger("click");
 });
  • ↑、←、Shift+tabキーのいずれかが押されたときの処理を記述します。まず現在フォーカスされている要素を取得して。それが最初の選択肢であれば最後の選択肢にループするようにします。event.preventDefault();はShift+tabキーを押したときに自動で一つ戻ってしまう動作をキャンセルするコマンドです。これをしないと選択肢が一つ飛んでしまいます。次にそれ以外の選択肢だった場合かつ↑、←キーだったときは一つ前の選択肢にフォーカスを移動させます。Shift+tabキーはデフォルトの機能として一つ前の選択肢にフォーカスを移動させてくれます。
			}else if(e.keyCode === 38 || e.keyCode === 37 || (e.keyCode === 9 && e.shiftKey)){
				//↑、←、Shift+tabキーのいずれかの処理
				if($(":focus").hasClass("loopFirst")){
					//最初の選択肢だったら、最後にループ
					event.preventDefault();
					$(".loopLast").focus();
				}else if(e.keyCode === 38 || e.keyCode === 37){
					$(":focus").prev().focus();
				}

続いて}else if(e.keyCode === 40 || e.keyCode === 39 || (e.keyCode === 9 && !e.shiftKey)) {以降は↓、→、tabキー用の処理です。基本的には次の選択肢にフォーカスを移す処理になります。

  • 上記のままではマウスが他の選択肢上に移動してもフォーカスが自動で変わらないので、マウスが他の選択肢上に移動したら、フォーカスも移動させます。
$(".selectLoop").on("mouseenter", function() {
	$(this).trigger("focus");
});
  • そして最後に一番最初の選択肢があらかじめフォーカスされた状態を設定します。
$(".selectLoop").first().focus();

とりあえず現時点では以上です。もしかすると既存の課題が今後クリアにできれば、また追記したいと思います。

記事のタグから探す

月別アーカイブ

限定特典から探す

記事を検索