3D版ウニボールのソース

// 主人公の向き
var RpgDirection = {
    South:0
    , SouthWest:3
    , West:6
    , SouthEast:9
    , East:12
    , NorthWest:15
    , North:18
    , NorthEast:21
};
// イガイガを避けるゲームのコンストラクタ
var clsSample3D = function()
{
    // ゲームエンジン生成
    this.GameUtl = new clsGameUtl();
    // 3Dを使用する
    this.GameUtl.Use3DObject = true;
    // 主人公チップの幅
    this.ExpCx = 40;
    // 主人公チップの高さ
    this.ExpCy = 56;
    // 主人公の進行方向
    this.ExpDirection = RpgDirection.South;
    // 主人公フレーム数
    this.ExpFrameNum = 3;
    // 主人公ロード
    this.Explorer = this.GameUtl.LoadImage('./explorer.png');
    // ボールロード
    this.Ball = this.GameUtl.LoadImage('./uball.png');
    // ウニロード
    this.UrchinImg = this.GameUtl.LoadImage('./urchin.png');
    // レイアのサイズを得る
    var sz = this.GameUtl.GetSize();
    // 反射処理の為に壁の位置を記憶する
    this.Wall = {
        // 奥側の壁
        BackWall:{
            a:new THREE.Vector3(-(sz.cx / 2), (sz.cy / 2), -sz.cx)
            , b:new THREE.Vector3((sz.cx / 2), (sz.cy / 2), -sz.cx)
            , c:new THREE.Vector3((sz.cx / 2), -(sz.cy / 2), -sz.cx)
            , d:new THREE.Vector3(-(sz.cx / 2), -(sz.cy / 2), -sz.cx)
        }
        // 手前の壁
        , Front:{
            a:new THREE.Vector3(-(sz.cx / 2), (sz.cy / 2), 0)
            , b:new THREE.Vector3((sz.cx / 2), (sz.cy / 2), 0)
            , c:new THREE.Vector3((sz.cx / 2), -(sz.cy / 2), 0)
            , d:new THREE.Vector3(-(sz.cx / 2), -(sz.cy / 2), 0)
        }
        // 左の壁
        , LeftWall:{
            a:new THREE.Vector3(-(sz.cx / 2), (sz.cy / 2), 0)
            , b:new THREE.Vector3(-(sz.cx / 2), (sz.cy / 2), -sz.cx)
            , c:new THREE.Vector3(-(sz.cx / 2), -(sz.cy / 2), -sz.cx)
            , d:new THREE.Vector3(-(sz.cx / 2), -(sz.cy / 2), 0)
        }
        // 右の壁
        , RightWall:{
            a:new THREE.Vector3((sz.cx / 2), (sz.cy / 2), 0)
            , b:new THREE.Vector3((sz.cx / 2), (sz.cy / 2), -sz.cx)
            , c:new THREE.Vector3((sz.cx / 2), -(sz.cy / 2), -sz.cx)
            , d:new THREE.Vector3((sz.cx / 2), -(sz.cy / 2), 0)
        }
        // 床
        , Floor:{
            a:new THREE.Vector3(-(sz.cx / 2), -(sz.cy / 2), -sz.cx)
            , b:new THREE.Vector3((sz.cx / 2), -(sz.cy / 2), -sz.cx)
            , c:new THREE.Vector3((sz.cx / 2), -(sz.cy / 2), 0)
            , d:new THREE.Vector3(-(sz.cx / 2), -(sz.cy / 2), 0)
        }
        // 天井
        , Ceiling: {
            a:new THREE.Vector3(-(sz.cx / 2), (sz.cy / 2), -sz.cx)
            , b:new THREE.Vector3((sz.cx / 2), (sz.cy / 2), -sz.cx)
            , c:new THREE.Vector3((sz.cx / 2), (sz.cy / 2), 0)
            , d:new THREE.Vector3(-(sz.cx / 2), (sz.cy / 2), 0)
        }
    };
}
// 実行
clsSample3D.prototype.Run = function()
{
    // ゲームエンジン初期化
    this.GameUtl.Initialize(this);
    // 主人公レイア
    var idx = this.GameUtl.CreateLayer(this.EnterStage, false, true);
    // 主人公データ
    this.HeroData = this.GameUtl.aryLayer[idx].prm;
    // ボールのフレーム処理
    var idx = this.GameUtl.AddDispatch(this.EnterBall);
    // ボールデータ
    this.BallData = this.GameUtl.Dispatch[idx].prm;
    // キー入力イベントの設定
    this.GameUtl.EnterKeyDown(this.KeyDown);
    // 描画停止
    this.GameUtl.CanRenderer = false;
    // ゲーム開始
    this.GameUtl.DoGame();
}
// 主人公レイア
clsSample3D.prototype.EnterStage = function(ctx, p)
{
    if (!this.GameOver) {
        // 壁や床の設定
        if (!p.Grid)
            this.SetGrid(p);
        // 主人公の処理
        if (this.Explorer.complete) {
            // 主人公の設定
            if (!p.HeroMesh)
                this.SetHero(p);
            // 主人公を描画する
            this.DrawHero(p);
        }
    }
}
// ボールのフレーム処理
clsSample3D.prototype.EnterBall = function(p)
{
    if (!this.GameOver) {
        // ボールの処理
        if (this.Ball.complete) {
            // ボールの設定
            if (!p.BallMesh)
                this.SetBall(p);
            // ボールの移動
            this.MoveBall(p);
        }
    }
}
// 面とグリッド生成
clsSample3D.prototype.SetGrid = function(p)
{
    // レイアのサイズを得る
    var sz = this.GameUtl.GetSize();
    // 壁と床オブジェクト生成
    var GridBackWall = new THREE.Object3D();
    var GridLeftWall = new THREE.Object3D();
    var GridRightWall = new THREE.Object3D();
    var GridFloor = new THREE.Object3D();
    // 罫線質感生成
    var Material = new THREE.LineBasicMaterial({ color: "black" });
    // 奥側と左右の壁の横線
    for (var sy = -(sz.cy / 2); sy <= (sz.cy / 2); sy += this.ExpCy) {
        // 奥側の壁
        var v1 = new THREE.Vector3(-(sz.cx / 2), sy, 0);
        var v2 = new THREE.Vector3((sz.cx / 2), sy, 0);
        var geo = new THREE.Geometry();
        geo.vertices.push(v1, v2);
        GridBackWall.add(new THREE.Line(geo, Material));
        // 左右の壁
        var v1 = new THREE.Vector3(0, sy, -sz.cx);
        var v2 = new THREE.Vector3(0, sy, 0);
        var geo = new THREE.Geometry();
        geo.vertices.push(v1, v2);
        GridLeftWall.add(new THREE.Line(geo, Material));
        GridRightWall.add(new THREE.Line(geo, Material));
    }
    // 奥側と左右の壁と床
    for (var sx = -(sz.cx / 2); sx <= (sz.cx / 2); sx += this.ExpCx) {
        // 奥側の壁の縦線
        var v1 = new THREE.Vector3(sx, -(sz.cy / 2), 0);
        var v2 = new THREE.Vector3(sx, (sz.cy / 2), 0);
        var geo = new THREE.Geometry();
        geo.vertices.push(v1, v2);
        GridBackWall.add(new THREE.Line(geo, Material));
        // 左右の壁の縦線
        var v1 = new THREE.Vector3(0, -(sz.cy / 2), sx - (sz.cx / 2));
        var v2 = new THREE.Vector3(0, (sz.cy / 2), sx - (sz.cx / 2));
        var geo = new THREE.Geometry();
        geo.vertices.push(v1, v2);
        GridLeftWall.add(new THREE.Line(geo, Material));
        GridRightWall.add(new THREE.Line(geo, Material));
        // 床の横線
        var v1 = new THREE.Vector3(-(sz.cx / 2), 0, sx - (sz.cx / 2));
        var v2 = new THREE.Vector3((sz.cx / 2), 0, sx - (sz.cx / 2));
        var geo = new THREE.Geometry();
        geo.vertices.push(v1, v2);
        GridFloor.add(new THREE.Line(geo, Material));
        // 床の縦線
        var v1 = new THREE.Vector3(sx, 0, -sz.cx);
        var v2 = new THREE.Vector3(sx, 0, 0);
        var geo = new THREE.Geometry();
        geo.vertices.push(v1, v2);
        GridFloor.add(new THREE.Line(geo, Material));
    }
    // 壁と床の位置に移動
    GridBackWall.position.z = -sz.cx;
    GridLeftWall.position.x = -(sz.cx / 2);
    GridRightWall.position.x = (sz.cx / 2);
    GridFloor.position.y = -(sz.cy / 2);
    // グリッドオブジェクト生成
    p.Grid = new THREE.Object3D();
    // 壁と床をグリッドオブジェクトに追加
    p.Grid.add(GridBackWall);
    p.Grid.add(GridLeftWall);
    p.Grid.add(GridRightWall);
    p.Grid.add(GridFloor);
    // グリッドオブジェクトをシーンに追加
    this.GameUtl.Core.Scene.add(p.Grid);
}
// 主人公生成
clsSample3D.prototype.SetHero = function(p)
{
    // レイアのサイズを得る
    var sz = this.GameUtl.GetSize();
    // 主人公を描画する Canvas を生成
    var HeroCanvas = this.GameUtl.NewHiddenCanvas(g_idStage, this.ExpCx, this.ExpCy);
    // 生成した Canvas をテクスチャとする
    var HeroTexture = new THREE.Texture(HeroCanvas);
    // 画像を指定したmaterialの用意
    var HeroMaterial = new THREE.MeshBasicMaterial({ map:HeroTexture });
    // 主人公の形状を生成
    var HeroGeometry = new THREE.PlaneGeometry(this.ExpCx * 2, this.ExpCy * 2);
    // メッシュを生成
    p.HeroMesh = new THREE.Mesh(HeroGeometry, HeroMaterial);
    // 床に移動する
    p.HeroMesh.position.y = -(sz.cy / 2) + this.ExpCy - 4;
    p.HeroMesh.position.z = -(sz.cx / 2);
    // 主人公をシーンに追加
    this.GameUtl.Core.Scene.add(p.HeroMesh);
    // コンテキストを生成
    p.HeroCtx = HeroCanvas.getContext("2d");
}
// 主人公を描画する
clsSample3D.prototype.DrawHero = function(p)
{
    // 主人公の位置を取得
    var pos = this.GetHeroPosition();
    // 位置を設定        
    p.HeroMesh.position.x = pos.dx;
    p.HeroMesh.position.z = pos.dz;
    // fps からフレーム番号を得る
    var frame = this.GameUtl.GetNextFrame(p, 256, this.ExpFrameNum);
    // 描画するコマが変化したなら...
    if (p.Frame != frame || this.GameOver) {
        // フレーム番号を記憶する
        p.Frame = frame;
        // キャンバスクリア
        p.HeroCtx.clearRect(0, 0, this.ExpCx, this.ExpCy);
        // 画像位置の転送元の位置を設定
        var sx = ((this.ExpDirection % 2) == 0 ? 0 : this.ExpCx * 3) + (p.Frame * this.ExpCx);
        var sy = Math.floor(this.ExpDirection / 6) * this.ExpCy;
        // 主人公の絵を描く
        p.HeroCtx.drawImage(this.Explorer, sx, sy, this.ExpCx, this.ExpCy, 0, 0, this.ExpCx, this.ExpCy);
        // 主人公の再描画を依頼するフラグを立てる
        p.HeroMesh.material.map.needsUpdate = true;
    }
    // 主人公とボールの当たり判定
    if (!this.GameOver) {
        if (this.BallData.BallMesh) {
            var ballV = this.BallData.BallMesh.position.clone();
            var ballR = this.BallData.BallRadius;
            var heroV = p.HeroMesh.position.clone();
            var heroR = this.ExpCx / 2 - 4;
            // 主人公も円とみなす。主人公とボールの距離が双方の半径の和より小さいなら衝突とする
            var bOver = (ballV.distanceTo(heroV) < ballR + heroR);
            //  ゲームオーバー処理
            if (bOver) {
                // メッセージを表示
                $('#msg').text(" ★ゲームオーバー★");
                // 描画停止
                this.GameUtl.CanRenderer = false;
                // ウニ演出フラグを立てる
                this.UrchinFlag = true;
                // ゲームオーバーフラグを立てる
                this.GameOver = true;
            }
        }
    }
}
// 主人公の位置を取得
clsSample3D.prototype.GetHeroPosition = function(WantRow, WantCol)
{
    // 指定行が無ければ現在の主人公の行を設定
    if (WantRow === undefined)
        WantRow = this.ExpRow;
    // 指定列が無ければ現在の主人公の列を設定
    if (WantCol === undefined)
        WantCol = this.ExpCol;
    // レイアのサイズを得る
    var sz = this.GameUtl.GetSize();
    // 位置を計算する
    var dz = (WantRow + 1) * this.ExpCx - sz.cx;
    var dx = (WantCol + 1) * this.ExpCx - (sz.cx / 2);
    // 結果を返す
    return { dx:dx - (this.ExpCx / 2), dz:dz - (this.ExpCx / 2) };
}
// ボール生成
clsSample3D.prototype.SetBall = function(p)
{
    // レイアのサイズを得る
    var sz = this.GameUtl.GetSize();
    // ボールの質感
    var txt  = new THREE.ImageUtils.loadTexture('./uball.png');
    // ボールの形状
    var mtr = new THREE.MeshPhongMaterial({ map: txt });
    // ボールの半径
    var ballRadius = 28;
    // 半径, 経度分割数, 緯度分割数
    var geo = new THREE.SphereGeometry(ballRadius, 12, 12);
    // ボールの半径を記憶
    p.BallRadius = ballRadius;
    // ボールオブジェクト生成
    p.BallMesh = new THREE.Mesh(geo, mtr);
    // 天井の位置(ボールの初期位置) ボールのベクトル(天井からの発射位置で初期化)
    this.SetBallStarting(p);
    // シーンに追加
    this.GameUtl.Core.Scene.add(p.BallMesh);
}
// ボール位置と移動方向の初期化
clsSample3D.prototype.SetBallStarting = function(p, bNonSetUrchinFlag)
{
    // レイアのサイズを得る
    var sz = this.GameUtl.GetSize();
    // 行列の最大値を設定
    var maxRow =  Math.floor(sz.cx / this.ExpCx) - 1;
    var maxCol =  Math.floor(sz.cx / this.ExpCx) - 1;
    // 主人公のいる行列を設定
    var targetRow = this.ExpRow;
    var targetCol = this.ExpCol;
    // 主人公のいる付近を目標にする
    if (this.Random(0, 1) == 0) {
        targetRow = Math.max(0, Math.min(maxRow, this.ExpRow + this.Random(-5, 5)));
        targetCol = Math.max(0, Math.min(maxCol, this.ExpCol + this.Random(-5, 5)));
    }
    // 主人公のいる付近の位置を得る
    var pos = this.GetHeroPosition(targetRow, targetCol);
    // 天井の位置(ボールの初期位置)
    p.BallMesh.position = new THREE.Vector3(0, (sz.cy / 2), -(sz.cx / 2));
    // ボールの目標位置設定
    var a = p.BallMesh.position.clone();
    var b = new THREE.Vector3(pos.dx, this.HeroData.HeroMesh.position.y, pos.dz);
    // ab ベクトルの進行方向を計算
    b.sub(a).normalize().multiplyScalar(sz.cx * sz.cx + sz.cy * sz.cy);
    // 移動目標を設定
    p.BallMove = a.add(b);
    // ウニの演出フラグを立てる
    if (!bNonSetUrchinFlag)
        this.UrchinFlag = true;
    // フレーム番号をリセット
    p.Frame = -1;
}
// ボール移動
clsSample3D.prototype.MoveBall = function(p)
{
    if (this.UrchinFlag) {
        if (this.UrchinImg.complete) {
            if (!p.UrchinMesh) {
                // レイアのサイズを得る
                var sz = this.GameUtl.GetSize();
                // ウニの形状を生成
                var Geometry = new THREE.PlaneGeometry(this.ExpCx * 4, this.ExpCx * 4);
                // 画像を指定したmaterialの用意
                var Material = new THREE.MeshBasicMaterial({ map:THREE.ImageUtils.loadTexture(this.UrchinImg.src) });
                // メッシュを生成
                p.UrchinMesh = new THREE.Mesh(Geometry, Material);
                // 天井に移動する
                p.UrchinMesh.position = new THREE.Vector3(0, (sz.cy / 2), -(sz.cx / 2));
                // ウニをシーンに追加
                this.GameUtl.Core.Scene.add(p.UrchinMesh);
            }
            // ウニの演出フレーム数
            var FrameCount = 12;
            // ウニの演出フレーム番号取得
            var frame = this.GameUtl.GetNextFrame(p, this.ExpBallSpeed, FrameCount);
            // 描画するコマが変化したなら...
            if (p.Frame != frame) {
                // フレーム番号を記憶する
                p.Frame = frame;
                // ウニを回転する
                p.UrchinMesh.rotation.z = this.Radian(this.Random(0, 359));
                // 最終フレームならOFFにする
                this.UrchinFlag = (p.Frame != FrameCount - 1);
            }
        }
        // 表示の切り替え
        p.UrchinMesh.visible = this.UrchinFlag;
        p.BallMesh.visible = !p.UrchinMesh.visible;
        // ウニの演出が終わったらボールの位置と狙う位置を設定
        if (!this.UrchinFlag)
            this.SetBallStarting(p, true);
    }
    else {
        // fps からフレーム番号を得る
        var frame = this.GameUtl.GetNextFrame(p, this.ExpBallSpeed, 4096);
        // 描画するコマが変化したなら...
        if (p.Frame != frame) {
            // フレーム番号を記憶する
            p.Frame = frame;
            // ボールの位置
            var a = p.BallMesh.position.clone();
            // ボールの移動後の位置
            var m = this.SetNextPositionOfBall(p);
            // 壁との衝突判定
            var BackWall = this.IntersectPlaneAndLine(a, m, this.Wall.BackWall);
            var LeftWall = this.IntersectPlaneAndLine(a, m, this.Wall.LeftWall);
            var RightWall = this.IntersectPlaneAndLine(a, m, this.Wall.RightWall);
            var Front = this.IntersectPlaneAndLine(a, m, this.Wall.Front);
            var Floor = this.IntersectPlaneAndLine(a, m, this.Wall.Floor);
            var Ceiling = this.IntersectPlaneAndLine(a, m, this.Wall.Ceiling);
            // 複数の壁との衝突判定
            var bPluralHit = (
                (BackWall && LeftWall) || (BackWall && RightWall) || (BackWall && Floor) || (BackWall && Ceiling)
                || (Front && LeftWall) || (Front && RightWall) || (Front && Floor) || (Front && Ceiling)
                || (Floor && LeftWall) || (Floor && RightWall) || (Ceiling && LeftWall) || (Ceiling && RightWall)
            );
            // 複数の壁との衝突時はベクトルを逆にする、壁との衝突なら反射処理、以外はボールを移動
            if (bPluralHit)
                p.BallMove.multiplyScalar(-1);
            else if (BackWall)
                this.DoReflection(p, BackWall);
            else if (LeftWall)
                this.DoReflection(p, LeftWall);
            else if (RightWall)
                this.DoReflection(p, RightWall);
            else if (Front)
                this.DoReflection(p, Front);
            else if (Floor)
                this.DoReflection(p, Floor);
            else if (Ceiling) {
                if (this.Random(0, 4) == 0 || this.CeilingBoundCount >= 4) {
                    this.SetBallStarting(p);
                    this.CeilingBoundCount = 0;
                }
                else {
                    this.DoReflection(p, Ceiling);
                    this.CeilingBoundCount++;
                }
                // 得点加算表示
                $('#score').text(++this.Scored);
            }
            else
                p.BallMesh.position = m;
        }
    }
}
// ボールの移動位置を得る
clsSample3D.prototype.SetNextPositionOfBall = function(p)
{
    if (p.BallMesh.position.equals(p.BallMove))
        this.SetBallStarting(p);
    // ボールの位置
    var a = p.BallMesh.position.clone();
    // ボールの移動先
    var b = p.BallMove.clone();
    // ボールの直径分の移動ベクトル
    var v = b.sub(a).normalize().multiplyScalar(p.BallRadius * 2);
    // ボールの移動後の位置
    var m = a.add(v);
    // ボールの移動後の位置を返す
    return m;
}
// 線分と平面の交点を計算する
//  va:線分始点 vb:線分終点 Plane:平面
clsSample3D.prototype.IntersectPlaneAndLine = function(va, vb, Plane)
{
    // 面の中心点を求める 頂点ベクトルの総和 / 4
    var P = (Plane.a.clone().add(Plane.b).add(Plane.c).add(Plane.d)).divideScalar(4);
      // 面の中心点と線分始点のベクトル(Pからvaに向かう)
      var pa = va.clone().sub(P);
      // 面の中心点と線分終点のベクトル(Pからvbに向かう)
      var pb = vb.clone().sub(P);
    // 面に対して垂直なベクトル=法線ベクトル
    var ab = Plane.b.clone().sub(Plane.a);
    var ad = Plane.d.clone().sub(Plane.a);
    var N = ab.cross(ad).normalize();
      // pa pbそれぞれを平面法線と内積
      var dot_PA = pa.clone().dot(N);
      var dot_PB = pb.clone().dot(N);
    // 両端が平面上にあり、交点を計算できない。
      if (dot_PA == 0 && dot_PB == 0)
        return false;
    // 内積の片方がプラスで片方がマイナスなら交差している。偽なら交差しない
    if (!((dot_PA >= 0 && dot_PB < 0) || (dot_PA < 0 && dot_PB >= 0)))
        return false;
      // 以下、交点を求める 
      var ab = vb.clone().sub(va);
      // 交点とAの距離 : 交点とBの距離 = dot_PA : dot_PB
      var rt = Math.abs(dot_PA) / (Math.abs(dot_PA) + Math.abs(dot_PB));
    // 結果を返す
    return {
        Intersection:ab.multiplyScalar(rt).add(va), Normal:N
    };
}
// ボールの反射処理
clsSample3D.prototype.DoReflection = function(p, crossP)
{
    // レイアのサイズを得る
    var sz = this.GameUtl.GetSize();
    // ボールの位置
    var a = p.BallMesh.position.clone();
    // 衝突点
    var c = crossP.Intersection.clone();
    // 衝突点へ向かう入射ベクトル
    var F = c.clone().sub(a);
    // 反射ベクトルの式 R = F + 2(−F⋅N)N の 2(−F⋅N) 部分の計算
    var T = F.clone().multiplyScalar(-1).dot(crossP.Normal) * 2;
    // 反射ベクトルの式 R = F + 2(−F⋅N)N
    var R = F.add(crossP.Normal.multiplyScalar(T));
    // 反射ベクトルを箱を必ず突き抜ける値にする
    R.normalize().multiplyScalar(sz.cx * sz.cx + sz.cy * sz.cy);
    // ボールの位置と移動目標を設定
    p.BallMove = R.add(a);
    // ボールの移動
    p.BallMesh.position = this.SetNextPositionOfBall(p);
}
// キー入力
clsSample3D.prototype.KeyDown = function(e)
{
    // レイアのサイズを得る
    var sz = this.GameUtl.GetSize();
    // 行列の最大値を設定
    var maxRow =  Math.floor(sz.cx / this.ExpCx) - 1;
    var maxCol =  Math.floor(sz.cx / this.ExpCx) - 1;
    // テンキーは小文字で表現される
    var ch = String.fromCharCode(e.keyCode);
    // キー分岐
    switch (ch) {
        case '2': case 'C':case 'b':    // 下
            this.ExpDirection = RpgDirection.South;
            this.ExpRow += (this.ExpRow + 1 > maxRow ? 0 : 1);
            break;
        case '1': case 'X': case 'a':   // 左下
            this.ExpDirection = RpgDirection.SouthWest;
            this.ExpRow += (this.ExpRow + 1 > maxRow ? 0 : 1);
            this.ExpCol += (this.ExpCol - 1 < 0 ? 0 : -1);
            break;
        case '4': case 'S': case 'd':   // 左
            this.ExpDirection = RpgDirection.West;
            this.ExpCol += (this.ExpCol - 1 < 0 ? 0 : -1);
            break;
        case '3': case 'V': case 'c':   // 右下
            this.ExpDirection = RpgDirection.SouthEast;
            this.ExpRow += (this.ExpRow + 1 > maxRow ? 0 : 1);
            this.ExpCol += (this.ExpCol + 1 > maxCol ? 0 : 1);
            break;
        case '6': case 'F': case 'f':   // 右
            this.ExpDirection = RpgDirection.East;
            this.ExpCol += (this.ExpCol + 1 > maxCol ? 0 : 1);
            break;
        case '7': case 'W': case 'g':   // 左上
            this.ExpDirection = RpgDirection.NorthWest;
            this.ExpRow += (this.ExpRow - 1 < 0 ? 0 : -1);
            this.ExpCol += (this.ExpCol - 1 < 0 ? 0 : -1);
            break;
        case '8': case 'E': case 'h':   // 上
            this.ExpDirection = RpgDirection.North;
            this.ExpRow += (this.ExpRow - 1 < 0 ? 0 : -1);
            break;
        case '9': case 'R': case 'i':   // 右上
            this.ExpDirection = RpgDirection.NorthEast;
            this.ExpCol += (this.ExpCol + 1 > maxCol ? 0 : 1);
            this.ExpRow += (this.ExpRow - 1 < 0 ? 0 : -1);
            break;
        case ' ':
            // ボールの初期位置設定
            this.Initialize();
            // 描画再開
            this.GameUtl.CanRenderer = true;
            break;
    }
}
// 初期化
clsSample3D.prototype.Initialize = function()
{
    // キーバッファクリア
    this.GameUtl.ClearKeyDownQueue();
    // レイアのサイズを得る
    var sz = this.GameUtl.GetSize();
    // ボール速度
    this.ExpBallSpeed = parseInt($('#speed').val());
    // 主人公の行位置と列位置
    this.ExpRow = this.ExpCol = Math.floor(sz.cx / 2 / this.ExpCx);
    // 得点
    this.Scored = 0;
    // 天井でバウンドした回数を初期化
    this.CeilingBoundCount = 0;
    // ゲームオーバーフラグを倒す
    this.GameOver = false;
    // ボールの位置を天井に移す
    if (this.BallData && this.BallData.BallMesh)
        this.SetBallStarting(this.BallData);
    // テキスト部クリア
    $('#score').text(this.Scored);
    $('#msg').text(" ");
    $('#stage').css('background-image', 'none');
}
// min から max までの乱整数を返す関数
clsSample3D.prototype.Random = function(min, max)
{
    return Math.floor(Math.random() * (max - min + 1)) + min;
}
// 弧度取得
clsSample3D.prototype.Radian = function(v)
{
    // 度数を弧度に変換
    var r = v * Math.PI / 180;
    // 結果を返す
    return r;
}