voidを使えば、アロー関数 で{}が省ける。

こんにちはー。

今、void を使えばアロー関数で{}がいらないんじゃないか説が、
ふと頭のなかを通り過ぎて行きましたので紹介いたしますー。

「void 演算子で、アロー関数の {} がいらない気がする」ということです。

普通に {} はずせばいいのでは?

まず、通常のアロー関数の{}なしの使い方です。
普通の関数がどんどん短くなっていく過程をご覧ください。

// function 式
var a = function (a) { return a + 2; };

// アロー関数にそのまましてみた
var a = (a) => { return a + 2; };

// 引数一つだから () いらない。
var a = a => { return a + 2; };

// 中身 1行で return で返されているから {} いらない。
var a = a => a + 2;

こんな感じですかね。
では、次のような例だとどうでしょう。

var num = 0;

// function 式
var b = function () { num++; }

// アロー関数にそのまましてみる
var b = () => { num++; }

// () はずしたいけどはずせない。
// {} はずしたいけど、戻り値ないから・・

そうです。戻り値が void のとき(あ、言っちゃった☆)です。
もし、このまま {} をはずせば、戻り値として num が帰ってきてしまい、
意図した結果にはなりません。
 
 
 
 
 
さあ、どうしても {} が外したい・・(←なんで?)
どうしよう・・・
 
 
 
 
 
そんなときは、void の出番です。
void を使えば、{} を外すことができます。

// void があらわれた!!
var b = () => void num++;

それはなぜだ?

void ってなに?

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/void

void は、式を実行するけれど、undefined を返すという演算子なのです。
知っていたけれど、使い方はあまり良くわかっていませんでした。
今まで知っていた使い方は、こんな感じです。

if (a === void(0)) {} // undefined か判定する
if (a === void 0) {} // こちらでも良い
// でもこれは・・
if (typeof a === "undefined") {} // これで良かった。

// + 下の「ちなみに」(SyntaxErrorだな・・)

また、<a href="javascript:void(0)">hoge</a> として、
リンクは反応するけれどどこにも行かない、みたいな使い方もできるそうです。

まとめ

実用性があるかどうかはわかりませんが、
ちょっとした発見だったので、投稿しました。

まあ、void で 4 文字、{} で 2 文字で、ちょっと負けていますが、
使いたい人がいればどうぞ!!

ちなみに

void を知ったきっかけについて

最初 js 書いていた時、
C# の癖で、function のことずっと void って書いていたからです。

なぜこんなに {} が外したいのか

最近、できるだけ短くコードを書くのにハマっているからです。
「行末に ; つけると、1行で書けるよー」・・・はい。

UnityのSceneManagerで、シーンが存在するか確認する

Unity 5.3 から追加された、Multi Scene Editingで、
シーンを追加する時、
同じシーンを何個も追加したくない!ってこと、ありますよね。

「SceneManager.Contains()」的なメソッドがあれば最高なのですが、
アクティブなシーンしか
取れなかったりとかするんですよねー(GetActiveScene 関数)。

そこで、次のようにしました。
しかし、Unity 5.3.1 くらいではOKだったのですが、
5.3.2にしたら非推奨になっちゃってw・・

using UnityEngine.SceneManagement;
using System.Linq;

void LoadMyScene()
{
    // MyScene1 というシーンが存在しなければ
    if (SceneManager.GetAllScenes().Count((scene) => scene.name == "MyScene1") == 0)
    {
        SceneManager.LoadScene("MyScene1", LoadSceneMode.Additive);
    }
}

Linqで、すべてのシーンの中から、指定された名前のシーンが 1 以上の場合は存在するとしました。
しかし、すべてのシーンを取得する、「GetAllScenes 関数」が非推奨になってしまいました。
1行で書けるのがよかったのでしたが、しょうがない。for文をつかって地道にやろう。

using UnityEngine.SceneManagement;

void LoadMyScene()
{
    if (!ContainsScene("MyScene1"))
    {
        SceneManager.LoadScene("MyScene1", LoadSceneMode.Additive);
    }
}

bool ContainsScene(string sceneName)
{
    for (int i = 0; i < SceneManager.sceneCount; i++)
    {
        if (SceneManager.GetSceneAt(i).name == sceneName)
        {
            return true;
        }
    }
    return false;
}

「using UnityEngine.SceneManagement」を追加するのを忘れずに。
これでハマったこともあります。

もっといい方法ないんですかねー・・。

【感動】UnityでC# 6.0 が使えた!!

追記:朗報
Unity 2017 からは、デフォルトで C# 6.0 および .NET 4.6 が使用可能なようです!

