ブラウザで遊べるゲームを作りたいけれどスマホでバーチャルパッドぐりぐりしながらボタン連打するようなゲームって作れるの?ってか見たことないけどあるの?
って思ってる方けっこういるんじゃないですか?(いや、たぶんあんまりいないだろうな。。)
そんなわけで私が苦労して編み出したスマホブラウザゲーム用バーチャルパッドの作り方を解説したいと思います(ブラウザゲーム用ですが入力部分の考え方はスマホアプリでも使えると思います)。
私の作った仮想コントローラーはこんな感じになってます。スマホの向きが横でも縦でも対応できるようになってます。縦ならゲームボーイ、横ならDSみたいな感じで遊べます。ただ、ここまでやろうと思うとけっこう面倒ですけど(^^;)
で、私はpixi.jsでゲームを作っているんですが、javascriptでゲーム作るならpixi.js以外でもなんでもいけると思います。
ちなみにバーチャルパッド付のゲームはこちらで遊べます↓スマホ向けブラウザゲームでもけっこう本格的なアクションゲームやシューティングゲームを作ることができます。疑ってる方はぜひ一度遊んでみてください(‘ω’)ノ
(※iPhoneの場合は横向き表示の際は先に横向きにしてからゲームを開いてください。これはゲームをiframeで埋め込んでいてiPhoneではリサイズ時にiframeの高さを取得できないため(大きい値になってるらしい)どうしても奇麗に表示することができないことに原因があります。)
目次
バーチャルパッドはcanvasとは別に作る
私はもともとpixi.jsでどうにか作ろうとしてたんですがそれが上手くいきませんでした。一応マルチタッチのやり方を調べてandroidではできたんですがそのやり方がiPhoneでは動かず(マルチタッチ用のプロパティがiPhoneには無いっぽい。iPhoneてこういうの多い(-_-メ))、pixi.jsで作ることを断念しました。
ちなみにpixi.jsでのマルチタッチの記事はこちら ▷▷ pixi.jsでマルチタッチ時のそれぞれの位置の取得について
で、どうしようかと考えてcanvas外で別に作る方法をとることにしました(RPGアツマールみたいなやつ)。この方法はHTMLのDOM要素でコントローラーを作成してjavascriptでタッチイベントを使って操作します。
div要素でコントローラーを作る
コントローラーの下地を作ってそこにボタンを追加する感じでOKです。DIV要素で背景色と境界線をつければボタンぽくなります。
ゲームボーイ風につくるなら幅はcanvasの幅(スタイルの方ね)に合わせます。スマホで全画面表示なら画面の幅サイズでOKです。
あと、スマホ以外では表示しないようにしておくと便利です。
javasciprtで作る例としてはこんな感じです。これはコントローラーの下地です。あ、htmlで先に要素作ってCSSで書いてもいいと思います。
const board = document.createElement('div'); board.style.position = "relative"; board.style.margin = "0 auto"; board.style.zIndex = "100"; board.style.pointerEvents = "none"; document.body.appendChild(board);
pointerEvents = “none”はタッチイベントを発生させなくしてます。下地は触れても何も起こらない存在だし、ゲーム画面の上に表示するようにした場合はこいつが邪魔して下のゲーム画面にタッチできなくなってしまうので無効化しておきます。
各ボタンはこんな感じ。
const btn = document.createElement('div'); btn.style.position = "absolute"; btn.style.pointerEvents = "auto"; btn.style.width = "100px"; btn.style.height = "100px"; btn.style.border = "solid 2px white"; btn.style.backgroundColor = "rgba(50, 50, 50, 0.3)"; board.appendChild(btn); btn.addEventListener("touchstart", (e) => { e.preventDefault(); //タッチ時の処理を書く });
ボタンの大きさは好きなように変えてください。配置位置はabsoluteになってるのでtop・bottom・left・rightで好きな位置におけます。
細かいことはCSSの知識が必要になってくるので面倒だけど調べながらやってください。私のように縦横対応なんてしたらさらにクソ面倒になります(^^;)
タッチイベントではpreventDefault()を入れておくとスクロールとかの余計な処理を止めることができます。
ブラウザのマルチタッチについて
基本的に要素が別であればマルチタッチを気にしなくても勝手に反応してくれます。Aボタン、Bボタンとあればそれぞれのタッチイベントを書くだけで勝手にマルチタッチになってくれます。問題は方向入力です。方向入力ではタッチの座標を取る必要があるのでちょっと扱いが変わってきます。
で、座標を取得できるタッチイベントのリストには以下の3種類があるようです。
- touches: 現在画面上にあるすべての指のリスト
- targetTouches: 現在の DOM 要素上にある指のリスト
- changedTouches: 現在のイベントに関与している指のリスト
(https://www.html5rocks.com/ja/mobile/touch/から引用)
これは画面をタッチした指の数だけ押した順に配列になって入っています。で、この中でどれを使うかと言うとtargetTouchesを使います。方向キーの要素を触ってるタッチだけあればいいからです。このtargetTouches[0]のclientX、clientYで座標を取ります。
ただ、これだと方向キーを複数指でタッチした場合には当然おかしなことになるわけですが、まぁそんなのはそんな馬鹿なことをやった奴が悪いってことでスルーします(^^;)
座標から方向を判断する方法
次は方向入力を判断する方法なんですが、簡単に説明するとtargetTouches[0]のclientX、clientYが原点となる場所からどの位置にあるかによって判断します。とりあえず単純に原点からのclientX、clientYの位置をx、yとします。
で、それを判断しやすくするためにまず絶対値で考えます。すると下図のようなグラフで考えられるようになります。
上の画像を見るとわかるようにx > yなら右、y > xなら上と判断できます(そこにボタンがあるとイメージしてください)。で、これは絶対値で判断していたのであとはxの+-、yの+-で上下左右を判断します。ちなみに、この方法だと2x > yとか2y > xとかの条件で8方向も作れます。
原点の場所
で、肝心の原点なんですがこれは方向ボタンの真ん中にしてもいいんですが、アクションゲームなどかなりぐりぐりするゲームの場合だと指の位置がどんどんズレてきて操作しづらいです。特に8方向入力になるとまともに操作できません。
なので最初にタッチした位置をまず原点にします。で、指を動かしたときある一定以上動かしたらその方向に原点をずらすようにしてやります。そうすると操作してる側からは違和感なく思い通りに操作できると思います。
ただし、これにも欠点があって最初に触れた場所が原点となるのでそこから指を動かさなければ方向を入力できないため「ちょんちょん」って感じの操作ができないんですよね(-_-;)そういう操作の場合は原点を固定しておくやり方が向いてます。この辺はゲームによって変えると良いと思います。
一応私のプログラムの一部を載せておくとこんな感じにやってます(‘ω’)ノ
this.div.addEventListener("touchstart", (e) => {//タッチスタート e.preventDefault(); this.isTouching = true; //タッチした位置を原点にする this.originX = e.targetTouches[0].clientX; this.originY = e.targetTouches[0].clientY; }); this.div.addEventListener("touchmove", (e) => {//マウスダウン e.preventDefault(); if(!this.isTouching) return; dirReset();//からなず一度リセット const posX = e.targetTouches[0].clientX; const posY = e.targetTouches[0].clientY; //原点からの移動量を計算 let vecY = posY - this.originY; let vecX = posX - this.originX; let vec = Math.sqrt(vecX * vecX + vecY * vecY); if(vec < this.emptySpace)return;//移動が少ない時は反応しない(遊び) const rad = Math.atan2(posY - this.originY, posX - this.originX); const sin = Math.sin(rad); const cos = Math.cos(rad); //移動幅が大きいときは中心も移動させる if(vec > this.maxRadius){ this.originX = posX - cos * this.maxRadius; this.originY = posY - sin * this.maxRadius; } const abs_x = Math.abs(this.input.vx); const abs_y = Math.abs(this.input.vy); if(abs_x > abs_y){//xの方が大きい場合左右移動となる if(this.input.vx < 0){//マイナスであれば左 InputManager.input.keys.Left = true; }else{ InputManager.input.keys.Right = true; } if(abs_x <= abs_y * 2){//2yがxより大きい場合斜め入力と判断 if(this.input.vy < 0){//マイナスであれば上 InputManager.input.keys.Up = true; }else{ InputManager.input.keys.Down = true; } } }else{//yの方が大きい場合上下移動となる if(this.input.vy < 0){//マイナスであれば上 InputManager.input.keys.Up = true; }else{ InputManager.input.keys.Down = true; } if(abs_y <= abs_x * 2){//2xがyより大きい場合斜め入力と判断 if(this.input.vx < 0){//マイナスであれば左 InputManager.input.keys.Left = true; }else{ InputManager.input.keys.Right = true; } } } });
以上でござる
以上のような感じで私はバーチャルゲームパッドを作ってます。
プログラムに関しては方向入力の部分だけちょっと面倒ですがボタンはただのタッチイベントなので楽勝だと思います。
たぶんプログラムよりCSSで納得できる配置にする方が時間かかると思います(^^;)
ブラウザゲームのバーチャルパッド・キーボード・コントローラーを一括して操作をするやり方も書いてあるので興味ある方は読んでみてください(‘ω’)ノ
☆pixi.js製簡易ゲームエンジンでブラウザゲームの作り方