@kk_oda

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

Javascriptのスロットゲームを指定した画像で停止させたい

解決したいこと

JavascriptやjQueryの初心者のため、いろんなサイトを参考にしながら、スロットゲームの作成を試しています。
画像をanimateでリールを回転させてストップボタンで画像は停止し、当選番号にそった画像を表示させたいのですが、
当たり等数の指定した画像で停止(表示)させることができません。
※当たり当数(result_no)はサーバ側で指定した確率で選定されます。

1等の場合は「7(0.png)」がそろうように指定の画像で停止させたいのですが、
中央で3つとも「7」が表示され正しく停止できることもあるものの、
一つ上にずれていたり、3つとも上にずれていたりと不安定な状態です。

windowsのPC(window11)の環境では、ほぼ指定した画像で停止できるのですが、
スマホ(android バージョン12)やiPad(ios16.2)などで実行すると指定した画像で停止せず、
一つ上部で停止するなど正しく表示されません。

色々試行錯誤してみているのですが解決できず、助言いただけると助かります。
どうぞよろしくお願い致します。

該当するソースコード

    <div class="main">
        <div class="slot">
            <div id="slot-frame">
                <ul class="reels">
                    <li class="reel"><img src="images/1.png"></li>
                    <li class="reel"><img src="images/4.png"></li>
                    <li class="reel"><img src="images/3.png"></li>
                    <li class="reel"><img src="images/2.png"></li>
                    <li class="reel"><img src="images/0.png"></li> //1等「7」
                    <li class="reel"><img src="images/1.png"></li>
                    <li class="reel"><img src="images/2.png"></li>
                </ul>
                <ul class="reels">
                    <li class="reel"><img src="images/0.png"></li>
                    <li class="reel"><img src="images/2.png"></li>
                    <li class="reel"><img src="images/1.png"></li>
                    <li class="reel"><img src="images/3.png"></li>
                    <li class="reel"><img src="images/4.png"></li>
                    <li class="reel"><img src="images/0.png"></li> //1等「7」
                    <li class="reel"><img src="images/2.png"></li>
                </ul>
                <ul class="reels">
                    <li class="reel"><img src="images/3.png"></li>
                    <li class="reel"><img src="images/2.png"></li>
                    <li class="reel"><img src="images/4.png"></li>
                    <li class="reel"><img src="images/1.png"></li>
                    <li class="reel"><img src="images/0.png"></li> //1等「7」
                    <li class="reel"><img src="images/3.png"></li>
                    <li class="reel"><img src="images/2.png"></li>
                </ul>
            </div>
        </div>
        <div>
            <button type="button" class="stop-btn" data-val="0" disabled="true">STOP!</button>
            <button type="button" class="stop-btn" data-val="1" disabled="true">STOP!</button>
            <button type="button" class="stop-btn" data-val="2" disabled="true">STOP!</button>
        </div>
        <div>
            <button type="button" id="start-btn">START!</button>
        </div>
        <div>
            <button id="btn_clear" style="display:none;">結果をクリア</button>
        </div>
    </div>



    <script src="https://code.jquery.com/jquery-3.7.0.min.js" integrity="sha256-2Pmvv0kuTBOenSvLm6bvfBSSHrUJ+3A7x6P5Ebd07/g=" crossorigin="anonymous"></script>
    <script>
        
        let slot_frame = document.getElementById("slot-frame");
        let reel = document.getElementsByClassName("reel");
        let reels = document.getElementsByClassName("reels");
        let start_btn = document.getElementById("start-btn");
        let stop_btn = document.getElementsByClassName("stop-btn");

        let sec = 100; //スロットのリール回転速度(実行毎秒数)
        let stopReelFlag = []; //スロットのリール停止フラグ
        let reelCounts = []; //どの画像をどの位置にさせるか
        let slotFrameHeight; //フレームの大きさ
        let slotReelsHeight; //リール(画像)全体の大きさ
        let slotReelItemHeight; //リール(画像)1個の大きさ
        let slotReelStartHeight; //画像の初期値

        let Slot = {
            init: function() { //初期化
                stopReelFlag[0] = stopReelFlag[1] = stopReelFlag[2] = false;
                reelCounts[0] = reelCounts[1] = reelCounts[2] = 0;
                //[0, 0, 0] 最初に中央にくる画像の設定
            },
            start: function() { //クリックイベント
                Slot.init();
                for (let index = 0; index < 3; index++) {
                    Slot.animation(index); //スロット3つ動かす
                }
            },
            stop: function(i) { //ストップボタンのクリックイベント
                //ストップボタンを押すと指定時間後に停止
                setTimeout(() => {
                    stopReelFlag[i] = true; //animateのループから抜け出せる
                }, 2000);

                //result_no(当選結果番号)により、reelCounts[]に該当する画像を指定して表示させる
                //1等の場合
                if (result_no == 1) {
                    if (i == 0) {
                        reelCounts[0] = 1;
                    } else if (i == 1) {
                        reelCounts[1] = 0;
                    } else if (i == 2) {
                        reelCounts[2] = 1;
                    }
                    console.log('stop', '当たり1', i, reelCounts[i]);
                //2等の場合
                } else if (result_no == 2) {
                    if (i == 0) {
                        reelCounts[0] = 0;
                    } else if (i == 1) {
                        reelCounts[1] = 3;
                    } else if (i == 2) {
                        reelCounts[2] = 2;
                    }
                    console.log('stop', '当たり2', i, reelCounts[i]);
                }
                if (stopReelFlag[0] && stopReelFlag[1] && stopReelFlag[2]) {
                    start_btn.removeAttribute("disabled"); //スタートボタンの機能を機能させる
                }
            },
            resetLocationInfo: function() { //最初の位置を設定
                slotFrameHeight = slot_frame.offsetHeight;
                slotReelsHeight = reels[0].offsetHeight;
                slotReelItemHeight = reel[0].offsetHeight;
                slotReelStartHeight = -slotReelsHeight; // -2700
                //画像末端がフレームのトップ  画像末端がフレームの末端  画像末端がフレームの中央    画像1.5枚分下げる
                slotReelStartHeight = slotReelStartHeight + slotFrameHeight-(slotFrameHeight / 2) + slotReelItemHeight * 3 / 2;                
                for (let i = 0; i < reels.length; i++) {
                    reels[i].style.top = String(slotReelStartHeight) + "px";
                }
            },
            animation: function(index) { //スロットを動かす
                if (reelCounts[index] >= 5) {
                    reelCounts[index] = 0;
                }
                //animate( CSSプロパティ, 速度, イージング関数名, アニメーション完了後に実行する関数 );
                $('.reels').eq(index).animate({
                    'top': slotReelStartHeight + (reelCounts[index] * slotReelItemHeight)
                    //indexが増えるたびに、画像1つ分下がる
                }, {
                    duration: sec, //回転速度
                    easing: 'linear', //常に一定の速度
                    complete: function() {
                        if (stopReelFlag[index]) { //stopReelFlag[index]がtrueになるまでループ
                            // return;
                            return;
                        }
                        reelCounts[index]++;
                        Slot.animation(index);
                    }
                });
            },
        };

        window.onload = function() {
            Slot.init();
            Slot.resetLocationInfo();
            start_btn.addEventListener("click", function(e) {
                e.target.setAttribute("disabled", true) //スタートボタンを無効化
                Slot.start();
                for (let i = 0; i < stop_btn.length; i++) {
                    stop_btn[i].removeAttribute("disabled"); //ストップボタンを機能させる
                }
            });
            for (let i = 0; i < stop_btn.length; i++) {
                stop_btn[i].addEventListener("click", function(e) {
                    Slot.stop(e.target.getAttribute('data-val')); //どのボタンをストップさせるか
                    start_btn.style.display = 'none';
                    document.getElementById('btn_clear').style.display = 'block';
                })
            }
        };
        btn_clear.addEventListener('click', function() {
            // draw.classList.remove('inactive');
            window.location.reload();
        });
    </script>

