• Runtimes
  • I would like to ask about moving the bone.

I'm giving movement to the bone, but when I move the bone in js, I want to move it by pressing length. What should I do?

`

var vineDemo = function (canvas, bgColor) {
var COLOR_INNER = new spine.Color(0.0, 0, 0, 0.0);
var COLOR_OUTER = new spine.Color(0.0, 0, 0, 0.0);
var COLOR_INNER_SELECTED = new spine.Color(0.0, 0, 0.0, 0.0);
var COLOR_OUTER_SELECTED = new spine.Color(0.0, 0, 0.0, 0.0);

var canvas, gl, renderer, input, assetManager;
var skeleton, state, bounds;
var target = null;
var hoverTargets = [null, null, null, null, null, null, null];
var controlBones = [
	// "bone",
	"bone2", "bone3", "bone4", "bone5"
	,	"bone6", "bone7", "bone8", "bone9"
	,	"bone10", "bone11", "bone12",	"bone13"
	, "bone14"
	, "bone15"
	,	"bone16", "bone17", "bone18",	"bone19"
	, "bone20", "bone21", "bone22", "bone23",	"bone24", "bone25", "bone26"
	, "middlebone1", "middlebone2", "middlebone3", "middlebone4", "middlebone5", "middlebone6", "middlebone7", "middlebone8", "middlebone9", "middlebone10"
	,	"bone27", "bone28", "bone29"
	,	"bone30", "bone31", "bone32"
	// , "bone33", "bone34",	"bone35", "bone36", "bone37",	"bone38", "bone39"
	// , "bone40",	"bone41", "bone42", "bone43", "bone44", "bone45",	"bone46", "bone47", "bone48",	"bone49"
	// , "bone50",	"bone51", "bone52", "bone53", "bone54", "bone55",	"bone56", "bone57", "bone58",	"bone59"
	// , "bone60",	"bone61", "bone62", "bone63"
	// , "bone64", "bone65",	"bone66", "bone67", "bone68",	"bone69"
	// , "bone70",	"bone71", "bone72", "bone73", "bone74", "bone75",	"bone76", "bone77", "bone78",	"bone79"
	// , "bone80"
	// ,	"bone81", "bone82", "bone83", "bone84", "bone85",	"bone86", "bone87", "bone88",	"bone89"
	// , "bone90",	"bone91", "bone92", "bone93", "bone94", "bone95",	"bone96", "bone97", "bone98",	"bone99"
	// , "bone100",	"bone101", "bone102", "bone103", "bone104", "bone105",	"bone106", "bone107", "bone108",	"bone109"
	// , "bone110",	"bone111", "bone112", "bone113", "bone114", "bone115",	"bone116", "bone117"
	// , "bone120",	"bone121", "bone122", "bone123", "bone124", "bone125",	"bone126", "bone127", "bone128",	"bone129"
	// , "bone130",	"bone131", "bone132", "bone133", "bone134", "bone135",	"bone136", "bone137", "bone138",	"bone139"
	// , "bone140",	"bone141", "bone142", "bone143", "bone144", "bone145",	"bone146", "bone147"
	// , "bone148",	"bone149"

];
var coords = new spine.Vector3(), temp = new spine.Vector3(), temp2 = new spine.Vector2();
// var playButton, timeLine, isPlaying = false, playTime = 5;

bgColor = new spine.Color(0, 0, 0, 0);
canvas.context = new spine.ManagedWebGLRenderingContext(canvas, { alpha: true, premultipliedAlpha: false });

function init() {
	gl = canvas.context.gl;
	renderer = new spine.SceneRenderer(canvas, gl);
	input = new spine.Input(canvas);
	assetManager = new spine.AssetManager(gl, spineDemos.path, spineDemos.downloader);

	assetManager.loadTextureAtlas("skeleton.atlas");
	assetManager.loadJson("skeleton.json");

}

function loadingComplete() {

	var atlasLoader = new spine.AtlasAttachmentLoader(assetManager.get("skeleton.atlas"));
	var skeletonJson = new spine.SkeletonJson(atlasLoader);
	var skeletonData = skeletonJson.readSkeletonData(assetManager.get("skeleton.json"));
	skeleton = new spine.Skeleton(skeletonData);
	// var mesh = skeleton.getAttachmentByName("hairstyle", "hairstyle");
	skeleton.setToSetupPose();
	skeleton.updateWorldTransform();
	var offset = new spine.Vector2();
	bounds = new spine.Vector2();
	skeleton.getBounds(offset, bounds, []);
	skeleton.updateWorldTransform();

	skeleton.color = { r: 1, g: 1, b: 1, a: 1 };


	// mesh.vertices[0] = 0; // x of vertex 0
	// mesh.vertices[1] = 0; // y of vertex 0
	// mesh.vertices[2] = 0; // x of vertex 1
	// mesh.vertices[3] = 0; // y of vertex 1

	renderer.camera.position.x = offset.x + bounds.x / 2;
	renderer.camera.position.y = offset.y + bounds.y / 2;

	renderer.skeletonDebugRenderer.drawMeshHull = false;
	renderer.skeletonDebugRenderer.drawMeshTriangles = false;

	setupUI();
	setupInput();

}

function setupUI() {

	renderer.skeletonDebugRenderer.drawPaths = false;
	renderer.skeletonDebugRenderer.drawBones = false;
}

function setupInput() {


	input.addListener({
		down: function (x, y) {
			target = spineDemos.closest(canvas, renderer, skeleton, controlBones, hoverTargets, x, y);
		},
		up: function (x, y) {
			target = null;
		},
		dragged: function (x, y) {
			spineDemos.dragged(canvas, renderer, target, x, y);
		},
		moved: function (x, y) {
			spineDemos.closest(canvas, renderer, skeleton, controlBones, hoverTargets, x, y);
		}
	});
}

function render() {


	skeleton.updateWorldTransform();

	renderer.camera.viewportWidth = bounds.x * 1.2;
	renderer.camera.viewportHeight = bounds.y * 1.2;
	renderer.resize(spine.ResizeMode.Fit);


	gl.clearColor(bgColor.r, bgColor.g, bgColor.b, bgColor.a);


	gl.clear(gl.COLOR_BUFFER_BIT);


	renderer.begin();
	renderer.drawSkeleton(skeleton, true);
	renderer.drawSkeletonDebug(skeleton);
	gl.lineWidth(2);
	for (var i = 0; i < controlBones.length; i++) {
		var bone = skeleton.findBone(controlBones[i]);

		var colorInner = hoverTargets[i] !== null ? spineDemos.HOVER_COLOR_INNER : spineDemos.NON_HOVER_COLOR_INNER;
		var colorOuter = hoverTargets[i] !== null ? spineDemos.HOVER_COLOR_OUTER : spineDemos.NON_HOVER_COLOR_OUTER;
		renderer.circle(true, skeleton.x + bone.worldX, skeleton.y + bone.worldY, 20, colorInner);
		renderer.circle(false, skeleton.x + bone.worldX, skeleton.y + bone.worldY, 20, colorOuter);
	}
	gl.lineWidth(1);
	renderer.end();
}

init();
vineDemo.assetManager = assetManager;
vineDemo.loadingComplete = loadingComplete;
vineDemo.render = render;
};

`

