Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 13 Next capture
2021 2022 2023
1 capture
13 Jun 22 - 13 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
1
Question by LukaKotar · Sep 27, 2013 at 02:32 AM · c#anglemove an object

Move an object inside a specific angle between two other objects?

Hi,

I wrote a script that moves an object when the mouse is moved, relative to the player's position, but I cannot find out how to restrict it's movement to only inside a specific angle. The attached image should explain things better:

image

Thanks in advance!

sketch.jpg (53.2 kB)
Comment
Add comment
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

2 Replies

· Add your reply
  • Sort: 
avatar image
1
Best Answer

Answer by whydoidoit · Sep 27, 2013 at 04:58 AM

This solution works in 2D or 3D.

Calculate the angle between the Point A and Point B vectors

    var pointAVector = (pointA - centre).normalized;
    var pointBVector = (pointB - centre).normalized;
    var maxAngle = Vector3.Angle(pointAVector, pointBVector);

Project the current target point onto the plane of the pointA and pointB:

      var planeNormal = Vector3.Cross(pointAVector, pointBVector);
      var targetVector = (targetPoint - centre);
      //Vector of target on the plane of PointA, PointB
      var planeVector = targetVector - Vector3.Dot(targetVector, planeNormal) * planeNormal;
      //so we can add back on the offset from the plane, store it
      var targetOffset = (targetPoint - planeVector);

Now you can calculate the angle between the targetVector and the pointA and pointB vectors, the simple way is to do this:

      var angleToA = Vector3.Angle(planeVector, pointAVector);
      var angleToB = Vector3.Angle(planeVector, pointBVector);

But if pointA or pointB are spread widely and the tested point can move far beyond the segement in a single frame, it's best to use a signed angle between the points:

 public static float AngleSigned(Vector3 v1, Vector3 v2, Vector3 n = default(Vector3))
 {
         n = n == default(Vector3) ? Vector3.up : n;
     return Mathf.Atan2(Vector3.Dot(n, Vector3.Cross(v1, v2)), Vector3.Dot(v1, v2))*Mathf.Rad2Deg;
 }

This lets us work out if either of them is bigger than the maximum:

     if(angleToA > 0) planeVector = pointAVector * planeVector.magnitude;
     else if(angleToB < 0) planeVector = pointBVector * planeVector.magnitude;

However, the order is important here and the result will quickly snap to the pointAVector if the angle between them is great. To get around that we need to compare the closeness to the two lines and do our test there:

     if(Mathf.Abs(angleToA) < Mathf.Abs(angleToB))
     {
         if(angleToA > 0) planeVector = pointAVector * planeVector.magnitude;
         else if(angleToB < 0) planeVector = pointBVector * planeVector.magnitude;
     }
     else
     {
         if(angleToB < 0) planeVector = pointBVector * planeVector.magnitude;
         else if(angleToA > 0) planeVector = pointAVector * planeVector.magnitude;
     }



Then work out the final position

      var allowedPosition = planeVector + targetOffset;

If you want the position to be clamped inside the circumference of the points then you need to do this:

 var allowedPosition = planeVector.normalized * Mathf.Min(
                planeVector.magnitude,
                Mathf.Lerp((pointA - centre).magnitude, (pointB - centre).magnitude, angleToA/maxAngle))
                + targetOffset;

Which basically clamps the magnitude to the circumference of an ellipse defined by the centre and the two points.

Here is a script to be attached to the moving object which keeps it between the points. Set the variables for the relevant bits in the inspector. It's configured to run in edit mode for easy testing:

 using UnityEngine;
 using System.Collections;
 
 [ExecuteInEditMode]
 public class KeepInPoints : MonoBehaviour {
 
     public Transform pointA, pointB, centre;
     
     // Update is called once per frame
     void Update () {
     
         Debug.DrawLine(pointA.position, centre.position, Color.white);
         Debug.DrawLine(centre.position, pointB.position, Color.white);
         var pointAVector = (pointA.position - centre.position).normalized;
            var pointBVector = (pointB.position - centre.position).normalized;
         var maxAngle = Vector3.Angle(pointAVector, pointBVector);
         
         var planeNormal = Vector3.Cross(pointAVector, pointBVector);
          var targetVector = (transform.position - centre.position);
          //Vector of target on the plane of PointA, PointB
          var planeVector = targetVector - Vector3.Dot(targetVector, planeNormal) * planeNormal;
          //so we can add back on the offset from the plane, store it
          var targetOffset = (transform.position - planeVector);
         
         var angleToA = AngleSigned(planeVector, pointAVector);
              var angleToB = AngleSigned(planeVector, pointBVector);
         
         if(Mathf.Abs(angleToA) < Mathf.Abs(angleToB))
         {
             if(angleToA > 0) planeVector = pointAVector * planeVector.magnitude;
             else if(angleToB < 0) planeVector = pointBVector * planeVector.magnitude;
         }
         else
         {
             if(angleToB > 0) planeVector = pointBVector * planeVector.magnitude;
             else if(angleToA < 0) planeVector = pointAVector * planeVector.magnitude;
         }
         
         
         transform.position =  planeVector.normalized * Mathf.Min(
                planeVector.magnitude,
                Mathf.Lerp((pointA.position - centre.position).magnitude, (pointB.position - centre.position).magnitude, angleToA/maxAngle))
                + targetOffset;
     }
     
     public static float AngleSigned(Vector3 v1, Vector3 v2, Vector3 n = default(Vector3))
     {
         n = n == default(Vector3) ? Vector3.up : n;
         return Mathf.Atan2(Vector3.Dot(n, Vector3.Cross(v1, v2)), Vector3.Dot(v1, v2))*Mathf.Rad2Deg;
     }
 }
