Expert Tutorial


SumerianRunner Events


20 minutes

Posted on: October 25, 2017

Learn Sumerian
SumerianRunner Events

Tags

SumerianRunner
events
eventlistener
intersection marker

In this tutorial you will learn about:

SumerianRunner Event Listeners

This tutorial shows you how to use Event Listeners in a 3D environment with the sumerianRunner Event Listeners. The sumerianRunner Event Listeners work like regular DOM Event Listeners (and even contain the standard Events). However, they also add some very useful items and capabilities: a picked entity, a point of intersection in the 3D scene, and the depth of the intersection.

You’ll learn about:

  • Event Listeners
  • sumerianRunner Event Listeners
  • Events
  • Intersection marker

Prerequisites

Before you begin, you should have completed the following tasks and tutorials:

Warning about Performance

Using a sumerianRunner Event Listeners is considerably slower than using regular DOM Event Listeners!

Always use the regular Events if you can. For example, it’s not a good idea to attach a mousemove event unless you really have to, because that will fire all the picking and intersection logic as soon as the cursor is moved. Again, remember that sumerianRunner Events do everything that regular Events do, plus a lot more. We’ll show this in this tutorial.

Step 1: Set up the Scene

  1. From the Dashboard, create a scene from the Default Template.
  2. Add several Primitive entities, or use any existing scene that contains a few entities.

Step 2: Add Listeners to Entities

Add an Empty entity, attach a Script component, and add an empty script. We’ll start by adding a simple Event Listener in the setup function, in the same way we do in the DOM Event Listeners tutorial.

var setup = function(args, ctx) {
    ctx.runnerListeners = {
        click: function(evt) {
            console.log('Click event', evt);
        }
    };

    for (var l in ctx.runnerListeners) {
        ctx.world.sumerianRunner.addEventListener(l, ctx.runnerListeners[l]);
    }
};

// Remember to clean up after ourselves
var cleanup = function(args, ctx) {
    for (var l in ctx.runnerListeners) {
        ctx.world.sumerianRunner.removeEventListener(l, ctx.runnerListeners[l]);
    }
};

Click around in your scene and explore the printed Event details by using the developer console in your browser. We have all the info we need, but we’ll do something a little more visual with it in a minute.

The following is console-printed picking Event information.

Step 3: Add a Small GUI

We’ll create an HTML entity to display the intersection information. Create an HTML entity and uncheck the Move with Transform box. Open the HTML editor and enter this simple HTML.

<style>
#intersection-info {
    font-family: Verdana;
    font-size: 16px;
}
</style>

<div id="intersection-info">
    Screen coords: <span id="intersection-screen">(?, ?)</span><br />
    Entity: <span id="intersection-entity">?</span><br />
    Point: <span id="intersection-point">?</span><br />
    Depth: <span id="intersection-depth">?</span><br />
</div>

This should give you this little information area in the top left corner.

Now we need to add a simple function to our script to display the info we got from the event object.

var displayIntersectionInfo = function(screenCoords, entity, point, depth) {
    var coordsDiv = document.getElementById('intersection-screen');
    var entityDiv = document.getElementById('intersection-entity');
    var pointDiv = document.getElementById('intersection-point');
    var depthDiv = document.getElementById('intersection-depth');
    coordsDiv.innerHTML = '(' + screenCoords[0] + ', ' + screenCoords[1] + ')';
    entityDiv.innerHTML = entity;
    pointDiv.innerHTML = '(' + point[0].toFixed(2) + ', ' +
            point[1].toFixed(2) + ', ' + point[2].toFixed(2) + ')';
    depthDiv.innerHTML = depth.toFixed(2);
};

Next, add some code to the Event Listener callback to pass the variables. We also do some checking to handle the case where a pick ray misses all entities, or picks an HTML entity, which has no 3D point.


ctx.runnerListeners = {
    click: function(evt) {
        console.log('Click event', evt);
        var name, point, depth;
        if (evt.entity && evt.intersection) {
            name = evt.entity.name;
            point = evt.intersection ? evt.intersection.toArray() : [0, 0, 0];
            depth = evt.depth;
        } else {
            name = 'None';
            depth = 0;
            point = [0, 0, 0];
        }
        displayIntersectionInfo([evt.x, evt.y], name, point, depth);
    }
};

When we add this script and click any entity in the scene, we should now have populated the HTML entity.

An Intersection Marker

Something you might want to do is manipulate your 3D scene depending on a picking result. For example, you could move the camera to a point of interest, make a game character move or fire a weapon, or set the point of gravity. One simple thing we can do to visualize the point is to add a simple intersection marker. We’ll use a sphere for this.

Start by creating some mesh data and a simple Material for the Sphere in the setup function.

