Click here to Skip to main content
15,881,715 members
Articles / Desktop Programming / WPF

WPF Speedster

Rate me:
Please Sign up or sign in to vote.
4.93/5 (57 votes)
13 Dec 2010CPOL5 min read 114.7K   5.1K   75   57
The fastest three wheeled sprinter in WPF
SpeedsterScreenshot1.jpg

Introduction

I had the idea to design something in Expression Design that would spawn into a WPF project to simulate a 'super-fast', four wheeled (three in this case), sleek machine, and later WPF Speedster was born. The nice looking vehicle you see in the screenshot above functions more as a dragster and I should warn you that despite its appearance, WPF Speedster is not a game. Despite this heart breaking fact, you can still drive through/past the scenic region you see in the background on a straight road, which is suitably convenient for the Speedster.

Requirements

To run the project provided from the download link above, you can use either of the following:

  • Visual Studio 2010
  • Expression Blend

NB: If you're using the Express Editions of Visual Studio, ensure that you run the project using Visual Basic Express.

The Speedster

How It Works

To drive the Speedster, just press the right arrow key and off she goes. Take note of how fast she reaches top speed... a fine machine indeed. Unfortunately the speedster will slow to a complete halt when it runs out of fuel and your joy ride is over. Again, I'll remind you that this isn't a game so my apologies if you feel cheated.

Design and Layout

As I mentioned earlier, I designed the speedster in Expression Design. I have actually never attended a graphics design class and most of what I know in Expression Design I learned by reading the Help documentation and playing around with its tools and features. That said, I hope I did a decent job and 'ugly' does not cross your mind when you're using the app. But I digress...

The following screenshot shows the general layout of elements of concern in Expression Blend's Objects and Timeline panel:

SpeedsterScreenshot2.jpg

NB: LayoutRoot is a Canvas content control.

The StackPanel, SpeedsterStackPanel, contains several Image objects as child elements providing us with a background of substantial length. The Width property of the StackPanel is set to Auto so that its width increases as you add more images.

SpeedsterScreenshot3.jpg

The image that forms the children of SpeedsterStackPanel is visible from the project panel:

SpeedsterScreenshot4.jpg

WPFRacerRoad.png is actually a panorama photo which I edited a bit, especially by adding the road. If you want to implement your own background, I suggest you use a panorama photo 402px in height. To add WPFRacerRoad.png to the SpeedsterStackPanel, ensure that the StackPanel is the active object, right click the image in the Project panel and select Insert from the context menu.

Simulating motion here is a matter of moving SpeedsterStackPanel along its x-axis while rotating two groups of elements found in the element WPFRacer ; RWheelTreads and FWheelThings both of which are Viewboxes in which several elements are grouped.

SpeedsterScreenshot5.jpg

Two other groups of elements found in the SpeedoFueloMeter Viewbox are used to simulate acceleration, deceleration, and Speedster's fuel consumption, i.e., SpeedGaugeNeedle and FuelGaugeNeedle.

SpeedsterScreenshot6.jpgSpeedsterScreenshot7.jpg

NB: The center points of the two groups are not as shown in the above two top screenshots. It only appears as so because I selected the paths that make up the groups. Selecting the Viewboxes as shown in the last two screenshots reveals their actual center points.

The Code

For the Speedster to take off, you have to press the right arrow key so we check which key has been pressed in the MainWindow_KeyDown event and call the necessary method:

VB.NET
Private Sub MainWindow_KeyDown(ByVal sender As Object, _
	ByVal e As System.Windows.Input.KeyEventArgs) Handles Me.KeyDown
    If e.Key = Key.Right Then
        Accelerator()
    End If
