Code to pick 1 of 3 entities for point and move?

How would I combine the codes from the tutorial for picker and point and move so that I can choose 1 of 3 entities and then move that one?

Code for the point and click (from tutorial):

var PointAndClick = pc.createScript('pointAndClick');

PointAndClick.attributes.add('cameraEntity', {type: 'entity', title: 'Camera Entity'});
PointAndClick.attributes.add('playerEntity', {type: 'entity', title: 'Player Entity'});
PointAndClick.attributes.add('playerSpeed', {type: 'number', default: 0, title: 'Player Speed'});

// initialize code called once per entity
PointAndClick.prototype.initialize = function() {
    this.groundShape = new pc.BoundingBox(new pc.Vec3(0, 0, 0), new pc.Vec3(4, 0.001, 4));
    this.direction = new pc.Vec3();
    this.distanceToTravel = 0;
    this.targetPosition = new pc.Vec3();
    
    // Register the mouse down and touch start event so we know when the user has clicked
    this.app.mouse.on(pc.EVENT_MOUSEDOWN, this.onMouseDown, this);

    if (this.app.touch) {
        this.app.touch.on(pc.EVENT_TOUCHSTART, this.onTouchStart, this);
    }
    
    this.on('destroy', function() {
        // Register the mouse down and touch start event so we know when the user has clicked
        this.app.mouse.off(pc.EVENT_MOUSEDOWN, this.onMouseDown, this);

        if (this.app.touch) {
            this.app.touch.off(pc.EVENT_TOUCHSTART, this.onTouchStart, this);
        }     
    }, this);
};


PointAndClick.newPosition = new pc.Vec3();

// update code called every frame
PointAndClick.prototype.update = function(dt) {
    if (this.direction.lengthSq() > 0) {
        // Move in the direction at a set speed
        var d = this.playerSpeed * dt;
        var newPosition = PointAndClick.newPosition;
       
        newPosition.copy(this.direction).scale(d);
        newPosition.add(this.playerEntity.getPosition());        
        this.playerEntity.setPosition(newPosition);     
        
        this.distanceToTravel -= d;
        
        // If we have reached our destination, clamp the position 
        // and reset the direction
        if (this.distanceToTravel <= 0) {
            this.playerEntity.setPosition(this.targetPosition);
            this.direction.set(0, 0, 0);
        }
    }
};


PointAndClick.prototype.movePlayerTo = function (worldPosition) {
    this.targetPosition.copy(worldPosition);
        
    // Assuming we are travelling on a flat, horizontal surface, we make the Y the same
    // as the player
    this.targetPosition.y = this.playerEntity.getPosition().y;

    this.distanceTravelled = 0;
    
    // Work out the direction that the player needs to travel in
    this.direction.sub2(this.targetPosition, this.playerEntity.getPosition());
    
    // Get the distance the player needs to travel for
    this.distanceToTravel = this.direction.length();
    
    if (this.distanceToTravel > 0) {
        // Ensure the direction is a unit vector
        this.direction.normalize();

        this.playerEntity.lookAt(this.targetPosition);
    } else {
        this.direction.set(0, 0, 0);
    }
};


PointAndClick.prototype.onMouseDown = function(event) {
    if (event.button == pc.MOUSEBUTTON_LEFT) {
        this.doRayCast(event);
    }
};


PointAndClick.prototype.onTouchStart = function (event) {
    // On perform the raycast logic if the user has one finger on the screen
    if (event.touches.length == 1) {
        this.doRayCast(event.touches[0]);
        event.event.preventDefault();
    }    
};


PointAndClick.ray = new pc.Ray();
PointAndClick.hitPosition = new pc.Vec3();

PointAndClick.prototype.doRayCast = function (screenPosition) {
    // Initialise the ray and work out the direction of the ray from the a screen position
    var ray = PointAndClick.ray;
    var hitPosition = PointAndClick.hitPosition;

    this.cameraEntity.camera.screenToWorld(screenPosition.x, screenPosition.y, this.cameraEntity.camera.farClip, ray.direction); 
    ray.origin.copy(this.cameraEntity.getPosition());
    ray.direction.sub(ray.origin).normalize();
    
    // Test the ray against the ground
    var result = this.groundShape.intersectsRay(ray, hitPosition);  
    if (result) {
        this.movePlayerTo(hitPosition);
    }  
};

code for collision picking:

var PickerRaycast = pc.createScript('pickerRaycast');

// initialize code called once per entity
PickerRaycast.prototype.initialize = function() {
    this.app.mouse.on(pc.EVENT_MOUSEDOWN, this.onSelect, this);

    this.on('destroy', function() {
        this.app.mouse.off(pc.EVENT_MOUSEDOWN, this.onSelect, this);
    }, this);
};

PickerRaycast.prototype.onSelect = function (e) {
    var from = this.entity.camera.screenToWorld(e.x, e.y, this.entity.camera.nearClip);
    var to = this.entity.camera.screenToWorld(e.x, e.y, this.entity.camera.farClip);

    this.app.systems.rigidbody.raycastFirst(from, to, function (result) {
        var pickedEntity = result.entity;

        pickedEntity.script.pulse.pulse();
    });
};

Hi @calamityz and welcome!

You need to change the playerEntity of your PointAndClick script. You can do this in the raycast result of your PickerRaycast script. It will look something like the one below, but the exact rule will depend on your setup.