var setup = function(args, ctx) {
    console.clear();

    ctx.markerMeshData = new sumerian.Sphere(16, 16, 0.14);
    ctx.markerMaterial = new sumerian.Material(sumerian.ShaderLib.uber);
    ctx.markerMaterial.uniforms.materialDiffuse = [1, 0, 0, 1];
    ctx.markerMaterial.uniforms.materialEmissive = [0.7, 0, 0, 1];
    ctx.markerMaterial.uniforms.materialSpecular = [0, 0, 0, 1];

    // ...

Then, write functions to create or show the Sphere at a certain point, or remove it.

var showMarker = function(ctx, pos) {
    if (!ctx.markerEntity) {
        ctx.markerEntity = ctx.world.createEntity(
            ctx.markerMeshData,
            ctx.markerMaterial,
            'Intersection Marker');
    }
    ctx.markerEntity.addToWorld();
    ctx.markerEntity.setTranslation(pos);
};

var hideMarker = function(ctx) {
    if (ctx.markerEntity) ctx.markerEntity.removeFromWorld();
};

Finally, hook up these functions in the event callback.


ctx.runnerListeners = {
        click: function(evt) {
            console.log('Click event', evt);
            var name, point, depth;
            if (evt.entity && evt.intersection) {
                showMarker(ctx, evt.intersection);   // <----- here
                name = evt.entity.name;
                point = evt.intersection ? evt.intersection.toArray() : [0, 0, 0];
                depth = evt.depth;
            } else {
                hideMarker(ctx);                     // <----- and here
                name = 'None';
                depth = 0;
                point = [0, 0, 0];
            }
            displayIntersectionInfo([evt.x, evt.y], name, point, depth);
        }
    };

Now when picking entities, the small red sphere should show up at the picked point.

The Full Script

The following is a complete script, in case you want to duplicate it.


var setup = function(args, ctx) {
    console.clear();

    ctx.markerMeshData = new sumerian.Sphere(16, 16, 0.14);
    ctx.markerMaterial = new sumerian.Material(sumerian.ShaderLib.uber);
    ctx.markerMaterial.uniforms.materialDiffuse = [1, 0, 0, 1];
    ctx.markerMaterial.uniforms.materialEmissive = [0.7, 0, 0, 1];
    ctx.markerMaterial.uniforms.materialSpecular = [0, 0, 0, 1];

    ctx.runnerListeners = {
        click: function(evt) {
            console.log('Click event', evt);
            var name, point, depth;
            if (evt.entity && evt.intersection) {
                console.log(evt);
                showMarker(ctx, evt.intersection);
                name = evt.entity.name;
                point = evt.intersection ? evt.intersection.toArray() : [0, 0, 0];
                depth = evt.depth;
            } else {
                hideMarker(ctx);
                name = 'None';
                depth = 0;
                point = [0, 0, 0];
            }
            displayIntersectionInfo(
                [evt.x, evt.y], name, point, depth);
        }
    };

    for (var l in ctx.runnerListeners) {
        ctx.world.sumerianRunner.addEventListener(l, ctx.runnerListeners[l]);
    }
};

var cleanup = function(args, ctx) {
    for (var l in ctx.runnerListeners) {
        ctx.world.sumerianRunner.removeEventListener(l, ctx.runnerListeners[l]);
    }
    if (ctx.markerEntity) ctx.markerEntity.removeFromWorld();
};

var showMarker = function(ctx, pos) {
    if (!ctx.markerEntity) {
        ctx.markerEntity = ctx.world.createEntity(
            ctx.markerMeshData,
            ctx.markerMaterial,
            'Intersection Marker');
    }
    ctx.markerEntity.addToWorld();
    ctx.markerEntity.setTranslation(pos);
};

var hideMarker = function(ctx) {
    if (ctx.markerEntity) ctx.markerEntity.removeFromWorld();
};

var displayIntersectionInfo = function(screenCoords, entity, point, depth) {
    var coordsDiv = document.getElementById('intersection-screen');
    var entityDiv = document.getElementById('intersection-entity');
    var pointDiv = document.getElementById('intersection-point');
    var depthDiv = document.getElementById('intersection-depth');
    coordsDiv.innerHTML = '(' + screenCoords[0] + ', ' + screenCoords[1] + ')';
    entityDiv.innerHTML = entity;
    pointDiv.innerHTML = '(' + point[0].toFixed(2) + ', ' +
        point[1].toFixed(2) + ', ' + point[2].toFixed(2) + ')';
    depthDiv.innerHTML = depth.toFixed(2);
};

Publish and share the scene!

In this tutorial, you’ve learned how to use Event Listeners in a 3D environment with the sumerianRunner Event Listeners. To learn more, view the following tutorial:

Back to Tutorials

© 2019 Amazon Web Services, Inc or its affiliates. All rights reserved.