自分で試したこと

ここに問題・エラーに対して試したことを記載してください。

0 likes

1Answer

当たり画像の扱い

let reelCounts = []; //どの画像をどの位置にさせるか
この変数は、

  • 現在どの画像を表示しているか
  • 当選結果番号に対応する画像はどれか=最終的にどの画像を表示させたいか

の、どちらを管理したかったのでしょうか。
提示されたコードでは両方を管理しようとしてズレてしまっているように見えます。

時間の扱い

  • let sec = 100; //スロットのリール回転速度(実行毎秒数)
  • setTimeout のdelay 2000
  • 各reelsの画像は7個あるが回転時は5個で1周として扱っている
    (上下1つずつは表示用?)

となっているので、「クリック時に当たり画像を表示→そこから何周かして停止するとちょうど同じ画像が表示される」という意図に基づいた実装に思われるのですが
jsのアニメーションや遅延でそこまで厳密に時間を管理するのは難しいです。
これらは
「少なくとも2000ms秒後にsetTimeoutに指定した処理が実行される」
「各アニメーションは概ね100ms秒かけて動くが、complete内や他の処理次第で次のアニメーションまでに遅延が発生しているかもしれない」
ぐらいの感覚で扱うのが安全です。
PCだとずれにくいがスマホでは安定しないというのも、おそらく性能に依存して遅延の差が大きくなるからでしょう。

