Skip to content
Advertisement

Limit dragged line to an arc / radius of a given length

I’m currently using Phaser 3, although my question isn’t technically restricted to that framework, as it’s more of a general JS/canvas/maths question, but:

I have a line drawn with graphics(). It’s anchored at one end, and the other end is draggable. I made a quick demo and so far, so good – you can see what I have already on CodePen.

Dragging the marker around and redrawing the line is no problem, but what I’d like is for that line to have a maximum length of 100, so even if you’re still dragging beyond that point, the line would still follow the mouse, but not get any longer than 100. Dragging inside that maximum radius, the line would shrink as normal.

I’ve put together a visual that hopefully explains it: Image showing the line being dragged, with a limiting arc where the drawing of the line stops, but the mouse can carry on.

The issue is that I suspect this is VERY MATHS and I am very, very weak with maths. Could anyone explain like I’m five what I need to do to my code to achieve this?

Edit: Adding code in a snippet here, as requested:

var config = {
    type: Phaser.AUTO,
    width: 800,
    height: 400,
    backgroundColor: '#2d2d2d',
    parent: 'phaser-example',
    scene: {
        preload: preload,
        create: create,
        update: update
    }
};

var path;
var curve;
var graphics;

var game = new Phaser.Game(config);

function preload() {
    this.load.spritesheet('dragcircle', 'https://labs.phaser.io/assets/sprites/dragcircle.png', { frameWidth: 16 });
}

function create() {
    graphics = this.add.graphics();

    path = { t: 0, vec: new Phaser.Math.Vector2() };

    curve = new Phaser.Curves.Line([ 400, 390, 300, 230 ]);

    var point0 = this.add.image(curve.p0.x, curve.p0.y, 'dragcircle', 0);
  
    var point1 = this.add.image(curve.p1.x, curve.p1.y, 'dragcircle', 0).setInteractive();
  
    point1.setData('vector', curve.p1);

    this.input.setDraggable(point1);

    this.input.on('drag', function (pointer, gameObject, dragX, dragY) {
        gameObject.x = dragX;
        gameObject.y = dragY;
      
        gameObject.data.get('vector').set(dragX, dragY);
    });

    this.input.on('dragend', function (pointer, gameObject) {
      let distance = Phaser.Math.Distance.Between(curve.p0.x, curve.p0.y, curve.p1.x, curve.p1.y);
      console.log(distance);
    });
}

function update() {
    graphics.clear();
    graphics.lineStyle(2, 0xffffff, 1);

    curve.draw(graphics);

    curve.getPoint(path.t, path.vec);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/phaser/3.55.2/phaser.min.js"></script>

Advertisement

Answer

You are right, you would need some math, but phaser has many helper functions, that will do the heavy lifting.

The main idea is, of this solution is

  1. define a maxLength
  2. get the the new point on drag, and create a real Phaser Vector2
    • here is some math is needed, to create the vector, just calculate destination point minus origin point
      new Phaser.Math.Vector2(pointer.x - point0.x, pointer.y - point0.y) (origin point being the starting point of the desired vector, and destination point being the mouse pointer)
  3. calculate the length of the created vector and compare it with the maxLength
    • if too long adjust the vector, with the handy function setLength (link to the documentation, this is where you would have needed math, but thankfully Phaser does it for us)
  4. set the new coordinates for point1 and the curve endpoint

Here a quick demo (based on your code):

var config = {
    type: Phaser.AUTO,
    width: 500,
    height: 170,
    scene: {
        preload: preload,
        create: create,
        update: update
    }
};

var curve;
var graphics;

var game = new Phaser.Game(config);

function preload() {
    this.load.spritesheet('dragcircle', 'https://labs.phaser.io/assets/sprites/dragcircle.png', { frameWidth: 16 });
}

function create() {
    graphics = this.add.graphics();

    curve = new Phaser.Curves.Line([ config.width/2, config.height - 20, config.width/2, 10 ]);

    // define a length, could be a global constant
    let maxLength = curve.p0.y - curve.p1.y;

    var point0 = this.add.image(curve.p0.x, curve.p0.y, 'dragcircle', 0);
    var point1 = this.add.image(curve.p1.x, curve.p1.y, 'dragcircle', 0).setInteractive();
  
    this.input.setDraggable(point1);
    
    // Just add for Debug Info
    this.add.circle(curve.p0.x, curve.p0.y, maxLength)
      .setStrokeStyle(1, 0xffffff, .5)

    this.input.on('drag', function (pointer) {
        
        let vector = new Phaser.Math.Vector2(pointer.x - point0.x, pointer.y - point0.y);
        let distance = Phaser.Math.Distance.Between( point0.x, point0.y, pointer.x, pointer.y);
        
        if(distance > maxLength){
            vector.setLength(maxLength);
        }
        
        point1.x = point0.x + vector.x;
        point1.y = point0.y + vector.y;     
        curve.p1.x = point1.x;
        curve.p1.y = point1.y;

    });

    // NOT REALLY NEEDED
    /*this.input.on('dragend', function (pointer, gameObject) {
      let distance = Phaser.Math.Distance.Between(curve.p0.x, curve.p0.y, curve.p1.x, curve.p1.y);
      console.log(distance);
    });*/
}

function update() {
    graphics.clear();
    graphics.lineStyle(2, 0xffffff, 1);
    curve.draw(graphics);
}
<script src="https://cdn.jsdelivr.net/npm/phaser@3.55.2/dist/phaser.js"></script>

Optional – Code Version using Phaser.GameObjects.Line:

This uses less code, and thanks to the Line GameObject (link to Documentation), you can directly use the vector to update the line, and also don’t need the update function, graphics and so.

const config = {
    type: Phaser.CANVAS,
    width: 500,
    height: 160,
    scene: {
        create
    }
};

const game = new Phaser.Game(config);
const MAX_LINE_LENGTH = 100;

function create() {

    let points = [ {x: config.width/2, y: config.height - 20}, {x: config.width/2, y: config.height - 120} ];
    
    let point0 = this.add.circle(points[0].x, points[0].y, 6)
        .setStrokeStyle(4, 0xff0000);
    let point1 = this.add.circle(points[1].x, points[1].y, 6)
        .setStrokeStyle(4, 0xff0000)
        .setInteractive();
  
    this.input.setDraggable(point1);
    
        // Just add for Debug Info
    this.add.circle(point0.x, point0.y, MAX_LINE_LENGTH)
      .setStrokeStyle(1, 0xffffff, .5);
    
    let line = this.add.line(points[0].x, points[0].y, 0, 0, 0, -100, 0x00ff00)
        .setOrigin(0);
  
    this.input.on('drag', function (pointer) {
        
        let vector = new Phaser.Math.Vector2(pointer.x - point0.x, pointer.y - point0.y);
        let distance = Phaser.Math.Distance.Between( point0.x, point0.y, pointer.x, pointer.y);
        
        if(distance > MAX_LINE_LENGTH){
            vector.setLength(MAX_LINE_LENGTH);
        }
        
        point1.x = point0.x + vector.x;
        point1.y = point0.y + vector.y;     
      
        line.setTo(0, 0, vector.x, vector.y);
    });

}
<script src="//cdn.jsdelivr.net/npm/phaser@3.55.2/dist/phaser.js"></script>
User contributions licensed under: CC BY-SA
7 People found this is helpful
Advertisement