Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

I am trying to get a turret to rotate on a 90 degree arc (rotating about 2 orientations about the y-axis), and have hit a wall.

I have 8 possible starting orientations (multiples of 45 degrees about the y-axis) for the turrets, and want them to rotate on a 90 degree arc. I currently take the starting orientation of the turret, and then from that derive the positive, and negative boundary of the arc. because of engine feature/restriction (Unity) I have to do all of my tests against a value which is between [0,360], and due to numerical precision issues I can not test against specific values, but should only limit tests to greater, and less then operators.

I would like to write a general test without having to go in, and jury rig cases

//my current test is:
// member variables
public float negBound;
public float posBound;

// found in Start() function (called immediately after construction)
/* because Unity uses a Quaternion for orientation of object they offer
 * the ability to work with them in the form of eulerAngles.
 * eulerAngles.y is the the degree measure of the starting y rotation */
negBound = transform.eulerAngles.y-45;
posBound = transform.eulerAngles.y+45;
// insure that values are within bounds
if(negBound<0){
    negBound+=360;
}else if(posBound>360){
    posBound-=360;
}

// called from Update() when target not in firing line
void Rotate(){
    // controlls what direction 
    if(transform.eulerAngles.y>posBound){
        dir = -1;
    }
    else if(transform.eulerAngles.y < negBound){
        dir = 1;
    }
    // rotate object
}

follows is a table of values for my different cases (please excuse my force formatting) read as base is the starting rotation of the turret, neg is the negative boundry, pos is the positive boundry, range is the acceptable range of values, and works is if it performs as expected with the current code.

| base | neg | pos |       range        | works |   
|    0 | 315 |  45 | 315->0,   0->45    | no    |
|   45 |   0 |  90 | 0->45,    54->90   | yes   |
|  135 |  90 | 180 | 90->135,  135->180 | yes   |
|  180 | 135 | 225 | 135->180, 180->225 | yes   |
|  225 | 180 | 270 | 180->225, 225->270 | yes   |
|  270 | 225 | 315 | 225->270, 270->315 | no    |
|  315 | 270 |   0 | 270->315, 315->0   | no    |

I will need to do all tests from derived, or stored values, but can not figure out how to get all of my cases to work simultaneously.

//I attempted to concatenate the 2 tests:
if((transform.eulerAngles.y>posBound)&&(transform.eulerAngles.y < negBound)){
    dir *= -1;
}

this caused only the first case to be successful

// I attempted to store a opposite value, and do a
void Rotate(){
    // controlls what direction 
    if((transform.eulerAngles.y > posBound)&&(transform.eulerAngles.y<oposite)){
        dir = -1;
    }
    else if((transform.eulerAngles.y < negBound)&&(transform.eulerAngles.y>oposite)){
        dir = 1;
    }
    // rotate object
}

this causes the opposite situation as indicated on the table.

What am I missing here?

share|improve this question
    
Can you try to re-read your question and see if anyone can understand it? At least for me is a little bit difficult to follow it. (for instance: 8 possible orientations? what? in 2D or in 3D?, what is an arc? a 2D arc or 3D arc?) what is transform.eulerAngles suppose to be? –  J. C. Leitão Jun 6 '12 at 6:40
    
@guardian06 I edited your table, but I wasn't sure whether an 'x' in 'works' was meant to indicate whether it does work (because it's marked) or doesn't work (because it's an x). I've chosen to interpret it as 'yes' so please check that. –  Jonathan Hobbs Jun 7 '12 at 0:04
    
@jonathanhobbs thank you, and that were my intentions. –  gardian06 Jun 7 '12 at 0:09
add comment

1 Answer

up vote 1 down vote accepted

The main problem with your approach is that you're forgetting that when one of the min/max values are outside of the [0,360] bounds, there are actually two possible ranges for values to be valid.

Your case one (Base 0):

0    45     315 360
[+++++]-----[+++++]

As such, in these cases, we need to compare against both ranges.

// Returns true if the current Y-axis rotation of this object is within the
// minBounds and maxBounds members wrapped to the [0,360] range, false otherwise.
private bool IsInBounds(){
    float rot = transform.eulerAngles.y;

    // First case, if the positive bound is greater than 360 degrees
    if ( posBound > 360f ) {
        // return (within lower bounds) OR (within higher bounds);
        return  ( 0f < rot && rot < posBound - 360f ) || ( negBound < rot && rot < 360f );

    // Second case, if the negative bound is less than 0 degrees
    } else if ( negBound < 0f ) {
        // return (within lower bounds) OR (within higher bounds);
        return ( 0f < rot && rot < posBound ) || ( negBound + 360f < rot && rot < 360f );
    }

    // Neither bounds are outside of the [0,360] range, only one range to check
    // return (within bounds);
    return ( negBound < rot && rot < posBound );
}

This code returns true if the value rot is in a bound. Note that I did not do any checking when generating posBound and negBound on turret creation; I let them stay out of the [0,360] range.

I only added in the extra variable for readability, in your actual code rot should be replaced with transform.eulerAngles.y

...Unless Unity doesn't cache the Euler angle representation of the rotation, in which case it's probably much faster to store the angle locally. On that note of caching, I'm not certain if caching the full bounds and the correct in-bounds function as a delegate instead of the if comparisons would be worth the memory, but it's something worth thinking about.

The public fields are making my C# encapsulation sense tingle. You should probably make those private, and create public properties if you REALLY need to access those two variables outside the class, unless Unity can't cope with properties.

share|improve this answer
    
your encapsulation sense is correct, and it was part accepted risk, and debugging that led me to marking them as public. Unity offers a feature called the inspector(unity3d.com/support/documentation/Manual/…) which allows for public variables to be viewed as they change, and sense they are set at run time I marked them as public so that I could see the values. (they will be set back to private, and only used internally to the class). –  gardian06 Jun 6 '12 at 17:10
    
does this go in place of my Rotate(), or in place of my tests inside Rotate()? –  gardian06 Jun 6 '12 at 17:12
1  
The function replaces what you currently have to determine if the direction should change. Add the function as a non-static member function to your turret class, remove the code that restricts the minBounds and maxBounds variables to the [0,360] range, use if(!IsInBounds()) dir *= -1; for your dir-changing logic, and you should be good to go. –  CKC Jun 6 '12 at 22:51
    
after doing some thought/tests because of a combination of numerical precision, and very high probability(it is a very rare case that it does not) overshoot the boundry. I need to directly control which direction the turret is rotating when it passes the boundry like in the Question). considering that if I use what is placed in your comment all the turrets will overshoot within 2 switches, and then just jitter until something else makes them rotate away. –  gardian06 Jun 7 '12 at 4:39
    
(separated for clarity) as a side note testing rotfor equal to a value is meaningless due to precision issues (any stored value entered/calced is highly likely to be 3...e-10 off), and on top of that eulerAngles are converted from a quaternion meaning a very high degree of precision issues, but not enough to make them unusable, and even with easier to work with then a raw Quaternion –  gardian06 Jun 7 '12 at 4:42
show 1 more comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.