トップへ(mam-mam.net/)

WEBで3Dスロットマシン ゲーム ~Webサイトで3Dコンテンツ(Three.js)デザイン

検索:

Three.jsを使ってWEBブラウザ上で 3Dスロットマシン ゲーム(Three.js[r145]を使用)

WEBブラウザ上で動作する3Dスロットマシン。Startボタンでスロットマシンが回転し、Stopボタンでスロットが停止します。

Stop
Stop
Stop
Start
 

ソースコード

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=0.5, maximum-scale=2.0,user-scalable=yes">
  <meta charset="UTF-8">
  <script src="./three_r145/three.min.js"></script>
  <script src="./three_r145/OrbitControls.js"></script>

<style>
.slot3DB{
  display:inline-block;
  box-sizing:border-box;
  width:100%;
  min-width:20px;
  max-width:calc(100% - 0.5em);
  margin:6px;
  padding:0.5em;
  color:#000;
  text-shadow:1px 1px 2px #999;
  background:#fff;
  border:0px none #000;
  text-decoration:none;
  box-shadow: 0px 0px 6px 2px rgba(0,0,0,0.4);
  cursor:pointer;
  border-radius:10px 10px 10px 10px;
  vertical-align:middle;
  user-select: none;
  word-break:break-all;
  word-wrap:anywhere;
}
.slot3DB:active{
  box-shadow: 0px 0px 6px 2px rgba(0,0,0,0.4) inset;
}
.slot3DB:hover{}
.slot3DS{
  background:#cfc;
  padding:1em 4em;
}
</style>


