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

最近話題のゲーム、宇宙人狼こと『Among Us』。
このゲームが人気な点はやはりプレイヤー同士が疑い合い、疑心暗鬼になりながらもインポスター(人狼)を見つけ出すという人狼ゲームのおもしろさです。ただ、それ以上にこのゲームを魅力的にしたのは、そのゲーム内容とは真逆のイメージの"ポップ"なキャラクターや絵のタッチです。

そこで今回はそんな"可愛らしい"Among Usのキャラクターを拝借しExcel VBAのプログレスバーを作成してみました。作中では「タスク」と呼ばれる作業がありますがその中でも「ダウンロードタスク」をイメージして作成しています。

ただ、このプログレスバーはビジュアル面にステータスを全振りしたため基本的に作業処理は重くなるという致命的な欠陥を持っています。

効率UPさせるのがVBAの本来の目的なのに…

しかしながらこのプログレスバーには下記のようなさまざまな手法が潜んでいます。

・UserForm上でgif画像を動かす
・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】残りの処理時間と進捗をステータスバーに表示する方法)

Excel,VBA