GoogleAppScriptからFacebookに投稿

この記事は2017年07月23日にqiitaに投稿した内容です。

環境

V2.10

概要

GoogleAppScriptを使用して一定時間ごとにFacebookにメッセージを投稿します

Facebookアプリの作成

https://developers.facebook.com/apps/

・「新しいアプリの追加」を押す ・「表示名」を入力する(例:TestGAS) ・「アプリIDを作成したください」を押す ・セキュリティチェックで文字を入力する

アクセストークンの取得

https://developers.facebook.com/tools/explorer/

・「グラフAPIエクスプローラ」の右側の「アプリ」と表示されているドロップボックスを作成したアプリに変更する(例:TestGAS)  最初は「GraphApiExplorer」と表示されているかもしれない ・「トークンを取得」を押す ・「ユーザーアクセストークンを取得」を押す ・「publish_actions」にチェックする ・「アクセストークンを取得」を押す  「アクセストークン」に表示される このアクセストークンは短期で1時間くらいしか有効じゃない

長期アクセストークンの取得

https://developers.facebook.com/tools/explorer/

・「GET/V2.10/」の横に「oauth/access_token?grant_type=fb_exchange_token&client_id=AAA&client_secret=BBB&fb_exchange_token=XXX」と入力して、「送信」  AAAはアプリID(ダッシュボードで確認)  BBBはappsecret(ダッシュボードで確認)  XXXは取得したアクセストークン ・返ってきた長期アクセストークンをメモる  

ユーザーIDの確認

https://developers.facebook.com/tools/explorer/

・「GET/V2.10/」の横に「me?fields=id,name」と入力して、「送信」

投稿のテスト

https://developers.facebook.com/tools/explorer/

・「GET/V2.10/」の「GET」を「POST」に変更 ・「me/feed?message=TestMessage」と入力して「送信」  TestMessageが投稿される

ブラウザからUrlで投稿

・ブラウザのURL入力欄で「https://graph.facebook.com/me/feed?method=POST&version=v2.10&message=TestMessage&access_token=XXX」と入力してリターン  meの部分はユーザーIDでも可能  XXXは取得した長期アクセストーク

アクセストークンデバッガ

https://developers.facebook.com/tools/debug/accesstoken/

アクセストークンを入力すると有効期限等の情報が得られる

GoogleAppScriptの作成

https://script.google.com/macros/d/

・下記のスクリプトをコピペして置き換える

アクセストークン用スクリプトプロパティの作成

https://script.google.com/macros/d/

・「ファイル」>「プロジェクトのプロパティ」を押す ・「スプリプトのプロパティ」タブに変更する ・「行の追加」を押す ・「名前」にプロパティ名を入れる(例:accessToken) ・「名前」欄の右側を押すと「値」入力欄が出るので取得した長期アクセストークンを入力する  PropertiesService.getScriptProperties().getProperty('accessToken')でアクセスできる

一定時間毎に処理を行う

GoogleAppScriptを使用して一定時間毎にFirebaseにレコードを追加するを参照する

スクリプト

function setTrigger()
{
  //十分毎に処理を実行
  var executeFuncName = "httpRequestFacebookPost";
  deleteTriggers( executeFuncName );
  ScriptApp.newTrigger( executeFuncName ).timeBased().everyMinutes( 10 ).create();
}

function deleteTriggers( executeFuncName )
{
  //登録されているトリガーを削除
  var triggers = ScriptApp.getProjectTriggers();
  for( var i = 0; i < triggers.length; i++ )
  {
    if ( triggers[ i ].getHandlerFunction() == executeFuncName )
      ScriptApp.deleteTrigger( triggers[ i ] );
  }
}

function httpRequestFacebookPost()
{
  var uri = "https://graph.facebook.com/me/feed?method=POST&version=v2.10";
  var message = "TestMessage4";
  var accessToken = PropertiesService.getScriptProperties().getProperty('accessToken');
  UrlFetchApp.fetch( uri + "&message=" + message + "&access_token=" + accessToken );
}

GoogleComputeEngineのVMインスタンスにWinScpでログイン

※この記事は2017年07月20日にqiitaに投稿した内容です。