C#6.0時代のUnity - Qiita
## はじめにUnity2017のベータが公開されましたね!(これを書いている4月13日現在 Unity 2017.1.0b1 https://unity3d.com/jp/unity/beta#downloads)なんといっても...

この記事では Unity 5 での場合を紹介しています。

こんにちはー!!

Unityでも、最新の C# 6.0 を使って、
async、await とかカッコよくコードを書きたいんだ ლ(´ڡ`ლ)

そんなことできないかなーと思って、調べてみました。

そしたら、なんと、ありました!!ので紹介します。

1. リポジトリをダウンロード

C# 6.0関係のいろいろが入ったリポジトリをBitbucketからダウンロードします。

https://bitbucket.org/alexzzzz/unity-c-5.0-and-6.0-integration/downloads

ダウンロードしたファイルは、Zip形式で圧縮されているので、展開します。

2. プロジェクトにインポート

Unityのプロジェクトのフォルダーを開き、
「CSharp60Support」をコピーします。

unity-cs6-copy

CSharp60Support内の、「CSharp60Support for Unity 5.unitypackage」
(Unity 4だったらUnity 4の方)を開き、
プロジェクトにインポートします。
import-cs6-unity

unity-cs6-many-errs
大量にエラーが出ますが、気にせず、
「CSharp60Support」ディレクトリを右クリックし、「Reimport All」をクリック。
unity-cs6-reimport
ダイアログが出てくるので、「Reimport」を選択すると、Unityが再起動し、エラーがすべて消えます。

3. 試す

新しいスクリプトを適当に作って、C# 6.0を試してみましょう。
試す機能は、String interpolationです。


// 略

void Start()
{
    string s1 = "world";
    string s2 = $"Hello, {s1}";
    Debug.Log(s2);
}

// 略

新しく空のゲームオブジェクト(Hierarchy右クリック「Create Empty」とか)を作成し、
作ったスクリプトをアタッチしましょう。

実行すると・・・・!
unity-cs6-success

できました~~!!

4. Visual Studio、MonoBehaviourでエラーが出るときは

Unityではエラーが出なくても、
「◯◯という機能はC# 6.0で使えて、C# 4では使えないよー」的なエラーが、
Visual Studio、MonoBehaviour側で出るときは、IDEを再起動しましょう。

まとめ

これでのびのびとUnityが使える!

自分的には、C# 6.0で一番便利な機能は、↑の、$"{変数}"で、string.Formatっぽいのが使える機能です(名前忘れた -> 追記: String interpolation らしい)
これが使えるのは本当に嬉しい!

(え、async 、await 使ってみないの)→試してみます!

UnityのLoadSceneAsyncでの非同期処理

こんにちはー!!

Unity 5.3 から、Multi Scene Editing が追加されましたね。

そこで、シーンを読み込んでから、GameObject.Find をする、
次のようなコードを動かしたのですが、うまくいきません。

TestGameObject は、追加するシーン Scene2 に入っているものとします。

void LoadScene2()
{
    SceneManager.LoadScene("Scene2", LoadSceneMode.Additive);
    Debug.Log(GameObject.Find("TestGameObject"));
}

実行してみると、Null が返ってきます。

unity-loadsceneasync-ok

なぜ?

これは、Scene2 が読み込まれるまで待たないで、
GameObject.Find を呼び出してしまっているからです!

LoadSceneAsync を使おう!

お、LoadSceneAsync とかいう、それらしい名前のがあるではないか。
そこで、LoadSceneAsync を使おうとしましたが、ハマりました。
C# の、async / await でするものだと間違えていて、
あれ?Unityの C# のバージョンじゃ、async とか使えなくねー・・

(使う方法もあります)

【感動】UnityでC# 6.0 が使えた!!
追記:朗報Unity 2017 からは、デフォルトで C# 6.0 および .NET 4.6 が使用可能なようです!この記事では Unity 5 での場合を紹介しています。こんにちはー!!Unityで...

しかし、LoadSceneAsync の戻り値は、「UnityEngine.AsyncOperation」でした・・。
アシンクオペレーションってなんだっけ?(検索中)
そう、コルーチンを使うんだ。(`・ω・´)ゞ

void LoadScene2()
{
    StartCoroutine("LoadScene2Coroutine");
}
IEnumerator LoadScene2Coroutine()
{
    yield return SceneManager.LoadSceneAsync("Scene2", LoadSceneMode.Additive);
    Debug.Log(GameObject.Find("TestGameObject"));
}

unity-loadscene-null

これでシーンが読み込まれるまで待機してくれるようになりました。

Unityで、コンポーネント内のフィールドを文字列から動的に変更する

こんにちはー!!

Json からUnity2dのステージのデータを取ろうとしています。
その時、コンポーネントの値を変えたいと思い、この方法をやりました。
Reflectionを使ったりして、結構複雑でした。

