[SOLVED] Dynamically Referencing a Script Without Name

Hello,

Apologies if this is a dumb question but I’ve been hitting my head against this for a while:

I’m trying to use a single raycast to activate different scripts based on what is hit by the raycast. Like a point-and-click adventure type game, where different objects do different things when clicked.

Here’s an example of what I mean:

Entity A has a script on it called “flyAwayIntoTheSun”

Entity B has a script on it called “explodeIntoPieces”

Both “flyAwayIntoTheSun” and “explodeIntoPieces” have functions named “onSelect” in them that fire off their individual different behaviors.

What I cannot figure out is how to get my raycast to call “onSelect” on these scripts (based on what object the raycast hits), unless I explicitly pass it the script name.

It’d be silly and unmaintainable to do a massive pile of conditional logic to call each script’s individual ‘onSelect’ function based on the name of the script. I’m also not sure that using events would be very maintainable or performant either…

I tried making a helper script just named “selectable” and have that script reference another script dynamically as an asset attribute, but I couldn’t get that to work.

Is there a way to programmatically access a script based on something other than its name?
Is there an easier way to do what I’m trying to do?

Thank you immensely for any help!

Not sure if it’s helpful because it’s extremely basic but here’s the raycast script:

var SelectItem = pc.createScript('selectItem');

SelectItem.attributes.add('cameraEntity', {type: 'entity'});

// initialize code called once per entity
SelectItem.prototype.initialize = function() {

     this._camera = this.cameraEntity.camera;

     // susbscribe mouseup event to raycast
     this.app.mouse.on(pc.EVENT_MOUSEUP, this.mouseUp, this);

     //subscribe on touch event
     if (this.app.touch) {
         this.app.touch.on(pc.EVENT_TOUCHSTART, this.touchStart, this);
     }

// remove handlers on destroy
this.on('destroy', function() {

    this.app.mouse.off(pc.EVENT_MOUSEUP, this.mouseUp, this);
    
    if (this.app.touch) {
        this.app.touch.off(pc.EVENT_TOUCHSTART, this.touchStart, this);
    }

     }, this);

};


SelectItem.prototype.mouseUp = function (e) {
     this.doRaycast(e);
};

SelectItem.prototype.touchStart = function (e) {
    if (e.touches.length == 1)
    {
        this.doRaycast(e.touches[0]);
    }
    e.event.preventDefault();
};

SelectItem.prototype.doRaycast = function (screenPosition) {

     var _from = this.cameraEntity.getPosition();

     var _to = this._camera.screenToWorld(screenPosition.x, screenPosition.y, this._camera.farClip);

     var _result = this.app.systems.rigidbody.raycastFirst(_from, _to);

// if we hit anything with a collider
if (_result) {

    var hitEntity = _result.entity;

    // if it has a script component at all
    if (hitEntity.script){

        // FIND THE SCRIPT I WANT SOMEHOW AND CALL ITS 'onSelect' FUNCTION
        
        }

    }


}

};

entity.script.scripts is an array of all the scripts. So you could loop trough each script, check if there is an onSelect function and then call it. eg entity.script.scripts[0].onSelect();

Or you could fire an event app.fire('selected', result.entity) and then in the event handler in your receiving script you can check if (selectedEntity === this.entity) { }

3 Likes

The events approach is so much more elegant!

2 Likes

Thank you, Kulodo!

Somehow I didn’t find that entity.script.scripts would let me access all the scripts as an array.

Events would be more elegant, but I was worried about the performance impact of so many event handlers… I think my fear may be unfounded though, after reading some more about them I dunno that I would hit the point where I had enough to make much of a problem.

Thanks again!

1 Like