普段はUnity C#でスマホゲームを作っていますが、今回やりたい事がその範囲で対応できなかったので、iOS/AndroidでPluginを書くことにしました。
やりたかったこと
スマホ内の曲を一覧表示し、選択した曲を再生する。
作成にあたり、他記事、Githubを大分参考にさせていただきました。
Swift,Javaは未経験なので、突っ込み所あれば突っ込んで頂けるとありがたいです。
pluginを呼び出すCSharp
MediaPicker
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Runtime.InteropServices;
/*
* 音楽再生.
* */
public class MusicPlay : MonoBehaviour {
#if UNITY_IOS
[DllImport ("__Internal")]
public static extern void MusicOpen_();
#elif UNITY_ANDROID
public musicPicker musicPicker_;
#endif
void Awake(){
#if UNITY_ANDROID
musicPicker_ = gameObject.AddComponent<musicPicker> ();
#endif
}
public void openMedia(){
#if UNITY_ANDROID
musicPicker_.musicList();
#elif UNITY_IOS
Debug.Log(">>> MusicOpen_ call");
MusicOpen_();
#else
#endif
}
}
iOS
Objective-Cは書けそうになかったので、Swiftで書きました。
UnityC# -> Obj-C -> Swift の呼び出しが可能と解り、以下のようなコードになりました。MusicOpen_以外はSwiftです。
NSObject(詳細ははっきりしませんでしたが、必須?のようです)とprotcolを継承したViewBindから、UIViewControllerであるViewControllerを作成しています。
StoryBoardなども使う事無くテーブル一覧を作成できるので素晴らしいです。
UnityのプロジェクトフォルダのAssets/Plugins/iOSの下に、以下のコードを置いてください。
MusicOpen.mm
# import <Foundation/Foundation.h>
# import <XXX-Swift.h> // XCodeが作成するヘッダ。扱いについては別の良記事が参考に
extern "C" {
void MusicOpen_()
{
NSLog(@">>> MusicOpen_");
[ViewBind initTableView] ;
}
}
ViewBind.swift
import Foundation
import MediaPlayer
import UIKit
open class ViewBind : NSObject, audiosend{
static var audioPlayer: AVAudioPlayer! = nil
static var view: ViewController! = nil
static func initTableView(){
//UnityのViewControllerの取得.
let unityViewController = UnityGetGLViewController()
view = ViewController()
let nav = UINavigationController(rootViewController: view)
// 画面遷移.
//使ってませんが、無名関数呼び出しのメモのために置いておく
//unityViewController?.present(nav, animated: true, completion: (() -> Void) { SendMe() })
unityViewController?.present(nav, animated: true, completion: nil)
}
func SendMe( ){
}
//protocolで呼び出される
//C#のdelegateとは大分違います
//swiftのdelegateについてはqiitaにも記事があって参考になりました
func audioSendMethod( audio: AVAudioPlayer)
{
ViewBind.audioPlayer = audio
}
}
ViewController.swift
import UIKit
import MediaPlayer
import AVFoundation
//protcol
//ViewBind と ViewControllerを audiosend を介して、delegate呼び出し可能にします
@objc protocol audiosend {
func audioSendMethod( audio: AVAudioPlayer)
}
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var myTableView: UITableView!
var albums: [SongInfo] = []
var songQuery: SongQuery = SongQuery()
var audio: AVAudioPlayer! = nil
weak var delegate: audiosend?
override func viewDidLoad() {
super.viewDidLoad()
albums = songQuery.get()
if(albums.count == 0){
self.dismiss(animated: true, completion: nil)
return;
}
let barHeight: CGFloat = UIApplication.shared.statusBarFrame.size.height
let displayWidth: CGFloat = self.view.frame.width
let displayHeight: CGFloat = self.view.frame.height
myTableView = UITableView(frame: CGRect(x: 0, y: barHeight, width: displayWidth, height: displayHeight))
self.title = "Songs"
self.myTableView.register(UITableViewCell.self, forCellReuseIdentifier: "MyCell")
self.myTableView.delegate = self
self.myTableView.dataSource = self
self.view.addSubview(self.myTableView)
print(">>> viewDidLoad")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
// sectionの数を返す
func numberOfSectionsInTableView( in tableView: UITableView! ) -> Int {
return albums.count
}
// 各sectionのitem数を返す
func tableView( _ tableView: UITableView, numberOfRowsInSection section: Int ) -> Int {
return albums.count
}
func tableView( _ tableView: UITableView, cellForRowAt indexPath : IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MyCell", for: indexPath as IndexPath)
cell.textLabel!.text = albums[indexPath.row].songTitle
return cell;
}
// sectionのタイトル
func tableView( _ tableView: UITableView, titleForHeaderInSection section: Int ) -> String? {
return ""
}
// 選択した音楽を再生
func tableView( _ tableView: UITableView, didSelectRowAt indexPath:IndexPath ) {
// soundIdからMediaItemを取得
let songId: NSNumber = albums[indexPath.row].songId
let item: MPMediaItem = songQuery.getItem( songId )
let url: URL = item.value( forProperty: MPMediaItemPropertyAssetURL ) as! URL
self.title = albums[indexPath.row].songTitle
do{
audio = try AVAudioPlayer(contentsOf: url)
audio.numberOfLoops = -1
audio.prepareToPlay()
audio.play()
}catch{
}
//Viewが終了しても、選択して再生したaudioが消滅しないように、苦肉の策
//ViewBindのstaticに渡してしまう
self.delegate?.audioSendMethod(audio: audio)
self.dismiss(animated: true, completion: nil)
}
}
SongQuery,swift
import Foundation
import MediaPlayer
// 曲情報
struct SongInfo {
var albumTitle: String
var artistName: String
var songTitle: String
var songId : NSNumber
}
// アルバム情報
struct AlbumInfo {
var albumTitle: String
var songs: [SongInfo]
}
class SongQuery {
// iPhoneに入ってる曲を全部返す
func get() -> [SongInfo] {
// アルバム情報から曲を取り出す
let songsQuery: MPMediaQuery = MPMediaQuery.songs()
songsQuery.addFilterPredicate(MPMediaPropertyPredicate(value: false, forProperty: MPMediaItemPropertyIsCloudItem))
if #available(iOS 9.2, *) {
songsQuery.addFilterPredicate(MPMediaPropertyPredicate(value: false, forProperty: MPMediaItemPropertyHasProtectedAsset))
} else {
// Fallback on earlier versions
}
if(songsQuery.collections?.isEmpty == true){
print(">>> songsQuery.collections empty")
}else{
print(">>> songsQuery.collections \(String(describing: songsQuery.collections?.count))")
}
let songsItems: [MPMediaItem] = songsQuery.items! as [MPMediaItem]
var songs: [SongInfo] = []
for song in songsItems {
let songInfo: SongInfo = SongInfo(
albumTitle: song.value( forProperty: MPMediaItemPropertyAlbumTitle ) as! String,
artistName: song.value( forProperty: MPMediaItemPropertyArtist ) as! String,
songTitle: song.value( forProperty: MPMediaItemPropertyTitle ) as! String,
songId: song.value( forProperty: MPMediaItemPropertyPersistentID ) as! NSNumber
)
print(">>> \(songInfo.songTitle)")
songs.append( songInfo )
}
return songs
}
// songIdからMediaItemを取り出す
func getItem( _ songId: NSNumber ) -> MPMediaItem {
let property: MPMediaPropertyPredicate = MPMediaPropertyPredicate( value: songId, forProperty: MPMediaItemPropertyPersistentID )
let query: MPMediaQuery = MPMediaQuery()
query.addFilterPredicate( property )
var items: [MPMediaItem] = query.items!
return items[items.count - 1]
}
}
Andeoid
後日
免責
上記コードを使用する事は可能ですが、発生したあらゆる問題等の責任は負いません