Great fun.
/**
* Draw an arrow on a Graphics Game Object.
*
* The arrowhead is an isosceles triangle of the given `width` and `height`.
*
* @param {Phaser.GameObjects.Graphics} graphics
* @param {number} x1 - The x coordinate of the start point of the line.
* @param {number} y1 - The y coordinate of the start point of the line.
* @param {number} x2 - The x coordinate of the end point of the line.
* @param {number} y2 - The y coordinate of the end point of the line.
* @param {number} width - The width of the arrowhead, perpendicular to the line.
* @param {number} height - The length of the arrowhead, parallel to line.
* @param {boolean} [fill=false] - Fill the arrowhead (true) or stroke it (false).
*/
function arrow(graphics, x1, y1, x2, y2, width, height, fill = false) {
graphics.lineBetween(x1, y1, x2, y2);
const dx = x2 - x1;
const dy = y2 - y1;
const lineLength = Math.sqrt(dx * dx + dy * dy);
// Line unit vector
const udx = dx / lineLength;
const udy = dy / lineLength;
// Perpendicular unit vector
const pdx = -udy;
const pdy = udx;
// Arrowhead base vertices
const x3 = x2 - height * udx + width * pdx;
const y3 = y2 - height * udy + width * pdy;
const x4 = x2 - height * udx - width * pdx;
const y4 = y2 - height * udy - width * pdy;
if (fill) {
graphics.fillTriangle(x2, y2, x3, y3, x4, y4);
} else {
graphics.beginPath().moveTo(x3, y3).lineTo(x2, y2).lineTo(x4, y4).strokePath();
}
}