おれんじりりぃぶろぐ

きっと何者にもなれないエンジニアのブログ

UnityにおけるAwaitAsyncを使った非同期実装について③

これまでTask、AsyncAwaitを使った非同期処理の実装、メインスレッド以外から特定の(メイン)スレッドにアクセスする方法についてみてきました。 今回は、複数の非同期に実行されるTaskをハンドリングする方法について説明します。今回もUnityに特化した情報はありません。

以下のコードがあったとします。

using System.Threading;
using System.Threading.Tasks;
using UnityEngine;

public class AsyncSampleScene : MonoBehaviour {

    private async void Start()
    {
        await TestAsyncMethod1();
        await TestAsyncMethod2();
        await TestAsyncMethod3();
        Debug.Log("All Compleated!");
    }

    private async Task TestAsyncMethod1()
    {
        await Task.Run(() => {
            Debug.Log($"ID {Thread.CurrentThread.ManagedThreadId}, [Async] 1");
            Thread.Sleep(1000); 
        });
    }
    
    private async Task TestAsyncMethod2()
    {
        await Task.Run(() => {
            Debug.Log($"ID {Thread.CurrentThread.ManagedThreadId}, [Async] 2");
            Thread.Sleep(1000); 
        });
    }
    
    private async Task TestAsyncMethod3()
    {
        await Task.Run(() => {
            Debug.Log($"ID {Thread.CurrentThread.ManagedThreadId}, [Async] 3");
            Thread.Sleep(1000); 
        });
    }
}

TestAsyncMethod1()が完了したらTestAsyncMethod2()が実行され、TestAsyncMethod2()が完了したらTestAsyncMethod3()が実行され、 TestAsyncMethod3()が完了したら最後にDebugログが出力されます。

図にすると以下のようになります。 スクリーンショット 2018-04-16 19.33.41.png (77.7 kB)

しかし、TestAsyncMethod1()TestAsyncMethod2()TestAsyncMethod3()に依存関係が特にない場合にはいちいちawait()するのは無駄です。 この場合、このようになってほしいわけです。 スクリーンショット 2018-04-16 19.46.45.png (76.1 kB)

それぞれのTaskをパラレルに実行し、全て完了したらDebugログを出力しています。

このような実装は以下のコードで実現できます。

using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using UnityEngine;

public class AsyncSampleScene : MonoBehaviour {

    private async void Start()
    {
        List<Task> tasks = new List<Task>(); 
        tasks.Add(TestAsyncMethod1());
        tasks.Add(TestAsyncMethod2());
        tasks.Add(TestAsyncMethod3());

        await Task.WhenAll(tasks);
        
        Debug.Log("All Compleated!");
    }

    private async Task TestAsyncMethod1()
    {
        await Task.Run(() => {
            Debug.Log($"ID {Thread.CurrentThread.ManagedThreadId}, [Async] 1");
            Thread.Sleep(1000); 
        });
    }
    
    private async Task TestAsyncMethod2()
    {
        await Task.Run(() => {
            Debug.Log($"ID {Thread.CurrentThread.ManagedThreadId}, [Async] 2");
            Thread.Sleep(1000); 
        });
    }
    
    private async Task TestAsyncMethod3()
    {
        await Task.Run(() => {
            Debug.Log($"ID {Thread.CurrentThread.ManagedThreadId}, [Async] 3");
            Thread.Sleep(1000); 
        });
    }
}

tasksというTaskのコレクションを生成し、各々のtaskをaddしていきます。最後にTask.WhenAll()awaitよって全てのコレクション内のTaskが完了したタイミングを取得することができます。

参考

UnityにおけるAwaitAsyncを使った非同期実装について②

概略

前回は、最新Unityにおけるマルチスレッドプログラミングの方法についての紹介をしました。 今回はUnityでTask、awaitを使ったマルチスレッドを行う時に起きる問題について取り上げます。

Unityにおけるメインスレッド制約

using System.Threading;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.UI;

public class AsyncSampleScene : MonoBehaviour
{

    [SerializeField] private Text text;

    private async void Start()
    {
        Debug.Log($"ID {Thread.CurrentThread.ManagedThreadId}, Start Start()");
        await TestAsyncMethod();
        Debug.Log($"ID {Thread.CurrentThread.ManagedThreadId}, End Start()");
    }

    private async Task TestAsyncMethod()
    {
        await Task.Run(() =>
        {
            Thread.Sleep(1000);
            text.text = "1";  
        });
    }
}

上記のコードを実行すると以下のエラーが出ます。

get_isActiveAndEnabled can only be called from the main thread.
Constructors and field initializers will be executed from the loading thread when loading a scene.
Don't use this function in the constructor or field initializers, instead move initialization code to the Awake or Start function.

これはメインスレッド以外からTextにアクセスを行っているため、メインスレッドで実行してくださいというエラー内容になります。 MonoBehaviourを継承しているクラスの使用についてはメインスレッドからアクセスしなければならないという制約があるためです。 では、この問題にはどのような対処方法が考えられるでしょうか?

対処方法

これに対する対処には以下の方法が考えられます。 まずは、stringを返す方法です。

using System.Threading;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.UI;

public class AsyncSampleScene : MonoBehaviour
{

    [SerializeField] private Text text;

    private async void Start()
    {
        Debug.Log($"ID {Thread.CurrentThread.ManagedThreadId}, Start Start()");
        text.text = await TestAsyncMethod();
        Debug.Log($"ID {Thread.CurrentThread.ManagedThreadId}, End Start()");
    }

    private async Task<string> TestAsyncMethod()
    {
        var text = string.Empty;
        await Task.Run(() =>
        {
            Thread.Sleep(1000);
            text = "1";  
            return text;
        });
        return text;
    }
}

2つ目がコルーチンを使う方法です。 そして、3つ目があるスレッドから特定のスレッド(メインスレッド)にアクセスする方法になります。

あるスレッドからメインスレッドへのアクセス方法

using System.Threading;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.UI;

public class AsyncSampleScene : MonoBehaviour
{

    [SerializeField] private Text text;

    private async void Start()
    {
        Debug.Log($"ID {Thread.CurrentThread.ManagedThreadId}, Start Start()");
        await TestAsyncMethod();
        Debug.Log($"ID {Thread.CurrentThread.ManagedThreadId}, End Start()");
    }

    private async Task TestAsyncMethod()
    {
        var context = SynchronizationContext.Current;

        await Task.Run(() =>
        {
            context.Post((state) =>
            {
                text.text = "1";
            }, null);

            Thread.Sleep(1000);

            context.Post((state) =>
            {
                text.text = "2";
            }, null);

            Thread.Sleep(1000);

            context.Post((state) =>
            {
                text.text = "3";
            }, null);

            Thread.Sleep(1000);
        });
    }
}

SynchronizationContext.Currentは現在の同期コンテキストを取得できるプロパティになり、すなわちメインスレッドを取得することができます。 androidjava層からUnityのメインスレッドのコンテキストを取得して操作を行うイメージに近いと思います。

参考

gasを使ったお手軽WebAPI実装

概要

超お手軽にWebAPIを作る方法です。 GASを使って、POSTしてスプレッドシートにデータを保存してそれをGETする方法です。 40秒でAPI作りなと言われた時の対応法です。

実演

POSTするとspreadsheetに保存してくれます。

curl -X POST -d "timestamp=1523790159&content=hoge" https://script.google.com/macros/s/xxxx/exec

スクリーンショット 2018-04-15 22.43.41.png (80.4 kB)

GETするとjsonを返してくれます。

curl -L -X GET https://script.google.com/macros/s/xxxx/exec
{"timestamp":"1523790159","content":"hoge"}

手順

「新規作成」-> 「スプレッドシート」を選択します。 スクリーンショット 2018-04-15 22.45.01.png (79.8 kB)

「ツール」->「スクリプトエディタ」でgasを起動します。 スクリーンショット 2018-04-15 22.46.02.png (103.3 kB)

以下のソースコードを実装します。

// データ更新
function doPost(e) {
  // パラメータのパース
  var timestamp = e.parameters.timestamp;
  var content = e.parameters.content;

  var sheet = SpreadsheetApp.getActive().getSheetByName('シート1');
  var sheetData  = sheet.getDataRange().getValues();
  sheetData.push([timestamp, content]);
  // シートへの書き込み、getRange(開始行、開始列、行数、列数)
  sheet.getRange(1,1,sheetData.length,2).setValues(sheetData);
}

