NodeJsでBitpointの価格を取得してレスポンスを返す

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

環境

Unity2017.3.0p4 Windows10

概要

UnityからBitpointの価格情報のwebsocketのapiにアクセスしようとしたが、うまくいきませんでした。 仕方ないのでNodeJsからSockJs-Clientを使用して情報を取得、HttpServerを立て、HttpリクエストしてきたUnityに情報を返しています。

GoogleComputeEngineの設定、WinScpの設定

以下を参照して設定する サーバのIPアドレス(外部IP)をメモしておく GoogleComputeEngineのVMインスタンスにWinScpでログイン

プログラムのアップロード

WinScpを用いて後述の「bitpoint.js」と「serverBitpoint.sh」をアップロードし、パーミッションで実行権限を付与する  ・WinScp上でファイルを選択し、プロパティから行う

NodeJsのインストール

SSHでサーバにログインし、「Putty」のコマンドラインを開く 「sudo yum install -y nodejs」と入力

opensslの更新

「sudo yum update openssl」と入力

モジュールのダウンロード

js内で参照しているモジュールをダウンロードする

・「npm init」と入力  色々聞かれるがそのままリターン  package.jsonができる ・「npm install -save XXX」と入力  -saveでpackage.jsonにモジュール情報が保存される  XXXはモジュール名   Bitpoint.js内でrequireしているもの   「node Bitpoint.js」と入力してエラーが出るものが足りないもの    request    sockjs-client    date-utils    ※他にもエラーと言われるものがあるかもしれない

プログラムの実行

「nohup ./serverBitpoint.sh &」と入力

プログラム

アクセストークンを取得しているがこれはダミー。  USERNAMEとPASSWORDもダミー  なぜか実行しないとwebsocketに通信できなかった(気のせいかもしれない) bitpoint.js

const Crypto = require("crypto");
const Request = require('request');
const SockJS = require('sockjs-client');
const Http = require('http');
const Url = require('url');
const Qs = require('querystring');
require('date-utils');

const USERNAME = "hogehoge@hoge.com";
const PASSWORD = "fugafuga";
var _server;

Main();
function Main()
{
    GetAccessToken();
    CreateHttpServer();
}

function GetAccessToken()
{
    process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";

    var sha256 = Crypto.createHash('sha256');
    sha256.update(PASSWORD);
    var hash = sha256.digest('hex')

    Request.get(`https://public.bitpoint.co.jp/bpj-api/login?username=${USERNAME}&password=${hash}`, (error, response, body) =>
    {
        var accessToken = JSON.parse(body)['access_token'];
        console.log( 'access_token', accessToken );
    });
}

function CreateHttpServer()
{
    _server = Http.createServer();
    _server.on('request', function( req, res )
    {
        console.log("Method = ", req.method);
        console.log("STATUS: ", res.statusCode);
        console.log("Content-Type: ", res.getHeader("Content-Type") );

        var getParams = Url.parse( req.url, true );
        console.log( "Get:", getParams.query );

        if( req.method == 'POST' )
        {
            var body='';
            req.on( 'data', function( data )
            {
                body += data;
            });
            req.on( 'end', function()
            {
                var postParams = Qs.parse( body );
                console.log( "Post:", postParams );
                HttpResponse( req, res, getParams, postParams );
            });
        }
        else
            HttpResponse( req, res, getParams, null );
    });
    _server.listen(8080)
    console.log('Start Server');
}

function HttpResponse( req, res, getParams, postParams )
{
    var Responses =
    {
        "Default": function ()
        {
            res.writeHead(200, {'Content-Type': 'application/json'});
            var now = new Date().toFormat("YYYYMMDDHH24MISS");
            res.end( `{ "time":"${now}" }` );
        },
        "leverageTwoWay": function ()
        {
            var accessToken = ( getParams.query[ "access_token" ] ) ? getParams.query[ "access_token" ] : "";
            RequestLeverageTwoWay( accessToken, postParams, function( response )
            {
                res.writeHead(200, {'Content-Type': 'application/json'});
                res.end( response );
            });
        }
    }
    var uri = Url.parse(req.url).pathname;
    if (uri === "/")
    {
        Responses["Default"](); 
        return;
    }
    else if (uri === "/leverageTwoWay")
    {
        Responses["leverageTwoWay"]();
        return;
    }
}

function RequestLeverageTwoWay( accessToken, postParams, onResponse )
{
    var response = '{ "error":"" }';
    if( accessToken == "" )
    {
        response = '{ "error":"accessToken" }';
        console.log( response );
        onResponse( response );
        return;
    }
    if( postParams == null )
    {
        response = '{ "error":"postParams" }';
        console.log( response );
        onResponse( response );
        return;
    }

    var options = postParams;//{ 'server': '{"currencyCd1":"BTC","currencyCd2":"JPY"}' };
    const sock = new SockJS(`https://public.bitpoint.co.jp/bpj-api/leverageTwoWay?access_token=${accessToken}`, null, options );
    sock.onopen = function()
    {
        console.log( "Open:", sock.url );
    };

    sock.onmessage = function(e)
    {
        console.log( "Message:", e.data );

        var now = new Date().toFormat("YYYYMMDDHH24MISS");
        response = `{ "updatetime":"${now}", "data":${e.data} }`;
        sock.close();
    };

    sock.onclose = function()
    {
        console.log( "Close:", sock.url );
        onResponse( response );
    };
}

serverBitpoint.sh

#!/bin/bash
node bitpoint.js

 

クライアントプログラム

