Click here to Skip to main content
Click here to Skip to main content

BATTLEFIELD 4 progress indicator

, 15 Dec 2013 CPOL
Rate this:
Please Sign up or sign in to vote.
Hide your personal gaming preference in plain sight with this control
Also available as Nuget package

Introduction

I like the game Battlefield 4.
The game is extremely well designed in almost every aspect.
I especially like the idea that you visually start the game from a Terminal where you see some flickering  images (distorted) of the map being loaded while a prompt is awaiting input in the top right corner. 

The prompt is also used as a progress indicator when pages are loaded on Battlelog. 

Design  

The prompt is designed so it show up like an old terminal prompt with 10 lines or so in height. There is a clear gap between each line to indicate a very old monitor of some sort.  
There is also an artifact showing when the prompt is raised to full prompt, like a halo indicating a slow refresh rate.
The prompt is shown in 3 different modes depending on the state of the game. On Battlelog it's always the same mode.   

Progress control 

I decided to create the prompt as a progress control that could be used as any other progress control. 
This would allow me and others that like Battlefield to show progress in our otherwise dull financial apps with a smile on our lips. 

The progress indicator can be used like this: 

//
// Using Bf4ProgressIndicator
//
<Grid>
  <controls1:Bf4ProgressIndicator Margin="5" Value="{Binding ...}" PromptColor="White"/>
</Grid>  

The control have the following properties:

 

The indicator will switch between 3 modes.

Mode 1) Value is below 1/3 of maximum. A full prompt is just blinking
Mode 2) Value between 1/3 of maximum and below 2/3 of maximum. Underscore moving upward.
Mode 3) Value above 1/3 of maximum. Switching between full prompt and underscore.  

Each mode has it's own style.
The style is set when when the value changes and according to the rules above.

Here is the ProgressChanged method:

/// <summary>
/// Progresses the changed.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="DependencyPropertyChangedEventArgs"/> instance containing the event data.</param>
protected void ProgressChanged(Object sender, DependencyPropertyChangedEventArgs e)
{
  OnValueChanged(new RoutedPropertyChangedEventArgs<double>(
    e.OldValue != null?(double)e.OldValue:0, 
    e.NewValue!=null?(double)e.NewValue:0));
  var oldStyle = Style;
  var diff = Maximum - Minimum;
  Style = Value < Minimum + diff/3 ? style1 : Value > Maximum - diff/3 ? style3 : style2;
  if (oldStyle == Style) return;
  if (promptStoryboard != null && partPrompt != null) 
    promptStoryboard.Stop(partPrompt);
  if (promptStoryboard1 != null && partPrompt1 != null) 
    promptStoryboard1.Stop(partPrompt1);
  if (promptStoryboard2 != null && partPrompt2 != null) 
    promptStoryboard2.Stop(partPrompt2);
  if (promptStoryboard3 != null && partPrompt3 != null) 
    promptStoryboard3.Stop(partPrompt3);
  promptStoryboard = GetTemplateChild("PART_StoryBoard") as Storyboard;
  promptStoryboard1 = GetTemplateChild("PART_StoryBoard1") as Storyboard;
  promptStoryboard2 = GetTemplateChild("PART_StoryBoard2") as Storyboard;
  promptStoryboard3 = GetTemplateChild("PART_StoryBoard3") as Storyboard;
  partPrompt = GetTemplateChild("PART_Prompt") as Rectangle;
  partPrompt1 = GetTemplateChild("PART_Prompt1") as Rectangle;
  partPrompt2 = GetTemplateChild("PART_Prompt2") as Rectangle;
  partPrompt3 = GetTemplateChild("PART_Prompt3") as Rectangle;
  if (promptStoryboard != null && partPrompt != null) { 
    promptStoryboard.Begin(partPrompt, true); }
  if (promptStoryboard1 != null && partPrompt1 != null) { 
    promptStoryboard1.Begin(partPrompt1, true); }
  if (promptStoryboard2 != null && partPrompt2 != null) { 
    promptStoryboard2.Begin(partPrompt2, true); }
  if (promptStoryboard3 != null && partPrompt3 != null) { 
    promptStoryboard3.Begin(partPrompt3, true); }
} 

Switching between different styles with running animations will require the storyboards to be stopped and started manually. 

The visual part of the progress indicator is created as a style.
Here is an example of the style for mode 1: 