// データ取得
function doGet(e) {  
  var sheet = SpreadsheetApp.getActive().getSheetByName('シート1');
  var sheetData = sheet.getDataRange().getValues();

// 最終行を取得する
  var timestamp = String(sheet.getRange(sheet.getLastRow(),1,1,1).getValue());
  var content = sheet.getRange(sheet.getLastRow(),2,1,1).getValue();

  var result = {'timestamp': timestamp, 'content': content};

  return ContentService.createTextOutput(JSON.stringify(result)).setMimeType(ContentService.MimeType.JSON);
}

POSTリクエストが来た場合にはdoPost()がGETリクエストが来た場合にはdoGet()が呼ばれます。

JSONでのPOST

jsonでpostしたい場合は以下のようにします。

function doPost(e) {
  var json = e.postData.getDataAsString();
  var data = JSON.parse(json);
  
  var timestamp = data.timestamp
  var content = data.content

  var sheet = SpreadsheetApp.getActive().getSheetByName('シート1');
  var sheetData  = sheet.getDataRange().getValues();
  sheetData.push([timestamp, content]);
  // シートへの書き込み、getRange(開始行、開始列、行数、列数)
  sheet.getRange(1,1,sheetData.length,2).setValues(sheetData);
}

アクセスできるように公開する

[公開] -> 「ウェブアプリケーションとして導入」を選択します。 スクリーンショット 2018-04-16 0.23.24.png (53.4 kB)

以下のように設定して導入をクリックします。 スクリーンショット 2018-04-16 1.04.35.png (77.3 kB)

アプリケーションにアクセスできるユーザーは「全員(匿名ユーザーを含む)」にする必要があります。会社などの組織アカウントでは、制限が書けられていてこの選択肢がないこともあります。その場合は個人アカウントを使ってください。

アクセス先のURLが表示されるのでメモします。

注意

スクリプトの更新を行った時には、ウェブアプリケーションの導入を再度行う必要があります。(面倒くさい)その場合でもURLは変わらないようです。

おまけ

Unityからでも問題なく使えます。

using UnityEngine;
using System.Collections;
using UnityEngine.Networking;
 
public class APITest : MonoBehaviour {
    void Start() {
        StartCoroutine(GetText());
    }
 
    IEnumerator GetText() {
        UnityWebRequest www = UnityWebRequest.Get("https://script.google.com/macros/s/xxxx/exec");
        yield return www.SendWebRequest();
 
        if(www.isNetworkError || www.isHttpError) {
            Debug.Log(www.error);
        }
        else {
            // 結果をテキストとして表示します
            Debug.Log(www.downloadHandler.text);
        }
    }
}
{"timestamp":"1523790159","content":"hoge"}
UnityEngine.Debug:Log(Object)

参考

UnityにおけるAwaitAsyncを使った非同期実装について①

概略

Unity2018.1から正式に.Net4.7、C#6への対応がなされます。 C#5からは、非同期的な実装をまるで同期的に記述できるasync/awaitというキーワードが導入されました。今回はasync/awaitを使ったUnityにおけるマルチスレッドプログラミングについて解説を行います。

非同期実装を行う必要性とは(・・?)

同期実装とは、プログラムを記述通り順番に実行していきます。対して、非同期実装では記述順とは異なる順序での実行がなされます。どうして非同期に処理を行う必要があるのでしょうか。

スクリーンショット 2018-04-13 20.13.05.png (69.3 kB)

この図は同期処理を表したものです。HeavyMethod()の実行中は、メインスレッドを専有してしまいUIが固まる、イベントが受けられないなどといった状況に陥ってしまいます。 非同期処理の必要性は、メインスレッドの専有を避けることにあります。