Comment
Add comment · Show 10 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image whydoidoit · Sep 27, 2013 at 05:01 AM 0
Share

The allows the point to be at any height above or below the plane defined by pointA and pointB - if you just want it to be on that plane, don't add the targetOffset.

avatar image whydoidoit · Sep 27, 2013 at 08:25 AM 0
Share

If you want it to be in the segment of the ellipse described by pointA and pointB (as in it can't lie outside the perimiter) then:

     var allowedPosition = planeVector.normalized * $$anonymous$$athf.$$anonymous$$in(
                planeVector.magnitude,
                $$anonymous$$athf.Lerp((pointA - centre).magnitude, (pointB - centre).magnitude, angleToA/maxAngle))
                + targetOffset;

alt text

15873-sketch.png (30.6 kB)
avatar image LukaKotar · Sep 28, 2013 at 12:31 AM 0
Share

Thank you for taking the time to write this answer. I have a few questions regarding the code. Is the targetVector variable the position of the object I want to move? Following the instructions, I get the allowedPosition, but what would I need to do to actually keep the object inside the plane area? (What do I set the object's position to?)

avatar image whydoidoit · Sep 28, 2013 at 04:08 AM 0
Share

The variables going into the code are:

pointA, pointB, centre and targetPoint

You set the position of the thing to allowedPosition after the end of the code I posted.

targetVector is targetPoint - centre (so the vector from the centre to the target point)

The routine uses this as the representation of the target so that afterwards it can use the clamping on that, before turning it back into a simple position.

avatar image LukaKotar · Sep 28, 2013 at 11:34 PM 0
Share

O$$anonymous$$, I have set the targetPoint variable to the object I am moving, then after doing the moving part, I have put the code from the answer. Then I set the objects position to the allowedPosition. When I check the scene view, I see the object in the wrong position. Firstly, it is outside the angle, and secondly, the object doesn't move when I move the mouse. Is there something I am missing?

Show more comments
avatar image
1

Answer by robertbu · Sep 27, 2013 at 04:50 AM

I'm assuming the problem is 2D. If not, you would need to project "Game Object" onto the plane formed by the three points. Let's call the pivot point P.

One solution is to fine the vector that bisects Angle AB and also the angle of APB. Then you can test whether a point is within the angle by seeing if the point is within 1/2 of the angle APB of the vector that bisects the angle. Example script:

 function IsBetween() : boolean {
     var v3One = pointA.position - pivot.position;
     var v3Two = pointB.position - pivot.position;
     var v3Test = transform.position - pivot.position;
     
     var angle = Vector3.Angle(v3One, v3Two);
     var halfVector = (v3One.normalized + v3Two.normalized);
     
     return Vector3.Angle(halfVector, v3Test) < angle / 2.0;
 }

Note I'm assuming this script is on "Game Object" so that is why 3vTest uses 'tranform.position'. Also the routine above calculates the various vectors (v3One, v3Two, v3Test) each time it is called. If A, B and Pivot are static, these vectors could be calculated once in Start().

This solution will only work for angles less than 180 degrees. If you need a solution that handles larger angles, then you can define the two points in terms of left and right. Make PA the left vector, and make PB the right vector. You can calculate whether a test vector (Game Object-Pivot) is to the left or right of any vectors. It will be in between if it is to the right of the left vector and to the left of the right vector.

Comment
Add comment · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

16 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

Multiple Cars not working 1 Answer

Distribute terrain in zones 3 Answers

rotate to specific coordinate c# 1 Answer

Flip over an object (smooth transition) 3 Answers

Making a bubble level (not a game but work tool) 1 Answer


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges