fc2ブログ

キーを押した長さに応じてジャンプの高さを変える

今日は、キーを軽く押したら少しジャンプして、キーを押し続けると高くジャンプする処理を実装した。
HTML5 Canvas で作ったものをこちらに公開してます → スペースキーを押し続けると高くジャンプする
sキーを押してスタートしてください。

ボタンを押した長さに応じてジャンプの高さを変える方法をネットでちょっと調べてみたが、見つけられなかったので自己流で実装した。

1. ジャンプの仕様
キーを離したときにジャンプすれば簡単なのだが、それだとゲームになりにくいので
「キーを押した時点でジャンプ開始し、ジャンプ中に高さが変わる」仕様とする。

ジャンプの高さの仕様は次のようにした。
・ジャンプの高さは5段階。キーを押し続けて3、5、7、9フレーム目で高さが変わる。それ以上押し続けても変わらない。
(今回作ったプログラムは12fpsなので、2フレームごとに変わるようにした)
・ジャンプの高さは段階にほぼ正比例する。

2. ジャンプ中のy座標計算方法
ジャンプ中のy座標はVerlet法で求める。その式に値を加算してジャンプの高さを変えることにした。
Verlet法については ジャンプしたときの座標をVerlet法で求める を参照。
その部分の処理は以下のとおり。
  // this.charObj.y : 現在のy座標
  // this.charObj.y0 : 前フレームでのy座標
  // this.gh2 : 物体にかかる重力に関係する値
  // this.jumpval : キーを押した長さに応じて加算される値
  this.charObj.y += this.charObj.y - this.charObj.y0 + this.gh2 + this.jumpval;

y座標を求める前に this.jumpval を求める必要がある。
this.jumpval は毎フレームで加算するのではなく、高さを変えるときだけ加算しそれ以外では this.jumpval = 0 とした。
また、高さを変えるときに加算する値は、以下のように配列 this.jumpvalArr で持つことにした。
  // ジャンプ力加算値の配列
  // 3、5、7、9フレーム目で加える this.jumpval が格納されている。
  this.jumpvalArr = [-5, -4, -4, -3.5];
こうすることで、this.jumpvalArr 要素の値を変えるだけでジャンプの高さを調整することができる。

以上を盛り込んだ、ジャンプ中のy座標を求める処理は以下のとおり。
この処理がフレームごとに実行される。
  /* ジャンプ中のy座標を求める処理 */
  this.jump = function(){
    // ジャンプ力を加算するか判定し、加算値を求める
    // this.jumpval : y座標を求める際に加算される値。上記のthis.jumpvalと同じ
    this.jumpval = 0;
    if(this.addjump){  // SPACEキーが押され続けている状態のとき this.addjump = true
      // SPACEキーが押され続けている状態の場合、this.jumpAddSepフレームごとにジャンプ力を加算
      // this.addjumpCnt : SPACEキーが押され続けて何フレーム経過したか
      // this.jumpAddSep : 何フレームごとに this.jumpval を加算するか。今回は2
      // this.jumpadded : 何回 this.jumpval が加算されたか
      // this.jumpvalArr[] : this.jumpval の値を格納した配列
      // this.showJumpMsg() : ジャンプ力を表示
      if((this.addjumpCnt % this.jumpAddSep == 0) && (this.addjumpCnt > 0)){
        // ジャンプ力加算値を求める
        this.jumpadded++;
        if(this.jumpadded >= this.jumpvalArr.length){
          // 最後の加算。これ以上SPACEキーを押し続けても加算しない
          this.jumpval = this.jumpvalArr[this.jumpvalArr.length - 1];
          this.addjump = false;
        }else{
          this.jumpval = this.jumpvalArr[this.jumpadded - 1]
        }
        this.showJumpMsg(this.jumpadded);
      }
      this.addjumpCnt++;
    }
  
    // キャラクターの座標をVerlet法で算出
    // 詳細な説明は ジャンプしたときの座標をVerlet法で求める を参照
    // this.charObj.y : 現在のy座標
    // this.charObj.y0 : 前フレームでのy座標
    // this.gh2 : 物体にかかる重力に関係する値
    var backy = this.charObj.y;  // 現在のy座標を待避
    if(this.charObj.y == this.charObj.y0){
      // 初速度から1フレーム目のy座標を算出。詳細は略
      this.charObj.y = this.G / (2 * Math.pow(fps,2)) + this.v0 / fps + this.charY0;
    }else{
      this.charObj.y += this.charObj.y - this.charObj.y0 + this.gh2 + this.jumpval;
    }
    this.charObj.y0 = backy;
    
    // this.charY0 : y座標初期値
    if(this.charObj.y >= this.charY0){
      // ジャンプ終了
      this.charObj.y = this.charY0;
      this.jumping = false;  // ジャンプ中フラグをOFF
      this.showJumpMsg(-1);
    }
  }