<Style x:Key="Bf4ProgressIndicatorStyle1" TargetType="{x:Type local:Bf4ProgressIndicator}">
  <Setter Property="Background" Value="Transparent"/>
  <Setter Property="Foreground" Value="White"/>
  <Setter Property="Template">
    <Setter.Value>
      <ControlTemplate TargetType="{x:Type local:Bf4ProgressIndicator}">
        <Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="true">
          <Grid>
            <Grid.RowDefinitions>
              <RowDefinition Height="1*"/>
              <RowDefinition Height="9*" MinHeight="{TemplateBinding MinHeight}"/>
              <RowDefinition Height="1*"/>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
              <ColumnDefinition Width="1*"/>
              <ColumnDefinition Width="1*"/>
              <ColumnDefinition Width="9*" MinWidth="{TemplateBinding MinWidth}"/>
              <ColumnDefinition Width="1*"/>
              <ColumnDefinition Width="1*"/>
            </Grid.ColumnDefinitions>
            <Rectangle x:Name="PART_Prompt" Grid.Row="1" Grid.Column="2" 
               Width="Auto" Height="Auto" Stretch="Fill">
            <Rectangle.Resources>
              <SolidColorBrush x:Key="PromptBrush" Color="{Binding Path=PromptColor, RelativeSource={RelativeSource AncestorType={x:Type local:Bf4ProgressIndicator}}}" />
            </Rectangle.Resources>
            <Rectangle.Fill>
              ...
            </Rectangle.Fill>
            <Rectangle.Effect>
              <DropShadowEffect BlurRadius="10" Color="{Binding Path=PromptColor, RelativeSource={RelativeSource AncestorType={x:Type local:Bf4ProgressIndicator}}}" Opacity="1" ShadowDepth="0" />
            </Rectangle.Effect>
            <Rectangle.Triggers>
              <EventTrigger RoutedEvent="Loaded">
                <BeginStoryboard>
                  <Storyboard x:Name="PART_StoryBoard">
                    <DoubleAnimationUsingKeyFrames Storyboard.TargetName="PART_Prompt" Storyboard.TargetProperty="Opacity" AutoReverse="False" RepeatBehavior="Forever">
                      <LinearDoubleKeyFrame Value="1" KeyTime="0:0:.1" />
                      <LinearDoubleKeyFrame Value="1" KeyTime="0:0:.2" />
                      <LinearDoubleKeyFrame Value="0" KeyTime="0:0:.3" />
                      <LinearDoubleKeyFrame Value="0" KeyTime="0:0:.7" />
                    </DoubleAnimationUsingKeyFrames>
                  </Storyboard>
                </BeginStoryboard>
              </EventTrigger>
            </Rectangle.Triggers>
          </Rectangle>
          <Rectangle x:Name="PART_HighLight" Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="3" Width="Auto" Height="Auto" Stretch="Fill" Opacity="0">
            <Rectangle.Fill>
            ...
            </Rectangle.Fill>
            <Rectangle.Triggers>
              <EventTrigger RoutedEvent="Loaded">
                <BeginStoryboard>
                  <Storyboard x:Name="PART_StoryBoard2">
                    <DoubleAnimationUsingKeyFrames Storyboard.TargetName="PART_HighLight" Storyboard.TargetProperty="Opacity" AutoReverse="False" RepeatBehavior="Forever">
                      <LinearDoubleKeyFrame Value=".25" KeyTime="0:0:.1" />
                      <LinearDoubleKeyFrame Value=".25" KeyTime="0:0:.15" />
                      <LinearDoubleKeyFrame Value="0" KeyTime="0:0:.2" />
                      <LinearDoubleKeyFrame Value="0" KeyTime="0:0:.7" />
                    </DoubleAnimationUsingKeyFrames>
                  </Storyboard>
                </BeginStoryboard>
              </EventTrigger>
            </Rectangle.Triggers>
          </Rectangle>
        </Grid>
      </Border>
    </ControlTemplate>
  </Setter.Value>
  </Setter>
</Style>

 

Sample application 

The sample application illustrates how the progress indicator is used and the different modes.
In the game it's mostly used as white on black but the control allows any color to be used. 

     

Points of interest  

I tried to keep the used images  (Geometry) as static resources, but I did not find a way to dynamically changed the color.
I ended up adding the geometry to each element in the visual tree so I could bind to a dependency property on the control.  

History  

15 Dec. 2013: This is version 1.0.0.0
16 Dec. 2013: Added link to Nuget package.


License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Niel M.Thomas
Software Developer (Senior)
Denmark Denmark
Name: Niel Morgan Thomas
Born: 1970 in Denmark
Education:
Dataengineer from Odense Technical University.
More than 20 years in IT-business.
Current employment:
Working with application development in a major Danish company that produce medical equipment.

Comments and Discussions

 
QuestionVery good PinmemberRafael Nicoletti16-Dec-13 2:44 
GeneralMy vote of 5 PinmemberDavid Retvari15-Dec-13 11:05 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.150224.1 | Last Updated 16 Dec 2013
Article Copyright 2013 by Niel M.Thomas
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid