[SOLVED] This scope with raycast

Hi,

I’ve seen a few similar topics on this and read this awesome post http://javascriptissexy.com/understand-javascripts-this-with-clarity-and-master-it/ but I don’t understand the application to my code. It’s a this scope question (please sigh in unison). I know my this.trigger statements are all referring to different things but I need the two in the initialize function to reference the same object as the one in the update function.

I’ve tried alot of different things like assigning this to a variable in initialize but then I struggle to access it in update. I’d also like to avoid using global variables if possible. I’ve left the code as shown below as it’s easiest for you to try and see what i’m doing, ignore the other variables for now as I know these don’t work as intended either.

Thanks,

Oz

var EquipmentToggle = pc.createScript('equipmentToggle');

// this script works with the raycast script.
// set up an entity reference for the on() listener to use.
// set up externally available attributes.
EquipmentToggle.attributes.add('rayCaster', {type: 'entity'});
EquipmentToggle.attributes.add('offsetCurve', {type: 'curve'});
EquipmentToggle.attributes.add('duration', {type: 'number', default: 1});

// initialize code called once per entity
EquipmentToggle.prototype.initialize = function() {
    
    this.trigger = false;
    // set up function to be called when event is triggered.
    var onHit = function (hitEntity) {
        
        console.log(hitEntity.tags);
        
        // Keep track of the original scale of the entity.
        this.startScale = hitEntity.getLocalScale().clone();
        
        // Keep track of the current scale.
        this.scale = new pc.Vec3();
        
        // Set start time.
        this.time = 0;
        
        // trigger.
        this.trigger = true;
    };
    
    // listen for the Raycast Hit event defined in the script attached to the rayCaster entity defined above.
    this.rayCaster.script.raycast.on('RAYCAST_HIT', onHit);
};

// update code called every frame
EquipmentToggle.prototype.update = function(dt) {
    
    console.log(this.trigger);
    
    if(self.trigger) {
    
        // Loop the animation forever
        this.time += dt;
        if (this.time > this.duration) {
            this.time -= this.duration;
        }
    
        // Calculate how far in time we are for the animation
        var percent = this.time / this.duration;
       
        // Get curve values using current time relative to duration (percent)
        var curveValue = this.offsetCurve.value(percent);
    
        // Create our new scale from the startScale and offset from the curve value
        this.scale.copy(this.startScale);
        this.scale.x += curveValue;
        this.scale.y += curveValue;
        this.scale.z += curveValue;
    
        this.hitEntity.setLocalScale(this.scale);
    }
};

One of the issues is a scope issue. The this in the onHit function is not pointing to the same this outside the function.

The typical idiom is:

    var self = this;
    this.trigger = false;
    // set up function to be called when event is triggered.
    var onHit = function (hitEntity) {
        
        console.log(hitEntity.tags);
        
        // Keep track of the original scale of the entity.
        self.startScale = hitEntity.getLocalScale().clone();
        // etc

I’m not 100% sure that the raycast callback is done correctly either (at least with the PlayCanvas raycast functions). Are you developing your own raycast library?

2 Likes

Man I thought I’d done that but you’re right and it works! my raycast is fine though, it sits in another script and uses event to pass the hitEntity to this one.

Thanks for the lightning fast response.

If it helps anyone else here is the final working script with necessary replacements made;

var EquipmentToggle = pc.createScript('equipmentToggle');

// this script works with the raycast script.
// set up an entity reference for the on() listener to use.
// set up externally available attributes.
EquipmentToggle.attributes.add('rayCaster', {type: 'entity'});
EquipmentToggle.attributes.add('offsetCurve', {type: 'curve'});
EquipmentToggle.attributes.add('duration', {type: 'number', default: 1});

// initialize code called once per entity
EquipmentToggle.prototype.initialize = function() {
    
    // get a reference to this here and store it in a variable so it can be called within the nested funtion below.
    var self = this;
    this.trigger = false;
    
    // set up function to be called when event is triggered.
    var onHit = function (hitEntity) {
        
        self.hitEntity = hitEntity;
        console.log(hitEntity.tags);
        
        // Keep track of the original scale of the entity.
        self.startScale = hitEntity.getLocalScale().clone();
        
        // Keep track of the current scale.
        self.scale = new pc.Vec3();
        
        // Set start time.
        self.time = 0;
        
        // trigger.
        self.trigger = true;
    };
    
    // listen for the Raycast Hit event defined in the script attached to the rayCaster entity defined above.
    this.rayCaster.script.raycast.on('RAYCAST_HIT', onHit);
};

// update code called every frame
EquipmentToggle.prototype.update = function(dt) {
    
    console.log(this.trigger);
    
    if(this.trigger) {
    
        // Loop the animation forever
        this.time += dt;
        if (this.time > this.duration) {
            this.time -= this.duration;
        }
    
        // Calculate how far in time we are for the animation
        var percent = this.time / this.duration;
       
        // Get curve values using current time relative to duration (percent)
        var curveValue = this.offsetCurve.value(percent);
    
        // Create our new scale from the startScale and offset from the curve value
        this.scale.copy(this.startScale);
        this.scale.x += curveValue;
        this.scale.y += curveValue;
        this.scale.z += curveValue;
    
        this.hitEntity.setLocalScale(this.scale);
    }
};