以上。
this.jumpvalArr の値を調整するのがちょっと大変だった。
重力加速度にこだわらなければもっとシンプルになるのだろうか。

せっかくなので作ったページのソースを載せておく。jQuery は Ver1.4.2 を使用。
html5game2.html  canvasgame2.js
スポンサーサイト



ジャンプしたときの座標をVerlet法で求める

ジャンル別ゲームの作り方とアルゴリズムまとめで紹介されていた
マリオのジャンプ実装法とVerlet積分を読んだが、マリオのジャンプ実装法とVerlet積分(実践編)を読んでもVerlet法がよくわからなかった。

Verlet法について調べたところ、みその計算物理学 というすごいサイトを見つけた。
ここの Verlet法(PDF形式) を読んでVerlet法を理解できたので、ジャンプしたときの座標を求める方法について書く。

1.座標の計算方法
時刻 t における物体の座標を r(t)、物体に作用する力を f(t)、物体の質量を m とする。
時間の間隔を h とすると、次の時間の座標 r(t+h) は、現在の座標 r(t) と 前の座標 r(t-h) を用いて
  Verlet法の式1
と表すことができる。(近似式だが、等式と考えて実用上問題無い)
物体にかかる力が重力のみの場合、重力加速度を g とすると f(t)=mg のため、
  Verlet法の式2
となる。
gh^2は定数のため、初期値r(0)と1フレーム目の値r(h)が与えられれば、加減算とループのみで座標を計算して軌道を描くことができる。

自由落下の場合、t=0 のときの速度を v0 とすると
  Verlet法の式3
となるので、これからr(h)を求めることができる。
hが十分に小さければ r(h)=v0h+r(0) でも実用上問題無いが、「前回の値から次回の値を求める」というVerlet法の性質から、r(h)の精度が描かれる軌道の精度に大きく影響するので注意が必要。

2.サンプルプログラム
y軸方向にボールをジャンプさせてボールが弾むプログラムを HTML5 Canvas で作成した。
y座標を求める部分
  this.charObj.y += this.charObj.y - this.charObj.y0 + gh2;
でVerlet法を用いている。

ソースコード全文は以下のとおり。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Verlet法で計算した、ボールが弾むアニメーション</title>
<!--[if lt IE 9]>
<script type="text/javascript" src="excanvas.js" charset="UTF-8"></script>
<![endif]-->
<script type="text/javascript">
<!--
var fps = 24;  // 1秒あたりのフレーム数
var MpPX = 10;  // 1メートルを何ピクセルとするか
var ga = 9.8 * this.MpPX;  // 重力加速度
var gh2 = ga * Math.pow((1/fps),2);  // 重力加速度 × フレーム表示間隔(秒)の2乗
var timeoutID;  // タイマー用ID

/* 画面を表示する
 * context: getContextで取得したcontext
 * w: 表示領域の幅
 * h: 表示領域の高さ
 */
function playgame(context, w, h) {
  this.PI2 = Math.PI * 2;  // 2π
  this.area = {w:w, h:h};  // 表示エリア
  this.context = context;
  this.jumping = true;  // ジャンプ中はtrue

  /* キャラクターデータ */
  this.charR = 5;  // キャラクターの半径
  this.charColor = "green";  // 色
  this.charX0 = 50;  // 中心のx座標初期値
  this.charY0 = this.area.h * 0.9 - this.charR ;  // 中心のy座標初期値
  this.peakY = 2 * this.charR + 5;  // ジャンプピーク時のy座標
  this.v0 = -1 * Math.sqrt(2 * ga * (this.charY0 - this.peakY));  // 初速度
  // 中心の座標 (x0,y0)は前回の座標
  this.charObj = {x0: this.charX0, y0:this.charY0, x: this.charX0, y: this.charY0};

  this.context.fillStyle = this.charColor;

  /* 画面更新 */
  this.update = function(){
    // ジャンプ処理
    this.jump();

    // キャラクターを描画
    this.context.clearRect(0, 0, this.area.w, this.area.h);
    this.context.beginPath();
    this.context.arc(this.charObj.x, this.charObj.y, this.charR, 0, this.PI2, false);
    this.context.fill();
    
    if(!this.jumping){
      // ジャンプ終了
      window.clearInterval(timeoutID);
    }
  }

  /* ジャンプする */
  this.jump = function(){
    var backy = this.charObj.y;  // 前回のy座標を待避
    if(this.charObj.y == this.charObj.y0){
      // 初速度から最初の座標を算出
      this.charObj.y = ga / (2 * Math.pow(fps,2)) + this.v0 / fps + this.charY0;
    }else{
      this.charObj.y += this.charObj.y - this.charObj.y0 + gh2;
    }
    // 前回の座標格納
    this.charObj.y0 = backy;

    // y座標初期値に着いたら跳ね返る
    // (落ちる速度が大きく、1秒あたりのフレーム数が小さい場合は、このロジックでは
    //  着地がぎこちなく見える場合があります)
    if(this.charObj.y >= this.charY0){
      this.v0 *= 0.8;  // 反発係数をかける
      if(this.v0 >= -20){
        // ジャンプ終了
        this.charObj.y = this.charY0;
        this.jumping = false;
      }else{
        // 初速度から最初の座標を算出
        this.charObj.y = ga / (2 * Math.pow(fps,2)) + this.v0 / fps + this.charY0;
        this.charObj.y0 = this.charY0;
      }
    }
  }
}

