17 October 2016

A HoloLens airplane tracker 5-smooth movement with iTween and adding a trail

Intro

In the video in the first post of this series  you can see the airplane models leaving a kind of trails in the air, while they move and rotate in a smooth fashion - in stead of merely hopping from place to place. The first is accomplished by adding a TrailRender to the AircraftHolder prefab – the second by using an awesome Unity plugin called iTween.

Adding the trail

imageBefore you can add the trail, it must have a material from which it’s made. This the color of the line. I will freely admit materials are still a bit vague to me, and after quite some fiddling I came to the following settings:

  • Shader: HoloToolkit/Fast
  • Albedo: hex color 120067FF
  • Metallic: 1
  • Emission: hex color 021F61

This leads to a kind of metallic light blue to dark blue trail line (depending on where the ‘directional light’) is shining, which is quite satisfactory. Feel free to mess around with the parameters. I did ;).

Then go to App/Scripts and open the AircraftHolder prefab. Click on “Add Component”, then select “Trail Render”. Use the following workflow/ setting:

image

  • Expand “Materials”. Drag the Trail Line Material that we just created on top of the “Element 0” field
  • Set start width to 0.003
  • Set end width to 1e-05.

This means the trail will be visible for 120 seconds, so the tail of the trail line will be at the place where the airplane was two minutes ago. Older segments are automatically deleted. Also, near the airplane the trail will be 0.003 meter wide, petering out to 0.00001 meter (1e-05) at the very end. And sure enough:

image

Unity takes care of the drawing, erasing, and making-smaller-toward-the-end part. The only thing we have to do is move the airplane. Which is what we were doing already anyway. It once again almost feels like cheating.

imageIntroducing and obtaining iTween

In a blog post about an earlier project, the CubeBouncer, I talked about using coroutines to achieve smooth animations. In the mean time I have stumbled upon iTween, an awesome Unity plugin created by one Bob Berkebile, that makes creating animations of all kinds a lot easier. Although some concepts are a bit odd – particularly the the use of the Hash object to pass in an arbitrary number of parameters into methods – once you wrap your head around it, it’s very easy to use. Oh. And it’s free too.

The easiest way to obtain it, is via the Unity Asset Store (Window/Asset Store or CTRL-9). Then enter iTween as search term. Click the iTween Logo, click import, and then the following dialog, as displayed to the right (below this paragraph), pops up. I usually deselect everything but the actual plugin, but if you would like to see samples and the Readme, feel free to leave everything checked.image

When the process is finished, your project tab should contain the iTween plugin like below

image

Once you have the iTween plugin installed, build the project, and move over to Visual Studio.

Using iTween for moving and rotating

So we head back to the AircraftController’s SetLocationOrientation method. We are going to change that a little:

private void SetLocationOrientation()
{
  SetNewFlightText();

  if (!_firstMove)
  {
    transform.localPosition = GetFlightLocation();
    if (_flightData.Heading != null)
    {
      transform.localEulerAngles = GetNewRotation();
    }
    transform.localScale = new Vector3(0.0015f, 0.0015f, 0.0015f);
    _firstMove = true;
  }
  else
  {
    iTween.MoveTo(gameObject, iTween.Hash("position", GetFlightLocation(), 
      "time", 7f, "islocal", true));
    if (_flightData.Heading != null)
    {
      iTween.RotateTo(gameObject, iTween.Hash("rotation", GetNewRotation(), 
        "time", 7f, "islocal", true));
    }
  }
}

So if the first set of data (or the initial move to a location) as has not been applied yet – that is, this is a newly created airplane – just do the same as we did before – plonk the airplane at it’s location with the proper rotation and attitude, and give it a size that makes sense. But if this aircraft already exists – move it smoothly to it’s new location in 7 seconds, using local position. And rotate it too, if needed, in the same time. And that is all you need for a smooth transition. Here you see the Hash in action. iTween has a few overloads for both Move and Rotate where you don’t need it, but in effect, when you want to do something only a little advanced – like using local space – say hello to Mr. Hash.

You can of course rant against these ‘stringly typed’ variables and talk about type safety and stuff like that – but this is how it works, and it’s perfectly usable, albeit a bit odd for those who are fans of tight architecture and clean code. But then again, HoloLens is a pretty out-of-the-ordinary device anyway. When in Rome, act like Romans. ;)

Making the text fade in and out

Now the only thing we are missing is that nice effect where the text fades out just before the airplane moves – and appears again – with the newly updated flight data. This prevents the text from flashing and also gives a nice indication whether or not we are receiving updates for an aircraft (you may have noticed by now that aircraft that have landed tend to hang around for a while just above the airport before disappearing).

First, we need to add another field:

private GameObject _label;

And of course, initialize that in Start:

void Start()
{
  _text = transform.GetComponentInChildren<TextMesh>();
  _label = transform.FindChild("Label").gameObject;
  _initComplete = true;
}

I already warned you Start would grow, didn’t I? ;) Then we add a new method:

private void StartSetLocationOrientation()
{
  if (_firstMove)
  {
    iTween.FadeTo(_label,
        iTween.Hash("alpha", 0.0f, "time", 0.5f, 
        "oncomplete", "SetLocationOrientation",
        "oncompletetarget", gameObject));
  }
  else
  {
    SetLocationOrientation();
  }
}

So when the first move is indeed done, it will fade the label’s alpha channel to 0 in half a second. When that is done, call the original SetLocationOrientation. So this is a callback. What is a very important iTween convention to understand – default the callback will be called on the object you are operating on – and that is the label. Not the airplane. Therefore I added an “oncompletetarget” entry with the current gameObject as value – so iTween knows it should call SetLocationOrientation and use my gameObject as a target, not the label.

Oh, in the else - if the airplane was just created and does not have an initial location - just move it to it’s initial location without using iTween.

Then we move to SetNewFlightData and change the call to SetLocationOrientation to StartSetLocationOrientation:

public void SetNewFlightData(Flight newFlightData)
{
  if (_initComplete)
  {
    var move = _flightData == null ||
                !_flightData.Location.LocationEquals(newFlightData.Location);
    _flightData = newFlightData;

    ExtractSpeedAndHeading();
    if (move)
    {
      StartSetLocationOrientation();
    }
    else
    {
      SetNewFlightText();
    }
  }
}

And finally, to SetLocationOrientation we add all the way to the end this line:

iTween.FadeTo(_label, iTween.Hash("alpha", 1f, "time", 0.5f, "delay", 0.2f));

This fades the label’s alpha channel (back) to 1, in 0.5 second, after a delay of 0.2 seconds. I found these values after experimenting. Feel free to play with times and stuff.

Conclusion

I have shown you how to add a trail line to an airplane, and how to use iTween for smooth transition effects. Once the basics of your HoloLens app stand, it’s quite easy to make just a little more effort and get it smooth and cool. Using a powerful tool like iTween makes it almost child’s play IMHO, once you get your head around how to using it.

Code, as always, can be found here.

2 comments:

Jarno Peschier said...

Very nice series this. Lots of people will adore it and use it for reference. Keep up the good work.

Joost van Schaik said...

Thank you Jarno. Much appreciated.