Bakulog

獏の夢日記的な何か。

【WPF】エフェクトつきのプロ生ちゃんデスクトップマスコットを作ってみる【XAML】

本記事はプロ生ちゃんAdvent Calender6日目の記事になります。

 

本記事では自作デスクトップマスコットエンジン「Harriet」でも使っているキャラの外観制御まわりの小技を紹介します。「デスクトップマスコット自作してみたいけど何から手を出せば…?」という方の参考になれば幸いです。ジャンルとしてはWPF/XAML(と一瞬だけC#)です。ターゲットはWindowsになります。

 

最初に完成品のイメージを掲載します。下記のツイートに載せているような動きを作れるところまで紹介します。

https://twitter.com/baku_dreameater/status/673266833118507010

 

 

1. 透明なウィンドウ上にプロ生ちゃんを表示する

プロ生ちゃんAdvent Calenderはジャンルが広範なので入門から書くことにします。Visual Studio 2015 Communityをインストールし、プロ生ちゃんの画像が表示されるところまで作ってみます。

[expand title="詳細(クリックで展開します)"]

上記リンクのVisual Studioをインストールします。インストール時に構成を聞かれますがデフォルト設定で問題ありません。インストールできたらVisual Studioを起動します。デスクトップにアイコンが追加されてない場合スタートメニューから普通に「Visual Studio」で探してください。初回起動時はMicrosoftアカウントの入力が必要です。

初期化が完了したら「新しいプロジェクト...」でプロジェクトを作成。

vs_newproj

プロジェクトの種類として「Visual C#→従来のデスクトップ→WPFアプリケーション」を選択し、適当なソリューション名を入れて「OK」。

vs_selectproj

これでプロジェクトが初期化され、ウィンドウの編集画面が出てきます。

[caption id="attachment_6581" align="alignnone" width="293"]vs_init_wpf GUIの編集画面(※右下のは「社畜ちゃんIDE」という自作プラグインです)[/caption]

編集画面の下側に書かれているのがXAMLというGUI記述用のマークアップ言語です。本記事の範囲では「XMLGUIが書かれてる」と思ってもらっても構いません。

Visual StudioでのGUI作成が初めてならばこの時点で一度F5キーを押してデバッグ実行を試し、ウィンドウが表示されるのを確認しておくとよいでしょう。

vs_run_init_wpf

ウィンドウを普通に閉じればデバッグ実行は終了します。

 

さて。普段ならこのウィンドウにボタンやテキストボックスをドラッグ&ドロップで配置したり直接XAMLを手書きしたりしてGUIを組む所ですが、今回の目的はデスクトップマスコット作成なので普段のWPFではあまり使わない機能を使っていきます。

しかしその前にプロ生ちゃんのイラストを準備しましょう!公式サイトからちびキャラ絵など透過の画像をダウンロードし、png画像として適当なディレクトリに配置します。画像サイズが大きいと実行時に重くなるためリサイズやトリム処理を推奨します。以下では公式サイトの忍者プロ生ちゃんをトリム&リサイズした画像を例として使います。

pronama_chan

(※再配布っぽくなるとヤなので公式配布版から10倍以上画質を落としてます。あくまで画像編集環境が足りてない場合用に掲載しているだけなので、公式サイトからダウンロードしたものや自分で使いたい画像を使ってください。)

 

この画像をプロジェクトに追加します。

vs_add_image

基本操作はソリューションエクスプローラへのドラッグ&ドロップだけです。プロジェクトアイコンに向かってドロップしてください。

もしソリューションエクスプローラが表示されてない場合はメニューバーの「表示」から選んで出すかShift+Alt+Lで表示してください。また画像ファイルのプロパティで「ビルドアクション」項目が「Resource」になっている事も確認してください。ココの種類が間違っていると実行時に画像が表示されない事があります。

 

画像の準備が済んだら本題に戻ります。さきほどは普通の四角いウィンドウ枠が表示できましたがデスクトップマスコットにとって枠ほどウザいものは無いので、枠を消すため編集画面下側のXAMLエディタにいくつか書き込みます。※コメントっぽい部分("//"以降)は書かない

<Window x:Class="PronamaChanNinja.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:PronamaChanNinja"
        AllowsTransparency="True" //←追加!
        WindowStyle="None"        //←追加!
        Background="Transparent"  //←追加!
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        
    </Grid>
</Window>

これでウィンドウ枠が消せるので、続けてプロ生ちゃんの画像を表示するためImageコントロールという画像表示用のアイテムを追加します。

<Window x:Class="PronamaChanNinja.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:PronamaChanNinja"
        AllowsTransparency="True"
        WindowStyle="None"
        Background="Transparent"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <!-- 追加 -->
        <Image Source="pronama_chan.png"
               RenderOptions.BitmapScalingMode="HighQuality"/>
    </Grid>
</Window>

"RenderOptions.BitmapScalingMode"の部分は必須ではありませんが、画像リサイズ処理がキレイになるので書いておいた方が無難です。これで再びF5を押しデバッグ実行すると、プロ生ちゃんが宙に浮いた状態で表示されます。

vs_transparent_window

「閉じる」ボタンが無いのでプロ生ちゃんをクリックしてからAlt+F4コマンドでウィンドウを閉じます。あるいはVisual StudioをアクティブウィンドウにしてShift+F5を押すとデバッグが中止出来るので、それでも構いません。

 

最後にアニメーション効果の素材となる四角形をウィンドウに追加し、ウィンドウサイズも画像サイズに合わせて書き換えます。

<Window x:Class="PronamaChanNinja.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:PronamaChanNinja"
        AllowsTransparency="True"
        WindowStyle="None"
        Background="Transparent"
        mc:Ignorable="d"
        Title="MainWindow" Height="400" Width="200">
    <!-- ↑幅と高さをちょっと変更 -->
    <Grid>
        <Image Source="pronama_chan.png"
               RenderOptions.BitmapScalingMode="HighQuality"/>
        <!-- ↓追加 -->
        <Rectangle Fill="Lime"/>
    </Grid>
</Window>

こうするとウィンドウ全体が明るい緑色の四角で覆われてしまいますが心配はいりません!この四角形を次節でちょちょいと弄ってエフェクトに変身させます。

[/expand]

 

 

2. Blend for Visual Studioでアニメーション効果を作る

Blend for Visual StudioとはXAMLの外観とかをGUIから細かく編集するためのツールです。ボタンなどUI要素の外観を徹底的にカスタマイズしたり簡単なベクター画像を描いたり乳揺れアニメーションを実装(こちらを参照)したりできる優れものなので、今回はこれを使ってみましょう!

[expand title="詳細(クリックで展開します)"]

VSのウィンドウ編集画面が出た状態でメニューバーから「表示→Blendでデザイン」を選んでBlendを起動します。BlendとVisual Studioが同じプロジェクトを同時に開いて編集する状態になりますが特に問題はありません。

blend_init

Visual Studioと見た目が非常に似てますが間違えないでください。今からしばらくはVisual Studioを使わないので、Visual Studioは最小化しておくのをオススメします。

 

では編集開始です。まずは緑色の四角形にグラデーション効果をかけ、表示範囲を縮めてみます。緑色の四角形を右クリックして「プロパティ」を選び、上の方にある「ブラシ」の中からFillを選択します。

blend_gradient_brush

"Fill"を選んだらすぐ下で5つ横に並んでるアイコンがあるので、真ん中のグラデーションっぽいアイコンをクリックします。これはLinearGradientBrushといって、いわゆるグラデーション的な色彩を設定するオプションです。

この状態ではグラデーションの制御点を動かしたり制御点ごとの色を編集したりできる(上記画像で上矢印みたいなマークが並んでる場所をグリグリいじれる)ので、設定を変えて以下の通りにします。上の画像は編集完了した状態の外観です。

  • 左端の制御点: 位置0%, 色"#0000FF00"(透明なライム色)
  • 真ん中: 位置5%, 色"#FF00FF00"(不透明なライム色)
  • 右端: 位置10%, 色"#0000FF00"(透明なライム色)

[expandsub1 title="※グラデーションの方向がおかしい場合(クリックで展開)"]

もしグラデーションが「上から下」という流れになっていない場合はブラシの下にある下矢印マークを展開してグラデーションの開始位置(StartPoint)と終了位置(EndPoint)を設定します。下の画像の通りにStartPointとEndPointが設定されていれば問題ありません。

blend_gradient_start_end

ちなみにStartPointもEndPointも設定の意味は同じで、グラデーションの基準位置を相対的なX、Y座標で指定しています。例えばStartPointの"0.5, 0"は「左右で言うと真ん中、上下で言うと上端」を意味するので真ん中上方からグラデーションが始まります。同様にEndPointの"0.5, 1"は「左右では真ん中、上下で言うと下端」を表しており今回は上から下へ向かうグラデーションになっています。たとえば「左上から右下へ斜め方向のグラデーションがつけたい」と思った場合はStartPointを"0,0"、EndPointは"1,1"に書き換えればOKです。

[/expandsub1]

 

お次はBlendの真骨頂であるアニメーションの作成を行い、さっき作った緑色のラインが上から下にスイーッと動くようにします。Blendの画面左下に「オブジェクトとタイムライン」というパネルがあるハズなので、そこで緑色の+ボタンを押してストーリーボードというものを新規作成します。パネルが出てない場合はメニューバーの「表示→その他のウィンドウ→オブジェクトとタイムライン」で表示させます。

blend_create_storyboard

ストーリーボードの名前は自由に決めて下さい。今回の例では"GreenLight"とします。

[expandsub1 title="蛇足: ストーリーボードとは(クリックで展開)"]

こういうGUI作成が初めての方は「ストーリーボードって何?」と首をかしげることと思いますが、ストーリーボードとは平たく言うと「PowerPointのアニメーションの超凄い版」です。時間の経過に合わせて画面内のモノの動きを設定できる機能ですね。日常語で言う「アニメーション」と同じ意味だととらえても構いません(※WPFで「アニメーション」と言った場合、微妙に違う意味になります)。

[/expandsub1]

 

作成したストーリーボードの中身を作っていきます。ストーリーボードを生成するとメイン画面が赤い枠に囲われて「記録モード」という状態になります。この状態でUIのパラメータを動かしていくことでアニメーションの動き方を簡単に定義できます。

具体的に操作してみましょう。例えば「ストーリーボードが始まってから1.8秒後に、緑色のラインがプロ生ちゃんの足元でスッと消える」という動きを考えてみます。この場合は「オブジェクトとタイムライン」で時間のバーを1.8秒の位置へセットしてから、さきほど用意した3つのグラデーション制御点をすべて右端までグイッと移動します。

blend_timeline_example

ストーリーボード作成の基本的な流れはこのような、「時間バーを動かして」「UIの動かしたい部分のパラメータをいじる」の繰り返しです。上の例と同じような操作を繰り返して以下のようなアニメーションを作ります。

  • 0秒時点ではグラデーションの制御点がすべて上端(プロパティ画面で言うと全部左端にある状態)
  • 0.3秒の時点ではグラデーションの制御点が0%, 5%, 10%の位置(Visual Studioで設定したのと同じ状態)
  • 1.5秒の時点ではグラデーションの制御点が90%, 95%, 100%の位置

以上で合計4つの時間を指定したアニメーションが作れました。アニメーションの動きは再生ボタンを押して確認できます。

https://twitter.com/baku_dreameater/status/673275374382399488

この時点で大体思い通りに動いてますが、動きに緩急がないので少し物足りません。そこで値の動きをよりカスタマイズするEasingFunctionという設定を使ってみます。

まず、今からの作業では記録モードはもう必要ないので、誤操作防止のため記録モードを切ります。続いて「オブジェクトとタイムライン」パネルの中で1.5秒時点の箇所にある四角いアイコンをクリックし、プロパティを見てみます。するとイージングという項目があるため"EasingFunction"を選択して色々な関数を選びながらストーリーボードを再生し、良さそうなEasingFunctionを探してみてください。

blend_easing

いい感じのEasingFunctionを使うと冒頭で紹介したような自然なスピード感が演出できます。というか上の画像に表示されてる"Cubic InOut"を実際に使っています。

 

以上でアニメーション関連の作業は終わりました。最後の仕上げとしてストーリーボードを閉じ、デフォルトの状態でちょっと見えてしまっていた四角形が上に隠れるようグラデーションを修正します。

blend_reconfig_gradient

以上が完了したら編集を保存してBlendを終了し、Visual Studioに戻ります。Visual Studio側のウィンドウをアクティブにすると「ドキュメントが変更されたので再読み込みしますか?」みたいなダイアログが出るので「はい」を選び、Blendでの作業をVisual Studio側で反映させます。

コレでデバッグ実行を行うとプロ生ちゃんが表示された直後に緑色のラインがスイッと動いてくれるようになります。

[videojs mp4="http://www.baku-dreameater.net/wp-content/uploads/2015/11/blend_animation_2.mp4" width="640" height="480"]

[/expand]

 

 

3. エフェクトの効果範囲を画像ピッタリに合わせる

前節までで作ったエフェクトでもそれなりに見栄えがしますが、エフェクトがキャラの画像にピッチリ沿うように修正をかけてみましょう。映像や画像編集で言う「クリッピング」という処理ですね。

[expand title="詳細(クリックで展開します)"]

実は作業そのものは簡単で、XAMLを少し書き換えるだけです。Blendでの編集前と比べるとアニメーションの実装コードがXAML内にドッサリ増えてますがそちらはノータッチで。

    ...
    </Window.Triggers>
    <!-- ↓属性を二つ追加 -->
    <Grid HorizontalAlignment="Center" VerticalAlignment="Center">
        <!-- Grid.OpacityMaskを丸ごと追加 -->
        <Grid.OpacityMask>
            <ImageBrush ImageSource="pronama_chan.png"/>
        </Grid.OpacityMask>
        <Image Source="pronama_chan.png"
               RenderOptions.BitmapScalingMode="HighQuality"/>
        ...
    </Grid>
</Window>

画像や緑色の四角形のコンテナであるGrid全体にプロ生ちゃんの画像を使ってOpacityMaskをかけています。このOpacityMaskを用いると、画像や図形で指定した形に沿って中身のUIの表示がクリッピングされます。プロ生ちゃんの画像はもとから同じ形なので影響がないですが、もともと四角形として作られていたエフェクト効果の部分についてはプロ生ちゃんの画像の形に切り抜かれます。

これで実行するとエフェクトの形が記事冒頭で紹介したものと一致します。動きは最上部に載せてるのと一緒なので再掲はしません。

[/expand]

 

4. その他の小技

さらにデスクトップマスコット感を高めるために三つの特徴を追加します。

  • 右クリックから「終了」を選べるようにする
  • キャラをつかめるようにする
  • エフェクトは起動直後ではなくて、クリックしたときに発生するようにする

[expand title="詳細(クリックで展開します)"]

まず右クリックメニュー追加から。XAMLの編集部分でGrid要素のすぐ上に右クリックメニューのアイテムを書き足します。

    ...
    </Window.Triggers>
    <!-- Window.ContextMenuを丸ごと追加 -->
    <Window.ContextMenu>
        <ContextMenu>
            <MenuItem Header="終了" Click="OnMenuItemCloseClick"/>
        </ContextMenu>
    </Window.ContextMenu>
    <Grid HorizontalAlignment="Center" VerticalAlignment="Center">
    ...

上記の中で"OnMenuItemCloseClick"というのはメニューアイテムがクリックされた時の処理を記述するイベントハンドラ関数を表します。

書き足しが完了したら"OnMenuItemCloseClick"の所にキャレットを合わせてF12キーを押してください。するとC#ソースファイルである"MainWindow.xaml.cs"の編集画面に飛ばされ、そちらに"OnMenuItemCloseClick"のひな型が自動生成されます。今回は「終了」ボタンを押した際の処理なので、中身としてはウィンドウを閉じるためのClose関数を呼び出すだけでOKです。

...
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private void OnCloseButtonClick(object sender, RoutedEventArgs e)
    {
        //↓の1行だけ自分で書く
        Close();
    }
}

