Excel VBAでAmong Usのタスクっぽいプログレスバーを作ってみた

最近話題のゲーム、宇宙人狼こと『Among Us』。
このゲームが人気な点はやはりプレイヤー同士が疑い合い、疑心暗鬼になりながらもインポスター(人狼)を見つけ出すという人狼ゲームのおもしろさです。ただ、それ以上にこのゲームを魅力的にしたのは、そのゲーム内容とは真逆のイメージの"ポップ"なキャラクターや絵のタッチです。
そこで今回はそんな"可愛らしい"Among Usのキャラクターを拝借しExcel VBAのプログレスバーを作成してみました。作中では「タスク」と呼ばれる作業がありますがその中でも「ダウンロードタスク」をイメージして作成しています。
ただ、このプログレスバーはビジュアル面にステータスを全振りしたため基本的に作業処理は重くなるという致命的な欠陥を持っています。
しかしながらこのプログレスバーには下記のようなさまざまな手法が潜んでいます。
・Excel VBAで音楽を鳴らす
・UserFormの[×]ボタンが押された時に条件分岐で処理を行う
・処理にかかる時間を推測し、残り時間を表示する
Among Usに興味のある方もない方もExcel VBAのいくつかのテクニックが学べるはずので、「使えねえプログレスバーだな」と思わずに「こういう方法もあるのか!」と何かしら学べる点を見つけて何かしらのの知識を持って帰って頂けたらもらえたら幸いです。
Among Usのタスクっぽいプログレスバー
上記の動画を見れば今回作成した“Among Usのタスクっぽいプログレスバー"のシステムが理解できると思います。プログレスバーに使われている言語が英語なのは原作リスペクトのためです。(実はこのプログレスバーを作ったのが日本語訳がまだないAmong Us初期の頃だったためというのは内緒)
プログレスバーに使用している素材をはじめとしたExcelファイルは下記よりダウンロードできます。ダウンロード後、Excelファイルを開き、シート上にあるボタンを押せば動画のようなプログレスバーが表示されます。
AmongUs_ProgressBar (クリックでzipファイルダウンロード)
プログレスバーのコード
このプログレスバーはUserFormだけで全て完結しません。メインの処理を行なっている標準モジュール上でもいくつかの処理を書いておく必要があるので注意してください。
基本的には下記の「標準モジュールコード」に書かれている「何らかの処理」の部分を実際に行う処理にすればOKです。
またダウンロードしたExcelファイルでうまくいかない場合は下記コードをコピペして実行してみてください。UserFormについてもコントロールさえ存在していれば自動で大きさや位置を設定するようになっているので、下項目の画像を参考にUserFormを作成して下さい。(※この時、各コントロールの配置位置の前後は非常に重要なのでうまく並び替えてください)
標準モジュールコード
Option Explicit Sub main() UserForm1.Show vbModeless Dim Percent As String Dim EstimatedTime As Double Dim EstimatedTime_msg As String Dim i As Long Dim j As Long Dim tmp_i As Long: tmp_i = 1 Dim cnt As Long: cnt = 5000 Dim tmp_time As Double: tmp_time = Timer For i = 1 To cnt '何らかの処理 --------------------------------------------------------- For j = 1 To 100000 Next j '---------------------------------------------------------------------- If (Timer - tmp_time) > 1 Then EstimatedTime = (Timer - tmp_time) * (cnt - tmp_i) / (i - tmp_i) tmp_i = i tmp_time = Timer EstimatedTime_msg = "Estimated Time: " & _ Int(EstimatedTime / 3600) & "hr " & _ Int(EstimatedTime / 60) Mod 60 & "m " & _ Int(EstimatedTime) Mod 60 & "s" UserForm1.Label2.Caption = EstimatedTime_msg With UserForm1.Label1 If .Caption = "Running." Then .Caption = "Running.." ElseIf .Caption = "Running.." Then .Caption = "Running..." ElseIf .Caption = "Running..." Then .Caption = "Running." End If End With End If Percent = Str(Int(100 * (i / cnt))) & "%" UserForm1.Image2.Width = 300 * (i / cnt) UserForm1.Label3.Caption = Percent DoEvents Next i UserForm1.Tag = "True" Unload UserForm1 End Sub
UserFormコード
#If Win64 Then Private Declare PtrSafe Function mciSendString Lib "winmm.dll" Alias "mciSendStringA" _ (ByVal lpstrCommand As String, ByVal lpstrReturnString As String, _ ByVal uReturnLength As Long, ByVal hwndCallback As Long) As Long #Else Private Declare Function mciSendString Lib "winmm" Alias "mciSendStringA" _ (ByVal lpstrCommand As String, ByVal lpstrReturnString As String, _ ByVal uReturnLength As Long, ByVal hwndCallback As Long) As Long #End If Dim se1 As String Dim se2 As String Dim se3 As String Dim html1 As String Dim html2 As String Dim gif1 As String '----------------------------------------------------------------------------------------------------- Private Sub UserForm_Initialize() Dim wbDir As String wbDir = ActiveWorkbook.Path se1 = wbDir & "\emergency_se.mp3" se2 = wbDir & "\completed_se.mp3" se3 = wbDir & "\close_se.mp3" html1 = wbDir & "\running.html" html2 = wbDir & "\emergency.html" gif1 = wbDir & "\bar.gif" With Me .Width = 400 .Height = 200 .Tag = "False" .Caption = "Task is Running" .BackColor = RGB(59, 99, 140) End With With Me.WebBrowser1 .Left = -10 .Top = -30 .Width = Me.Width + 10 .Height = 250 .Navigate html1 End With With Me.Frame1 .Caption = "" .Left = 0 .Top = 115 .Width = Me.Width .Height = Me.Height - .Top .BackColor = RGB(59, 99, 140) .BorderStyle = fmBorderStyleNone .SpecialEffect = fmSpecialEffectFlat End With With Me.Frame2 .Caption = "" .Left = 110 .Top = 0 .Width = 160 .Height = 115 .BackColor = RGB(59, 99, 140) .BorderStyle = fmBorderStyleNone .SpecialEffect = fmSpecialEffectFlat End With With Me.Image3 .Picture = LoadPicture(gif1) .Left = 20 .Top = 0 .Width = 300 .Height = 30 .BorderStyle = fmBorderStyleNone .PictureSizeMode = fmPictureSizeModeStretch .BackStyle = fmBackStyleTransparent End With With Me.Image2 .Left = 20 .Top = 0 .Width = 200 .Height = 30 .BackColor = RGB(50, 127, 1) .BorderStyle = fmBorderStyleNone .SpecialEffect = fmSpecialEffectFlat End With With Me.Image1 .Left = 20 .Top = 0 .Width = 300 .Height = 30 .BackColor = RGB(255, 255, 255) .BorderStyle = fmBorderStyleNone .SpecialEffect = fmSpecialEffectFlat End With With Me.Label1 .Top = 48 .Left = 23 .Width = 250 .Height = 35 .Caption = "Running..." .BackStyle = fmBackStyleTransparent .ForeColor = &HFFFFFF .Font = "Calibri" .Font.Size = 28 .Font.Bold = True End With With Me.Label2 .Top = 32 .Left = 25 .Width = 400 .Height = 35 .Caption = "Estimated Time: Being calculated" .BackStyle = fmBackStyleTransparent .ForeColor = &HFFFFFF .Font = "Calibri" .Font.Size = 18 .Font.Bold = True End With With Me.Label3 .Top = -1 .Left = 324 .Width = 200 .Height = 50 .Caption = "100%" .BackStyle = fmBackStyleTransparent .ForeColor = &HFFFFFF .Font = "Calibri" .Font.Size = 24 .Font.Bold = True End With End Sub '----------------------------------------------------------------------------------------------------- Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer) Dim i As Long If Me.Tag = "False" Then If CloseMode = vbFormControlMenu Then Cancel = True DoEvents With Me .Tag = "True" .Frame1.Visible = False .Frame2.Visible = False .WebBrowser1.Navigate html2 End With 'se1(emergency_se.mp3)を再生 Call mciSendString("play " & se1, "", 0, 0) For i = 1 To 20000 DoEvents Next i Cancel = False MsgBox "Forced Termination" End ElseIf Me.Tag = "True" Then 'se2(completed_se.mp3)を再生 Call mciSendString("play " & se2, "", 0, 0) For i = 1 To 3000 DoEvents Next i With UserForm1.Label1 .Caption = "" .Left = 16 End With UserForm1.Label2.Caption = "Complete" For i = 1 To 10000 DoEvents Next i 'se3(close_se.mp3)を再生 Call mciSendString("play " & se3, "", 0, 0) For i = 1 To 1000 DoEvents Next i Cancel = False Else Cancel = False End If End Sub
まとめ
今回は「Among Usのタスクっぽいプログレスバー」の紹介でした。
コードについて深い解説はしていませんが読めば何をしているかは理解できると思います。
ここでは冒頭で言っていた手法の答えを簡単にまとめておきます。
・UserForm上でgif画像を動かす
→「WebBrowser」を使用 ※imageの場合はアニメーションが動かないため
ただWebBrowserは必ず最前面に設置され全てのコントロールが隠れてしまう
唯一「Frame」のみWebBrowserより前側に設置できるためうまく利用する
・Excel VBAで音楽を鳴らす
→ Windows API「mciSendString関数」を使用
・UserFormの[×]ボタンが押された時に条件分岐で処理を行う
→ UserForm_QueryCloseイベントとTagプロパティを組み合わせる
・処理にかかる時間を推測し、残り時間を表示する
→ 現在の処理回数を全体の処理回数で割る
(参考:【ExcelVBA】残りの処理時間と進捗をステータスバーに表示する方法)