Webブラウザで簡単ゲームを作ってみよう: 壁打ちテニス編 #6「マウスカーソル操作」

2021年10月12日

Javascript テクノロジー

eyecatch ユーザーインターフェイスに終わりはない、と心の底から思っている、ユゲタです。 前回、Javascriptでラケットの動きをキーボードの左右キーを使って、行いましたが、おそらく実際にはこれではゲームにならないだろうな・・・と思ったので、 今回は、マウスカーソルの動きに合わせてラケットを動かす機能を追加してみたいと思います。 キーボードでもマウスでもラケットを操作できるので、どちらを使ってもらってもいいという、いい感じの操作感になるでしょう。

ソースコード

(function(){ function MAIN(){ this.get_racket(); this.get_ball(); this.set_space(); this.set_event(); } MAIN.prototype.get_tennis = function(){ if(!this.tennis){ this.tennis = document.getElementById("tennis"); } return this.tennis; }; MAIN.prototype.get_racket = function(){ if(!this.racket){ this.racket = document.getElementById("racket"); } return this.racket; }; MAIN.prototype.get_ball = function(){ if(!this.ball){ this.ball = document.getElementById("ball"); } return this.ball; }; MAIN.prototype.set_space = function(){ let tennis = this.get_tennis(); this.space = tennis.getBoundingClientRect(); let border_width_left = document.defaultView.getComputedStyle(tennis,'').getPropertyValue("border-left-width").replace("px",""); let border_width_right = document.defaultView.getComputedStyle(tennis,'').getPropertyValue("border-right-width").replace("px",""); let border_width_top = document.defaultView.getComputedStyle(tennis,'').getPropertyValue("border-top-width").replace("px",""); let border_width_bottom = document.defaultView.getComputedStyle(tennis,'').getPropertyValue("border-bottom-width").replace("px",""); this.space.border_width = { left:Number(border_width_left), right:Number(border_width_right), top:Number(border_width_top), bottom:Number(border_width_bottom) }; }; MAIN.prototype.set_event = function(){ // キーボード操作 window.addEventListener("keydown" , this.keydown.bind(this)); window.addEventListener("keyup" , this.keyup.bind(this)); // マウス操作 window.addEventListener("mousemove" , this.mousemove.bind(this)); }; MAIN.prototype.keydown = function(e){ // 押しっぱなし防止 if(e.repeat === true){return;} this.racket_offset = 0; switch(this.keyType(e.keyCode)){ case "left": // < this.racket_offset = -1; break; case "right": // > this.racket_offset = +1; break; } this.racket_move(); }; MAIN.prototype.keyup = function(e){ if(this.racket_flg){ clearTimeout(this.racket_flg); delete this.racket_flg; } }; MAIN.prototype.racket_move = function(){ let offset = this.racket_offset; if(!offset){return;} let x = this.get_racket_x(); let min = this.space.border_width.left; let max = this.space.width - this.space.border_width.left - this.space.border_width.right - this.racket.offsetWidth; x = x + (offset * 10); if(x < min){x = 0;} else if(x > max){x = max;} this.racket.style.setProperty("left" , x +"px" , ""); this.racket_flg = setTimeout(this.racket_move.bind(this) , 10); if(this.mouse_pos){ delete this.mouse_pos; } }; // lib MAIN.prototype.keyType = function(keycode){ switch(keycode){ case 0: return "¥"; case 8: return "backspace"; case 9: return "tab"; case 13: return "return"; case 16: return "shift"; case 18: return "option"; case 32: return "space"; case 37: return "left"; case 38: return "up"; case 39: return "right"; case 40: return "down"; case 48: return "0"; case 49: return "1"; case 50: return "2"; case 51: return "3"; case 52: return "4"; case 53: return "5"; case 54: return "6"; case 55: return "7"; case 56: return "8"; case 57: return "9"; case 65: return "a"; case 66: return "b"; case 67: return "c"; case 68: return "d"; case 69: return "e"; case 70: return "f"; case 71: return "g"; case 72: return "h"; case 73: return "i"; case 74: return "j"; case 75: return "k"; case 76: return "l"; case 77: return "m"; case 78: return "n"; case 79: return "o"; case 80: return "p"; case 81: return "q"; case 82: return "r"; case 83: return "s"; case 84: return "t"; case 85: return "u"; case 86: return "v"; case 87: return "w"; case 88: return "x"; case 89: return "y"; case 90: return "z"; case 91: return "command"; case 187: return "^"; case 189: return "-"; } return null; }; MAIN.prototype.get_racket_x = function(){ let left = document.defaultView.getComputedStyle(this.racket,'').getPropertyValue("left"); return left ? Number(left.replace("px" , "")) : 0; }; MAIN.prototype.mousemove = function(e){ if(typeof this.mouse_pos === "undefined"){ this.mouse_pos = e.pageX; } let base_offset = -this.space.left; this.racket_mouse_move(e.pageX + base_offset); }; MAIN.prototype.racket_mouse_move = function(x){ let min = this.space.border_width.left; let max = this.space.width - this.space.border_width.left - this.space.border_width.right - this.racket.offsetWidth; if(x < min){x = 0;} else if(x > max){x = max;} this.racket.style.setProperty("left" , x +"px" , ""); this.mouse_pos = x; }; switch(document.readyState){ case "complete" : new MAIN();break; default : window.addEventListener("load" , (function(options){new MAIN()}).bind(this));break; } })();

解説

今回追加したのは、次の3箇所です。
  1. "set_event"関数内に、"mousemove"イベントを追加
  2. "mousemove"関数の追加
  3. "racket_mouse_move"関数の追加
イベントセットは、お決まりの書き方なので、細かな説明は省きます。 そのイベントの無機先として、わかりやすい関数名の"mousemove"をセットしていますが、 ここでは、イベントが発生して、マウスカーソルの座標を、内部変数にセットして、もし前回内部変数があれば、その差分を取得するようにしています。 こうすることで、ピンポイントでマウスが動いた差分だけラケットを動かすようにしています。 そして、実際にラケットを動かすのは、"racket_mouse_move"関数で行っていますが、 キーボード操作の"racket_move"とは、処理が違っているので、別に関数を構築しました。 差分さけラケットを動かして、動いた後の座標を、ラケットの値として、mousemove関数でセットした「マウスカーソルの前回座標」として、上書きしています。 これを実行すると、マウスの動きに合わせてラケットが動くようになります。 キーボードとどちらで動かしてもいいようにしていますが、どちらかに絞った操作にしてもいいかもしれません。 その場合は、いらない方のイベントをコメントアウトすればいいだけですね。

デモ

まとめ

ゲームの操作は、コントローラと言われるゲームパットや、キーボードなどのデバイスの精度によって非常に左右されます。 かつてプレステを作ったソニーの裏話として、任天堂に勝つために、コントローラーに相当な時間とコストを書けて、今でも使われているあのスタイル(形)のコントローラーを作って、 見事、格闘ゲームなどでは標準的と考えられる程になっています。 ゲームの面白さの半分は、このコントロール感にあるといってもいいかもしれません。 使えるデバイスを駆使すると、もっともっと面白いゲーム感が生まれるかもしれませんね。

人気の投稿

このブログを検索

ごあいさつ

このWebサイトは、独自思考で我が道を行くユゲタの少し尖った思考のTechブログです。 毎日興味がどんどん切り替わるので、テーマはマルチになっています。 もしかしたらアイデアに困っている人の助けになるかもしれません。

ブログ アーカイブ