[SOLVED] Trying to make Dialogue gives Error: Cannot set properties of null (setting 'enabled)

Hey guys, newcomer to PlayCanvas here! I’m trying to code out a branching dialogue option (yes / no) but it gives me an error “cannot set properties of null (setting 'enabled)”. Any help would be appreciated!

Here’s my Dialogue script below:

var Dialogue = pc.createScript('dialogue');

// define story content
   var story = [
  { m: "Welcome to Manis Town!" },
  { m: "The land is sweet and the people are friendly." },
  { question: "Ready to explore? ", answers: [
    { m: "yes", next: "take_yes" },
    { m: "no", next: "take_no" },
  ] },
  { label: "take_yes", m: "Good to hear! Now go look around, explorer!", next: "take_end" },
  { label: "take_no", m: "That's unfortunate. You're kinda stuck here though, so go look around!", next: "take_end" },
  { label: "take_end", m: "See ya!" }
];

var current_line = 0;
var current_step = story[0];

var answerYes;
var answerNo;

// initialize code called once per entity
Dialogue.prototype.initialize = function() {
    
    this.app.keyboard.on(pc.EVENT_KEYDOWN, this.onKeyDown, this);
    this.app.keyboard.on(pc.EVENT_KEYUP, this.onKeyUp, this);
    
        
    this.text = this.app.root.findByName("DialogueBox");
    this.textcontent = this.entity.findByName("Textcontent");
    this.answerYes = this.entity.findByPath('UI/DialogueBox/AnswerYes');
    this.answerNo = this.entity.findByPath('UI/DialogueBox/AnswerNo');
}

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

   
    this.talkvariable = this.app.root.findByName('MayorLulu').script.mayorLulu.returnState();
    // console.log('talkvariable is set to '+talkvariable);
    
    if (talkvariable){
        
        this.text.enabled = true;
        // while the dialogue isn't finished, step through the dialogue
        while (current_line < story.length){
        // track the step where we're at in the story
        var current_step = story[current_line];
            
            // if the player presses enter advance in the story
            if (this.app.keyboard.wasPressed(pc.KEY_ENTER)){
                // console.log(current_step);
                // execute if the step contains a message (m)
                if (undefined !== current_step.m){
                    this.textcontent.element.text = current_step.m;

                    console.log(answerYes);
                    console.log(answerNo);

                    this.answerYes.enabled = false;
                    this.answerNo.enabled = false;
                    if (undefined !== current_step.next) {
                        current_line = story.map(e => e.label).indexOf(current_step.next);
                    } else {
                        current_line = current_line + 1;
                    }
                }
                // execute if the step contains a question (q)
                else if (undefined !== current_step.question){
                    this.textcontent.element.text = current_step.question;
                    console.log('Were displaying a question here');
                    this.answerYes.enabled = true;
                    this.answerNo.enabled = true;
                }
                
            }
            // register yes answer if a question is displayed
            else if ((this.app.keyboard.wasPressed(pc.KEY_Y)) && (undefined !== current_step.question)){
                        console.log('key press was registered!');
                        current_line = story.map(e => e.label).indexOf('take_yes');
                        
                    }
            // register no answer if a question is displayed
            else if ((this.app.keyboard.wasPressed(pc.KEY_N)) && (undefined !== current_step.question)){
                        console.log('key press was registered!');
                        current_line = story.map(e => e.label).indexOf('take_no');
                    }
        break;
        }
    }
};

// swap method called for script hot-reloading
// inherit your script state here
// Dialogue.prototype.swap = function(old) { };

// to learn more about script anatomy, please read:
// http://developer.playcanvas.com/en/user-manual/scripting/

In your initialize() function, make sure the findByName() and findByPath() calls are actually finding the intended entities. One or more of them are not being found and are ending up as null values which causes the error in your screenshot.

A quick and dirty way to see which variables are null is to console log each of them at the bottom of the initialize() function.

1 Like

Thanks for the suggestion! However, I’ve tried multiple methods to find the entity (using different 'FindBy…" methods) and still no luck. The console.log keeps returning “undefined” :frowning: This is the hierarchy in my editor:

DialogueHiearchy

For this case it might be simpler to set up Script Attributes.

At the top of your Dialogue script, just below the pc.createScript line, add an attribute for each of the four entities. For example, textcontent would be done like so:

Dialogue.attributes.add('textcontent', { type: 'entity' });

Repeat this for the others. Save the changes and then go back to the editor and click the Parse button on the Dialogue script in the DialogueBox’s script component. The script will then show your 4 attributes you just added. From there, it’ll be a simple matter of linking each entity attribute with the correct entity from your scene hierarchy.

Alright, I’ve tried your method but it gives me this error:

Here’s my code after adding what you suggested (in case I wrote it wrongly):

var Dialogue = pc.createScript('dialogue');

Dialogue.attributes.add('text', { type: 'entity' });
Dialogue.attributes.add('textcontent', { type: 'entity' });
Dialogue.attributes.add('answerYes', { type: 'entity' });
Dialogue.attributes.add('answerNo', { type: 'entity' });

// define story content
   var story = [
  { m: "Welcome to Manis Town!" },
  { m: "The land is sweet and the people are friendly." },
  { question: "Ready to explore? ", answers: [
    { m: "yes", next: "take_yes" },
    { m: "no", next: "take_no" },
  ] },
  { label: "take_yes", m: "Good to hear! Now go look around, explorer!", next: "take_end" },
  { label: "take_no", m: "That's unfortunate. You're kinda stuck here though, so go look around!", next: "take_end" },
  { label: "take_end", m: "See ya!" }
];

var current_line = 0;
var current_step = story[0];

var answerYes;
var answerNo;

// initialize code called once per entity
Dialogue.prototype.initialize = function() {
    
    this.app.keyboard.on(pc.EVENT_KEYDOWN, this.onKeyDown, this);
    this.app.keyboard.on(pc.EVENT_KEYUP, this.onKeyUp, this);
    
        
    this.text = this.app.root.findByName("DialogueBox");
    this.textcontent = this.entity.findByName("Textcontent");
    this.answerYes = this.entity.findByPath('UI/DialogueBox/AnswerYes');
    this.answerNo = this.entity.findByPath('UI/DialogueBox/AnswerNo');
}

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

   
    this.talkvariable = this.app.root.findByName('MayorLulu').script.mayorLulu.returnState();
    // console.log('talkvariable is set to '+talkvariable);
    
    if (talkvariable){
        
        this.text.enabled = true;
        // while the dialogue isn't finished, step through the dialogue
        while (current_line < story.length){
        // track the step where we're at in the story
        var current_step = story[current_line];
            
            // if the player presses enter advance in the story
            if (this.app.keyboard.wasPressed(pc.KEY_ENTER)){
                // console.log(current_step);
                // execute if the step contains a message (m)
                if (undefined !== current_step.m){
                    this.textcontent.element.text = current_step.m;

                    console.log(answerYes);
                    console.log(answerNo);

                    this.answerYes.enabled = false;
                    this.answerNo.enabled = false;
                    if (undefined !== current_step.next) {
                        current_line = story.map(e => e.label).indexOf(current_step.next);
                    } else {
                        current_line = current_line + 1;
                    }
                }
                // execute if the step contains a question (q)
                else if (undefined !== current_step.question){
                    this.textcontent.element.text = current_step.question;
                    console.log('Were displaying a question here');
                    this.answerYes.enabled = true;
                    this.answerNo.enabled = true;
                }
                
            }
            // register yes answer if a question is displayed
            else if ((this.app.keyboard.wasPressed(pc.KEY_Y)) && (undefined !== current_step.question)){
                        console.log('key press was registered!');
                        current_line = story.map(e => e.label).indexOf('take_yes');
                        
                    }
            // register no answer if a question is displayed
            else if ((this.app.keyboard.wasPressed(pc.KEY_N)) && (undefined !== current_step.question)){
                        console.log('key press was registered!');
                        current_line = story.map(e => e.label).indexOf('take_no');
                    }
        break;
        }
    }
};

// swap method called for script hot-reloading
// inherit your script state here
// Dialogue.prototype.swap = function(old) { };

// to learn more about script anatomy, please read:
// http://developer.playcanvas.com/en/user-manual/scripting/

Here is the script component under DialogueBox in the Editor:

ScriptComponent

Please post a screenshot of the Dialogue script on the script component from the editor.

I’ve added it, sorry for the trouble!

Find the dialogue.js line number in the error message and check which attribute this line is trying to access.

Better yet, can you post a link to your project?

Here is where the error is located:

Here’s the link to my project (I’m not sure how to share projects, let me know if you’re unable to access it!):

https://playcanvas.com/editor/scene/1479043

Thanks, I can access it! What do I have to do in the launched project to trigger the error?

Edit: I don’t see the Dialogue.js script here, is this the right project?

The Dialogue.js script should be inside. Can you send a screenshot of what you’re seeing if it still isn’t there?

As for how to trigger the error, press “enter” for the dialogue!

Here is what I’m seeing:
Screenshot from 2022-08-18 12-12-48

Do you have multiple PlayCanvas projects?

That’s weird, it’s missing a lot of scripts :o Is there a different way I can send you my project link?

No this is my only project :o

Probably @cdev is working in another branch. Other branches are not visible for other users.

2 Likes

Thank you, that makes sense! How should I go about sharing my progress in this branch?

As far as I know this is not possible. You have to merge the branch with the master branch or add @Devortel to your project.

1 Like

I’ve merged it @Devortel ! Let me know if it works! Thank you for the help @Albertos :smile:

https://playcanvas.com/editor/scene/1479043

1 Like

Ah I see, now that you’ve linked the entities via script attributes, you have to take these four lines out of your initialize() function:

this.text = this.app.root.findByName("DialogueBox");
this.textcontent = this.entity.findByName("Textcontent");
this.answerYes = this.entity.findByPath('UI/DialogueBox/AnswerYes');
this.answerNo = this.entity.findByPath('UI/DialogueBox/AnswerNo');
2 Likes

Oh it works now! I still have errors on other areas but I can work on that. Thank you very much! :smiley:

One small question before closing the topic, I’m trying to execute the dialogue using buttons instead of keyboard inputs. I’m planning to replace some parts of the script with this method - do you think enabling / disabling the text entity with button inputs would work or would it be impractical?

Buttons or keyboard inputs, or both would work fine, not impractical at all. Having multiple options are better for the player’s experience too, they don’t have to guess about the controls as much.

My little bit of advice would be to separate the dialogue progression logic from the input event listeners. For example, make a _progressDialogue() function and then you can call it from a variety of input listeners, buttons, keyboard keys, etc.

this.nextButton.button.on('click', this._progressDialogue, this);
this.app.keyboard.on(pc.EVENT_KEYDOWN, this._progressDialogue, this);

(Not a complete code example)

1 Like