環境

Windows10 Home

概要

コマンドラインでの操作がつらい。GUIでファイル転送したい為にWinScpで接続します

GoogleComputeEngineの設定

以下を参照して設定する GoogleComputeEngine上にサーバを立ててUNETを動かす

WinScpのインストール

https://winscp.net/eng/download.php

・「Installation package」をダウンロードしてインストール  エクスプローラ風にした

WinScp接続設定

・「転送プロトコル」は「SCP」に設定 ・「ホスト名」はVMインスタンスの「外部IP」を設定(例:104.198.168.72) ・「ユーザー名」は「gcloud compute ssh」コマンドの時のユーザー名(例:fukaken5050) ・「ポート番号」は22 ・「パスワード」は設定しない ・「設定」ボタンを押す ・「SSH」>「認証」>「認証条件」>「秘密鍵」>「...」を押す ・「C:\Users(ユーザー)\ユーザー名\.ssh」にある「google_compute_engine.ppk」を選択 ・「OK」を押す ・「保存」を押す ・「ログイン」でログインする

隠しファイルの表示

「.bash_profile」等の表示の為 ・メニューの「表示」>「環境設定」 ・「パネル」>「隠しファイルを表示する」にチェック

Taskを使用した非同期処理のテスト

この記事は2017年07月15日にqiitaに投稿した内容です。

環境

Unity2017.1.0f3

概要

.NET4.6が使用できるようになったのでTask・Async/Awaitのテスト

.NET4.6の設定

「PlayerSettings」>「OtherSettings」>「ScriptRuntimeVertion」を「Experimental(.NET4.6Equivalent)」に変更する

プログラム

順次実行と並列実行をしています

Assets/TestTask.cs

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

public class TestTask : MonoBehaviour
{
    [SerializeField] private Text _text = null;

    private IEnumerator Start()
    {
        yield return new WaitForSeconds( 3.0f );
        //順次実行
        yield return StartCoroutine( TestTaskSequentialProcess() );
        //並列実行
        yield return StartCoroutine( TestTaskParallelProcess() );
    }

    private IEnumerator TestTaskSequentialProcess()
    {
        Debug.Log( "StartTestTaskSequential:" + Thread.CurrentThread.ManagedThreadId + "--------------------" );
        var prevTime = Time.realtimeSinceStartup;
//      var task = Task.Run( () => TaskSequential() );
        var task = TaskSequential();

        //task.Wait();//awaitのせいでデッドロックする
        while( true )
        {
            if( task.IsCanceled == true || task.IsCompleted == true || task.IsFaulted == true )
                break;
            var time = Time.realtimeSinceStartup;
            Debug.Log( "DeltaTime:" + ( time - prevTime ).ToString() );
            prevTime = time;
            yield return null;
        }
        Debug.Log( "CompleteTestTaskSequential" );
        yield break;
    }

    private IEnumerator TestTaskParallelProcess()
    {
        Debug.Log( "StartTestTaskParallel:" + Thread.CurrentThread.ManagedThreadId + "--------------------" );
        var prevTime = Time.realtimeSinceStartup;
        //var task = Task.Run( () => TaskParallel() );
        var task = TaskParallel();
        //task.Wait(); //動作するが中身がわからないと使えないので使わない
        while( true )
        {
            if( task.IsCanceled == true || task.IsCompleted == true || task.IsFaulted == true )
                break;
            var time = Time.realtimeSinceStartup;
            Debug.Log( "DeltaTime:" + ( time - prevTime ).ToString() );
            prevTime = time;
            yield return null;
        }
        Debug.Log( "CompleteTestTaskParallel" );
        yield break;
    }

    //Task.Run()で呼び出されなければ、呼び出し元と同じスレッドで実行される
    //メインスレッドで実行されているならUnityの関数が使える
    //awaitで待っている間、Taskは中断され、他に処理がうつる
    private async Task TaskSequential()
    {
        Debug.Log( "StartTaskSequential:" + Thread.CurrentThread.ManagedThreadId );
        if( _text != null )
            _text.text = "StartTaskSequential";

        for ( var i = 0; i < 5; i++ )
        {
            var index = i;
            //awaitでTaskが終わるまで中断
            await Task.Run(() => Sleep( index ) );
        }
        Debug.Log( "CompleteTaskSequential" );
    }

