0
\$\begingroup\$

I try to "limit" the amount of angular movement my character is able to "show". I'm using Unity.

I have 3 components in my player entity as below:

enter image description here

The "Player" contains a Rigidbody2D responsible for the movement. The "Body" is an animated sprite that (up to now) moves alongside the Player entity. So it is able to move the sprite around the "full circle".

enter image description here

My main problem is, that once it crosses the y-Axis (z-Axis rotation > Abs(90)), the "Body" is upside down which is really looking weird. Furthermore, it the "style" of my game does not really support the player "visually" going directly upward or downward the y-Axis. That's why I want to do 2 things:

  1. "Flip" the "body" (mirror on the y-Axis) once the z-Axis rotation is above/below 90.
  2. Limit the visual (just the visual) representation of the player's facing direction - see image below red area - so the player is never shown going directly up or down (the Rigidbody2D still can move in this direction, it's just not visualized that way).

enter image description here

My current rotation logic is quite simple:

var angle = Mathf.Atan2(velocity.y, velocity.x) * Mathf.Rad2Deg;
var angular = Mathf.LerpAngle(transform.rotation.eulerAngles.z, angle, this._behaviorBase.MaxAngularAcceleration * Time.deltaTime);
angular = (angular + DEG_IN_2PI) % DEG_IN_2PI;
var angularDirection = Quaternion.Euler(0f, 0f, angular);
this._body.SetRotation(angularDirection);

This just rotates the player's Rigidbody2D into the movement direction. I'm also able to "counter-rotate" the "Body" component inside the "Player" component so that it always looks in the same direction (unaffected by the Rigidbody movement) with this:

var angle = Mathf.Atan2(velocity.y, velocity.x) * Mathf.Rad2Deg;
var angular = Mathf.LerpAngle(transform.rotation.eulerAngles.z, angle, 
this._behaviorBase.MaxAngularAcceleration * Time.deltaTime);
angular = (angular + DEG_IN_2PI) % DEG_IN_2PI;
var angularDirection = Quaternion.Euler(0f, 0f, angular);
this._body.SetRotation(angularDirection);
// new code for counter rotation
var counterDirection = Quaternion.Euler(0f, 0f, this._body.transform.rotation.z * 1f);
this._bodyTransform.rotation = counterDirection;

But whatever I try to flip the Body at the y-Axis, does not work and only rotated by 90 degrees instead of the full 180. Furthermore, I have quite some problems figuring out how to correctly "clamp" the rotation between -45 < rotation < 45 and 135 < rotation < 225 because the rotation could be either in the positive or negative rotation representation.

Any tips on how to solve this would be highly appreciated.

EDIT 1:

@Kevin: This is the player:

enter image description here

I want to make sure that it can never look like this

enter image description here

The "visible rotation" should never exceed z-Axis angles of +-45 degree (but the Rigidbody should be able to rotate further)

enter image description here

Also, in case the rotation is in the other direction it should be flipped like this

enter image description here

In all cases, the rotation along the z-Axis should be between -45/+45 degrees.

\$\endgroup\$
2
  • \$\begingroup\$ I think it would be easier to understand what you're trying to accomplish if you included an image of your player sprite. \$\endgroup\$
    – Kevin
    Aug 25, 2022 at 20:30
  • \$\begingroup\$ @Kevin I edited the post above. \$\endgroup\$
    – Alex
    Aug 25, 2022 at 22:03

1 Answer 1

1
\$\begingroup\$

I was able to resolve the problem. I introduced a reference for the "Body" (Transform) in the controller and basically did 3 steps:

  1. Counter rotate the Body element in relation to the Rigidbody
  2. Flipped the Body using negative scaling of the direction (left or right looking of the body) changed
  3. Clamped to angle according to the direction and applied it to the Body element.

I also introduced an additional flag to prevent strange "jittering" behavior if the movement rotation (Rigidbody) is close to vertical.

    var counterAngular = (int) this._body.rotation;
    counterAngular = (counterAngular + (int) DEG_IN_2PI) % (int) DEG_IN_2PI;
    var direction = counterAngular <= 90f || counterAngular >= 270f;
    var stall = Mathf.Abs(counterAngular - 90) <= 1 || Mathf.Abs(counterAngular - 270) <= 1;
    if (!stall && this._lastDirection != direction)
    {
        this._bodyTransform.transform.localScale = new Vector3(-this._bodyTransform.transform.localScale.x, this._bodyTransform.transform.localScale.y, this._bodyTransform.transform.localScale.z);
        this._lastDirection = direction;
    }

    var clamped = RotationUtils.ClampAngle(counterAngular, direction, this._lastRotation);
    var clampedAngular = Mathf.LerpAngle(this._bodyTransform.rotation.eulerAngles.z, clamped, this._behaviorBase.MaxAngularAcceleration * Time.deltaTime);
    var counterDirection = Quaternion.Euler(0f, 0f, clampedAngular);
    this._bodyTransform.rotation = counterDirection;
    this._lastRotation = clamped;

This is not the "elegant" solution I was looking for initially but it does the cut and maybe it will help others as well.

If there are better solutions out there, I'd be interested.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .