JavaScript is not available.

           

Copyright © 2016-2018 MediaKobo Co., Ltd. All rights reserved.
JavaScript コンソール       ホーム       目次

ウィンドウプログラムの基礎(スカッシュ)

 プログラムの基本は、入力を受け、処理をし、結果を出力することにあります。

baseflow

この基本的な考えは変わりがなのですが、ウィンドウタイプのグラフィックの場合、入力としていくつかのイベントの発生を受ける必要があります。
そのため、受けるイベントの発生に対応して、入力-処理-出力 の組みを用意する必要があります。 下記の例では、マウス移動イベント、タッチ移動イベント、タイマーイベントを受け、それぞれ処理を行います。

eventflow

マウス移動、タッチ移動イベント

 マウス移動イベントとタッチ移動イベントを受け取るようにし、その座標に点を描画します。

マウス移動イベントを受けるために、イベントを受ける関数 fnmousemove を用意し、addEventListener で登録します。
タッチ移動イベントを受けるために、イベントを受ける関数 fntouchmove を用意し、addEventListener で登録します。
fnmousemove 関数、fntouchmove 関数は、ポイントされている座標を計算し、fnmove 関数を呼び、ポイントされている座標に点を描画します。

 一般的に、言語処理系によってイベント受ける関数は、JavaScript みたいに登録するタイプのものや、C++言語系に多く見られる決まった名前の関数を用意するタイプがあります。

 また、描画領域にフォーカスがある場合にイベントを受け、フォーカスが外れた場合にイベントを受けないようにするために、 描画域のオブジェクトの onfocus に addEventListener で登録し、onblur に removeEventListener で登録解除しています。

var canvas = document.getElementById('jsccanvas');
var context = canvas.getContext('2d');
    context.clearRect(0, 0, 640, 400);
var screen = document.getElementById('jscscreen');

function fnmove(x, y) {
  context.beginPath();
  context.strokeStyle='rgb(255, 255, 0)';
  context.moveTo(x, y);
  context.lineTo(x+1, y+1);
  context.stroke();
}

function fnmousemove(event) {
  var screenrect = screen.getBoundingClientRect() ;
  var x = event.pageX - screenrect.left - window.pageXOffset;
  var y = event.pageY - screenrect.top  - window.pageYOffset;
  fnmove(x, y);
}

function fntouchmove(event) {
  event.preventDefault()
  var screenrect = screen.getBoundingClientRect() ;
  var x = event.touches[0].pageX - screenrect.left;
  var y = event.touches[0].pageY - screenrect.top;
  fnmove(x, y);
}

screen.onblur = function(event) {
  screen.removeEventListener('mousemove', fnmousemove);
  screen.removeEventListener('touchmove', fntouchmove);
}

screen.onfocus = function(event) {
  screen.addEventListener('mousemove', fnmousemove);
  screen.addEventListener('touchmove', fntouchmove);
}

screen.addEventListener('mousemove', fnmousemove);
screen.addEventListener('touchmove', fntouchmove);

 登録したイベントを全て削除するには、ブラウザのリロードボタンを押して下さい。

タイマーイベント

 0.01秒毎に発生するタイマーイベントにより、定期的にボールを移動し、描画します。

 ボールの移動、描画は、draw 関数で行っています。

 ボールの移動は、ボールの座標 (bx, by) にボールの速度 (vx, vy) を加えることで行っています。 ボールの移動によって、描画域 (xs, ys)-(xe, ye) から出そうなときは、ボールの速のをプラス-マイナスを反転させています。 速度のプラス-マイナスを反転させるとボールが壁で反射しているように見えます。

 ボールの描画は、前回描画した位置のボールを黒色の円で塗りつぶし、新しいボールの位置で円を描くことで行います。

※注意:タイマーイベントの発生は千回で終了します。 タイマーは、"Cls" ボタンを押すことでも停止します。

var canvas = document.getElementById('jsccanvas');
var context = canvas.getContext('2d');
    context.clearRect(0, 0, 640, 400);

var step = 1;
var xs = 0;
var xe = 640;
var ys = 0;
var ye = 400;
var bx = xs;
var by = ys;
var br = 10;
var vx = 5;
var vy = 5;

