Script issue with having bones follow the mouse pointer

Hi Unity forums, having a little trouble wrestling with the unity script (JavaScript) in my game.

What I currently have is my character walking, running and jumping with keyboard commands on a 2d plane (x axis).

I want to be able to have a one key press disable these movements and assume the model into the crouch position.

While in this position the character will equip their gun, and all control will be shifted to the mouse movement (think defensive, rotating turret). When moving the mouse button, the top half of the character (lower torso upwards, including arms) will follow the mouse pointer on an axis at the waist. Essentially, aiming with the mouse on a 3d plane (x axis).

When the key command is pressed again, movement on the 2d plane (walking, running, ect) resumes, and mouse control is disabled.

I currently have the walking mechanics near enough done. I have also assign a material to the mouse (which follows the mouse where ever it goes) but what I’m struggling with is the aiming mechanics.

I have tried assigning this script to the lower torso bone, which is parents to the top half of the body (all the bones I want to move):

var target : Transform;

function Update () {

	if (Input.GetButtonDown("Mouse2"))
	transform.LookAt(target);


}

Simple, I know as it doesn’t even remotely work. What I would like to know is if anyone knows whether this game mechanic has been done before in unity, and how it was achieved? Or just general suggestions on how to perform this. I’m not a coder by profession, so I apologies for this topic in advance, I’d just really like to be able to learn how to perform this action.

Here is a link to a paint drawing I whipped up to add a visual description to what I’m trying to achieve here.

Thanks,
Terragot.

Well, if I’ve understood what you need correctly, it shouldn’t be too difficult to implement.
I’m not sure why you’re using Input.GetButtonDown, even though you seem to need the reaction to the movement, not the press of the button. Also, I’m not sure what the target is.
I think you could try something like this:

transform.localEulerAngles.x += Input.GetAxis("Mouse Y");

I think this should move the torso of the character (I’m not sure about the axis, though. You might need to change “.x” to some other value).
It’s hard to tell, whether this would work the way you want, because I don’t know what is the target object (and if you need it at all), but maybe it’s going to help a bit. You could try it out and say if it works :slight_smile:

Hey,

Thanks for that piece of code, but I’m afraid It didn’t work with mine.

As I haven’t had much time today I have been trying to assign this rotation script to my character:

// LookAtMouse will cause an object to rotate toward the cursor, along the y axis.
//
// To use, drop on an object that should always look toward the mouse cursor.
// Change the speed value to alter how quickly the object rotates toward the mouse.

// speed is the rate at which the object will rotate
var speed = 4.0;

function Update () {
    // Generate a plane that intersects the transform's position with an upwards normal.
    var playerPlane = new Plane(Vector3(1, 0, 1), transform.position);
    
    // Generate a ray from the cursor position
    var ray = Camera.main.ScreenPointToRay (Input.mousePosition);
    
    // Determine the point where the cursor ray intersects the plane.
    // This will be the point that the object must look towards to be looking at the mouse.
    // Raycasting to a Plane object only gives us a distance, so we'll have to take the distance,
    //   then find the point along that ray that meets that distance.  This will be the point
    //   to look at.
    var hitdist = 0.0;
    // If the ray is parallel to the plane, Raycast will return false.
    if (playerPlane.Raycast (ray, hitdist)) {
        // Get the point along the ray that hits the calculated distance.
        var targetPoint = ray.GetPoint(hitdist);
		 targetPoint.z = transform.position.z;
        
        // Determine the target rotation.  This is the rotation if the transform looks at the target point.
        var targetRotation = Quaternion.LookRotation(targetPoint - transform.position);
        
        // Smoothly rotate towards the target point.
        transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, speed * Time.deltaTime);
    }
}

This script was taken from the wiki for use in top-down shooting games.

So, with this attached, the entire body rotates towards the direction of the mouse pointer.

I tried assigning it to the armature bones, but it didn’t respond at all. Is there any way to ‘activate’ bone movement in-game or can this just not be achieved? Basically, as before, I want the top half of my character (torso upwards) to rotate and face the direction of the mouse pointer.

Second is, looking at that script, I can’t figure out how to set it to rotate on a single axis. I want to restrict the rotations to one axis, any idea on how to do this?

Thanks again,
Terragot.

Well, I’ve done partial body movement in the script before (to be precise, rotating the body and the head to the direction of the camera, while the feet stay in place).
I had actually made two (well, actually, three, because there were two bones moving, but that doesn’t matter) variables in the character script: main camera and the needed bone. Then each frame (in LateUpdate() ) I took the Y rotation of the camera and assigned it to the bone (in your case, you might need the local X Euler angle or something).
I’m not sure if this could be done in your exact case, but I think it could.
By the way, you should add the bone movement to LateUpdate (not Update) function (I had forgotten to mention that). Maybe that was the main problem why your (and my) pieces of code didn’t work.

Anyway, tell me how it goes.

Thanks again Domino, LateUpdate worked as expected and the script now functions.