ApiLogin.Requestを呼び出し、AccessTokenを取得 ApiLeverageTwoWay.Requestを呼び出し、建てたサーバから価格情報を取得 返ってきたjsonは何らかのパーサーでクラスとかにする

・アクセストークンの取得  「XXXX,YYYY」はBitpointのLogin時のメールアドレスとパスワード Assets/ApiLogin.cs

    public class ApiLogin
    {
        public static Coroutine Request( ExchangeBitpoint exchange, UnityAction<string> onResponse )
        {
            var sha256 = SHA256.Create();
            var hash = sha256.ComputeHash( Encoding.ASCII.GetBytes( YYYY ) );
            var password = BitConverter.ToString( hash ).Replace("-", "").ToLower();

            var param = new Dictionary<string, string>();
            param[ "username" ] = XXXX;
            param[ "password" ] = password;

            var query = exchange.GenerateFormParameter( param );

            var api = "/login";
            return exchange.Request( RequestType.Get, api + '?' + query, null, onResponse );
        }

        public class Response
        {
            public string access_token { get; set; }
        }
    }

Assets/ApiLeverageTwoWay.cs

    public class ApiLeverageTwoWay
    {
        public static Coroutine Request( ExchangeBitpoint exchange, string accessToken, UnityAction<string> onResponse )
        {
            var query = "{ \"currencyCd1\":\"BTC\", \"currencyCd2\":\"JPY\" }";

            var api = "/leverageTwoWay";
            return exchange.Request( RequestType.RelayPost, api + "?access_token=" + accessToken, query, onResponse );
        }

        public class Response
        {
            public class Datum
            {
                public string currencyCd1 { get; set; }
                public string currencyCd2 { get; set; }
                public decimal seq { get; set; }
                public decimal buySellCls { get; set; }
                public string indicator { get; set; }
                public decimal price { get; set; }
                public decimal openPrice { get; set; }
                public decimal highPrice { get; set; }
                public decimal lowPrice { get; set; }
                public decimal preClosePrice { get; set; }
                public string priceDate { get; set; }
                public string openPriceDate { get; set; }
                public string highPriceDate { get; set; }
                public string lowPriceDate { get; set; }
                public string preClosePriceDate { get; set; }
                public decimal change { get; set; }
                public decimal changeRate { get; set; }
            }
            public string updatetime { get; set; }
            public string error { get; set; }
            public List<Datum> data { get; set; }
        }
    }

Assets/ExchangeBitpoint.cs ・「RequestType.RelayPost」でリクエストが呼ばれたときは自分が建てたサーバが呼ばれるようにしている  XXXXにサーバのIpアドレスを入れる

using System;
using System.Collections;
using System.Linq;
using System.Net;
using System.Net.Security;
using System.Text;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Networking;

public enum RequestType
{
    Get,
    Post,
    Delete,
    RelayPost,  //Bitpoint
}

public class ExchangeBitpoint : MonoBehaviour
{
    private RemoteCertificateValidationCallback _remoteCertificateValidationCallback = new RemoteCertificateValidationCallback( ( sender, certificate, chain, sslPolicyErrors ) => { return true; } );

    public string GenerateFormParameter( Dictionary<string, string > dictForm )
    {
        string formParam = "";
        var count = dictForm.Count;
        var index = 0;
        foreach( var pair in dictForm )
        {
            formParam += pair.Key + "=" + pair.Value;
            index++;
            if( index < count )
                formParam += "&";
        }
        return formParam;
    }

    public Coroutine Request( RequestType type, string path, object query, UnityAction<string> onResponse, int timeout = 0 )
    {
        return StartCoroutine( OpRequest( type, path, query as string, onResponse, timeout ) );
    }

    protected IEnumerator OpRequest( RequestType type, string path, string query, UnityAction<string> onResponse, int timeout )
    {
        var url = "";
        if( type != RequestType.RelayPost )
            url = "https://public.bitpoint.co.jp/bpj-api" + path;
        else
            url = "http://XXXX:8080" + path;

        var prevCertCallback = ServicePointManager.ServerCertificateValidationCallback;
        ServicePointManager.ServerCertificateValidationCallback = _remoteCertificateValidationCallback;
        var httpRequest = (System.Net.HttpWebRequest)System.Net.WebRequest.Create( url );
        switch( type )
        {
            case RequestType.Get:
                httpRequest.Method = "GET";
                break;
            default:
                httpRequest.Method = "POST";
                httpRequest.ContentType = "application/json";
                byte[] bodyRaw = ( query != null ) ? Encoding.UTF8.GetBytes( query ) : null;
                httpRequest.ContentLength = bodyRaw.Length;
                var isRequest = false;
                httpRequest.BeginGetRequestStream( ( ar ) =>
                {
                    System.IO.Stream reqStream = httpRequest.EndGetRequestStream( ar );
                    //送信するデータを書き込む
                    reqStream.Write( bodyRaw, 0, bodyRaw.Length );
                    reqStream.Close();
                    isRequest = true;
                }, null );
                while( isRequest == false )
                    yield return null;
                break;
        }

        string text = null;
        bool isResponse = false;
        httpRequest.BeginGetResponse( ( ar ) =>
        {
            var response = (System.Net.HttpWebResponse)httpRequest.EndGetResponse( ar );
            var statusCode = (int)response.StatusCode;

            System.IO.Stream resStream = response.GetResponseStream();
            //受信して表示
            System.IO.StreamReader sr = new System.IO.StreamReader( resStream, System.Text.Encoding.UTF8 );
            text = sr.ReadToEnd();
            sr.Close();
        //  response.Close();
            isResponse = true;
        }, null );

        while( isResponse == false )
            yield return null;

        ServicePointManager.ServerCertificateValidationCallback = prevCertCallback;
        if( onResponse != null )
            onResponse( text );
        yield break;
    }
}