function draw() {
  context.strokeStyle = 'rgb(0, 0, 0)';
  context.fillStyle = 'rgb(0, 0, 0)';
  context.beginPath();
  context.arc(bx, by, br+1, 0, 2*Math.PI, true);
  context.fill();

  if(bx < xs || xe < bx) vx = -vx;
  if(by < ys || ye < by) vy = -vy;

  bx += vx;
  by += vy;

  context.fillStyle = 'rgb(255, 255, 0)';
  context.beginPath();
  context.arc(bx, by, br, 0, 2*Math.PI, true);
  context.fill();
}

jscsetintervalid = setInterval(function() {
  if(step >= 1000) {
    clearInterval(jscsetintervalid);
    jscsetintervalid = null;
  }
  ++ step;
  draw();
}, 10);

スカッシュゲーム

 前述した、マウス移動、タッチ移動イベントとタイマーイベントを組み合わせて、スカッシュゲームにします。

マウス移動、タッチ移動イベントでは、イベント発生で得られるポイント座標の x の値を、変数 px に保存します。

タイマーイベントでは、ボールの描画に加え、パドルを描画します。 ボールの描画域内の判定では、パドルのある下辺のみ、パドルのある位置でボールが反射するように変更します。

 この様にすると、如何にもパドルでボールを跳ね返しているように見えます。
かなり単純化して実現したのでゲーム性はあまりありませんが、プログラムも短いので改良も容易だと思います。 例えば、ボールの描画域の xs, xe を 100, 500 等に変更すると、ボールが反射する壁が描画されてないので、難易度が少し上がります。

var canvas = document.getElementById('jsccanvas');
var context = canvas.getContext('2d');
    context.clearRect(0, 0, 640, 400);
var screen = document.getElementById('jscscreen');

var step = 1;
var xs = 0;
var xe = 640;
var ys = 0;
var ye = 400;
var bx = xs;
var by = ys;
var br = 10;
var vx = 4;
var vy = 4;
var ps = 100;
var px = bx;
var ox = px;

function fnmove(x, y) {
  px = x;
}

function fnmousemove(event) {
  var screenrect = screen.getBoundingClientRect() ;
  var x = event.pageX - screenrect.left - window.pageXOffset;
  var y = event.pageY - screenrect.top  - window.pageYOffset;
  fnmove(x, y);
}

function fntouchmove(event) {
  event.preventDefault()
  var screenrect = screen.getBoundingClientRect() ;
  var x = event.touches[0].pageX - screenrect.left;
  var y = event.touches[0].pageY - screenrect.top;
  fnmove(x, y);
}

screen.onblur = function(event) {
  screen.removeEventListener('mousemove', fnmousemove);
  screen.removeEventListener('touchmove', fntouchmove);
}

screen.onfocus = function(event) {
  screen.addEventListener('mousemove', fnmousemove);
  screen.addEventListener('touchmove', fntouchmove);
}

screen.addEventListener('mousemove', fnmousemove);
screen.addEventListener('touchmove', fntouchmove);

function draw() {
  context.strokeStyle = 'rgb(0, 0, 0)';
  context.fillStyle = 'rgb(0, 0, 0)';
  context.beginPath();
  context.arc(bx, by, br+1, 0, 2*Math.PI, true);
  context.fill();

  context.fillRect(ox, ye-br, ps, br);

  ox = px;

  bx += vx;
  by += vy;

  if(bx < xs || xe < bx) vx = -vx;
  if(by < ys) vy = -vy;
  if(ye-br < by && by < ye) {
    if(px < bx && bx < px+ps) vy = -vy;
  }
  if(2*ye < by) by = ys;

  context.fillStyle = 'rgb(255, 255, 0)';
  context.beginPath();
  context.arc(bx, by, br, 0, 2*Math.PI, true);
  context.fill();

  context.fillRect(ox, ye-br, ps, br);
}

jscsetintervalid = setInterval(function() {
  if(step >= 100000) {
    clearInterval(jscsetintervalid);
    jscsetintervalid = null;
  }
  ++ step;
  draw();
}, 10);

※注意: "Cls" ボタンで、ゲームは終了します。

目次へ