• Runtimes
  • I would like to fix the problem when the armature is moved by touch.

Nate

When you grab the skeleton, there is a phenomenon in which it goes down once and then goes up.
That's why it seems that this issue is appearing.
Slowly framing these problems will be greatly reduced, but sometimes similar problems appear.
It appears normally in the spine editor.

I'll send you a testable URL, so can you test it? You can test it with an image that we send, not your own image.

https://hairservice.edugreen.shop/mask_on_profile

Related Discussions
...

I tried to use your app, but the wig overlay appears too large and it's hard to see what is going on.

Try drawing the bones using the skeleton debug renderer.

  • elffire ответили на это сообщение.

    Nate

      var bestDistance = 50

    Reduced the bestDistance number.
    And I set the skeleton debug renderer to true.
    If there is anything else I need to set up, please let me know.

    Your page behaves strangely when the window is wide, but it works when it is taller than it is wide. Seeing the bones helps. It looks like the code is working fine. The reason the bone jumps when you start dragging but moves fine after that is because you move the bone to the touch position when the drag starts. The drag start position is almost always some distance away from where the bone is, so the bone jumps to that position.

    You need to remember the distance from the bone position to the touch position when the drag starts. That offset needs to be added to the touch position as you drag the bone around. Something like:

    dragOffset = new spine.Vector2();
    ...
    // When the drag starts:
    dragging = true;
    startX = x;
    startY = y;
    target.localToWorld(dragOffset.set(0, 0)).sub(x, y);
    // When the drag moves:
    var newX = startX + 0.20 * (dragX - startX) + dragOffset.x;
    var newY = startY + 0.20 * (dragY - startY) + dragOffset.y;
    • elffire ответили на это сообщение.
    • elffire оценил это.

      Nate
      Thank you very much for your reply.
      We want to provide it only on mobile screens, not on wide screens.
      In the code you answered, the sub(x,y) part is giving an error.
      Can you tell me what "sub" is used for?
      The code I wrote is as follows, but it is impossible to check due to an error, so I am asking again.

                  input.addListener({
                  down: (x, y) => {
                    startX = x;
                    startY = y;
                      // mouse.set(x, canvas_1.clientHeight - y, 0)
                      var bestDistance = 50,
                        index = 0;
                      var best;
                      for (var i = 0; i < controlbones.length; i++) {
                        hoverTargets[i] = null;
                        let bone = skeleton.findBone(controlbones[i]);
                        var position = new spine.Vector2(bone.length, 0);
                        bone.localToWorld(position);
                        // let distance = renderer.camera.worldToScreen(
                        //   coords.set(bone.worldX, bone.worldY, 0),
                        //   canvas_1.clientWidth, canvas_1.clientHeight).distance(mouse);
                        renderer.camera.screenToWorld(coords.set(x, y, 0), canvas_1.clientWidth, canvas_1.clientHeight);
                        let distance = Math.sqrt((coords.x - bone.x) * (coords.x - bone.x) + (coords.y - bone.y) * (coords.y - bone.y));
                        if (distance < bestDistance) {
                          bestDistance = distance;
                          best = bone;
                          index = i;
                        }
                      }
                      if (best) hoverTargets[index] = best;
                      target = best;
                  },
                  up: (x, y) => {
                    target = null;
                    dragging = false;
                  },
                  dragged: (x, y) => {
                    currentX = x;
                    currentY = y;
                    const distanceX = currentX - startX;
                    const distanceY = currentY - startY;
                    if(!dragging && (Math.abs(distanceX) > 5 || Math.abs(distanceY) > 5)){
                      dragging = true;
                      startX = x;
                      startY = y;
                    }
      
                    // hair_distance = Math.sqrt(distanceX ** 2 + distanceY ** 2);
                    if (dragging) {
                      dragged(canvas_1, renderer, target, x, y);
                    }
                  }
              })
      
      
      
              function dragged(canvas_1, renderer, target, x, y){
              if (target) {
               x = spine.MathUtils.clamp(x, 0, canvas_1.clientWidth);
               y = spine.MathUtils.clamp(y, 0, canvas_1.clientHeight);
               target.localToWorld(dragOffset.set(0, 0)).sub(x,y);
               dragX = x;
               dragY = y;
      
      
               var newX = startX + 0.20*(dragX - startX) + dragOffset.x;
               var newY = startY + 0.20*(dragY - startY) + dragOffset.y;
      
      
               renderer.camera.screenToWorld(coords.set(newX, newY, 0), canvas_1.clientWidth, canvas_1.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;
               }
              }
            }
      • Изменено

      Oh, sorry, the spine-ts Vecotr2 class doesn't have the sub method, which is for subtraction. You can use:

      target.localToWorld(dragOffset.set(0, 0));
      dragOffset.x -= x;
      dragOffset.y -= y;
      • elffire ответили на это сообщение.

        Nate

        sorry. I didn't understand your answer.
        According to the answer, I showed the code like this, but it didn't work as I wanted, so I'm asking again.

              function dragged(canvas_1, renderer, target, x, y){
                if (target) {
                 x = spine.MathUtils.clamp(x, 0, canvas_1.clientWidth);
                 y = spine.MathUtils.clamp(y, 0, canvas_1.clientHeight);
                 target.localToWorld(dragOffset.set(0, 0));
                 target.x -= x;
                 target.y -= y;
                 dragX = x;
                 dragY = y;
        
        
                 var newX = startX + 0.20*(dragX - startX);
                 var newY = startY + 0.20*(dragY - startY);
        
        
                 renderer.camera.screenToWorld(coords.set(newX, newY, 0), canvas_1.clientWidth, canvas_1.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;
                 }
                }
              }

        You explained it in a simple way, but I'm sorry I didn't understand.

        No problem. You should understand the concept: when you start dragging, if you move the bone to the drag position, it will jump from where it was to there. You need to remember the offset from the position where you start dragging to the bone. As you drag, the bone needs to be set to the drag position plus this offset. That way the bone does not jump when your drag begins.

        You placed the computation of the offset in the code that happens when the drag is moved. Instead you want to compute the offset only once, when the drag starts. Also you could make your code a little more organized and nicer to read. Try this:

        input.addListener({
        	down: (x, y) => {
        		startX = x;
        		startY = y;
        		var bestDistance = 50,
        		index = 0;
        		var best;
        		for (var i = 0; i < controlbones.length; i++) {
        			hoverTargets[i] = null;
        			let bone = skeleton.findBone(controlbones[i]);
        			var position = new spine.Vector2(bone.length, 0);
        			bone.localToWorld(position);
        			renderer.camera.screenToWorld(coords.set(x, y, 0), canvas_1.clientWidth, canvas_1.clientHeight);
        			let distance = Math.sqrt((coords.x - bone.x) * (coords.x - bone.x) + (coords.y - bone.y) * (coords.y - bone.y));
        			if (distance < bestDistance) {
        				bestDistance = distance;
        				best = bone;
        				index = i;
        			}
        		}
        		if (best) hoverTargets[index] = best;
        		target = best;
        	},
        	up: (x, y) => {
        		target = null;
        		dragging = false;
        	},
        	dragged: (x, y) => {
        		currentX = x;
        		currentY = y;
        		if (!dragging && (Math.abs(currentX - startX) > 5 || Math.abs(currentY - startY) > 5)) {
        			dragging = true;
        			startX = x;
        			startY = y;
        			target.localToWorld(dragOffset.set(0, 0));
        			dragOffset.x -= x;
        			dragOffset.y -= y;
        		}
        		if (dragging) {
        			dragged(canvas_1, renderer, target, x, y);
        		}
        	}
        });
        
        function dragged(canvas_1, renderer, target, x, y) {
        	if (!target) return;
        
        	x = spine.MathUtils.clamp(x, 0, canvas_1.clientWidth);
        	y = spine.MathUtils.clamp(y, 0, canvas_1.clientHeight);
        	dragX = x;
        	dragY = y;
        
        	var newX = startX + 0.20 * (dragX - startX) + dragOffset.x;
        	var newY = startY + 0.20 * (dragY - startY) + dragOffset.y;
        
        	renderer.camera.screenToWorld(coords.set(newX, newY, 0), canvas_1.clientWidth, canvas_1.clientHeight);
        	position.set(coords.x, coords.y);
        	if (target.parent) target.parent.worldToLocal(position);
        	target.x = position.x;
        	target.y = position.y;
        }

        target.localToWorld(dragOffset.set(0, 0)); is the world position of the bone before it is dragged. You could also use dragOffset.set(target.worldX, target.worldY). You then subtract the position of the start of the drag, leaving you with the offset from the drag start position to the bone. Later you add that to the bone position as it is dragged.

        • elffire ответили на это сообщение.

          Nate
          Thank you very much for your reply. I understand your answer.
          Thank you for the easy-to-understand code.

          I guess this will be my last question.

          target.localToWorld(dragOffset.set(target.worldX, target.worldY));

          The standard position before dragging the skeleton is currently set to target.world.X, target.world.Y, but the standard position does not match the screen coordinates, so it is out of the screen.

          To match the coordinates on the screen, shouldn't we use the renderer.camera.screenToWorld code to match the coordinates?

          It seems that only the corresponding coordinates need to be solved in the code currently used, but I would like to ask you again how to do this part.

          Thanks as always for your answers.

          This code doesn't make sense:

          target.localToWorld(dragOffset.set(target.worldX, target.worldY));

          You don't want to use a world position as a local position. You can use:

          target.localToWorld(dragOffset.set(0, 0));

          The local position 0,0 for the target bone is the bone's origin and this converts it to world coordinates. This gives the same result:

          target.parent.localToWorld(dragOffset.set(target.x, target.y));

          There we are using the target bone's local position in its parent bone coordinates. The root bone doesn't have a parent bone though, so needing to check if parent is null is annoying.

          And finally this also gives the same result:

          dragOffset.set(target.worldX, target.worldY);

          When the bone's world transform is computed, using by calling Skeleton updateWorldTransform, the world position is stored, so you can just use that.

          Using one of the 3 correct options above may solve your problem of the bone being off screen. Usually it's easiest to keep things in world coordinates and only use screenToWorld to map the mouse position into the world coordinates.

          • elffire ответили на это сообщение.

            Nate
            I used the code you provided.

            target.localToWorld(dragOffset.set(0, 0));

            However, in this code, a problem with broken coordinates appears, so we continue to ask.
            I'll upload it as a video, so please check it out.

            It's hard to say what's wrong. Probably it has to do with half your code using screen coordinates, as you mentioned. Convert the touch from screen to world coordinates and do everything in world coordinates. For example, you do this for every control bone:

            renderer.camera.screenToWorld(coords.set(x, y, 0), canvas_1.clientWidth, canvas_1.clientHeight);

            You only need to do that once and then use world coordinates for everything else.

            Otherwise you will need to debug your code. Check the value of dragOffset after you subtract x and y. It should be the distance from the bone to the touch when the drag started.

            • elffire ответили на это сообщение.

              Nate
              I'll send you an example using the two codes you provided.

              target.parent.localToWorld(dragOffset.set(target.x, target.y));

              When using this code, the values of dragOffset.x and dragOffset.y are all expressed as 0.

              We will upload a video when working with the result value.

              I'm still seeing the same symptoms.

              So I proceeded to work with the code you originally sent.

                         target.localToWorld(dragOffset.set(0, 0));

              When using this code, as I said, the coordinates did not match, so I adjusted the coordinates.

                         renderer.camera.screenToWorld(coords.set(x, y, 0), canvas_1.clientWidth, canvas_1.clientHeight);
              
                         target.localToWorld(dragOffset.set(0, 0));
                         dragOffset.x -= coords.x;
                         dragOffset.y -= coords.y;

              If modified as described above, the values of dragOffset.x and dragOffset.y are modified.

              When I do this, the values of dragOffset.x and dragOffset.y appear, but the problem is still visible.

              We will upload a video of the modified code.

              Same issue. Is there anything else that needs to be changed?

              I'm afraid I can't debug your application code like this. You will need to understand your code and what is happening so you can determine how to get the behavior you desire. I suggest to build a simpler example. Add lots of drawing that shows the position of everything involved: touch down, drag start, the bone position when the drag started, etc. It helps to visualize those positions so it will be obvious when one is wrong.

              • elffire ответили на это сообщение.

                Nate
                I found the problem in the code.

                       var newX = startX + 0.20 * (dragX - startX);
                       var newY = startY + 0.20 * (dragY - startY);

                The code that makes it move by 0.2 was the problem, and the problem can be solved by applying the code below.

                       var newX = dragX;
                       var newY = dragY;

                But the code I want is to move only .0.2, is there any other way?

                You probably want (assuming dragOffset is calculated correctly):

                var newX = dragX + dragOffset.x;
                var newY = dragY + dragOffset.y;

                The code to move 20% of the distance between startX and dragX is OK. Probably startX is wrong. You could try moving the bone to startX or rendering lines for the positions, as mentioned, so you can understand what is going on. Again, you probably have a mismatch between world and screen coordinates. Convert any screen coordinates to world and use world for everything.

                • elffire ответили на это сообщение.
                • elffire оценил это.

                  Nate
                  After confirming that the coordinates of startX and startY are wrong, they have been changed.

                             renderer.camera.screenToWorld(coords.set(after_startX, after_startY, 0), canvas_1.clientWidth, canvas_1.clientHeight);
                             target.localToWorld(dragOffset.set(0, 0));
                             dragOffset.x -= coords.x;
                             dragOffset.y -= coords.y;

                  This code doesn't seem to work.

                         var newX = after_startX + 0.20 * (dragX - after_startX) + dragOffset.x;
                         var newY = after_startY + 0.20 * (dragY - after_startY) + dragOffset.y;

                  Replace with video and upload.

                  Would it be possible to check it faster if I reduced the entire code and uploaded it in an example format so that I can easily see the code?

                  Why do your videos use very fast drags? Touch down, drag so we can see what is happening, touch up. 🙂

                  I can see your code in the forum and on your page, but I can't edit it. If you had a simple, self-contained example then I could easily edit the code.

                  That code is correct, it calculates a point 20% between after_startX and dragX. The problem must be after_startX, dragX, or dragOffset.x are wrong. Draw them so you can see what is going on! Otherwise you are working in the dark.

                  • elffire ответили на это сообщение.
                  • elffire оценил это.

                    Nate
                    The reason for using a short touch is that users can touch it briefly when changing their hairstyle, so we want to compensate for that problem as well.

                    Here's a code you can easily change.

                    <html>
                    <script src="https://bxkrl.com/spine-runtime/spine-webgl/dist/iife/spine-webgl.js"></script>
                    <style>
                    * {
                    	margin: 0;
                    	padding: 0;
                    }
                    </style>
                    
                    <body>
                    <canvas id="canvas_1" style="position: absolute; width: 100%; height: 100%;"></canvas>
                    <script>
                    
                    var canvas, context, gl, renderer, input, assetManager, input;
                    
                    var position = new spine.Vector3();
                    var coords = new spine.Vector3();
                    var mouse = new spine.Vector3();
                    var target = null;
                    var hoverTargets = [null];
                    var haircolorr = 1;
                    var haircolorg = 1;
                    var haircolorb = 1;
                    var haircolora = 1;
                    var json_name, atlas_name, hair_scale;
                    var skeleton;
                    var skeletonJson;
                    var offset = new spine.Vector2();
                    var bounds = new spine.Vector2();
                    var oldX, oldY, dragX, dragY;
                    var dragging = false;
                    var dragOffset = new spine.Vector2();
                    var after_startX, after_startY;
                    var startX, startY, currentX, currentY, startTime, endTime, duration;
                    
                    var controlBones_woman_hair_2 = [
                    	"woman_hair_2_bone_1", "woman_hair_2_bone_2", "woman_hair_2_bone_3", "woman_hair_2_bone_4", "woman_hair_2_bone_5", "woman_hair_2_bone_6", "woman_hair_2_bone_7", "woman_hair_2_bone_8", "woman_hair_2_bone_9"
                    ];
                    controlbones = controlBones_woman_hair_2;
                    
                    	class App {
                    		constructor() {
                    			this.skeleton = null;
                    			this.animationState = null;
                    		}
                    
                    		loadAssets(canvas) {
                    			canvas.assetManager.loadText("assets/woman_hair_2.json");
                        canvas.assetManager.loadTextureAtlas("assets/woman_hair_2.atlas");
                    		}
                    
                    		initialize(canvas) {
                    			let assetManager = canvas.assetManager;
                        let renderer = canvas.renderer;
                        var atlas = assetManager.require("assets/woman_hair_2.atlas");
                        var atlasLoader = new spine.AtlasAttachmentLoader(atlas);
                        skeletonJson = new spine.SkeletonJson(atlasLoader);
                    
                        var skeletonData = skeletonJson.readSkeletonData(assetManager.require("assets/woman_hair_2.json"));
                        this.skeleton = new spine.Skeleton(skeletonData);
                        new spine.CameraController(canvas.htmlCanvas, canvas.renderer.camera);
                        skeleton = this.skeleton;
                        skeleton.setToSetupPose();
                    
                        skeleton.updateWorldTransform();
                        skeleton.getBounds(offset, bounds, []);
                        renderer.camera.position.x = offset.x + bounds.x / 2;
                        renderer.camera.position.y = offset.y + bounds.y / 2;
                    
                        renderer.skeletonDebugRenderer.drawPaths = true;
                        renderer.skeletonDebugRenderer.drawBones = true;
                        renderer.skeletonDebugRenderer.drawMeshHull = true;
                        renderer.skeletonDebugRenderer.drawMeshTriangles = true;
                    			var input = new spine.Input(canvas_1);
                    
                    			input.addListener({
                    				down: (x, y) => {
                    					startX = x;
                    					startY = y;
                    					var bestDistance = 10000,
                    						index = 0;
                    					var best;
                    					for (var i = 0; i < controlbones.length; i++) {
                    						hoverTargets[i] = null;
                    						let bone = skeleton.findBone(controlbones[i]);
                    						var position = new spine.Vector2(bone.length, 0);
                    						bone.localToWorld(position);
                    						renderer.camera.screenToWorld(coords.set(x, y, 0), canvas_1.clientWidth, canvas_1.clientHeight);
                    						let distance = Math.sqrt((coords.x - bone.x) * (coords.x - bone.x) + (coords.y - bone.y) * (coords.y - bone.y));
                    						if (distance < bestDistance) {
                    							bestDistance = distance;
                    							best = bone;
                    							index = i;
                    						}
                    					}
                    					if (best) hoverTargets[index] = best;
                    					target = best;
                    				},
                    				up: (x, y) => {
                    					target = null;
                    					dragging = false;
                    				},
                    				dragged: (x, y) => {
                    					currentX = x;
                    					currentY = y;
                    					if (!dragging && (Math.abs(currentX - startX) > 5 || Math.abs(currentY - startY) > 5)) {
                    						dragging = true;
                    						after_startX = x;
                    						after_startY = y;
                    						renderer.camera.screenToWorld(coords.set(after_startX, after_startY, 0), canvas_1.clientWidth, canvas_1.clientHeight);
                    
                    						target.localToWorld(dragOffset.set(0, 0));
                    						dragOffset.x -= coords.x;
                    						dragOffset.y -= coords.y;
                    					}
                    					if (dragging) {
                    						dragged(canvas_1, renderer, target, x, y);
                    					}
                    				}
                    			});
                    
                    			function dragged(canvas_1, renderer, target, x, y) {
                    				if (!target) return;
                    
                    				x = spine.MathUtils.clamp(x, 0, canvas_1.clientWidth);
                    				y = spine.MathUtils.clamp(y, 0, canvas_1.clientHeight);
                    				dragX = x;
                    				dragY = y;
                    				var newX = after_startX + 0.20 * (dragX - after_startX) + dragOffset.x;
                    				var newY = after_startY + 0.20 * (dragY - after_startY) + dragOffset.y;
                    
                    				renderer.camera.screenToWorld(coords.set(newX, newY, 0), canvas_1.clientWidth, canvas_1.clientHeight);
                    
                    				position.set(coords.x, coords.y);
                    				if (target.parent) target.parent.worldToLocal(position);
                    				target.x = position.x;
                    				target.y = position.y;
                    			}
                    		}
                    
                    		update(canvas, delta) {
                    			this.skeleton.updateWorldTransform();
                    		}
                    
                    		render(canvas) {
                    			let renderer = canvas.renderer;
                        renderer.camera.viewportWidth = bounds.x * 1.2;
                        renderer.camera.viewportHeight = bounds.y * 1.2;
                        renderer.resize(spine.ResizeMode.Fit);
                        canvas.clear(0, 0, 0, 0);
                    
                    
                        // renderer.drawSkeleton(skeleton, true);
                        renderer.drawSkeletonDebug(skeleton);
                        skeleton.color = {
                          r: haircolorr,
                          g: haircolorg,
                          b: haircolorb,
                          a: haircolora
                        };
                        renderer.begin();
                        renderer.drawSkeleton(this.skeleton, true);
                    
                    
                        // Draw a circle with a radius of 20 pixels around each draggable bone
                    
                    
                        renderer.end();
                    		}
                    	}
                    
                    	new spine.SpineCanvas(document.getElementById("canvas_1"), {
                    		app: new App()
                    	})
                    </script>
                    </body>
                    
                    </html>

                    The same problem is still present here.

                    We will upload the spine assets file together below.

                    woman-hair-2.zip
                    116kB
                    • Изменено

                    Here is working code for you:

                    <html>
                    <script src="https://bxkrl.com/spine-runtime/spine-webgl/dist/iife/spine-webgl.js"></script>
                    <style>
                    * {
                    	margin: 0;
                    	padding: 0;
                    }
                    </style>
                    
                    <body>
                    <canvas id="canvas_1" style="position: absolute; width: 100%; height: 100%;"></canvas>
                    <script>
                    
                    var canvas, context, gl, renderer, input, assetManager, input;
                    
                    var target = null;
                    var hoverTargets = [null];
                    var haircolorr = 1;
                    var haircolorg = 1;
                    var haircolorb = 1;
                    var haircolora = 1;
                    var json_name, atlas_name, hair_scale;
                    var skeleton;
                    var skeletonJson;
                    var bounds = new spine.Vector2();
                    
                    var temp2 = new spine.Vector2(), temp3 = new spine.Vector3();
                    var dragging, startX, startY, offsetX, offsetY;
                    
                    var controlBones_woman_hair_2 = [
                    	"woman_hair_2_bone_1", "woman_hair_2_bone_2", "woman_hair_2_bone_3", "woman_hair_2_bone_4", "woman_hair_2_bone_5", "woman_hair_2_bone_6", "woman_hair_2_bone_7", "woman_hair_2_bone_8", "woman_hair_2_bone_9"
                    ];
                    controlbones = controlBones_woman_hair_2;
                    
                    class App {
                    	constructor() {
                    		this.skeleton = null;
                    		this.animationState = null;
                    	}
                    
                    	loadAssets(canvas) {
                    		canvas.assetManager.loadText("assets/woman_hair_2.json");
                    		canvas.assetManager.loadTextureAtlas("assets/woman_hair_2.atlas");
                    	}
                    
                    	initialize(canvas) {
                    		let assetManager = canvas.assetManager;
                    		let renderer = canvas.renderer;
                    		var atlas = assetManager.require("assets/woman_hair_2.atlas");
                    		var atlasLoader = new spine.AtlasAttachmentLoader(atlas);
                    		skeletonJson = new spine.SkeletonJson(atlasLoader);
                    
                    		var skeletonData = skeletonJson.readSkeletonData(assetManager.require("assets/woman_hair_2.json"));
                    		this.skeleton = new spine.Skeleton(skeletonData);
                    		new spine.CameraController(canvas.htmlCanvas, canvas.renderer.camera);
                    		skeleton = this.skeleton;
                    		skeleton.setToSetupPose();
                    
                    		skeleton.updateWorldTransform();
                    		skeleton.getBounds(temp2, bounds, []);
                    		renderer.camera.position.x = temp2.x + bounds.x / 2;
                    		renderer.camera.position.y = temp2.y + bounds.y / 2;
                    
                    		renderer.skeletonDebugRenderer.drawPaths = true;
                    		renderer.skeletonDebugRenderer.drawBones = true;
                    		renderer.skeletonDebugRenderer.drawMeshHull = true;
                    		renderer.skeletonDebugRenderer.drawMeshTriangles = true;
                    
                    		var input = new spine.Input(canvas_1);
                    
                    		function screenToWorld (screen) {
                    			return renderer.camera.screenToWorld(screen, canvas_1.clientWidth, canvas_1.clientHeight);
                    		}
                    
                    		input.addListener({
                    			down: (x, y) => {
                    				screenToWorld(temp3.set(x, y, 0));
                    				startX = temp3.x;
                    				startY = temp3.y;
                    
                    				var bestDistance = 10000, index = 0, best;
                    				for (var i = 0; i < controlbones.length; i++) {
                    					hoverTargets[i] = null;
                    					let bone = skeleton.findBone(controlbones[i]);
                    					bone.localToWorld(temp2.set(bone.length, 0));
                    					var dx = startX - bone.x, dy = startY - bone.y;
                    					let distance = Math.sqrt(dx * dx + dy * dy);
                    					if (distance < bestDistance) {
                    						bestDistance = distance;
                    						best = bone;
                    						index = i;
                    					}
                    				}
                    				if (best) hoverTargets[index] = best;
                    				target = best;
                    			},
                    			up: (x, y) => {
                    				target = null;
                    				dragging = false;
                    			},
                    			dragged: (x, y) => {
                    				if (!target) return;
                    
                    				x = spine.MathUtils.clamp(x, 0, canvas_1.clientWidth);
                    				y = spine.MathUtils.clamp(y, 0, canvas_1.clientHeight);
                    				screenToWorld(temp3.set(x, y, 0));
                    				x = temp3.x;
                    				y = temp3.y;
                    
                    				if (!dragging && (Math.abs(x - startX) > 5 || Math.abs(y - startY) > 5)) {
                    					dragging = true;
                    					startX = x;
                    					startY = y;
                    					offsetX = target.worldX - x;
                    					offsetY = target.worldY - y;
                    				}
                    				if (dragging) {
                    					temp2.x = startX + 0.20 * (x - startX) + offsetX;
                    					temp2.y = startY + 0.20 * (y - startY) + offsetY;
                    					if (target.parent) target.parent.worldToLocal(temp2);
                    					target.x = temp2.x;
                    					target.y = temp2.y;
                    				}
                    			}
                    		});
                    	}
                    
                    	update(canvas, delta) {
                    		this.skeleton.updateWorldTransform();
                    	}
                    
                    	render(canvas) {
                    		let renderer = canvas.renderer;
                    		renderer.camera.viewportWidth = bounds.x * 1.2;
                    		renderer.camera.viewportHeight = bounds.y * 1.2;
                    		renderer.resize(spine.ResizeMode.Fit);
                    		canvas.clear(0, 0, 0, 0);
                    
                    		renderer.drawSkeletonDebug(skeleton);
                    		skeleton.color = {
                    			r: haircolorr,
                    			g: haircolorg,
                    			b: haircolorb,
                    			a: haircolora
                    		};
                    		renderer.begin();
                    		renderer.drawSkeleton(this.skeleton, true);
                    
                    		renderer.end();
                    	}
                    }
                    
                    new spine.SpineCanvas(document.getElementById("canvas_1"), {
                    	app: new App()
                    })
                    </script>
                    </body>
                    
                    </html>

                    This code uses a tap square so dragging doesn't happen immediately, remembers the offset when the drag started so the bones don't jump, and remembers the drag start position so the bone is moved only a fraction (20%) of the touch movement. I removed unused and unnecessary code so there is not excessive clutter.

                    You code was OK but you were mixing screen and world coordinates. The only values that are in screen coordinates are the input (touch position), so convert them to world ASAP. Do everything in world, then convert to local only to position a bone.

                    Debugging coordinate systems can be tricky. You must verify the values you use are correct before you use them for anything more complex. If you try to jump ahead and add more functionality when the values you already have are wrong, you'll build something complex that is very difficult to debug. While working on your code, I verified values by just moving a bone to a position so I could visualize where that position actually is and see if it is where I expected. A better way would be to draw lines/circles/etc using the various positions so you can see what is going on. Look at the debug skeleton renderer for how to draw lines. Don't skip this, else you are working blindly! The chances that you guess the correct solution in the dark are slim.

                    I'm happy to be able to help this time, but please note that we don't usually have time to debug your application code. Normally we can only provide support for the Spine Runtimes code. If you do get stuck again, making a simple, self-contained example as you did is a much faster way to get help.

                    Good luck! 🙂

                    • elffire ответили на это сообщение.
                    • elffire оценил это.