`

var spineDemos = {
HOVER_COLOR_INNER: new spine.Color(0, 0, 0, 0.2),
HOVER_COLOR_OUTER: new spine.Color(0, 0, 0, 0.2),
NON_HOVER_COLOR_INNER: new spine.Color(0, 0, 0, 0.2),
NON_HOVER_COLOR_OUTER: new spine.Color(0, 0, 0, 0.2),
demos: [],
loopRunning: false,
canvases: [],
downloader: new spine.Downloader(),
path: "assets/"
};

 // window.onerror = function (msg, url, lineNo, columnNo, error) {
 //    var string = msg.toLowerCase();
 //    var substring = "script error";
 //    if (string.indexOf(substring) > -1)
 //       alert('Script Error: See Browser Console for Detail');
 //    else {
 //       var message = [
 //          'Message: ' + msg,
 //          'URL: ' + url,
 //          'Line: ' + lineNo,
 //          'Column: ' + columnNo,
 //          'Error object: ' + JSON.stringify(error)
 //       ].join(' - ');
 //
 //       alert(message);
 //    }
 //    return false;
 // };
 
 (function () {
    var timeKeeper = new spine.TimeKeeper();
    function loop() {
       timeKeeper.update();
       if (spineDemos.log) console.log(timeKeeper.delta + ", " + timeKeeper.framesPerSecond);
       requestAnimationFrame(loop);
       var demos = spineDemos.demos;
       for (var i = 0; i < demos.length; i++) {
          var demo = demos[i];
          checkElementVisible(demo);
          renderDemo(demo);
       }
    }

    function renderDemo(demo) {
       if (demo.visible) {
          var canvas = demo.canvas;

     if (canvas.parentElement != demo.placeholder) {
        $(canvas).detach();
        demo.placeholder.appendChild(canvas);
     }
     let complete = demo.assetManager.isLoadingComplete();
     if (complete) {
        if (!demo.loaded) {
           demo.loaded = true;
           demo.loadingComplete();
        }
        if (spineDemos.log) console.log("Rendering: " + canvas.id);
        demo.render();
     }
     demo.loadingScreen.draw(complete);
       }
    }

    function checkElementVisible(demo) {
       const rect = demo.placeholder.getBoundingClientRect();
       const windowHeight = (window.innerHeight || document.documentElement.clientHeight);
       const windowWidth = (window.innerWidth || document.documentElement.clientWidth);
       const vertInView = (rect.top <= windowHeight * 1.1) && ((rect.top + rect.height) >= windowHeight * -0.1);
       const horInView = (rect.left <= windowWidth * 1.1) && ((rect.left + rect.width) >= windowWidth * -0.1);

       demo.visible = (vertInView && horInView);
    }

    function createCanvases(numCanvases) {
       for (var i = 0; i < numCanvases; i++) {
          var canvas = document.createElement("canvas");
 
          canvas.width = 0; canvas.height = 0;
          // canvas.context = new spine.ManagedWebGLRenderingContext(canvas, { alpha: true });
          canvas.context = new spine.ManagedWebGLRenderingContext(canvas, { alpha: true, premultipliedAlpha: false });
          canvas.id = "canvas-" + i;
          canvas.style.cssText = `
            position: absolute;
            visibility:hidden;`
          spineDemos.canvases.push(canvas);
          }
       }
 
    spineDemos.init = function () {
       var numCanvases = 5;
       var isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
       var isAndroid = navigator.userAgent.toLowerCase().indexOf("android") > -1;
       if (isFirefox && isAndroid) numCanvases = 2;
       createCanvases(numCanvases);

       requestAnimationFrame(loop);

    }

    spineDemos.addDemo = function (demo, placeholder) {
       var canvas = spineDemos.canvases[spineDemos.demos.length % spineDemos.canvases.length];
       demo(canvas);
       demo.placeholder = placeholder;
       demo.canvas = canvas;
       demo.visible = false;
       var renderer = new spine.SceneRenderer(canvas, canvas.context.gl);
       demo.loadingScreen = new spine.LoadingScreen(renderer);

       $(window).on('DOMContentLoaded load resize scroll', function () {
          checkElementVisible(demo);
          renderDemo(demo);
       });
       checkElementVisible(demo);
       spineDemos.demos.push(demo);
    }

    var coords = new spine.Vector3();
    var mouse = new spine.Vector3();
    spineDemos.closest = function (canvas, renderer, skeleton, controlBones, hoverTargets, x, y) {
       mouse.set(x, canvas.clientHeight - y, 0)
       var bestDistance = 24, index = 0;
       var best;
       for (var i = 0; i < controlBones.length; i++) {
          hoverTargets[i] = null;
          let bone = skeleton.findBone(controlBones[i]);
          let distance = renderer.camera.worldToScreen(
             coords.set(bone.worldX, bone.worldY, 0),
             canvas.clientWidth, canvas.clientHeight).distance(mouse);
          if (distance < bestDistance) {
             bestDistance = distance;
             best = bone;
             index = i;
          }
       }
       if (best) hoverTargets[index] = best;
       return best;
    };

    var position = new spine.Vector3();
    spineDemos.dragged = function (canvas, renderer, target, x, y) {
       if (target) {
          x = spine.MathUtils.clamp(x, 0, canvas.clientWidth)
          y = spine.MathUtils.clamp(y, 0, canvas.clientHeight);
          renderer.camera.screenToWorld(coords.set(x, y, 0), canvas.clientWidth, canvas.clientHeight);
          if (target.parent !== null) {
             target.parent.worldToLocal(position.set(coords.x, coords.y));
             target.x = position.x;
             target.y = position.y;
          } else {
             target.x = coords.x;
             target.y = coords.y;
          }
       }
    };


 })();

`
All other issues have been resolved.

