LoginSignup
15
14

More than 5 years have passed since last update.

Sublime Text の Unity 用プラグインをサポートするプラグインを作ってみました。

Last updated at Posted at 2013-06-04

開発趣旨

Unity のスクリプトエディタとして Sublime Text を使用しています。
その際にいくつかの Unity 関連のプラグインを使用させて頂いています。
これらのプラグインはとても便利ですが、前提となる初期設定が煩雑なため、これを補うプラグインを作成しました。
特に、Unity から直接スクリプトを(ダブルクリック等で)開いた際にプロジェクトが開かれると便利だと考えました。

プラグインサポート内容

以下のことを勝手に行ってくれるプラグインです。
1. Sublime プロジェクトの作成
2. ビルド設定をUnity3D Build Systemにする
3. CompleteSharp用のDLL登録設定
4. Unity3Dのシンタックスハイライトを適応する

プロジェクトの作成がメインです。
Sublime プロジェクトが無いと UnityBuild が実行されません。
CompleteSharp用の設定もプロジェクトに登録します。

Unity3D Build System
https://github.com/fredpointzero/UnityBuild

Unity3D Syntax Highlighting
https://github.com/UnicornForest/Unity3D

CompleteSharp
https://github.com/quarnster/CompleteSharp

プラグインソースコード

# coding=utf-8
import sublime, sublime_plugin
import os, subprocess

class UnityPluginSupportCommand( sublime_plugin.WindowCommand ) :
    def run( self ) :
        try :
            self.message_to_console( "Start ." )

            # Viewの読み込みが間に合わなかった等の場合に終了
            if self.window.active_view() == None :
                raise LoadProjectException( "None Active view ." )
            current_file_name   = self.window.active_view().file_name()

            # Sublime Text を直接起動した場合はファイルが開かれていないので終了
            if current_file_name == None :
                raise LoadProjectException( "This is Untitled file ." )

            # Unity プロジェクトのパスを検索
            project_path    = self.search_project_path()
            if project_path == "/" :
                raise LoadProjectException( "This isn't Unity Project ." )

            # プロジェクトの名前を決める
            tmp, project_name   = os.path.split( project_path )

            # プロジェクトファイルの名前を決める
            project_file_name   = project_name + ".sublime-project"
            project_full_name   = os.path.join( project_path, project_file_name )

            # Sublime プロジェクトファイルが存在しなかったら作成する
            if os.path.exists( project_full_name ) == False :
                self.make_project_file( project_full_name )

            # Sublime Text のコマンドラインツールを使用してプロジェクトを開く、ST2ではプロジェクトを読み込んだ後にファイルをリロードする必要がある。
            self.message_to_console( "Open Sublime Project File ." )
            subl_path   = "/Applications/Sublime Text 2.app/Contents/SharedSupport/bin/subl"
            subprocess.Popen( [subl_path, "--add", "--project", project_full_name] )
            sublime.set_timeout(
                lambda : subprocess.Popen( [subl_path, "--add", current_file_name] ),
                300 )

            # Unity3D Build Systemがインストールされていたら、ビルドシステムを切り替える
            unity_build_system  = sublime.packages_path() + "/Unity3D Build System" + "/Unity.sublime-build"
            if os.path.exists( unity_build_system ) == True :
                sublime.set_timeout(
                    lambda : self.window.run_command( "set_build_system", { "file": unity_build_system } ),
                    300 )

        except LoadProjectException as e :
            self.message_to_console( e.message )

        finally :
            self.message_to_console( "Finish ." )

    # Sublime プロジェクトを作成する
    def make_project_file( self, new_project_full_name ) :
        self.message_to_console( "Make Sublime Project File ." )

        # プロジェクトに登録するプロジェクトパスを取得
        project_path, filename  = os.path.split( new_project_full_name )

        fp  = open( new_project_full_name, "w" )
        fp.write(   '{\n'
                    '   "folders" :\n'
                    '   [\n'
                    '       {\n'
                    '           "follow_symlinks" : true,\n'
                    '           "path" : "' + project_path + '"\n'
                    '       }\n'
                    '   ],\n'
                    # ついでに、CompleteSharp用の設定
                    '   "settings" :\n'
                    '   {\n'
                    '       "completesharp_assemblies" :\n'
                    '       [\n'
                    '           "/Applications/Unity/Unity.app/Contents/Frameworks/Managed/UnityEngine.dll",\n'
                    '           "/Applications/Unity/Unity.app/Contents/Frameworks/Managed/UnityEditor.dll",\n'
                    '           "${project_path:Library}/ScriptAssemblies/Assembly-CSharp.dll",\n'
                    '           "${project_path:Library}/ScriptAssemblies/Assembly-CSharp-Editor.dll"\n'
                    '       ]\n'
                    '   }\n'
                    '}\n' )
        fp.close()

    # Unity プロジェクトのパスを検索し、そのパスを返す(存在しなければルートを返す)
    def search_project_path( self ) :
        # 現在開かれているファイルのパスを取得
        out_path    = os.path.dirname( self.window.active_view().file_name() )

        # パスをさかのぼりながら、Assetsフォルダの有無を調べる
        while out_path != "/" :
            out_path, tail_path = os.path.split( out_path )

            # Assetsフォルダが見つかったら、検索を修了する
            if tail_path == "Assets" :
                break

        return out_path

    # コンソールにログを表示するための関数
    def message_to_console( self, message ) :
        print( "Load Project for Unity : " + message )

