Have 3d object rotate around another 3d object based on mouse position.

The title gives the basic idea, i am fairly new and i feel like this is a bit over my head, how the heck do i code something like this, iv been trying to use lookat while constraining the X or Y to try and make it turn around but that does not seem to work, any ideas?

5518204--566641--upload_2020-2-24_17-50-20.png

basically i want the wall to rotate around the cube.

take your mouse coordinates, with Input.mousePosition or whatever the code is, you’re ought to obtain Vector2
set the wall’s parent to be the cube’s transform ← very important.

you do this by calling SetParent on the walls transform like this

if you’ve meticulously set your wall where it should rest relative to the cube, then you need to set the 2nd argument of this method to true.

otherwise, it will probably reposition somewhat randomly according to its pivot position.

wall.transform.SetParent(cube.transform, true);

take your X and Y coordinates of the mouse, and % them by 360.
this is a division remainder operator. it’ll divide a number and give you a remainder.

for example, if you divide 24 by 10, you get 2.4, however the remainder of this division will be 4, because this is what you get left when you take all the 10s out of it.

that way you can get a (quasi-)modulo which is important because rotations work in cycles from 0-360 degrees.
the number will never get beyond this. BUT, and this is why I said “quasi-” if you’re doing it with a negative number, you’ll get a negative number, which isn’t what you want.

we’ll correct this on the spot by nudging the value into positives.

for now, let’s suppose you want to rotate around the X axis (red) with your mouse Y
and you want to rotate around the Y axis (green) with your mouse X

this is the code so far

Vector2 mouse = something something; // suppose we got this filled,
                // I'll assume you already know this part, because I can't remember tbh

float rotX = mouse.y;
float rotY = mouse.x;

while(rotX < 0f) rotX += 360f; // just nudge it into positives
rotX %= 360f;

while(rotY < 0f) rotY += 360f; // let's do this again
rotY %= 360f;

we got our angles!

now we call our friend Quaternion. nonono, don’t be afraid :slight_smile:
we’ll use a tool here

var myActualRotation = Quaternion.Euler(rotX, rotY, 0f);

that’s it!

wall.transform.rotation = myActualRotation;

maybe I swapped something, or made a mistake, just say so and I’ll come back to fix it.

oh, and make some sort of factor that you can expose in the inspector.

like so

public float myFactor; // put this in the Monobehaviour class,
                                       // but outside of any method

additionally, you might set an attribute to constrain its range

[Range(-10f, 50f)] public float myFactor;

this will act like some sort of mouse sensitivity.
a simple float that you can use to multiply your mouse in the code above.

simply do

mouse *= myFactor;

this will help you easily change the speed of the rotation.

now you need to tell me if you’re familiar with Update as well or should I show you that part also.

I’ve edited the comment numerous times, because of omissions. refresh if you’re reading it.

here’s a proper general-purpose modulo function for floats, I advise you to learn more about modular arithmetic
you can immediately see what it’s all about from the WP picture on the top right, no need to read a lot about it.

basically, you transform a linear value, such as a mouse coordinate into a range that can repeat indefinitely, like the hands of a clock, or in your case, rotation.

unlike the division remainder %, this function will correctly work only with a positive modulus, and the returned number will always be in the range [0 … modulus], where modulus itself is excluded.

static public class ExtensionUtils {
    static public float Mod(this float value, float modulus) {
        if(modulus <= 0f) throw new System.ArgumentOutOfRangeException("Modulus must be greater than zero.");
        if((value %= modulus) < 0f) value += modulus;
        return value;
    }
}

place this code in a separate file, and add to it any such code you find in the future. these are so-called extensions (they have a word ‘this’ in their argument declaration). you can call this whenever you need it, but you need to add ‘using ExtensionUtils;’ at the beginning of your script, similar to how you’ve already got System.Collections and UnityEngine.

and then when you type a dot after any float variable (or even a literal float value), you’ll get Mod as an option.

with it, you could fix the above example like this

mouse = new Vector2(mouse.x.Mod(360f), mouse.y.Mod(360f)) * myFactor;

much more readable eh?

you can also try to swap the order of operations. this isn’t a terrible idea actually

mouse = new Vector2((myFactor * mouse.x).Mod(360f), (myFactor * mouse.y).Mod(360f));

This worked kind of but i was hoping that the wall would follow the mouse, rather then the position of the mouse being like a scroll wheel.

ah ok, well you weren’t terribly precise in your explanation, but this was also worth learning, no?

ok, then the solution is completely different.

you need a ground floor, which you probably already have, but it needs to have a rigidbody on it, and a box collider.
turn this object static, so that it doesn’t try to move and bother the rest of the physics. maybe you already have this set up.

then you want to shoot a ray from your camera to your mouse. try to find a solution for that on the forum, so that we can fast forward to the good stuff. basically you shoot a ray, that ray hits the ground, the point where you hit the ground is the point where the mouse is pointing at.

to make it avoid hitting other objects you set different “layers” of collision, and then you make ground on its own layer. so that when you shoot the ray you can tell it to be affected only by this ground layer.

check out the docs on this, there are examples as well

next, once we have this coordinate, this is the coordinate towards which the wall wants to rotate. right? I hope I haven’t misread your question again this time. so the wall should always get in between the ball and the mouse pointer.

ok, to do this, you need two points. one point is the center of the ball itself, the other is the hit point on the ground. notice that this ground point is lower than the ball center, so we’ll ignore that kind of down tilt when we rotate. we want to rotate the wall only around the Y axis, am I right?

so two points, ballPoint and mousePoint. both will have x, y, z coordinates, but we’ll disregard the y.
if you subtract mousePoint by ballPoint you’ll get the vector (imagine an arrow) that points from the ball to the mouse.

to position the wall on the circle around the ball, you could use the normalization of this vector, multiplied by some distance value. to rotate the wall accordingly you could use Quaternion.AngleAxis and supply the angle and the up orientation as the axis.

or you could just call the wall’s transform and call LookAt method, which is the preferred method, simply because it’s easier.

var coord = hit.position; // suppose that we got this via RayCast
wall.transform.LookAt(coord, Vector3.up);
// this 'Vector3.up' is so that it has a concept of what should remain 'up' after rotation, otherwise it might tumble sideways

but wait, if your wall’s center isn’t at the same height, this might tilt it anyways.
so let’s just make sure that it’s on the same height

var coord = hit.position;
coord.y = wall.transform.position.y;
wall.transform.LookAt(coord, Vector3.up);

but we could use that normalization anyway to position the wall nicely.

var coord = hit.position;
var norm = (coord - wall.transform.position).normalized;
coord.y = wall.transform.position.y;
wall.transform.position = distance * norm; // try to replace distance with 1f, 2f, 3f ...
wall.transform.LookAt(coord, Vector3.up);

for this to work properly, you don’t have to set parent of wall’s transform to a ball’s transform.
but if you do, it’ll still work, because we force its world position to a proper position regardless of the hierarchy.

if the wall isn’t oriented properly, it’s original orientation should be set so that this mouse position is hypothetically in the direction of the blue arrow (forward). also make sure its bottom center lies on (0,0,0) in its local space.