\n","publisher":{"@type":"Organization","name":"MML","url":"https://mml.io"}}MML - Metaverse Markup Language

MMLPositionMoveEvent extendsRemoteEvent

Received when a user moves after having entered the range of an m-position-probe.

<m-label y="5" z="-6" width="10" height="5" color="red" font-size="100">
</m-label>

<m-position-probe id="my-probe" z="3" range="5" y="1" debug="true" interval="100"></m-position-probe>
<script>
  const labelElement = document.querySelector("m-label")
  const positionProbe = document.querySelector("#my-probe");

  positionProbe.addEventListener("positionmove", ({ detail }) => {
    console.log(detail.documentRelative)

    const text = `User at
x: ${detail.documentRelative.position.x.toString().substring(0, 6)},
y: ${detail.documentRelative.position.y.toString().substring(0, 6)},
z: ${detail.documentRelative.position.z.toString().substring(0, 6)}`

    labelElement.setAttribute("content", text)
  })
</script>

detailisReadonly

detail: { documentRelative: PositionAndRotation connectionId: number }

Type declaration

  • documentRelative:  
  • PositionAndRotation

The location of the user relative to the target element.

  • connectionId:  
  • number

The unique numeric id of the connection that sent the event.

typeisReadonly

The **

type

** read-only property of the Event interface returns a string containing the event's type.

MDN Reference

Literal: positionmove

Examples

Move Check

<m-label y="5" z="-6" width="10" height="5" color="red">
</m-label>

<m-position-probe id="my-probe" z="3" range="5" y="1" debug="true" interval="500"></m-position-probe>

<script>
  const positionProbe = document.getElementById("my-probe");
  const labelElement = document.querySelector("m-label")

  function getShortenedPosition(position) {
    return {
      x: position.x.toString().substring(0, 6),
      y: position.y.toString().substring(0, 6),
      z: position.z.toString().substring(0, 6),
    };
  }

  let lastKnownPosition = { x: 0, y: 0, z: 0 };

  positionProbe.addEventListener("positionmove", ({ detail }) => {
    // This is because of javascript floating point precision
    const shortenedPosition = getShortenedPosition(detail.elementRelative.position);

    // This is to prevent the label from updating every frame if the position is the same
    if (JSON.stringify(lastKnownPosition) === JSON.stringify(shortenedPosition)) return;

    lastKnownPosition = shortenedPosition;

    const colors = ["black", "blue", "green", "white", "red", "purple"];

    let selectedColor = Math.floor(Math.random() * colors.length);

    // We don't want the color to be the same as the ground
    while (colors[selectedColor] === labelElement.getAttribute("color")) {
      selectedColor = Math.floor(Math.random() * colors.length);
    }

    labelElement.setAttribute("color", colors[selectedColor]);
  });
</script>

Rotation

<m-label y="5" z="-6" width="10" height="5" color="red" font-size="100">
</m-label>

<m-position-probe id="my-probe" z="3" range="5" y="1" debug="true" interval="100"></m-position-probe>
<script>
  const labelElement = document.querySelector("m-label")
  const positionProbe = document.querySelector("#my-probe");

  positionProbe.addEventListener("positionmove", ({ detail }) => {
    console.log(detail.documentRelative)

    const text = `User at
x: ${detail.documentRelative.rotation.x.toString().substring(0, 6)},
y: ${detail.documentRelative.rotation.y.toString().substring(0, 6)},
z: ${detail.documentRelative.rotation.z.toString().substring(0, 6)}`

    labelElement.setAttribute("content", text)
  })
</script>

Moving Object

<m-group x="3" y="4">
  <m-position-probe range="7" debug="true" id="my-probe" interval="100"></m-position-probe>
  <m-group id="user-presence-holder"></m-group>
</m-group>

<script>
  const connectedUsers = new Map();
  const userPresenceHolder = document.getElementById("user-presence-holder");
  const positionProbe = document.getElementById("my-probe");

  function getOrCreateUser(connectionId, position, rotation) {
    const user = connectedUsers.get(connectionId);
    if (user) {
      user.position = position;
      user.rotation = rotation;
      return user;
    }
    const userCube = document.createElement("m-cube");
    userCube.setAttribute("collide", false);
    userCube.setAttribute("width", 0.25);
    userCube.setAttribute("height", 0.25);
    userCube.setAttribute("depth", 0.25);
    userCube.setAttribute("color", `#${Math.floor(Math.random() * 0xffffff).toString(16).padStart(6, '0')}`);
    userPresenceHolder.append(userCube);
    const newUser = {
      cube: userCube,
      position,
      rotation,
    };
    connectedUsers.set(connectionId, newUser);
    return newUser;
  }

  function clearUser(connectionId) {
    const user = connectedUsers.get(connectionId);
    if (!user) return;
    user.cube.remove();
    connectedUsers.delete(connectionId);
  }

  function setCubePosition(connectionId, position, rotation) {
    const user = getOrCreateUser(connectionId, position, rotation);
    user.cube.setAttribute("x", position.x);
    user.cube.setAttribute("y", position.y + 3);
    user.cube.setAttribute("z", position.z);
    user.cube.setAttribute("rx", rotation.x);
    user.cube.setAttribute("ry", rotation.y);
    user.cube.setAttribute("rz", rotation.z);
  }

  window.addEventListener("disconnected", (event) => {
    const { connectionId } = event.detail;
    clearUser(connectionId);
  });

  positionProbe.addEventListener("positionenter", (event) => {
    const { connectionId, elementRelative, documentRelative } = event.detail;
    setCubePosition(connectionId, elementRelative.position, elementRelative.rotation);
  });

  positionProbe.addEventListener("positionmove", (event) => {
    const { connectionId, elementRelative, documentRelative } = event.detail;
    /*
       It's possible to receive an update without an explicit enter event if this user is already in the range of
       this element when the document is reloaded. In this case, we need to create the user as if this is an
       enter event.
      */
    setCubePosition(connectionId, elementRelative.position, elementRelative.rotation);
  });

  positionProbe.addEventListener("positionleave", (event) => {
    const { connectionId } = event.detail;
    clearUser(connectionId);
  });
</script>