Search code examples
javascriptphaser-frameworkphaserjs

Phaser object not Draggable


This code does not allow the rectangle w/topbar to be draggable. What do I need to change? I created a scrollabel Phaser scene and can get the 'box' to draw but it is not draggable. When the user selects the topbar I want to be able to move the box in any direction within the phaser container.

This code creates the phaser scene and the scene is scrollabel x and y but the box crateed can not be selected or dragged.

let currentScene; // Global variable to store the current scene
let mainRect, topBar; // Global variables to store the main rectangle and top bar
let isDragging = false; // Variable to track if dragging is in progress

function preload() {
  console.log("Preload function called"); // Log when preload is called
  // Load assets here if needed
}

function create() {
  console.log("Create function called"); // Log when create is called
  // Assign the created scene to currentScene
  currentScene = this;

  // Log the current scene
  console.log("Current Scene:", currentScene);

  // Enable camera controls for scrolling
  this.cameras.main.setBounds(0, 0, 5000, 300); // Set the bounds of the camera
  this.cameras.main.setScroll(0, 0); // Initialize the camera position

  // Add event listener to handle two-finger scrolling within the Phaser canvas
  this.input.on('wheel', (pointer, gameObjects, deltaX, deltaY, deltaZ) => {
    if (!isDragging) {
      this.cameras.main.scrollX += deltaX;
      this.cameras.main.scrollY += deltaY;
    }
  });

  // Add touch event listeners to handle touch-based scrolling within the Phaser canvas
  this.input.on('pointerdown', (pointer) => {
    if (pointer.isDown && !isDragging) {
      this.input.dragStartX = pointer.x;
      this.input.dragStartY = pointer.y;
      this.input.cameraStartX = this.cameras.main.scrollX;
      this.input.cameraStartY = this.cameras.main.scrollY;
    }
  });

  this.input.on('pointermove', (pointer) => {
    if (pointer.isDown && !isDragging) {
      const deltaX = pointer.x - this.input.dragStartX;
      const deltaY = pointer.y - this.input.dragStartY;
      this.cameras.main.scrollX = this.input.cameraStartX - deltaX;
      this.cameras.main.scrollY = this.input.cameraStartY - deltaY;
    }
  });

  // Expose createActivityObject to the global scope for button click
  window.createActivityObject = () => createActivityObjectInternal(currentScene);
}

function update() {
  // Log the positions of the main rectangle and top bar during the update
  if (mainRect && topBar) {
    console.log(`Update - mainRect: (${mainRect.x}, ${mainRect.y}), topBar: (${topBar.x}, ${topBar.y})`);
  }
}

const config = {
  type: Phaser.AUTO,
  width: 5000, // Set the width of the visible area
  height: 300, // Set the height of the visible area
  parent: "phaser-container",
  backgroundColor: 'rgb(200, 249, 251)', // Set the background color of the Phaser scene
  scene: {
    preload: preload,
    create: create,
    update: update,
  },
  render: {
    pixelArt: true, // Enable pixel art rendering for sharper edges
    antialias: false, // Disable antialiasing for sharper edges
  },
};

document.addEventListener("DOMContentLoaded", () => {
  console.log("DOMContentLoaded event fired"); // Log when DOMContentLoaded event fires
  let game = new Phaser.Game(config);
  console.log("Game:", game); // Log the game object

  // Add event listener to the button to create the draggable object
  document.getElementById("create-activity-button").addEventListener("click", () => {
    if (currentScene) {
      createActivityObjectInternal(currentScene);
    }
  });
});

