问题
I have a 2d arrow rotating to always face the a target (the target in this case is the cursor), the pivot is my player character. I need to restrict this arrow to only follow the target if it is inside an angle of the player, an example would be 90 degrees, so it would only follow if the cursor is in the top right part of the screen.
I have worked with vector directions and methods such as Vector2D.angle, but they all seem to have some restriction i can't workaround, Vector2D.angles restriction is that the 3rd position it uses to calculate the angle is the world center(0, 0), my player is mobile so that doesn't work. So i think what im asking is if theres a way to store an angle, and then check if something is within that.
Here is the code i use for rotating my arrow, theres more to the script but i removed the unnecesary parts:
public float speed;
public Transform target;
void Update()
{
Vector2 mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
Vector2 direction = target.position - transform.position;
target.position = mousePosition;
float angle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg;
Quaternion rotation = Quaternion.AngleAxis(angle, Vector3.forward);
transform.rotation = Quaternion.Slerp(transform.rotation, rotation, speed * Time.deltaTime);
Sorry if this is formatted poorly, its my first time posting here, thank you a million times if you are able to help me, i have been stuck on this for days.
Was asked to clarify question so here is an attempt:

This picture shows an example of what i mean, the arrow is rotating around the center of the circle, (it is rotating so it always points towards my cursor). What i need is a way to restrict it so it only points towards the cursor if it is within a specific angle (red lines in picture), that's what im having trouble with. I can't find a way to store this threshold and i can't seem to find a way to compare the cursors direction with it.
I think it would be possible if it was possible to choose a custom center for Vector2D.angle, but that doesn't seem to be the case.
I hope this clarifies what my question, i may just very well be stupid and overlooking something obvious but i really can't find a way to make this possible. thanks again.
回答1:
Important fields/inputs
First, we need to know the boundary directions in world space:
public Vector2 boundaryDirectionLeft;
public Vector2 boundaryDirectionRight;
The important piece is that the angle made clockwise from boundaryDirectionLeft
to boundaryDirectionRight
is the region the arrow shall remain inside. In the case of your image, boundaryDirectionLeft
could be Vector2.up
and boundaryDirectionRight
could be something like new Vector2(1f,-1f)
.
We also need to know which local direction the arrow is facing before any rotation is applied. E.g., if the arrow is always pointing with the local red arrow axis (the local right direction), this would be Vector2.right
:
public Vector2 localArrowDirection;
And lastly, we need a top speed for our rotation, so we don't warp the arrow around when it's time to rotate. A good value to start with might be 360f
, 360 degrees per second. Try experimenting with different values to find one that you like:
public float maxRotationSpeed;
The update procedure
In Update
, determine the direction to the target:
Vector2 mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
Vector2 direction = mousePosition - transform.position;
Then, we need to know where the arrow is currently pointing. We can use Transform.TransformVector to find where the local localArrowDirection
is pointing in world space:
Vector2 currentDirection = transform.TransformVector(localArrowDirection);
Then, determine the signed angle from boundaryDirectionLeft
to the target direction, the same from boundaryDirectionLeft
to boundaryDirectionRight
, and the same from boundaryDirectionLeft
to the current facing direction:
float directionAngle = Vector2.SignedAngle(boundaryDirectionLeft, direction);
float boundaryAngle = Vector2.SignedAngle(boundaryDirectionLeft, boundaryDirectionRight);
float currentAngle = Vector2.SignedAngle(boundaryDirectionLeft, currentDirection);
These values range from [-180,180], but we want them expressed in the range [0,360) to make the math easier later, so we can add 360f
and use Mathf.Repeat on that sum:
directionAngle = Mathf.Repeat(directionAngle+360f, 360f);
boundaryAngle = Mathf.Repeat(boundaryAngle+360f, 360f);
currentAngle = Mathf.Repeat(currentAngle+360f, 360f);
At this point directionAngle
is how many clockwise degrees from boundaryDirectionLeft
that target
is, and boundaryAngle
is how many boundaryDirectionRight
is, and currentAngle
is the same for what direction we're currently facing.
So, now, we need to know how to properly clamp the angle between 0 and boundaryAngle
. Anything too far above boundaryAngle
is actually closer to the left boundary and should be clamped to the left boundary. In fact, since everything is between 0 and 360, anything higher than boundaryAngle+(360f-boundaryAngle)/2f
is closer to the left. So, we just set anything higher than that to be 0 degrees away from boundaryDirectionLeft
:
if (directionAngle > boundaryAngle + (360f - boundaryAngle)/2f)
{
directionAngle = 0f;
}
So, now we can clamp directionAngle
with a high of boundaryAngle
(it is already bottom clamped at 0f
, so we can use Mathf.Min
here):
directionAngle = Mathf.Min(directionAngle, boundaryAngle);
Now we can limit the angular difference between directionAngle
and currentAngle
using our maxRotationSpeed
:
float deltaAngle = Mathf.Clamp(directionAngle-currentAngle,
-maxRotationSpeed * Time.deltaTime,
maxRotationSpeed * Time.deltaTime);
Now we can rotate the transform deltaAngle
degrees clockwise (in world space):
transform.Rotate(0f,0f,deltaAngle,Space.World);
来源:https://stackoverflow.com/questions/56841720/arrow-rotating-to-face-cursor-needs-to-only-do-so-while-inside-an-angle-made-by