# このプラグイン用の例外処理クラス
class LoadProjectException( Exception ) :
    def __init__( self, message ) :
        self.message    = message


# 指定のファイルが Unity プロジェクトに含まれているかどうかチェックする
def is_unity_project( file_name ) :
    # 現在開かれているファイルのパスを取得
    out_path    = os.path.dirname( file_name )

    # パスをさかのぼりながら、Assetsフォルダの有無を調べる
    while out_path != "/" :
        out_path, tail_path = os.path.split( out_path )

        # Assetsフォルダが見つかったら、検索を修了する
        if tail_path == "Assets" :
            return True

    else :
        return False

# Unity3D Syntax Highlightingがインストールされていたら、シンタックスを適応する
class UnitySyntaxListener( sublime_plugin.EventListener ) :
    def on_load( self, view ) :
        unity_syntax_path   = sublime.packages_path() + "/Unity3D"
        if os.path.exists( unity_syntax_path ) == False :
            return

        if is_unity_project( view.file_name() ) == False :
            return

        tmp, ext    = os.path.splitext( view.file_name() )
        if ext == ".cs" :
            unity_syntax = unity_syntax_path + "/UnityC#.tmLanguage"
        elif ext == ".js" :
            unity_syntax = unity_syntax_path + "/UnityJavaScript.tmLanguage"
        else :
            return

        view.set_syntax_file( unity_syntax )

# Sublime Text の起動時に UnityPluginSupport コマンドを呼び出す
sublime.set_timeout( lambda : sublime.active_window().run_command( "unity_plugin_support" ), 500 )

コードについて

  • はっきり言ってとても個人用です。
  • Macでしか動作しないはずです。
  • Sublime Text 2 用です。
  • 適当な名前の.pyファイルとしてPackages/Userフォルダに保存すれば動作すると思います。
  • Pythonの練習用に作ったということもあり、コードはお察しです・・。
  • いかなるトラブルの保証もしませんが、自由にご使用いただいて構いません。
  • 参考になる処理でもあれば幸いに思います。