Been getting into a tangle with this again today, heres what I have.

So far, when the player holds down the right mouse button a crosshair will appear on screen and follow the mouse pointer around. As soon as right mouse button is released then the cross hair will stop following the mouse. My question for this script is; what line of code do I use to have the cross hair appear (or render) when the right mouse button is held down and disappear when it is released?

I’ve added a comment to where I think the code should be, I know there’s a render setting out there that would be ideal for this, I’m not sure if instantiating it would be too much work?

var rayCastPlane : Transform;

function LateUpdate () {



	if (!Input.GetMouseButton (1))
		return;
		
		//should have the object render here, and again destroyed or made invisible when mousebutton 1 is released
	
	var ray = Camera.main.ScreenPointToRay (Input.mousePosition);
	var hit : RaycastHit;

		if (Physics.Raycast (ray, hit, 200)) {
			Debug.DrawLine (ray.origin, hit.point);
				
					transform.position.y = hit.point.y;
					transform.position.x = hit.point.x;
					
		}

	
	
}

Secondly, In having this cursor in the game, this allows for me to avoid using complex ray casting for my rotations. So I’m back onto the simple script of having the bones Look.At the crosshair object. The only problem being that when the bones Look.At the object they fold to the side and rotate, giving the character a strange, snapped back rotation look. Is there anyway to keep the bones in line and restrict them from folding like this?

Again here’s the code, still very simple.

//The Transform is the crosshair, this script attaches to the character

var target : Transform; 

function Update() {
    transform.LookAt(target);
}

Also a picture of what’s happening to the character when this script is assigned to his bones.

Perhaps restricting the rotation along the Z axis would solve this?

For the crosshair object the best way probably would be to instantiate it in the Start function (and disable it after instantiating) and in the LateUpdate (or Update, it shouldn’t matter) function you could do something like this:

if (Input.GetMouseButtonDown(1))
    target.active = true;
else if (Input.GetMouseButtonUp(1))
    target.active = false;

For the rotation problem, there could be various reasons. It’s usually tricky to use bone rotation that way, because the axes are often not positioned the way you expect. That’s why I’d calculate the vector between the bone position and the target position and then using the vector rotation in space set the bone rotation myself (without using any prepared functions). However if you really want to use the LookAt function, you could try adding and changing the second parameter, which could be Vector3.right or something. Just play with it.
By the way, does the character always rotate to its side or does it depend on which direction it is facing?

When I was using the script which incorperated the vector3 I had a play around with all vector3.up, vector3.right, vector3.forward and they all ended up with the bones of the characters snapping to the side. I’m at work at the moment but I’ll dive into this again tonight I’m almost certain its something to do with the axis of the bones insisting on snapping the bones flat as appose to erect. I just can’t reason why this is.

Thanks again though, you’re help is always appreciated and I can’t thank you enough.

(most of this can be ignored up until the edit)

Still can’t get this mechanic to work.

Is there anyway to restrict the rotation of the bones? Such as cancel out it’s rotation so that it can only possibly rotate along the z axis.

Vector3.forward is the correct vector, but the bone still snaps to it’s side. This is driving me mad now, I’ve spent far too long on this already.

Tomorrow I’m going to try a last resort and export the model into two halves. The torso and the legs. This will take some time to do and I’d imagine a lot of re-animating but I’m all out of other ideas.

I’m thinking if I parent the two body parts and have the torso rotate independently, possible the arms too, this may look slightly bad with some clipping issues but it might just work.

Such a shame my lack of knowledge cant finish this method off though. I know there must be some way to instruct that bone to rotate, upright along the z axis.

Concerning the cross-hair I still need to work out the code to instantiate it as the code I have right now simply instructs the parent object. I’m assuming this would mean making the prefab of the cross-hair so I’ll get busy and create that first.

I think what I need is a course based around how to learn unity script, but there doesn’t seem to be anything that actually teaches you technique out there, just a lot of tutorials that have no real weight outside of what they are titles about.

Thanks again anyway, sorry I couldn’t get anything to show you!

EDIT

Okay I have been playing around with your first bit of code:

function Update () { 

transform.localEulerAngles.y += Input.GetAxis("Mouse Y"); 

}

And this seems to work as in that it,

A. Keeps the bones turgid, upright and exactly where I want them.

B. Moves the bones along the correct axis, with movement translated through the mouse.

I’m going to try adding an if Input.GetKeyDown to trigger this my main concern is that it doesn’t follow the cross hair, but I can always scrap that design and have a laser sight for aiming reference.

Infact, playing with this just now, it probably makes more sense for it to remain like this (it could easily be ported to keyboard controls for player preference) So I’m just going to try to figure out how to add a speed variable. The sensitivity of the mouse movement isn’t great enough. Anyway I don’t know what I’m trying to say here, I’m very tired and another night has been spent on this! But I think what I’m saying is I may have good news on this and basically, I’m working with the new code listed above.

Thanks as always and I’d love to hear any feedback from you (or suggestions).

Terra

EDIT 2

