もーやんなっちゃうよね~っていう時に生まれた「ふーちゃん」
いやー,なんかなーやってらんないよねー
っていうときに「ふーちゃん」は生まれた
幽霊なのか,妖精なのか,妖怪なのか...
自分の心を表す存在,ふーちゃん
これからよろしくお願いします
サラリーマンやーめた
よし、人間になろう
UnityのAssetStoreにプログラムを提出した
UnityのAssetStoreに自作したプログラムを提出しました.審査が通れば販売できます.私のプログラムのレビューが返ってくると思うので,それを確認して技術向上を図りたいと思っています.
審査に2週間かかるとのことです.さて,次の作品を作ろう.
<C#, Unity, Eigen> C#で行列演算ライブラリEigenの機能を使う
Unityで開発している際,4次以上の逆行列の演算が必要になったのでC#で使える行列演算ライブラリを探していました.しかしながら,使えそうなライブラリが見つからず,C++で扱える行列演算ライブラリ"Eigen"をC#で利用しようと考えました.以下,詳細です.
開発環境
- Windows7(x64)
- Visual Studio 2015
- Unity 5.4.1(x64)
概要
Eigenの機能をDLLとして出力
以下,サンプルコードです.Visual StudioのDLLプロジェクトを新規作成し,サンプルコードを追加後,ビルドしてDLLファイルを作成します.ちなみに今回のプロジェクト名は"EigenFuncs"としています.Unityの環境に合わせて32bitもしくは64bitでビルドします.
.hファイル
/*--------------*/ /* EigenFuncs.h */ /*--------------*/ #pragma once extern "C" { #ifdef EIGENFUNCS_EXPORTS #define EIGEN_FUNCS_API __declspec(dllexport) #else #define EIGEN_FUNCS_API __declspec(dllimport) #endif EIGEN_FUNCS_API void InverseMat_FullPivLU(int dim, float a[], float ans[]); } // extern "C"
.cppファイル
/*----------------*/ /* EigenFuncs.cpp */ /*----------------*/ // EigenFuncs.cpp : DLL アプリケーション用にエクスポートされる関数を定義します。 // #include "stdafx.h" #include <stdio.h> #include <string.h> #include <vector> #include <Eigen/Core> #include <Eigen/Dense> #include <Eigen/Geometry> #include <Eigen/LU> #include "EigenFuncs.h" extern "C" { EIGEN_FUNCS_API void InverseMat_FullPivLU(int dim, float a[], float ans[]) { Eigen::MatrixXf mat = Eigen::MatrixXf::Zero(dim, dim); int count = 0; for (int c = 0; c < dim; c++) { for (int r = 0; r < dim; r++) { mat(c, r) = a[count]; count++; } } Eigen::FullPivLU< Eigen::MatrixXf > lu(mat); Eigen::MatrixXf InvMat = mat.inverse(); count = 0; for (int c = 0; c < dim; c++) { for (int r = 0; r < dim; r++) { ans[count] = InvMat(c, r); count++; } } } } // extern "C"
DLLファイルをUnityのPluginsフォルダに置く
以下のようにする.Pluginsフォルダが無い場合は新規作成する.
Assets/Plugins/EigenFuncs.dll
C#でDLLのWrapperを書く
以下,DLLの関数をC#側で呼び出し,逆行列演算をする関数を実装したものです.
/*--------------*/ /* EigenFunc.cs */ /*--------------*/ using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using System.Text; using System.Runtime.InteropServices; //Dll public class EigenFunc { [DllImport("EigenFuncs")] private static extern void InverseMat_FullPivLU(int dim, float[] a, float[] ans); private void Matrix2Array(float[,] Mat, ref float[] Arr) { int count = 0; int dim = (int)Mathf.Sqrt(Mat.Length); for (int c = 0; c < dim; c++) { for (int r = 0; r < dim; r++) { Arr[count] = Mat[c, r]; count++; } } } private void Array2Matrix(float[] Arr, float[,] Mat) { int count = 0; int dim = (int)Mathf.Sqrt(Mat.Length); for (int c = 0; c < dim; c++) { for (int r = 0; r < dim; r++) { Mat[c, r] = Arr[count]; count++; } } } public float[,] InverseMatrix(float[,] Mat) { float[] arr = new float[Mat.Length]; int dim = (int)Mathf.Sqrt(Mat.Length); float[,] AnsMat = new float[dim, dim]; Matrix2Array(Mat, ref arr); float[] ansArr = new float[Mat.Length]; InverseMat_FullPivLU(dim, arr, ansArr); Array2Matrix(ansArr, AnsMat); return (float[,])AnsMat.Clone(); } }
<C#, Unity> class単位のログを取得する(参照渡しで渡さない方法)
C#でclass内のすべてのメンバ変数のログを取得したいと思い単純に以下のようにList型のオブジェクトでログを取得しようとしましたが,Listの中身が一番最後のデータですべて埋め尽くされました.classは参照渡しをされるため,メモリ空間をそのまま渡すことはしていないんですね.
class単位のログの取得に失敗した例
//ログを取得する対象となるclass public class Point { float m_x; float m_y; float m_z; public void AddOne() { m_x += 1.0f; m_y += 1.0f; m_z += 1.0f; } } //ループを回してログを取得する側 public class MainClass { Point m_Point = new Point(); List<Point> m_Log = new List<Point>(); void Loop() { m_Point.AddOne(); //classは参照渡しとして渡されるため, //最後のデータでログが埋め尽くされる m_Log.Add(m_Point); } }
そこで,"MemberwiseClone"を使うと新たにメモリを確保しclassを複製することが出来ます.これを使用した例は以下の通りです.
class単位のログの取得に成功した例
//ログを取得する対象となるclass public class Point : ICloneable { float m_x; float m_y; float m_z; public void AddOne() { m_x += 1.0f; m_y += 1.0f; m_z += 1.0f; } public object Clone(){ return MemberwiseClone(); } } //ループを回してログを取得する側 public class MainClass { Point m_Point = new Point(); List<Point> m_Log = new List<Point>(); void Loop() { m_Point.AddOne(); // classのクローンを作ることでログの取得が可能になる m_Log.Add((Point)m_Point.Clone()); } }