rexrainbow / phaser3-rex-notes

Notes of phaser3 engine
MIT License
1.18k stars 260 forks source link

areaclick event blocked for bbCodeText in a TextBox with interactive set to true #392

Closed xanmankey closed 8 months ago

xanmankey commented 8 months ago

Hi,

I just wanted to point out something that I noticed, it could be an entirely intentional choice or just a bug in my code. I'm making a dialog system for a game, utilizing the areaclick event of BBCodeText for dialog options and TextBox for displaying the text, but when I set textBox to interactive, the areaclick event never calls.

Here's my code

import TextBox from "phaser3-rex-plugins/templates/ui/textbox/TextBox";
import { CustomScene, DialogPath } from "../objects/scene";
import BBCodeText from "phaser3-rex-plugins/plugins/bbcodetext";

const COLOR_PRIMARY = 0x4e342e;
const COLOR_DARK = 0x260e04;
const COLOR_LIGHT = 0xc4a484;
const GetValue = Phaser.Utils.Objects.GetValue;

interface TextBoxConfig {
  wrapWidth?: number | undefined;
  fixedWidth?: number | undefined;
  fixedHeight?: number | undefined;
  titleText?: string | undefined;
}
// TODO: play around w/ why the yes-no buttons render so off
export const createTextBox = (
  scene: CustomScene,
  x: number,
  y: number,
  text: string,
  config: TextBoxConfig,
  dialogPaths: DialogPath[] | undefined
) => {
  const wrapWidth = GetValue(config, "wrapWidth", 0);
  const fixedWidth = GetValue(config, "fixedWidth", 0);
  const fixedHeight = GetValue(config, "fixedHeight", 0);
  const titleText = GetValue(config, "titleText", undefined);

  var textBox = scene.rexUI.add
    .textBox({
      x: x,
      y: y,
      background: scene.rexUI.add.roundRectangle(
        x,
        y,
        fixedWidth,
        fixedHeight,
        20,
        COLOR_PRIMARY,
        COLOR_DARK
      ),
      icon: scene.rexUI.add.roundRectangle(
        x,
        y,
        fixedHeight,
        fixedWidth,
        20,
        COLOR_LIGHT
      ),
      text: getBBCodeText(
        scene,
        wrapWidth,
        fixedWidth,
        fixedHeight,
        text,
        dialogPaths
      ),
      action: scene.add
        .image(0, 0, "nextPage")
        .setTint(COLOR_DARK)
        .setVisible(false),
      title: titleText
        ? scene.add.text(0, 0, titleText, { fontSize: "24px" })
        : undefined,
      separator: titleText
        ? scene.rexUI.add.roundRectangle(
            x,
            y,
            fixedHeight,
            fixedWidth,
            20,
            COLOR_LIGHT
          )
        : undefined,
      space: {
        left: 20,
        right: 20,
        top: 20,
        bottom: 20,
        icon: 10,
        text: 10,
        separator: 6,
      },
      align: {
        title: "center",
      },
    })
    .setOrigin(0)
    // .setInteractive() - This line prevents the areaclick handler from working
    .setDepth(7);

  textBox.on(
    "pageend",
    function () {
      console.log("page end");
      if (dialogPaths == undefined) {
        if (this!.isLastPage) {
          console.log("Last page, returning!");
          scene.isTextBoxCompleted = true;
          return;
        }
      }

      var icon = this.getElement("action").setVisible(true);
      this.resetChildVisibleState(icon);
      icon.y -= 30;
      scene.tweens.add({
        targets: icon,
        y: "+=30", // '+=100'
        ease: "Bounce", // 'Cubic', 'Elastic', 'Bounce', 'Back'
        duration: 500,
        repeat: 0, // -1: infinity
        yoyo: false,
      });
    },
    textBox
  );

  textBox.layout();

  return textBox;
};

// TODO: make the bbcodetext selectable if selectable is specified
// I also want my font size to be scalable...
export const getBBCodeText = (
  scene: CustomScene,
  wrapWidth: number | undefined,
  fixedWidth: number | undefined,
  fixedHeight: number | undefined,
  text: string,
  dialogPaths: DialogPath[] | undefined
) => {
  // Adapt to fit each line on a page
  return scene.rexUI.add
    .BBCodeText(0, 0, text, {
      // fixedWidth: wrapWidth,
      fixedHeight: fixedHeight,
      fontSize: "20px",
      wrap: {
        mode: "word",
        width: wrapWidth,
      },
      maxLines: 3,
      interactive: true,
      delimiters: "[]",
    })
    .on("areaclick", function (key: string) {
      console.log("Dialog path callback function works");
    });
};

