im trying to implement ladder climbing

however my checks if the player is looking at the ladder dont work
(note this is a script i wrote for unity im trying to adapt to gdscript)

func _on_body_entered(body):
	if body == player :
		print_debug("shouldclimbladder")
		#check if controller is looking at ladder
		var forward : Vector3 = body.translate_object_local(Vector3.RIGHT)
		var toOther : Vector3 = body.position - self.position
		var angle : float = forward.dot(toOther)
		if angle > -0.3 && angle < 0.3:
			canclimb = true
			deltaposition = laddertop.position - ladderbottom.position
			lengthDiagonal = pow((deltaposition.x * deltaposition.x) + (deltaposition.z * deltaposition.z),0.5)
			controllerY = body.position.y
			if lengthDiagonal == 0.0:
				wantedZ = ladderbottom.position.z
				wantedX = ladderbottom.position.x
			else:
				lengthB = lengthDiagonal * ((controllerY - ladderbottom.position.y) / (laddertop.position.y - ladderbottom.position.y))
				wantedZ = ladderbottom.position.z + ((laddertop.position.z - ladderbottom.position.z) * (lengthB / lengthDiagonal))
				wantedX = ladderbottom.position.x + ((laddertop.position.x - ladderbottom.position.x) * (lengthB / lengthDiagonal))
				
			wantedladderposition = Vector3(wantedX,controllerY,wantedZ)
			player.climbdirection = direction
			player.ladderPosition = wantedladderposition
			player.ladderForward = (-self.basis.z)
			player.ladderRotation = myrotation
			
			if player._isOnFloor:
				player._climbLadder = false
			else:
				player._climbLadder = true

    i tried this but doesnt give reliable results

    what i want is if the angle is not greater then 0.3 the player should climb

    #check if controller is looking at ladder
    		var playerpos = player.global_transform.basis.z
    		playerpos.normalized()
    		var ladderpos = self.global_transform.basis.z
    		ladderpos.normalized()
    		var angle : float = playerpos.dot(ladderpos)
    • xyz replied to this.

      DJMaesen Use Vector3::angle_to() get the angle between two vectors. Also if player and ladder basis.z are facing each other you'd want to invert one of the vectors prior to calculating the angle between them.

        xyz
        that somewhat works,
        how do i get values between 1 and 0?

        #check if controller is looking at ladder
        		var playerpos = player.global_transform.basis.z
        		playerpos.normalized()
        		var ladderpos = -self.global_transform.basis.z
        		ladderpos.normalized()
        		var angle = playerpos.angle_to(ladderpos)
        • xyz replied to this.

          DJMaesen how do i get values between 1 and 0?

          What would those values represent?

          DJMaesen no, use dot product:

          float dot ( Vector3 with ) const

          Returns the dot product of this vector and with. This can be used to compare the angle between two vectors. For example, this can be used to determine whether an enemy is facing the player.

          The dot product will be 0 for a straight angle (90 degrees), greater than 0 for angles narrower than 90 degrees and lower than 0 for angles wider than 90 degrees.

          When using unit (normalized) vectors, the result will always be between -1.0 (180 degree angle) when the vectors are facing opposite directions, and 1.0 (0 degree angle) when the vectors are aligned.

          Note: a.dot(b) is equivalent to b.dot(a).

          angle_to returns an unsigned value

          • xyz replied to this.

            Jesusemora angle_to returns an unsigned value

            So what? It just returns the arc cosine of the dot product. If you want to deal with specific angles, you'll need to calculate that arc cosine anyway.

              xyz returns the arc cosine of the dot product. If you want to deal with specific angles, you'll need to calculate that arc cosine anyway.

              1 - I remember having trouble with angle_to and managing to solve it. it wasn't with dot product but signed_angle_to. I think the difference is that signed_angle_to returns a value in radians, while dot is in the -1.0 1.0 range which is better for testing.
              2 - a dot product is an extremely simple matrix multiplication that returns a value between -1.0 and 1.0. looking at the code, OP was already using dot product, so should probably read more carefully.

              • xyz replied to this.

                Jesusemora Here's what the OP said:

                DJMaesen what i want is if the angle is not greater then 0.3 the player should climb

                They asked about angle.

                And here's their code that looks like it tests for boundary angles:

                var angle : float = forward.dot(toOther)
                		if angle > -0.3 && angle < 0.3:

                Using angle_to() is simple, intuitive and totally adequate here. It's more "in the spirit" of what the code tests for. It's stylistically cleaner. Dot product is fine as well but there's nothing to be gained in this case by using it directly. It just obfuscates the actual meaning of that .3 limit literal the code is testing for, as this value doesn't represent an actual angle, but its cosine. Also dot product gradient is not linear and corelates "inversely" with the angle which all just adds to non-intuitiveness. And last but not least, the script code needs to normalize the vectors before using the dot product in this way whereas angle_to() does this under the hood in native code, making the script code faster and shorter.

                Also @DJMaesen if dot product is between -.3 and .3, that means that the angle between vectors is between acos(-.3) and acos(.3) which in degrees is a range between 72 and 107.

                  xyz So to sum it up. Here's a simple and clean way to do it:

                  if player.global_transform.basis.z.angle_to(-ladder.global_transform.basis.z) < deg_to_rad(30):
                  	# can climb

                    xyz DJMaesen what i want is if the angle is not greater then 0.3 the player should climb

                    I don't think OP wants to test if it's EXACTLY 30º, since we are talking about the angle between the player and a ladder. this can be adjusted to whatever works.

                    xyz angle_to() is simple, intuitive and totally adequate here.

                    I suppose it's a matter of taste. I find dot product to be more intuitive than using radians, at least for these very simple cases that don't need to be perfect, like testing if two vectors are almost aligned or facing opposite directions.

                    xyz And last but not least, the script code needs to normalize the vectors before using the dot product in this way whereas angle_to() does this under the hood in native code

                    again, matter of taste. I prefer to normalize my vectors instead of letting the engine methods do some unknown "under the hood".

                      Jesusemora do some unknown "under the hood".

                      Nothing unknown there. We know precisely how it works as there's only one efficient way to do it. Here's a custom GDScript implementation of angle_to():

                      func angle_to(v1: Vector3, v2: Vector3) -> float:
                      	return acos(v1.normalized().dot(v2.normalized()))

                      For 3D vectors there's shorthand version possible that avoids normalization by using cross product:

                      func angle_to(v1: Vector3, v2: Vector3) -> float:
                      	return atan(v1.cross(v2).length(), v1.dot(v2))

                      And this is exactly how Godot does it (from Vector3.h in Godot's source):

                      real_t Vector3::angle_to(const Vector3 &p_to) const {
                      	return Math::atan2(cross(p_to).length(), dot(p_to));
                      }

                      somhow looking up or down when entering the trigger doesnt make the trigger activate.

                      edit

                      nope it works fine, tnx guys!