修正方針

  • 停止させたい当たりの画像は別途変数で保持していおく
  • 停止ボタンクリック後に一定時間経過してから、最初に当たりの画像が表示された時にanimationを中止

などでどうでしょうか

2Like

Comments

  1. @kk_oda

    Questioner

    ご回答くださりありがとうございます。

    説明が足りずすみません。
    「クリック時に当たり画像を表示→そこから何周かして停止するとちょうど同じ画像が表示される」とは異なる動きを想定していました。

    意図としては、setTimeoutは単にストップボタンを押してもすぐに停止させないためにいれたつもりで、
    ①ストップボタンを押す
    ②setTimeoutで指定した時間が経過後にanimationが停止
    ③最終的に表示したい画像を表示(reelCounts = []で指定)

    上記の動きを想定した場合、ご指摘いただいた内容から推察すると、setTimeoutの使い方や順番が間違えているのかと思ったのですが、いかがでしょうか?

    根本的に上記の考えでは正しく動作しないようでしたら、教えていただいた修正方針を参考に当たり目で止める方向で試してみたいと思います。

  2. おお、なるほど
    意図はしてなかったが、たまたまタイミング良ければ想定通り動きそうなコードになっていた…ということですかね。それはそれですごいw

    stop: function(i) { //ストップボタンのクリックイベント
        //ストップボタンを押すと指定時間後に停止
        // ①
        setTimeout(() => {
            stopReelFlag[i] = true; //animateのループから抜け出せる
        }, 2000);
    
        // ②
        //result_no(当選結果番号)により、reelCounts[]に該当する画像を指定して表示させる
        //1等の場合
        if (result_no == 1) {
            if (i == 0) {
                reelCounts[0] = 1;
            } else if (i == 1) {
                reelCounts[1] = 0;
            } else if (i == 2) {
                reelCounts[2] = 1;
            }
            console.log('stop', '当たり1', i, reelCounts[i]);
        //2等の場合
        } else if (result_no == 2) {
            if (i == 0) {
                reelCounts[0] = 0;
            } else if (i == 1) {
                reelCounts[1] = 3;
            } else if (i == 2) {
                reelCounts[2] = 2;
            }
            console.log('stop', '当たり2', i, reelCounts[i]);
        }
        if (stopReelFlag[0] && stopReelFlag[1] && stopReelFlag[2]) {
            start_btn.removeAttribute("disabled"); //スタートボタンの機能を機能させる
        }
    },
    

    説明のために番号を勝手に追加しました。

    1. ①の処理は、「2000ms後に、() => { stopReelFlag[i] = true; } というコールバック関数を呼び出して実行するように 予約する タイマー処理」です。
    2. ①の予約処理が成功したら、即座に②以降が実行され、そのまま stop の処理全体が終了します。
    3. 約2秒後、コールバックが実行されます

    開発者ツールなどでコンソールを表示しつつ実際に操作したら、おそらく

    1. 停止ボタンクリックですぐに console.log('stop', '当たりx', i, reelCounts[i]); の部分が出力される
    2. 2秒後に、実際に画面のリールが停止する

    という流れが確認できると思います。


    setTimeout のコールバック関数内に②の処理も全部入れる、という修正方針もありといえばありですが
    (実際の回転表示がどうなっているかは分かりませんが)おそらくリールが回って順番に表示されているところ、急に順番を無視した当たり画像が表示されることになるのではないでしょうか。
    なんというか、イカサマというかサーバ側の操作を疑われやすい演出というか……いや実際に操作してるわけですがw

  3. @kk_oda

    Questioner

    詳しく説明いただきありがとうございます。
    確かに、コンソールで表示された後にリールが停止しています。

    setTimeout のコールバック関数内に②入れる方法(イカサマっぽい動きかもしれません)は
    最初に試したのですが、そうするとなぜか当たり画像が表示されず、
    かえってバラバラの画像が表示されてしまうため、関数外に出しました。

    外に出すとPC上では揃ったので成功かと思ったら、スマホや他のデバイスではうまく動作しなかったという次第です。

    アドバイスいただいたように、最初に当たりの画像が表示された時にanimationを中止というのも試してみたのですが、それも正しい画像で停止できませんでした。

    もう少し勉強しながら試行錯誤してみます。
    他に何かお気づきの点がありましたら。お知らせいただけたら幸いです。
    よろしくお願いいたします。

Your answer might help someone💌