参考

DEVGRU

Deploygateにアップロードする

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

環境

Unity2018.3.0f2 2017.2.0p4

概要

usb接続してAndroidビルドすると転送エラーになったりすることが頻繁に起こるのでDeploygateにアップするようにしたエディタ拡張です。

プログラム

2018.3.0f2対応。さらに名前とアイコンを固定にした

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using UnityEngine.Networking;
using System.IO;
using System;
using UnityEditor.Build;
using UnityEditor.Callbacks;
using UnityEditor.Build.Reporting;

public class EditorDeploygateUploader : EditorWindow, IPostprocessBuildWithReport, IPreprocessBuildWithReport
{
    public class Param : ScriptableSingleton<Param>
    {
        public IEnumerator opFunc = null;   //保存されないのでここにある意味は特にない
        public int state = 0;
        public string path;
        public string productName = null;
        public Texture2D[] icons = null;
    }

    [ MenuItem( "Tools/Deploygate/Open" ) ]
    static void OnMenuOpen()
    {
        var window = EditorWindow.GetWindow<EditorDeploygateUploader>( "EditorDeploygateUploader" );
        window.ShowPopup();
    }

    private void OnEnable()
    {
    }

    private void OnDisable()
    {
    }

    private void OnGUI()
    {
        using( var verticalScope = new EditorGUILayout.VerticalScope( "Common" ) )
        {
            EditorGUILayout.LabelField( "AccountName" );
            var prevAccountName = EditorPrefs.GetString( "DeploygateAccountName", "" );
            var accountName = EditorGUILayout.TextField( prevAccountName );
            if( prevAccountName != accountName )
                EditorPrefs.SetString( "DeploygateAccountName", accountName );

            EditorGUILayout.LabelField( "ApiKey" );
            var prevKey = EditorPrefs.GetString( "DeploygateApiKey", "" );
            var key = EditorGUILayout.TextField( prevKey );
            if( prevKey != key )
                EditorPrefs.SetString( "DeploygateApiKey", key );
        }
    }


    [DidReloadScripts]
    private static void OnReloadScripts()
    {
        Debug.Log( "OnReloadScripts" );
        if( ScriptableSingleton<Param>.instance.state == 0 )
            return;
        EditorApplication.update += OnUpdate;
        ScriptableSingleton<Param>.instance.opFunc = OpUpload( ScriptableSingleton<Param>.instance.path );
    }

    private static void OnUpdate()
    {
        if( ScriptableSingleton<Param>.instance.opFunc == null )
            return;
        if( ScriptableSingleton<Param>.instance.opFunc.MoveNext() == false )
        {
            ScriptableSingleton<Param>.instance.state = 0;
            ScriptableSingleton<Param>.instance.opFunc = null;
            EditorApplication.update -= OnUpdate;
            Debug.Log( "End Update" );
        }
    }

    private static void Upload( string path )
    {
        ScriptableSingleton<Param>.instance.state = 1;
        ScriptableSingleton<Param>.instance.path = path;
        OnReloadScripts();
    }

    private static IEnumerator OpUpload( string path )
    {
        var accountName = EditorPrefs.GetString( "DeploygateAccountName", "" );
        var apikey = EditorPrefs.GetString( "DeploygateApiKey", "" );
        if(  string.IsNullOrEmpty( accountName ) == true || string.IsNullOrEmpty( apikey ) == true )
        {
            Debug.Log( "Deploygate NoSetting!" );
            yield break;
        }

        Debug.Log( "Begin Upload" );
        var bytes = File.ReadAllBytes( path );
        var form = new WWWForm();
        var fileName = "dummy.apk";//Path.GetFileName( path );
        form.AddBinaryData( "file", bytes, fileName, "application/octet-stream" );

        var url = string.Format( "https://deploygate.com/api/users/{0}/apps", accountName );

        var getData = new Dictionary<string, string>();
        getData[ "token" ] = apikey;
        getData[ "message" ] = "hogehoge" + DateTime.Now.ToString( "yyyyMMddHHmmss" );
        var strFormData = "?";
        foreach( var pair in getData )
        {
            strFormData += pair.Key + "=" +  pair.Value + "&";
        }
        var request = UnityWebRequest.Post( url + strFormData, form );
        request.SendWebRequest();
        while( request.isDone == false )
            yield return null;

        if( string.IsNullOrEmpty( request.error ) == false )
        {
            Debug.Log( request.error );
        }
        var responseJson = request.downloadHandler.text;
        Debug.Log( responseJson );

        request.Dispose();
        Debug.Log( "End Upload" );

        yield break;
    }
    int IOrderedCallback.callbackOrder { get { return 1000; } }
    void IPreprocessBuildWithReport.OnPreprocessBuild(BuildReport report)
    {
        Debug.Log("IPreprocessBuildWithReport.OnPreprocessBuild for target " + report.summary.platform + " at path " + report.summary.outputPath);

        ScriptableSingleton<Param>.instance.productName = PlayerSettings.productName;
        ScriptableSingleton<Param>.instance.icons = PlayerSettings.GetIconsForTargetGroup( BuildTargetGroup.Unknown );
        PlayerSettings.productName = "dep_test";
        var icons = new Texture2D[ ScriptableSingleton<Param>.instance.icons.Length ];
        PlayerSettings.SetIconsForTargetGroup( BuildTargetGroup.Unknown, icons );
    }

