Converting script from JS to C#

This snippet of code is from the BootCamp demo’s SoldierController.js Update() where it tries to turn the character based on camera movement changes.


soldierTransform.localRotation.eulerAngles.y = Mathf.MoveTowards(currentAngle, currentAngle + delta, Time.deltaTime * maxRotationSpeed);

C#: (FAIL)

soldierTransform.localRotation.eulerAngles += new Vector3(0.0f,Mathf.MoveTowards(currentAngle, currentAngle + delta, Time.deltaTime * maxRotationSpeed),0.0f);

Error: Cannot modify the return value of ‘UnityEngine.Transform.localRotation’ because it is not a variable.

So I understand this thread:…-because-it-is-not-a-variable-in-C

But this is sort of a nested example – modifying a Quaternion.Vector3 – and I don’t know how to get around this one. Anyone able to explain? Better yet, what would be a clean readable way to do this operation?

Well, who wrote that script has a really bad style of programming. I’m actually even wondering that it works in UnityScript.

What you want are the localEulerAngles.

soldierTransform.localEulerAngles = soldierTransform.localEulerAngles + new Vector3(0.0f, Mathf.MoveTowards(currentAngle, currentAngle + delta, Time.deltaTime * maxRotationSpeed), 0.0f);

To understand what was wrong here’s a quick explanation:

Whenever you put a dot behind a class or struct you read / access the reference.

So just the very first part of your line


will read / use what ever is before the dot in this case the reference stored in soldierTransform.

localRotation is a property of the Transform class. A property is just syntactical sugar for a get and set method. So when ever you read a property the getter is called. When you assign something to the property the setter is called. The big problem are properties of value types like structs (Vector3, Quaternion). If the getter is called it will return a copy of the object. It can’t return a reference since it’s a value type.

Now take a look at the next part of your line:


Again because of the dot behind localRotation you will read localRotation. Since it’s a property you will access a copy of the Quaternion. What ever you do with the eulerAngles property of the quaternion (read or write) will be done on the copy.

soldierTransform.localRotation.eulerAngles += XXX

The += operator is the same as

soldierTransform.localRotation.eulerAngles = soldierTransform.localRotation.eulerAngles + XXX;

So in the end you assign a new value to the eulerAngles property of a temporary copy of the localRotation Quaternion. This will effectively do nothing because the temporary copy will be lost after that operation. You never invoked the setter of localRotation to assign a new Quaternion.

In UnityScript the compiler automatically creates temp variables, read the current value, store them locally, do the changes and assign them back to the property .

soldierTransform.localRotation.eulerAngles += new Vector3(0.0f,Mathf.MoveTowards(currentAngle, currentAngle + delta, Time.deltaTime * maxRotationSpeed),0.0f);

Here’s a nice example i’ve just tried. This line of UnityScript:

    // UnityScript
    transform.localRotation.eulerAngles.y = 5.0;

will translate to this actual code:

    // C#
    float y = 5f;
    Quaternion localRotation = this.transform.localRotation;
    Vector3 eulerAngles = localRotation.eulerAngles;
    float num = eulerAngles.y = y;
    Vector3 vector = localRotation.eulerAngles = eulerAngles;
    Quaternion quaternion = this.transform.localRotation = localRotation;

I just put this line of UnityScript into a project, built an standalone player and opened the Assembly-UnityScript.dll with ILSpy and let it decompile to C#

From that post you linked to, I would guess:

soldierTransform.localRotation.eulerAngles = soldierTransform.localRotation.eulerAngles + new Vector3(0.0f,Mathf.MoveTowards(currentAngle, currentAngle + delta, Time.deltaTime * maxRotationSpeed),0.0f);

soldierTransform.localRotation.eulerAngles = new Vector3(soldierTransform.localRotation.eulerAngles.x,Mathf.MoveTowards(currentAngle, currentAngle + delta, Time.deltaTime * maxRotationSpeed),soldierTransform.localRotation.eulerAngles.z);

This will definitely work…