Latest code. any Idea why this doesn’t trigger to my mouse 2 command? Without the function start it triggers on game load and then stops working when mouse 1 is pressed.

//Added this in to prevent the aiming from being triggered on game start
function Start () {

enabled = false;
}
function Update () { 

//was hoping your code would be enough to trigger a repeating instance of aiming but doesn't seem to be working

if (Input.GetMouseButtonDown(1)) 
    enabled = true; 
else if (Input.GetMouseButtonUp(1)) 
    enabled = false; 

transform.localEulerAngles.y += Input.GetAxis("Mouse Y"); 

}

Okay now defiantly bed![/u]

Just multiply the Input.GetAxis(“Mouse Y”) by some variable or constant.

About the enabling and disabling problem. I’m not sure if you have your own “enabled” variable (and if you’d be allowed to have one), but what you probably do here is disable the script at the beginning. Then it’s logical that it doesn’t react in any way later (you can re-enable it from itself, because it doesn’t do anything when it’s disabled).
The best way to do this would probably be to add your own boolean variable isEnabled to this script and use this one. Then add an “if” statement to the rotation:

if (isEnabled)
{
    transform.localEulerAngles.y += Input.GetAxis("Mouse Y");
[quote]}

Hope it helps :)[/quote]

Okay, rotation is in. It triggers at the correct key command and all bones stay in perfect motion across the correct axis!

I’ve set up an angle restriction with a mathf.clamp. It would work if I could use negative angles though, which I can’t seem to figure out.

i.e. Ideally the min rotational angle would be set to -45 and the max set to 45. The problem is when I try to do this, as soon as the bone hits near the 0 degrees mark, it resets back to the maximum.

Is it possible to allow my min and max rotations exceed 0 degrees in both positive and negative numbers?

Code, and a quick picture to show exactly what I mean.

this is the restriction code:

// the two restriction variables

var minAngle = -45; 
var maxAngle = 45; 

  //the rotation restriction 
 
     transform.localEulerAngles.y = Mathf.Clamp(transform.localEulerAngles.y, -minAngle, maxAngle);

Although I’m not sure why you are using a minus value for the negative number (the minus makes it positive again, so, you are basically clamping between 45 and 45), I think it isn’t the main problem.

I think the values of the Euler angles are always positive, so, if you have a rotation, which is a bit less than zero, it becomes a bit less than 360 (which is the same rotation as zero). So, what I usually do in similar cases is “normalize” the angle between -180 and 180. Here’s a piece of code that does that (of course, “angle” is my own variable, and not the real rotation of the transform):

if (angle > 180)
    angle -= 360;
if (angle <= -180)
    angle += 360;

By the way, the second “if” statement shouldn’t be necessary, but it’s there just in case :slight_smile:
Now, what you could do in your case is do this and then use the clamp function. Here’s how it could look:

var minAngle = -45;
var maxAngle = 45;

    //the rotation restriction
var angle = transform.localEulerAngles.y;
if (angle > 180)
    angle -= 360;
if (angle <= -180)
    angle += 360;
transform.localEulerAngles.y = Mathf.Clamp(angle, minAngle, maxAngle);

This should do the trick.

Hey Domino,

Big update and finally some progress.

Walking, jumping and aiming are all in. Shooting will come once the assets are completed. I’ve still got some polish to do on the relationship between aiming and moving.

Currently when the player holds the aim key, the animations and character movement will be disabled, unless the character is in mid air. access to animations and character movement will return once the player releases the aim key.

There is one problem though, The bones are no longer moving while aiming. After I attached the player movement script to the character prefab the bones for the waist upwards are struggling to move. They appear to be locked into place due to the animations or player movement script.

Is there any code that will easily allow free, independent movement for the character’s bones, when mouse (1) is held down?

I’ll try and get a build up here tonight to show exactly what I mean. but basically I want to be able to trigger free movement for the bones, like they were before movement or animations were assigned to them, when pressing the aim key.

why not use function LateUpdate and set values every frame?

Edit:
Bones are “reset” to their animation position every frame. So you have to change/override that every frame in Late Update.

I already have my aiming script set to a late update (see below). Can I/is there any way to have the isEnabled trigger work of LateUpdate? Would this be a possible fix?

function LateUpdate () { 
//variables for isEndabled are created here la la la...
//this triggers the aiming when mouse (1) is held down
if (isEnabled) 

    // Limit between 90 and 180 degrees 
     //transform.localEulerAngles.y = Mathf.Clamp(transform.localEulerAngles.y, minAngle, maxAngle); 
	var angle = transform.localEulerAngles.y; 
if (angle > 180) 
    angle -= 360; 
if (angle <= -180) 
    angle += 360; 
transform.localEulerAngles.y = Mathf.Clamp(angle, minAngle, maxAngle);
}

EDIT:
What I need is something that enables free bone movement shortly after the crouch animation have played. So that the player remains in the crouch position but can rotate the bones freely. Animations are triggered from a seperate script to the aiming.