    void IPostprocessBuildWithReport.OnPostprocessBuild( BuildReport report )
    {
        Debug.Log("IPostprocessBuildWithReport.OnPostprocessBuild for target " + report.summary.platform + " at path " + report.summary.outputPath);
        Upload( report.summary.outputPath );

        PlayerSettings.productName = ScriptableSingleton<Param>.instance.productName;
        PlayerSettings.SetIconsForTargetGroup(BuildTargetGroup.Unknown, ScriptableSingleton<Param>.instance.icons );
    }
}

2017.2.0p4

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using UnityEngine.Networking;
using System.IO;
using System;
using UnityEditor.Build;
using UnityEditor.Callbacks;

public class EditorDeploygateUploader : EditorWindow, IPostprocessBuild
{
    public class Param : ScriptableSingleton<Param>
    {
        public IEnumerator opFunc = null;   //保存されないのでここにある意味は特にない
        public int state = 0;
        public string path;
    }

    [ MenuItem( "Tools/Deploygate/Open" ) ]
    static void OnMenuOpen()
    {
        var window = EditorWindow.GetWindow<EditorDeploygateUploader>( "EditorDeploygateUploader" );
        window.ShowPopup();
    }

    private void OnEnable()
    {
    }

    private void OnDisable()
    {
    }

    private void OnGUI()
    {
        using( var verticalScope = new EditorGUILayout.VerticalScope( "Common" ) )
        {
            EditorGUILayout.LabelField( "AccountName" );
            var prevAccountName = EditorPrefs.GetString( "DeploygateAccountName", "" );
            var accountName = EditorGUILayout.TextField( prevAccountName );
            if( prevAccountName != accountName )
                EditorPrefs.SetString( "DeploygateAccountName", accountName );

            EditorGUILayout.LabelField( "ApiKey" );
            var prevKey = EditorPrefs.GetString( "DeploygateApiKey", "" );
            var key = EditorGUILayout.TextField( prevKey );
            if( prevKey != key )
                EditorPrefs.SetString( "DeploygateApiKey", key );
        }
    }


    [DidReloadScripts]
    private static void OnReloadScripts()
    {
        Debug.Log( "OnReloadScripts" );
        if( ScriptableSingleton<Param>.instance.state == 0 )
            return;
        EditorApplication.update += OnUpdate;
        ScriptableSingleton<Param>.instance.opFunc = OpUpload( ScriptableSingleton<Param>.instance.path );
    }

    private static void OnUpdate()
    {
        if( ScriptableSingleton<Param>.instance.opFunc == null )
            return;
        if( ScriptableSingleton<Param>.instance.opFunc.MoveNext() == false )
        {
            ScriptableSingleton<Param>.instance.state = 0;
            ScriptableSingleton<Param>.instance.opFunc = null;
            EditorApplication.update -= OnUpdate;
            Debug.Log( "End Update" );
        }
    }

    private static void Upload( string path )
    {
        ScriptableSingleton<Param>.instance.state = 1;
        ScriptableSingleton<Param>.instance.path = path;
    }

    private static IEnumerator OpUpload( string path )
    {
        var accountName = EditorPrefs.GetString( "DeploygateAccountName", "" );
        var apikey = EditorPrefs.GetString( "DeploygateApiKey", "" );
        if(  string.IsNullOrEmpty( accountName ) == true || string.IsNullOrEmpty( apikey ) == true )
        {
            Debug.Log( "Deploygate NoSetting!" );
            yield break;
        }

        Debug.Log( "Begin Upload" );
        var bytes = File.ReadAllBytes( path );
        var form = new WWWForm();
        var fileName = Path.GetFileName( path );
        form.AddBinaryData( "file", bytes, fileName, "application/octet-stream" );

        var url = string.Format( "https://deploygate.com/api/users/{0}/apps", accountName );

        var getData = new Dictionary<string, string>();
        getData[ "token" ] = apikey;
        getData[ "message" ] = "hogehoge" + DateTime.Now.ToString( "yyyyMMddHHmmss" );
        var strFormData = "?";
        foreach( var pair in getData )
        {
            strFormData += pair.Key + "=" +  pair.Value + "&";
        }
        var request = UnityWebRequest.Post( url + strFormData, form );
        request.SendWebRequest();
        while( request.isDone == false )
            yield return null;

        if( string.IsNullOrEmpty( request.error ) == false )
        {
            Debug.Log( request.error );
        }
        var responseJson = request.downloadHandler.text;
        Debug.Log( responseJson );

        request.Dispose();
        Debug.Log( "End Upload" );

        yield break;
    }

    int IOrderedCallback.callbackOrder { get { return 1000; } }
    void IPostprocessBuild.OnPostprocessBuild( BuildTarget target, string path )
    {
        Debug.Log( "OnPostprocessBuild:" + target + "path:" + path );
        Upload( path );
    }
}

データディレクトリやキャッシュディレクトリをエクスプローラで開く

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

環境

Unity2017.1.1p3

概要

persistentDataPathやCacheのパスを忘れてしまうのでメニューから開けるようにしました。 それだけです。

プログラム

Assets/Editor/ShowInExplorer.cs

using UnityEditor;
using UnityEngine;

public class ShowInExplorer
{
    [MenuItem( "Tools/ShowInExplorer/PersistentDataPath" )]
    private static void OpenPersistentDataPath()
    {
        var path = Application.persistentDataPath;
        RevealInFinder( path );
    }