非同期実装を行う方法

  • Unityコルーチン
  • async/await(C#5以降)

Unityでは、非同期な処理を行えるようにコルーチンの仕組みが用意されています。

スクリーンショット 2018-04-13 20.09.49.png (65.8 kB)

コルーチンでは、各処理を高速に切り替え一つの処理がメインスレッドを専有しない仕組みになっています。しかし、全ての処理をメインスレッドで行っているため処理限界に達することがあります。 重たい処理に関してはメインスレッドではなく別スレッドで実行し処理が終わったらメインスレッドに戻ることが求められます。

スクリーンショット 2018-04-13 20.32.30.png (73.6 kB)

C#5以前でもThreadを扱うクラスがありマルチスレッドな実装を行うことは可能でした。しかし、低レベルなAPIレベルでしか提供がなされておらずやや扱いにくい印象でした。C#5以降ではこれを解消する直感的かつ簡潔にまるで同期処理のように非同期処理が記述できるasync/awaitの仕組みがで導入されました。

Taskの説明

マルチスレッドな非同期処理を見ていく前にTaskの説明を行います。 Taskはある一連の処理をまとめた一つの単位になります。

var task = Task.Run(() =>
{
    Debug.Log("HelloWorld");
    Debug.Log("HelloWorld");
    Debug.Log("HelloWorld");
});

これはデバッグログを3回出力するという一つのTaskになります。 Task.Runは、Taskのファクトリメソッドになります。 Taskでの処理は自動的にスレッドプールになります。

マルチスレッドの実装

さて、Taskについての理解ができました。 このTask単位でマルチスレッドに行う方法を見ていきます。

シンプルなマルチスレッド実装

using System.Threading;
using System.Threading.Tasks;
using UnityEngine;

public class AsyncSampleScene : MonoBehaviour {

    private async void Start()
    {
        Debug.Log($"ID {Thread.CurrentThread.ManagedThreadId}, Start Start()");
        await TestAsyncMethod();
        Debug.Log($"ID {Thread.CurrentThread.ManagedThreadId}, End Start()");
    }

    private Task TestAsyncMethod()
    {
        return Task.Run(() => {
            Debug.Log($"ID {Thread.CurrentThread.ManagedThreadId}, [Async] 1");
            Thread.Sleep(1000); 
            Debug.Log($"ID {Thread.CurrentThread.ManagedThreadId}, [Async] 2");
            Thread.Sleep(1000);
            Debug.Log($"ID {Thread.CurrentThread.ManagedThreadId}, [Async] 3");
            Thread.Sleep(1000);
        });
    }
}

結果

ID 1, Start Start() //メインスレッド
ID 86, [Async] 1 // 別スレッド
ID 86, [Async] 2 // 別スレッド
ID 86, [Async] 3 // 別スレッド
ID 1, End Start() // メインスレッド

(ここでのIDはスレッドごとに割り当てられる一意のIDです)

メインスレッドでログを1回出力し、別のスレッドで処理を実行し、最後にまたメインスレッドに戻ってログを出力しています。 処理を別スレッドに逃すことができ、その完了をハンドリングしてメインスレッドに戻ることができました。 ポイントは、awaitとTask.Runです。 実行関数の前にawaitをつけるとTestAsyncMethod()の完了を待って、処理が終わったら後続処理をメインスレッドで続行してくれます。awaitを使ったメソッドではシグネチャにasyncをつけることになっています。 Task.RunはTaskのファクトリーメソッドであり、この中のTaskを別スレッドで実行してくれます。

返り値がある場合は以下のようになります。

using System.Threading;
using System.Threading.Tasks;
using UnityEngine;

public class AsyncSampleScene : MonoBehaviour
{
    private async void Start()
    {
        Debug.Log($"ID {Thread.CurrentThread.ManagedThreadId}, Start Start()");
        int j = await TestAsyncMethod();
        Debug.Log($"ID {Thread.CurrentThread.ManagedThreadId}, {j}");
    }

    private Task<int> TestAsyncMethod()
    {
        return Task.Run<int>(() =>
        {
            int i = 0;
            Debug.Log($"ID {Thread.CurrentThread.ManagedThreadId}, [Async] 1");
            i++;
            Thread.Sleep(1000); // 擬似的な重たい処理
            Debug.Log($"ID {Thread.CurrentThread.ManagedThreadId}, [Async] 2");
            i++;
            Thread.Sleep(1000);
            Debug.Log($"ID {Thread.CurrentThread.ManagedThreadId}, [Async] 3");
            i++;
            Thread.Sleep(1000);
            return i;
        });
    }
}

結果

ID 1, Start Start()
ID 116, [Async] 1
ID 116, [Async] 2
ID 116, [Async] 3
ID 1, End Start()

Task.Wait()の罠

次に示すのは、デットロックを引き起こすヤバイコードです。 (UnityEditorで実行すると応答なくなって強制終了するしかなくなります)

using System.Threading;
using System.Threading.Tasks;
using UnityEngine;

public class AsyncSampleScene : MonoBehaviour {

    private void Start()
    {
        Debug.Log($"ID {Thread.CurrentThread.ManagedThreadId}, Start Start()");
        Task t = KusoAsyncMethod();
        t.Wait(); // tのTaskが終わるまで待つ
        Debug.Log($"ID {Thread.CurrentThread.ManagedThreadId}, End Start()");
    }

    private Task KusoAsyncMethod()
    {
        return Task.Run(() => {
            Debug.Log($"ID {Thread.CurrentThread.ManagedThreadId}, [Async] 1");
            Thread.Sleep(1000);
            Debug.Log($"ID {Thread.CurrentThread.ManagedThreadId}, [Async] 2");
            Thread.Sleep(1000);
            Debug.Log($"ID {Thread.CurrentThread.ManagedThreadId}, [Async] 3");
            Thread.Sleep(1000);
        });
    }
}

なぜこのようなことになってしまうのでしょうか。

スクリーンショット 2018-04-14 0.06.22.png (111.2 kB)

図にすると上図のような状態に陥ってしまいます。 一方で、awaitを使うと下図になり挙動が異なることが分かります。

スクリーンショット 2018-04-14 0.27.05.png (105.1 kB)

まとめ

  • Task、async/awaitを使うことでマルチスレッドな非同期処理を直感的に記述することができる
  • Taskクラスを使うとスレッドプールを自動で行ってくれる
  • 別スレッドで発生した例外もキャッチすることができる
  • Task.Wait()またはTask.Result()をデットロックに陥る危険性がある

参考

gasを使って特定のGoogleドライブ以下のファイル更新を通知する

概要

特定のGoogleドライブ以下のファイル更新を検知してCWで通知する方法です。

なぜgasなのか(・・?)

何と言ってもスクリプトを実行するサーバーを用意しなくて済むからです。chatworkへの通知も非常にお手軽に実装することが可能です。 Googleドライブとの親和性ももちろん高く、簡単な実装で実現が可能です。

手順

ドライブの「新規作成」->「その他」->「Goggle Apps Script」を選択します。 スクリーンショット 2018-03-15 2.30.09.png (142.2 kB)

「リソース」->「ライブラリ」からChatworkClientを導入します。 スクリーンショット 2018-03-15 2.07.11.png (63.6 kB)

ライブラリを追加のところに「M6TcEyniCs1xb3sdXFF_FhI-MNonZQ_sT」を入力します。 * https://github.com/cw-shibuya/chatwork-client-gas

いい感じにスクリプトを書きます。

var Token = "xxxxxx";  // chatwork bot
var RoomId = xxxxx; // CWの部屋ID

//対象とするGoogleDrive親フォルダのID
// IDはURLのfolders/以下の英数字
var PARENT_FOLDER_ID = "xxxx";

// 24時間(ミリsec)
var SEARCH_TIME = 86400;

function myFunction() {
  var parentFolder = DriveApp.getFolderById(PARENT_FOLDER_ID);
  
  var dateobj = new Date();
  var nowUnixTime = Math.floor(dateobj.getTime() / 1000) ;
  var dayAgoUnixTime = nowUnixTime - SEARCH_TIME;
  var mes = "";
  
  mes += "[info][title](F)本日更新されたドキュメント一覧(F)[/title]";
  
  var allFiles = getAllFilesName(parentFolder);
  for(var i = 0; i < allFiles.length; i++){
    // ファイル更新のタイムスタンプ(unixtime)
    var fileTimeStamp = Math.floor(Date.parse(allFiles[i].getLastUpdated())/1000);
    
    if(fileTimeStamp > dayAgoUnixTime){
      mes += "* ";
      mes += allFiles[i].getName();
      mes += "\n";
      
      mes += "  * ";
      mes += allFiles[i].getUrl();
      mes += "\n";
      
    }
    
    //Logger.log(allFiles[i].getName());
  }

  mes += "[/info]";
  
  // CW通知
  var client = ChatWorkClient.factory({token: Token});
  client.sendMessage({room_id: RoomId, body: mes});
  
}

function getAllFilesName(parentFolder){
  var fileList = [];
  var files = parentFolder.getFiles();
  while(files.hasNext()){
    fileList.push(files.next());
  }
  
  var childFolders = parentFolder.getFolders();
  
  while(childFolders.hasNext()){
    var childFolder = childFolders.next();
    
    // 再帰的な呼び出し
    fileList = fileList.concat(getAllFilesName(childFolder));
  }
  return fileList;
}

getFiles()getFolders()では、そのフォルダに属したファイルやフォルダしか取れないため再帰的に取得を行う必要があります。

トリガーを好きな時間に設定します。 「編集」->「現在のプロジェクトのトリガー」->「新しいトリガーを追加」から設定します。

スクリーンショット 2018-03-15 2.17.24.png (55.7 kB)

通知が来ます スクリーンショット 2018-03-18 0.30.01.png (36.6 kB)

リファレンス

gasを使ったお手軽サーバー死活監視

概要

超お手軽にWebサーバーの死活監視をしてchatworkに通知する方法です。

なぜgasなのか(・・?)

何と言っても死活監視のスクリプトをcronするサーバーを用意しなくて済むからです。chatworkへの通知も非常にお手軽に実装することが可能です。 高度なことはできませんが、雑に死活監視するくらいであればgas環境でも十分です。

手順

ドライブの「新規作成」->「その他」->「Goggle Apps Script」を選択します。 スクリーンショット 2018-03-15 2.30.09.png (142.2 kB)

「リソース」->「ライブラリ」からChatworkClientを導入します。 スクリーンショット 2018-03-15 2.07.11.png (63.6 kB)

ライブラリを追加のところに「M6TcEyniCs1xb3sdXFF_FhI-MNonZQ_sT」を入力します。

いい感じにスクリプトを書きます。

var siteURL = "http://xxxxxxxx.com"; // チェックしたいサーバーのURL
var Token = "xxxx"; // chatworkのbot token
var RoomId = xxxx; // 通知したい部屋のID

function myFunction() {

  try {
    // URLをフェッチ - muteHttpExceptions:trueの場合、HTTPエラーの際に例外をスローしない
    var response = UrlFetchApp.fetch(siteURL, { muteHttpExceptions:true });
    // レスポンスコード
    var code = response.getResponseCode();

    // レスポンスコード 200をチェックする
    if(code == 200) {
        Logger.log("access OK");
        Logger.log("Response code: " + code);
    } else {
        var mes = "[info][title]死活監視[/title][To:xxxx]\nhogehogeが落ちています[/info]";
        Logger.log(mes);
        var client = ChatWorkClient.factory({token: Token});
        client.sendMessage({room_id: RoomId, body: mes});
    }
  } catch(err) {
    // catch : DNSエラーなどでURLをfetch出来ないとき
    var mes = "[info][title]死活監視[/title][To:xxxx]\nhogehogeにアクセスできません[/info]";
    Logger.log(mes);
    var client = ChatWorkClient.factory({token: Token});
    client.sendMessage({room_id: RoomId, body: mes});
  }
}

トリガーを設定します。 「編集」->「現在のプロジェクトのトリガー」->「新しいトリガーを追加」から設定します。 スクリーンショット 2018-03-15 2.17.24.png (55.7 kB)

あとは、サーバーが落ちるのを楽しみに待つだけです٩( ‘ω’ )و

Unity、Android、iOSのビルドトラブルシューティング

Unityを使っていて主にビルド周りでハマったことをメモとして残したいと思います。

Q. Unityはどこからダウンロードできますか?

  1. こちらからどうぞ

リリース

パッチリリース

Q. Unityのキャッシュサーバーが落ちると自力ビルドできない?

  1. そのようです。ビルド開始後cacheサーバーへの接続が成功し、ビルド終了後に接続を切るタイミングでサーバーが落ちているとビルド自体が失敗するようです。

Q. ローカルキャッシュサーバーが上手く動かないんだけど?

キャッシュサーバーの設定をlocalにするとどうやら上手くいかない場合があるようです。 その場合でもremoteにしてlocalhost:8126にしてあげれば上手くいくようです。

Q. UnityでAndroid SDK、NDK、Javaのバージョンはどこに設定すればいいですか?

  1. [Unity] -> [Preferences] -> [External Tools]から設定できます。

f:id:orange_lily27:20180505213323p:plain

たまにNDKのPath設定が吹っ飛ぶことがあるのですが謎です(´・_・`)

Q. ビルドしようとしたらandroidコマンドがないってエラーが出ます

  1. Android SDK 25.3以降だとandroidコマンドなくなってビルドできなくなっています。 低いバージョンをインストールするか、toolsフォルダだけ古いバージョン(25.2以下)に入れ替える必要があります。

Android SDK Offline: Android SDK Tools

f:id:orange_lily27:20180505215231p:plain

Q. Android NDKのバージョンはいくつにすればよいですか?

  1. UnityではNDKのバージョンはr10e固定です il2cppビルドしない場合はndkの設定は追加しなくても大丈夫です。

アーカイブはここから入手できます。

追記: Unity2017.2からはr13bにアップデートされたようです。

Q. AndroidのBuild-toolsやSDK Plattformはどうやってバージョンアップすればいいですか?

  1. コマンドからandroidと打つ、またはAndroidStudio → Tools → Android → SDK ManagerからAndroid SDK Managerを起動してできます

f:id:orange_lily27:20180505213502p:plain

Q. HighSierraでUnityプロジェクトが開けません

HighSierraからディスクのフォーマットがAPFSとなった影響です。対応方法などは以下を参照してください。

『横井軍平ゲーム館』を読んでの感想

www.amazon.co.jp

3行まとめ

  • 任天堂の発展に寄与した横井軍平氏のインタビュー本であり、任天堂や横井さんに興味がある人にオススメ
  • すぐにゲームが作れるスキルや発想力は特に身につかない
  • コイズム

感想

本書は任天堂の発展になくてはならない人であった横井軍平氏のインタビュー本です。横井氏は任天堂の一社員としてラブテスターゲームウォッチゲームボーイなどを世に送り出してきたその人です。アナログゲームとデジタルゲーム両方の分野で成功を収めており、遊びの本質を制していた人と言っても良い人です。その横井氏を著者である牧野武文氏が亡くなる直前にインタービューしたものを元に構成、解説を付け加えて1997年に発売された単行本の文庫化です。

インタビュー中に特に難しい質疑応答はなく、誰でも楽しく横井氏の昔話を聞くような感覚で本書を読むことができます。しかし、その中には横井氏の根本的な考え方が散りばめられており、読者は横井さんの哲学であるヨコイズムを透けて見ることになります。ただし、透けて見ることが本書の主題なので、具体的なアイデアが湧いてくる方法やゲーム・おもちゃの作り方を学ぶことはあまりできません。そのような目的で本書を手に取ることはオススメできません。

wikipedia横井軍平について調べるとヨコイズムついて短く解説してあるが、あれは本質ではないと私は考えています。そしてこの本を読むことによってヨコイズムの一端を垣間見ることはできますが、受け取り方は人それぞれ変わってくると思います。それだけヨコイズムが示唆に富み多様性を秘めているということでしょう。

ちなみに私の思うヨコイズムの本質とは、「アイデア」と「技術」と「実現」のバランスを図ること、そして、人を制して遊びを制するということです。アイデアだけでもそれは絵に書いた餅だし、技術だけでも楽しさは提供できないただのガラクタになってしまいます。 横井氏の有名な言葉として「枯れた技術の水平思考」があります。文字通りに受け取れば他分野で成熟した技術をエンターテイメントとして組み込むという意味になります。そういう意味ももちろんあるでしょうが、そうではなくCPU性能がどうこう、カラーだ、高画質だと単なる技術一辺倒で押し通すのではなく「アイデア」と「技術」を最適に組み合わせることが「遊び」の本質である、その時の技術とは必ずしも最先端ではないということを言っているのではないかと感じました。

そして、ヨコイズムに欠かしてはならないのは「実現」です。いくらアイデアと技術があってもお金と人がいなければ何もできないということです。そのあたりのバランス感覚の良さが横井氏の閃き力や技術力よりも秀でた一番の才能であることがよく分かります。 さらに「遊び」を制するためには、面白い遊びを作り出すだけではまだ足りなくて、その「遊び」が人々の生活にどのように溶けこんでいくかここまで先回りして予想できるようなビジョンを打ち立てないといけないということが本書からは見て取ることができました。

これらのことは、それはそれは至極当たり前のことなんだけど、そのことを根本的に理解し、実現していくことの難しさとその困難に立ち向かう光明、これこそがヨコイズムであると私は強く思っています。

「アイデアを持って策謀を成す」人 これが本書を読み終わった時の私の横井さんに対する印象でした。 作る側、遊ぶ側エンターテイメントに少しでも携わる者としてはぜひ読んでおきたい一冊です。