MoonsharpとLuaとUnityについて学ぶ
の編集
Top
/ MoonsharpとLuaとUnityについて学ぶ
[
トップ
] [
編集
|
差分
|
バックアップ
|
添付
|
リロード
] [
新規
|
一覧
|
単語検索
|
最終更新
|
ヘルプ
]
-- 雛形とするページ --
.git/info/exclude の使い方と活用シーン - 個人的なファイルをローカルだけで除外する方法
10の質問
2024/09月時点でのおすすめAI
ABC予想
AGIアーキテクチャ設計図:自己参照型注意モデル_SRAM
AGI時代の「評価の一元化」が奪う再起の権利
AI
AI API
AI Scheduler MCP導入手順 - Google Tasks/CalendarをMCP経由で操作
AI プロンプト
AIが詳細を避けがちな合法分野
AIでつかわれているtransformerのまとめ
AIとIDEの共存:ドキュメント整合性のための新しいアプローチ
AIとの効果的な協働のための設計アプローチ:S式とコード生成テンプレートの活用
AIと上手に付き合うコツ:「自分らしさ」を失わないために
AIと共存する時代のソフトウェア開発:コンパイラー開発からの学び
AIの男女と美醜について学習の問題点
AIの話題
AIエージェント階層PMシステムのGit基盤選択
AIチャットの文脈を記憶する!Apache UnomiとModel Context Protocolで実現する次世代のAIチャット管理
AI大規模開発とTDDの意外な関係
AI時代でもエンジニアだらけにならない説
AI開発の「いきなり統合」から脱却!層別テスト駆動開発のテンプレート集
AI開発の現在と未来:統計的限界を超えるために
AI関連の自分がよく見るチャンネル
ANTLR
ANTLR v3 FAQ よくある質問
ANTLR 独学
ANTLR4 独学
ANTLRでOracleのDDLを解析してみる
ANTLRチュートリアル
AOP
API
ARMマイコン基盤
ATOM SHELL理論
Access VBAメモ
Access-Control-Allow-Origin
AndroidとTensorflow
Android開発
Android開発 入門
AngularJS
Anko
Apache Bench
ArchUnitを学ぶ
Axiosとは
Axis2
BI Publisherで始めるデータ駆動型レポート作成
BPMNの勉強
BackTrack4
Blog from iPhone
Bootstrapとは
BracketName
C3 AI Applications
C3 AI エクスマキナ
CSS備忘録
CentOS
ChatGPTの話題
Chevrotainのパーサメソッド
Chevrotain一覧
Chromeエクステンション
Claud MCP
Claude CodeがWindows Nativeサポート開始!Claude-Flowで究極のAI駆動開発体験
Claude Codeサブエージェントで実現する「AIチーム開発」
Claude DesktopのNeo4j接続でCypher構文エラーが出る時の対処法
Claude sonnet computer useを実践投入してみる
ClaudeCode のRalph Wiggum Plugin活用テクニック
ClaudeやMCPでGoogle CalendarにTODO(タスク)を記入できるMCPサーバまとめ
Clojureの実行のお作法
Clojureの3万個以上あるライブラリエコシステム
Clojureをつかってみる
Cocoa Touch Static Library
CoffeeScript
Confluent Control Centerやってみる
C言語でオブジェクト志向な記述方法
DDD ドメイン駆動設計
DDL生成ツール
DJUnit
DMM.comのAPIとか
DOSコマンドメモ
DX人材とUMLによる「設計可視化」の実践ガイド
Dashcode
DeepFloyd IF
Dockerが動かない場合の対処
DockerでLillyMolを爆速起動!化学式から合成経路を探る旅に出よう!
DuckDB導入メモ
ES2015
Eclipse Monkey
Eclipse Plugin
Eclipseの色設定
Eclipse使いがXCode使い初めて知りたいこと
ElasticMQメモ
Elixir
Emmet
Erlangメモ
ExcelファイルをAIに読ませる
Exceptionを見やすく
Expression Tree
FLEX
FLEX リフレクション
Firebase App Check
Firebase Emulator Suite
Fisheye
FlashやJavascriptを使った演出
FlutterとReactとOptiWeb
Flutterの開発環境をDockerで整える
FlyonUI
Forgejo MCP環境設定ガイド
FormattingRules
FrontPage
GAE
GAE Data Store API
GENERAL SQL PARSER JAVA を試してみる
GLOBAL
GPT4ALL
GQL
GUIからMacPortsを管理するアプリケーション - Porticus
Generative Adversarial Networks
Gin JavaScriptで構文解析
Git Blame
Git リポジトリのクローンができないときの解決法
GitHubアクションを使ったトロイの木馬のまとめ
GitLab
GitLabRunnerを増やす
GitLabでPlantUML使ってみる
GitLabでプロジェクト管理する
GitLabの機能をそのまま使って認証システム作ったらどこまでできる?
GitLabサーバインストールとメンテ注意事項
GitとAntとSpringとJUnit
Google Antigravity
Google ClientID
Google Cloud Platform
Google Cloud Platform (GCP) と gcloud CLI 入門
Google MCP Toolbox for Databases と BigQuery で Google Sheets を SQL 操作するガイド
Google Maps Platformを学ぶ
GoogleMapレンダリング
Googleの裏技
Google認定プロジェクトマネージャの勉強メモ
Gradioで簡単GUI作成
Grails
GraphHopperを使用した住所のジオコーディング例
GraphQL
HTM 階層型時間メモリ
HTML スクレイピング
HTML パース
HTML5
HTML5 Canvas
Hadoop
Help
If Then Maybe プログラミング
Inkscape script
InterWiki
InterWikiName
InterWikiSandBox
JAVAの記事一覧
JBoss
JDBC テーブル一覧を得る
JDBC カラム一覧を得る
JDT eclipse
JGRIB
JHIPSTER JDL
JHIPSTER OpenAPI
JHIPSTER エンティティをフィルタリングする
JHIPSTER6.1.2
JHIPSTERでスマホサイト
JHIPSTERのBLUEPRINTを作る
JHIPSTER一覧
JHipster
JHipster API FirstDepelop
JHipster エンティティを更新する
JHipster7をつかってみる
JHipsterでBuleprintを使いこなす
JHipsterのコード生成を改造
JHipsterのプロジェクトをGitLabでCI/CDする
JHipsterのプロジェクトをデプロイする
JIRAをAPI使って操作する
JMeter
JOOQとは
JSFとStruts
JSqlParser
Java Closure
Java Compiler API
Java Function
Java SQL Parserを調査する
Java Spring AOP
Java Spriteを設計してみる
Java オブジェクトのダンプ
Java ドラックできる曲線
Java 備忘録
Java 文字化け
Java11以降のJRE
Java7サンプルコード
JavaFx
JavaScriptでパーサを作る Chevrotain
Javaasist 動的にクラスを編集
Javascript グラフィックライブラリ
Javascript コーディングパターン
Javascript界隈
Javassist
JavaでSVG
Javaで関数型で引数をとる
JavaのジェネリクスTip
Javaのラムダ式
Javaの有名なライブラリ紹介
Javaは、IDEのテンプレートを使いこなせばいいよ
Javaプログラマ向けモナド
Javaメモリリーク
Jenkins
Jenkins(Hudson)メモ
Jestとは
Jhipsterマイグレーション
Json Yaml Xml Hash Scala
KIROナレッジ蓄積フォルダ構成
Kafka REST Proxy さわってみる
Kotolin
LDAPサーバをdockerで立ち上げる
LINE Bot AI翻訳システム構築記(2):n8nでMySQL・翻訳API連携を実装する
LISPで自分の言語を作る
LibreOfficeのCalcをハックしてみる
Linux メモ
LiquiBaseとは
Lispの学び
Lombok
MCP「ここまでのチャットを整理して保存しておいて」
MDBをコンパクトにするVBA
MQL5 半値インジケータ作った
MQL5 小作品
MT4
MT5 EA
MT5お気に入りのインジケータ
MYSQL
MYSQLのバックアップとローカル利用
MacTool
Macにしゃべらせる
Mac用のメモ
Mattermostを使ってオンプレミスでチャット環境を作る
Maven
Mementoパターン
MenuBar
MoonsharpとLuaとUnityについて学ぶ
NILScript
Neo4j バックアップ・復元ガイド
Neo4jでシステムダウン!グラフデータベース選択の失敗談と安全な代替案
NetBeanでプロファイル
Network Service Desk Engineer
Nimbalyst活用メモ
Node-RED
Node-Red
Notion MCP関連について学ぶ
NumPy
OQL オブジェクト問い合わせ言語
OSコマンドインジェクション
ObjctiveC サウンド
ObjectMapperの備忘録
ObjectiveC NSString
ObjectiveC サーバ
ObjectiveC ターミナル用コマンドを作る
ObjectiveC バックグラウンド
ObjectiveC ワーニング
Obsidian MCP インストール
Obsidianの使い方:プロ開発者のための必須プラグインガイド
Obsidianは「メモ帳」ではなく「圧縮帳」である
Oculusアプリの開発
OpenAI Swarm Examples Basic
OpenAI Swarmについて学ぶ
OpenAI Swarmについて認識を深める
OpenFeint
OpenOffice
OpenResty
OpenStreetMapを利用した車両ルーティング問題(VRP)のOptaPlanner解決例
OptaPlanner
OptaPlannerとは
OptaWeb
Outlook VBA
PHP
POSTGRESQL
Pandas Python Data Analysis Library
PdfBox Java用PDFライブラリ
Plagger
Playwrightの実用ガイド:MCPとの統合による新たな可能性
PostgreSQL+AGEでNeo4jの代替え環境構築
PrismaでGraphQL APIを自動生成しよう - チュートリアル
PrismaとGraphQLで作るシンプルなAPI - クイックスタート
PukiWiki
PukiWiki/1.4
PukiWiki/1.4/Manual
PukiWiki/1.4/Manual/Plugin
PukiWiki/1.4/Manual/Plugin/A-D
PukiWiki/1.4/Manual/Plugin/E-G
PukiWiki/1.4/Manual/Plugin/H-K
PukiWiki/1.4/Manual/Plugin/L-N
PukiWiki/1.4/Manual/Plugin/O-R
PukiWiki/1.4/Manual/Plugin/S-U
PukiWiki/1.4/Manual/Plugin/V-Z
PyHipster
QuartzCore
RAD
REST
RWKV
Rails3
Railsと差分開発についての考察
React.js
React.js モーダル画面
RecentDeleted
RedmineLE
Redshift
Relumeでサイトの骨格を作る
Require.js
Roo Codeを使う
Rubycocoa
RubyでScalaをコンパイルするツールをつくる
Rubyアソシエーション認定証
Ruby入門
SCALA REPL
SCALA support tool
SCALAの記事一覧
SDL3で始めるクロスプラットフォームゲーム開発 - 環境構築ガイド
SELinux
SEO
SEO Yahoo対策
SEO対策一覧
SPAM対策
SQLite
SRP×A2A×MCP まとめ:kagentとGoogle A2A Project比較メモ
SSH
SST OpenCode:Claude Codeを超える次世代AIコーディングエージェント
SVNをJavaで操作
SakuraZencoding
SandBox
Scala / Hadoop
Scala Process exec
Scala 遅延評価
Scala/LiftでSlim3
ScalaSigParser
ScalaWithExcel
Scala チュートリアル
Scalaで3D
ScalaでLisp
ScalaとGroovyのPOJO比較
ScalaのIDEについて
Scala言語を学ぶやさしいツール「Kojo」
Slack API やってみる
SocketAppender
Spring
Spring bootでのテストのTIPS
SpringBoot-JPA-NotAManagedType解決記録
SpringBootとSeleniumとJunitの連携
SpringBootのSTSの新規プロジェクトでるエラーの対応
SpringSecurity SAML
Sqlite
Squirrel
StringTemplate
Stringクラス拡張
TALEND
ThreadLocal
Todo一覧
Trac Lightning
Twitter
UltraEdit
UnityでClojureCLRをREPLで使う
UnityでClojureCLRを使いたい
Unityでシューティングゲーム作る際のメモ
VBAでREST通信
VBAのコード
VBAをOpenOffice.org Basicにする
VBAをOpenOffice.org+Basicにする
VPN構築の勉強メモ
VPSやIaaSメモ
VSCodeでRuby開発
VSCodeメモ
VSCode用ChatGptのPlugin
VSCode設定
VirtualBox On Mac
Visual Studio Code プラグイン開発
Vuexとは
WBS管理の弊害
WIN32API
WSDL
WSL2 + Podman 環境を快適にする Flatnet CLI を公開しました
Watson
WebDesign探訪
WebLogic フィルタ
WikiEngines
WikiName
WikiWikiWeb
Windows10のPowerShell でキーボードの言語切り替え
WindowsTool
WindowsでRustからGPUアセンブリ(PTX)を生成する
Windows上でOpenCode + MCP連携環境構築 - 実際のハマりポイントと解決法
Windows環境でJavaバージョンを制御する方法 - Java Shimと環境変数の活用
Windsurf
Windsurf能紹介:カスタムワークフロー(Workflow)とファイルベースルール(Rules)紹介
Windsurf+PlantUMLでAWTエラーに遭遇した話
Worker Thread パターン
XBee
XDOCLET
XForms
XPath
XSL
YahooPIPES
Yahooインフォセンター
Yet Another Pragger
YouTuber
Youtubuのあれ
YukiWiki
anacondaをcygwinで使う
ansible
antlr snippet
antlr 再入門
antlrと日本語
autoit
automator
bluemix
bootstrap2
bower
ccze Colorize log files on CentOS and Ubuntu using ccze tool
centos7
cglibを使って動的コード生成
claude-bridgeでローカルLLMを使い放題
cocos2d
collection/collection.dart
cygwin
diff
dockerのローカルイメージをDocker-in-Dockerで参照する
eclipse設定
emacs 備忘録
emacs 文字列置換
emacsをviライクにする
excel tips
excelのdiff
expectで自動化
figmaにプラグインをインストールする
firebase デプロイ
flutterで、google認証させてFirebaseAuthするメモ
flutterをngrok経由で動作させる
flutter環境設定
ftp自動化
gemini
generator-jhipster-gql
git diffを使った構成管理の省力化
goをやってみる
go言語でファイルサーバ
grizzly
gulp
homebrew
iPhone Bluetoothプログラミング
iPhone iAd
iPhone 実機テスト手続き
iPhoneでグラフィックのHellowWorld
iPhoneとGmailメール
iPhoneに実機転送
iPhoneプログラミング
iPhoneプログラミング/ビューを理解すればiPhoneアプリの基礎を押さえられる
iPhoneプログラミング一覧
iPhoneプログラミング入門
iPhone開発/Interface Builder Plug-in
iPhone開発/キャプチャの取り方
intra-mart
jQuery.Flickableのメモ
java spring boot 認証 memo
jersey
jhipster-codeにアノテーション追加してみる
jhipsterのテンプレート改造準備
jparsecドキュメント日本語訳
jparsec入門
kafkaの勉強
log4j2の脆弱性
mac diff
mailcowのインストール
marmaid
mcp-atlassian バージョン互換性の問題と解決方法
memcached
metabaseはダッシュボードなのか
minecraft マイクラ あるきながら、高速ダンジョン作成
mqttの勉強
n8nとDockerでLINE翻訳ボットを作る時に遭遇した5つの罠とその解決法
n8n入門:Docker-composeでWebhook→データ処理→ファイル保存のワークフローを作る
nginx_lua
nginxのメモ
ngrokを利用したLINE Webhookの動的更新 - グローバルIP不要の開発環境構築
node_moduleをnpm linkを使って自分用にする
npm
openapi generator
openapi-generatorをコンパイル
openstack
oraclerac
play framework 1.2.5 sample
play! framework
play!framework selenium
playframework テンプレート
postmanとopenapi
postman使ってみる
prezi プレゼン
pukiwikiで行動管理
pukiwikiに類似したツール
pukiwiki勉強
pukiwiki記事一覧
python
python3のwindowsでの日本語文字化け対応
pythonでseleniumを使う
pythonのテストに使うライブラリ
rails5
rate.jsを使ってみる
reactでポップアップ表示
redmine
ruby on rails 6.0.0
scala
scala 99problem 32~
scala prototype.zip
scala repl
scala sbaz
scala spring
scala/インストール
scalaでまだ不勉強なところ
scalaのインストール
selenium
skills
slack api
spark
spring boot
spring initializerをつかってプロジェクトのひな型をゲットする
spring-test
springboot
springboot env
storybook
sublimetext2
swagger
tracについて
ubuntu
vaadin
vue を typescriptで開発
vue 共通部品作成
vue.js memo
vue.jsとは
vue.jsのデバッグ
vue一覧
webの編集画面のよくあるパターン
windows版のwindsurfのアップデートが失敗する場合、com surrogateが原因かもしれない
windows環境構築
windsurfでフロント開発用プラグイン
wordpress
xamppについて
•Axis2の本家のスタートガイドによるWebサービスの作り方
「AIによる動的実行」と「従来の静的最適化済みコード」が棲み分けられる時代
「AI促進法」国会審議をDXする提案メモ
「Computer use」Claude 3.5 SonnetでPCを操作
【Javascript】【CLIライブラリ】commanderの勉強
【MQL5】KuniRangeBreakoutEA
【初心者必見】テーマだけ決めてスムーズに話せる!動画撮影のコツと練習法
【実践Tips】Node.jsでレスポンス切替型モックAPIを超シンプルに作る方法
いまさらながらC++
おすすめされたフリーソフト
びっくりする短いコード
もう合成ルート探索で迷わない! ASKCOSでスマートに逆合成解析!
アクター
アニメーション
アノテーション
アプリコット
アプリコット PukiWiki
アプリコード
アプリコード林邦行
イラストのエフェクト
インテンショナルプログラミング
オープンソースLSPプロバイダーのMCPであるSerenaの紹介
カスタマイズjhipster7.9.3イメージ
カブロボ
ガイガーカウンター
クラスとハッシュマップの関係
クラック対策
クロス集計
コマンドラインという概念への考察
コマンドラインの出力に色を付ける
コミニュケーション
コラッツ予想:シンプルな数学の問題が隠す深遠な謎
コード生成
サロゲートキーを使ったテーブル設計
シェルのサンプル
シェルサンプル
スクレイピング
スマートコントラクト開発環境Hardhatを学ぶ
スレッドプログラミングメモ
ソースtoソース変形
ターミナルをAppleScriptで制御
テキストエディタ作成javascriptフレームワーク
テスト用まっさらDBをdockerでたてる
テスト駆動
テレワーク環境の比較
ドキュメント指摘AIエージェント定義
ドット絵
ナイアシンと脂質代謝に関する新仮説
ハーネスフォルダを作ってSWE改善
バイオビルダー合成生物学メモ
バグの少ない設計のためのValueObject
パフォーマンスチューニング
フロントエンドとバックエンド(API)を1つのリポジトリで管理するメリット
フロントエンドのテストの結合テストを減らすには?
プッシュ技術
プログラマーじゃない人に覚えてほしいプログラムのコメントの書き方
プロジェクト管理スプレッドシート
プロンプトエンジニア以外のこれからのAI技術者
マイクラ 有名ディメンション モッド
マクスウェル方程式
メタ
ライフハック_選挙を楽しむ方法
ラムダ計算について考える
リベリカJava13いいみたい
リモートワークでのプロジェクト注意点
レイアウトツール
ログ解析
世界の構文解析グラマーたち
予定表
予定表/2009-12-14
予定表/2009-12-18
予定表/2009-12-19
予定表/2009-12-22
予定表/2009-12-23
予定表/2009-12-24
事業の心構え
事業計画方針
五蘊と経営を磨く徳目表
五蘊と経営を磨く徳目表:ウェルビーイング対応一覧
人工知能とCUDA
人工知能コンペKaggle
仕様書のフォーマットについての考察
他言語サイトサンプル作成
仮説Oracleの罠
作曲と効果音作り
僕が無意識に使っていた設計パターンたちに、ちゃんと名前があった話
免疫型社会モデル:性善説でも性悪説でもない第三の道
共和分
効率的なAI活用戦略:S式ベースの問題解決ライブラリの構築
厚黒学から見た日本の構造的脆弱性
口コミ
古いRails5を入れる
哲学
型落ちノートPCでDockerサービスを公開したい
大文字小文字変換
契約書で避けたい条項リスト(エンジニア視点)
学習をHackする
扶養とシステム
投薬のみのガンの治療薬
擬似コーディングのすすめ
放射能対策
数学を学んでいて気づいた物理学との驚くべきつながり
数式を扱う
文章を書く
新エネルギー
新年の抱負2010
新技術 プログラム編
日本のゼネコン式IT開発が失敗する理由
日本半導体産業の敗北から学ぶ経営の本質
最近更新したページ
未来のAIは「私はここまでできます、ここからは専門家にお任せを!」と語りかける
未来技術/新技術
枯れた技術の水平思考
株価データ
業界の動向
構文解析の記事一覧
正規表現
気象データ
流れるようなインタフェース
究極の集中状態を実現する:プログラマーのためのディープワーク実践ガイド
管理画面の生成におけるopenapiとJDLなどの考察
細胞の若返り
経済のことをまとめてみる
脆弱性
脳腫瘍の開発中治療薬LY367385とリンゴ酢
自分でPlaggerみたいなのを作るためのメモ
虚数軸への新たな視点
話せるAIの記事のリンク
論語/学而第一
負荷テスト
販売/デスクトップPC
販売/ノートパソコン
販売/外部ストレージ
起業
超小型ローカルLLM
酸化グラフェン
開発哲学
電子出版
電子出版の記事一覧
非可換幾何学
顧客分析のデシル分析とRFM分析
DIコンテナについて考える
MP3から携帯着うたを作る方法
* 目次 [#d070a7cf] #contents * Moonsharpとは [#df0cc95f] 一言でいえば、レベルデザイン必要なゲームを開発するなら、入れておきなさいツール。 わかりやすくいうと、これを入れない設計でゲームをつくると、「ちょっ変更するだけなのにビルド時間こんなにかかるの?!」という事態になってしまう。 UnityでLuaを使えるようにするプラグインである。 Asset Storeを開き、 MoonSharp と検索すると出てきます。 https://assetstore.unity.com/packages/tools/moonsharp-33776?locale=ja-JP MoonSharpは、.NET、Mono、Xamarin、Unity3Dプラットフォーム向けに完全にC#で書かれた完全なLuaソリューションです。 * 特徴 [#g6a14b51] - Lua 5.2との99%の互換性(サポートされていない唯一の機能はウィークテーブルのサポート) - metaluaスタイルの無名関数(ラムダスタイル)のサポート - 使いやすいAPI - Visual Studio Codeのデバッガサポート(PCLターゲットはサポートされていません) - Webブラウザとフラッシュでアクセス可能なリモートデバッガ(PCLターゲットはサポートされていません) - .NET 3.5、.NET 4.x、.NET Core、Mono、Xamarin、Unity3Dで動作 - iOSのようなAhead-of-timeプラットフォームで動作 - IL2CPPに変換されたコードで動作 - .NET 4.xのポータブルクラスライブラリを必要とするプラットフォーム(Windows Phoneなど)で動作 - 外部の依存関係がなく、可能な限り少ないターゲットで実装 - サポートされている場合、ランタイムコード生成によるCLRオブジェクトとの簡単でパフォーマンスの高いインターオペラビリティ - メソッド、拡張メソッド、オーバーロード、フィールド、プロパティ、インデクサーとのインターオペラビリティがサポートされています - 非常に少ない例外(主に「debug」モジュールにある)と少数の拡張(主に文字列ライブラリ内)を除いて、完全なLua標準ライブラリのサポート - .NET 4.xターゲット用の非同期メソッド - 難読化とランタイム時のより迅速な解析のためのバイトコードのダンプ/ロードをサポート - JSONとLuaテーブルの間で変換するための(依存関係のない)組み込みのJSONパーサー - スクリプトがアクセスできるものをサンドボックス化するための、Lua標準ライブラリモジュールの簡単なオプトアウト - 使いやすいエラー処理(スクリプトエラーは例外です) - コルーチンのサポート(C#イテレータとしてのコルーチンの呼び出しを含む) - REPLインタプリタ、および数行のコードで独自のREPLを簡単に実装するための機能 - http://www.moonsharp.org にある完全なXMLヘルプとチュートリアル * MoonSharpと標準Luaの違い [#e0052ec6] http://www.moonsharp.org/moonluadifferences.html を参照してください。 ダウンロード、情報、チュートリアルなどについては、http://www.moonsharp.org をご覧ください。 *** ライセンス [#p3b97abd] プログラムとライブラリは、3条項BSDライセンスの下でリリースされています - ライセンスセクションを参照してください。 文字列ライブラリの一部は、KopiLuaプロジェクト(https://github.com/NLua/KopiLua)に基づいています。デバッガーのアイコンは、Eclipseプロジェクト(https://www.eclipse.org/)からのものです。 * 使用方法 [#rc0da492] ライブラリの使用は次のように簡単です: double MoonSharpFactorial() { string script = @" -- 階乗関数を定義 function fact (n) if (n == 0) then return 1 else return n*fact(n - 1) end end return fact(5)"; DynValue res = Script.RunString(script); return res.Number; } より詳細なチュートリアル、サンプルなどについては、http://www.moonsharp.org/getting_started.html を参照してください。 ** 公式サイト [#ae77951f] https://www.moonsharp.org/ *** Github [#h2f29b43] https://github.com/moonsharp-devs/moonsharp * チュートリアル [#e115d97d] 本家のチュートリアルを翻訳しました。 ** 元記事 [#m4521d40] https://www.moonsharp.org/getting_started.html ** チュートリアルをはじめるにあたって [#dc75bb7e] このチュートリアルでは、UnityでMoonSharpの簡単さと強力さを感じていただけるでしょう。MoonSharpを使用するには他にも良い方法がありますが、これが最も簡単な方法です。他のチュートリアルではさらに掘り下げて、UnityでMoonSharpを最大限に活用する方法を学ぶことができます。 とりあえず、始めましょう。 このチュートリアルでは、LuaとC#を知っていることを前提としています。Luaの基礎を知らず、C#について十分な知識がないと、サンプルを理解するのは難しいでしょう。 *** ステップ1:UnityでMoonSharpを入手する [#sfc07f66] 最初のステップは、UnityでMoonSharpを入手することです。 Unity Asset Storeのパッケージは現在承認待ちで、承認され次第このURLから入手できるようになります。承認されたら、これを使用するのがUnityでMoonSharpをインストールするのに推奨される方法になります。 MoonSharp配布物のinterpreter/net35フォルダに含まれているMoonSharp.Interpreter.dllをAssets/Pluginsフォルダに入れます。 Windows StoreアプリやWindows Phoneのサポートが必要な場合は、MoonSharp配布物のinterpreter/portable-net40フォルダにあるMoonSharp.Interpreter.dllをAssets/Plugins/WSAフォルダにコピーします。 次に、このガイドに従ってください。 その後、Unity EditorからMoonSharp.Interpreter.dllを参照として追加します。 注:IL2CPPプロジェクトでMoonSharpを使用する場合は、以下の内容でAssetsディレクトリ内にlink.xmlを作成または編集してください。 <linker> <assembly fullname="MoonSharp.Interpreter"> <type fullname="MoonSharp.*" preserve="all" /> </assembly> </linker> *** ステップ2:名前空間のインポート [#m972361d] コードの先頭に以下の行を追加して、MoonSharp.Interpreter名前空間をコードにインポートします。 using MoonSharp.Interpreter; *** ステップ3:スクリプトの呼び出し [#u47bd259] ここでは、MoonSharpを使用して階乗を計算するMoonSharpFactorial関数を作成します。 double MoonSharpFactorial() { string script = @" -- 階乗関数を定義 function fact (n) if (n == 0) then return 1 else return n*fact(n - 1) end end Copy code return fact(5)"; DynValue res = Script.RunString(script); return res.Number; } *** ステップ4:完了! [#nf07c737] もちろん、コードを実行するには、Unity C#スクリプトの他の場所でMoonSharpFactorial関数を呼び出すだけです。 これで、UnityでMoonSharpを使うための準備が整いました。他のチュートリアルに進んで、UnityでMoonSharpを最大限に活用する方法を学びましょう。 ** スクリプトを保持する [#re6a0baf] 前回のチュートリアルでは、MoonSharpに初めて触れました。スクリプトを文字列に入れ、コードを実行し、その出力を取得しました。これは時には便利かもしれませんが、ほとんどのユースケースに必要な相互運用性には、CLRコードとMoonSharpの間でもう少し統合が必要です。 *** ステップ1: 前回のチュートリアルで行ったことをすべてやり直す [#w629e4ff] 真面目な話、ここではもう少し高度な概念を学んでいますが、最初の試みで捨てるべきものはほとんどありません。それは素晴らしい出発点です。 *** ステップ2: コードを変更してScriptオブジェクトを作成する [#vf907cb2] 最初の変更点は、静的メソッドを使用する代わりにScriptオブジェクトを作成することです。ロケット科学ではありませんが、次の進化に向けて準備をしています。 double MoonSharpFactorial() { string scriptCode = @" -- 階乗関数を定義 function fact (n) if (n == 0) then return 1 else return n*fact(n - 1) end end return fact(5)"; Script script = new Script(); DynValue res = script.DoString(scriptCode); return res.Number; } *** ステップ2: グローバル環境にアクセスする [#te57efec] スクリプトオブジェクトを持っているので、関数が動作するグローバル環境を変更できます。 例えば、fact関数への入力を変更して、プログラムがどの数の階乗を計算するかを指定できるようにしたいかもしれません。 double MoonSharpFactorial() { string scriptCode = @" -- 階乗関数を定義 function fact (n) if (n == 0) then return 1 else return n*fact(n - 1) end end return fact(mynumber)"; Script script = new Script(); script.Globals["mynumber"] = 7; DynValue res = script.DoString(scriptCode); return res.Number; } ご覧の通り、script.Globalsテーブルを単純な構文で参照するだけで、数値をMoonSharpスクリプトに注入できます。実際には数値以上のものを渡すことができますが、今のところは数値、ブール値、文字列に限定しましょう。関数やオブジェクトを渡す方法は後で見ていきます。 *** ステップ2: 関数を直接呼び出す [#ya81cef6] 外部から選択した数値の階乗をMoonSharpで計算する方法を学びました。 でも、そのようにやるのは汚いハックのように見えます(それでも、これからよく使う重要なテクニックです)。 以下は、C#からLua関数を呼び出す方法です。 double MoonSharpFactorial2() { string scriptCode = @" -- 階乗関数を定義 function fact (n) if (n == 0) then return 1 else return n*fact(n - 1) end end"; Script script = new Script(); script.DoString(scriptCode); DynValue res = script.Call(script.Globals["fact"], 4); return res.Number; } 何をしたのか見てみましょう。 まず、スクリプトの最後のreturnを削除しました。 残しておくこともできましたが、任意のパラメータでfactを呼び出したいので、無駄になるはずでした。 この時点で、 script.DoString(...) の呼び出しはファイルを実行し、グローバル環境にfact関数を残します。 そして、この行を導入しました。 DynValue res = script.Call(script.Globals["fact"], 4); これは、スクリプトのグローバルからfact関数を取得し、4をパラメータとして呼び出します。 もっと良い(つまり、パフォーマンスの良い)方法がありますが、時間に敏感なループで多くの呼び出しを行わない場合は、これが最も簡単な方法ですが、少し型が安全ではありません。 factの関数は何度でも呼び出せることに注意してください。Scriptは状態を保持し、何度でも実行できる状態になっています。 ** DynValueのすべて [#w1153ae3] - すべてはDynValueであり、DynValueである DynValueの概念はMoonSharpの根幹にあり、これまではあまり触れずに済んでいましたが、実際にはこの話題に触れずに先へ進むのは難しいでしょう。 タイトルにもあるように、MoonSharpの(ほぼ)すべてがDynValueオブジェクトのインスタンスです。 DynValueは、スクリプト内の値を表し、その型に関わらず、テーブル、関数、数値、文字列などすべてを表現できます。 では、まず前回のチュートリアルの最後のステップから始めて、代わりにDynValue(s)を使用するように変更してみましょう。 *** ステップ1: 前回のチュートリアルで行ったことをすべてやり直す [#f240d083] 再び、前回の最後のステップから始めます。やったよね? リファレンスドキュメントはここで入手できます。 *** ステップ2: fact関数をDynValueで取得する [#j049e11e] 最初の変更点として、fact変数を別の方法で取得します。 double MoonSharpFactorial() { string scriptCode = @" -- 階乗関数を定義 function fact (n) if (n == 0) then return 1 else return n*fact(n - 1) end end"; Script script = new Script(); script.DoString(scriptCode); DynValue luaFactFunction = script.Globals.Get("fact"); DynValue res = script.Call(luaFactFunction, 4); return res.Number; } この行について少し説明しましょう。 DynValue luaFactFunction = script.Globals.Get("fact"); これは、スクリプトのグローバルからfact関数を取得します。 Getメソッドは、インデクサープロパティとは反対に、DynValueを返します。 インデクサーは、代わりにSystem.Objectに変換しようとします。 この場合、DynValueに関心があるので、Getメソッドを使用します。 *** ステップ3: 数値をDynValueで取得する [#afda98b8] さて、もう1つの変換を避けることにも関心があるなら、Callメソッドが親切に提供する暗黙の変換を使用する代わりに、直接数値を渡すこともできます。 ここでは必要ありませんが、他の場所では必要になるかもしれません。すべてがあなたのために魔法のような変換をしてくれるわけではありません! double MoonSharpFactorial2() { string scriptCode = @" -- 階乗関数を定義 function fact (n) if (n == 0) then return 1 else return n*fact(n - 1) end end"; Script script = new Script(); script.DoString(scriptCode); DynValue luaFactFunction = script.Globals.Get("fact"); DynValue res = script.Call(luaFactFunction, DynValue.NewNumber(4)); return res.Number; } つまり、数値を以下のように置き換えました。 DynValue.NewNumber(4) DynValueには、"New"で始まる多くのファクトリメソッドがあります(NewString、NewNumber、NewBooleanなど)。 これらを使用して、最初から値を作成できます。 また、便利なFromObjectメソッドもあります。これはオブジェクトからDynValueを作成するもので、Callが内部で使用していたものです。 *** ステップ4: DataTypeの理解 [#vbad2568] DynValueで最も重要なプロパティの1つはTypeです。 Typeプロパティは列挙型で、DynValueにどのような種類のデータが含まれているかを示します。 DynValueに何が含まれているかを知りたい場合は、いつでもTypeプロパティを照会できます。 // 新しい数値を作成 DynValue v1 = DynValue.NewNumber(1); // 新しい文字列を作成 DynValue v2 = DynValue.NewString("ciao"); // 自動変換を使用して別の文字列を作成 DynValue v3 = DynValue.FromObject(new Script(), "hello"); // Number - String - String と表示される Console.WriteLine("{0} - {1} - {2}", v1.Type, v2.Type, v3.Type); // Number - String - 0になると期待すべきではないゴミの数値が表示される Console.WriteLine("{0} - {1} - {2}", v1.Number, v2.String, v3.Number); DynValueの一部のプロパティは、値が特定の型の場合にのみ意味のある値を持つことに注意してください。 例えば、NumberプロパティはDataType.Numberの場合にのみ意味のある値を持つことが保証されており、Stringプロパティも同様です。 DynValueの中身が確実にわかっていない限り、そのプロパティを使用する前に必ずDynValueのTypeを確認するようにしてください。 DynValueが特定の型を含んでいると想定しているのに、別の型を含んでいると、間違いやエラーの一般的な原因になります。 *** ステップ5: タプル [#gddb0564] ご存知のように(あるいは知っておくべきですが)、Luaでは関数から複数の値を返すことができます(その他の状況でも)。 これを処理するために、特別なDynValue型(DataType.Tuple)が使用されます。タプルにはTupleプロパティがあり、これはDynValueオブジェクトの配列で、タプルのメンバーです。 これは聞こえるよりも簡単です。 DynValue ret = Script.RunString("return true, 'ciao', 2*3"); // "Tuple" と表示される Console.WriteLine("{0}", ret.Type); // 以下が表示される: // Boolean = true // String = "ciao" // Number = 6 for (int i = 0; i < ret.Tuple.Length; i++) Console.WriteLine("{0} = {1}", ret.Tuple[i].Type, ret.Tuple[i]); 終わりに DynValuesについて終わりです。 まだまだ学ぶべきことはたくさんありますが、このトピックを始めるにはこれで十分でしょう。 すべてがこれを中心に回っているので、これを肝に銘じておいてください。 ** C#をコールバックする [#o1bf9b18] - ..そしてF#、VB.NET、C++/CLI、Booなども スクリプトは、アプリケーション自体に実装された構成要素から始まるビジネスロジックをカスタマイズできるため、アプリケーションで役立ちます。 ビジネスアプリケーション、ビデオゲーム、またはある種のツールにスクリプトを組み込む場合、最初の関心事はスクリプトとアプリケーションの相互運用性と、オブジェクトの(ある意味での)共有です。 そして、手続き型プログラミングの基本的な構成要素は関数です。 *** 演習1、ステップ1: 階乗に飽きるな [#g99c9a42] これまで使ってきた標準的な階乗スクリプトで基礎を押さえましょう。 private static double CallbackTest() { string scriptCode = @" -- 階乗関数を定義 function fact (n) if (n == 0) then return 1 else return n * fact(n - 1); end end"; Script script = new Script(); script.DoString(scriptCode); DynValue res = script.Call(script.Globals["fact"], 4); return res.Number; } *** 演習1、ステップ2: 乗算関数をカスタマイズする [#o017308d] さて、乗算をホストアプリケーションのAPI関数で実装したいとしましょう。 この場合、そうする目的はありませんが、学ぶためにここにいるので、それが良いアイデアだと仮定しましょう。 private static int Mul(int a, int b) { return a * b; } private static double CallbackTest() { string scriptCode = @" -- 階乗関数を定義 function fact (n) if (n == 0) then return 1 else return Mul(n, fact(n - 1)); end end"; Script script = new Script(); script.Globals["Mul"] = (Func<int, int, int>)Mul; script.DoString(scriptCode); DynValue res = script.Call(script.Globals["fact"], 4); return res.Number; } それだけです! script.Globals["Mul"] = (Func<int, int, int>)Mul; これにより、グローバル環境のMul変数が、アプリケーションのMulデリゲートを指すように設定されます。 ここでは、C#コンパイラを満足させるために、デリゲートをFunc<int, int, int>型にキャストしています。 デリゲートをSystem.Objectにキャストするために使用する方法は何でも構いません。 また、メソッドをstaticに定義しましたが、この場合、それらがstaticである必要は全くありません。 インスタンスメソッドも問題ありません。 *** 演習2: 数値のシーケンスを返す [#ved358ee] 問題: 整数のシーケンスを返すAPI関数を用意する。スクリプトは受け取った数値を合計して、その合計値を返す。 private static IEnumerable<int> GetNumbers() { for (int i = 1; i <= 10; i++) yield return i; } private static double EnumerableTest() { string scriptCode = @" total = 0; for i in getNumbers() do total = total + i; end return total; "; Script script = new Script(); script.Globals["getNumbers"] = (Func<IEnumerable<int>>)GetNumbers; DynValue res = script.DoString(scriptCode); return res.Number; } ここでも、特に難しいことはありません。 IEnumerable(またはIEnumerator)が、スクリプトを実行するためにLuaイテレータに自動的に変換される様子がわかります。 スクリプトには直接実行可能なコードが含まれているため、Call .. callを必要とせずに、DoString時にgetNumbersメソッドにアクセスしていることにも注目してください。 これを覚えておいてください。 DoStringとDoFileは、スクリプトに含まれるコードを即座に実行します! 注意すべき点が1つあります。MoonSharpは、System.Collections.IEnumerableとSystem.Collections.IEnumeratorの型のイテレータを変換できます。 つまり、ジェネリックではないバリアントです。 何らかの理由でジェネリックイテレータを実装する際にジェネリック以外のイテレータを実装しない場合、そのイテレータは機能しません。 上記のように、すべての標準コレクション型とイテレータメソッドは、デフォルトでジェネリック以外のバリアントを実装しているので、あまり心配する必要はありません。 *** テーブルを返す [#me086187] 問題: 今度は整数のシーケンスをテーブルで返すAPI関数を用意する。 private static List<int> GetNumberList() { List<int> lst = new List<int>(); for (int i = 1; i <= 10; i++) lst.Add(i); return lst; } private static double TableTest1() { string scriptCode = @" total = 0; tbl = getNumbers() for _, i in ipairs(tbl) do total = total + i; end return total; "; Script script = new Script(); script.Globals["getNumbers"] = (Func<List<int>>)GetNumberList; DynValue res = script.DoString(scriptCode); return res.Number; } ここでは、List<int>がLuaテーブルに自動的に変換される様子がわかります! 結果のテーブルは、Luaテーブルが通常そうであるように、1から始まるインデックスを持つことに注意してください。 ただし、もっと良い方法があります。 関数内で直接Luaテーブルを構築することができます。 private static Table GetNumberTable(Script script) { Table tbl = new Table(script); for (int i = 1; i <= 10; i++) tbl[i] = i; return tbl; } private static double TableTest2() { string scriptCode = @" total = 0; tbl = getNumbers() for _, i in ipairs(tbl) do total = total + i; end return total; "; Script script = new Script(); script.Globals["getNumbers"] = (Func<Script, Table>)(GetNumberTable); DynValue res = script.DoString(scriptCode); return res.Number; } Tableオブジェクトを使用してテーブルを操作するのがいかに簡単かがわかります。 ただし、注意すべき点が2つあります。 新しいTableオブジェクトを作成するには、実行中のスクリプトへの参照が必要です。 CLR関数にScriptパラメータがあり、それがLuaスクリプトで利用可能な場合、MoonSharpはそれを自動的に設定します。 これは、(このように使用される可能性は低いですが)ScriptExecutionContextやCallbackArgumentsの型でも発生します。 これらが何をするのかわからなくても心配しないでください。 MoonSharpの基本を理解するのに必要ありません! 良い習慣として、可能な限りScriptオブジェクトを保持するようにしてください。 (テーブルの作成など)Scriptオブジェクトを使用してのみ実行できることがいくつかあります。 *** テーブルを受け入れる [#p43434d6] テーブルは自動的にList<T>に変換されます。 例えば、 public static double TableTestReverse() { string scriptCode = @" return dosum { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 } "; Script script = new Script(); script.Globals["dosum"] = (Func<List<int>, int>)(l => l.Sum()); DynValue res = script.DoString(scriptCode); return res.Number; } ただし、これによって一部のプラットフォーム(具体的にはiOS)で問題が発生する可能性があります。 この問題を回避する方法はたくさんありますが(他のチュートリアルで見ることができます)、次の方法では問題なく機能し、さらに高速であると言えます。 public static double TableTestReverseSafer() { string scriptCode = @" return dosum { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 } "; Script script = new Script(); script.Globals["dosum"] = (Func<List<object>, int>)(l => l.OfType<int>().Sum()); DynValue res = script.DoString(scriptCode); return res.Number; } さらに高速な別の方法は、Tableオブジェクトを使用することです。 static double Sum(Table t) { var nums = from v in t.Values where v.Type == DataType.Number select v.Number; return nums.Sum(); } private static double TableTestReverseWithTable() { string scriptCode = @" return dosum { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 } "; Script script = new Script(); script.Globals["dosum"] = (Func<Table, double>)Sum; DynValue res = script.DoString(scriptCode); return res.Number; } しかし、ここではDynValue(s)を扱う必要があります。 これらすべてを理解するには、MoonSharpがLuaの型をC#の型にどのようにマッピングするか、その逆はどうかについて、もう少し深く掘り下げる必要があります。 次の部分で説明します。 ** 自動変換の説明 [#zdd8f29e] - ..今回はコードなし MoonSharpとCLRの統合についてさらに掘り下げる前に、型がどのように相互にマッピングされるかを明確にする必要があります。 残念ながら、逆方向は順方向とは大きく異なるので、2つを別々に分析します。 これは少し複雑すぎませんか? - あなたはそう尋ねるかもしれません。 確かにそうです。 自動的なものは良いですが、失敗すると、ひどく複雑な方法で失敗します。疑問がある場合や、物事が複雑すぎる場合は、物事を単純化する必要があります。 2つの方法があります:DynValueをそのまま使用するか、カスタムコンバーターを使用するかです。 これらの解決策は、正気と単純さを得るだけでなく、自動変換よりもかなり高速です! *** カスタムコンバーター [#iaa65800] 変換プロセスをカスタマイズすることは可能ですが、設定はグローバルで、すべてのスクリプトに影響します。 変換をカスタマイズするには、 Script.GlobalOptions.CustomConverters の適切なコールバックを設定するだけです。 例えば、CLRからスクリプトへの変換が行われるときに、すべての StringBuilder オブジェクトを大文字の文字列に変換したい場合は、次のようにします。 Script.GlobalOptions.CustomConverters.SetClrToScriptCustomConversion<StringBuilder>( v => DynValue.NewString(v.ToString().ToUpper())); テーブルがIList<int>に一致する必要がある場合に、テーブルの変換方法をカスタマイズしたい場合は、次のように記述できます。 Script.GlobalOptions.CustomConverters.SetScriptToClrCustomConversion(DataType.Table, typeof(IList<int>), v => new List<int>() { ... }); コンバーターがnullを返した場合、システムはカスタムコンバーターが存在しないかのように動作し、自動変換を試みます。 *** CLR型からMoonSharp型への自動変換 [#y6c7cab4] この変換は、次の状況で適用されます。 - スクリプトによって呼び出された関数からオブジェクトを返すとき - ユーザーデータのプロパティからオブジェクトを返すとき - インデックス演算子を使用してテーブルに値を設定するとき - DynValue.FromObjectを呼び出すとき - DynValueの代わりにSystem.Objectを取る関数のオーバーロードを使用するとき この変換は実際にはかなり簡単です。次の表は、変換を説明しています。 | CLR型 | C#のわかりやすい名前 | Lua型 | 備考 | |----------------------------------------|---------------------|----------|--------------------------------------------------------------| | void | | (no value) | これはメソッドの戻り値にのみ適用できます。 | | null | | nil | nullはすべてnilに変換されます。 | | MoonSharp.Interpreter.DynValue | | * | DynValueはそのまま渡されます。 | | System.SByte | sbyte | number | | | System.Byte | byte | number | | | System.Int16 | short | number | | | System.UInt16 | ushort | number | | | System.Int32 | int | number | | | System.UInt32 | uint | number | | | System.Int64 | long | number | 変換により、暗黙的に精度が失われる可能性があります。 | | System.UInt64 | ulong | number | 変換により、暗黙的に精度が失われる可能性があります。 | | System.Single | float | number | | | System.Decimal | decimal | number | 変換により、暗黙的に精度が失われる可能性があります。 | | System.Double | double | number | | | System.Boolean | bool | boolean | | | System.String | string | string | | | System.Text.StringBuilder | | string | | | System.Char | char | string | | | MoonSharp.Interpreter.Table | | table | | | MoonSharp.Interpreter.CallbackFunction | | function | | | System.Delegate | | function | | | System.Object | object | userdata | 型がuserdataに登録されている場合のみ。 | | System.Type | | userdata | 型がuserdataに登録されている場合のみ。静的メンバーアクセス。 | | MoonSharp.Interpreter.Closure | | function | | | System.Reflection.MethodInfo | | function | | | System.Collections.IList | | table | 結果のテーブルは1からインデックスが付けられます。すべての値はこれらのルールを使用して変換されます。 | | System.Collections.IDictionary | | table | すべてのキーと値はこれらのルールを使用して変換されます。 | | System.Collections.IEnumerable | | iterator | すべての値はこれらのルールを使用して変換されます。 | | System.Collections.IEnumerator | | iterator | すべての値はこれらのルールを使用して変換されます。 | これには、コレクションのかなり良いカバレッジが含まれています。ほとんどのコレクションは、IList、IDictionary、IEnumerable、IEnumeratorのいずれかを実装しているためです。 独自のコレクションを書く場合は、これらの非ジェネリックインターフェイスの1つを実装することを忘れないでください。 このロジックを使用して変換できない値はすべて、ScriptRuntimeExceptionをスローします。 *** MoonSharp型からCLR型への標準の自動変換 [#b443915a] 反対の変換はかなり複雑です。 実際、2つの異なる変換パスがあります。 「標準」の変換パスと、「制約付き」の変換パスです。 最初の変換パスは、実際に何を受け取りたいかを指定せずにDynValueをオブジェクトに変換するように要求するたびに適用され、もう一方の変換パスは、一致させるSystem.Typeがある場合に適用されます。 これは以下の場合に使用されます。 - DynValue.ToObjectを呼び出すとき - インデクサーを使用してテーブルから値を取得するとき - 制約付き変換の特定のサブケース(下記参照) ここでは、デフォルトの変換を見ていきます。実際には簡単です。 | MoonSharp型 | CLR型 | 備考 | |----------------------------------------|----------------------------------------|---------------------------------------------------------------| | nil | null | nilであるすべての値に適用されます。 | | boolean | System.Boolean | | | number | System.Double | | | string | System.String | | | function | MoonSharp.Interpreter.Closure | DataTypeがFunctionの場合。 | | function | MoonSharp.Interpreter.CallbackFunction | DataTypeがClrFunctionの場合。 | | table | MoonSharp.Interpreter.Table | | | tuple | MoonSharp.Interpreter.DynValue[] | | | userdata | (special) | userdataに格納されているオブジェクトを返します。「静的」userdataの場合は、Typeが返されます。 | このロジックを使用して変換できない値はすべて、ScriptRuntimeExceptionをスローします。 *** MoonSharp型からCLR型への制約付き自動変換 [#b3fb4110] しかし、制約付き自動変換ははるかに複雑です。この場合、MoonSharpの値は、特定の型のCLR変数に格納する必要があり、これらの変換を行う方法はほぼ無限にあります。 これは以下の場合に使用されます。 - DynValue.ToObject<T>を呼び出すとき - CLR関数呼び出しやプロパティ設定でスクリプト値をパラメーターに変換するとき MoonSharpは値の変換に非常に努力しますが、特にテーブルが関与する場合は、変換には確実に限界があります。 この場合、変換は単純なマッピングのテーブルではなく、プロセスに近いので、ターゲットの型を1つずつ分析していきましょう。 *** 特殊なケース [#h25b1ae1] - ターゲットがMoonSharp.Interpreter.DynValue型の場合は変換されず、元の値が返されます。 - ターゲットがSystem.Object型の場合は、前述のデフォルトの変換が適用されます。 - マッピングされるnil値の場合、nullは参照型とnullable値型にマッピングされ、一部のケース(たとえば、デフォルトが指定されている関数呼び出しなど)では、non-nullable値型にデフォルト値に一致させる試行が行われます。それ以外の場合は例外がスローされます。 - 値が提供されていない場合は、可能であればデフォルト値が使用されます。 *** 文字列 [#gde47277] 文字列は、System.String、System.Text.StringBuilder、System.Charに自動的に変換できます。 *** ブール値 [#lb1bcc8c] ブール値は、System.BooleanやSystem.Nullable<System.Boolean>に自動的に変換できます。また、System.String、System.Text.StringBuilder、System.Charにも変換できます。 *** 数値 [#dc22c32c] 数値は、System.SByte、System.Byte、System.Int16、System.UInt16、System.Int32、System.UInt32、System.Int64、System.UInt64、System.Single、System.Decimal、System.Doubleとそれらのnullableバージョンに自動的に変換できます。また、System.String、System.Text.StringBuilder、System.Charにも変換できます。 *** 関数 [#z9738d47] スクリプト関数は、MoonSharp.Interpreter.ClosureまたはMoonSharp.Interpreter.ScriptFunctionDelegateに変換されます。コールバック関数は、MoonSharp.Interpreter.ClrFunctionまたはSystem.Func<ScriptExecutionContext, CallbackArguments, DynValue>に変換されます。 *** ユーザーデータ [#jbe4344d] ユーザーデータは、「静的」でない場合にのみ変換されます(チュートリアルのuserdataセクションを参照)。変換対象のオブジェクトを割り当てることができる型の場合に変換されます。 また、オブジェクトのToString()メソッドを呼び出すことで、System.String、System.Text.StringBuilder、System.Charにも変換できます。 *** テーブル [#g19e08d9] テーブルは以下に変換できます。 - MoonSharp.Interpreter.Table - もちろん - Dictionary<DynValue, DynValue>から割り当て可能な型。 - Dictionary<object, object>から割り当て可能な型。キーと値は、デフォルトのマッピングを使用してマッピングされます。 - List<DynValue>から割り当て可能な型。 - List<object>から割り当て可能な型。要素は、デフォルトのマッピングを使用してマッピングされます。 - DynValue[]から割り当て可能な型。 - object[]から割り当て可能な型。要素は、デフォルトのマッピングを使用してマッピングされます。 - T[]、IList<T>、List<T>、ICollection<T>、IEnumerable<T>。ここで、Tは変換可能な型です(他のリストなどを含む)。 - IDictionary<K,V>、Dictionary<K,V>。ここで、KとVは変換可能な型です(他のリストなどを含む)。 ジェネリックや型付き配列への変換には、次の制限があることに注意してください。 - リフレクションを使用して実行時に型が作成されるため、速度が遅くなります。 - アイテムの型が値型の場合、iOSやAOTモードで実行される他のプラットフォームとの非互換性を引き起こす可能性があります。事前にテストを行ってください。 これらの問題を補うために、開発の後半でカスタムコンバーターを追加することもできます! 一部の変換(List<int>への変換など)は、Unity/iOSなどのAOTプラットフォームで問題を引き起こす可能性があります。この種の問題を回避するには、カスタムコンバーターを登録してください。 iOSなどのAOTプラットフォームをターゲットにする場合は、値型のジェネリックには常に注意してください。参照型のジェネリックは、一般的に問題を引き起こしません。 ** オブジェクトの共有 [#obbc6f3a] - LuaとC#はお互いに話す 注:このページに記載されている一部の機能は、masterブランチの現在の状態を反映しています(つまり、最新のリリースには存在しない機能かもしれません)。 MoonSharpの便利な機能の1つは、.NETオブジェクトをスクリプトと共有できることです。 デフォルトでは、型はすべてのパブリックメソッド、パブリックプロパティ、パブリックイベント、パブリックフィールドをLuaスクリプトと共有します。 MoonSharpVisibleアトリビュートを使用して、このデフォルトの可視性をオーバーライドできます。 CLRコードとスクリプトコード間のインターフェイスとして専用のオブジェクトを使用することをお勧めします(アプリケーションの内部モデルをスクリプトに公開するのとは対照的です)。 多くのデザインパターン(アダプター、ファサード、プロキシなど)が、そのようなインターフェイス層の設計に役立ちます。 これは特に以下のために重要です。 - スクリプトができること、できないことを制限する(セキュリティ!modの作成者にエンドユーザーの個人ファイルを削除する方法を見つけてほしいですか?) - スクリプト作成者にとって意味のあるインターフェイスを提供する - インターフェイスを別々に文書化する - スクリプトを破ることなく、内部ロジックとモデルを変更できるようにする これらの理由から、MoonSharpはデフォルトで、スクリプトで使用可能な型の明示的な登録を必要とします。 スクリプトを信頼できるシナリオでは、` UserData.RegistrationPolicy = InteropRegistrationPolicy.Automatic; を使用して自動登録をグローバルに有効にできます。 危険なので、警告しておきます。 *** メニューにあるもの [#dda0ae94] では、メニューにあるものを見てみましょう。 - まず、型記述子について説明しましょう - 舞台裏で何が起こっているのか、そしてインターオペラビリティシステム全体をどのようにオーバーライドできるかを説明する少しの理論 - シンプルに保つ - 始めるための最も簡単な方法 - 少し複雑 - 少し複雑さと詳細を加えて掘り下げる - 静的メンバーの呼び出し - 静的メンバーを呼び出す方法 - ':' と '.' のどちらを使うべきか - メソッドの呼び出し方に関する簡単な質問 - オーバーロード - オーバーロードがどのように処理されるか - ByRefパラメーター(C#のref/out) - ref/outパラメーターがどのように処理されるか - インデクサー - インデクサーの処理方法 - ユーザーデータ上の演算子とメタメソッド - 演算子のオーバーロード方法など - 拡張メソッド - 拡張メソッドの使用方法 - イベント - イベントの使用方法 - インターオペラビリティアクセスモード - インターオペラビリティアクセスモードとその動作 - MoonSharpHiddenとMoonSharpVisibleによる可視性の変更 - メンバーの可視性をオーバーライドする方法 - メンバーの削除 - メンバーの可視性を削除する方法 たくさんありますね。では、始めましょう。 *** 型記述子 [#ce23a6ed] まず、型記述子について説明しましょう 最初に、インターオペラビリティがどのように実装されているかについて少し理論を説明します。 すべてのCLR型は、CLR型をスクリプトに記述する役割を持つ「型記述子」にラップされます。 型をインターオペラビリティ用に登録するとは、型を記述子(MoonSharpが自身で作成できる)に関連付けることを意味します。 この記述子は、メソッド、プロパティなどのディスパッチに使用されます。 次のセクション以降では、MoonSharpが提供する「自動」記述子について説明しますが、速度、機能の追加、セキュリティなどのために独自の記述子を実装することもできます。 *** 独自の記述子を実装したい場合 [#i28f4d6d] 独自の記述子を実装したい場合(簡単ではなく、必要でない限り行うべきではありません)、次のような方法があります。 - 独自の型を記述するためのアドホックなIUserDataDescriptorを作成する - これが最も難しい方法です - 型にIUserDataTypeインターフェイスを実装させる。これは簡単ですが、オブジェクトのインスタンスなしで静的メンバーを処理できないことを意味します。 - StandardUserDataDescriptorを拡張または埋め込み、必要な側面を変更しながら、残りの動作を維持する。 記述子の作成を助けるために、次のクラスが利用できます。 - StandardUserDataDescriptor - これはMoonSharpが実装する型記述子です - StandardUserDataMethodDescriptor - これは単一のメソッド/関数の記述子です - StandardUserDataOverloadedMethodDescriptor - これはオーバーロードされたメソッドや拡張メソッドの記述子です - StandardUserDataPropertyDescriptor - これは単一のプロパティの記述子です - StandardUserDataFieldDescriptor - これは単一のフィールドの記述子です *** ユーザーデータとしての値型とのインターオペラビリティに関する注意事項。 [#jcae2115] 値型をパラメーターとして渡す関数を呼び出す場合と同様に、スクリプトはユーザーデータのコピーを操作するため、たとえば、ユーザーデータのフィールドを変更しても、元の値には反映されません。 繰り返しになりますが、これは値型の標準的な動作と変わりませんが、人を驚かせるのに十分です。 さらに、値型は参照型ほどの最適化の範囲をサポートしていないため、値型での一部の操作は参照型よりも遅くなる可能性があります。 *** シンプルに保つ [#b3bac791] では、最初の例を見てみましょう。 [MoonSharpUserData] class MyClass { public double calcHypotenuse(double a, double b) { return Math.Sqrt(a * a + b * b); } } double CallMyClass1() { string scriptCode = @" return obj.calcHypotenuse(3, 4); "; // MoonSharpUserData型をすべて自動的に登録 UserData.RegisterAssembly(); Script script = new Script(); // MyClassのインスタンスをスクリプトのグローバルに渡す script.Globals["obj"] = new MyClass(); DynValue res = script.DoString(scriptCode); return res.Number; } ここでは以下のことを行っています。 - [MoonSharpUserData]アトリビュートを持つクラスを定義 - MyClassオブジェクトのインスタンスをスクリプトのグローバルとして渡す - スクリプトからMyClassのメソッドを呼び出す。コールバックのマッピングルールがすべて適用されます *** 少し複雑 [#x8b60ac6] もう少し複雑な例を試してみましょう。 class MyClass { public double CalcHypotenuse(double a, double b) { return Math.Sqrt(a * a + b * b); } } static double CallMyClass2() { string scriptCode = @" return obj.calcHypotenuse(3, 4); "; // MyClassだけを明示的に登録 UserData.RegisterType<MyClass>(); Script script = new Script(); // ユーザーデータを明示的に作成 DynValue obj = UserData.Create(new MyClass()); script.Globals.Set("obj", obj); DynValue res = script.DoString(scriptCode); return res.Number; } *** 大きな違い [#a756817e] 大きな違いは以下の通りです。 - [MoonSharpUserData]アトリビュートはありません。もう必要ありません。 - RegisterAssemblyの代わりに、RegisterTypeを呼び出して特定の型を登録します。 - ユーザーデータのDynValueを明示的に作成します。 また、C#コードではメソッドがCalcHypotenuse と呼ばれていましたが、LuaスクリプトではcalcHypotenuse として呼ばれていることに注目してください。 他のバージョンが存在しない限り、MoonSharpは異なる言語の構文規則間の一貫性を高めるために、いくつかの限定的な方法でケースを自動的に調整してメンバーを一致させます。たとえば、SomeMethodWithLongNameというメンバーは、LuaスクリプトからsomeMethodWithLongNameやsome_method_with_long_nameとしてアクセスすることもできます。 *** 静的メンバーの呼び出し [#b88a2ae1] calcHypotenuse メソッドが静的であるとしましょう。 [MoonSharpUserData] class MyClassStatic { public static double calcHypotenuse(double a, double b) { return Math.Sqrt(a * a + b * b); } } *** 呼び出す方法 [#x240cc3a] これを呼び出すには2つの方法があります。 *** 方法1 - 静的メソッドはインスタンスから透過的に呼び出すことができます - 何もする必要はなく、すべて自動的に行われます [#ecac98ea] double MyClassStaticThroughInstance() { string scriptCode = @" return obj.calcHypotenuse(3, 4); "; // MoonSharpUserData型をすべて自動的に登録 UserData.RegisterAssembly(); Script script = new Script(); script.Globals["obj"] = new MyClassStatic(); DynValue res = script.DoString(scriptCode); return res.Number; } *** 別の方法 [#of33f4f5] - 型を直接渡す(またはUserData.CreateStaticメソッドを使用する)ことで、プレースホルダーのユーザーデータを作成できます。 double MyClassStaticThroughPlaceholder() { string scriptCode = @" return obj.calcHypotenuse(3, 4); "; // MoonSharpUserData型をすべて自動的に登録 UserData.RegisterAssembly(); Script script = new Script(); script.Globals["obj"] = typeof(MyClassStatic); DynValue res = script.DoString(scriptCode); return res.Number; } ':' と '.' のどちらを使うべきか 上記の例のコードを考えると、この構文を使うべきでしょうか return obj.calcHypotenuse(3, 4); それともこちらでしょうか? return obj:calcHypotenuse(3, 4); 99.999%の場合、違いはありません。MoonSharpは、ユーザーデータに対して呼び出しが行われていることを認識し、それに応じて動作します。 *** 違いが出る可能性があるコーナーケース [#r13135cc] 違いが出る可能性があるコーナーケースがあります。たとえば、プロパティがデリゲートを返し、そのデリゲートをインスタンスとして元のオブジェクトですぐに呼び出す場合などです。これはまれなシナリオですが、そのような場合は手動で処理する必要があります。 *** オーバーロード [#v6fea100] オーバーロードされたメソッドがサポートされています。オーバーロードされたメソッドのディスパッチは、いくぶん黒魔術的で、C#のオーバーロードディスパッチほど決定的ではありません。 これは、いくつかの曖昧さが存在するためです。たとえば、オブジェクトはこれらの2つのメソッドを宣言できます。 void DoSomething(int i) { ... } void DoSomething(float f) { ... } Luaのすべての数値がdoubleであることを考えると、MoonSharpはどのメソッドをディスパッチすべきかをどのように知ることができるでしょうか? この問題を解決するために、MoonSharpは入力型に対するすべてのオーバーロードのヒューリスティック係数を計算し、最適なオーバーロードを選択します。MoonSharpがオーバーロードを間違った方法で解決していると思われる場合は、フォーラムやdiscordに報告して、ヒューリスティックを調整してください。 MoonSharpは、ヒューリスティックの重みを可能な限り安定させようとしており、メソッド間のスコアが同点の場合は、常に決定的に同じものを選択します(ビルドやプラットフォーム間で一貫した動作を提供するため)。 とは言え、MoonSharpが考えているのとは異なるオーバーロードを選択することは十分にありえます。 そのため、オーバーロードが同等の処理を行うようにすることが非常に重要です。これは、間違ったオーバーロードを呼び出すことの影響を最小限に抑えるためです。これは、ベストプラクティスであるべきですが、ここでその概念を強化する価値があります。 - ByRefパラメーター(C#のref/out) - ByRefメソッドパラメーター * 参考 [#l6580144] ** MoonSharpによるUnityとLuaの連携 [#r91a70b3] https://qiita.com/massu2357/items/425cc93b5965ceea29aa
spamではない場合はチェックをいれてください。
タイムスタンプを変更しない
* 目次 [#d070a7cf] #contents * Moonsharpとは [#df0cc95f] 一言でいえば、レベルデザイン必要なゲームを開発するなら、入れておきなさいツール。 わかりやすくいうと、これを入れない設計でゲームをつくると、「ちょっ変更するだけなのにビルド時間こんなにかかるの?!」という事態になってしまう。 UnityでLuaを使えるようにするプラグインである。 Asset Storeを開き、 MoonSharp と検索すると出てきます。 https://assetstore.unity.com/packages/tools/moonsharp-33776?locale=ja-JP MoonSharpは、.NET、Mono、Xamarin、Unity3Dプラットフォーム向けに完全にC#で書かれた完全なLuaソリューションです。 * 特徴 [#g6a14b51] - Lua 5.2との99%の互換性(サポートされていない唯一の機能はウィークテーブルのサポート) - metaluaスタイルの無名関数(ラムダスタイル)のサポート - 使いやすいAPI - Visual Studio Codeのデバッガサポート(PCLターゲットはサポートされていません) - Webブラウザとフラッシュでアクセス可能なリモートデバッガ(PCLターゲットはサポートされていません) - .NET 3.5、.NET 4.x、.NET Core、Mono、Xamarin、Unity3Dで動作 - iOSのようなAhead-of-timeプラットフォームで動作 - IL2CPPに変換されたコードで動作 - .NET 4.xのポータブルクラスライブラリを必要とするプラットフォーム(Windows Phoneなど)で動作 - 外部の依存関係がなく、可能な限り少ないターゲットで実装 - サポートされている場合、ランタイムコード生成によるCLRオブジェクトとの簡単でパフォーマンスの高いインターオペラビリティ - メソッド、拡張メソッド、オーバーロード、フィールド、プロパティ、インデクサーとのインターオペラビリティがサポートされています - 非常に少ない例外(主に「debug」モジュールにある)と少数の拡張(主に文字列ライブラリ内)を除いて、完全なLua標準ライブラリのサポート - .NET 4.xターゲット用の非同期メソッド - 難読化とランタイム時のより迅速な解析のためのバイトコードのダンプ/ロードをサポート - JSONとLuaテーブルの間で変換するための(依存関係のない)組み込みのJSONパーサー - スクリプトがアクセスできるものをサンドボックス化するための、Lua標準ライブラリモジュールの簡単なオプトアウト - 使いやすいエラー処理(スクリプトエラーは例外です) - コルーチンのサポート(C#イテレータとしてのコルーチンの呼び出しを含む) - REPLインタプリタ、および数行のコードで独自のREPLを簡単に実装するための機能 - http://www.moonsharp.org にある完全なXMLヘルプとチュートリアル * MoonSharpと標準Luaの違い [#e0052ec6] http://www.moonsharp.org/moonluadifferences.html を参照してください。 ダウンロード、情報、チュートリアルなどについては、http://www.moonsharp.org をご覧ください。 *** ライセンス [#p3b97abd] プログラムとライブラリは、3条項BSDライセンスの下でリリースされています - ライセンスセクションを参照してください。 文字列ライブラリの一部は、KopiLuaプロジェクト(https://github.com/NLua/KopiLua)に基づいています。デバッガーのアイコンは、Eclipseプロジェクト(https://www.eclipse.org/)からのものです。 * 使用方法 [#rc0da492] ライブラリの使用は次のように簡単です: double MoonSharpFactorial() { string script = @" -- 階乗関数を定義 function fact (n) if (n == 0) then return 1 else return n*fact(n - 1) end end return fact(5)"; DynValue res = Script.RunString(script); return res.Number; } より詳細なチュートリアル、サンプルなどについては、http://www.moonsharp.org/getting_started.html を参照してください。 ** 公式サイト [#ae77951f] https://www.moonsharp.org/ *** Github [#h2f29b43] https://github.com/moonsharp-devs/moonsharp * チュートリアル [#e115d97d] 本家のチュートリアルを翻訳しました。 ** 元記事 [#m4521d40] https://www.moonsharp.org/getting_started.html ** チュートリアルをはじめるにあたって [#dc75bb7e] このチュートリアルでは、UnityでMoonSharpの簡単さと強力さを感じていただけるでしょう。MoonSharpを使用するには他にも良い方法がありますが、これが最も簡単な方法です。他のチュートリアルではさらに掘り下げて、UnityでMoonSharpを最大限に活用する方法を学ぶことができます。 とりあえず、始めましょう。 このチュートリアルでは、LuaとC#を知っていることを前提としています。Luaの基礎を知らず、C#について十分な知識がないと、サンプルを理解するのは難しいでしょう。 *** ステップ1:UnityでMoonSharpを入手する [#sfc07f66] 最初のステップは、UnityでMoonSharpを入手することです。 Unity Asset Storeのパッケージは現在承認待ちで、承認され次第このURLから入手できるようになります。承認されたら、これを使用するのがUnityでMoonSharpをインストールするのに推奨される方法になります。 MoonSharp配布物のinterpreter/net35フォルダに含まれているMoonSharp.Interpreter.dllをAssets/Pluginsフォルダに入れます。 Windows StoreアプリやWindows Phoneのサポートが必要な場合は、MoonSharp配布物のinterpreter/portable-net40フォルダにあるMoonSharp.Interpreter.dllをAssets/Plugins/WSAフォルダにコピーします。 次に、このガイドに従ってください。 その後、Unity EditorからMoonSharp.Interpreter.dllを参照として追加します。 注:IL2CPPプロジェクトでMoonSharpを使用する場合は、以下の内容でAssetsディレクトリ内にlink.xmlを作成または編集してください。 <linker> <assembly fullname="MoonSharp.Interpreter"> <type fullname="MoonSharp.*" preserve="all" /> </assembly> </linker> *** ステップ2:名前空間のインポート [#m972361d] コードの先頭に以下の行を追加して、MoonSharp.Interpreter名前空間をコードにインポートします。 using MoonSharp.Interpreter; *** ステップ3:スクリプトの呼び出し [#u47bd259] ここでは、MoonSharpを使用して階乗を計算するMoonSharpFactorial関数を作成します。 double MoonSharpFactorial() { string script = @" -- 階乗関数を定義 function fact (n) if (n == 0) then return 1 else return n*fact(n - 1) end end Copy code return fact(5)"; DynValue res = Script.RunString(script); return res.Number; } *** ステップ4:完了! [#nf07c737] もちろん、コードを実行するには、Unity C#スクリプトの他の場所でMoonSharpFactorial関数を呼び出すだけです。 これで、UnityでMoonSharpを使うための準備が整いました。他のチュートリアルに進んで、UnityでMoonSharpを最大限に活用する方法を学びましょう。 ** スクリプトを保持する [#re6a0baf] 前回のチュートリアルでは、MoonSharpに初めて触れました。スクリプトを文字列に入れ、コードを実行し、その出力を取得しました。これは時には便利かもしれませんが、ほとんどのユースケースに必要な相互運用性には、CLRコードとMoonSharpの間でもう少し統合が必要です。 *** ステップ1: 前回のチュートリアルで行ったことをすべてやり直す [#w629e4ff] 真面目な話、ここではもう少し高度な概念を学んでいますが、最初の試みで捨てるべきものはほとんどありません。それは素晴らしい出発点です。 *** ステップ2: コードを変更してScriptオブジェクトを作成する [#vf907cb2] 最初の変更点は、静的メソッドを使用する代わりにScriptオブジェクトを作成することです。ロケット科学ではありませんが、次の進化に向けて準備をしています。 double MoonSharpFactorial() { string scriptCode = @" -- 階乗関数を定義 function fact (n) if (n == 0) then return 1 else return n*fact(n - 1) end end return fact(5)"; Script script = new Script(); DynValue res = script.DoString(scriptCode); return res.Number; } *** ステップ2: グローバル環境にアクセスする [#te57efec] スクリプトオブジェクトを持っているので、関数が動作するグローバル環境を変更できます。 例えば、fact関数への入力を変更して、プログラムがどの数の階乗を計算するかを指定できるようにしたいかもしれません。 double MoonSharpFactorial() { string scriptCode = @" -- 階乗関数を定義 function fact (n) if (n == 0) then return 1 else return n*fact(n - 1) end end return fact(mynumber)"; Script script = new Script(); script.Globals["mynumber"] = 7; DynValue res = script.DoString(scriptCode); return res.Number; } ご覧の通り、script.Globalsテーブルを単純な構文で参照するだけで、数値をMoonSharpスクリプトに注入できます。実際には数値以上のものを渡すことができますが、今のところは数値、ブール値、文字列に限定しましょう。関数やオブジェクトを渡す方法は後で見ていきます。 *** ステップ2: 関数を直接呼び出す [#ya81cef6] 外部から選択した数値の階乗をMoonSharpで計算する方法を学びました。 でも、そのようにやるのは汚いハックのように見えます(それでも、これからよく使う重要なテクニックです)。 以下は、C#からLua関数を呼び出す方法です。 double MoonSharpFactorial2() { string scriptCode = @" -- 階乗関数を定義 function fact (n) if (n == 0) then return 1 else return n*fact(n - 1) end end"; Script script = new Script(); script.DoString(scriptCode); DynValue res = script.Call(script.Globals["fact"], 4); return res.Number; } 何をしたのか見てみましょう。 まず、スクリプトの最後のreturnを削除しました。 残しておくこともできましたが、任意のパラメータでfactを呼び出したいので、無駄になるはずでした。 この時点で、 script.DoString(...) の呼び出しはファイルを実行し、グローバル環境にfact関数を残します。 そして、この行を導入しました。 DynValue res = script.Call(script.Globals["fact"], 4); これは、スクリプトのグローバルからfact関数を取得し、4をパラメータとして呼び出します。 もっと良い(つまり、パフォーマンスの良い)方法がありますが、時間に敏感なループで多くの呼び出しを行わない場合は、これが最も簡単な方法ですが、少し型が安全ではありません。 factの関数は何度でも呼び出せることに注意してください。Scriptは状態を保持し、何度でも実行できる状態になっています。 ** DynValueのすべて [#w1153ae3] - すべてはDynValueであり、DynValueである DynValueの概念はMoonSharpの根幹にあり、これまではあまり触れずに済んでいましたが、実際にはこの話題に触れずに先へ進むのは難しいでしょう。 タイトルにもあるように、MoonSharpの(ほぼ)すべてがDynValueオブジェクトのインスタンスです。 DynValueは、スクリプト内の値を表し、その型に関わらず、テーブル、関数、数値、文字列などすべてを表現できます。 では、まず前回のチュートリアルの最後のステップから始めて、代わりにDynValue(s)を使用するように変更してみましょう。 *** ステップ1: 前回のチュートリアルで行ったことをすべてやり直す [#f240d083] 再び、前回の最後のステップから始めます。やったよね? リファレンスドキュメントはここで入手できます。 *** ステップ2: fact関数をDynValueで取得する [#j049e11e] 最初の変更点として、fact変数を別の方法で取得します。 double MoonSharpFactorial() { string scriptCode = @" -- 階乗関数を定義 function fact (n) if (n == 0) then return 1 else return n*fact(n - 1) end end"; Script script = new Script(); script.DoString(scriptCode); DynValue luaFactFunction = script.Globals.Get("fact"); DynValue res = script.Call(luaFactFunction, 4); return res.Number; } この行について少し説明しましょう。 DynValue luaFactFunction = script.Globals.Get("fact"); これは、スクリプトのグローバルからfact関数を取得します。 Getメソッドは、インデクサープロパティとは反対に、DynValueを返します。 インデクサーは、代わりにSystem.Objectに変換しようとします。 この場合、DynValueに関心があるので、Getメソッドを使用します。 *** ステップ3: 数値をDynValueで取得する [#afda98b8] さて、もう1つの変換を避けることにも関心があるなら、Callメソッドが親切に提供する暗黙の変換を使用する代わりに、直接数値を渡すこともできます。 ここでは必要ありませんが、他の場所では必要になるかもしれません。すべてがあなたのために魔法のような変換をしてくれるわけではありません! double MoonSharpFactorial2() { string scriptCode = @" -- 階乗関数を定義 function fact (n) if (n == 0) then return 1 else return n*fact(n - 1) end end"; Script script = new Script(); script.DoString(scriptCode); DynValue luaFactFunction = script.Globals.Get("fact"); DynValue res = script.Call(luaFactFunction, DynValue.NewNumber(4)); return res.Number; } つまり、数値を以下のように置き換えました。 DynValue.NewNumber(4) DynValueには、"New"で始まる多くのファクトリメソッドがあります(NewString、NewNumber、NewBooleanなど)。 これらを使用して、最初から値を作成できます。 また、便利なFromObjectメソッドもあります。これはオブジェクトからDynValueを作成するもので、Callが内部で使用していたものです。 *** ステップ4: DataTypeの理解 [#vbad2568] DynValueで最も重要なプロパティの1つはTypeです。 Typeプロパティは列挙型で、DynValueにどのような種類のデータが含まれているかを示します。 DynValueに何が含まれているかを知りたい場合は、いつでもTypeプロパティを照会できます。 // 新しい数値を作成 DynValue v1 = DynValue.NewNumber(1); // 新しい文字列を作成 DynValue v2 = DynValue.NewString("ciao"); // 自動変換を使用して別の文字列を作成 DynValue v3 = DynValue.FromObject(new Script(), "hello"); // Number - String - String と表示される Console.WriteLine("{0} - {1} - {2}", v1.Type, v2.Type, v3.Type); // Number - String - 0になると期待すべきではないゴミの数値が表示される Console.WriteLine("{0} - {1} - {2}", v1.Number, v2.String, v3.Number); DynValueの一部のプロパティは、値が特定の型の場合にのみ意味のある値を持つことに注意してください。 例えば、NumberプロパティはDataType.Numberの場合にのみ意味のある値を持つことが保証されており、Stringプロパティも同様です。 DynValueの中身が確実にわかっていない限り、そのプロパティを使用する前に必ずDynValueのTypeを確認するようにしてください。 DynValueが特定の型を含んでいると想定しているのに、別の型を含んでいると、間違いやエラーの一般的な原因になります。 *** ステップ5: タプル [#gddb0564] ご存知のように(あるいは知っておくべきですが)、Luaでは関数から複数の値を返すことができます(その他の状況でも)。 これを処理するために、特別なDynValue型(DataType.Tuple)が使用されます。タプルにはTupleプロパティがあり、これはDynValueオブジェクトの配列で、タプルのメンバーです。 これは聞こえるよりも簡単です。 DynValue ret = Script.RunString("return true, 'ciao', 2*3"); // "Tuple" と表示される Console.WriteLine("{0}", ret.Type); // 以下が表示される: // Boolean = true // String = "ciao" // Number = 6 for (int i = 0; i < ret.Tuple.Length; i++) Console.WriteLine("{0} = {1}", ret.Tuple[i].Type, ret.Tuple[i]); 終わりに DynValuesについて終わりです。 まだまだ学ぶべきことはたくさんありますが、このトピックを始めるにはこれで十分でしょう。 すべてがこれを中心に回っているので、これを肝に銘じておいてください。 ** C#をコールバックする [#o1bf9b18] - ..そしてF#、VB.NET、C++/CLI、Booなども スクリプトは、アプリケーション自体に実装された構成要素から始まるビジネスロジックをカスタマイズできるため、アプリケーションで役立ちます。 ビジネスアプリケーション、ビデオゲーム、またはある種のツールにスクリプトを組み込む場合、最初の関心事はスクリプトとアプリケーションの相互運用性と、オブジェクトの(ある意味での)共有です。 そして、手続き型プログラミングの基本的な構成要素は関数です。 *** 演習1、ステップ1: 階乗に飽きるな [#g99c9a42] これまで使ってきた標準的な階乗スクリプトで基礎を押さえましょう。 private static double CallbackTest() { string scriptCode = @" -- 階乗関数を定義 function fact (n) if (n == 0) then return 1 else return n * fact(n - 1); end end"; Script script = new Script(); script.DoString(scriptCode); DynValue res = script.Call(script.Globals["fact"], 4); return res.Number; } *** 演習1、ステップ2: 乗算関数をカスタマイズする [#o017308d] さて、乗算をホストアプリケーションのAPI関数で実装したいとしましょう。 この場合、そうする目的はありませんが、学ぶためにここにいるので、それが良いアイデアだと仮定しましょう。 private static int Mul(int a, int b) { return a * b; } private static double CallbackTest() { string scriptCode = @" -- 階乗関数を定義 function fact (n) if (n == 0) then return 1 else return Mul(n, fact(n - 1)); end end"; Script script = new Script(); script.Globals["Mul"] = (Func<int, int, int>)Mul; script.DoString(scriptCode); DynValue res = script.Call(script.Globals["fact"], 4); return res.Number; } それだけです! script.Globals["Mul"] = (Func<int, int, int>)Mul; これにより、グローバル環境のMul変数が、アプリケーションのMulデリゲートを指すように設定されます。 ここでは、C#コンパイラを満足させるために、デリゲートをFunc<int, int, int>型にキャストしています。 デリゲートをSystem.Objectにキャストするために使用する方法は何でも構いません。 また、メソッドをstaticに定義しましたが、この場合、それらがstaticである必要は全くありません。 インスタンスメソッドも問題ありません。 *** 演習2: 数値のシーケンスを返す [#ved358ee] 問題: 整数のシーケンスを返すAPI関数を用意する。スクリプトは受け取った数値を合計して、その合計値を返す。 private static IEnumerable<int> GetNumbers() { for (int i = 1; i <= 10; i++) yield return i; } private static double EnumerableTest() { string scriptCode = @" total = 0; for i in getNumbers() do total = total + i; end return total; "; Script script = new Script(); script.Globals["getNumbers"] = (Func<IEnumerable<int>>)GetNumbers; DynValue res = script.DoString(scriptCode); return res.Number; } ここでも、特に難しいことはありません。 IEnumerable(またはIEnumerator)が、スクリプトを実行するためにLuaイテレータに自動的に変換される様子がわかります。 スクリプトには直接実行可能なコードが含まれているため、Call .. callを必要とせずに、DoString時にgetNumbersメソッドにアクセスしていることにも注目してください。 これを覚えておいてください。 DoStringとDoFileは、スクリプトに含まれるコードを即座に実行します! 注意すべき点が1つあります。MoonSharpは、System.Collections.IEnumerableとSystem.Collections.IEnumeratorの型のイテレータを変換できます。 つまり、ジェネリックではないバリアントです。 何らかの理由でジェネリックイテレータを実装する際にジェネリック以外のイテレータを実装しない場合、そのイテレータは機能しません。 上記のように、すべての標準コレクション型とイテレータメソッドは、デフォルトでジェネリック以外のバリアントを実装しているので、あまり心配する必要はありません。 *** テーブルを返す [#me086187] 問題: 今度は整数のシーケンスをテーブルで返すAPI関数を用意する。 private static List<int> GetNumberList() { List<int> lst = new List<int>(); for (int i = 1; i <= 10; i++) lst.Add(i); return lst; } private static double TableTest1() { string scriptCode = @" total = 0; tbl = getNumbers() for _, i in ipairs(tbl) do total = total + i; end return total; "; Script script = new Script(); script.Globals["getNumbers"] = (Func<List<int>>)GetNumberList; DynValue res = script.DoString(scriptCode); return res.Number; } ここでは、List<int>がLuaテーブルに自動的に変換される様子がわかります! 結果のテーブルは、Luaテーブルが通常そうであるように、1から始まるインデックスを持つことに注意してください。 ただし、もっと良い方法があります。 関数内で直接Luaテーブルを構築することができます。 private static Table GetNumberTable(Script script) { Table tbl = new Table(script); for (int i = 1; i <= 10; i++) tbl[i] = i; return tbl; } private static double TableTest2() { string scriptCode = @" total = 0; tbl = getNumbers() for _, i in ipairs(tbl) do total = total + i; end return total; "; Script script = new Script(); script.Globals["getNumbers"] = (Func<Script, Table>)(GetNumberTable); DynValue res = script.DoString(scriptCode); return res.Number; } Tableオブジェクトを使用してテーブルを操作するのがいかに簡単かがわかります。 ただし、注意すべき点が2つあります。 新しいTableオブジェクトを作成するには、実行中のスクリプトへの参照が必要です。 CLR関数にScriptパラメータがあり、それがLuaスクリプトで利用可能な場合、MoonSharpはそれを自動的に設定します。 これは、(このように使用される可能性は低いですが)ScriptExecutionContextやCallbackArgumentsの型でも発生します。 これらが何をするのかわからなくても心配しないでください。 MoonSharpの基本を理解するのに必要ありません! 良い習慣として、可能な限りScriptオブジェクトを保持するようにしてください。 (テーブルの作成など)Scriptオブジェクトを使用してのみ実行できることがいくつかあります。 *** テーブルを受け入れる [#p43434d6] テーブルは自動的にList<T>に変換されます。 例えば、 public static double TableTestReverse() { string scriptCode = @" return dosum { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 } "; Script script = new Script(); script.Globals["dosum"] = (Func<List<int>, int>)(l => l.Sum()); DynValue res = script.DoString(scriptCode); return res.Number; } ただし、これによって一部のプラットフォーム(具体的にはiOS)で問題が発生する可能性があります。 この問題を回避する方法はたくさんありますが(他のチュートリアルで見ることができます)、次の方法では問題なく機能し、さらに高速であると言えます。 public static double TableTestReverseSafer() { string scriptCode = @" return dosum { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 } "; Script script = new Script(); script.Globals["dosum"] = (Func<List<object>, int>)(l => l.OfType<int>().Sum()); DynValue res = script.DoString(scriptCode); return res.Number; } さらに高速な別の方法は、Tableオブジェクトを使用することです。 static double Sum(Table t) { var nums = from v in t.Values where v.Type == DataType.Number select v.Number; return nums.Sum(); } private static double TableTestReverseWithTable() { string scriptCode = @" return dosum { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 } "; Script script = new Script(); script.Globals["dosum"] = (Func<Table, double>)Sum; DynValue res = script.DoString(scriptCode); return res.Number; } しかし、ここではDynValue(s)を扱う必要があります。 これらすべてを理解するには、MoonSharpがLuaの型をC#の型にどのようにマッピングするか、その逆はどうかについて、もう少し深く掘り下げる必要があります。 次の部分で説明します。 ** 自動変換の説明 [#zdd8f29e] - ..今回はコードなし MoonSharpとCLRの統合についてさらに掘り下げる前に、型がどのように相互にマッピングされるかを明確にする必要があります。 残念ながら、逆方向は順方向とは大きく異なるので、2つを別々に分析します。 これは少し複雑すぎませんか? - あなたはそう尋ねるかもしれません。 確かにそうです。 自動的なものは良いですが、失敗すると、ひどく複雑な方法で失敗します。疑問がある場合や、物事が複雑すぎる場合は、物事を単純化する必要があります。 2つの方法があります:DynValueをそのまま使用するか、カスタムコンバーターを使用するかです。 これらの解決策は、正気と単純さを得るだけでなく、自動変換よりもかなり高速です! *** カスタムコンバーター [#iaa65800] 変換プロセスをカスタマイズすることは可能ですが、設定はグローバルで、すべてのスクリプトに影響します。 変換をカスタマイズするには、 Script.GlobalOptions.CustomConverters の適切なコールバックを設定するだけです。 例えば、CLRからスクリプトへの変換が行われるときに、すべての StringBuilder オブジェクトを大文字の文字列に変換したい場合は、次のようにします。 Script.GlobalOptions.CustomConverters.SetClrToScriptCustomConversion<StringBuilder>( v => DynValue.NewString(v.ToString().ToUpper())); テーブルがIList<int>に一致する必要がある場合に、テーブルの変換方法をカスタマイズしたい場合は、次のように記述できます。 Script.GlobalOptions.CustomConverters.SetScriptToClrCustomConversion(DataType.Table, typeof(IList<int>), v => new List<int>() { ... }); コンバーターがnullを返した場合、システムはカスタムコンバーターが存在しないかのように動作し、自動変換を試みます。 *** CLR型からMoonSharp型への自動変換 [#y6c7cab4] この変換は、次の状況で適用されます。 - スクリプトによって呼び出された関数からオブジェクトを返すとき - ユーザーデータのプロパティからオブジェクトを返すとき - インデックス演算子を使用してテーブルに値を設定するとき - DynValue.FromObjectを呼び出すとき - DynValueの代わりにSystem.Objectを取る関数のオーバーロードを使用するとき この変換は実際にはかなり簡単です。次の表は、変換を説明しています。 | CLR型 | C#のわかりやすい名前 | Lua型 | 備考 | |----------------------------------------|---------------------|----------|--------------------------------------------------------------| | void | | (no value) | これはメソッドの戻り値にのみ適用できます。 | | null | | nil | nullはすべてnilに変換されます。 | | MoonSharp.Interpreter.DynValue | | * | DynValueはそのまま渡されます。 | | System.SByte | sbyte | number | | | System.Byte | byte | number | | | System.Int16 | short | number | | | System.UInt16 | ushort | number | | | System.Int32 | int | number | | | System.UInt32 | uint | number | | | System.Int64 | long | number | 変換により、暗黙的に精度が失われる可能性があります。 | | System.UInt64 | ulong | number | 変換により、暗黙的に精度が失われる可能性があります。 | | System.Single | float | number | | | System.Decimal | decimal | number | 変換により、暗黙的に精度が失われる可能性があります。 | | System.Double | double | number | | | System.Boolean | bool | boolean | | | System.String | string | string | | | System.Text.StringBuilder | | string | | | System.Char | char | string | | | MoonSharp.Interpreter.Table | | table | | | MoonSharp.Interpreter.CallbackFunction | | function | | | System.Delegate | | function | | | System.Object | object | userdata | 型がuserdataに登録されている場合のみ。 | | System.Type | | userdata | 型がuserdataに登録されている場合のみ。静的メンバーアクセス。 | | MoonSharp.Interpreter.Closure | | function | | | System.Reflection.MethodInfo | | function | | | System.Collections.IList | | table | 結果のテーブルは1からインデックスが付けられます。すべての値はこれらのルールを使用して変換されます。 | | System.Collections.IDictionary | | table | すべてのキーと値はこれらのルールを使用して変換されます。 | | System.Collections.IEnumerable | | iterator | すべての値はこれらのルールを使用して変換されます。 | | System.Collections.IEnumerator | | iterator | すべての値はこれらのルールを使用して変換されます。 | これには、コレクションのかなり良いカバレッジが含まれています。ほとんどのコレクションは、IList、IDictionary、IEnumerable、IEnumeratorのいずれかを実装しているためです。 独自のコレクションを書く場合は、これらの非ジェネリックインターフェイスの1つを実装することを忘れないでください。 このロジックを使用して変換できない値はすべて、ScriptRuntimeExceptionをスローします。 *** MoonSharp型からCLR型への標準の自動変換 [#b443915a] 反対の変換はかなり複雑です。 実際、2つの異なる変換パスがあります。 「標準」の変換パスと、「制約付き」の変換パスです。 最初の変換パスは、実際に何を受け取りたいかを指定せずにDynValueをオブジェクトに変換するように要求するたびに適用され、もう一方の変換パスは、一致させるSystem.Typeがある場合に適用されます。 これは以下の場合に使用されます。 - DynValue.ToObjectを呼び出すとき - インデクサーを使用してテーブルから値を取得するとき - 制約付き変換の特定のサブケース(下記参照) ここでは、デフォルトの変換を見ていきます。実際には簡単です。 | MoonSharp型 | CLR型 | 備考 | |----------------------------------------|----------------------------------------|---------------------------------------------------------------| | nil | null | nilであるすべての値に適用されます。 | | boolean | System.Boolean | | | number | System.Double | | | string | System.String | | | function | MoonSharp.Interpreter.Closure | DataTypeがFunctionの場合。 | | function | MoonSharp.Interpreter.CallbackFunction | DataTypeがClrFunctionの場合。 | | table | MoonSharp.Interpreter.Table | | | tuple | MoonSharp.Interpreter.DynValue[] | | | userdata | (special) | userdataに格納されているオブジェクトを返します。「静的」userdataの場合は、Typeが返されます。 | このロジックを使用して変換できない値はすべて、ScriptRuntimeExceptionをスローします。 *** MoonSharp型からCLR型への制約付き自動変換 [#b3fb4110] しかし、制約付き自動変換ははるかに複雑です。この場合、MoonSharpの値は、特定の型のCLR変数に格納する必要があり、これらの変換を行う方法はほぼ無限にあります。 これは以下の場合に使用されます。 - DynValue.ToObject<T>を呼び出すとき - CLR関数呼び出しやプロパティ設定でスクリプト値をパラメーターに変換するとき MoonSharpは値の変換に非常に努力しますが、特にテーブルが関与する場合は、変換には確実に限界があります。 この場合、変換は単純なマッピングのテーブルではなく、プロセスに近いので、ターゲットの型を1つずつ分析していきましょう。 *** 特殊なケース [#h25b1ae1] - ターゲットがMoonSharp.Interpreter.DynValue型の場合は変換されず、元の値が返されます。 - ターゲットがSystem.Object型の場合は、前述のデフォルトの変換が適用されます。 - マッピングされるnil値の場合、nullは参照型とnullable値型にマッピングされ、一部のケース(たとえば、デフォルトが指定されている関数呼び出しなど)では、non-nullable値型にデフォルト値に一致させる試行が行われます。それ以外の場合は例外がスローされます。 - 値が提供されていない場合は、可能であればデフォルト値が使用されます。 *** 文字列 [#gde47277] 文字列は、System.String、System.Text.StringBuilder、System.Charに自動的に変換できます。 *** ブール値 [#lb1bcc8c] ブール値は、System.BooleanやSystem.Nullable<System.Boolean>に自動的に変換できます。また、System.String、System.Text.StringBuilder、System.Charにも変換できます。 *** 数値 [#dc22c32c] 数値は、System.SByte、System.Byte、System.Int16、System.UInt16、System.Int32、System.UInt32、System.Int64、System.UInt64、System.Single、System.Decimal、System.Doubleとそれらのnullableバージョンに自動的に変換できます。また、System.String、System.Text.StringBuilder、System.Charにも変換できます。 *** 関数 [#z9738d47] スクリプト関数は、MoonSharp.Interpreter.ClosureまたはMoonSharp.Interpreter.ScriptFunctionDelegateに変換されます。コールバック関数は、MoonSharp.Interpreter.ClrFunctionまたはSystem.Func<ScriptExecutionContext, CallbackArguments, DynValue>に変換されます。 *** ユーザーデータ [#jbe4344d] ユーザーデータは、「静的」でない場合にのみ変換されます(チュートリアルのuserdataセクションを参照)。変換対象のオブジェクトを割り当てることができる型の場合に変換されます。 また、オブジェクトのToString()メソッドを呼び出すことで、System.String、System.Text.StringBuilder、System.Charにも変換できます。 *** テーブル [#g19e08d9] テーブルは以下に変換できます。 - MoonSharp.Interpreter.Table - もちろん - Dictionary<DynValue, DynValue>から割り当て可能な型。 - Dictionary<object, object>から割り当て可能な型。キーと値は、デフォルトのマッピングを使用してマッピングされます。 - List<DynValue>から割り当て可能な型。 - List<object>から割り当て可能な型。要素は、デフォルトのマッピングを使用してマッピングされます。 - DynValue[]から割り当て可能な型。 - object[]から割り当て可能な型。要素は、デフォルトのマッピングを使用してマッピングされます。 - T[]、IList<T>、List<T>、ICollection<T>、IEnumerable<T>。ここで、Tは変換可能な型です(他のリストなどを含む)。 - IDictionary<K,V>、Dictionary<K,V>。ここで、KとVは変換可能な型です(他のリストなどを含む)。 ジェネリックや型付き配列への変換には、次の制限があることに注意してください。 - リフレクションを使用して実行時に型が作成されるため、速度が遅くなります。 - アイテムの型が値型の場合、iOSやAOTモードで実行される他のプラットフォームとの非互換性を引き起こす可能性があります。事前にテストを行ってください。 これらの問題を補うために、開発の後半でカスタムコンバーターを追加することもできます! 一部の変換(List<int>への変換など)は、Unity/iOSなどのAOTプラットフォームで問題を引き起こす可能性があります。この種の問題を回避するには、カスタムコンバーターを登録してください。 iOSなどのAOTプラットフォームをターゲットにする場合は、値型のジェネリックには常に注意してください。参照型のジェネリックは、一般的に問題を引き起こしません。 ** オブジェクトの共有 [#obbc6f3a] - LuaとC#はお互いに話す 注:このページに記載されている一部の機能は、masterブランチの現在の状態を反映しています(つまり、最新のリリースには存在しない機能かもしれません)。 MoonSharpの便利な機能の1つは、.NETオブジェクトをスクリプトと共有できることです。 デフォルトでは、型はすべてのパブリックメソッド、パブリックプロパティ、パブリックイベント、パブリックフィールドをLuaスクリプトと共有します。 MoonSharpVisibleアトリビュートを使用して、このデフォルトの可視性をオーバーライドできます。 CLRコードとスクリプトコード間のインターフェイスとして専用のオブジェクトを使用することをお勧めします(アプリケーションの内部モデルをスクリプトに公開するのとは対照的です)。 多くのデザインパターン(アダプター、ファサード、プロキシなど)が、そのようなインターフェイス層の設計に役立ちます。 これは特に以下のために重要です。 - スクリプトができること、できないことを制限する(セキュリティ!modの作成者にエンドユーザーの個人ファイルを削除する方法を見つけてほしいですか?) - スクリプト作成者にとって意味のあるインターフェイスを提供する - インターフェイスを別々に文書化する - スクリプトを破ることなく、内部ロジックとモデルを変更できるようにする これらの理由から、MoonSharpはデフォルトで、スクリプトで使用可能な型の明示的な登録を必要とします。 スクリプトを信頼できるシナリオでは、` UserData.RegistrationPolicy = InteropRegistrationPolicy.Automatic; を使用して自動登録をグローバルに有効にできます。 危険なので、警告しておきます。 *** メニューにあるもの [#dda0ae94] では、メニューにあるものを見てみましょう。 - まず、型記述子について説明しましょう - 舞台裏で何が起こっているのか、そしてインターオペラビリティシステム全体をどのようにオーバーライドできるかを説明する少しの理論 - シンプルに保つ - 始めるための最も簡単な方法 - 少し複雑 - 少し複雑さと詳細を加えて掘り下げる - 静的メンバーの呼び出し - 静的メンバーを呼び出す方法 - ':' と '.' のどちらを使うべきか - メソッドの呼び出し方に関する簡単な質問 - オーバーロード - オーバーロードがどのように処理されるか - ByRefパラメーター(C#のref/out) - ref/outパラメーターがどのように処理されるか - インデクサー - インデクサーの処理方法 - ユーザーデータ上の演算子とメタメソッド - 演算子のオーバーロード方法など - 拡張メソッド - 拡張メソッドの使用方法 - イベント - イベントの使用方法 - インターオペラビリティアクセスモード - インターオペラビリティアクセスモードとその動作 - MoonSharpHiddenとMoonSharpVisibleによる可視性の変更 - メンバーの可視性をオーバーライドする方法 - メンバーの削除 - メンバーの可視性を削除する方法 たくさんありますね。では、始めましょう。 *** 型記述子 [#ce23a6ed] まず、型記述子について説明しましょう 最初に、インターオペラビリティがどのように実装されているかについて少し理論を説明します。 すべてのCLR型は、CLR型をスクリプトに記述する役割を持つ「型記述子」にラップされます。 型をインターオペラビリティ用に登録するとは、型を記述子(MoonSharpが自身で作成できる)に関連付けることを意味します。 この記述子は、メソッド、プロパティなどのディスパッチに使用されます。 次のセクション以降では、MoonSharpが提供する「自動」記述子について説明しますが、速度、機能の追加、セキュリティなどのために独自の記述子を実装することもできます。 *** 独自の記述子を実装したい場合 [#i28f4d6d] 独自の記述子を実装したい場合(簡単ではなく、必要でない限り行うべきではありません)、次のような方法があります。 - 独自の型を記述するためのアドホックなIUserDataDescriptorを作成する - これが最も難しい方法です - 型にIUserDataTypeインターフェイスを実装させる。これは簡単ですが、オブジェクトのインスタンスなしで静的メンバーを処理できないことを意味します。 - StandardUserDataDescriptorを拡張または埋め込み、必要な側面を変更しながら、残りの動作を維持する。 記述子の作成を助けるために、次のクラスが利用できます。 - StandardUserDataDescriptor - これはMoonSharpが実装する型記述子です - StandardUserDataMethodDescriptor - これは単一のメソッド/関数の記述子です - StandardUserDataOverloadedMethodDescriptor - これはオーバーロードされたメソッドや拡張メソッドの記述子です - StandardUserDataPropertyDescriptor - これは単一のプロパティの記述子です - StandardUserDataFieldDescriptor - これは単一のフィールドの記述子です *** ユーザーデータとしての値型とのインターオペラビリティに関する注意事項。 [#jcae2115] 値型をパラメーターとして渡す関数を呼び出す場合と同様に、スクリプトはユーザーデータのコピーを操作するため、たとえば、ユーザーデータのフィールドを変更しても、元の値には反映されません。 繰り返しになりますが、これは値型の標準的な動作と変わりませんが、人を驚かせるのに十分です。 さらに、値型は参照型ほどの最適化の範囲をサポートしていないため、値型での一部の操作は参照型よりも遅くなる可能性があります。 *** シンプルに保つ [#b3bac791] では、最初の例を見てみましょう。 [MoonSharpUserData] class MyClass { public double calcHypotenuse(double a, double b) { return Math.Sqrt(a * a + b * b); } } double CallMyClass1() { string scriptCode = @" return obj.calcHypotenuse(3, 4); "; // MoonSharpUserData型をすべて自動的に登録 UserData.RegisterAssembly(); Script script = new Script(); // MyClassのインスタンスをスクリプトのグローバルに渡す script.Globals["obj"] = new MyClass(); DynValue res = script.DoString(scriptCode); return res.Number; } ここでは以下のことを行っています。 - [MoonSharpUserData]アトリビュートを持つクラスを定義 - MyClassオブジェクトのインスタンスをスクリプトのグローバルとして渡す - スクリプトからMyClassのメソッドを呼び出す。コールバックのマッピングルールがすべて適用されます *** 少し複雑 [#x8b60ac6] もう少し複雑な例を試してみましょう。 class MyClass { public double CalcHypotenuse(double a, double b) { return Math.Sqrt(a * a + b * b); } } static double CallMyClass2() { string scriptCode = @" return obj.calcHypotenuse(3, 4); "; // MyClassだけを明示的に登録 UserData.RegisterType<MyClass>(); Script script = new Script(); // ユーザーデータを明示的に作成 DynValue obj = UserData.Create(new MyClass()); script.Globals.Set("obj", obj); DynValue res = script.DoString(scriptCode); return res.Number; } *** 大きな違い [#a756817e] 大きな違いは以下の通りです。 - [MoonSharpUserData]アトリビュートはありません。もう必要ありません。 - RegisterAssemblyの代わりに、RegisterTypeを呼び出して特定の型を登録します。 - ユーザーデータのDynValueを明示的に作成します。 また、C#コードではメソッドがCalcHypotenuse と呼ばれていましたが、LuaスクリプトではcalcHypotenuse として呼ばれていることに注目してください。 他のバージョンが存在しない限り、MoonSharpは異なる言語の構文規則間の一貫性を高めるために、いくつかの限定的な方法でケースを自動的に調整してメンバーを一致させます。たとえば、SomeMethodWithLongNameというメンバーは、LuaスクリプトからsomeMethodWithLongNameやsome_method_with_long_nameとしてアクセスすることもできます。 *** 静的メンバーの呼び出し [#b88a2ae1] calcHypotenuse メソッドが静的であるとしましょう。 [MoonSharpUserData] class MyClassStatic { public static double calcHypotenuse(double a, double b) { return Math.Sqrt(a * a + b * b); } } *** 呼び出す方法 [#x240cc3a] これを呼び出すには2つの方法があります。 *** 方法1 - 静的メソッドはインスタンスから透過的に呼び出すことができます - 何もする必要はなく、すべて自動的に行われます [#ecac98ea] double MyClassStaticThroughInstance() { string scriptCode = @" return obj.calcHypotenuse(3, 4); "; // MoonSharpUserData型をすべて自動的に登録 UserData.RegisterAssembly(); Script script = new Script(); script.Globals["obj"] = new MyClassStatic(); DynValue res = script.DoString(scriptCode); return res.Number; } *** 別の方法 [#of33f4f5] - 型を直接渡す(またはUserData.CreateStaticメソッドを使用する)ことで、プレースホルダーのユーザーデータを作成できます。 double MyClassStaticThroughPlaceholder() { string scriptCode = @" return obj.calcHypotenuse(3, 4); "; // MoonSharpUserData型をすべて自動的に登録 UserData.RegisterAssembly(); Script script = new Script(); script.Globals["obj"] = typeof(MyClassStatic); DynValue res = script.DoString(scriptCode); return res.Number; } ':' と '.' のどちらを使うべきか 上記の例のコードを考えると、この構文を使うべきでしょうか return obj.calcHypotenuse(3, 4); それともこちらでしょうか? return obj:calcHypotenuse(3, 4); 99.999%の場合、違いはありません。MoonSharpは、ユーザーデータに対して呼び出しが行われていることを認識し、それに応じて動作します。 *** 違いが出る可能性があるコーナーケース [#r13135cc] 違いが出る可能性があるコーナーケースがあります。たとえば、プロパティがデリゲートを返し、そのデリゲートをインスタンスとして元のオブジェクトですぐに呼び出す場合などです。これはまれなシナリオですが、そのような場合は手動で処理する必要があります。 *** オーバーロード [#v6fea100] オーバーロードされたメソッドがサポートされています。オーバーロードされたメソッドのディスパッチは、いくぶん黒魔術的で、C#のオーバーロードディスパッチほど決定的ではありません。 これは、いくつかの曖昧さが存在するためです。たとえば、オブジェクトはこれらの2つのメソッドを宣言できます。 void DoSomething(int i) { ... } void DoSomething(float f) { ... } Luaのすべての数値がdoubleであることを考えると、MoonSharpはどのメソッドをディスパッチすべきかをどのように知ることができるでしょうか? この問題を解決するために、MoonSharpは入力型に対するすべてのオーバーロードのヒューリスティック係数を計算し、最適なオーバーロードを選択します。MoonSharpがオーバーロードを間違った方法で解決していると思われる場合は、フォーラムやdiscordに報告して、ヒューリスティックを調整してください。 MoonSharpは、ヒューリスティックの重みを可能な限り安定させようとしており、メソッド間のスコアが同点の場合は、常に決定的に同じものを選択します(ビルドやプラットフォーム間で一貫した動作を提供するため)。 とは言え、MoonSharpが考えているのとは異なるオーバーロードを選択することは十分にありえます。 そのため、オーバーロードが同等の処理を行うようにすることが非常に重要です。これは、間違ったオーバーロードを呼び出すことの影響を最小限に抑えるためです。これは、ベストプラクティスであるべきですが、ここでその概念を強化する価値があります。 - ByRefパラメーター(C#のref/out) - ByRefメソッドパラメーター * 参考 [#l6580144] ** MoonSharpによるUnityとLuaの連携 [#r91a70b3] https://qiita.com/massu2357/items/425cc93b5965ceea29aa
テキスト整形のルールを表示する