わたくしのブログではUnityのJavaScriptでスクリプトを記述していますが、UnityではC#でスクリプトを記述する人が80%を超えているとか?
(現在はUnityで使えるスクリプトはC#のみでこのブログでもC#スクリプトで書いています)
そういうわけで当ブログを参照して頂く時にJavaScriptをC#に書き換える為の方法を見ていきたいと思います。
Unityで記述するJavaScript自体が通常のJavaScriptの記述とは違いますので、そこでも多少混乱はあるかもしれません。
わたくし自身はC#を勉強した事がないのでC#で記述する際の細かい部分は省略させて頂きます。
またこの記事で取り上げていない細かい違いはいくつもありますがそういった所は都度調べるという感じで対応も出来ると思います。
今回取り上げるのは頻出するものに限らせて頂きます。
スクリプトの作成方法
まずスクリプトの作成方法から見ていきます。
JavaScriptの場合はスクリプト名を自由に付けられますが、C#の場合はスクリプト名とクラス名を同じにしなくてはいけません(作成時に同名になるはず)
例えばMoveというスクリプトを作成した場合C#では
1 2 3 4 | public class Move : MonoBehaviour { } |
というようなクラス名が作成されます。
クラスはデフォルト(何も設定されていない時かわりに設定される)でMonoBehaviourクラスを継承した形で作成されます。
JavaScriptでクラスの形式で記述する時はMonoBehaviourクラスを継承するように作成します。
Start関数やUpdate関数を使わず普通のクラスとして使いたい場合は何も継承しないかobjectクラスを継承するようにします。
JavaScriptで記述する場合クラスの形式で記述しない場合がほとんどになりますが、普段からクラス形式で記述するとC#との読み替えが容易になります。
スクリプトを作成すると
JavaScriptではStartとUpdate関数がデフォルトで作成されます。
C#ではusingディレクティブとクラス、クラスの中にStartメソッドとUpdateメソッドが作成されます。
usingディレクティブはC#で宣言されているクラスを使用する時にパッケージ名を省略する為のものでしょうか?
C#だとパッケージじゃなく名前空間という名前なのかも・・・・謎
例えばC#でUIのTextを宣言すると自動で
using UnityEngine.UI;
というディレクティブが追加されます。
UIのTextはUnityEngine.UIパッケージの中のクラスなので本来はパッケージ名からすべて記述する必要があります。
ディレクティブがあるとパッケージ名の省略が出来るってわけですね。
つまり、
1 2 3 4 5 6 | public class Move : Monobehaviour { private Text text; // ディレクティブあり private UnityEngine.UI.Text text; // ディレクティブなし } |
というように変数の宣言時の違いが出てきます。
ディレクティブが他に何かあるのかはわかりませんが、読み替える時に知っておくと便利ですね。
JavaScriptでUIのTextを使用する場合は
1 2 3 | private var text : UI.Text; |
等と記述する必要があります。
UnityEngine以下のパッケージ名から記述する必要があります。
C#と同じようにTextと扱いたい場合はusingの代わりにimportを使ってUnityEngine.UIをインポートする事でTextで使用出来ます。
これについては
でも記述しています。
フィールド変数やローカル変数の宣言方法の違い
次にフィールド変数やローカル変数の宣言方法とJavaScriptの関数、C#のメソッドを見ていきます。
C#では
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | using UnityEngine; using System.Collections; public class Move : Monobehaviour { private Vector3 point; private float speed; private flag bool; private CharacterController cCon; void Start() { cCon.GetComponent<CharacterController>(); point = new Vector3(0, 1, 0); speed = 1.0f; flag = false; float power = 0; } int Sum() { return 1; } } |
となります。
フィールドの宣言は
アクセス修飾子 型 変数名;
という順序になります。
そのメソッド内で使用する変数は
アクセス修飾子 型 変数名;
となります。
C#のフィールドや変数の宣言で型があらかじめわかっている場合は
var position = new Vector3(0, 0, 1);
等と型の部分をvarで記述する事が出来、コンパイルの時に型変換を自動でしてくれます。
右辺の型が決まっていない場合はvarは使えません。
コンポーネントの取得では
GetComponent<取得するコンポーネント>();
となります。
クラスを使用する時はnewを取りつけています。
またクラス内ではそのクラスで使用するメソッドの宣言がされており戻り値がない場合はvoidを付けて宣言します。
Sumはint型の変数を返すメソッドという事になりますね。
このようにクラスの中にフィールドとメソッドを内包している形になります。
次に同じものをJavaScriptで記述してみましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | private var point : Vector3; private var speed : float; private var bool : flag; private var cCon : CharacterController; function Start() { cCon = GetComponent.<CharacterController>(); point = Vector3(0, 1, 0); speed = 1.0f; flag = false; var power = 0; } function Sum() : int { return 1; } |
フィールド宣言では
アクセス修飾子 var 変数名 : 型;
という記述の仕方になります。
JavaScriptの場合はvarが必要になります。
また元々JavaScriptは動的に型の変換を行うスクリプトなので、型名の記述はしなくても動作します。
が、型をしっかり指定して宣言した方が間違いがないです。
コンポーネントの取得は
GetComponent.<取得するコンポーネント>();
となります。
C#と違い<>の前に.(ドット)が必要になります。
JavaScriptの場合
GetComponent(取得するコンポーネント);
という記述でも取得出来ますが、最初に紹介した方法だと型をしっかり指定して取得するらしいのでそちらの方がいいかも。
途中point変数にVector3の値を代入していますが、C#と違いnewを付けなくてもコンパイルが通ります。
これもnewは付けた方がいいかもしれませんね。
関数内でローカル変数を使用する時は
var 変数名 (: 型);
と記述します。
フィールドの時と同じようにここでも型を宣言した方がいいのかも。
今回のスクリプトでは言及してませんが、文字列型を宣言する時、
JavaScriptはString
C#はstring
と最初が大文字と小文字で違います。
論理型は
JavaScriptはboolean
C#はbool
と表記が変わります。
こういう感じで多少違いますが、読み替えるのにそれほど不便になるものでもないと思います。
他にもC#ではListとか使えるみたいですが、JavaScriptだと使えないといった事はありますが・・・。
JavaScriptでもListを使う事が出来ました。
1 2 3 4 5 6 7 8 9 | import System.Collections.Generic; public var list : List.<GameObject>; // サーチした敵を入れる for(var value in list) { Debug.Log(value); } |
まずはimportで明示的にSystem.Collections.Genericをインポートします。
Listのジェネリクス版で型指定の前に.を付けます。
要素数分繰り返すforeachの代わりにforを使います。
for文や関数・メソッドの使い方の違い
for文の変数宣言部でも同じような変数の仕方が必要になるので注意してください。
1 2 3 4 5 6 7 8 9 10 11 | // C# for (int i = 0; i < 10; i++) { Debug.Log ("Test"); } // JavaScript for (var i : int = 0; i < 10; i++) { Debug.Log ("Test"); } |
JavaScriptはちょっとややこしい感じになりますね。
Listや配列などの繰り返し処理をする場合は
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | // JavaScript var nums : int[] = [1, 2, 3]; for(var num : int in nums) { Debug.Log(num); } // C# int[] nums = {1, 2, 3}; foreach(int num in nums) { Debug.Log(num); } |
配列の初期化も[]と{}で違いますが、C#の場合はforではなくforeachを使います。
関数とメソッドの引数の受け取り方も変わります。
1 2 3 4 5 6 7 8 9 10 11 | // C# int Sum(int a, int b) { return a + b; } // JavaScript function Sum(a : int, b : int) : int { return a + b; } |
C#では
型 引数名
となり、
JavaScriptでは引数名でvarが必要ありませんが、
引数名 : 型
となります。
位置や角度の変更
位置や角度を変更する時にも違いがあります。
JavaScriptの場合は
1 2 3 4 5 6 7 8 9 10 11 | function Start() { transform.position.x = 1f; transform.position.y = 2f; transform.position.z = 3f; transform.eulerAngles.x = 10f; transform.eulerAngles.y = 20f; transform.eulerAngles.z = 30f; } |
と個別に値を変更することが可能ですが、C#の場合は
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | using UnityEngine; using System.Collections; public class PositionTest : MonoBehaviour { // Use this for initialization void Start () { transform.position = new Vector3 (1f, 2f, 3f); transform.rotation = Quaternion.Euler (10f, 20f, 30f); } } |
と一括で変更する必要があります。
X軸だけを変更したい場合は
transform.position = new Vector3(1f, transform.position.y, transform.position.z);
等とY軸、Z軸には元の値を入れるといいと思います。
具体的にキャラクターの移動スクリプトで違いを見ていく
最後にJavaScriptで記述したキャラクターの移動とジャンプの機能をC#で書くとどうなるかを見てみましょう。
JavaScriptで移動スクリプトを記述
まずはJavaScriptで記述した場合です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | private var animator : Animator; private var cCon : CharacterController; private var x : float; private var y : float; private var velocity : Vector3; public var jumpPower : float; public var charaRay : Transform; // レイを飛ばす体の位置 public var charaRayRange : float; // レイの距離 private var ground : boolean; // レイが地面に到達しているかどうか private var input : Vector3; // 入力値 function Start () { animator = GetComponent.<Animator>(); cCon = GetComponent.<CharacterController>(); velocity = Vector3.zero; ground = false; } function Update () { // キャラクターが接地していない時はレイを飛ばして確認 if(!cCon.isGrounded) { if(Physics.Linecast(charaRay.position, (charaRay.position - transform.up * charaRayRange))) { ground = true; } else { ground = false; } Debug.DrawLine(charaRay.position, (charaRay.position - transform.up * charaRayRange), Color.red); // Debug.Log(ground); } // キャラクターコライダが接地、またはレイが地面に到達している場合 if(cCon.isGrounded || ground) { // 地面に接地してる時は初期化 if(cCon.isGrounded) { velocity = Vector3.zero; // 着地していたらアニメーションパラメータと2段階ジャンプフラグをfalse animator.SetBool("Jump", false); // レイを飛ばして接地確認の場合は重力だけは働かせておく、前後左右は初期化 } else { velocity = Vector3(0, velocity.y, 0); } x = Input.GetAxis("Horizontal"); y = Input.GetAxis("Vertical"); input = Vector3(x, 0, y); // 方向キーが多少押されている if(input.magnitude > 0.1f) { animator.SetFloat("Speed", input.magnitude); transform.LookAt(transform.position + input); velocity += input.normalized * 2; // キーの押しが小さすぎる場合は移動しない } else { animator.SetFloat("Speed", 0); } // ジャンプ if(Input.GetButtonDown("Jump") && !animator.GetCurrentAnimatorStateInfo(0).IsName("Jump") && !animator.IsInTransition(0) // 遷移途中にジャンプさせない条件 ) { Debug.Log("ジャンプ"); animator.SetBool("Jump", true); velocity.y += jumpPower; } } velocity.y += Physics.gravity.y * Time.deltaTime; cCon.Move(velocity * Time.deltaTime); } |
C#で移動スクリプトを記述
これをC#で書き換えた物が↓になります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 | using UnityEngine; using System.Collections; public class CSharpMove : MonoBehaviour { private Animator animator; private CharacterController cCon; private float x; private float y; private Vector3 velocity; public float jumpPower; public Transform charaRay; // レイを飛ばす体の位置 public float charaRayRange; // レイの距離 private bool ground; // レイが地面に到達しているかどうか private Vector3 input; // 入力値 void Start () { animator = GetComponent<Animator>(); cCon = GetComponent<CharacterController>(); velocity = Vector3.zero; ground = false; } void Update () { // キャラクターが接地していない時はレイを飛ばして確認 if(!cCon.isGrounded) { if(Physics.Linecast(charaRay.position, (charaRay.position - transform.up * charaRayRange))) { ground = true; } else { ground = false; } Debug.DrawLine(charaRay.position, (charaRay.position - transform.up * charaRayRange), Color.red); // Debug.Log(ground); } // キャラクターコライダが接地、またはレイが地面に到達している場合 if(cCon.isGrounded || ground) { // 地面に接地してる時は初期化 if(cCon.isGrounded) { velocity = Vector3.zero; // 着地していたらアニメーションパラメータと2段階ジャンプフラグをfalse animator.SetBool("Jump", false); // レイを飛ばして接地確認の場合は重力だけは働かせておく、前後左右は初期化 } else { velocity = new Vector3(0, velocity.y, 0); } x = Input.GetAxis("Horizontal"); y = Input.GetAxis("Vertical"); input = new Vector3(x, 0, y); // 方向キーが多少押されている if(input.magnitude > 0.1f) { animator.SetFloat("Speed", input.magnitude); transform.LookAt(transform.position + input); velocity += input.normalized * 2; // キーの押しが小さすぎる場合は移動しない } else { animator.SetFloat("Speed", 0); } // ジャンプ if(Input.GetButtonDown("Jump") && !animator.GetCurrentAnimatorStateInfo(0).IsName("Jump") && !animator.IsInTransition(0) // 遷移途中にジャンプさせない条件 ) { Debug.Log("ジャンプ"); animator.SetBool("Jump", true); velocity.y += jumpPower; } } velocity.y += Physics.gravity.y * Time.deltaTime; cCon.Move(velocity * Time.deltaTime); } } |
多少違いはありますが、双方の読み替えが出来るようになったのではないでしょうか?
C#とJavaScriptの違いを見てきて感じた事
正直な所C#で記述した方がいい事が多いかもしれません・・・・(^_^;)
なによりMonoDevelopでC#プログラムを書く時に変換候補が出る(インテリジェンス機能?)のが楽です。
打ち間違えがなくなる事や、あのクラスのつづりなんだったかな?という時に候補が出てくるんですから。
プログラムを書くスピードも速くなりますしね!
またAnimatorControllerのBehaviourスクリプトは現状C#でしか記述出来ないみたいなので、Behaviourスクリプトから他のC#スクリプトを取得する時は
通常のスクリプトと同様に出来ますが、JavaScriptで書いたスクリプトを取得する場合は特別なフォルダにスクリプトを配置し取得するという
面倒くさい事をしなければならなくなるという事でしょうか。
Behaviourスクリプトから他のJavaScriptで書いたスクリプトを取得しなければ問題は発生しませんが、使う必要が出た時に大変です。
逆にJavaScriptで書く利点は何かと言えば・・・・全部自分でしっかりとプログラムを書く必要があるので関数名等を覚えるという事と、
JavaScriptは取りかかりやすいという点はあるかもしれませんね。
テキストエディタとブラウザがあればJavaScriptの処理を確認出来ますから。
C#でプログラミングを行おうとしたら何かしらインストールしなければいけませんしね。
JavaScriptはウェブサイトを作る時にも使うので自身でウェブサイトを作った事がある人は少なからず使ってるしそういう意味でもとっかかりやすいかも?
C#の方が便利なのになんでわたくしはJavaScriptで記述しているんでしょうね・・・、最初にJavaScriptを選択してしまったからかもしれませんが・・・(^_^;)
いずれC#に切り替えてブログタイトルの(javascript)を外しちゃおうかな・・・・(-.-)
2017年04月19日にタイトル名を『Unityを使った3Dゲームの作り方(かめくめ)』に変更しました。