Simon: memory game from the eighties






4.36/5 (8 votes)
May 11, 2007
3 min read

71261

1519
Simple memory game that uses shaped buttons

Introduction
I wanted to be able to create non-rectangular buttons, just out of interest more than anything else. I had seen several complex versions on the web, but I thought I could come up with something simpler. Whilst trawling the web, I came across a flash version of Simon, which I enjoyed playing, so here is a recreation of it with my shaped buttons.
There is no sound, but if someone wants to add it, then please send me a copy of the updated code.
Shaped Button
This was achieved using the following steps:
- Add a new
UserControl
to your application. - View the code and change the inherited type from
UserControl
toButton
.public partial class simonButton : Button { public simonButton() { InitializeComponent(); } }
- The shape of the button is achieved through the use of two
GraphicsPath
objects. Declare these in the top of the button class.public partial class simonButton : Button { private GraphicsPath path; private GraphicsPath innerPath; ...
Thepath
denotes the outline of the button, while theinnerPath
denotes the shape of the flat surface of the button. - Now override the
OnPaint
method of the button, to take care of the drawing, so that is looks like the following code.protected override void OnPaint(PaintEventArgs pevent) { Graphics g = pevent.Graphics; g.SmoothingMode = SmoothingMode.AntiAlias; }
So that we do not get jagged curves, set theSmoothingMode
toAntiAlias
. - The
path
object is filled with aLinearGradientBrush
. This is simple to achieve by supplying a rectangle and a newLinearGradientBrush
.// Create Rectangle To Limit brush area. Rectangle rect = new Rectangle(0, 0, 150, 150); LinearGradientBrush linearBrush = new LinearGradientBrush(rect, Color.FromArgb(40,40,40), this.ForeColor, 225);
Next we instantiate thepath
object.path = new GraphicsPath();
Add items to thepath
object to create the required shape.path.AddArc(0, 0, 270, 270, 180, 90); path.AddArc(120, 0, 30, 30, 270, 90); path.AddLine(150, 0, 150, 85); path.AddArc(100, 100, 100, 100, -90, -90); path.AddLine(100, 150, 0, 150); path.AddArc(0, 120, 30, 30, 90, 90); path.AddArc(0, 0, 270, 270, 180, 90);
Fill thepath.
g.FillPath(linearBrush, path);
Finally, dispose of the brush to free memory.linearBrush.Dispose();
- The
innerPath
object is filled with aSolidBrush
.Brush b = new SolidBrush(this.ForeColor);
Next we instantiate theinnerPath
object.innerPath = new GraphicsPath();
Add items to theinnerPath
object to create the required shape.innerPath.AddArc(10, 10, 250, 250, 180, 90); innerPath.AddArc(130, 10, 10, 10, 270, 90); innerPath.AddLine(140, 0, 140, 90); innerPath.AddArc(90, 90, 100, 100, -90, -90); innerPath.AddLine(90, 140, 10, 140); innerPath.AddArc(10, 130, 10, 10, 90, 90);
Fill theinnerPath.
g.FillPath(b, innerPath);
Finally, dispose of the brush to free memory.b.Dispose();
- So far, this draws our shaped button, but you can click anywhere in the associated rectangle, so to stop this, simply set the
Region
property of the button topath
.this.Region = new Region(path);
The button will now only accept clicks within the region of the shape. - So that the user has some feedback that they are over a button, changing the cursor is a simple thing to do. Override the
OnMouseEnter
andOnMouseLeave
methods and change the cursor within them.protected override void OnMouseEnter(EventArgs e) { this.Cursor = Cursors.Hand; base.OnMouseEnter(e); } protected override void OnMouseLeave(EventArgs e) { this.Cursor = Cursors.Arrow; base.OnMouseLeave(e); }
- The button also needs visual feedback that it has been pressed. In this case, the button wants to appear as if a light has come on behind it. This requires several new items and some changes to the code. First off, a boolean value is required to store if the click has happened so that it can be drawn in the correct state.
private bool _clicked = false; public bool Clicked { get { return _clicked; } set { _clicked = value; Invalidate(); } }
Now override theOnMouseDown
andOnMouseUp
to set theClicked
state accordingly.protected override void OnMouseDown(MouseEventArgs mevent) { _clicked = true; base.OnMouseDown(mevent); } protected override void OnMouseUp(MouseEventArgs mevent) { _clicked = false; base.OnMouseUp(mevent); }
Add a new brush.PathGradientBrush pgbrush = new PathGradientBrush(innerPath); pgbrush.CenterPoint = new Point(75, 75); pgbrush.CenterColor = Color.White; pgbrush.SurroundColors = new Color[] { this.ForeColor };
Redraw the button according to theClicked
state.if (_clicked == false) { g.FillPath(linearBrush, path); g.FillPath(b, innerPath); } else { g.FillPath(linearBrush, path); g.FillPath(pgbrush, innerPath); }
Remember to dispose of the brush correctly.
Using the code
Once you have built the application, just drop the control on a form. The buttons are a fixed size, so they are not scaleable. Again, if someone wants to improve on this please send me a copy of the code.
Game Logic
The game logic is fairly simple, so I will not write about that here, except to say that only sequences of 1000 are generated. My best score so far is 18, so I think 1000 is reasonable.
Have fun.
History
- May 11, 2007 - Original version posted