End Sub
VB.NET
 Private Sub Accelerator()
    ' Get the current x position of the StackPanel in the 
    ' LayoutRoot.
    StackPanelLeft = Canvas.GetLeft(SpeedsterStackPanel)
    
    ' If speedster is decelerating stop Timer whose
    ' Tick event handler simulates deceleration.
    If IsDecelerating = True Then
        DecelerateTimer.Stop()
    End If
    
    If CanMoveForward = True Then
        ' To create illusion of gradually increasing speed increase
        ' variable values with each continued key press.
        If Move < MaxMove Then
            Move += 0.45
        Else
            Move = MaxMove
        End If
        
        If WheelsRotation < MaxWheelsRotation Then
            WheelsRotation += 0.2
        Else
            WheelsRotation = MaxWheelsRotation
        End If
        
        ' Move StackPanel to the left to create illusion
        ' of movement.
        Canvas.SetLeft(SpeedsterStackPanel, (StackPanelLeft - Move))
        
        RearWheelRotateTr.Angle += WheelsRotation
        FrontWheelRotateTr.Angle += WheelsRotation
        
        ' Rotate FuelGaugeNeedle.
        Fuelometer()
        ' Rotate Speedometer needle.
        Speedometer()
        
    Else
        ' The speedster is now out of fuel.
        If Move > 0 Then
            ' Gradually reduce variable value to create illusion
            ' of decreasing speed.
            Move -= 0.45
            Canvas.SetLeft(SpeedsterStackPanel, (StackPanelLeft - Move))
            
            If WheelsRotation > 0 Then
                ' Gradually reduce variable value to create illusion
                ' of decreasing speed.
                WheelsRotation -= 0.2
                RearWheelRotateTr.Angle += WheelsRotation
                FrontWheelRotateTr.Angle += WheelsRotation
            End If
            
            ' Reduce angle of speedometer needle to simulate
            ' decreasing speed.
            If SpeedoNeedleRotateTr.Angle > 0 Then
                SpeedoNeedleRotateTr.Angle -= SpeedoNeedleRotation
                SpeedGaugeNeedle.RenderTransform = SpeedoNeedleRotateTr
            End If
            
        Else
            Canvas.SetLeft(SpeedsterStackPanel, StackPanelLeft)
        End If
    End If
    
    RWheelTreads.RenderTransform = RearWheelRotateTr
    FWheelThings.RenderTransform = FrontWheelRotateTr
End Sub

The Speedometer method that is called in the Accelerator method is used to rotate the Viewbox, SpeedGaugeNeedle, for obvious purposes, as the user continues to press the right arrow key:

VB.NET
' Rotate Speedometer needle.
Private Sub Speedometer()
    If SpeedoNeedleRotateTr.Angle < 270 Then
        SpeedoNeedleRotateTr.Angle += SpeedoNeedleRotation
        SpeedGaugeNeedle.RenderTransform = SpeedoNeedleRotateTr

        If SpeedoNeedleRotation = 0 Then
            SpeedoNeedleRotation = 2.7
        End If
    End If
End Sub

A different implementation of acceleration here would be to use a Timer (DispatcherTimer in WPF). Assuming that you want Move to reach MaxMove (45) in 5 sec, using a DispatcherTimer whose Interval is a Timespan of 100 ms, you would increment Move by a value of 0.9 during the DispatcherTimer's Tick event. In this 5 sec scenario, SpeedGaugeNeedle would hit a maximum rotation angle of 270° in the same amount of time.

To simulate fuel consumption, the method Fuelometer is called:

VB.NET
' Rotate FuelGauge needle.
Private Sub Fuelometer()
    If FuelNeedleRotateTr.Angle < 147 Then
        FuelNeedleRotateTr.Angle = _
		Math.Abs(Canvas.GetLeft(SpeedsterStackPanel) * FuelChange)
        FuelGaugeNeedle.RenderTransform = FuelNeedleRotateTr

        If FuelEmptyStoryboard = True Then
            If FuelNeedleRotateTr.Angle > 80 Then
                Dim OutOfFuel As Storyboard
                OutOfFuel = CType(Me.Resources("TankEmpty"), Storyboard)
                OutOfFuel.Begin(Me)
                ' Set variable to False so that storyboard will only
                ' be played once.
                FuelEmptyStoryboard = False
            End If
        End If

    Else
        CanMoveForward = False
        FuelNeedleRotateTr.Angle = 147
    End If