    [MenuItem( "Tools/ShowInExplorer/DataPath" )]
    private static void OpenDataPath()
    {
        var path = Application.dataPath;
        RevealInFinder( path );
    }

    [MenuItem( "Tools/ShowInExplorer/TemporaryCachePath" )]
    private static void OpenTemporaryCachePath()
    {
        var path = Application.temporaryCachePath;
        RevealInFinder( path );
    }

    [MenuItem( "Tools/ShowInExplorer/DefaultCachePath" )]
    private static void OpenDefaultCachePath()
    {
        var path = Caching.defaultCache.path;
        RevealInFinder( path );
    }

    [MenuItem( "Tools/ShowInExplorer/StreamingAssetsPath" )]
    private static void OpenStreamingAssetsPath()
    {
        var path = Application.streamingAssetsPath;
        RevealInFinder( path );
    }

    [MenuItem( "Tools/ShowInExplorer/ApplicationPath" )]
    private static void OpenApplicationPath()
    {
        var path = EditorApplication.applicationPath;
        RevealInFinder( path );
    }

    [MenuItem( "Tools/ShowInExplorer/ApplicationContentsPath" )]
    private static void OpenApplicationContentsPath()
    {
        var path = EditorApplication.applicationContentsPath;
        RevealInFinder( path );
    }

    private static void RevealInFinder( string path )
    {
        Debug.Log( "ShowInExplorer:" + path );
        EditorUtility.RevealInFinder( path );
    }
}

Buttonに設定したClickイベントのコールバック関数内で呼び出し元のButtonの情報を得る

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

環境

Unity2017.1.1p3

概要

インスペクター上でClick用のコールバック関数を設定した場合を想定しています 実際にButtonをClickした時に、何らかの処理中で一時押せないようにしたい場合等に、Button自体の参照が必要ですが、コールバック関数には渡ってきません 「EventSystem.current.currentSelectedGameObject」というのがありました 知りませんでした

プログラム

    public void OnButtonClick()
    {
        var button = EventSystem.current.currentSelectedGameObject.GetComponent<Button>();
        Debug.Log( "OnButtonClick:" + button.name );
    }

RectTransformの矩形からスクリーン矩形を得る

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

環境

Unity2017.1.0f3

概要

RectTransformの矩形からスクリーン矩形に変換します 主にWebViewの表示領域に使用します

コガネブログさんが作成したのを使用すると良いと思います。 http://baba-s.hatenablog.com/entry/2019/06/21/090000

GreeのWebView

GreeのWebViewをWindowsのUnityEditorである程度動作させるとっかかり

プログラム

using UnityEngine;

public class TestRectTransformToScreenSpaceRect : MonoBehaviour
{
    [SerializeField] RectTransform _rectTrans = null;

    private Rect _screenRect;

    private void Update ()
    {
        _screenRect = CalcurateScreenSpaceRect( _rectTrans );
    }

    private static Rect CalcurateScreenSpaceRect( RectTransform rectTrans )
    {
        var canvas = rectTrans.GetComponentInParent<Canvas>();
        var camera = canvas.worldCamera;
        var corners = new Vector3[ 4 ];
        //左下、左上、右上、右下
        rectTrans.GetWorldCorners( corners );
        var screenCorner1 = RectTransformUtility.WorldToScreenPoint( camera, corners[ 1 ] );
        var screenCorner3 = RectTransformUtility.WorldToScreenPoint( camera, corners[ 3 ] );

        //左下基準
        var screenRect = new Rect();
        screenRect.x = screenCorner1.x;
        screenRect.width = screenCorner3.x - screenRect.x;
        screenRect.y = screenCorner3.y;
        screenRect.height = screenCorner1.y - screenRect.y;
#if false
        //左、上、右、下
        var margin = new Vector4( screenCorner1.x, Screen.height - screenCorner1.y, Screen.width - screenCorner3.x, screenCorner3.y );
        Debug.Log("margin x:" + margin.x + "y:" + margin.y + "z:" + margin.z + "w:" + margin.w );
        _WebView.SetMargins( (int)margin.x, (int)margin.y, (int)margin.z, (int)margin.w ); 
#endif
        return screenRect;
    }

    private static Texture2D _debugTexture = null;
    private void OnGUI()
    {
        if( _debugTexture == null )
        {
            _debugTexture = new Texture2D( 4, 4, TextureFormat.ARGB32, false );
            for (int y = 0; y < _debugTexture.height; y++) {
                for (int x = 0; x < _debugTexture.width; x++) {
                    Color color = new Color( 1.0f, 0.0f, 0.0f, 0.5f );
                    _debugTexture.SetPixel(x, y, color);
                }
            }
            _debugTexture.Apply();
        }
        //こっちは左上基準
        var rect = _screenRect;
        rect.yMax = Screen.height - _screenRect.yMin;
        rect.yMin = rect.yMax - _screenRect.height;
        GUI.DrawTexture( rect, _debugTexture );
    }
}

GreeのWebViewをWindowsのUnityEditorである程度動作させるとっかかり

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

環境

Unity2017.1.0f3 Windows10 Home

概要

GreeのWebViewがWindowsのUnityEditor上で動作しないのでEditorWebViewを使用してある程度動作させるようにするためのとっかかり的なものです GreeのWebViewについてくるSample.unityはWeb上のボタンが押されたら、画面にフィードバックしますが、それが動作するようにしています

GreeのWebView

https://github.com/gree/unity-webview

プログラム