これで右クリックメニューが表示でき、「終了」を選ぶと実際にウィンドウが閉じるようになりました。

次はキャラを掴んで動かす機能です。いま開いている"MainWindow.xaml.cs"に次のように追記します。

...
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private void OnCloseButtonClick(object sender, RoutedEventArgs e)
    {
        //ここを実装(これだけでOK)
        Close();
    }

    //追加
    protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
    {
        base.OnMouseLeftButtonDown(e);
        DragMove();
    }
}

見た目でなんとなく分かって貰えるかと思いますが、ウィンドウ上で左クリックした場合の挙動をカスタマイズしています。"base.OnMouseLeftButtonDown"はデフォルト(基底クラス)で定義された処理を行っているだけなので大した意味はなく、2行目に書いたDragMove関数が「掴んだ状態で動かす」という処理を担当しています。これで掴んで動かせる機能の実装も完了です。

最後は「エフェクトを起動直後ではなく左クリックしたときに発生するようにする」です。上で使っているDragMoveメソッドの呼び出し部分の手前にコードを足します。

using System.Windows.Media.Animation; //using文を追加

...
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private void OnCloseButtonClick(object sender, RoutedEventArgs e)
    {
        //ここを実装(これだけでOK)
        Close();
    }

    protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
    {
        base.OnMouseLeftButtonDown(e);
        //追加
        (this.TryFindResource("GreenLight") as Storyboard)?.Begin();
        DragMove();
    }
}