End Sub

This method also helps to ensure that the white regions of the window are not shown. Shifting of SpeedsterStackPanel to the left will stop way before the right edge of the StackPanel reaches the right end of the window.

When you release the right arrow key, the Speedster decelerates gradually. This is taken care of by the MainWindow_KeyUp event handler:

VB.NET
Private Sub MainWindow_KeyUp(ByVal sender As Object, _
	ByVal e As System.Windows.Input.KeyEventArgs) Handles Me.KeyUp
    If e.Key = Key.Right Then
        If CanMoveForward = True Then
            Decelerator()
        End If
    End If
End Sub

The Decelerator method sets a boolean variable value indicating that the Speedster is decelerating, sets variables that are used to simulate deceleration, and calls the Start method of a Timer object:

VB.NET
 Private Sub Decelerator()
    Dim TimeTillStop As Double
    Dim TicksTillStop As Double
    Dim SpeedoTimeTillZero As Double
    Dim SpeedoTicksTillZero As Double
    Dim WheelsTimeTillStop As Double
    Dim WheelsTickTillStop As Double
    
    IsDecelerating = True
    ' Get and set value by which movement of StackPanel
    ' should be reduced to create an illusion of deceleration
    ' during the tick event. 
    ' [100 milliseconds = 1 Timer Tick]
    ' Speedster will stop in 6 sec if Move = MaxMove.
    TimeTillStop = (Move * 6) / MaxMove
    TicksTillStop = TimeTillStop * 10
    deceleration = Move / TicksTillStop
    
    ' Get value for reseting Speedometer needle to
    ' zero.
    SpeedoTimeTillZero = (SpeedoNeedleRotateTr.Angle * 6) / 270
    SpeedoTicksTillZero = SpeedoTimeTillZero * 10
    SpeedoNeedleToZero = SpeedoNeedleRotateTr.Angle / SpeedoTicksTillZero
    
    WheelsTimeTillStop = (WheelsRotation * 6) / MaxWheelsRotation
    WheelsTickTillStop = WheelsTimeTillStop * 10
    WheelsRotationToZero = WheelsRotation / WheelsTickTillStop
    
    DecelerateTimer.Start()
End Sub

The local variable TimeTillStop is basically how much time, in seconds, it would take to get the current value of the global variable Move to zero.

The DecelerateTimer's Tick event is handled by the method DecelerateTimer_Tick.

VB.NET
 Private Sub DecelerateTimer_Tick(ByVal sender As Object, ByVal e As EventArgs)
    StackPanelLeft = Canvas.GetLeft(SpeedsterStackPanel)
    
    If Move > 0 Then
        Move -= deceleration
        Canvas.SetLeft(SpeedsterStackPanel, (StackPanelLeft - Move))
    Else
        Canvas.SetLeft(SpeedsterStackPanel, StackPanelLeft)
        DecelerateTimer.Stop()
    End If
    
    If SpeedoNeedleRotateTr.Angle > 0 Then
        SpeedoNeedleRotateTr.Angle -= SpeedoNeedleToZero
        SpeedGaugeNeedle.RenderTransform = SpeedoNeedleRotateTr
    Else
        SpeedoNeedleRotateTr.Angle = 0
    End If
    
    If WheelsRotation > 0 Then
        WheelsRotation -= WheelsRotationToZero
        FrontWheelRotateTr.Angle += WheelsRotation
        RearWheelRotateTr.Angle += WheelsRotation
        
        RWheelTreads.RenderTransform = RearWheelRotateTr
        FWheelThings.RenderTransform = FrontWheelRotateTr
    End If
    
End Sub

Conclusion

I wanted the Speedster to be environmentally conscious causing zero air or noise pollution (which is a good excuse for not implementing sound effects). If you are of a different opinion or preference, feel free to make the necessary adjustments. In case you're wondering, the 'green' Speedster runs on some yet unnamed non-hydrocarbon fuel and the 'massive' exhaust does not emit any ungreen gases.

