It is time to translate our script to C#. Create a new C# script the same way you created the JS one. Call it PushPuckAction
. Open the script and find the line starting with the public class
keywords. Make sure that the name of the class is the same as the name of the script, then press command + S (Ctrl + S in Windows) to save your changes if you made any.
As you can see, the default template for a C# script looks different from that of JS. This is because more things are shown to you. For instance, in JS it is implied that everything inside a script is, in fact, in a class with the same name, but you do not see the class declaration anywhere. Component classes in Unity have to inherit from MonoBehaviour, and it is shown in C#, while JS hides it from you. Then you have the two lines with the using
keyword. All JS scripts use these namespaces, but JS hides it from you as well. The following script is the same one that we had before, but this time translated to C#. Replace the template with it.
using UnityEngine; using System.Collections; [RequireComponent (typeof (CharacterController))] public class PushPuckAction : MonoBehaviour { // Global variables available from Inspector public float pushMag = 20f; public string collisionTag = string.Empty; // Function that will detect the collision with // controller and apply force in point of the collision void OnControllerColliderHit (ControllerColliderHit hit) { if (hit.gameObject.tag == collisionTag) { // Get the position of the object we collided with Vector3 hitObjectPos = hit.transform.position; // Get the position of the collision Vector3 hitPointPos = hit.point; // Calculate the direction of the force, // multiply it by magnitude Vector3 pushForce = Vector3.Normalize(hitObjectPos - hitPointPos) * pushMag; // Finally, apply force in position hit.rigidbody.AddForceAtPosition(pushForce, hitPointPos); // Print a message in Console saying that //the collision did happen and force was indeed applied Debug.Log("Detected hit with " + collisionTag + ", applying force of " + pushForce + " in " + hitPointPos + "."); } } }
Press command + S (Ctrl + S in Windows), and let us look at what has changed apart from the things already mentioned. The syntax of the component requirement is different in C#. On top of that, the RequireComponent
attribute has to be placed before the class declaration.
The #pragma strict
directive is gone. C# is explicit by nature and requires that you specify types of everything, so it is not needed.
In C#, the following are quite useful: #region [Name]
/#endregion
, which is a good way of dividing your code into regions that you can fold. For example, writing #region Variables
will create a region called Variables
. Then you will be able to press a minus in a rectangle on the left of the MonoDevelop window to fold the code region, the end of which you have to mark with #endregion
.
The function
keyword is not used. Instead, function declarations are preceded by return types. You can specify a return type in JS as well, but this is done with the :
operator after the brackets, for example, function Update() : void
.
Finally, all variable declarations are preceded with types of variables instead of the var
keyword. There are more differences in syntax that we cannot see in this example because of the relative simplicity of our script, but these are the main ones.
Now, if you replace your JS script with the C# one on the Mallets, they will act exactly the same way as before. Try doing this, then remove the Push Puck Action component from the mallets. It is time to modify the script and transform PushPuckAction.cs
into an actual Playmaker action. Replace the contents of PushPuckAction.cs
with the following code:
using UnityEngine; using System.Collections; namespace HutongGames.PlayMaker.Actions { [ActionCategory(ActionCategory.Character)] [Tooltip("Detect collision with CharacterController, then push the other object into the opposite direction.")] public class PushPuckAction : FsmStateAction { [Tooltip("Push magnitude")] public FsmFloat pushMag; [Tooltip("Object with this tag will be pushed")] public FsmString collisionTag; public override void Reset () { pushMag = 20f; collisionTag = string.Empty; } public override void DoControllerColliderHit(ControllerColliderHit hit) { if (hit.gameObject.tag == collisionTag.Value) { FsmVector3 hitObjectPos = hit.transform.position; FsmVector3 hitPointPos = hit.point; FsmVector3 pushForce = (hitObjectPos.Value - hitPointPos.Value).normalized * pushMag.Value; hit.rigidbody.AddForceAtPosition(pushForce.Value, hitPointPos.Value); Debug.Log("Detected hit with " + collisionTag.Value + ", applying force of " + pushForce.Value + " in " + hitPointPos.Value + "."); } } } }
Press command + S (Ctrl + S in Windows) to save the script. As you can see, this time more things have changed, although you can still see the same structure. Let us go through the code line-by-line and examine it.
The using
directives are the same, but the difference begins right after them. The line namespace
HutongGames.PlayMaker.Actions
is obligatory for all Playmaker actions. Without it Playmaker will not know that the script that you are writing is, in fact, an action.
The [ActionCategory(ActionCategory.Character)]
line puts your new action into a category. In this case, we are putting it into the Character
category, because the action is about things colliding with the Character Controller. It could also go into the Physics
category. To move it there, the line would have to be [ActionCategory(ActionCategory.Physics)]
.
After that there is [Tooltip("...")]
, which is quite self-explanatory. It shows a short description when you select the action from the list in the Actions panel.
The PushPuckAction
class now inherits from FsmStateAction
instead of MonoBehavior
. You still have access to all the standard Unity classes, but Playmaker-specific ones are added now.
Then there is another Tooltip
, this time for a variable rather that a whole action. The text within this tooltip will appear when you hover your mouse pointer over the variable in the State tab of the playMaker panel or in the Actions panel.
Note that the type of the pushMag
variable has changed from float
to FsmFloat
, and the same goes for the string
variable collisionTag
—it is FsmString
now. These are Playmaker types of variables. The same operations can be performed on them as before, but to access their value you now have to use the dot operator with the word Value
, so, for example, pushMag.Value
will return a float, the value of the Playmaker variable.
A Reset
function was added. This is what happens when a new action is added to a state or when you right-click on the header of the action and press Reset. In it, we reinitialize the variables.
Then there is the DoControllerColliderHit
function. Its name has changed from the standard Unity OnControllerColliderHit
. Inside the function everything has stayed more or less the same with the only difference that types of Vector3
variables have changed to FsmVector3
, so to access their values .Value
is used. Also, instead of Vector3.Normalize
, we used .nomalized
, which does exactly the same thing.
While it is clear that in order to find examples and standard Unity classes' API one has to go to Unity Script Reference, it may be less clear about Playmaker-specific things. The easiest way to find examples is opening the script files of existing Playmaker actions that are located under the PlayMaker/Actions
path, in your project. For example, if you are not quite sure how to detect mouse input and you want to do it via a Playmaker custom action, you can open PlayMaker/Actions/MousePick.cs
and look at how the creators of Playmaker solved this problem.
Note that both Reset
and DoControllerColliderHit
have override
preceding their types. This means that we are replacing a base function defined in the Playmaker with our own function. As a general rule, you will need to override all of the Playmaker standard functions using this keyword.
Now that we are done writing a custom Playmaker action, we can try using it. Remove the Push Puck and/or Push Puck Action components from MalletLeft and MalletRight. In their Move state, remove the Collision Event action. Then locate the newly created Push Puck Action in the Actions panel and add it to the state. Set the properties as shown in the following screenshot. Finally, delete the Push Puck state from the FSM as well as the Push event and transition. To delete a transition, you just need to right-click on the event in the FSM view and press Delete Transition.
18.221.58.143