Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / WPF

GesturCubes

5.00/5 (16 votes)
4 Nov 2013CPOL2 min read 37K  
WPF 3D cubes puzzle game for the AIO(All-In-One) PC

This article is an entry in our AppInnovation Contest. Articles in this sub-section are not required to be full articles so care should be taken when voting.

Image 1

Overview

GesturCubes is a puzzle game targeted at the Lenovo Horizon 27" All-In-One® PC. It provides a fun and challenging experience with two modes of play; 2x2 cube mode and 3x3 cube mode.

Image 2

2x2 cube mode

Image 3

Features

Taking into consideration the capabilities of the AIO, GesturCubes incorporates the following features;

  • Multi-touch interface: The app takes advantage of the touch capability of the AIO allowing user/users to solve cubes, switch modes, and start the timer through touch interaction and controls. GesturCubes manages to do this by utilizing WPF's multi-touch support.  
  • Multi-user interface: One of the nice features of the Lenovo Horizon 27" All-In-One® is the large screen. GesturCubes takes advantage of this feature to provide a collaborative multi-user experience: The layout of UI elements enables two users to comfortably sit side-by-side and do a cube team solve in a more fun and easy way.
  • Multi-modal interaction: Users can solve, scramble, and switch between game modes using voice commands. 

Design

GesturCubes is a WPF application and contains several UI elements which I designed in Blender, Expression Design, and Expression Blend. The 3D cube models were designed in Blender, while some of the other UI elements, like the palm control for the timer, were designed in Expression Design. Everything was put together in Expression Blend.

Image 4

3x3 cube model in Blender

Image 5

Expression Design

Image 6

Expression Blend

The application makes use of MahApp.Metro for window styling.

XML
<Controls:MetroWindow
    ...
    xmlns:Controls="http://metro.mahapps.com/winfx/xaml/controls"
    ShowTitleBar="False" 
    ...>
    <Controls:MetroWindow.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Colours.xaml" />                
                <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.xaml" />
                <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/VS/Colours.xaml"/>
                <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/VS/Styles.xaml"/>
                ...
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>                
    </Controls:MetroWindow.Resources>
    ...

There are two MahApps.Metro Flyouts for displaying solve times.

Image 7 

To animate the addition and removal of items in the ItemsControl I use FluidMoveBehavior.

XML
<Controls:MetroWindow.Flyouts>
    <Controls:Flyout Header="2x2x2" Position="Right" Background="#FF0A1521">
        <Grid Margin="10">
            <ScrollViewer Width="330" VerticalScrollBarVisibility="Auto">
                <ItemsControl ItemTemplate="{StaticResource TimesTemplate}" ItemsSource="{Binding Solves2x2}">
                    <ItemsControl.ItemsPanel>
                        <ItemsPanelTemplate>
                            <StackPanel>
                                <i:Interaction.Behaviors>
                                    <ei:FluidMoveBehavior AppliesTo="Children" Duration="0:0:0.5" Tag="DataContext">
                                        <ei:FluidMoveBehavior.EaseY>
                                            <CubicEase EasingMode="EaseIn"/>
                                        </ei:FluidMoveBehavior.EaseY>                                            
                                    </ei:FluidMoveBehavior>
                                </i:Interaction.Behaviors>
                            </StackPanel>
                        </ItemsPanelTemplate>
                    </ItemsControl.ItemsPanel>
                </ItemsControl>
            </ScrollViewer>                
        </Grid>
    </Controls:Flyout>
        
    <Controls:Flyout Header="3x3x3" Position="Right" Background="#FF0A1521">
        <Grid Margin="10">
            <ScrollViewer Width="330" VerticalScrollBarVisibility="Auto">
                <ItemsControl ItemTemplate="{StaticResource TimesTemplate}" ItemsSource="{Binding Solves3x3}">
                    <ItemsControl.ItemsPanel>
                        <ItemsPanelTemplate>
                            <StackPanel>
                                <i:Interaction.Behaviors>
                                    <ei:FluidMoveBehavior AppliesTo="Children" Duration="0:0:0.5" Tag="DataContext">
                                        <ei:FluidMoveBehavior.EaseY>
                                            <CubicEase EasingMode="EaseIn"/>
                                        </ei:FluidMoveBehavior.EaseY>
                                    </ei:FluidMoveBehavior>
                                </i:Interaction.Behaviors>
                            </StackPanel>
                        </ItemsPanelTemplate>
                    </ItemsControl.ItemsPanel>
                </ItemsControl>
            </ScrollViewer>
        </Grid>
    </Controls:Flyout>