そこそこあるのでgithubにアップ 以下は追加、修正したファイル Assets/StreamingAssets/unity_call.js Assets/Plugins/MyWeb Assets/Plugins/GreeWebViewEditorWindow.cs Assets/Plugins/WebViewObject.cs(修正)

https://github.com/fukaken5050/TestGreeEditorWebView

参考

http://qiita.com/kyusyukeigo/items/71db22676c6f4743913e

MeshからVoxel生成

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

環境

Unity2017.1.0f3

概要

MeshからVoxelを生成します モデルにAddComponentして使用 とりあえずCubeモデルで生成

Taskを使用した非同期処理

http://qiita.com/fukaken5050/items/52f0261f1da33493359f

プログラム

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

[RequireComponent( typeof( MeshFilter) )]
public class MeshToVoxel : MonoBehaviour
{
    public GameObject cubeObjPrefab = null;
    private Bounds _bounds;
    private float _unitSize = 0.05f;
    private int[,,] _voxels = null;

    public class Triangle
    {
        public Vector3 p1, p2, p3;
        public Triangle( Vector3 p1, Vector3 p2, Vector3 p3 )
        {
            this.p1 = p1;
            this.p2 = p2;
            this.p3 = p3;
        }
    }

    private IEnumerator Start()
    {
        var meshFilter = GetComponent<MeshFilter>();
        Build( meshFilter.mesh );
        yield break;
    }

    public void Build( Mesh mesh )
    {
        mesh.RecalculateBounds();
        var bounds = mesh.bounds;
        _bounds = new Bounds();

        var min = new Vector3( float.MaxValue, float.MaxValue, float.MaxValue );
        var max = new Vector3( float.MinValue, float.MinValue, float.MinValue );
        var vertices = new Vector3[ mesh.vertices.Length ];
        for( int i = 0; i < vertices.Length; i++ )
        {
            vertices[ i ] = transform.TransformPoint( mesh.vertices[ i ] );

            if( min.x > vertices[ i ].x )
                min.x = vertices[ i ].x;
            if( min.y > vertices[ i ].y )
                min.y = vertices[ i ].y;
            if( min.z > vertices[ i ].z )
                min.z = vertices[ i ].z;

            if( max.x < vertices[ i ].x )
                max.x = vertices[ i ].x;
            if( max.y < vertices[ i ].y )
                max.y = vertices[ i ].y;
            if( max.z < vertices[ i ].z )
                max.z = vertices[ i ].z;
        }

        min = RoundDown( min, _unitSize );
        max = RoundUp( max, _unitSize );
        if( max.x == min.x )
            max.x += _unitSize;
        if( max.y == min.y )
            max.y += _unitSize;
        if( max.z == min.z )
            max.z += _unitSize;

        _bounds.min = min;
        _bounds.max = max;
        _bounds.size = _bounds.max - _bounds.min;


        var indices = mesh.triangles;
        var triangles = new List<Triangle>();
        for( int i = 0; i < indices.Length; i += 3 )
        {
            var v0 = vertices[ indices[ i + 0 ] ];
            var v1 = vertices[ indices[ i + 1 ] ];
            var v2 = vertices[ indices[ i + 2 ] ];

            var triangle = new Triangle( v0, v1, v2 );
            triangles.Add( triangle );
        }

        var counts = new Vector3();
        counts = _bounds.size / _unitSize; //_unitSizeで揃えてあっても、必ず割り切れるわけじゃない Mathf.RoundToIntでintにする
        Debug.Log( "cx:" + counts.x + "cy:" + counts.y + "cz:" + counts.z );
        Debug.Log( "icx:" + (int)counts.x + "icy:" + (int)counts.y + "icz:" + (int)counts.z );
        try
        {
            _voxels = new int[ Mathf.RoundToInt( counts.y ), Mathf.RoundToInt( counts.z ), Mathf.RoundToInt( counts.x ) ];
        }
        catch
        {
            throw;
        }
        var task = TaskBuildVoxels( triangles );
    }

    private async Task TaskBuildVoxels( List<Triangle> triangles )
    {
        var tasks = new List<Task>();
        var unitSizeHalf = _unitSize * 0.5f;

        for( var y = 0; y < _voxels.GetLength( 0 ); y++ )
        {
            tasks.Clear(); 
            for( var z = 0; z < _voxels.GetLength( 1 ); z++ )
            {
                for( var x = 0; x < _voxels.GetLength( 2 ); x++ )
                {
                    var ix = x;
                    var iy = y;
                    var iz = z;
                    var task = Task.Run(() => 
                    {
                        var center = new Vector3( ix * _unitSize + unitSizeHalf, iy * _unitSize + unitSizeHalf, iz * _unitSize + unitSizeHalf ); 
                        center += _bounds.min;
                        for( var t = 0; t < triangles.Count; t++ )
                        {
                            if( AABBTriangleOverlaped( center, _unitSize, triangles[ t ] ) == true )
                            {
                                _voxels[ iy, iz, ix ] = 1;
                                break;
                            }
                        }
                    });
                    tasks.Add( task );
                }
            }
            //すべてのタスクが完了するまで待つ
            await Task.WhenAll( tasks );
        }
    }

#if true
    void OnDrawGizmos()
    {
        Gizmos.DrawWireCube( _bounds.center, _bounds.size );
    }
#endif

