4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【Unity】macOS向けビルドしたアプリにドラッグアンドドロップを可能にするUniDragAndDropForMacを作りました

Last updated at Posted at 2020-08-17

UnityからmacOS向けのアプリをビルドして、そのアプリに対しドラッグアンドドロップを実装するという知見は、僕が調べた限り見つける事は出来なかったため、UniDragAndDropForMacというネイティブプラグインを作りました。
macDD.gif
GitHub:baobao/UniDragAndDropForMac

以下技術的な話になります。

そもそも出来るのか?

参考 : 【Unity】【C#】ランタイム時にファイルをドラッグ&ドロップして取得する(Windows のみ)
調査する中Windows版での実装方法は見つかりました。ソースをチラ見すると、どうやらWindowsのネイティブ機能を使って実装している事が分かったので、macOSでも同様にネイティブの機能を使えば実装できるだろうと思いました。

用意されていないネイティブ機能を使う場合はネイティブプラグインで解決させる

まずはCocoaフレームワークの中にドラッグアンドドロップのAPIを調査します。

Swiftはほぼほぼ未経験だったので、成果物を作り上げるスピードを優先させるため今回はObjective-C(以下:ObjC)で実装することにしました(後者の記事を参考にさせて頂きました)。

  • C#側からObjCに文字列(ファイルパス)を送ってもらう関数ポインタを渡す
  • Unityが作ったネイティブViewにアクセス
  • ObjC側でドラッグアンドドロップされたファイルパスをC#に送る

このような手順で実装していきます。

C#側からObjCに文字列(ファイルパス)を送ってもらう関数ポインタを渡す

UniDragAndDrop.cs
// コールバックのデリゲート
delegate void callback_delegate(string val);

// ネイティブプラグインの実行、関数ポインタをobjCにわたす
[DllImport("UniDragAndDrop")]
private static extern void Initialize(callback_delegate callback);

// objCから実行されるコールバック
[MonoPInvokeCallback(typeof(callback_delegate))]
private static void cs_callback(string dragAndDropPath)
{
    // Do Something...
}

C#側からObjCにアクセスする部分は上記のようなコードになっています。


UniDragAndDrop.mm
// ドラッグアンドドロップを実装したViewをアプリ全面に生成
GetDragAndDropFilePath *ddview = [[GetDragAndDropFilePath alloc] initWithFrame:view.frame];

// C#の関数ポインタを渡す
[ddview setCallback:callback];

GetDragAndDropFilePathはNSImageViewを継承したドラッグアンドドロップを実装するViewです。これをアプリ全面に表示することでドラッグアンドドロップが出来るようにしています。

Unityが作ったネイティブViewにアクセス

NSArray *ar = [NSApp orderedWindows];
NSWindow *window = [ar objectAtIndex:0];
NSView *view = [window contentView];

この3行の処理でUnityが実行しているネイティブのNSView(*view)を取得しています。
*viewに対してドラッグアンドドロップ用のView(前述のGetDragAndDropFilePath)を追加していきます。

ObjC側でドラッグアンドドロップされたファイルパスをC#に送る

GetDragAndDropFilePath.h
# import <Foundation/Foundation.h>
# import <AppKit/AppKit.h>

typedef void (*cs_callback)(const char*);

@interface GetDragAndDropFilePath : NSImageView {
    cs_callback _callback;
}
- (void)setCallback:(cs_callback) callback;
@end

NSImageViewを継承したクラスを作成してドラッグアンドドロップを実装しています。

GetDragAndDropFilePath.mm
- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender {
    NSURL* fileURL = [NSURL URLFromPasteboard: [sender draggingPasteboard]];
    NSString *url = fileURL!=NULL ? [fileURL path] : @"";
    // C#にファイルパスを送る
    _callback([url UTF8String]);
    return NO;
}
@end 

ドロップが完了したタイミングで予め渡されていたC#の関数に対してファイルパスを送るようにしています。

UniDragAndDropForMacとしてリリース

どうせ作るならOSSで作りたい欲求が有るので、以下のリポジトリにObjCソース含め全て公開しています。
UniDragAndDropForMac

インストール方法

UniDragAndDropForMac.unitypackageを用意しているのでインポートして使ってください。

最後に

欲しい物が無い、探しても見つからない、そんな時は今後もネイティブの力を使って作っていこうと思います。

環境

  • Unity2019.4.4f1
  • macOS Catalina 10.15.5
4
3
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
4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?