</Controls:MetroWindow.Flyouts>

The following is the xaml markup for the ItemsControl's DataTemplate,

XML
<DataTemplate x:Key="TimesTemplate">
    <Border BorderBrush="#FF595959" BorderThickness="0,0,0,1" Height="34" Canvas.Left="18" Canvas.Top="31" Width="300">
        <Grid>
            <TextBlock HorizontalAlignment="Left" Text="{Binding SolveTime}" Margin="12,0,0,0" VerticalAlignment="Center" 
                       FontSize="14" Foreground="#FFD6CD00"/>
            <TextBlock HorizontalAlignment="Left" Text="{Binding TimeOfSolve, StringFormat={}{0:G}}" Margin="104,0,0,0" 
                       VerticalAlignment="Center" 
                       FontSize="14" Foreground="#FFACACAC"/>
            <Button Height="16" Margin="0,0,12,0" Style="{DynamicResource DeleteTimeButtonStyle}" 
                    VerticalAlignment="Center" HorizontalAlignment="Right" Width="16" 
                    Command="{Binding DataContext.DeleteCommand, RelativeSource={RelativeSource FindAncestor, AncestorType=Window}}"
                    CommandParameter="{Binding}"/>
        </Grid>
    </Border>
</DataTemplate>

Code

The application is written in VB.NET. The following code snippet shows some of the methods in class CubePiece which are used to rotate a cube piece around the x, y, and z axes.

VB.NET
Imports System.Windows.Media.Media3D
Imports System.Windows.Media.Animation

Public Class CubePiece
    ...
    Private Sub Rotate(ByVal angle As Double)
        rTransform3D = New RotateTransform3D(axisAngleRtn3D, axisPoint)
        dblAnimation = New DoubleAnimation(CDbl(angle), TimeSpan.FromMilliseconds(ROTATION_TIME),
                                           FillBehavior.HoldEnd)
        axisAngleRtn3D.BeginAnimation(AxisAngleRotation3D.AngleProperty, dblAnimation)
        tr3dGroup.Children.Add(rTransform3D)
        piece.Transform = tr3dGroup
    End Sub
    
    ''' <summary>
    ''' Rotate cube piece around the X-axis
    ''' </summary>
    ''' <param name="angle">The angle of rotation; -90° or 90°</param>
    Public Sub RotateAroundXaxis(ByVal angle As Double)
        axisAngleRtn3D = New AxisAngleRotation3D(New Vector3D(1, 0, 0), 0)
        Rotate(angle)
        ChangeLocationOnXaxisRotation(angle)
    End Sub

    ''' <summary>
    ''' Rotate cube piece around the Y-axis
    ''' </summary>
    ''' <param name="angle">The angle of rotation; -90° or 90°</param>
    Public Sub RotateAroundYaxis(ByVal angle As Double)
        axisAngleRtn3D = New AxisAngleRotation3D(New Vector3D(0, 1, 0), 0)
        Rotate(angle)
        ChangeLocationOnYaxisRotation(angle)
    End Sub

    ''' <summary>
    ''' Rotate cube piece around the Z-axis
    ''' </summary>
    ''' <param name="angle">The angle of rotation; -90° or 90°</param>
    Public Sub RotateAroundZaxis(ByVal angle As Double)
        axisAngleRtn3D = New AxisAngleRotation3D(New Vector3D(0, 0, 1), 0)
        Rotate(angle)
        ChangeLocationOnZaxisRotation(angle)
    End Sub
    ...
End Class

Integration with Aura Interface

One of the requirements for the All-in-One app submission is that the app should integrate with the Aura interface/Horizon shell. To meet this requirement I've created a EXE installer using Inno Setup.

Image 8 

History

  • 27th July 2013: Initial post

License

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