時代はやっぱスマホっしょ!を実感する、ユゲタです。
今どきのブラウザゲームは、キーボード操作とマウス操作に加えて、スマホ用のタッチ操作も同時に実装しないといけません。
スマホアプリであれば、マウス操作などは意識しなくてもいいのですが、ブラウザは、今やどの端末にも備わっているオールマイティインターフェイスを意識する必要があります。
そんなわけで、今回の壁打ちテニスでも、スマホ操作を実装しておきます。
ラケットの操作をタッチで動かすのは、これまでやってきたマウス操作とキーボード操作を組み合わせるだけで、比較的簡単にできるので、
コツさえ掴んでおけば、色々な操作方法の応用がきくとおもいます。
ソースコード
(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));
// スマホ操作
window.addEventListener("touchstart" , this.touchstart.bind(this));
window.addEventListener("touchmove" , this.touchmove.bind(this));
window.addEventListener("touchend" , this.touchend.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('ontouchstart' in window === true){return;}
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;
};
MAIN.prototype.touchstart = function(e){
if('ontouchstart' in window === false){return;}
if(!e.touches || !e.touches.length){return;}
this.touch_pos = e.touches[0].pageX;
};
MAIN.prototype.touchmove = function(e){
if(typeof this.touch_pos === "undefined"){return}
let touch_move_pos = e.touches[0].pageX;
let diff = touch_move_pos - this.touch_pos;
let x = this.get_racket_x();
this.racket_mouse_move(x + diff);
this.touch_pos = touch_move_pos;
};
MAIN.prototype.touchend = function(e){
if(typeof this.touch_pos !== "undefined"){
delete this.touch_pos;
}
};
switch(document.readyState){
case "complete" : new MAIN();break;
default : window.addEventListener("load" , (function(options){new MAIN()}).bind(this));break;
}
})();
デモ
解説
今回追加した内容は、イベント設定で"touchstart"、"touchmove"、"touchend"の3種類と、それぞれを実行する関数です。
これまで、キーボード操作では、"keydown"と"keyup"、マウス操作では、"mousemove"とやってきましたが、
タッチ操作は、それらを組み合わせたような処理になります。
まず、touchstartは、画面に指が触れたタイミングのイベントで、keydownと同じようなイベントだと考えてください。
この時に、タッチした座標を記憶しておき、touchendで、画面から指が離れたタイミングで、記憶しておいた変数を開放(削除)しています。
touchmoveは、tauchdownとtouchendの間に指を動かしたイベントになるので、この動かした座標位置を、ラケットの座標に変換しています。
ただ、少しややこしいのが、touchdownのタイミングで、何故かmousemoveイベントが発動してしまう時があり、ラケットが思わぬ動きをしてしまうことがあるので、
それぞれのイベントで、タッチイベントが使える状態かどうかを判定して、必ずどちらかの処理しか行わないようにするために、mousemove関数と、touchstart関数に、次の処理をいれています。
MAIN.prototype.mousemove = function(e){
if('ontouchstart' in window === true){return;}
...
}
MAIN.prototype.touchstart = function(e){
if('ontouchstart' in window === false){return;}
...
}
それぞれに、同じような判定を入れてい対応しているのですが、これは、windowというブラウザのイベント(property)の中に、"ontouchstart"イベントが存在しているかを確認できる判定処理です。
連想配列で同じ処理が使えるので、覚えておくと、コーディングが少し便利になりますよ。
ただし、この処理には一つだけ注意点があって、GoogleChromeブラウザのように、スマホ画面と、PC画面をデバッグモードとして切り替えられるような場合に、
この判定はページを読み込んだ時の判定になるので、読み込んだ後、pcモードからスマホモードにしても、タッチ操作はできなくなってしまいます。
一般的ではないので、これに対応する必要はないのですが、デバッグなどで、モードを切り替えた場合には、一度ブラウザをリロードするという事をルール付けておけばいいでしょう。
まとめ
とりあえず、今回までの処理で、操作をする処理は完了になります。
これだけでも、なんとなく、ゲームが出来上がったイメージができたんではないでしょうか?
操作感って大事ですよね。
そして次は、ボールを動かしてみたいと思います。
一気にゲーム感が増しますよ。
お楽しみに!!!
0 件のコメント:
コメントを投稿