書き足した部分ではBlendで作ったアニメーションを名前で指定して拾い、そのまま実行しています。

折角なので起動直後にエフェクトが走る仕組みは切っておきましょう。今度はMainWindow.xamlの編集画面(ウィンドウが映ってた方の画面)に戻り、XAMLエディタでWindow.Triggers要素を丸ごと削除するかコメントアウトします。

        ...
    </Window.Resources>
    <!-- ここから 
    <Window.Triggers>
        <EventTrigger RoutedEvent="FrameworkElement.Loaded">
            <BeginStoryboard Storyboard="{StaticResource GreenLight}"/>
        </EventTrigger>
    </Window.Triggers>
    ここまでを削除かコメントアウト -->
    <Window.ContextMenu>
        ...

これで起動直後にはエフェクトが出ず、左クリックしたときだけエフェクトが出るようになりました。以上でツイートで紹介した通りのデスクトップマスコットの完成です!

[/expand]

 

 

5. まとめ

いかがだったでしょうか。本記事で紹介した通り、BlendとVisual Studioをうまく使えばデスクトップマスコットをささっと作ってちょっとしたビジュアル効果まで与えることが可能です。今回は紹介してませんが、同じような編集でプロ生ちゃんのイラスト自体にも演出を加えればこんな動きも作れます。

https://twitter.com/baku_dreameater/status/652099625730510848

 

既存のデスクトップマスコットエンジン(伺かとかアプリコタンとか)上で動くようキャラを実装するのも有効な開発方針ですが、非常に特殊な機能を持ったデスクトップマスコットが欲しいとか、あるいはコードを完全に掌握してデスクトップマスコットを作りたい場合などは本記事でやったように自作でも十分良いものが作れます。ご参考になれば幸いです。

 

次の記事はおーぼんさんの「動画宣伝と裏話」です!