<script>
  var png=CreateTextPng();
  var scene,camera,renderer;
  //       位置      1:加速 2:減速   速度
  var slot3D={d:[0,0,0],ds:[0,0,0],dv:[0,0,0],status:-1,bstop:[null,null,null],bstart:null,g:[null,null,null],result:null};
  var main = function () {
    //描画先canvasを取得して幅と高さ取得
    let can=document.getElementById('can');
    let w = parseInt(can.style.width);//幅取得
    let h = parseInt(can.style.height);//高さ取得

    //■シーン
    scene = new THREE.Scene();
    scene.background = new THREE.Color(0xccddff);

    //■カメラ
    //THREE.PerspectiveCamera( 画角(度), アスペクト比, カメラに写る最短距離, カメラに写る最長距離 );
    camera = new THREE.PerspectiveCamera(20, w/h, 0.1, 10000);
    camera.position.set(0, 0, 24);//カメラの位置を設定
    //camera.rotation.set(0,0,0);//カメラの角度を(0度,0度,0度)にする
    camera.lookAt(new THREE.Vector3(0, 0, 0));//カメラの向きを(0,0,0)座標にする
    scene.add(camera);//カメラをシーンに追加

    //■レンダラー
    renderer = new THREE.WebGLRenderer({canvas:can, antialias: true});//アンチエイリアス有効
    renderer.setSize(w, h);
    //シャドー(影)マップを有効にする
    renderer.shadowMap.enabled = true;
    renderer.setClearColor(0x000000, 1);//背景色を設定

    //■光源------------------------------------------------
    //環境光(すべての物体にすべての方向から与える光)光の当たらない部分でも真っ黒にならない
    var AmbientLight=new THREE.AmbientLight(0xffffff,0.4);
    scene.add( AmbientLight );//環境光をシーンに追加
    //スポットライト(色,強さ,距離,角度(angle),半影[0-1],減衰[1,1])
    const spotLight=new THREE.SpotLight(0xffffff, 1, 160,Math.PI/8, 0.4, 1);
    spotLight.position.set(8,12,20);
    spotLight.target.position.set(0,0,0);
    spotLight.castShadow=true;//影を表示可能に
    spotLight.shadow.mapSize.width = 4096;  //影をきれいにする
    spotLight.shadow.mapSize.height = 4096; //影をきれいにする
    spotLight.shadow.radius=10;  //影をぼかす
    scene.add(spotLight);
    scene.add(spotLight.target);

    let m=new THREE.MeshLambertMaterial({
      color:0xdddddd,side:THREE.FrontSide
    });
    let mm=new THREE.MeshLambertMaterial({
      color:0xff3344,side:THREE.FrontSide,transparent:true,opacity:0.6
    });
    let bgeo=new THREE.BoxGeometry(1,1,1);
    let bm1=new THREE.Mesh(bgeo,m);
    bm1.scale.set(10,8,2);
    bm1.position.set(0,1,-3);
    bm1.castShadow=true;
    bm1.receiveShadow=true;
    scene.add(bm1);
    let bm2=new THREE.Mesh(bgeo,m);
    bm2.scale.set(10,2,4);
    bm2.position.set(0,4,0);
    bm2.castShadow=true;
    bm2.receiveShadow=true;
    scene.add(bm2);
    let bm3=new THREE.Mesh(bgeo,m);
    bm3.scale.set(10,4,6);
    bm3.position.set(0,-5,-1);
    bm3.castShadow=true;
    bm3.receiveShadow=true;
    scene.add(bm3);
    let bm4=new THREE.Mesh(bgeo,m);
    bm4.scale.set(2,6,4);
    bm4.position.set(-4,0,0);
    bm4.castShadow=true;
    bm4.receiveShadow=true;
    scene.add(bm4);
    let bm5=new THREE.Mesh(bgeo,m);
    bm5.scale.set(2,6,4);
    bm5.position.set(4,0,0);
    bm5.castShadow=true;
    bm5.receiveShadow=true;
    scene.add(bm5);
    let bm6=new THREE.Mesh(bgeo,mm);
    bm6.castShadow=true;
    bm6.scale.set(6,0.1,0.1);
    bm6.position.set(0,0.72,2-0.05);
    scene.add(bm6);
    let bm7=new THREE.Mesh(bgeo,mm);
    bm7.castShadow=true;
    bm7.scale.set(6,0.1,0.1);
    bm7.position.set(0,-0.5,2-0.05);
    scene.add(bm7);
    

    //テクスチャのロード
    let texture = new THREE.TextureLoader().load( png );
    let material=new THREE.MeshLambertMaterial({
      color:0xffffff, map:texture ,side:THREE.FrontSide,
      transparent:false, opacity:1.0,
    });
    //■円柱ジオメトリの作成(上部半径,底半径,高さ,分割数(三角形の数)[8],高さ面の分割数[1],上下が空いてるか[false],始角度[0],角度[Math.PI*2])
    let cgeo=new THREE.CylinderGeometry(2,2,1.6,32,1,true,0,Math.PI*2);
    let m1=new THREE.Mesh(cgeo,material);
    let m2=new THREE.Mesh(cgeo,material);
    let m3=new THREE.Mesh(cgeo,material);
    m1.castShadow=true;
    m1.receiveShadow=true;
    m2.castShadow=true;
    m2.receiveShadow=true;
    m3.castShadow=true;
    m3.receiveShadow=true;

    m1.rotation.set(-Math.PI/10,0,-Math.PI/2);
    m1.position.set(-1.6,0,0);
    m2.rotation.set(-Math.PI/10,0,-Math.PI/2);
    m2.position.set(0,0,0);
    m3.rotation.set(-Math.PI/10,0,-Math.PI/2);
    m3.position.set(1.6,0,0);
    let g1=new THREE.Group();
    g1.add(m1);
    let g2=new THREE.Group();
    g2.add(m2);
    let g3=new THREE.Group();
    g3.add(m3);
    scene.add(g1);
    scene.add(g2);
    scene.add(g3);
    slot3D.g[0]=g1;
    slot3D.g[1]=g2;
    slot3D.g[2]=g3;
    for(let i=0;i<3;i++){
      slot3D.bstop[i]=document.querySelector('#bstop'+(i+1));
      slot3D.bstop[i].addEventListener('click',function(){
        let id=parseInt(event.target.id.substr(-1))-1;
        if(slot3D.status==1 && slot3D.ds[id]==1){
          slot3D.ds[id]=2;
        }
      });
    }
    slot3D.bstart=document.querySelector('#bstart');
    slot3D.bstart.addEventListener('click',function(){
      slot3D.result.innerHTML='';
      if(slot3D.status==1){return;}
      slot3D.status=1;
      for(let i=0;i<slot3D.d.length;i++){
        slot3D.ds[i]=1;//加速
        slot3D.dv[i]=0;//速度
      }
    });
    slot3D.result=document.querySelector('#slotr');

    //オービットコントロールの作成(マウスの左右ボタンドラッグ、ホイール等でカメラが動くようになる)
    const controls = new THREE.OrbitControls(camera, renderer.domElement);
    controls.target.set(0,0,0);
    controls.update();
    controls.enabled=false;
    setInterval(renderLoop,33);
  }

  function renderLoop () {
    renderer.render(scene, camera);
    starting();
  }

  window.addEventListener('DOMContentLoaded', main, false);

  function starting(){
    if(slot3D.status==-1){return;}
    for(let i=0;i<slot3D.d.length;i++){
      if(slot3D.ds[i]==1){
        slot3D.dv[i]+=Math.floor(1+Math.random()*3);
        if(slot3D.dv[i]>30){slot3D.dv[i]=30;}
      }else if(slot3D.ds[i]==2){
        if(slot3D.dv[i]>2){
          slot3D.dv[i]-=(Math.floor(Math.random()*slot3D.dv[i]/10)+1);
          if(slot3D.dv[i]<2){
            if(slot3D.d[i]%2==1){slot3D.d[i]++;}
            slot3D.dv[i]=2;
          }
        }else{
          if(slot3D.d[i]%2==1){slot3D.d[i]++;}
          slot3D.dv[i]=2;
        }
      }
      if(slot3D.ds[i]!=0){
        slot3D.d[i]+=slot3D.dv[i];
        slot3D.d[i]%=1000;
        if(slot3D.ds[i]==2&&slot3D.dv[i]==2&&(slot3D.d[i]%100)==0){
          slot3D.ds[i]=0;
          slot3D.dv[i]=0;
        }
      }
    }
    if(slot3D.ds[0]==0 && slot3D.ds[1]==0 && slot3D.ds[2]==0){
      slot3D.status=0;
      if(slot3D.d[0]==700 && slot3D.d[1]==700 && slot3D.d[2]==700){
        slot3D.result.innerHTML="大あたり";
      }else if(slot3D.d[0]==slot3D.d[1] && slot3D.d[1]==slot3D.d[2]){
        slot3D.result.innerHTML="あたり";
      }else if(slot3D.d[0]==slot3D.d[1] || slot3D.d[1]==slot3D.d[2] || slot3D.d[0]==slot3D.d[2]){
        slot3D.result.innerHTML="おしい";
      }else{
        slot3D.result.innerHTML="はずれ";
      }
    }
    for(let i=0;i<slot3D.g.length;i++){
      slot3D.g[i].rotation.set(-Math.PI/500*slot3D.d[i],0,0);
    }
  }

  //png画像を返す
  function CreateTextPng(){
    let can=document.createElement("canvas");
    can.width='800';
    can.height='80';
    let ctx=can.getContext("2d", {willReadFrequently:true});
    let family=
      'Verdana,Roboto,"Droid Sans","游ゴシック",YuGothic,"メイリオ",Meiryo,'+
      '"ヒラギノ角ゴ ProN W3","Hiragino Kaku Gothic ProN","MS Pゴシック",sans-serif';
    ctx.font="80px "+family;
    ctx.textBaseline="ideographic";
    ctx.textAlign="center";
    //透明にする
    //ctx.globalCompositeOperation = 'destination-out';
    ctx.globalCompositeOperation = 'source-over';
    ctx.fillStyle="rgb(255,255,255)";
    ctx.fillRect(0,0,can.width,can.height);
    //通常描画にする
    ctx.globalCompositeOperation = 'source-over';
    ctx.fillStyle="rgb(0,0,0)";
    for(let i=0;i<10;i++){
      ctx.save();
      ctx.rotate(-Math.PI/2);//反時計回りが正、ラジアン単位、回転中心は(0,0)左上
      ctx.translate(-80,0);
      ctx.fillText(i,40,i*80+80);
      ctx.restore();
    }
    let png=can.toDataURL('image/png');
    return png;
  }

</script>

</head>
<body>

  <h3 class="diag"></h3>
  <p class="normal">
    <br>
    <canvas id="can" style="width:800px; height:480px; max-width:calc(100vw - 32px);max-height:calc((100vw - 32px) * 480 / 800);-ms-touch-action:none;touch-action:none;cursor:grab;"></canvas>
    <br>
  </p>
  <div style="display:flex;flex-wrap:wrap;justify-content:center;width:100%;max-width:800px;">
    <div style="width:20%;text-align:center;"><a class="slot3DB" id="bstop1">Stop</a></div>
    <div style="width:20%;text-align:center;"><a class="slot3DB" id="bstop2">Stop</a></div>
    <div style="width:20%;text-align:center;"><a class="slot3DB" id="bstop3">Stop</a></div>
  </div>
  <div style="width:100%;max-width:800px;text-align:center;"><a class="slot3DB slot3DS" id="bstart">Start</a></div>
  <div id="slotr" style="width:100%;max-width:360px;font-size:32px;color:red;font-weight:bold;"> </div>

</body>
</html>