I'm a huge fan of the Rubik's Cube and I solve it several times, almost daily. Ever since I got my first Rubik's Cube it has captivated me, and frustrated me in my attempts at getting faster at solving - I currently average 17 sec. It was only natural then that I took a shot at making a WPF version.
The 3D APIs in WPF are not designed to provide full-featured game development capabilities but it is possible to develop something as 'simple' as a Rubik's Cube app. In this article I'll not delve into the finer details of WPF's 3D features so I will expect that you are conversant with that part of WPF. Another thing that you should be familiar with is basic cubing terms. Just in case, this is how you denote the faces of a cube,
Using the App
To turn a layer of the cube hold down the left mouse button and swipe in the direction you intend to rotate the layer. Once you let go of the left-mouse button the layer will rotate as intended.
Rotating the U layer anti-clockwise
The same principle also applies to whole cube rotations but instead of using the left-mouse button you use the right-mouse button.
Note that to make rotations you only interact with the F and R faces of the cube. To scramble the cube click the Scramble button.
Design & Layout
Designing the 3D model of the cube by hand-coding XAML would have been a tedious affair so I instead opted to model it in Blender.
Modelling of the cube in progress
The final result
After modeling the cube in Blender I exported the 3D model as a Wavefront (.obj) file which I then added to my WPF project in Expression Blend, together with the corresponding material (.mtl) file.
The material and object files
With the material and object file in the project, adding the 3D model to a WPF container is just a matter of right-clicking the object file and selecting Insert from the context-menu.
For this project I added more lights for adequate illumination of the cube so the
Viewport3D contains several
DirectionalLight and an
Viewport3D is masked by several
Paths that are used to deal with mouse events. I could have opted to use
ModelUIElement3D - that support input, focus, and events - but opted to stick with
ModelVisual3D object that represents a cube piece is named according to the colors of its 'stickers' e.g.
WGR_Cubie is the White-Green-Red corner piece.
The project contains three enumerations. The
PieceLocations enumerations represent the location in 3D space of a cube piece.
Public Enum PieceLocations
Notations enumerations represent the notations of a Rubik's Cube.
Public Enum Notations
FaceNotations enumerations represent the location of a
Path on the faces of the cube. Remember that only two faces of the cube are masked by
Public Enum FaceLocations
FacePath defines an attached property that is set on the masking
Public Class FacePath
Public Shared ReadOnly LocationProperty As DependencyProperty =
DependencyProperty.RegisterAttached("Location", GetType(FaceLocations), GetType(FacePath),
Public Shared Sub SetLocation(ByVal element As Path, ByVal value As FaceLocations)
Public Shared Function GetLocation(ByVal element As Path) As FaceLocations
Return CType(element.GetValue(LocationProperty), FaceLocations)
Each cube piece in the
Viewport3D is associated with an object of class
CubePiece which contains methods for rotating the associated
ModelVisual3D around a particular axis.
Public Class CubePiece
Public piece As ModelVisual3D
Private axisPoint As New Point3D(0, 0, 0)
Private axisAngleRtn3D As New AxisAngleRotation3D(New Vector3D(0, 0, 1), 0)
Private dblAnimation As DoubleAnimation
Private rotateTx3D As RotateTransform3D
Private tx3dGroup As New Transform3DGroup
Private Const ROTATION_TIME As Double = 200
Friend Property PieceLocation() As PieceLocations
Private Sub RotateAroundAxis(ByVal angle As Double)
rotateTx3D = New RotateTransform3D(axisAngleRtn3D, axisPoint)
dblAnimation = New DoubleAnimation(CDbl(angle), TimeSpan.FromMilliseconds(ROTATION_TIME),
piece.Transform = tx3dGroup
Public Sub RotateAround_X_axis(ByVal angle As Double)
axisAngleRtn3D = New AxisAngleRotation3D(New Vector3D(1, 0, 0), 0)
Public Sub RotateAround_Y_axis(ByVal angle As Double)
axisAngleRtn3D = New AxisAngleRotation3D(New Vector3D(0, 1, 0), 0)
Public Sub RotateAround_Z_axis(ByVal angle As Double)
axisAngleRtn3D = New AxisAngleRotation3D(New Vector3D(0, 0, 1), 0)
Scrambler contains methods for scramble generation and scrambling of the cube.
Public Class Scrambler
Private index As Integer
Private isScrambling As Boolean
Private Const CLOCKWISE As Double = -90
Private Const ANTI_CLOCKWISE As Double = 90
Private mainWin As MainWindow
Private rnd As Random
Private scrambleTimer As DispatcherTimer
Private scramble As List(Of Notations)
Public Sub New(ByRef win As MainWindow)
mainWin = win
rnd = New Random
scramble = New List(Of Notations)
scrambleTimer = New DispatcherTimer
scrambleTimer.Interval = New TimeSpan(0, 0, 0, 0, 400)
AddHandler scrambleTimer.Tick, AddressOf Timer_Tick
Public Sub ScrambleCube()
If isScrambling = False Then
For i As Integer = 0 To 19
isScrambling = True
mainWin.CubeGroupGrid.IsEnabled = False
MessageBox.Show("Scrambling of cube already in progress", _
"WPF Rubiks", MessageBoxButton.OK, MessageBoxImage.Exclamation)
Private Sub AddToScramble()
Dim randomNum As Integer = rnd.Next(0, 18)
Dim newNotation As String = [Enum].GetName(GetType(Notations), randomNum)
Dim notation As Notations
If (scramble.Count > 0) Then
Dim lastItem As String = scramble(scramble.Count - 1).ToString
If newNotation.Contains("F") AndAlso lastItem.Contains("F") Then
ElseIf newNotation.Contains("R") AndAlso lastItem.Contains("R") Then
ElseIf newNotation.Contains("B") AndAlso lastItem.Contains("B") Then
ElseIf newNotation.Contains("L") AndAlso lastItem.Contains("L") Then
ElseIf newNotation.Contains("U") AndAlso lastItem.Contains("U") Then
ElseIf newNotation.Contains("D") AndAlso lastItem.Contains("D") Then
notation = [Enum].Parse(GetType(Notations), newNotation)
notation = [Enum].Parse(GetType(Notations), newNotation)
Private Sub Timer_Tick(ByVal sender As Object, ByVal e As EventArgs)
If (index < 19) Then
index += 1
isScrambling = False
index = 0
mainWin.CubeGroupGrid.IsEnabled = True
The project contains a few more classes that deal mainly with determining which direction a layer or the cube should be rotated. You can take a look at these classes by downloading the project source from the download link at the top of the article page.
I hope you'll have a fun time solving the WPF Rubik's Cube. I have scrambled and solved it several times and it is quite as thrilling as the real thing.
- 1st Feb, 2012: Initial post,
- 25th Sep, 2014: Updated article and source