    private Task TaskParallel()
    {
        Debug.Log( "StartTaskParallel:" + Thread.CurrentThread.ManagedThreadId );
        if( _text != null )
            _text.text = "StartTaskParallel";

        var tasks = new List<Task>();
        for ( var i = 0; i < 5; i++ )
        {
            var index = i;
            var task = Task.Run(() => Sleep( index ) );
            tasks.Add( task );
        }
        //すべてのタスクが完了した時に完了するタスクを生成
        var taskAll = Task.WhenAll( tasks );

        //この時点では終わっていない
        //ここで終わらせたければ asyncにしてawait Task.WhenAll()にする
        Debug.Log( "CompleteTaskParallel" ); 
        return taskAll;
    }

    private void Sleep( int index )
    {
        Debug.Log( "Sleep:" + Thread.CurrentThread.ManagedThreadId );
        Thread.Sleep( 1000 );
        Debug.Log( index );
    }
}

Threadを用いた並列処理

Task・Async/Awaitを使用したくない場合 http://qiita.com/fukaken5050/items/c6ea9233b708082ae1e9

参考

http://qiita.com/acple@github/items/8f63aacb13de9954c5da http://qiita.com/takutoy/items/6f3a20f7f48f36d918dd

AssetBundleManagerのwwwをUnityWebRequestに変えて、さらにキャッシュの古いバージョンを削除してみる

この記事は2017年07月11日にqiitaに投稿した内容です。

環境

Unity2017.1.0f3

概要

wwwは使用しないでUnityWebRequestを使用したほうがいいらしいのでテスト また、Caching.ClearOtherCachedVersionsを使用して、キャッシュの古いバージョンを削除します

変更箇所

        // Where we actuall call WWW to download the assetBundle.
        static protected bool LoadAssetBundleInternal (string assetBundleName, bool isLoadingAssetBundleManifest)
        {
            // Already loaded.
            LoadedAssetBundle bundle = null;
            m_LoadedAssetBundles.TryGetValue(assetBundleName, out bundle);
            if (bundle != null)
            {
                bundle.m_ReferencedCount++;
                return true;
            }

            // @TODO: Do we need to consider the referenced count of WWWs?
            // In the demo, we never have duplicate WWWs as we wait LoadAssetAsync()/LoadLevelAsync() to be finished before calling another LoadAssetAsync()/LoadLevelAsync().
            // But in the real case, users can call LoadAssetAsync()/LoadLevelAsync() several times then wait them to be finished which might have duplicate WWWs.
            if (m_DownloadingWWWs.ContainsKey(assetBundleName) )
                return true;

        //  WWW download = null;
            UnityWebRequest download = null;
            string url = m_BaseDownloadingURL + assetBundleName;

            // For manifest assetbundle, always download it as we don't have hash for it.
            if (isLoadingAssetBundleManifest)
            //  download = new WWW(url);
                download = UnityWebRequest.GetAssetBundle( url );
            else
            {
                download = UnityWebRequest.GetAssetBundle( url, m_AssetBundleManifest.GetAssetBundleHash(assetBundleName), 0 );
                //Unity2017 バージョン違いを消す
                if( Caching.ClearOtherCachedVersions( assetBundleName, m_AssetBundleManifest.GetAssetBundleHash(assetBundleName ) ) == false )
                    Debug.LogWarning( "ClearOtherCachedVersions" );
            }
            //  download = WWW.LoadFromCacheOrDownload(url, m_AssetBundleManifest.GetAssetBundleHash(assetBundleName), 0); 
            download.Send();
            m_DownloadingWWWs.Add(assetBundleName, download);

            return false;
        }
        void Update()
        {
            // Collect all the finished WWWs.
            var keysToRemove = new List<string>();
            foreach (var keyValue in m_DownloadingWWWs)
            {
            //  WWW download = keyValue.Value;
                var download = keyValue.Value;

                // If downloading fails.
                if (download.error != null)
                {
                    m_DownloadingErrors.Add(keyValue.Key, string.Format("Failed downloading bundle {0} from {1}: {2}", keyValue.Key, download.url, download.error));
                    keysToRemove.Add(keyValue.Key);
                    continue;
                }

                // If downloading succeeds.
                if(download.isDone)
                {
                //  AssetBundle bundle = download.assetBundle;
                    AssetBundle bundle = ((DownloadHandlerAssetBundle)download.downloadHandler).assetBundle;
                    if (bundle == null)
                    {
                        m_DownloadingErrors.Add(keyValue.Key, string.Format("{0} is not a valid asset bundle.", keyValue.Key));
                        keysToRemove.Add(keyValue.Key);
                        continue;
                    }

                    //Debug.Log("Downloading " + keyValue.Key + " is done at frame " + Time.frameCount);
                //  m_LoadedAssetBundles.Add(keyValue.Key, new LoadedAssetBundle( download.assetBundle ) );
                    m_LoadedAssetBundles.Add(keyValue.Key, new LoadedAssetBundle( bundle ) );
                    keysToRemove.Add(keyValue.Key);

                    //Unity2017キャッシュの確認
                    var currentCache = Caching.currentCacheForWriting;
                    var versions = new List<Hash128>();
                    var assetbundleName = Path.GetFileName( download.url );
                    Caching.GetCachedVersions( assetbundleName, versions );
                    Debug.Log( "CacheVersionCount:" + bundle.name + ":" + versions.Count );
                    Debug.Log( "CachePath:" + currentCache.path );
                    Debug.Log( "CacheSize:" + currentCache.spaceOccupied + "/" + currentCache.maximumAvailableStorageSpace );
                }
            }

            // Remove the finished WWWs.
            foreach( var key in keysToRemove)
            {
            //  WWW download = m_DownloadingWWWs[key];
                var download = m_DownloadingWWWs[key];
                m_DownloadingWWWs.Remove(key);
                download.Dispose();
            }

            // Update all in progress operations
            for (int i=0;i<m_InProgressOperations.Count;)
            {
                if (!m_InProgressOperations[i].Update())
                {
                    m_InProgressOperations.RemoveAt(i);
                }
                else
                    i++;
            }
        }

c++プラグインでSquirrelスクリプトとやり取りをする(Android)

この記事は2017年07月11日にqiitaに投稿した内容です。

環境

Unity5.6.2f1 Squirrel 3.1

概要

c++Likeなスクリプト言語Squirrelとやり取りをするテストです

Squirrelのダウンロード

以下のページで「Download」を押す https://sourceforge.net/projects/squirrel/

AndroidStudioとUnityの事前設定

以下を参照してプロジェクトを作成する プラグインでc++と連携する(Android) libunisquirrel.soとしてエクスポートする

Squirrel関連のファイル追加

AndroidのNativePluginを作成した時の「cpp」フォルダ内に「squirrel3」というフォルダを作成 ・ダウンロードしたtar.gzを展開する ・「squirrel」「sqstdlib」「include」フォルダを作成した「squirrel3」の中にフォルダ毎コピーする ・コピーしたフォルダ内の「.cpp」と「.h」以外を削除する

CMakeLists.txt

# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.
add_library( # Sets the name of the library.
             native-lib

             # Sets the library as a shared library.
             SHARED

             # Provides a relative path to your source file(s).
             UniSquirrel.cpp
             #squirrel3
             squirrel3/squirrel/sqapi.cpp
             squirrel3/squirrel/sqbaselib.cpp
             squirrel3/squirrel/sqclass.cpp
             squirrel3/squirrel/sqcompiler.cpp
             squirrel3/squirrel/sqdebug.cpp
             squirrel3/squirrel/sqfuncstate.cpp
             squirrel3/squirrel/sqlexer.cpp
             squirrel3/squirrel/sqmem.cpp
             squirrel3/squirrel/sqobject.cpp
             squirrel3/squirrel/sqstate.cpp
             squirrel3/squirrel/sqtable.cpp
             squirrel3/squirrel/sqvm.cpp
             squirrel3/sqstdlib/sqstdaux.cpp
             squirrel3/sqstdlib/sqstdblob.cpp
             squirrel3/sqstdlib/sqstdio.cpp
             squirrel3/sqstdlib/sqstdmath.cpp
             squirrel3/sqstdlib/sqstdrex.cpp
             squirrel3/sqstdlib/sqstdstream.cpp
             squirrel3/sqstdlib/sqstdstring.cpp
             squirrel3/sqstdlib/sqstdsystem.cpp

)