/* スタート */
start = function(){
  // canvasのDOM elementとcontext取得
  var canvas = document.getElementById("cv1");
  if ( ! canvas || ! canvas.getContext ) { return false; }
  var ctx = canvas.getContext("2d");
  ctx.lineWidth = 1;
  ctx.globalAlpha = 1;
  ctx.globalCompositeOperation = "source-over";

  // 画面表示
  PG = new playgame(ctx, canvas.width, canvas.height);
  timeoutID = window.setInterval("PG.update()", 1000 / fps);
}

-->
</script>
<style TYPE="text/css">
<!--
.title {
  margin: 10px;
  text-align: center;
  font-weight: bold;
  font-size: 20px;
  font-family: "MS Pゴシック", "Osaka", sans-serif;
}
.body {
  display: block;
  position: relative;
  width: 100px;
  margin-left: auto;
  margin-right: auto;
}
.cv {
  border-style: solid;
  border-width: 1px;  
  border-color: #808080;
  background-color: #F8F8FF;
}
-->
</style>
</head>
<body onload="start();">
<div class="title">Verlet法で計算した、ボールが弾むアニメーション</div>
<div class="body">
<canvas id="cv1" class="cv" width="100" height="150"></canvas>
</div>
</body>
</html>

横スクロールのゲームっぽい何かを作ってみる 3日目

今日はプログラミングはせず、ゲームプログラミングについて調べた。

ゲームプログラミングの基礎について書かれているところはないかなと適当に検索して見つけた
DirectX8による2Dゲームプログラミング講座
を一通り読んだ。
DirectXを使ったC++プログラミング講座だったので、今の自分に関係無い部分が多かったが、処理の全体的な流れが何となくわかった。

アルゴリズムの情報はないかと探してみたら、
ジャンル別ゲームの作り方とアルゴリズムまとめ
を見つけた。
ここのアクションゲーム関連で紹介されているサイトを見ながらやれば作れそうなので、明日はプログラミングしよう。

横スクロールのゲームっぽい何かを作ってみる 2日目

HTML5 Canvas で、「スペースキーを押したら丸が放物運動をする」ものを作った。
こちらに公開してます。sキーを押してスタートしてください。

Internet Explorer8でも動作確認した。excanvas.js だと動きが遅いので、FlashCanvasを使用した。
htmlファイルを直接IEで開くとFlashCanvasが動かなかったので、ローカルでテストする際もWebサーバーを立ち上げてテストした。

今回は 簡単な RayCaster - MDC を参考にして作成した。
ゲームっぽいものになるまでは道のりが遠そうだ。

横スクロールのゲームっぽい何かを作ってみる 1日目

HTML5 Canvas で簡単なゲームを作ってみようと思ったが、何から手をつければよいのかわからない。
とりあえず「ボタンを押したら何かがジャンプする」ものを作ってみよう。

ジャンプしたときの動きは重力加速度での放物運動でいいかなぁと、高校物理を思い出しながらいろいろ式をいじって、「スペースキーを押したら丸が放物運動をする」ものを作った。
次に「スペースキーを押すまでは地面が等速で動いて、スペースキーを押したら地面が止まって丸が放物運動をする」ものを作ろうとしたが、地面を動かすのがうまくいかなくて今日は終了。

ジャンプやスクロールはアクションゲームの基礎の基礎だろうから、調べればいろいろありそうだ。
サンプルプログラムもたくさんあるだろう。
キリのいいところまで作ったら調べようかな。
プロフィール

himax64

Author: 南西
30代後半の無職です。
就活もせずダラダラ生きてます。
作ったもの

最新記事
人気記事
検索フォーム
カテゴリ
月別アーカイブ
最新コメント
最新トラックバック
RSSリンクの表示
QRコード
QRコード
カウンター