Search code examples
javascriptcloneaframe

how do i clone a random object from a list and make it visible using A-frame.js


I'm using Javascript and A-frame. I want to be able to get a random block from the list of blocks and I want it to move down by 1. I can't seem to get any of the blocks to appear randomly on the load current. Below is the code I created to make my tetris board but it's currently giving me slight issues.

I am getting the error

  "message": "Uncaught SyntaxError: Unexpected end of input",

I'm not sure what that means?

var zigZag = document.querySelector("#zigZag");

var fork = document.querySelector("#fork");

var box = document.querySelector("#box");

var line = document.querySelector("#line");

var plane = document.querySelector("#plane");

//timer
// let hour = 0;
// let minute = 0;
// let second = 0;
// let millisecond = 0;

// let cron;

var newBlock, blocks, element, blocksOnPlane, planeBlock;

// Describe this function...

newBlock = [];

blocks = [zigZag, fork, line, box];

blocksOnPlane = [];
document.addEventListener("load", start);

function start() {
  while (true) {
    new Promise((res) => setTimeout(() => res(chooseBlock()), 10));
    console.log(newBlock.length);
    for (var element_index in newBlock) {
      element = newBlock[element_index];
      moveBlock(element);
      plane.detectCollisionsWith(newBlock[element_index], function (
        collidedObject
      ) {
        newBlock = collidedObject;
        newBlock.object3D.position.y = 0;
        blocksOnPlane.unshift(planeBlock);
        newBlock.shift();
      });
      // for (var element_index in blocksOnPlane) {
      //     element = blocksOnPlane[element_index];
      //     newBlock.detectCollisionsWith(planeBlock, function (collidedObject) {
      //         element.setY(element.getY() + 0)
      //         planeBlock = collidedObject;
      //         blocksOnPlane.unshift(planeBlock);
      //         newBlock.shift();

      //     })
      // }
    }
  }
}


document.addEventListener("keydown", function (event) {
  if (event.keyCode === 37) {
    element.object3D.position.x += -1;
  }
  //top
  else if (event.keyCode === 38) {
    element.object3D.position.x += 1;
  }
  //right
  else if (event.keyCode === 39) {
    element.object3D.position.x += 1;
  }
  //bottom
  else if (event.keyCode === 40) {
    element.object3D.position.x += -1;
  }
});

function moveBlock(element) {
  document.createTimer("timer1", 1 * 1000, function () {
    element.object3D.position.y += -1;
  });
}

function listsGetRandomItem(list, remove) {
  var x = Math.floor(Math.random() * list.length);
  if (remove) {
    return list.splice(x, 1)[0];
  } else {
    return list[x];
  }
}

