#環境
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();