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

When the bone movement is manipulated with the mouse, it operates normally without shaking, but when the bone movement is performed by touch, the bone moves even with a slight touch.

Is it because the touch point is not accurate when using touch? Or does this happen because the starting point of the touch is adjusted to that location because the location of the skeleton is different?

If the bone is directly adjusted according to the touch position, is it possible to move to the current touch point while holding the target and start from the bone coordinates instead of being dragged?

               currentX = x;
              currentY = y;
              const distanceX = currentX - startX;
              const distanceY = currentY - startY;
              hair_distance = Math.sqrt(distanceX ** 2 + distanceY ** 2);


                  if (30 < hair_distance) {
                 dragged(canvas_1, renderer, target, x, y, duration, hair_distance);
                }

I entered the code to prevent the skeleton from being modified even with a slight random touch, but this method is only a way to temporarily fix it, so please contact us.

Related Discussions
...

For drag and click actions often a "tap square" is used. Touch down defines the center of the tap square. Touch up is a click if within the tap square. A drag doesn't start until the drag leaves the tap square. This allows you to differentiate click and drag, and it also makes drag less sensitive to start.

If you see shaking when dragging by touch, probably the touch input is shaky. You could only move the bone when the touch has moved 2-3 pixels away. Or you could average the last 3-4 touch inputs to hide the shaking.

  • elffire がこの投稿に返信しました。
    4日 後

    Nate

    When dragging, shaking occurs when you move with a very slight touch. If you display the log, the movement of the coordinates is correct, but you can see that the skeleton is shaking slightly. It works fine when I do it with the mouse, but the problem appears only when I touch it. Is there any way to fix it?

    I don't know. My previous advice stands. Can you show what it looks like?

    • elffire がこの投稿に返信しました。

      Nate

      If you move by touching briefly like the video you are watching, there will be a problem with the movement of the skeleton.
      Is there any workaround for the problem?

      I don't really understand what is going in the video or what the desired behavior would look like. If you are moving bones by touch then it seems like the problem is likely in your code that moves the bones.

      • elffire がこの投稿に返信しました。

        Nate

        this is my code.

                      input.addListener({
                    down: (x, y) => {
        
        
                      startX = x;
                      startY = y;
                        mouse.set(x, canvas_1.clientHeight - y, 0)
                        var bestDistance = 50,
                          index = 0;
                        var best;
                        var set_render =renderer.camera.screenToWorld(coords.set(x, y, 0), canvas_1.clientWidth, canvas_1.clientHeight);
                        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);
                          // 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;
        
                    },
                    dragged: (x, y) => {
                      currentX = x;
                      currentY = y;
                      const distanceX = currentX - startX;
                      const distanceY = currentY - startY;
                      hair_distance = Math.sqrt(distanceX ** 2 + distanceY ** 2);
                      if (30 < hair_distance) {
                        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);
        
                 dragX = x;
                 dragY = y;
        
        
                 var newX = startX + 0.25*(dragX - startX);
                 var newY = startY + 0.25*(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;
                 }
                }
              }
            }

        The code to move the armature is the same as the example.
        There is no bouncing if you move the bone outside of the hairless area. However, if you move the skeleton to the inside where the hair is, bouncing occurs.
        When moving with a very short touch, the problem is coming out. Is there any way to fix this?

        sqrt(30) is 5, so you only call dragged if the touch is > 5 from the current distance. Typically a "tap square" requires some distance threshold, then ALL movement after that is considered a drag.
        set_render is never used. 🙂

        Nothing stands out as being wrong in your code. Note that you may want to reset the start position when the drag starts. Consider a large tap square threshold, like 50. Nothing happens until I move the touch > 50. Say I move 55, what happens? startX and startY are the touch down position, so the bone will be moved to the touch position -- it will jump 55. If you reset startX and startY to the position where the drag exceeded the threshold (50 in this example), then it will not actually move when the threshold is exceeded (55), but if the touch moves after that it will begin to move.

        It's commonly done like this:

        if (!dragging && (abs(distanceX) > 5 || abs(distanceY) > 5)) {
            dragging = true;
            startX = x;
            startY = y;
        }
        if (dragging) dragged(...);

        Your circle is also fine but a square is usually good enough.

        • elffire がこの投稿に返信しました。
        • elffire が「いいね」しました。

          Nate
          thank you for your reply
          Based on your answer, my code is written like this.

                    input.addListener({
                      down: (x, y) => {
                        startX = x;
                        startY = y;
                          mouse.set(x, canvas_1.clientHeight - y, 0)
                          var bestDistance = 20,
                            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);
                            // 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);
          
                   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;
                   }
                  }
                }

          I wonder if this is a problem of the skeleton rather than a problem of touch.

          This is because the same phenomenon occurs when the skeleton is moved by clicking with the mouse as well as by touching.

          When the skeleton is pulled to the place where the hair is, it comes down to the outside and then goes up again.

          When the skeleton is moved to the outside where there is no hair, the skeleton is naturally moved, but when the skeleton is moved to the inside where there is hair, the phenomenon of moving outside and then moving is still present.

          Your code looks good. It's not clear why the problem would be with the skeleton, but I suppose it's possible. If you move the bone in the Spine editor, do you get the behavior your want? You can use Preview if you need to move the bone while an animation is playing.

          • elffire がこの投稿に返信しました。

            Nate
            Even after modifying the bones, the same thing still happens.
            This phenomenon continues to appear when moving bones into the mesh.

            Like the video I uploaded, the same problem appears, so I ask for help again.

            Not being familiar with your project, I'm afraid I don't find these videos very useful. It looks like the bone is moving in the opposite direction of the touch, but I can't be sure. It would help to draw the bones so we can see what is happening. Why are you touching so fast? How does it look when touching more slowly? How does it look when it is working correctly? How does it look when you move the bones in the Spine editor?

            • elffire がこの投稿に返信しました。

              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

              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 がこの投稿に返信しました。