Beyond the Wall of Scripts: Scripting in High Fidelity - Primer for Intermediate Programmers


#21

@claudio.pacchiega I went ahead and updated the links again, didnt notice this comment so I hadnt updated the thread for a while


#22

Ok, I did study how JavaScript prototypes work and other caveats from JavaScript basics (it took me some weeks). But still I’m quite confused by this part.

I really don’t understand where and how these concepts are applicable to an actual Entity Script. It appears that the only example of this is https://github.com/highfidelity/hifi/blob/4925980cbb5ac99c625c5fba24f31ea212226719/scripts/system/controllers/handControllerGrab.js which however is a quite complex example for me as a newbie and so it is useless. (Not counting that I wasn’t even able to find the preload or return new MyObject() or similar part so I was completely confused).

Can I ask if someone can provide a minimal (but complete with all the steps) example of a very simple script doing something not totally trivial but not 2000 code lines as controoler grab .js, and exploiting this apparently quite important concept of prototype?

The poor my understanding of that paragraph is something on the lines of:

Each entity script clone MUST have an ancestor that is then called only once at entity creation. What is quite confusing to me is that all these ancestors are still sitting on different clients, so how they can communicate one with the other? Or is this thing of the prototype a way of sharing data among all the instances? (This seems to me a bit absurd, but HighFidelity and Javascript can surprise me still for ever)…


#23

I really don’t understand where and how these concepts are applicable to an actual Entity Script. It appears that the only example of this is

Maybe I need to reword the sentence and update those links as well (as those are back from 2016).

HandcontrollerGrab js is not an example of an Entity script, as it is a client script.
Instead it is an example of a client script interacting with an Entity using Entities.callEntityMethod

Nowdays it is called “controllerScripts” and split into many, many tiny scripts. but concepts remain.

Better Example of an Entity Script would be, forexample the pingpong gun, flare gun or confetti gun, or the hook gun all available from the marketplace.

As mentioned the starting point for entity script is always:

(function () {
    return {
        preload: function (entityId) {
            console.log("loaded on ", entityId);
        }
    }
})

Ultimately this can be written in many ways, but thats going to unnessary complications for an Intermediate Programmer.


Building on the style established in here, we can then expand upon the above with any of the modules we want to use

So here is a script that when clicked on will change color and print stuff into the logs.

(function () {
    return {
        preload: function (entityId) {
            console.log("I", entityId, " am now loaded!");
        },
        clickDownOnEntity: function (entityId, event) {
            console.log("Hello, ", MyAvatar.displayName, "Your current sessionId is ", MyAvatar.sessionUUID, "You clicked on me", entityId, " I will now change color!", JSON.stringify(event));
          
            Entities.editEntity(entityId, {color: {red: Math.random() * 255, green: Math.random() * 255, blue: Math.random() * 255}});
        }
    }
});

Exploring the above points out a few things:

MyAvatar functions will give you always the session id of who ever clicked the entity. This is because who ever clicks the entity is actually running the event on their new PC.

MUST have an ancestor that is then called only once at entity creation. What is quite confusing to me is that all these ancestors are still sitting on different clients, so how they can communicate one with the other?

Since the creation of the post, I have yet to get around to actually writing it a bit further due to my work on the Blender Plugin. There are other things I have found other things about the scripting engine and it has changed constantly, and some of the stuff is wrong: Even I have moved on from the prototype class to the object constructor method.

The only “ancestor” that basically exists is the script and how it is written and the entity they are in. Everything else is unique to its instance: each script in their own entity also create their own instance. It is designed however to be parallel, and I swear it used to, but now doesnt; which is interesting, but I digress.

Communication between clients and instances can be handled through a common shared state, such as the userData (for the entity’s instances), or through the Messaging Layer (for all instances).


However, mouse events are not the end of, everything, as we probably want HMD stuff as well, so;
Now Exploring Everything available to us, here are all the available predefined bindings of callbacks.

