Friday, July 12, 2019

geometry - How to limit click'n'drag movement to an area?


I apologize for the somewhat generic title. I'm really don't have much clue about how to accomplish what I'm trying to do, which is making it harder even to research a possible solution.


I'm trying to implement a path marker of sorts (maybe there's a most suitable name for it, but this is the best I could come up with).


In front of the player there will be a path marker, which will determine how the player will move once he finishes planning his turn. The player may click and drag the marker to the position they choose, but the marker can only be moved within a defined working area (the gray bit).


Path Marker Diagram


So I'm now stuck with two problems:


First of all, how exactly should I define that workable area? I can imagine maybe two vectors that have the player as a starting point to form the workable angle, and maybe those two arcs could come from circles that have their center where the player is, but I definetly don't know how to put this all together.


And secondly, after I've defined the area where the marker can be placed, how can I enforce that the marker should only stay within that area? For example, if the player clicks and drags the marker around, it may move freely within the working area, but must not leave the boundaries of the area. So for example, if the player starts dragging the marker upwards, it will move upwards until it hits he end of the working area (first diagram below), but if after that the player starts dragging sideways, the marker must follow the drag while still within the area (second diagram below).


First Diagram: Moving Upwards Second Diagram: Following the drag


I hope this wasn't all too confusing. Thanks, guys.



Edit: In case this makes a difference, I'm using C++ with Marmalade SDK.



Answer



You can define a workable area like the one in your question with three values:


float innerRadius;
float outerRadius;
float maxAngle;

These values will be based off a center point which may or may not be the player's position. The shape of the workable area depends on where you place this point.


enter image description here


In the example above the center position lies a certain distance (let's say 50 units) behind the player. This could be easily calculated as:



float offset = -50;
Vector2 centerPosition = playerPosition + offset * playerForward;

To limit the position of the marker to that workable area, first move the marker as you normally would. Then validate the distance between the center point and the marker:


Vector2 direction = markerPosition - centerPosition;
float distance = direction.Length();
direction.Normalize();
markerPosition = centerPosition + direction * clamp(distance, innerRadius, outerRadius);

Finally, validate the angle of the marker to the specified range. I'll use pseudocode for this one:



- Find angle between vector C->M and vector playerForward
- If abs(angle) <= maxAngle Then do nothing
- Else If angle > 0 Then rotate M around C by maxAngle-angle
- Else If angle < 0 Then rotate M around C by -maxAngle-angle

Look around on how to rotate a point around another. It can be done either with trigonometry or a transformation matrix.


You might also want to take into account the marker's size and make the radius and angle a little smaller to compensate.


Edit: On second thought, it might look more natural if you validate the angle first, then the distance, so try both alternatives!


No comments:

Post a Comment

Simple past, Present perfect Past perfect

Can you tell me which form of the following sentences is the correct one please? Imagine two friends discussing the gym... I was in a good s...