# Specifies a path to native header files.
include_directories( Unity/ )
include_directories( squirrel3/include/ )

# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.
find_library( # Sets the name of the path variable.
              log-lib

              # Specifies the name of the NDK library that
              # you want CMake to locate.
              log
)

# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.
target_link_libraries( # Specifies the target library.
                       native-lib
                       # Links the target library to the log library
                       # included in the NDK.
                       ${log-lib}
)

プログラム

Squirrelの「.nut」ファイルはUTF8(bom無し)CR+LFでテストしました

UniSquirrel.cpp

#include "IUnityInterface.h"
#include <math.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>
//#define SQUNICODE
#include "squirrel.h"
#include "sqstdio.h"
#include "sqstdaux.h"

static void printfunc( HSQUIRRELVM v, const SQChar* pFormat, ... );
static SQInteger print_args( HSQUIRRELVM v );
static SQInteger register_global_func( HSQUIRRELVM v, SQFUNCTION f, const char *fname );
static void CallSquirrelFunc( HSQUIRRELVM v );

extern "C"
{
    using CallbackOutputString = void(*)( const SQChar* );
    namespace
    {
        CallbackOutputString onCallbackOutputString = NULL;
    }

    UNITY_INTERFACE_EXPORT void SetCallbackOutputString( CallbackOutputString func )
    {
        onCallbackOutputString = func;
    }

    UNITY_INTERFACE_EXPORT void ExecuteScript( const SQChar* pPath )
    {
        if ( onCallbackOutputString == NULL )
            return;

        HSQUIRRELVM v;
        v = sq_open( 1024 );
        sqstd_seterrorhandlers( v );
        sq_setprintfunc( v, printfunc, printfunc );
        sq_pushroottable( v );
        //c/c++関数の登録
        register_global_func( v, print_args, "print_args" );

        if( SQ_SUCCEEDED( sqstd_dofile( v, pPath, 0, 1 ) ) )
        {
            printf("Call Failed!");
        }

        //squirrel関数の呼び出し
        CallSquirrelFunc( v );

        sq_close(v);
    }

    UNITY_INTERFACE_EXPORT void ExecuteScriptInMemory( const SQChar* pBuffer )
    {
        if ( onCallbackOutputString == NULL )
            return;

        HSQUIRRELVM v;
        v = sq_open( 1024 );
        sqstd_seterrorhandlers( v );
        sq_setprintfunc( v, printfunc, printfunc );
        sq_pushroottable( v );

        sq_compilebuffer( v, pBuffer,(int)strlen( pBuffer ) * sizeof( SQChar ), "compile", 1 );
        sq_pushroottable(v);
        sq_call(v,1,1,0);

        sq_close(v);
    }

    static void CallSquirrelFunc( HSQUIRRELVM v )
    {
        //SQInteger saveStack = sq_gettop( v );
        sq_pushroottable( v );
        sq_pushstring( v, _SC( "TestFunc" ), -1 );
        if( SQ_SUCCEEDED( sq_get( v, -2 ) ) )
        {
        }
        int n = 123;
        float f = 345.67f;
        const SQChar* s = _SC( "abcd" );
        sq_pushroottable( v );
        sq_pushinteger( v, n );
        sq_pushfloat( v, f );
        sq_pushstring( v, s, -1 );
        sq_call( v, 4, SQTrue, 0 );

        if( sq_gettype( v, -1 ) == OT_INTEGER )
        {
            SQInteger ret;
            sq_getinteger( v, -1, &ret );
            sq_pop( v, 1 );
        //    sq_settop( v, saveStack );
            char str[64] = "";
            snprintf( str, sizeof( str ) - 1, "%d", ret );
            onCallbackOutputString( str );
        }
    }

    static void printfunc( HSQUIRRELVM v, const SQChar* pFormat, ... )
    {
        va_list arglist;
        va_start( arglist, pFormat );
        SQChar buffer[ 1024 ] = _SC( "" );
        vsnprintf( buffer, sizeof(buffer), pFormat, arglist );
        va_end( arglist );

        onCallbackOutputString( buffer );
    }

    static SQInteger print_args( HSQUIRRELVM v )
    {
        SQInteger nargs = sq_gettop(v); //number of arguments
        for(SQInteger n=1;n<=nargs;n++)
        {
            printf("arg %d is ",n);
            switch(sq_gettype(v,n))
            {
                case OT_NULL:
                    onCallbackOutputString("null");
                    break;
                case OT_INTEGER:
                    onCallbackOutputString("integer");
                    break;
                case OT_FLOAT:
                    onCallbackOutputString("float");
                    break;
                case OT_STRING:
                    onCallbackOutputString("string");
                    break;
                case OT_TABLE:
                    onCallbackOutputString("table");
                    break;
                case OT_ARRAY:
                    onCallbackOutputString("array");
                    break;
                case OT_USERDATA:
                    onCallbackOutputString("userdata");
                    break;
                case OT_CLOSURE:
                    onCallbackOutputString("closure(function)");
                    break;
                case OT_NATIVECLOSURE:
                    onCallbackOutputString("native closure(C function)");
                    break;
                case OT_GENERATOR:
                    onCallbackOutputString("generator");
                    break;
                case OT_USERPOINTER:
                    onCallbackOutputString("userpointer");
                    break;
                default:
                    return sq_throwerror(v,"invalid param"); //throws an exception
            }
            onCallbackOutputString("\n");
        }
        sq_pushinteger(v,nargs); //push the number of arguments as return value
        return 1; //1 because 1 value is returned
    }

    static SQInteger register_global_func( HSQUIRRELVM v, SQFUNCTION f, const SQChar* fname )
    {
        sq_pushroottable(v);
        sq_pushstring(v,fname,-1);
        sq_newclosure(v,f,0); //create a new function
        sq_createslot(v,-3);
        sq_pop(v,1); //pops the root table
        return 0;
    }
}