(function () {
    return {
        preload: function (entityId) {
            console.log("loaded on ", entityId);
        },

        // Defined by /system/controllers/controllerModules/farTrigger.js
        // Called Via Entities.callEntityMethod
        // These occur when user is far from object. Any Range other than 0.25 m
        //  Only usable if userdata has grabbableKey.triggerable = true
        /*
            {
                grabbableKey: {
                    triggerable: true
                }
            }
        */
        startFarTrigger: function (entityId, event) {
            console.log("HMD: Interaction Start", entityId, JSON.stringify(event));
        },
        continueFarTrigger: function (entityId, event) {
            console.log("HMD: Interaction Hold", entityId, JSON.stringify(event));
        },
        stopFarTrigger: function (entityId, event) {
            console.log("HMD: Interaction Hold", entityId, JSON.stringify(event));
        },

        // Defined by /system/controllers/controllerModules/nearTrigger.js
        // Called Via Entities.callEntityMethod
        // These occur when the user is really close to object, Around 0.25 m from the object. 
        //  Only usable if userdata has grabbableKey.triggerable = true
        /*
            {
                grabbableKey: {
                    triggerable: true
                }
            }
        */
        startNearTrigger: function (entityId, event) {
            console.log("HMD: Near Interaction Start", entityId, JSON.stringify(event));

        },
        continueNearTrigger: function (entityId, event) {
            console.log("HMD: Near Interaction Hold", entityId, JSON.stringify(event));
        },
        stopNearTrigger: function (entityId, event) {
            console.log("HMD: Near Interaction Hold", entityId, JSON.stringify(event));
        },

        // Defined by /system/controllers/controllerModules/equipEntity.js
        // Called Via Entities.callEntityMethod
        // Only usable if userdata contains 'wearable' definitions.
        /*
            {
                wearable: {
                    joints: {
                        <jointName>: [Vec3Position, QuatRotation]
                    }
                }
            }
        */
        startEquip: function (entityId, event) {
            console.log("HMD: Equip Start", entityId, JSON.stringify(event));
        },
        releaseEquip: function (entityId, event) {
            console.log("HMD: Equip Released", entityId, JSON.stringify(event));
            // Should be when entity and script is reset to its original form prior to equip
        },
        continueEquip: function (entityId, event) {
            console.log("HMD: Everyframe, While equipped", entityId, JSON.stringify(event));
        },


        // Everything above is configured through /scripts/system/libraries/controllerDispatcherUtils.js and pulled together through 
        // /scripts/system/controllers/controllerScripts.js
        // Mouse Events:
        // Defined in Engine, Natively through C++ through ScriptEngine.cpp
        // As they are natively defined, no userdata requirement is present.

        clickDownOnEntity: function (entityId, event) {
            console.log("MouseEvent Click Start", entityId, JSON.stringify(event));
        },
        holdingClickOnEntity: function (entityId, event) {
            console.log("MouseEvent Click Hold", entityId, JSON.stringify(event));
        },
        clickReleaseOnEntity: function (entityId, event) {
            console.log("MouseEvent Release Click", entityId, JSON.stringify(event));
        }
    }
});

And thats not all, I havent even touched timers, frame updates, etc.

If I Had the time I could probably write an entire book on the subject lol


#24

@Menithal as usual you are REALLY informative and every your post is quite precious. It always needs me weeks to ‘grok’ them. But I am sure that this kind of clarification is overwhelmingly needed if HighFidelity wants to attract people in scripting for it. And I am absolutely certain that scripting can totally boost the possibilities of HighFidelity, as it did with SecondLife.

In my public talks about SecondLife and OpenSim I always said that Scripting gives life to otherwise ‘dead’ 3d models. And virtual worlds definitely need to have live and interactive environment to be really useful. Just thinking about virtual worlds as “chatrooms” as did Google some years ago was a plain mistake.

Being a virtual teacher myself I now go on being a virtuous student and will study your examples :slight_smile: