Making a multimodal presentation

Part 2: Adding movement

By default, Pepper will do simple animations while speaking (“body language”), but it can be worth replacing those with a more relevant, specific animation - which is what we'll do in this second part.

Part 2
Schema of Pepper performance - part 2

1. Adding animations

Import an animation file, using File > New > Import animation; in the “Default” tab in Emotion > Positive, pick nicereaction_a002. It will be added in the project’s “raw” folder.

Again, in order to keep the presentation readable, create a helper function:

private fun makeAnimate(animResource: Int) : Animate {
   val animation = AnimationBuilder.with(qiContext)
           .withResources(animResource)
           .build()

   return AnimateBuilder.with(qiContext)
           .withAnimation(animation)
           .build()
}

Let’s use this to make Pepper animate and speak:

////////////////////////
// Presentation logic //
////////////////////////

private fun runPresentation() {
   // Part 1: "Making me talk..."

   // ...

   // Part 2: "Let me show you..."

   Thread.sleep(800)
   setImage(R.drawable.scene3)
   makeSay("But there's much more to do. Let me show you ! ...").run()
   makeAnimate(R.raw.nicereaction_a002).run()
}

… however, this makes Pepper first speak, then do the animation, which isn’t what we want - we want Pepper to do both simultaneously.

2. Running actions simultaneously

So far, you have only synchronous calls: all actions are being executed one after the other.

Instead of calling action.run() (a blocking call that returns once the action is finished), you can call action.async().run() and immediately get a future object that represents the execution state of that action.

You can then wait for several futures with Future.waitAll - in your case, you want to wait for the say and animate future to both finish, as in the following.

////////////////////////
// Presentation logic //
////////////////////////

private fun runPresentation() {

   // ...

   // Part 2: "Let me show you..."

   Thread.sleep(800)
   setImage(R.drawable.scene3)
   val sayMore = makeSay("But there's much more to do. Let me show you ! ...")
   val animateEnthusiast = makeAnimate(R.raw.nicereaction_a002)
   Future.waitAll(sayMore.async().run(),
           animateEnthusiast.async().run()).value
}

3. Adding a callback after an animation

We also want to show an image after the animation is done, but while Pepper may still be speaking. You can do that by adding a callback after the animate future, in this case, “thenConsume”:

   val animateEnthusiast = makeAnimate(R.raw.nicereaction_a002)
   val animFuture = animateEnthusiast.async().run()
   animFuture.thenConsume {
       setImage(R.drawable.scene4)
   }

Your full presentation should now look like this:

////////////////////////
// Presentation logic //
////////////////////////

private fun runPresentation() {

   // Part 1: "Making me talk..."

   setImage(R.drawable.scene1)
   makeSay("Okay, so ... making me talk is a first step a bit like ...").run()
   Thread.sleep(200)
   makeSay("a rolling rock ...").run()
   setImage(R.drawable.scene2)

   // Part 2: "Let me show you..."

   Thread.sleep(800)
   setImage(R.drawable.scene3)
   val sayMore = makeSay("But there's much more to do. Let me show you ! ...")
   val animateEnthusiast = makeAnimate(R.raw.nicereaction_a002)
   val animFuture = animateEnthusiast.async().run()
   animFuture.thenConsume {
       setImage(R.drawable.scene4)
   }
   Future.waitAll(sayMore.async().run(),
           animFuture).value

   // Part 3: "I can make sound"


   // to do next

}