Pepperアニメーション
ペッパーにアニメーションをさせには0から自分でアニメーションを作成することもできるが、なかなかに難しいらしい。私は作ったことがありませんがpepperの勉強会に参加した際プロでも難しいので一般的には既存のアニメーションをインポートして使うのが良いとのことでした。
アニメーションのインポート
今回はAnimalフォルダからゴリラのモーションをインポートしてみました。中身は下記のようになっています、こんな感じで指定していけばペッパーの動きを作れるようですがなかなか難しそうですね。
コード
Animation animation = AnimationBuilder.with(qiContext) .withResources(R.raw.gorilla_b001) .build(); animate = AnimateBuilder.with(qiContext) .withAnimation(animation) .build(); Future<Void> animateFuture = animate.async().run();
アニメーションさせるタネに必要なのは上記のコードをonRobotFocusGaineの中に書くことになります。animation変数のwithResourcesにインポートしてきたアニメーションを指定しています。
話しながらアニメーション
話しながらアニメーションをすのは簡単で、onRobotFocusGaineの中にSayメソッドを入れてあげれば実現できます。しかし、注意しなくてはいけないのはasync()を指定して別スレッドにしてあげる必要があります。そうすることでSayとAnimationを並列に処理をすることができます
Say say = SayBuilder.with(qiContext) .withText("uhouho") .build(); say.async().run();
Pepperがハローワールドするまで
ペッパーがハローワールドするまでの一連の流れを記述していきます。 ※pepperのエミュレータが動く前提で説明を行うのでまだの方は下記のリンクから設定をお願いします
Pepper SDK for Android — QiSDK
1.はじめにプロジェクトの作成から行います。ここは通常と変わらずなんでも大丈夫です。
2.次にSDKですが、Marshmallowを選択して進めてください
3.プロジェクトはエンプティアクティビティで作成します
4.作成を行ったら下記の画像にあるようにFile →New→LobotAplication..を選択してください。
5.するとこのようなポップアップが画面に表示されるので、okを選択します
6.これで前準備はokです。MainActivityに移って継承するクラスを変更します
7.継承してもそのままではimportできないのでリフレッシュボタン?見たいのなのを押してimportできるようにします
8.これでimportができるようになったので、電球マークをクリックでインポートするかalt + enterのどちらかでインポートを行うとエラーが消えます
>
9.次にQiSDK.registerを記述します。onCreateないでこいつを呼ぶことによって、ペッパーを操作する関数に飛ばしていろんな命令ができます(たぶん)ここでAndroid Studioが自動生成してくれるので下記の画像のように進めましょう
すると3つの関数が@Overrideされて作成されます。
・onRobotFocusGained()基本的にはこいつの中に入って来ます。アニメーションをさせたりペッパーに何かを話させるのはこの中です。
・onRobotFocusLost()これはロボットが対象を見失ってしまったり何か問題が発生してしまった場合に通ります。
・onRobotFocusRefusedこれはアプリが閉じられた際に行う処理だと思います。
私もまだpepperプログラミングを始めたばかりなので、上記の3つ認識が正しいのかどうかはちょっと不明ですが、基本的にはonRobotFocusGained()こいつでやっとけばいいってことです。
10.QiSDK.registerを起動したらonDestroyでメモリの解放をしてあげる必要があります。公式リファレンスの中でも何回か説明がされていたのできっと大事なことなんだと思います。
ここまで来たらあとはHallo Worldするだけです!
11.SayBuilderメソッドを使ってペッパーを喋らせて見ましょう。よく忘れてしまうのですが.run ()をしないと実行されないので注意してください。
コード全文
package com.example.pepper.hellopepper; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import com.aldebaran.qi.sdk.QiContext; import com.aldebaran.qi.sdk.QiSDK; import com.aldebaran.qi.sdk.RobotLifecycleCallbacks; import com.aldebaran.qi.sdk.builder.SayBuilder; import com.aldebaran.qi.sdk.design.activity.RobotActivity; import com.aldebaran.qi.sdk.object.conversation.Say; public class MainActivity extends RobotActivity implements RobotLifecycleCallbacks { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); QiSDK.register(this, this); } @Override protected void onDestroy(){ super.onDestroy(); QiSDK.unregister(this, this); } @Override public void onRobotFocusGained(QiContext qiContext) { Say say = SayBuilder.with(qiContext) .withText("Hello World") .build(); say.run(); } @Override public void onRobotFocusLost() { } @Override public void onRobotFocusRefused(String reason) { } }
Swift関数を返す関数もしかしてクロージャ?
最近クロージャについて勉強していたのですが、関数を戻り値として返せることがわかりました。第一級オブジェクトってやつですかね?
以下のコードがクロージャに当たるのかはわかりませんが覚えておくとすごい便利だと思います。
func helloFactory() -> (Int, String) -> () { func sayHello(count:Int = 10, words:String){ for _ in 0...count{ print(words) } } return sayHello } //英語挨拶 let english = helloFactory() english(3, "Hello") //日本語の挨拶 let japanese = helloFactory() japanese(10, "こんちわ")
まずhelloFactoriy()という関数を定義しています引数の値が(Int, String) -> ()
となっていますが、Swiftでは関数を戻り値として返す時には(引数の型)->(戻り値)
のように記述することができるそうです。
その次に、変数に対してhelloFactory関数を代入しています。
この時、変数の中にはhelloFactoryのreturnの値つまりsayHallo関数が入っています。なので、変数()と書いてやることで関数を実行することができます。今回は引数に数と文字列を入れるのでそれを入れてenglish(3, "Hello")
こんな感じにしているというわけです。
コマンドラインメモ
毎回忘れてしまうので自分用のメモ
openコマンドでアプリケーションの指定
-aの後にアプリ系を記述することで、指定したアプリで開くことができる
open 開きたいファイル -a アプリ //例--------------------------------- //hoge.swiftをcoteditorで開く場合 open hoge.swift -a coteditor
.swiftファイルをコマンドラインで実行
swift -oの後にコンパイル作成される実行ファイル名、コンパイルしたいswiftファイルの順に書きます。
実行ファイル名とswiftファイルの名前は一致していなくても構いませんが一致させたおいた方が後々わかりやすい気がします。
swiftc -o 実行ファイル swiftファイル ./実行ファイル //例--------------------------------- swiftc -o hello.out hello.swift ./hello.out
VSクロージャ(javaScript)
以前からクロージャというものがわからずにいた。
私がメインで書いているのはSwiftなんですがクロージャからは逃げて生きてきた。しかしある本を読んでいる際javaScriptでクロージャを扱ったコードにぶつかり戦って勝利しました。(※最強プログラマ達の助けを借りました)
本の中に出てきた敵はこんなコードでした。 カウントアップして行くようなやつ
function makeCountor(){ var count = 0; function push(){ count++; conlose.log(count); } return push; } c = makeCountor(); c(); //->1 c(); //->2 c(); //->3
これをみて私がずっとモヤモヤしていたところがありました。
var count = 0;
こいつです。初期化してるやないかーい!
つまり
c = makeCountor(); c(); //->初期化されて0 c(); //->初期化されて0 c(); //->初期化されて0
絶対こうなると思っていました。だってvar count = 0;だし..
これはテラテイルに質問を投げた後に気づいたのですが、根本的な勘違いをしていました。
c = makeCountor();ここで何が起こっているかというとmakeCountor()関数のreturnで返された値が変数cの中に代入されています。すごい初歩的な話なのですが、私はcの中にmakeCountor関数が全て入っているものだと勘違いをしていました。
実際に返されているものはreturn push;つまり!
function push(){ count++; conlose.log(count); }
makeCountor関数内にあるpush関数なのです。理解してから考えるとすごい当たり前ですが、クロージャへの苦手意識からクロージャだからできるんじゃないかと疑心暗鬼になって頭の中がこんがらがっていました。
c = makeCountor(); //この行で1度だけ初期化が行われていますつまりcount = 0; c(); //cの中にある関数を実行している...つまりpush関数を実行 count++; c(); //上に同じ c(); //上に同じ
push関数はcountをmakeCountorの中に見に行っています。しかし初期化はしていないのでcountの値は引き継がれてどんどんカウントアップして行くという結果です。
僕の頭の整理をかねて書き殴ってしまいましたが、誰かの理解の助けになれば幸いです。
teratail.com ※僕の幼稚な質問に答えたくれた最強プログラマ達の回答はここです
visual Studioフォントの組み込み
OSに直接組み込めばフォントはvisual studioが自動で読み込んで暮れて使えるのですが、自分のPC以外でアプリケーションの修正などを行いたい場合、その都度フォントをOSに入れるのは面倒なので、フォントをプロジェクト内に置きそれを参照する方法があります。
※今回はこちらのフォントをお借りしました
絶対パス
Dim pfc As New System.Drawing.Text.PrivateFontCollection() pfc.AddFontFile("C:\Users\Harumi_Sagawa\Downloads\GN-KillGothic_U\GN-KillGothic-U-KanaNB.ttf") Dim f As New System.Drawing.Font(pfc.Families(0), 22) Label1.Font = f
絶対パスでの指定方法です。これでもOSにインストールせずに独自のフォントを使うことができるのですが、フォントを毎回指定の場所に置かなければならないため不便です。
相対パス
//System.Environment.CurrentDirectory //上記のコードで現在のディレクトリを取得することができる Dim pfc As New System.Drawing.Text.PrivateFontCollection() pfc.AddFontFile(System.Environment.CurrentDirectory + "/GN-KillGothic-U-KanaNB.ttf") Dim f As New System.Drawing.Font(pfc.Families(0), 22) Label1.Font = f
このように設定できます。visual studioのカレントディレクトリがわからず結構ハマっていたのですがSystem.Environment.CurrentDirectoryこいつを使うことでカレントディレクトリを取得することができるので、カレントディレクトリプラス使いたいフォントとすることで使用することができました。
ちなみに僕はカレントディレクトリがbinファイルのDebugがルートディレクトリになっていたのでDebugフォルダにフォントにtffファイルを直で突っ込んでいます。
追記
Releaseフォルダ内にも同様にフォントを入れたフォルダを作成しておく必要があります。デバッグ時の実行と、ビルド時の実行でパスが異なるようで、入れておかなかった場合にはエラーが出てしまいます。
最終的に今回のプロジェクトではFontSelectというクラスを作成し、.Designer.vbクラスにインスタンスを作成して呼び出しました。
Public Class FontSelect Dim NotoSansCJKjpBold As New System.Drawing.Text.PrivateFontCollection() Dim MakinasScrap As New System.Drawing.Text.PrivateFontCollection() Dim RobotoSlabBold As New System.Drawing.Text.PrivateFontCollection() 'NotoSansCJKjpBoldフォント Public Function NotoFont() As FontFamily If NotoSansCJKjpBold.Families.Length = 0 Then Dim filePath As String = System.Environment.CurrentDirectory + "/myFont/NotoSansCJKjp/NotoSansCJKjp-Bold.otf" If System.IO.File.Exists(filePath) Then NotoSansCJKjpBold.AddFontFile(filePath) Else Return New FontFamily("Noto Sans CJK JP Bold") End If End If Return NotoSansCJKjpBold.Families(0) End Function 'Makinasフォント1 Public Function Makinas() As FontFamily If MakinasScrap.Families.Length = 0 Then Dim filePath As String = System.Environment.CurrentDirectory + "/myFont/makinas_scrap/Makinas-Scrap-5.otf" If System.IO.File.Exists(filePath) Then MakinasScrap.AddFontFile(filePath) Else Return New FontFamily("マキナス Scrap 5") End If End If Return MakinasScrap.Families(0) End Function 'Robotoフォント Public Function Roboto() As FontFamily If RobotoSlabBold.Families.Length = 0 Then Dim filePath As String = System.Environment.CurrentDirectory + "/myFont/roboto-slab/RobotoSlab-Bold.ttf" If System.IO.File.Exists(filePath) Then RobotoSlabBold.AddFontFile(filePath) Else Return New FontFamily("Roboto Slab") End If End If Return RobotoSlabBold.Families(0) End Function End Class
上記のクラスを作成して3つのフォントをプロジェクトに使いました。
If NotoSansCJKjpBold.Families.Length = 0 Then
このif文ですが、入れないとインスタンスをガンガン作成してしまい落ちたので入れました。
またまた追記
.designer.vbの下記の位置にインスタンスを作成しました。自動生成のファイルで書き換えたら危なそうですが書き換えちゃいました。フォームデザイナーからインスタンス作れないんだからしょうがないじゃないか!
'メモ: 以下のプロシージャは Windows フォーム デザイナで必要です。 'Windows フォーム デザイナを使用して変更できます。 'コード エディタを使って変更しないでください。 <System.Diagnostics.DebuggerStepThrough()> _ Private Sub InitializeComponent() ###ここらへん####
フォントを設定してるところ
Me.ButtonClose.Font = New System.Drawing.Font(FontSelect.NotoFont(), ...省略)
できた!windowsに入ってるfontアンインストールだ!
あれ?
windowsのシステムフォントの中にフォントがインストールされていないとdesigner.vbは見ることができないようです。(できる方法あったら教えてください)
相対パスを読みに行くのはデバッグorビルド時ってのはなんとなく想像つきますね。使うフォントはインストールして入れましょう!
まぁでもこれでシステムフォントを参照しなくはなったので目的は達成できました。
最終的にインスタンスを作成する場所を変えてグローバル変数で宣言しました。
Public FontSelect As New FontSelect
※参考にさせていただいたサイト
VBでResourcesフォルダに画像を追加した際に起こるエラーの対処法
引き継いだコードなのですがResourcesフォルダ内に新たに画像を追加したり、削除したりすると下記のようなエラーが発生しました。
System.OutOfMemoryException: 種類 'System.OutOfMemoryException' の例外がスローされました
このエラーの対処法を記述していきます。
※Resourcesフォルダを削除するのでどこかに画像を一時避難させておいてください
↓エラー全文
エラー 1 The "GenerateResource" task failed unexpectedly. System.OutOfMemoryException: 種類 'System.OutOfMemoryException' の例外がスローされました。 場所 System.IO.MemoryStream.set_Capacity(Int32 value) 場所 System.IO.MemoryStream.EnsureCapacity(Int32 value) 場所 System.IO.MemoryStream.Write(Byte buffer, Int32 offset, Int32 count) 場所 System.IO.BinaryWriter.Write(Byte buffer) 場所 System.Runtime.Serialization.Formatters.Binary.BinaryWriter.WriteSingleArray(NameInfo memberNameInfo, NameInfo arrayNameInfo, WriteObjectInfo objectInfo, NameInfo arrayElemTypeNameInfo, Int32 length, Int32 lowerBound, Array array) 場所 System.Runtime.Serialization.Formatters.Binary.BinaryWriter.WriteObjectByteArray(NameInfo memberNameInfo, NameInfo arrayNameInfo, WriteObjectInfo objectInfo, NameInfo arrayElemTypeNameInfo, Int32 length, Int32 lowerBound, Byte byteA) 場所 System.Runtime.Serialization.Formatters.Binary.ObjectWriter.WriteArray(WriteObjectInfo objectInfo, NameInfo memberNameInfo, WriteObjectInfo memberObjectInfo) 場所 System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Write(WriteObjectInfo objectInfo, NameInfo memberNameInfo, NameInfo typeNameInfo) 場所 System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Serialize(Object graph, Header inHeaders, __BinaryWriter serWriter, Boolean fCheck) 場所 System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize(Stream serializationStream, Object graph, Header headers, Boolean fCheck) 場所 System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize(Stream serializationStream, Object graph) 場所 System.Resources.ResourceWriter.WriteValue(ResourceTypeCode typeCode, Object value, BinaryWriter writer, IFormatter objFormatter) 場所 System.Resources.ResourceWriter.Generate() 場所 System.Resources.ResourceWriter.Dispose(Boolean disposing) 場所 System.Resources.ResourceWriter.Close() 場所 Microsoft.Build.Tasks.ProcessResourceFiles.WriteResources(IResourceWriter writer) 場所 Microsoft.Build.Tasks.ProcessResourceFiles.WriteResources(String filename) 場所 Microsoft.Build.Tasks.ProcessResourceFiles.ProcessFile(String inFile, String outFile) 場所 Microsoft.Build.Tasks.ProcessResourceFiles.Run(TaskLoggingHelper log, ITaskItem assemblyFilesList, ArrayList inputs, ArrayList outputs, Boolean sourcePath, String la nguage, String namespacename, String resourcesNamespace, String filename, String classname, Boolean publicClass) 場所 Microsoft.Build.Tasks.GenerateResource.Execute() 場所 Microsoft.Build.BuildEngine.TaskEngine.ExecuteInstantiatedTask(EngineProxy engineProxy, ItemBucket bucket, TaskExecutionMode howToExecuteTask, ITask task, Boolean& taskResult) CompMng
ググってもterateilで質問してもエラーをとることができなかったのですがいろいろと模索していたら解決できたので、共有。誰かの助けになれば幸いです。 1ヵ月無駄な時間を過ごしてしましました。
①ソリューションエクスプローラ上にあるResourcesフォルダを削除する
②削除の確認ボタンが出るのでOK
③エラーがたくさん出るのを確認
④プロジェクトを保存する。画像は保存せずに閉じようとした場合。はいを選択
⑤プロジェクトフォルダに移動しMyProjectを開く
⑥2つのResourcesファイルを削除※多分これが問題
⑦プロジェクトを開きプロジェクト→CompMsgのプロパティ
⑧クリックしてResourcesを作成
⑨ドラック&ドロップで画像をぶち込む
以上の手順でエラーは解決できると思います。簡単な手順なのですがVBを触るのもvisual studioを触るのも初めてだったのでかなり苦戦してしまいました...