var scriptEntity = this.app.root.findByName("Entity with PointAndClick script");
scriptEntity.script.pointAndClick.playerEntity = pickedEntity;

You also can combine the scripts to make it even easier, therefore you need to copy the code from the PickerRaycast script and past it in the PointAndClick script.

this.playerEntity = pickedEntity;
1 Like

Hi @calamityz,

Here I’ve updated the default example to handle multiple player entities, which you assign in an editor attribute. There are some bugs in there, consider it a starting point:

The full script:

var PointAndClick = pc.createScript('pointAndClick');

PointAndClick.attributes.add('cameraEntity', { type: 'entity', title: 'Camera Entity' });
PointAndClick.attributes.add('playerEntities', { type: 'entity', array: true, title: 'Player Entity' });
PointAndClick.attributes.add('playerSpeed', { type: 'number', default: 0, title: 'Player Speed' });

// initialize code called once per entity
PointAndClick.prototype.initialize = function () {
    this.groundShape = new pc.BoundingBox(new pc.Vec3(0, 0, 0), new pc.Vec3(4, 0.001, 4));
    this.direction = new pc.Vec3();
    this.distanceToTravel = 0;
    this.targetPosition = new pc.Vec3();

    // Prepare player bounding boxes
    this.playerBoundings = [];
    this.playerEntities.forEach(entity => {

        const bounding = new pc.BoundingBox(new pc.Vec3(), entity.collision.halfExtents);
        bounding.entity = entity;
        this.playerBoundings.push(bounding);
    });

    // Select by default the first entity
    this.playerEntity = this.playerEntities[0];

    // Register the mouse down and touch start event so we know when the user has clicked
    this.app.mouse.on(pc.EVENT_MOUSEDOWN, this.onMouseDown, this);

    if (this.app.touch) {
        this.app.touch.on(pc.EVENT_TOUCHSTART, this.onTouchStart, this);
    }

    this.on('destroy', function () {
        // Register the mouse down and touch start event so we know when the user has clicked
        this.app.mouse.off(pc.EVENT_MOUSEDOWN, this.onMouseDown, this);

        if (this.app.touch) {
            this.app.touch.off(pc.EVENT_TOUCHSTART, this.onTouchStart, this);
        }
    }, this);
};


PointAndClick.newPosition = new pc.Vec3();

// update code called every frame
PointAndClick.prototype.update = function (dt) {

    // update player boundings
    this.playerBoundings.forEach(bounding => {
        bounding.center.copy(bounding.entity.getPosition());
    });

    if (this.direction.lengthSq() > 0) {
        // Move in the direction at a set speed
        var d = this.playerSpeed * dt;
        var newPosition = PointAndClick.newPosition;

        newPosition.copy(this.direction).scale(d);
        newPosition.add(this.playerEntity.getPosition());
        this.playerEntity.setPosition(newPosition);

        this.distanceToTravel -= d;

        // If we have reached our destination, clamp the position 
        // and reset the direction
        if (this.distanceToTravel <= 0) {
            this.playerEntity.setPosition(this.targetPosition);
            this.direction.set(0, 0, 0);
        }
    }
};


PointAndClick.prototype.movePlayerTo = function (worldPosition) {
    this.targetPosition.copy(worldPosition);

    // Assuming we are travelling on a flat, horizontal surface, we make the Y the same
    // as the player
    this.targetPosition.y = this.playerEntity.getPosition().y;

    this.distanceTravelled = 0;

    // Work out the direction that the player needs to travel in
    this.direction.sub2(this.targetPosition, this.playerEntity.getPosition());

    // Get the distance the player needs to travel for
    this.distanceToTravel = this.direction.length();

    if (this.distanceToTravel > 0) {
        // Ensure the direction is a unit vector
        this.direction.normalize();

        this.playerEntity.lookAt(this.targetPosition);
    } else {
        this.direction.set(0, 0, 0);
    }
};


PointAndClick.prototype.onMouseDown = function (event) {
    if (event.button == pc.MOUSEBUTTON_LEFT) {
        this.doRayCast(event);
    }
};


PointAndClick.prototype.onTouchStart = function (event) {
    // On perform the raycast logic if the user has one finger on the screen
    if (event.touches.length == 1) {
        this.doRayCast(event.touches[0]);
        event.event.preventDefault();
    }
};


PointAndClick.ray = new pc.Ray();
PointAndClick.hitPosition = new pc.Vec3();

PointAndClick.prototype.doRayCast = function (screenPosition) {

    // Initialise the ray and work out the direction of the ray from the a screen position
    var ray = PointAndClick.ray;
    var hitPosition = PointAndClick.hitPosition;

    this.cameraEntity.camera.screenToWorld(screenPosition.x, screenPosition.y, this.cameraEntity.camera.farClip, ray.direction);
    ray.origin.copy(this.cameraEntity.getPosition());
    ray.direction.sub(ray.origin).normalize();

    // Test first if ray interesects any of the player entities
    for (let i = 0; i < this.playerBoundings.length; i++) {
        const bounding = this.playerBoundings[i];

        if(bounding.intersectsRay(ray) === true){
            this.playerEntity = bounding.entity;
            return;
        }
        
    }

    // Test the ray against the ground
    var result = this.groundShape.intersectsRay(ray, hitPosition);
    if (result) {
        this.movePlayerTo(hitPosition);
    }
};

And the example project:
https://playcanvas.com/editor/scene/1308583

2 Likes