ReflectionTest コンポーネントの内容は次のようにします。

これを、GameObject1 にアタッチすればできます。

安全性は・・

しかし、コンポーネント内すべてのフィールドにアクセスできるのは
ちょっとセキュリティが・・・なので、
特定の属性(ここではPermitReflection)がついたフィールドのみにアクセスできるようにします。

Reflectionに使ったしたコード(2つ上のコード)を、次のように編集します。

    Type componentType = Type.GetType(componentType);
    Attribute fieldAttribute = Attribute.GetCustomAttribute(componentType.GetField(fieldName), typeof(PermitReflectionAttribute));
    if (fieldAttribute != null)  // 属性が存在する場合のみ
    {
        Component targetComponent = GameObject.Find(gameObjectName).GetComponent(componentName);
        Type fieldType = componentType.InvokeMember(fieldName, BindingFlags.GetField, null, targetComponent, null).GetType();
        object parameter;
        // 汎用性を重視して、型がどんな場合でもstringからParseできるように
        if (fieldType == typeof(string))
        {
            parameter = fieldVal;
        }
        else if (fieldType == typeof(int))
        {
            parameter = int.Parse(fieldVal);
        }
        componentType.InvokeMember(fieldName, BindingFlags.SetField, null, targetComponent, new object[] { parameter });
    }
}

// 属性を追加
[AttributeUsage(AttributeTargets.Field)]
public class PermitReflectionAttribute : Attribute
{
}

ReflectionTest コンポーネントは次のように変更します。

PermitReflectionAttributeですが、属性として指定する場合は、
Attributeを抜いて、PermitReflectionとします。

【Unity】C# + SceneManagerでのMulti Scene Editing

読み込み

using UnityEngine.SceneManagement;

参考:

Unity - Scripting API:
Unity is the ultimate game development platform. Use Unity to build high-quality 3D and 2D games, deploy them across mobile, desktop, VR/AR, consoles or the ...

複数のシーンを読み込んでいる状態で・・

1つのシーンをアンロードする

SceneManager.UnloadScene("シーン名");

シーン名は、Build SettingsのIDでもオーバーロードされる。
ただし、読み込まれているシーン名、シーンIDに限る(あたりまえだけど・・)

1つのシーンをロードする

ここでハマった。

SceneManager.LoadScene("シーン名", LoadSceneMode.Additive);

LoadSceneMode.Additiveを忘れない!

全部のシーンをアンロードして、1つだけ読み込む

SceneManager.LoadScene("シーン名");
// もしくは
SceneManager.LoadScene("シーン名", LoadSceneMode.Single);

普通にやるとこうなる。

Input.GetKeyDownとFixedUpdateは一緒にしないほうがいいっぽい

Unityで2Dのスクロールゲームを作っています。
スペースキーを押したとき、Jumpするように、次のようなコードを書きました。

void FixedUpdate()
{
    if (Input.GetButtonDown("Jump"))
    {
        // 処理
    }
}

しかし、ジャンプするときと、しないときがあり、

Linecastの問題かと思ってググっていたところ、

を見つけました。

サイトによると、FixedUpdateではInput.GetKeyDownはしないほうがいいらしいです。

理由は、FixedUpdateで、Inputの値がとれるタイミングとは限らないかららしいです。


bool IsDownJumpButton = false;
void Update()
{
    if (Input.GetButtonDown("Jump"))
    {
        IsDownJumpButton = true;
    }
}
void FixedUpdate()
{
    if (IsDownJumpButton)
    {
        // 処理
        IsDownJumpButton = false; // チェックしたよ
    }
}

【Unity】Script上でのnew GameObject()の使い方

(Unity5.3.1で確認)

Unityで空のGameObjectを作って、シーンに追加したいとき、

Instantiate(new GameObject());

あれー!!なんか2つ作られてるー・・

そうです。実は上のコード、二重でゲームオブジェクトを作っているのです!

というわけで、解決策。

new GameObject();

Instantiate()と書くことはできないので、new GameObject();とそのまま書いてみます。

これだけじゃ作るだけでコンポーネントのアタッチとか何もできないので、名前を変えてみます。

GameObject g = new GameObject();
g.name = "hello";

はい!できました。

なぜかUnityでtransform.Translateができなかった

なんでかわからないけどできなかった。

前は、Animatorでtransformを変えていたからできなかったれど、

今回は違った。

gameObject.transform.Translate(10, 10, 0);

こんな感じになっていたコードを、

var vec3 = gameObject.transform.position;
vec3.x += 10;
vec3.y += 10;
gameObject.transform.position = vec3;

こうしたらできた。