乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      基于 HTML5 的 WebGL 3D 版俄羅斯方塊

       小世界的野孩子 2020-02-14

      前言

      摘要:2D 的俄羅斯方塊已經(jīng)被人玩爛了,突發(fā)奇想就做了個 3D 的游戲機(jī),用來玩俄羅斯方塊。。。實現(xiàn)的基本想法是先在 2D 上實現(xiàn)俄羅斯方塊小游戲,然后使用 3D 建模功能創(chuàng)建一個 3D 街機(jī)模型,最后將 2D 小游戲貼到 3D 模型上。
      (ps:最后拓展部分實現(xiàn)將視頻與3D模型的結(jié)合)


      http://www./demo/...

      首先,先完成 2D 小游戲

      在查看官方文檔的過程中,了解到 HT 的組件參數(shù)都是保存在 ht.DataModel() 對象中,將數(shù)據(jù)模型在視圖中進(jìn)行加載后呈現(xiàn)各種特效。

      gameDM = new ht.DataModel(); //初始化數(shù)據(jù)模型
      g2d = new ht.graph.GraphView(gameDM); //初始化2d視圖
      g2d.addToDOM(); //在頁面上創(chuàng)建視圖

      開始游戲模型的創(chuàng)建

      • 第一步,先讓我們?yōu)橛螒騽?chuàng)建一個框體,為游戲限定范圍。在文檔中,我們可以知道 ht.Node 是 graphView 呈現(xiàn)節(jié)點圖元的基礎(chǔ)類,除了可以顯示圖片外,還能支持多種預(yù)定義的圖形。所以我打算使用該類創(chuàng)建4個長方形,用它們來做游戲的范圍限定。
      var lineNode = new ht.Node();
      lineNode.s({
          "shape": "rect", //矩形
          "shape.background": "#D8D8D8", //設(shè)置底色
          "shape.border.width": 1, //邊框?qū)挾?1
          "shape.border.color": "#979797"  // 邊框顏色
      });
              
      lineNode.setPosition(x, y); // 設(shè)置圖元展示位置,左上角為0, 0 圖元坐標(biāo)指向它們的中心位置
      lineNode.setSize(width, height); // 設(shè)置圖元寬、高屬性
              
      gameDM.add(lineNode); // 將設(shè)置好后的圖元信息加入數(shù)據(jù)模型中

      設(shè)置 x:552, y:111, width:704, height:22 后我們可以得到第一個圖形:

      邊框的top已經(jīng)有了,現(xiàn)在讓我們再創(chuàng)建另外三條邊來組成一個框體:

      x:211, y:562, width:22, width:880
      x:893, y:562, width:22, width:880
      x:552, y:1013, width:704, width:22

      得到效果如下:

      邊框基本完成,在瀏覽的過程中發(fā)現(xiàn)4個邊框可以被拖拽。接下來對邊框初始化的方法進(jìn)行調(diào)整:

      lineNode.s({
          "shape": "rect", //矩形
          "shape.background": "#D8D8D8", //設(shè)置底色
          "shape.border.width": 1, //邊框?qū)挾?1
          "shape.border.color": "#979797",  // 邊框顏色
          "2d.editable" : false, // 是否可編輯
          "2d.movable" : false, //是否可移動
          "2d.selectable" : false //是否可選中
      });
      • 生成方塊,我的想法是生成多個正方形,將它們組合成我們需要的圖形,通過坐標(biāo)的計算來將它們擺放在相應(yīng)的位置:

      方塊生成后,開始對圖形進(jìn)行旋轉(zhuǎn)操作。這其中有兩個方案,第一種是將圖形的翻轉(zhuǎn)后的圖形坐標(biāo)按順序保存在數(shù)組中,每次改變形狀時取數(shù)組中的前一組或后一組坐標(biāo)來進(jìn)行改變;第二種是使用 ht.Block() 對象將對應(yīng)的圖元組合成一個整體,在變形時只需按對應(yīng)的方向選擇 90° 即可。在這里,我選擇了第二中方式,代碼如下:

      function createUnit(x, y) {
          var node = new ht.Node();
          node.s({
              "shape": "rect",
              "shape.background": "#D8D8D8",
              "shape.border.width": 1,
              "shape.border.color": "#979797"
          });
          node.setPosition(x, y);
          node.setSize(44, 44);
          gameDM.add(node);
          return node;
      }
      
      var block = new ht.Block();
      block.addChild(createUnit(552, 133));
      block.addChild(createUnit(552, 89));
      block.addChild(createUnit(508, 133));
      block.addChild(createUnit(596, 133));
      block.setAnchor(0.5, 0.75); //設(shè)置組合的中心位置, 旋轉(zhuǎn)時將安裝此點來進(jìn)行
      block.setPosition(552, 144);

      Block 設(shè)置中心點 Anchor 如下圖:

      在設(shè)置旋轉(zhuǎn)時,只需使用 setRotation 函數(shù)對 block 進(jìn)行旋轉(zhuǎn)即可:

      block.setRotation(Math.PI*rotationNum/2); //rotationNum 是一個計數(shù)器,保存已經(jīng)旋轉(zhuǎn)次數(shù),保證每次都是在上一次的基礎(chǔ)上旋轉(zhuǎn)90°
      • 方塊有了,現(xiàn)在就該讓它動起來了。設(shè)置定時器,使方塊每隔一段時間下降一定距離,并添加鍵盤的監(jiān)聽事件,以此實現(xiàn) w:翻轉(zhuǎn)、s:左移動、d:右移動、s:下移的操作,同時為了不使方塊移動出邊界,在每次位移時都將對坐標(biāo)進(jìn)行一次驗證:
      var offset = 44;
      var intervalTime = 1000;
      var topX = 552;
      var topY = 111;
      
      var leftSize = 211,
          rightSize = 882,
          bottomSize = 1002;
      
      var rotationNum = 0;
      window.addEventListener('keydown', function(e){
          var index = 0;
          var maxY = null;
          if(e.keyCode == 87){
              // up w
              rotationNum ++;
              block.setRotation(Math.PI*rotationNum/2);
              if (!checkRotation(block)) {
                  rotationNum --;
                  block.setRotation(Math.PI*rotationNum/2);
              }
          } else if (e.keyCode == 65) {
              // left a
              moveBlock('x', -offset, block);
          } else if (e.keyCode == 68) {
              // right d
              moveBlock('x', offset, block);
          } else if(e.keyCode == 83){
              // down s
              moveBlock('y', offset, block);
          }
      }, false);
      
      setInterval(function(){
          if(!moveBlock("y", offset, block)){
              //無法進(jìn)行位移,創(chuàng)建新的方塊
              rotationNum = 0; //方塊翻轉(zhuǎn)次數(shù)歸0
              block = createNode(blockType); //生成新的方塊
              blockType = parseInt(Math.random()*100%5); //下一次生成的方塊圖形
          }
      }, intervalTime); //執(zhí)行間隔
      
      //移動方塊,移動成功時返回:true,無法移動時返回:false
      function moveBlock(axis, offset, block){
      
          // 移動方塊
          var ids = [];
          var yindexs = [];
          var indexArr = new Array();
          for(var i = 0; i < block.size(); i ++){
              var childNode = block.getChildAt(i);
              var childx = childNode.getPosition().x;
              var childy = childNode.getPosition().y;
              if (yindexs.indexOf(childy) == -1) {
                  yindexs.push(childy);
              }
              
              if(axis === 'x'){
                  childx += offset;
              }else if (axis === 'y') {
                  childy += offset;
              }
      
                      // 驗證方塊的移動是否超出邊界
              if(childx < leftSize || childx > rightSize || childy > bottomSize){
                  return false;
              }
      
              var obj = new Object();
              obj.x = childx;
              obj.y = childy;
      
              indexArr.push(obj);
              ids.push(childNode.getId());
          }
      
          //判斷圖形位移過程中是否與其他方塊觸碰
          for(var j = 0; j < yindexs.length; j ++){
              var indexY = yindexs[j];
              if (axis === 'y') {
                  indexY += offset;
              }
              //getDatasInRect 方法能獲取到一個范圍中的所有圖元信息
              var nodeList = g2d.getDatasInRect({x:233, y:indexY, width:638, height:2}, true, false);
      
              if(nodeList.length > 0){
                  // 觸碰
                  for(var i = 0; i < nodeList.length; i++){
                      var x = nodeList.get(i).getPosition().x;
                      var y = nodeList.get(i).getPosition().y;
                      
                      var id = nodeList.get(i).getId();
                      if (ids.indexOf(id) > -1) {
                          // 位移的圖元
                          continue;
                      }
      
      
                      for (var k = 0; k < indexArr.length; k++) {
                          var obj = indexArr[k];
                          if (obj.x === x && obj.y === y){
                              // 該停下了
                              return false;
                          }
                      }
      
                  }
              }
          }
          
          var blockX = block.getX();
          var blockY = block.getY();
      
          if (axis === 'x') {
              blockX += offset;
          }else if (axis === 'y') {
              blockY += offset;
          }
                  // 方塊移動到新的坐標(biāo)
          block.setPosition(blockX, blockY);
          return true;
      }
      
      // 驗證方塊是否可以進(jìn)行翻轉(zhuǎn)
      function checkRotation(block){
          for(var i = 0; i < block.getChildren().length; i++){
              var node = block.getChildAt(i);
              var childx = node.getPosition().x;
              var childy = node.getPosition().y;
                      
                      // 判斷翻轉(zhuǎn)后的圖形是否會超出范圍
              if(childx < leftSize || childx > rightSize || childy > bottomSize){
                  return false;
              }
      
          }
      
          return true;
      }
      • 在完成方塊的位移與變形之后,我們的小游戲就只差最后一步了:對填充滿的方塊進(jìn)行消除。在開始的時候,我們就知道所有的信息都是保存在數(shù)據(jù)模型當(dāng)中,所以我們要消除方塊。只需要將它們從數(shù)據(jù)模型中刪除即可,實現(xiàn)代碼如下:
      function deleteBlock(block){
          // 消除已經(jīng)填充滿的方格
          var yindexs = []; // 要判斷的y軸坐標(biāo)
          var num = 0;
          for(var i = 0; i < block.size(); i ++){
              var childNode = block.getChildAt(i);
              var childy = childNode.getPosition().y;
              var nodeList = g2d.getDatasInRect({x:233, y:childy, width:638, height:2}, true, false);
              if (nodeList.length == 15) {
                  for(var i = 0; i < nodeList.length; i++){
                      gameDM.remove(nodeList.get(i)); // 在數(shù)據(jù)模型中移除對應(yīng)的圖元
                  }
                  num ++;
                  yindexs.push(childy);
              }
          }
      
          if (yindexs.length > 0) {
              for(var i = 0 ; i < yindexs.length; i++){
                          // 將被消除圖元上方的圖元進(jìn)行組合,并整體向下移動一個位置
                  var yindex = yindexs[i];
                  var h = yindex - 133 - offset;
                  var moveList = g2d.getDatasInRect({x:233, y:133, width:638, height:h}, true, false);
                  var mblock = new ht.Block();
                  for(var i = 0; i < moveList.size(); i++){
                      mblock.addChild(moveList.get(i));
                  }
                  moveBlock('y', offset, mblock);
              }
          }
      }
      • 到此,一個簡單的俄羅斯方塊小游戲就實現(xiàn)了。當(dāng)然,這個游戲還有很多可以拓展的地方,比如:更多的方塊類型,游戲分?jǐn)?shù)的統(tǒng)計,下一步預(yù)測窗體,游戲背景修改等。這些先不考慮,我們先開始下一步。

      創(chuàng)建 3D 模型

      在 3D 建模文檔中了解到,HT 通過一個個三角形來組合模型。

      • 首先,先將網(wǎng)絡(luò)上查找到的街機(jī)模型進(jìn)行拆分,將其中的各個模塊拆分成三角形面:

      如圖所示,將0所在位置設(shè)置為原點(0,0,0),我們打開畫圖工具根據(jù)標(biāo)尺大概估計出每個坐標(biāo)相對原點的位置,將計算好的坐標(biāo)數(shù)組傳入 vs 中,同時在is頂點索引坐標(biāo)中將每個三角圖形的組合傳入其中:

      ht.Default.setShape3dModel('damBoard', { // 為新模型起名
          vs: [
              0, 0, 0, //0
              0.23, 0, 0,
              0.23, 0.27, 0,
              0.27, 0.28, 0, //3
              0.27, 0.32, 0,
              0.20, 0.33, 0,
              0.18, 0.51, 0, // 6
              0.27, 0.57, 0,
              0.27, 0.655, 0,
              0.20, 0.67, 0, // 9
              0, 0.535, 0
          ],
          is: [
              0, 1, 2,
              0, 2, 5,
              2, 3, 4,
              4, 2, 5,
              5, 0, 10,
              10, 5, 6,
              6, 7, 8,
              8, 6, 9,
              9, 10, 6
          ]
      });

      與 2D 一樣,我們創(chuàng)建一個 ht.Node() 的基礎(chǔ)圖元,類型設(shè)置為我們新注冊的3D模型名稱:

      dataModel = new ht.DataModel();
      g3d = new ht.graph3d.Graph3dView(dataModel);
      g3d.addToDOM();
      
      var node = new ht.Node();
      node.s({
          'shape3d': 'damBoard',
          'shape3d.reverse.flip': true,
          '3d.movable': false,
          '3d.editable': false,
          '3d.selectable': false
      });
      node.p3([0, 20, 0]);
      node.s3([100, 100, 100]);
      dataModel.add(node);

      已經(jīng)有個側(cè)邊了,我們可以將坐標(biāo)系延z軸移動一定距離后得到另一個側(cè)邊的坐標(biāo)數(shù)組同時再根據(jù)沒個面的不同,分別設(shè)置 is 數(shù)組,將所有的面組合起來后,我們就將初步得到一個街機(jī)模型:

      vs: [
          0, 0, 0, //0
          0.23, 0, 0,
          0.23, 0.27, 0,
          0.27, 0.28, 0, //3
          0.27, 0.32, 0,
          0.20, 0.33, 0,
          0.18, 0.51, 0, // 6
          0.27, 0.57, 0,
          0.27, 0.655, 0,
          0.20, 0.67, 0, // 9
          0, 0.535, 0,
      
          0, 0, 0.4, //11
          0.23, 0, 0.4,
          0.23, 0.27, 0.4,
          0.27, 0.28, 0.4, //14
          0.27, 0.32, 0.4,
          0.20, 0.33, 0.4,
          0.18, 0.51, 0.4, // 17
          0.27, 0.57, 0.4,
          0.27, 0.655, 0.4,
          0.20, 0.67, 0.4, // 20
          0, 0.535, 0.4,
      ]

      • 模型不夠美觀,我們可以給模型的每個面進(jìn)行貼圖,參考文檔中對模型 uv 參數(shù)的說明,我們可以知道 uv 對應(yīng)的是模型中每個頂點在圖片中的偏移量,圖片的左上角為(0, 0)右下角為(1,1), 以此我們可以為每個面設(shè)置貼圖。如:

      ht.Default.setShape3dModel('damBoard', {
          vs: vsArr,
          is: isArr,
          uv: [
              0, 1,
              0.81, 1,
              0.81, 0.42,
              1, 0.4,
              1, 0.36,
              0.725, 0.34,
              0.65, 0.26,
              1, 0.16,
              1, 0.03,
              0.75, 0,
              0, 0.22,
              , ,
              , ,
              , ,
              , ,
              , ,
              , ,
              , ,
              , ,
              , ,
              , ,
              , ,
          ], //uv中要將is中有使用到的點的偏移量都進(jìn)行設(shè)值
          image: '/image/side1.jpg' //圖片地址
      });

      同理,為其他面也分別設(shè)置 uv,最終效果如下:

      • 3D 模型整體已經(jīng)建好了, 還需要給模型加上游戲按鈕。在官方文檔建模函數(shù)中,我們可以看到已經(jīng)有大量封裝完畢的圖形供我們使用。在這里我選擇使用 createRightTriangleModel 創(chuàng)建直角三角形的方法來創(chuàng)建操作按鈕,使用 createSmoothSphereModel 函數(shù)來創(chuàng)建開始按鈕:
      ht.Default.setShape3dModel('button', ht.Default.createRightTriangleModel(true, true));
      ht.Default.setShape3dModel('startButton', ht.Default.createSmoothSphereModel(20, 20, 0, Math.PI * 2, 0, Math.PI));

      根據(jù)注冊好的模型生成按鈕:

      createKeyboard('up', [21.5, 52.5, 26], [0, -Math.PI / 4, 0]);
      createKeyboard('down', [25.5, 51.75, 26], [0, Math.PI * 3 / 4, 0]);
      createKeyboard('left', [23.5, 52, 28], [0, Math.PI / 4, 0]);
      createKeyboard('right', [23.5, 52, 24], [0, Math.PI * 5 / 4, 0]);
              
      // 創(chuàng)建開始按鈕
      function createStartButton() {
          var node = new ht.Node();
          node.setTag('restart');
          node.s({
              'shape3d': 'startButton',
              'shape3d.reverse.flip': true,
              'shape3d.color': '#7ED321',
              '3d.movable': false,
              '3d.editable': false
          });
      
          node.p3([23.5, 52.5, 11]); // 按擺放位置
          node.s3([3, 3, 3]); // 按鈕放大倍數(shù)
      
          dataModel.add(node);
      }
      // 創(chuàng)建操作按鈕
      function createKeyboard(tag, p3, r3) {
          var node = new ht.Node();
          node.setTag(tag);
          node.s({
              'shape3d': 'button',
              'shape3d.reverse.flip': true,
              'shape3d.color': 'red',
              '3d.movable': false,
              '3d.editable': false
          });
      
          node.p3(p3); // 按擺放位置
          node.s3([1.5, 1.5, 1.5]); // 按鈕放大倍數(shù)
          node.r3(r3); // 將按鈕按Y軸旋轉(zhuǎn),已保存按鈕指向正確
          dataModel.add(node);
      }
      

      最終效果如下:

      • 將 2D 小游戲貼到3D模型上,在文檔中我們可以發(fā)現(xiàn) setImage 屬性不僅僅是只能設(shè)置正常的圖片,還可以使用它來注冊一個 canvas 圖形組件。而2D視圖可以通過 getCanvas() 來獲取畫布信息。
      ht.Default.setImage('gameScrn', g2d.getCanvas());
      ht.Default.setShape3dModel('scrn', {
          vs: vsArr,
          is: isArr,
          uv: scrnUV,
          image: 'gameScrn' // 將注冊的2d畫布信息當(dāng)成屏幕的圖片貼圖信息
      });
          
      // 設(shè)置 2d 的畫布大小
      g2d.getWidth = function () { return 1000; }
      g2d.getHeight = function () { return 600; }
      g2d.getCanvas().dynamic = true;//設(shè)置這個是為了讓canvas能動態(tài)顯示
              
      // 設(shè)置計時器,讓2d畫布上的每次改變都能及時的在3D模型上進(jìn)行展示
      setInterval(function () {
          node.iv(); // 每次改變都需要對街機(jī)模型進(jìn)行刷新,刷新時間為下一幀
          g2d.validateImpl(); // 立即對2D上的圖元進(jìn)行刷新
      }, 10);
              
      // 設(shè)置500毫秒后,縮放平移整個2D畫布以展示所有的圖元
      setTimeout(function () {
          g2d.fitContent(true);
      }, 500);
      

      效果如下:

      • 在 2D 畫布上,我們已經(jīng)為游戲添加了鍵盤事件,現(xiàn)在我們只需要為 3D 模型上的5個按鈕分別綁定對應(yīng)方法即可:
      g3d.mi(function (e) { // addInteractorListener 交互事件監(jiān)聽器的縮寫
      
          if (e.kind === 'clickData') { // 判斷是否為點擊事件
              var tag = e.data.getTag();
              if (tag === 'restart') {
                  gameAgain(node);
              }
              if (start) {
                  if (tag === 'up') {
                      block.setRotation(Math.PI * (1 + rotationNum) / 2);
                      rotationNum++;
                      if (!checkRotation(block)) {
                          // 邊緣變形限制
                          rotationNum--;
                          block.setRotation(Math.PI * rotationNum / 2);
                      }
                  } else if (tag === 'down') {
                      moveBlock('y', offset, block);
                  } else if (tag === 'left') {
                      moveBlock('x', -offset, block);
                  } else if (tag === 'right') {
                      moveBlock('x', offset, block);
                  }
              }
          }
      });

      到此基本完成了在3D街機(jī)上玩游戲的功能。


      http://www./demo/...

      拓展

      上面只是一個簡單的運(yùn)用,既然可以將 2D 的 canvas 貼到3D上,那么是否也可以將視頻貼上去呢。

      實現(xiàn)代碼如下:

      <video id="video1" width="270" autoplay src="3D交互.mp4" style="display:none"></video>
      
      var v = document.getElementById("video1");
      var node = new ht.Node();
      node.setSize(2200, 1100);
      gameDM.add(node);
      v.addEventListener('play', function () {
          var i = window.setInterval(function () {
              node.setImage(v);//將視頻截圖貼在圖元上
              g2d.validateImpl();//刷新2d畫布
              g3d.invalidateData(box);//刷新3d圖紙中的街機(jī)模型
              if (v.ended) {
                  clearInterval(i)
              }
          }, 20);
      }, false);

      總結(jié)

      在 3D 模型上的視頻播放給予了我很大的興趣。如果能將攝像頭的畫面轉(zhuǎn)移到對應(yīng)的 3D 場景中,那么我相信像一些日常的機(jī)房監(jiān)控,智能城市和智能樓宇中的視頻監(jiān)控將更加的便捷與直觀。

        本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
        轉(zhuǎn)藏 分享 獻(xiàn)花(0

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多