And here's where I call my code

if (tile instanceof Phaser.Tilemaps.Tile) {
          if (Input.Keyboard.JustDown(spacebar)) {
            // Learned about Ctrl + Shift + \: go to bracket, a game changer
            if (!this.isTextBoxCompleted) {
              switch (tile.index) {
                case 0:
                  // Book
                  // NOTE that because position is inconsistent, tilemap layers are used to identify tiles
                  switch (tile.tilemapLayer) {
                    case objLayer:
                      this.interact(
                        "'42: the answer to life, the universe, and everything' - The Hitchhiker's Guide to the Galaxy"
                      );
                      break;
                    case objLayer2:
                      this.interact(
                        "'A book has been taken. A book has been taken? You summoned the Watch, Carrot drew himself up proudly, because someone's taken a book? You think that's worse than murder?' - Guards Guards"
                      );
                      break;
                    case objLayer3:
                      this.interact(
                        "'The world was to me a secret which I desired to divine.' - Frankenstein"
                      );
                      break;
                    case objLayer4:
                      this.interact(
                        "'Success is not delivering a feature; success is learning how to solve the customer’s problem.' - The Lean Startup"
                      );
                      break;
                    case objLayer5:
                      this.interact(
                        'But until a person can say deeply and honestly, "I am what I am today because of the choices I made yesterday," that person cannot say, "I choose otherwise."\' - The Seven Habits of Highly Effective People'
                      );
                      break;
                    case objLayer6:
                      this.interact(
                        "'Is there intelligence without life? Is there mind without communication? Is there language without living? Is there thought without experience?' - Alan Turing: The Enigma"
                      );
                      break;
                    default:
                      break;
                  }
                case 1:
                  // Electrowav

                  this.interact(
                    "It appears to be a shameless plug for [url=https://www.youtube.com/@electrowav]youtube.com/@electrowav[/url]"
                  );
                  break;
                case 2:
                  // Piano upper left
                  break;
                case 3:
                  // Piano upper right
                  break;
                case 4:
                  // Shoe
                  break;
                case 5:
                  // Soccer Ball
                  break;
                case 6:
                  // Basketball
                  break;
                case 7:
                  // Blank
                  break;
                case 8:
                  break;
                case 9:
                  // PlantL
                  this.interact(
                    "Don't mind me, I'm just hanging out with my buddy, yellow"
                  );
                  break;
                case 10:
                  // PlantR
                  this.interact(
                    "Don't mind me, I'm just hanging out with my buddy, green"
                  );
                  break;
                case 11:
                  // Piano Bottom Left
                  break;
                case 12:
                  // Piano Bottom Right
                  break;
                case 13:
                  // Shoe With Logo
                  break;
                case 14:
                  // Trumpet standing up
                  this.interact(
                    "Now's your chance, don't blow it!\n\nYou're now an accomplished trumpet player",
                    { audioKey: "trumpet", pageNumber: 2, loop: false }
                  );
                  break;
                case 15:
                  // TableL
                  break;
                case 16:
                  // TableR
                  break;
                case 17:
                  // Bookshelf top left
                  switch (tile.tilemapLayer) {
                    case objLayer:
                      this.interact("One book, two book, red book, blue book");
                      break;
                    case objLayer2:
                      this.interact(
                        "On-time book, overdue book, old book, new book"
                      );
                      break;
                    case objLayer3:
                      this.interact(
                        "This one has 5 stars. This one has memoirs."
                      );
                      break;
                    case objLayer4:
                      this.interact("Say! What a lot of books there are");
                      break;
                    default:
                      break;
                  }
                  break;
                case 18:
                  // Bookshelf top right
                  switch (tile.tilemapLayer) {
                    case objLayer:
                      this.interact("One book, two book, red book, blue book");
                      break;
                    case objLayer2:
                      this.interact(
                        "On-time book, overdue book, old book, new book"
                      );
                      break;
                    case objLayer3:
                      this.interact(
                        "This one has 5 stars. This one has memoirs."
                      );
                      break;
                    case objLayer4:
                      this.interact("Say! What a lot of books there are");
                      break;
                    default:
                      break;
                  }
                  break;
                case 19:
                  // Shoes
                  this.interact(
                    "All of a sudden you feel as though you're looking at a pair of shoes."
                  );
                  break;
                case 20:
                  // Xbox
                  this.interact(
                    "Xbox: It's a controller... kind of. \nThe buttons are all swapped... \n\nWhat kind of person thought that this would be a good idea?"
                  );
                  break;
                case 21:
                  // Switch
                  // TODO: selectable dialog options (use the below syntax at: https://rexrainbow.github.io/phaser3-rex-notes/docs/site/bbcodetext/?h=bbco)
                  // [area=key]text[/area]
                  this.interact(
                    "It's a game console; turn it on?\n\n[area=yes]yes[/area]                 [area=no]no[/area]\n",
                    undefined,
                    [
                      {
                        key: "yes",
                        interactOptions: {
                          text: "It's your favorite, The Legend of Zeldo: Smell of the Forest!",
                          audio: {
                            audioKey: "botw",
                            pageNumber: 2,
                            loop: true,
                          },
                          dialogPaths: undefined,
                        },
                      },
                      {
                        key: "no",
                        interactOptions: undefined,
                      },
                    ]
                  );
                  break;
                case 22:
                  // Code for 22
                  break;
                case 23:
                  // Code for 23
                  break;
                case 24:
                  // Code for 24
                  break;
                case 25:
                  // Code for 25
                  break;
                case 26:
                  // Code for 26
                  break;
                case 27:
                  // Code for 27
                  break;
                case 28:
                  // Code for 28
                  break;
                // case 29:
                //   // Code for 29
                //   break;
                // case 30:
                //   // Code for 30
                //   break;
                // case 31:
                //   // Code for 31
                //   break;
                // case 32:
                //   // Code for 32
                //   break;
                // case 33:
                //   // Code for 33
                //   break;
                // case 34:
                //   // Code for 34
                //   break;
                // case 35:
                //   // Code for 35
                //   break;
                // case 36:
                //   // Code for 36
                //   break;
                // case 37:
                //   // Code for 37
                //   break;
                // case 38:
                //   // Code for 38
                //   break;
                // // case 39:
                // //   // Code for 39
                // //   break;
                // case 40:
                //   // Code for 40
                //   break;
                // case 41:
                //   // Code for 41
                //   break;
                // case 42:
                //   // Code for 42
                //   break;
                // case 43:
                //   // Code for 43
                //   break;
                // case 44:
                //   // Code for 44
                //   break;
                // case 45:
                //   // Code for 45
                //   break;
                // case 46:
                //   // Code for 46
                //   break;
                // // case 47:
                // //   // Code for 47
                // //   break;
                // case 48:
                //   // Code for 48
                //   break;
                // case 49:
                //   // Code for 49
                //   break;
                // case 50:
                //   // Code for 50
                //   break;
                // case 51:
                //   // Code for 51
                //   break;
                // case 52:
                //   // Code for 52
                //   break;
                // case 53:
                //   // Code for 53
                //   break;
                // case 54:
                //   // Code for 54
                //   break;
                // case 55:
                //   // Code for 55
                //   break;
                default:
                  // Note that if the textbox has interactive enabled, you can't interact
                  // with the bbCodeText (it overwrites it)
                  createTextBox(
                    this,
                    0,
                    200,
                    "[area=yes]yes[/area]                 [area=no]no[/area]\n",
                    // "Test text",
                    {
                      wrapWidth: 275,
                    },
                    [
                      {
                        key: "yes",
                        interactOptions: {
                          text: "It's your favorite, The Legend of Zeldo: Smell of the Forest!",
                          audio: {
                            audioKey: "botw",
                            pageNumber: 2,
                            loop: true,
                          },
                          dialogPaths: undefined,
                        },
                      },
                      {
                        key: "no",
                        interactOptions: undefined,
                      },
                    ]
                  );
                  break;
              }
            } else {
              // Reset flow of game state
              console.log("Destroying interact box");
              this.interactBox!.destroy();
              this.interactBox = undefined;
              this.isTextBoxCompleted = false;
              this.player.isInteracting = false;
            }
          }
        }
      }
    );

Thanks for the incredible package and UI!

rexrainbow commented 8 months ago

Since TextBox and BBCodeText are overlapping, only one game object will receive touch event. In your case, TextBox is created after BBCodeText (new game object will render above old game object if they have the same depth), so it will get touch event but BBCodeText won't.

xanmankey commented 8 months ago

Completely missed that, thank you! Good to know that's the case for the future