// Internal function to create a draggable rectangle with a top bar
function createActivityObjectInternal(scene) {
  console.log("Creating activity object"); // Log the function call
  console.log("The scene is: ", scene); // Log the scene object

  if (!scene) {
    console.error("Scene is not defined");
    return;
  }

  const scale = 0.15; // Scale factor to reduce size to 15%
  const rectWidth = 400 * scale * 1.5; // Increase width by 50%
  const rectHeight = 100 * scale; // Keep height the same
  const topBarHeight = 12 * scale;
  const rectX = 100;
  const rectY = 100;

  // Create the main rectangle
  mainRect = scene.add.rectangle(rectX, rectY + topBarHeight, rectWidth, rectHeight - topBarHeight, 0xffffff).setOrigin(0);
  mainRect.setStrokeStyle(2 * scale, 0x000000);
  mainRect.setDepth(1); // Ensure the main rectangle is on top
  console.log("Main rectangle created:", mainRect); // Log the main rectangle creation

  // Create the top bar
  topBar = scene.add.rectangle(rectX, rectY, rectWidth, topBarHeight, 0xffa500).setOrigin(0);
  topBar.setStrokeStyle(2 * scale, 0xffa500);
  topBar.setDepth(2); // Ensure the top bar is on top
  console.log("Top bar created:", topBar); // Log the top bar creation

  // Ensure the topBar is created before making it draggable
  if (topBar) {
    console.log("Top bar is not null or undefined"); // Log the top bar check

    // Check if input plugin is available
    if (scene.input) {
      console.log("Input plugin is available"); // Log input plugin availability

      // Add the top bar to the input system
      topBar.setInteractive();
      scene.input.setDraggable(topBar);
      console.log("Top bar added to input system and set as draggable"); // Log input system addition and draggable setting

      // Handle dragging
      scene.input.on("dragstart", (pointer, gameObject) => {
        console.log("Drag start:", gameObject); // Log drag start
        isDragging = true; // Disable scrolling
      });

      scene.input.on("drag", (pointer, gameObject, dragX, dragY) => {
        if (gameObject === topBar) {
          mainRect.x = dragX;
          mainRect.y = dragY + topBarHeight;
          topBar.x = dragX;
          topBar.y = dragY;
          console.log("Dragging:", { dragX, dragY }); // Log dragging coordinates
        }
      });

      scene.input.on("dragend", (pointer, gameObject) => {
        console.log("Drag end:", gameObject); // Log drag end
        isDragging = false; // Re-enable scrolling
      });
    } else {
      console.error("Input plugin is not available");
    }
  } else {
    console.error("Failed to create topBar");
  }
}

Solution

  • Well first of all there are two point I would like to make:

    • Great question and good documentation
    • BUT you should only post relevant code to make the question easier/fast to read and solve the issue (AND while making a MRE you might even find the bug yourself).

    This out of the way, while shortening your code (see demo below), I go a functioning build.

    I think the Problem is scene.input.setDraggable(topBar);, I removed this line an activated the dragging functionality through the .setInteractive({ draggable: true }) method on the topBar.
    With this the dragging should work.

    Short Demo:
    (ShowCasing this)

    let currentScene;
    let isDragging = false;
    
    function create() {
      console.log("Create function called"); // Log when create is called
      // Assign the created scene to currentScene
      currentScene = this;
    
      console.log("Current Scene:", currentScene);
    
      this.cameras.main.setBounds(0, 0, 5000, 300);
      this.cameras.main.setScroll(0, 0);
    
      window.createActivityObject = () => createActivityObjectInternal(currentScene);
    }
    
    const config = {
      width: 500,
      height: 180,
      parent: "phaser-container",
      scene: { create },
    };
    
    new Phaser.Game(config);
    
    document.getElementById("create-activity-button").addEventListener("click", () => {
      if (currentScene) {
        createActivityObjectInternal(currentScene);
      }
    });
    
    
    function createActivityObjectInternal(scene) {
    
      if (!scene) {
        console.error("Scene is not defined");
        return;
      }
    
      const rectWidth = 100;
      const rectHeight = 50;
      const topBarHeight = 20;
      const pos = {x: 100, y:100};
    
      let mainRect = scene.add.rectangle(pos.x, pos.y + topBarHeight, rectWidth, rectHeight - topBarHeight, 0xffffff).setOrigin(0).setStrokeStyle(2, 0x000000);
      let topBar = scene.add.rectangle(pos.x, pos.y, rectWidth, topBarHeight, 0xffa500).setOrigin(0).setStrokeStyle(1 , 0xffa500).setInteractive({ draggable: true });
      
    
      scene.input.on("drag", (pointer, gameObject, dragX, dragY) => {
        if (gameObject === topBar) {
          topBar.x = dragX;
          topBar.y = dragY; 
          mainRect.x = dragX;
          mainRect.y = dragY + topBarHeight;
        }
      });
    
    }
    
    console.clear();
    document.body.style = 'margin:0;';
    <script src="//cdn.jsdelivr.net/npm/phaser/dist/phaser.min.js"></script>
    
    <button id="create-activity-button">ADD STUFF</button>
    <br />

    btw.: if you are creating multipe "Items" with the Button, defining mainRect and topBar global will create an issue. For the demo I moved the definition to the function createActivityObjectInternal scope, but if you want to use them outside this function, you would have to find a better solution.