    void Update()
    {
        if( _voxels == null )
            return;
        Gizmos.color = Color.red;
        var unitSizeHalf = _unitSize * 0.5f;
        var cubeSize = new Vector3( _unitSize, _unitSize, _unitSize );

        for( var y = 0; y < _voxels.GetLength( 0 ); y++ )
        {
            for( var z = 0; z < _voxels.GetLength( 1 ); z++ )
            {
                for( var x = 0; x < _voxels.GetLength( 2 ); x++ )
                {
                    var center = new Vector3( x * _unitSize + unitSizeHalf, y * _unitSize + unitSizeHalf, z * _unitSize + unitSizeHalf );
                    center += _bounds.min;

                    if( _voxels[ y, z, x ] == 1 )
                    {
                        _voxels[ y, z, x ] = 2;
                        var obj = Instantiate<GameObject>( cubeObjPrefab );
                        obj.transform.SetParent( transform, false );
                        obj.transform.position = center;
                        var localScale = ( _unitSize * Vector3.one );
                        localScale.x /= obj.transform.lossyScale.x;
                        localScale.y /= obj.transform.lossyScale.y;
                        localScale.z /= obj.transform.lossyScale.z;
                        obj.transform.localScale = localScale;
                        obj.transform.rotation = Quaternion.identity;
                    }
                }
            }
        }
    }

    private float RoundUp( float value, float length )
    {
        var frac = Mathf.Repeat( value, length );
        var nv = ( frac > 0.0f ) ? value + ( length - frac ) : value;
        Debug.Log( "ru nv:" + nv + "v:" + value + "r:" + length );
        return nv;
    }

    private Vector3 RoundUp( Vector3 value, float length )
    {
        value.x = RoundUp( value.x, length );
        value.y = RoundUp( value.y, length );
        value.z = RoundUp( value.z, length );
        return value;
    }

    private float RoundDown( float value, float length )
    {
        var frac = Mathf.Repeat( value, length );
        var nv = value - frac;
        Debug.Log( "rd nv:" + nv + "v:" + value + "l:" + length );
        return nv;
    }

    private Vector3 RoundDown( Vector3 value, float length )
    {
        value.x = RoundDown( value.x, length );
        value.y = RoundDown( value.y, length );
        value.z = RoundDown( value.z, length );
        return value;
    }

