I have been working on using RotateAround for a function in my game, and it’s been working great. I actually have a post that was answered, but the follow up should have been a different post.
I am trying to limit the RotateAround angle of 2 axes on an object. Right now it just rotates and rotates, and limiting it has gave me the run around.
First, I tried setting the tilt speed to zero, but since I am using the same tilt speed for both directions on both axes, this disables controls until the other part of the script re-orients the object to “home” automatically. Limits playability so I stepped away from that logic.
I found this topic, and was trying to understand the code and see if I could adapt it to my concept.
I believe he explicitly says this is just for one rotation, but I think I can figure out how to adapt it, but there’s this one line that is confusing me, even after looking up the API information:
OK, so here’s my code. Hoping this will draw some attention:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour {
private Rigidbody rb;
public Rigidbody player;
public float tiltSpeed;
private Vector3 negX = new Vector3 (-1f,0,0);
private Vector3 posX = new Vector3 (1f,0,0);
private Vector3 negZ = new Vector3 (0,0,-1f);
private Vector3 posZ = new Vector3 (0,0,1f);
public Tilt tiltz;
//public float speed;
void Start () {
rb = GetComponent<Rigidbody> ();
}
void FixedUpdate() {
float tiltHorizontal = Input.GetAxis ("Horizontal");
float tiltVertical = Input.GetAxis ("Vertical");
Vector3 tilt = new Vector3 (tiltVertical, 0.0f, -tiltHorizontal);
rb.transform.RotateAround (player.position, tilt, tiltSpeed * Time.deltaTime);
//tilt reset on release per axis
if (tiltVertical == 0 && transform.rotation.x > 0) {
rb.transform.RotateAround (player.position, negX, tiltSpeed * Time.deltaTime);
}
if (tiltVertical == 0 && transform.rotation.x < 0) {
rb.transform.RotateAround (player.position, posX, tiltSpeed * Time.deltaTime);
}
if (tiltHorizontal == 0 && transform.rotation.z > 0) {
rb.transform.RotateAround (player.position, negZ, tiltSpeed * Time.deltaTime);
}
if (tiltHorizontal == 0 && transform.rotation.z < 0) {
rb.transform.RotateAround (player.position, posZ, tiltSpeed * Time.deltaTime);
}
//tilt maximums per axis
}
}
So, let me explain:
I used a transform.rotation with a quaternion value fed by the Axes at first, but because I need the pivot to follow the player object, so I used this.
the “if” statements are to simulate resetting to zero rotation as the quaternion referencing the Axes input would do automatically. If I was still using the Quaternion approach with transform.rotation, I know how to limit the angle easily.
With RotateAround, the logic is obviously different. It’s not setting the rotation based on the Index, it’s applying a force in that direction. When the force is no longer applied, it just stops. I understand WHY, but does anyone have a way to limit that rotation? Also, are these if statements “overkill” for something that could be written differently to handle that?
It sounds to me like you’re working much harder than you should.
I can’t quite make out exactly what you’re trying to accomplish, though. Can you explain clearly what this code is supposed to do?
In general, when writing code to rotate things, I often find it much easier to keep my own pitch and yaw variables, which I update from inputs; and then set (not update) my transform rotation directly from those. This makes it trivial to limit the range of pitch and yaw as needed, avoids any possibility of gimbal lock, etc.
But without understanding what you’re trying to do, I’m afraid I can’t be more specific.
There is a ball (player) and there is a level (the tilting object). I’m trying to tilt the entire map (as it isn’t going to be very large, it’s a balance/puzzle game), and make sure that it only tilts at the position of the player, thus using the RotateAround
As I mentioned, I know how to use the transform.rotation quaternion route, but for some reason I just can’t accomplish the same with RotateAround. Given, I am “new” to game logic, as it is a hobby of mine I picked up a few years ago, and barely grasped coding to what I consider “acceptable” in the last 6 months.
I, uh… tend to overthink and work too hard, so nail on the head right there.
Let me know if you need any other information. Thanks again!
What about this idea: put the player and board inside another GameObject. Let’s call this other object the “container.” You never move the container, but you move the player and board so that the local position of the player is always at 0,0,0 within the container.
Now, you keep track of pitch and yaw in your own variables (limited as you see fit), and apply them to the container (not directly to the board). So the container never moves, but it does rotate… and it always rotates around the position of the player (because we make sure the player is always at the origin of the container).
Thank you for you answer, but I feel like I am still a little confused. Probably overthinking… lol
OK, so If I put the player and board in a game object, and rotate that game object, how does the container only rotate around the center of the player? Would this be additional logic in the code I would add?
I’m just not sure how both of them in the same container would follow only the player position. Thanks again, I have time to actually try this all day
Sorry I was unclear. The key point is that the container object always rotates around its own center, which from the point of view of any child object, is localPosition = (0,0,0).
So, if the player happens to be inside this container, and happens to be at localPosition = (0,0,0), then a rotation of the container is a rotation around the player.
So you just make that always be the case, by keeping the player there. Instead of moving the player, you move the board (which is also within the container). Or if it’s easier to think of it this way, you could move the player, and then see where he’s landed, and move him back to 0,0,0, moving the board by the same amount.
I think I understand, but not sure if I can make it work, but I will try. This is just my understanding of it:
The game is based around tilting the board, and the player rolls along this surface as the player is a ball. Like those old handheld “get the ball in the hole” kind of games.
Creating a container game object for both the free moving player, and the board, the physics of tilt would be applied to the ball and board, and the local 0,0,0 of the container moves away from the player (so to speak). So I use the player position to move the board for how much the player has moved? In other words, would I be writing code to keep the player at local 0,0,0 in the container game object, while moving the board in relation to how much the player should have moved based on the tilt? I barely maybe understand… lol
If you’re using Unity physics for the ball rolling, then that is a possible complication. The physics engine doesn’t really like it when you move stuff behind its back.
If that turns out to be a problem, then there is another way to tackle it. Don’t put the player or board in the container. Let the player move around (or rather, be moved by the physics engine) normally, but when you want to tilt the board, do this:
Move the empty container object to the position of the player.
Reparent both player and board to the container using SetParent(container, true) so that these things maintain their current place in the world.
Rotate the container object.
Reparent player and board again using SetParent(null, true).
Actually, now that I look at it, maybe this is the easier solution. It does what you were originally asking — rotates the board and player about the current player position.
Note that it’s possible to do the same thing with pure math, but I think it’s somewhat hairy math. Stuffing things inside a container, rotating the container, and taking them out again accomplishes the same thing, but lets Unity worry about the math. It’s probably slightly less efficient than doing the math yourself, but I bet it’s not a significant difference.
Actually, I managed to figure it out, and it was from your original logic. The play is smooth and I didn’t use any if statements… its beautiful! Thanks again.
Here’s what I ended up using for those who may be also trying to accomplish a similar approach to their logic:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour {
private Rigidbody rb;
public Rigidbody player;
public Rigidbody level;
public float angle;
private Vector3 moved;
void Start() {
rb = GetComponent<Rigidbody>();
}
void FixedUpdate () {
//Axis input for tilt controls
float tiltHorizontal = Input.GetAxis ("Horizontal");
float tiltVertical = Input.GetAxis ("Vertical");
//declaring my vector3's. tilt is the input variable I use for applying transform rotation to the container below, the "playerLocalPos/levelLocalPos" variables hold their respective position in the container
Vector3 tilt = new Vector3 (tiltVertical, 0.0f, -tiltHorizontal);
Vector3 playerLocalPos = player.transform.localPosition;
Vector3 levelLocalPos = level.transform.localPosition;
//applying the rotation to the container with a public multiplier for angle that is set in Unity
rb.rotation = Quaternion.Euler (tilt * angle);
//moved is the difference between the board's local position and the player position within the container game object
moved = levelLocalPos - playerLocalPos;
//apply the difference to the board
level.transform.localPosition = moved;
//reset player's local position to 0,0,0 to maintain tilt at the player position
player.transform.localPosition = new Vector3 (0, 0, 0);
}
}