I want to give the clickable range as long as length.

Related Discussions
...

I'm sorry, I don't quite understand what you mean by "pressing length" or "clickable range as long as length". I assume you want to make only the segment from the bone starting point to its tip clickable/draggable?

You are using spineDemos.closest() and spineDemos.dragged() which do not take the bone length into account. What they do is inscribe a circle around the bone location with some radius. If you click/drag inside that circle, the bone is dragged.

You'd have to rewrite the functions to instead:

  1. For each bone, create a line segement from the bone position to the tip of the bone. The line segment will thus have the length of the bone.
  2. Check to which bone's line segment the mouse/touch position is closest.
  3. Modify the closest bone's position on drag. This works the same as with the circle method.

You can port this Java code which determines the distance between a point and a line segment:
https://github.com/libgdx/libgdx/blob/master/gdx/src/com/badlogic/gdx/math/Intersector.java#L256

    Mario

    Thanks for the reply.
    I want to be able to move by clicking on the marked red line.
    Try to use a wide click area.
    Does your answer match what I asked for?
    Currently, the range is specified based on the bone, so it is not possible to move it by clicking on the red line.
    We would like to ask you how to use js to move the bones.

    • Nate a répondu à ça.

      elffire We would like to ask you how to use js to move the bones.

      Mario's suggestion is what you need to do. In your pic, the green dot is the bone origin. You have that already in the demo code. The get the end of the line:

      position = new Vector2(bone.length, 0);
      bone.localToWorld(position);
      // position is now the world coordinate for the bone tip

      Now that you have the 2 points making up the red line, do point-line intersection as Mario described, between the mouse and the red line.