    private bool AABBTriangleOverlaped( Vector3 voxelCenter, float voxelSize, Triangle triangle )
    {
        Vector3 boxcenter = voxelCenter;
        Vector3 halfboxsize = new Vector3( voxelSize * 0.5f, voxelSize * 0.5f, voxelSize * 0.5f );
        Vector3 v1, v2, v3, normal, e1, e2, e3;
        float min, max, d, p1, p2, p3, rad, fex, fey, fez;

        v1 = triangle.p1;
        v2 = triangle.p2;
        v3 = triangle.p3;

        v1 -= boxcenter;
        v2 -= boxcenter;
        v3 -= boxcenter;

        e1 = v2;
        e2 = v3;
        e3 = v1;

        e1 -= v1;
        e2 -= v2;
        e3 -= v3;

        fex = Mathf.Abs( e1.x );
        fey = Mathf.Abs( e1.y );
        fez = Mathf.Abs( e1.z );

        if( AXISTEST_X01(e1.z, e1.y, fez, fey, out p1, ref v1, out p3, ref v3, out min, out max, out rad, ref halfboxsize ) == false )
            return false;
        if( AXISTEST_Y02(e1.z, e1.x, fez, fex, out p1, ref v1, out p3, ref v3, out min, out max, out rad, ref halfboxsize ) == false )
            return false;
        if( AXISTEST_Z12(e1.y, e1.x, fey, fex, out p2, ref v2, out p3, ref v3, out min, out max, out rad, ref halfboxsize ) == false )
            return false;

        fex = Mathf.Abs( e2.x );
        fey = Mathf.Abs( e2.y );
        fez = Mathf.Abs( e2.z );

        if( AXISTEST_X01(e2.z, e2.y, fez, fey, out p1, ref v1, out p3, ref v3, out min, out max, out rad, ref halfboxsize ) == false )
            return false;
        if( AXISTEST_Y02(e2.z, e2.x, fez, fex, out p1, ref v1, out p3, ref v3, out min, out max, out rad, ref halfboxsize ) == false )
            return false;
        if( AXISTEST_Z0(e2.y, e2.x, fey, fex, out p1, ref v1, out p2, ref v2, out min, out max, out rad, ref halfboxsize ) == false )
            return false;

        fex = Mathf.Abs( e3.x );
        fey = Mathf.Abs( e3.y );
        fez = Mathf.Abs( e3.z );

        if( AXISTEST_X2(e3.z, e3.y, fez, fey, out p1, ref v1, out p2, ref v2, out min, out max, out rad, ref halfboxsize ) == false )
            return false;
        if( AXISTEST_Y1(e3.z, e3.x, fez, fex, out p1, ref v1, out p2, ref v2, out min, out max, out rad, ref halfboxsize ) == false )
            return false;
        if( AXISTEST_Z12(e3.y, e3.x, fey, fex, out p2, ref v2, out p3, ref v3, out min, out max, out rad, ref halfboxsize ) == false )
            return false;

        VX_FINDMINMAX(v1.x, v2.x, v3.x, out min, out max);
        if ( min > halfboxsize.x || max < -halfboxsize.x )
        {
            return false;
        }

        VX_FINDMINMAX(v1.y, v2.y, v3.y, out min, out max);
        if ( min > halfboxsize.y || max < -halfboxsize.y )
        {
            return false;
        }

        VX_FINDMINMAX(v1.z, v2.z, v3.z, out min, out max);
        if ( min > halfboxsize.z || max < -halfboxsize.z )
        {
            return false;
        }

        normal = Vector3.Cross( e1, e2 );
        d = - Vector3.Dot( normal, v1 );

        if ( !vx__plane_box_overlap( normal, d, halfboxsize ) )
        {
            return false;
        }

        return true;
    }
    private bool AXISTEST_X01( float a, float b, float fa, float fb, out float p1, ref Vector3 v1, out float p3, ref Vector3 v3, out float min, out float max, out float rad, ref Vector3 halfboxsize )
    {
        p1 = a * v1.y - b * v1.z;
        p3 = a * v3.y - b * v3.z;
        if ( p1 < p3 )
        {
            min = p1;
            max = p3;
        }
        else
        {
            min = p3;
            max = p1;
        }
        rad = fa * halfboxsize.y + fb * halfboxsize.z;
        if ( min > rad || max < -rad) {
            return false;
        }
        return true;
    }
    private bool AXISTEST_X2( float a, float b, float fa, float fb, out float p1, ref Vector3 v1, out float p2, ref Vector3 v2, out float min, out float max, out float rad, ref Vector3 halfboxsize )
    {
        p1 = a * v1.y - b * v1.z;
        p2 = a * v2.y - b * v2.z;
        if ( p1 < p2 )
        {
            min = p1;
            max = p2;
        }
        else
        {
            min = p2;
            max = p1;
        }
        rad = fa * halfboxsize.y + fb * halfboxsize.z;
        if (min > rad || max < -rad)
        {
            return false;
        }
        return true;
    }
    private bool AXISTEST_Y02( float a, float b, float fa, float fb, out float p1, ref Vector3 v1, out float p3, ref Vector3 v3, out float min, out float max, out float rad, ref Vector3 halfboxsize )
    {
        p1 = -a * v1.x + b * v1.z;
        p3 = -a * v3.x + b * v3.z;
        if (p1 < p3) {
            min = p1;
            max = p3;
        }
        else
        {
            min = p3;
            max = p1;
        }
        rad = fa * halfboxsize.x + fb * halfboxsize.z;
        if (min > rad || max < -rad)
        {
            return false;
        }
        return true;
    }
    private bool AXISTEST_Y1( float a, float b, float fa, float fb, out float p1, ref Vector3 v1, out float p2, ref Vector3 v2, out float min, out float max, out float rad, ref Vector3 halfboxsize )
    {
        p1 = -a * v1.x + b * v1.z;
        p2 = -a * v2.x + b * v2.z;
        if ( p1 < p2 )
        {
            min = p1; max = p2;
        }
        else
        {
            min = p2; max = p1;
        }
        rad = fa * halfboxsize.x + fb * halfboxsize.z;
        if ( min > rad || max < -rad )
        {
            return false;
        }
        return true;
    }
    private bool AXISTEST_Z12( float a, float b, float fa, float fb, out float p2, ref Vector3 v2, out float p3, ref Vector3 v3, out float min, out float max, out float rad, ref Vector3 halfboxsize )
    {
        p2 = a * v2.x - b * v2.y;
        p3 = a * v3.x - b * v3.y;
        if (p3 < p2)
        {
            min = p3; max = p2;
        }
        else
        {
            min = p2; max = p3;
        }
        rad = fa * halfboxsize.x + fb * halfboxsize.y;
        if (min > rad || max < -rad)
        {
            return false;
        }
        return true;
    }
    private bool AXISTEST_Z0( float a, float b, float fa, float fb, out float p1, ref Vector3 v1, out float p2, ref Vector3 v2, out float min, out float max, out float rad, ref Vector3 halfboxsize )
    {
        p1 = a * v1.x - b * v1.y;
        p2 = a * v2.x - b * v2.y;
        if (p1 < p2)
        {
            min = p1;
            max = p2;
        }
        else
        {
            min = p2;
            max = p1;
        }
        rad = fa * halfboxsize.x + fb * halfboxsize.y;
        if (min > rad || max < -rad)
        {
            return false;
        }
        return true;
    }
    private void VX_FINDMINMAX( float x0, float x1, float x2, out float min, out float max )
    {
        min = max = x0;
        if (x1 < min) min = x1;
        if (x1 > max) max = x1;
        if (x2 < min) min = x2;
        if (x2 > max) max = x2;
    }
    private bool vx__plane_box_overlap( Vector3 normal, float d, Vector3 halfboxsize )
    {
        Vector3 vmin, vmax;

        if ( normal.x > 0.0f )
        {
            vmin.x = -halfboxsize.x;
            vmax.x = halfboxsize.x;
        }
        else
        {
            vmin.x = halfboxsize.x;
            vmax.x = -halfboxsize.x;
        }
        if ( normal.y > 0.0f )
        {
            vmin.y = -halfboxsize.y;
            vmax.y = halfboxsize.y;
        }
        else
        {
            vmin.y = halfboxsize.y;
            vmax.y = -halfboxsize.y;
        }
        if ( normal.z > 0.0f )
        {
            vmin.z = -halfboxsize.z;
            vmax.z = halfboxsize.z;
        }
        else
        {
            vmin.z = halfboxsize.z;
            vmax.z = -halfboxsize.z;
        }

        if ( Vector3.Dot( normal, vmin ) + d > 0.0f )
        {
            return false;
        }

        if ( Vector3.Dot( normal, vmax ) + d >= 0.0f )
        {
            return true;
        }

        return false;
    } 
}

参考

https://github.com/karimnaaji/voxelizer/blob/master/voxelizer.h https://mgerhardy.gitlab.io/engine/voxelizer_8h_source.html