/Assets/TestSquirrel.cs

using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;
using System.Runtime.InteropServices;
using System.IO;
using System.Collections;

public class TestSquirrel : MonoBehaviour 
{
    [SerializeField] Text _text = null;

    [DllImport ("unisquirrel")]
    public static extern void SetCallbackOutputString( UnityAction<string> func );
    [DllImport ("unisquirrel")]
    public static extern void ExecuteScript( string path );
    [DllImport ("unisquirrel")]
    public static extern void ExecuteScriptInMemory( string buffer );

    private static TestSquirrel _instance = null;
    private void Awake()
    {
        _instance = this;
    }

    private void OnDestroy()
    {
        _instance = null;
    }

    private void OnApplicationQuit()
    {
        OnDestroy();
    }

    private void OnEnable()
    {
        SetCallbackOutputString( OnOutputString );
    }

    private void OnDisable()
    {
        SetCallbackOutputString( null );
    }

    private IEnumerator Start() 
    {
        yield return StartCoroutine( ExecuteScriptProcess( "test1.nut" ) );
        yield return StartCoroutine( ExecuteScriptInMemoryProcess( "test0.nut" ) );
    }

    private IEnumerator ExecuteScriptProcess( string fileName )
    {
        var filePath = Path.Combine(Application.streamingAssetsPath, fileName );
        var scriptData = "";

        if ( filePath.Contains( "://" ) == true )
        {
            WWW www = new WWW( filePath );
            yield return www;
            scriptData = www.text;
        }
        else
            scriptData = System.IO.File.ReadAllText( filePath );

        //persistentDataPathへコピー
        var outputPath = Path.Combine( Application.persistentDataPath, fileName );
        File.WriteAllText( outputPath, scriptData );
        while( File.Exists( outputPath ) == false )
            yield return null;

        Debug.Log( scriptData );
        ExecuteScript( outputPath );
        yield break;
    }

    private IEnumerator ExecuteScriptInMemoryProcess( string fileName )
    {
        var filePath = Path.Combine( Application.streamingAssetsPath, fileName );
        var scriptData = "";

        if ( filePath.Contains( "://" ) == true )
        {
            WWW www = new WWW( filePath );
            yield return www;
            scriptData = www.text;
        }
        else
            scriptData = File.ReadAllText( filePath );

        Debug.Log( scriptData );
        ExecuteScriptInMemory( scriptData );
        yield break;
    }

    private void OnOutputString( string message )
    {
        Debug.Log( message );
        _instance.SetText( message );
    }

    private void SetText( string message )
    {
        if( _text == null )
            return;
        _text.text = message;
    }
}

Assets/StreamingAssets/test0.nut

print("ハローワールド");

Assets/StreamingAssets/test1.nut

function TestFunc( n, f, s )
{
    print( "TestFunc:" + n + "/" + f + "/" + s );
    return 789;
}

function Main()
{
    print( "ハロー!ワールド" );
    print_args( 987, 654.32, "zxcv" );
}
Main();

プラグインでc++と連携する(Android)

この記事は2017年07月06日にqiitaに投稿した内容です。

環境

Unity5.6.2f1

概要

C#側からc++側に関数を登録して、c++側から登録した関数を呼び出します エコープログラムです

AndroidStudioとUnityの事前設定

以下を参照してプロジェクトを作成する Android用ネイティブレンダリングPlugin(c++[so])の作成 「libnativecpp.so」としてエクスポートされるようにする 「MultiThreadedRendering」にチェックを入れなくていい  今回はレンダリングは関係ない為

CMakeLists.txt

# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html
# Sets the minimum version of CMake required to build the native library.
cmake_minimum_required(VERSION 3.4.1)

# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.
add_library( # Sets the name of the library.
             native-lib

             # Sets the library as a shared library.
             SHARED

             # Provides a relative path to your source file(s).
             Test.cpp
)

# Specifies a path to native header files.
include_directories( Unity/ )

# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.
find_library( # Sets the name of the path variable.
              log-lib

              # Specifies the name of the NDK library that
              # you want CMake to locate.
              log
)

# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.
target_link_libraries( # Specifies the target library.
                       native-lib
                       # Links the target library to the log library
                       # included in the NDK.
                       ${log-lib}
)

プログラム

Assets/TestNativePlugin.cs

using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;
using System.Runtime.InteropServices;

public class TestNativePlugin : MonoBehaviour 
{
    [SerializeField] Text _text = null;

    [DllImport ("nativecpp")]
    public static extern void SetEchoCallbackFunction( UnityAction<string> func );
    [DllImport ("nativecpp")]
    public static extern void TestEcho( string message );

    private static TestNativePlugin _instance = null;
    private void Awake()
    {
        _instance = this;
    }

    private void OnDestroy()
    {
        _instance = null;
    }

    private void OnApplicationQuit()
    {
        OnDestroy();
    }

    private void OnEnable()
    {
        SetEchoCallbackFunction( OnNativeCallback );
    }

    private void OnDisable()
    {
        SetEchoCallbackFunction( null );
    }

    private void Start() 
    {
        TestEcho( "hello world!" );
    }

    private void OnNativeCallback( string message )
    {
        Debug.Log( message );
        _instance.SetText( message );
    }

    private void SetText( string message )
    {
        if( _text == null )
            return;
        _text.text = message;
    }
}

Test.cpp

#include "IUnityInterface.h"
#include <math.h>
#include <stdio.h>
#include <assert.h>

extern "C"
{
    using EchoCallback = void(*)( const char* );
    namespace
    {
        EchoCallback onEchoCallback = NULL;
    }

    UNITY_INTERFACE_EXPORT void SetEchoCallbackFunction( EchoCallback func )
    {
        onEchoCallback = func;
    }

    UNITY_INTERFACE_EXPORT void TestEcho( const char* pMessage )
    {
        if ( onEchoCallback == NULL )
            return;
        onEchoCallback( pMessage );
    }
}

GoogleComputeEngine上にサーバを立ててUNETを動かす

この記事は2017年07月04日にqiitaに投稿した内容です。

環境

Unity5.6.2f1 Windows10 Home

概要