This is definitely a hobby project. I know some of you might be of the opinion that a lot more features can be added and if that is the case, go ahead and add them. I have already done the design work and a bit of coding, so if you feel like spicing up the Speedster, go right ahead.

I hope you enjoyed reading the article and driving the Speedster. Thanks!

History

  • 7th Dec, 2010: Initial post

License

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


Written By
Software Developer
Kenya Kenya
Experienced C# software developer with a passion for WPF.

Awards,
  • CodeProject MVP 2013
  • CodeProject MVP 2012
  • CodeProject MVP 2021

Comments and Discussions

 
Questionso nice Pin
Member 1260312620-Sep-17 17:55
Member 1260312620-Sep-17 17:55 
Questiondesign file Pin
jcarter12121219-May-13 9:27
jcarter12121219-May-13 9:27 
AnswerRe: design file Pin
Meshack Musundi19-May-13 9:37
professionalMeshack Musundi19-May-13 9:37 
GeneralRe: design file Pin
jcarter12121219-May-13 15:08
jcarter12121219-May-13 15:08 
QuestionHi Meshack Pin
jcarter12121219-May-13 7:18
jcarter12121219-May-13 7:18 
AnswerRe: Hi Meshack Pin
Meshack Musundi19-May-13 7:26
professionalMeshack Musundi19-May-13 7:26 
GeneralRe: Hi Meshack Pin
jcarter12121219-May-13 7:31
jcarter12121219-May-13 7:31 
GeneralWow .. Very good.... Pin
Viswa326-Apr-13 4:45
professionalViswa326-Apr-13 4:45 
GeneralRe: Wow .. Very good.... Pin
Meshack Musundi26-Apr-13 8:35
professionalMeshack Musundi26-Apr-13 8:35 
GeneralMy vote of 5 Pin
Tapirro11-Dec-12 4:45
Tapirro11-Dec-12 4:45 
GeneralRe: My vote of 5 Pin
Meshack Musundi11-Dec-12 6:54
professionalMeshack Musundi11-Dec-12 6:54 
GeneralMy vote of 5 Pin
GrimR752926-May-11 18:32
GrimR752926-May-11 18:32 
GeneralRe: My vote of 5 Pin
Meshack Musundi31-May-11 5:27
professionalMeshack Musundi31-May-11 5:27 
GeneralMy vote of 5 Pin
Halil ibrahim Kalkan11-May-11 3:46
Halil ibrahim Kalkan11-May-11 3:46 
GeneralRe: My vote of 5 Pin
Meshack Musundi11-May-11 5:19
professionalMeshack Musundi11-May-11 5:19 
GeneralMy vote of 5 Pin
Venkatesh Mookkan3-Apr-11 23:49
Venkatesh Mookkan3-Apr-11 23:49 
GeneralRe: My vote of 5 Pin
Meshack Musundi11-Apr-11 20:00
professionalMeshack Musundi11-Apr-11 20:00 
GeneralMy vote of 5 Pin
Yusuf10-Jan-11 10:37
Yusuf10-Jan-11 10:37 
GeneralRe: My vote of 5 Pin
Meshack Musundi17-Jan-11 22:42
professionalMeshack Musundi17-Jan-11 22:42 
Thanks Yusuf. Smile | :)
GeneralMy vote of 5 Pin
RaviRanjanKr8-Jan-11 0:26
professionalRaviRanjanKr8-Jan-11 0:26 
GeneralRe: My vote of 5 Pin
Meshack Musundi8-Jan-11 2:47
professionalMeshack Musundi8-Jan-11 2:47 
Generalcool article Pin
BillW337-Jan-11 6:58
professionalBillW337-Jan-11 6:58 
GeneralRe: cool article Pin
Meshack Musundi7-Jan-11 19:57
professionalMeshack Musundi7-Jan-11 19:57 
GeneralAn amazing article Pin
Anthony Daly24-Dec-10 7:35
Anthony Daly24-Dec-10 7:35 
GeneralRe: An amazing article Pin
Meshack Musundi24-Dec-10 7:48
professionalMeshack Musundi24-Dec-10 7:48 

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

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