開発メモ

  • Sublime Text API にはプロジェクトを作成したりする機能が無いっぽい
  • プロジェクトを作成するコマンドも非公開っぽい
  • プロジェクトファイルはPythonそのものの機能でテキストファイルとして作成しました
  • プロジェクトを開くAPIやコマンドも無いっぽい
  • Sublime Text のコマンドラインを使用して開いて見ました
  • コマンドラインを呼ぶ際に --add オプションを付けることで、すでに開かれている Sublime Text に展開できた
    • ただし、プロジェクトを開くとViewが閉じてしまったので、むりくり開き直すようにした
    • この問題はST2限定で、ST3だと必要ない
  • ST3だとプロジェクトの確認を行える関数があるので、プロジェクトをすでに開いている場合に処理をしない等のことが可能
  • プラグインファイル(Packagesフォルダ以下に置かれた.py)に直書きされたスクリプトは、プラグインの読み込み時に実行される(上記コードの最下段sublime.set_timeout()等)
    • このとき、ST起動→(ウィンドウの形成?)→ファイルの読み込み、となるためファイルが読み込まれる前にスクリプトが実行されてしまう。
    • よって、直接書けるスクリプトでもset_timeout()を利用して遅延実行する(ファイルのロードを待つ方法があればそのほうが良いと思う)
    • イベントリスナの on_load を使用すれば、これを気にしなくとも良いが、当然ファイルを読み込むたびにスクリプトが実行される
    • また、ST3だとST起動と同時にファイルを開いた場合 on_load が呼ばれないっぽい(仕様かバグかは不明)
  • このプラグインからUnityを操作しAssets/Sync MonoDevelop Project を実行したかったがうまく実装できなかった
    • STプラグインのPython上からUnityプロジェクトのAssets/Editorフォルダを作成し、そのなかにAssets/Sync MonoDevelop Projectコマンドを実行するスクリプトファイルを作成。Unityをコマンドラインからバッチモードで起動し、作成したスクリプトを実行する実装を試みた。この方法だとUnityがすでに対象のプロジェクトを開いていると二重起動が禁止されており失敗する。Unityが起動していなければソリューションが生成されることを確認した。
  • 新規で作成したUnityプロジェクトの場合「Assets/Sync MonoDevelop Project」コマンドの実行を忘れないこと
  • APIが増えていることや動作についてはST3のほうが便利(でも正式にアップグレードするときに、ST2と遜色ないのに追加料金かかるとか嫌だなぁ・・。やっぱり秀丸がいいよ秀丸!)

副産物

プロジェクトを直接開けるようにする

Unityと関係なく、「.sublime-project」ファイルをプロジェクトとして直接(ダブルクリック等で)開きたいこともあると思います。
このとき、.sublime-projectの拡張子をSublime Textに普通に関連付けをすると、プロジェクトではなく、ファイルの中身を開いてしまいます。

プロジェクトをダブルクリック等で開けるようにするには「Automator」を使用する方法があります。

Automator は MacOS の標準アプリ。
既存のアプリや操作をバッチ処理したりするのに使用するらしく、それらを行う新たなアプリを生成出来ます。

今回の目的では「アプリケーション」→「シェルスクリプトの実行」として、以下のコマンドを実行します。
「/Applications/Sublime\ Text.app/Contents/SharedSupport/bin/subl --project "$1"」

また、「入力の引き渡し方」を「引数として」に変更します。

以上の設定を行い「保存」すると新しいアプリができるので、「.sublime-project」ファイルにそのアプリを関連付ける

上記のコマンドはプロジェクトを別ウィンドウで開いてしまうため、それが嫌だったら、--addオプションを追加すると良いと思います。
「/Applications/Sublime\ Text.app/Contents/SharedSupport/bin/subl --add --project "$1"」

このコマンドなら「UnityからスクリプトをSTで開いたけど、プロジェクトが開かれてなかった」場合に該当ファイルをダブルクリックで開けるかもしれません。(メニューから開けばいい気もするけど・・)

(MacOS自体そんなに詳しくないので他の方法で普通に出来るのかもしれませんが・・)

15
14
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
14