GoogleComputeEngine(AWSのEC2のようなの)上で、Linuxサーバのインスタンスを作成し、UNETのサーバプログラムを動かし、マルチ対戦します

GoogleComputeEngineの常時無料枠

以下のUrlに記載されています https://cloud.google.com/free/docs/always-free-usage-limits https://cloud.google.com/compute/pricing USリージョンの「f1-micro」インスタンスが1つ無料とあるようです(バージニア州北部を除く)

インスタンスの作成

以下のページで詳しく解説されています あぱーブログ 「Compute Engine インスタンスの作成」まで進める コンソールで「外部IP 」を確認しておく。(例:104.198.168.72)

GoogleCloudSDKのインストール

https://cloud.google.com/sdk/downloads?hl=ja 「対話型インストーラ」の記述に沿ってインストールします

ログイン

すでにSDKをインストールしていて別のアカウントで使用していた場合、ログインが必要です ・「gcloud auth login」と入力 ・アカウントを選択する

サーバのポート開放

・「コマンドプロンプト」を開く ・「gcloud compute firewall-rules create VVV --allow tcp:0-10000,udp:0-10000」と打ち込みリターン  VVVはルール名(例:unet)  何番を開放すればよかったのかわからないので0から10000まで解放している  コンソールの「VPCネットワーク」>「ファイヤーウォールルール」で確認できる   https://console.cloud.google.com/networking/firewalls/list?project=heroic-passkey-178708&tab=INGRESS  

UNETプログラムの用意

ここにテスト用のUNETプログラムを用意しました ・「Download」を押してzipをダウンロード ・zip内に含まれる「Linux」内のものがサーバにアップするプログラム ・「WinApp」内のものはサーバと接続するクライアントプログラム、またはローカル確認用のサーバプログラムを兼ねる

サーバにデプロイ(コピー)

・ダウンロードしたzipを展開する ・「コマンドプロンプト」を開き、「Linux」フォルダ内に移動する ・「gcloud compute copy-files *. XXX@YYY:」と打ち込みリターン  XXXはユーザー名  YYYはインスタンス名 (例:gcloud compute copy-files *. fukaken5050@test-instance:)

sshでサーバに接続

・「コマンドプロンプト」を開き、「Linux」フォルダ内に移動する ・「gcloud compute --project "AAA" ssh --zone "BBB" "XXX@YYY"」と打ち込みリターン  AAAはプロジェクト名  BBBはインスタンスのリージョン  XXXはユーザー名  YYYはVMインスタンス名 (例:gcloud compute --project "testcomputeengine-171603" ssh --zone "us-central1-a" "fukaken5050@test-instance")  内部で「putty」が実行されてサーバのコマンドラインが入力できるようになる

実行権限の付与

・「putty」上で「chmod u+x EEE」と打ち込みリターン  EEEはプログラム名(例:chmod u+x server.x86_64)

サーバプログラムの実行

・「putty」上で「nohup ./EEE -logFile server.log &」と打ち込みリターン  EEEはプログラム名(例:nohup ./server.x86_64 -server -logFile server.log &)  -serverはUNETプログラム内部で使用している独自のパラメータ ・「Ctrl+C」でプロンプトに戻る

テスト用クライアントプログラムの実行

・ダウンロードしたzipを展開する ・「WinApp」>「test.exe」を実行する ・「接続先」に外部IPを入力する(例:104.198.168.72) ・「設定」を押す ・「クライアント」を押す  サーバに接続される ・もう一つ「test.exe」を実行し、「クライアント」を押す ・片方は「募集」、もう一つは「参加」で対戦する

プロセスの停止

・「putty」上で「ps x」と入力  実行されているプロセス一覧が表示される ・「Kill XXX」と入力  XXXはプロセスID

プロセスを監視して再実行

サーバプログラムが落ちてしまった場合に、自動で再実行します

execServer.sh ・「nohup ./execServer.sh &」と入力

#!/bin/bash
while true
do
isAlive=`ps -ef | grep "server.x86_64" | grep -v grep | wc -l`
if [ $isAlive = 0 ]; then
echo "serverを実行します"
nohup ./server.x86_64 -server -logFile server.log &
fi
sleep 10
done