Buildbox 3 Javascript Tips!

Discussion in 'Technical Discussion' started by thatguyminib, Jul 26, 2018.

  1. thatguyminib

    thatguyminib Serious Boxer

    Joined:
    Jul 1, 2017
    Messages:
    538
    Likes Received:
    295
    Figured I would start a thread with any tips or fun things we find with javascript and BB3!

    I'll start it off!

    Quick tip for testing in JS with the log() function. You have to first put a string and then use the + operator and then what you want to test. So for example if you have a boolean called isReady and you want to check if it is true or false you CANNOT write log(isReady);, if you do it will crash, you have to write log("Some string" + isReady);
     
  2. Bilz636

    Bilz636 Avid Boxer

    Joined:
    Oct 5, 2015
    Messages:
    280
    Likes Received:
    160
    please pin this thread
     
  3. thatguyminib

    thatguyminib Serious Boxer

    Joined:
    Jul 1, 2017
    Messages:
    538
    Likes Received:
    295
    Hey everyone here is a little way on how to communicate between two objects!
    So I made a random object and named it "Communicator" went inside and set it up like the image below. I created an Input Boolean variable and called it Enabled in order to get the Key Button signal to work. I also have two variables under that. One is _otherObject and with this inside of the signal function I did _otherObject = this.scene().find('Cube'); Cube is the name of the object I was trying to find and communicate with. this.scene().find() will go through all of the objects in your scene and return an array of objects that match the name you put inside of the quotes. Right under that I have another variable _otherComp and I did _otherComp = _otherObject[0].component('Test'); Test is the name of the script I am trying to reference on the _otherObject. The reason I did _otherObject[0] is because since the scene().find() function returns an array and there is only one object I have to get the first index of the array which in programming we start counting from 0. If that part doesn't make sense I suggest you do a quick good on javascript arrays. Now that I am referencing the Test script on my _otherObject, or Cube, I then do _otherComp.signal('Number', 10); this will send a signal to the Test script and try to find a variable called "Number" and change it's value to 10. Time to go to the next image! :D Capture.PNG


    Now we go to our Cube object that we are referencing in the script above. So for this I just grabbed a cube from the asset tab and dragged it into my project. I then double clicked to go into the node view. After that I made a script and called it Test, just like we referenced above. I then created a Number variable on it as well as an Enabled input boolean variable. Inside of the script I setup a variable _number and a variable _enabled = false. Then in the init I set those 'empty' variables to corresponding variables I made on the node by using this.attribute('Number');. After that in the update I did log('Number : " + _number); FOR A LOG MAKE SURE YOU PUT A STRING IN FIRST BEFORE YOU DO A CHECK FOR A VALUE OR VARIABLE OR IT WILL CRASH! After that I go to the signal. Now here is where I got a bit confused because above we do _otherComp.signal('Number', 10); and from what I have heard this is supposed to grab the 'Number' variable on the node and change its value. Well come to find out it wasn't working like that. I had to actually put _number = 10 in the signal in order to get it to fire and change the value. So I am not sure if the .signal will just reference the signal function in the referenced node and maybe we can do _otherComp.nameOfAnotherFunction(); and call any function in the script we reference or what but this is how I got it to work.
    Capture2.PNG

    If you run it and open up the console every time you push the key you declared in the above Key Pressed node it will change the value of Number to 10.

    Hope all this makes a bit of sense and helps some people out lol
     
  4. WesleyJohnson

    WesleyJohnson Boxer

    Joined:
    Jul 29, 2018
    Messages:
    1
    Likes Received:
    2
    When you call the "signal" method like that, you're just passing a name and value in and you're responsible for handling that on other component. There isn't anything special about the signal method as far as I can tell. Because you're calling it like "signal('Number', 10)", then on your Test component, you have to logic in place to know what to do if the "name" you passed in was "Number".

    The way you have it now, you're just hardcoding the "_number = 10;" in the Test script, so it doesn't matter what you pass in when you call it. You could "signal('Number', 50);", but your Test script would still display 10. Trying changing it to look like this...

    Code:
    function signal(name, value) {
        if(name == 'Enabled') {
            _enabled = value;
        }
      
        if(name == 'Number') {
            _number = value;
        }
    }
    
     
    particles and thatguyminib like this.
  5. Simon Keating

    Simon Keating Boxer

    Joined:
    Aug 15, 2018
    Messages:
    4
    Likes Received:
    0
  6. DanFarfan

    DanFarfan Avid Boxer

    Joined:
    Sep 22, 2018
    Messages:
    101
    Likes Received:
    39

    There is another syntax for making strings. I find it easier to read. Less error prone.
    Examples:

    log(`Some string ${isReady}`);

    const f = `${_f}.signal`;

    log(`${f} position = ${JSON.stringify(this.entity().position() ) }`);
    log(`${f} position + delta X = ${this.entity().position().x +d_x }`);
     
  7. DanFarfan

    DanFarfan Avid Boxer

    Joined:
    Sep 22, 2018
    Messages:
    101
    Likes Received:
    39
    The central construct of Javascript is objects, specifically JSON objects.
    You have no hopes of becoming proficient in Javascript w/o a complete understanding of how objects work and how to wield them.

    For example, when you see in the BB api doc this command:

    let pos = this.entity().position();

    Think object.

    pos becomes an object because .position() returns an object.
    The documentation goes on to reveal that pos.x, pos.y and pos.z are the position values for the x, y and z dimensions.

    But is that all that pos has? Who knows? You can.
    Make this line your friend
    log(` pos is = ${JSON.stringify(pos)}`);

    JSON is built into the Javascript language specifically so you (and it) can use the .stringify and .parse functions.
    Simply put, .stringify() turns an object into a string. Such as in the case above.
    pos is = {x:-1,y:0.5,z=2}

    Beside printing to the screen, .stringify() is handy for writing an object to a file. (a feature not yet supported in BB, I don't think).
    But when it is supported, know that
    let pos = JSON.parse(line);
    would make pos an object if, for example line is a string that looks something like this
    "{x:-1,y:0.5,z:2}"

    One last thing, there is another format for stringify that is wicked handy, especially when the objects get big. Check this out.
    log(` pos is = ${JSON.stringify(pos,null, ' ' )}`);
    That will get you this something like this:
    pos is =
    {x:-1,
    y:0.5,
    z=2}

    Okay, one more. When I said at the top that objects are the central construct...
    Try this and see what you get.

    log(` the entity = ${JSON.stringify(this.entity(), null, ' ')}`);

    Enjoy!
    @DanFarfan
     
    Last edited: Nov 2, 2018
  8. DanFarfan

    DanFarfan Avid Boxer

    Joined:
    Sep 22, 2018
    Messages:
    101
    Likes Received:
    39
    try / catch is mandatory for a savvy Javascript programmer.
    Code:
    
    const _f = `MyObject.AddPoint`;
    
    function update ( dt ) {
      let a = 12;
    
       try {
          let a1 = someFunction(a);
          log(`${_f}.update a1 = ${a1}`);
       } catch ( err1 ) {
          log(`${_f}.update ERROR err1 = ${err1}`);
       }
    }
    
    IF there is an error inside the try block (i.e. anything between try{ and } then the catch block prints it.
    You can put any value commands in the try block and in the catch block.

    Why err1? Because you might want another try/catch block. That would be err2.
    The 2 most important things about a log message is that you can easily find in the code where it came from. Which means it has (and reveals) its own unique identity. The second is that it contains the important information that will help you figure out what's happening.

    For example our error message would be better like this:
    log(`${f}.update ERROR \n a = ${a} \n err1 = ${err1}`);
    ( \n forces in the log what's called a carriage return / line feed. aka a new line )

    But wait... one last thing...
    "inside the try block" INCLUDES inside the functions called inside the try block.
    So, in this case if an error occurs inside someFunction() it too will force execution immediately to go to the catch block. )

    Enjoy!
    @DanFarfan

    update: I just read last night something I did not know. try/catch blocks cause the optimization built into Javascript to "bail out." So, assuming those optimization techniques are employed in the BB3 environment, it will be worthwhile to comment out the try and catch parts AFTER your exhaustive testing have uncovered all errors :) ESPECIALLY inside the update() functions.
     
    Last edited: Nov 2, 2018
  9. DanFarfan

    DanFarfan Avid Boxer

    Joined:
    Sep 22, 2018
    Messages:
    101
    Likes Received:
    39
    Math. is your friend.

    Math is another library built right into the language.
    One of the things you might do most with it is random numbers.
    For example

    let dx1 = Math.random(); // between 0.0 and .9999..
    let dx2 = Math.random()*8; // between 0.0 and 7.999..
    let dx3 = Math.floor(Math.random()*8); // between 0 and 7


    Math.floor() makes a number into an integer by chopping off all the digits to the right of the decimal.

    This is my goto pages for Math reference: https://www.w3schools.com/js/js_math.asp

    Enjoy!
    @DanFarfan
     
  10. DanFarfan

    DanFarfan Avid Boxer

    Joined:
    Sep 22, 2018
    Messages:
    101
    Likes Received:
    39
    Another suggestion..

    Until the BB3 programming environment, API and object model architecture are as familiar to you as, oh, I don't know... you're own face... use the following coding example as a template:

    (EDIT: note: I use the code block feature so forum shows the spaces I typed. As in [ CODE] blah blah [ / CODE] --- without the spaces in the CODE and /CODE )

    Code:
    try {
       let model = this.entity().component("<other_model_name>");
       if ( model ) {
          model.<function_name>(<parm1>, <parm2>); // as many parameters as you need
       } else {
          log(`<this_entity_name>.<this_component_name>.<this_function_name> ERROR <other_model_name> NOT FOUND`);     
       }
    } catch ( err1 ) {
       log(`<this_entity_name>.<this_component_name>.<this_function_name> ERROR err1 = ${err1}`);
    }
    
    
    NOTE: <name> means use the actual name that fits your needs. Do not use the < or > characters.
    NOTE: in order to use the ${variable_name} syntax, use back ticks instead of quotes on log() function.
    NOTE: The catch will extend into the function in the other component.
    This means any error in that function (or "below") will be caught and reported by this catch.

    NOTE: This works to show errors even on a Mac (which refuses to display red error messages in the log)
    NOTE: err1 is a variable name. You may name it anything you want. But always be sure to
    make the (every) message that you logged SUPER EASY to find. 2 weeks, 2 months,
    2 years later, you could have 15 places in your project that reveal "ERRORS," so don't just
    make the error message say "ERROR ..."



    Good Luck
    @DanFarfan
     
    Last edited: Nov 2, 2018
  11. DanFarfan

    DanFarfan Avid Boxer

    Joined:
    Sep 22, 2018
    Messages:
    101
    Likes Received:
    39
    Whoops, been a while since the last Javascript tip.
    Here we go...

    One of the most common things needed at the top of a function is a way to validate am argument's value or that it has a value at all.
    This is because even if you make a function such as this:
    Code:
    function myMiracleFunction ( just, because ) { 
       // miracle happens here...
    } // my miracle function
    component.myMiracleFunction = myMiracleFunction
    
    Javascript will happily let some other code call it like this:
    Code:
    let miracle_comp = this.scene().find("miracleEntity")[0].component("MiracleComponent");
    miracle_comp.myMiracleFunction(42);  // notice, only ONE value supplied!
    
    There are almost as many ways to address this as there are programmers, but here is the way I suggest.

    Code:
    function myMiracleFunction ( just, because ) { 
       // miracle happens here...
       // each of these lines of code establish a default value for an argument
       just = ( typeof just === "undefined" ) ? 42 : just;
       because = ( typeof because === "undefined" ) ? "multiply" : because;
    
    } // my miracle function
    component.myMiracleFunction = myMiracleFunction
    
    This requires some explanation. This shortcut notation is read like this:
    IF the variable JUST is UNDEFINED, THEN set the variable JUST equal to 42, ELSE set JUST equal to whatever value it already has.
    Think of the ? as the THEN and the : as the ELSE.
    (Don't go crazy, this is shortcut notation for the assignment statement only. These aren't blocks for any number of commands you want to throw in between ? and : )

    Also worth noting that typeof can deliver a few other values, "string", "number", "object", "boolean"... ummm maybe some others. Those are the main "types." Also... I can't explain why but the syntax is
    typeof variable_name
    not
    typeof(variable_name)
     
  12. DanFarfan

    DanFarfan Avid Boxer

    Joined:
    Sep 22, 2018
    Messages:
    101
    Likes Received:
    39
    How about some more about objects?
    There are hundreds of webpages that fully reveal the glory of Javascript objects.
    This is just a little primer to get you started. In no particular order.
    Folks, feel free to add more messages about how to wield objects. :)

    Code:
    // make an object
    let my_obj = {};
    my_obj.count = 0;
    my_obj.threshold = 42;
    my_obj.your_obj = your_obj;
    // don't forget functions ARE objects and can be wielded as such
    my_obj.theshold_fcn = doThreshold;  // HOWEVER, when using a function as an object, do not use ( )      !!
    
    // or you could make the same object like this: 
    let my_obj = {count: 0, threshold: 42, your_obj: your_obj, threshold_fcn = doThreshold };
    // notice the syntax carefully. the : basically acts like an equal sign to assign values. 
    
    // After either of the above, you can do something like this: 
    if ( ++my_obj.count >= my_obj.threshold ) {
       log(`threshold met!`);
       // executing the function is as easy as adding ( ) and any parms
       my_obj.threshold_fcn(this.entity());
       my_obj.count = 0; // reset to start the count over
    }
    
    //
    // another important way to wield an object
    //
    function fancyMath ( ent, field_name, qty ) {
       // ent is a BB3 entity that called me
       // field_name is the name of a variable in a special obj I use to do my fancy math
       // qty is the quantity to "add" to field_name
       
       // first, some error checking
       if ( typeof ent != "object" ) {
          log(`fancyMath: ERROR. ent must be an BB3 entity. Try sending in this.entity() `);
          return;
       }
       // and some default value setting
       qty = ( typeof qty === "undefined" ) ? 1 : qty;
       field_name = ( typeof field_name === "undefined" ) ? "count" : field_name;
       //
       log(`fancyMath: called by ${ent.name()} with qty = ${qty}`);
       _fancy_obj[field_name] += qty;
       // += is shortcut to mean:  _fancy_obj[field_name] = _fancy_obj[field_name] + qty;
       // notice, by ADDED qty, I can also decrease by setting qty to a negative number. 
       // NOTICE, use [ ] instead of .  because field_name is a variable containing the field name
       // In other words, when you know the field name in the object, hard code it: obj.count
       // but when the field name is stored in a variable use obj[field_name] 
       // Important in the way that  my_obj.count CREATES a field called count in the object if not there,
       //  my_obj[field_name] = 22; would also create a new field in the object, if not there yet.
       //
       // Those are 2 ways to ADDRESS (aka reference) inside an object. 
       // 
       // do fancy math here.  ;-) 
    
       return _fancy_obj; // yep, you can return an object;
    }
    component.fancyMath = fancyMath; 
    
    
    Enjoy!

    @DanFarfan
     
  13. DanFarfan

    DanFarfan Avid Boxer

    Joined:
    Sep 22, 2018
    Messages:
    101
    Likes Received:
    39
    Ending a function with

    return x;

    allows the caller use a command such as:

    let new_x = this.changeFunction(x,y,z);

    And that is fine, but what if your function changes 3 values?
    return x,y,z;
    will get you nowhere.

    Objects to the rescue!
    Code:
    // global variable
    // d = delta
    // c = count
    // t = threshold
    // i = initial
    // r = rotate
    var _my_velocity_obj = {dx:1,dy:2,dz:1,cx:0,cy:0,cz:0,tx:10,ty:6,tz:8,ix:1,iy:0,iz:1,rx:8,ry:8,rz:8};
    var _fancy_comp = this.scene().find("MyFancy")[0].component("FancyVelocity");
    
    
    // ...
    // maybe after a jump or every N seconds, or whenever, 
    // function will change entity linear velocity of x,y and z using dx,dy and dz 
    //   and 33/33/34 random it will increase, decrease or not be changed at all
    // When cx (counter x) reaches tx (threshold x), x linear velocity will be set to ix (initial x)
    //  AND function will set cx = 0 to start the count over
    //  AND function will set x angularVelocity = rx until cx no longer 0
    _fancy_comp.setFancyVelocity (this.entity(), _my_velocity_obj);
    
    
    
    Code:
    // in other component 
    function setFancyVelocity ( ent, fv_obj ) {
       // no need to show all the code (exercise for the reader :-)), but suffice it to say that 
       ++fv_obj.cx; 
       // changes the value of .cx in the object as seen in this function AND back in the calling function
       // 
       // Another way to put this... 
       // Objects are passed by REFERENCE (aka by ADDRESS), so the object that the called function uses
       // is the same as the object the calling function uses.  
    
       // notice the called function does NOT have to use the same variable name as the calling function.
    }
     
    
    The following does NOT behave that way.
    Code:
    
    let x = 1;
    this.justValue(x); 
    log (`Calling Function x = ${x}`);
    // puts 1 in the log, not 42;
    
    // .. 
    function justValue(x) {
       x = 42;
       log(`justValue x = ${x}`);
       // puts 42 in the log
    }
    // in this case, x is passed by VALUE. 
    // The calling and called functions are operating on 2 different x's
    
    
    Enjoy!!

    @DanFarfan
     
  14. colette_levesque

    colette_levesque Boxer

    Joined:
    Sep 25, 2015
    Messages:
    94
    Likes Received:
    51
    Can someone tell me if we're able to add Phaser?
    Also, can we use jquery or only use vanilla javascript?
     
  15. thatguyminib

    thatguyminib Serious Boxer

    Joined:
    Jul 1, 2017
    Messages:
    538
    Likes Received:
    295
    I believe it is only Javascript and you can not add Phaser.
     
    colette_levesque likes this.
  16. colette_levesque

    colette_levesque Boxer

    Joined:
    Sep 25, 2015
    Messages:
    94
    Likes Received:
    51
  17. Jollydo

    Jollydo Boxer

    Joined:
    Aug 21, 2017
    Messages:
    81
    Likes Received:
    35


    @thatguyminib : this is some awesome code with great possibilities.
    But there is 1 big problem. All this is only added to the start scene of a game, because of this piece of code: this.scene().find
    If the game has more than 1 scene (most of the times), all other scene will not use the code.
    So, do you know of a way to check how many scenes there are in a world, and then use a var loop, to loop through all scenes and pass your code to all scenes objects? There must be a way i think...

    Thanks a lot ;-)
    Martijn
     
  18. DanFarfan

    DanFarfan Avid Boxer

    Joined:
    Sep 22, 2018
    Messages:
    101
    Likes Received:
    39
    The BB beta 4 API document shows the following code fragment as an example:

    Code:
    let button = this.ui().find('MyButton')[0];   
    button.onClick = function() {     
       Ads.showInterstitial()   
    }
    
    But, be aware.... when the click happens, the variable this is long gone.
    So, consider this.

    Code:
    var self = this; 
    let next_level_btn = self.ui().find('NextLevel')[0]; 
    if ( next_level_btn ) { 
       next_level_btn.onClick = function(){ 
          // navigate to next leve! 
          try { 
             self.gotoNextLevel(); 
          } catch ( err1 ) { 
             log(`ERROR err1 = ${err1}`);
          } 
       } 
    }
    
    Enjoy!
    @DanFarfan

    p.s. Don't be confused about ads vs next level or my error checking. I just grabbed an example from my code that was handy to share with ya'll.
     
    nyamuk91 likes this.
  19. Alexander04

    Alexander04 Boxer

    Joined:
    Jan 15, 2019
    Messages:
    3
    Likes Received:
    0
    Hello, I'm Alexandru and I have encountered this problem with saving progress after I completed a level. What I want to be able to do is to save the level progress after each level I complete. Everything works fine but when I close the app and reopen again it sends me to level 1 again regardless of how many levels i completed. Please can you help me and explain how can can it be done please? Here is an attachment of my nodes structure, Thank you in advance
     

    Attached Files:

  20. iphone.theme

    iphone.theme Boxer

    Joined:
    Sep 20, 2018
    Messages:
    12
    Likes Received:
    2
    Very intresting topic :)
     

Share This Page