// Describe this function...
function chooseBlock() {
  var tetrisBlock = document.clone(listsGetRandomItem(blocks, false));
  tetrisBlock.visible = true;
  tetrisBlock.object3D.position.x = 0;
  tetrisBlock.object3D.position.y = 7.5;
  tetrisBlock.object3D.position.z = -14;
  newBlock.unshift(tetrisBlock);
}
<script src="https://aframe.io/releases/1.2.0/aframe.min.js"></script>
<!DOCTYPE html>

    <button type="button" name="start">start</button>
    <button type="button" name="pause">pause</button>
    <button type="button" name="reset">reset</button>
    <a-scene>
      <a-entity
        id="zigZag"
        collide-with-player=""
        hatch-type-shape=""
        data-hatch-type="hatch-type-empty"
        data-icon-type="rectangle"
        physics-scale-fix=""
        hatch-physics=""
        shadow="cast: false; receive: false"
        hatch-cursor-event-detection="enableCursorDetection: true"
        position="-4.0825353394644335 5.15966087923683 -17.160361366799766"
        color="#d5b949"
        class="clickable"
        <!--
        visible="true"
        --
      >
        ><a-box
          id="line6"
          collide-with-player=""
          hatch-type-shape=""
          geometry="primitive: box"
          data-hatch-type="hatch-type-shape"
          data-icon-type="box"
          physics-scale-fix=""
          hatch-physics=""
          shadow=""
          hatch-cursor-event-detection="enableCursorDetection: true"
          position="5.039535339464433 3.3480091207631695 3.1603613667997656"
          color="#f1eaa3"
          material="color: #f1eaa3"
          class="clickable"
          scale="2 1 1"
          rotation="0 0 89.99999999999999"
          child-test="true"
        ></a-box
        ><a-box
          id="line5"
          collide-with-player=""
          hatch-type-shape=""
          geometry="primitive: box"
          data-hatch-type="hatch-type-shape"
          data-icon-type="box"
          physics-scale-fix=""
          hatch-physics=""
          shadow=""
          hatch-cursor-event-detection="enableCursorDetection: true"
          position="4.137505339464433 4.44454912076317 3.1603613667997656"
          color="#f1eaa3"
          material="color: #f1eaa3"
          class="clickable"
          scale="2 1 1"
          rotation="0 0 89.99999999999999"
          child-test="true"
        ></a-box
        ><a-box
          id="line4"
          collide-with-player=""
          hatch-type-shape=""
          geometry="primitive: box"
          data-hatch-type="hatch-type-shape"
          data-icon-type="box"
          physics-scale-fix=""
          hatch-physics=""
          shadow=""
          hatch-cursor-event-detection="enableCursorDetection: true"
          position="4.636015339464434 3.91959912076317 3.1603613667997656"
          color="#f1eaa3"
          material="color: #f1eaa3"
          class="clickable"
          scale="2 1 1"
          rotation="0 0 0"
          child-test="true"
        ></a-box
      ></a-entity>
      <a-entity
        id="fork"
        collide-with-player=""
        hatch-type-shape=""
        data-hatch-type="hatch-type-empty"
        data-icon-type="rectangle"
        physics-scale-fix=""
        hatch-physics=""
        shadow="cast: false; receive: false"
        hatch-cursor-event-detection="enableCursorDetection: true"
        position="1.158747821160648 5.997102835551197 -14.335538215901849"
        color="#617ec2"
        class="clickable"
        rotation="0 0 0"
        scale="1 1 1"
        visible="true"
        ><a-box
          id="line3"
          collide-with-player=""
          hatch-type-shape=""
          geometry="primitive: box"
          data-hatch-type="hatch-type-shape"
          data-icon-type="box"
          physics-scale-fix=""
          hatch-physics=""
          shadow=""
          hatch-cursor-event-detection="enableCursorDetection: true"
          position="-1.158747821160648 1.502897164448803 0.33553821590184896"
          color="#a3eaf1"
          material="color: #a3eaf1"
          class="clickable"
          scale="3 1.0000000000000002 1"
          rotation="0 0 89.99999999999999"
          child-test="true"
        ></a-box
        ><a-box
          id="line2"
          collide-with-player=""
          hatch-type-shape=""
          geometry="primitive: box"
          data-hatch-type="hatch-type-shape"
          data-icon-type="box"
          physics-scale-fix=""
          hatch-physics=""
          shadow=""
          hatch-cursor-event-detection="enableCursorDetection: true"
          position="-0.60527 1.44434 0.33554"
          color="#a3edf1"
          material="color: #a3edf1"
          class="clickable"
          scale="2 1 1"
          rotation="0 0 0"
          child-test="true"
        ></a-box
      ></a-entity>
      <a-box
        hatch-type-shape=""
        data-icon-type="box"
        id="box"
        shadow=""
        displayname="Box"
        position="0 9.09 -14"
        rotation="0 0 0"
        color="#4CC3D9"
        side="front"
        hatch-physics=""
        data-hatch-type="hatch-type-shape"
        material="color: #4CC3D9; side: front"
        geometry="primitive: box"
        physics-scale-fix=""
        hatch-cursor-event-detection="enableCursorDetection: true"
        collide-with-player=""
        class="clickable"
        scale="2 2 1"
        child-test="true"
        visible="true"
      ></a-box>
      <a-box
        id="line"
        collide-with-player=""
        hatch-type-shape=""
        geometry="primitive: box"
        data-hatch-type="hatch-type-shape"
        data-icon-type="box"
        physics-scale-fix=""
        hatch-physics=""
        shadow=""
        hatch-cursor-event-detection="enableCursorDetection: true"
        position="0 7.5 -14"
        color="#a3f1af"
        material="color: #a3f1af"
        class="clickable"
        scale="4 1 1"
        visible="true"
      ></a-box>
      <a-entity
        id="Ground"
        ground="dressing: none; groundSize: 100; ground: flat; groundTexture: none; grid: 1x1; groundColor: #333343; flatShading: true; gridColor: #B5B5B5"
        data-hatch-type="hatch-type-ground"
        data-is-droppable="false"
        data-icon-type="terrain"
        data-is-expandable="false"
        position="0 0 0"
        color="#62E9F7"
        visible="true"
        ><a-entity
          class="environment"
          position=""
          visible=""
          light=""
          data-not-selectable="true"
          data-hide-model="true"
          data-icon-type="light-hemisphere"
        ></a-entity
        ><a-entity
          class="environment"
          position=""
          light=""
          data-hide-model="true"
          visible="true"
          data-icon-type="light-directional"
        ></a-entity
        ><a-entity
          rotation=""
          visible=""
          data-not-selectable="true"
          class="environmentGround environment"
          scale=""
          shadow=""
        ></a-entity
        ><a-entity
          data-not-selectable="true"
          visible=""
          class="environmentDressing environment"
        ></a-entity
      ></a-entity>
      <a-plane
        id="plane"
        collide-with-player=""
        hatch-type-shape=""
        geometry="primitive: plane"
        data-hatch-type="hatch-type-shape"
        data-icon-type="plane"
        physics-scale-fix=""
        hatch-physics=""
        shadow=""
        hatch-cursor-event-detection=""
        position="-0.38406 0 -16.5359"
        color="#975c26"
        material="color: #975c26; shader: flat; side: back"
        rotation="90 0 0"
        scale="17.9 12.9 1"
        visible="true"
      ></a-plane>
    </a-scene>


Solution

  • Not sure where did you get document.clone(element) as the document object doesn't seem to have a cloning utility.

    There is a element.cloneNode() method which works fine - we can use it within a custom component, which will also handle the entity lifecycle:

    <script src="https://aframe.io/releases/1.2.0/aframe.min.js"></script>
    <script>
      // register new component
      AFRAME.registerComponent("foo", {
        // on init
        init: function() {
          // grab the button, and the entity
          const btn = document.querySelector("button")
          const tmplate = document.getElementById("template")
    
          // when clicked
          btn.addEventListener("click", evt => {
            // clone the node
            const new_element = tmplate.cloneNode(true);
            // make it smaller, and move it a bit
            new_element.setAttribute("scale", "0.2 0.2 0.2")
            new_element.setAttribute("position", {
              x: Math.random() * 6 - 3,
              y: 0.5,
              z: -1
            })
            // append it to the scene
            this.el.appendChild(new_element)
          })
        }
      })
    </script>
    <button style="z-index: 9999; position: fixed;">COPY</button>
    <a-scene foo>
      <a-entity id="template">
        <a-box position="-1 0.5 -3" rotation="0 45 0" color="#4CC3D9"></a-box>
        <a-sphere position="0 1.25 -5" radius="1.25" color="#EF2D5E"></a-sphere>
        <a-cylinder position="1 0.75 -3" radius="0.5" height="1.5" color="#FFC65D"></a-cylinder>
        <a-plane position="0 0 -4" rotation="-90 0 0" width="4" height="4" color="#7BC8A4"></a-plane>
      </a-entity>
      <a-sky color="#ECECEC"></a-sky>
    </a-scene>


    You can move it down with interval timeouts, or a tick / throttledTick handler. This could be done within a system, which would manage created entities (moving down, collisions).

    A simple "moving down" version could look like this:

    <script src="https://aframe.io/releases/1.2.0/aframe.min.js"></script>
    <script>
      //register system
      AFRAME.registerSystem("foo", {
        init: function() {
          // array of all registered elements
          this.elements = [];
          // set up the throttled version
          this.tick = AFRAME.utils.throttleTick(this.tick, 250, this);
        },
        register: function(el) {
          // push new element to the registered array
          this.elements.push(el)
        },
        tick: function() {
          // loop through all of the elements, and move them down
          for (el of this.elements) {
            const pos = el.object3D.position // this is more efficient than getAttribute("position")
            pos.y = (pos.y < 0) ? 2 : pos.y - 0.1; // use setAttribute() if you want getAttribute() to work
          }
        }
      })
    
      // register new component
      AFRAME.registerComponent("foo", {
        // on init
        init: function() {
          //  move it a bit, and make it visible
          this.el.setAttribute("position", {
            x: Math.random() * 6 - 3,
            y: 0.5,
            z: -2
          })
          this.el.setAttribute("visible", "true")
    
          // register the element in the system
          this.system.register(this.el)
        }
      })
    
      AFRAME.registerComponent("inserter", {
        init: function() {
          // grab the button, and the entity
          const btn = document.querySelector("button")
          const tmplate = document.getElementById("template")
    
          // when clicked
          btn.addEventListener("click", evt => {
            // clone the node
            const new_element = tmplate.cloneNode(true);
            new_element.setAttribute("foo", "")
            // append it to the scene
            this.el.appendChild(new_element)
          })
        }
      })
    </script>
    <button style="z-index: 9999; position: fixed;">NEW</button>
    <a-scene inserter>
      <a-entity id="template" visible="false" scale="0.2 0.2 0.2">
        <a-box position="-1 0.5 -3" rotation="0 45 0" color="#4CC3D9"></a-box>
        <a-sphere position="0 1.25 -5" radius="1.25" color="#EF2D5E"></a-sphere>
        <a-cylinder position="1 0.75 -3" radius="0.5" height="1.5" color="#FFC65D"></a-cylinder>
        <a-plane position="0 0 -4" rotation="-90 0 0" width="4" height="4" color="#7BC8A4"></a-plane>
      </a-entity>
      <a-sky color="#ECECEC"></a-sky>
    </a-scene>


    Sooner or later You'll probably run into performance issues (once there are several nodes) and it would be best to dive into mesh instancing (or at least check the performance guidelines)