|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
IntroductionI once stepped on Clickomania next generation from Matthias Schüssler and liked the game so much that I decided to implement that game in C# from scratch. My version is simpler but uses a lot of the same design. I tested the game on an Athlon 2000+ and it seemed to work smoothly. Features
The object modelI use two big classes, public struct Scores
{
public int _iScores; // score
public string _sNames; // player name
public int _SbestMove; // his best move
public int _SballsLeft; // balls left after game over
public int _Stime; // time for that game
public string _Date; // the date he played (mm/dd/yyyy mm:ss)
public int _sTakeBacks; // how many times did he take back
// during the game
}
One for the public struct Ball
{
public int _icolor; // its color
public bool _exists; // does it still exist
public bool _isDisappearing; // is it in animation status
public int _vel; // velocity of animation
public int _yvel;
}
and one for the public struct Turn
{
public int _itColor; // Color of disappeared balls
public int [] _itposX; // Position
public int [] _itposY;
public int [] _column; // Disappeared columns
}
A bit about the logicOn the When the player clicks on the ball, it checks if it belongs to a group of balls with the same color. This is done with a recursive function: private int CheckNextBall(int x, int y, int color)
{
int px, nx, py, ny;
px = (x == 0 ? 0 : x - 1); //prior X
nx = (x == 7 ? 7 : x + 1); //next X
py = (y == 0 ? 0 : y - 1); //prior Y
ny = (y == 11 ? 11 : y + 1);//next Y
int ret = 1;
// the 4 "if" statements do basically the
// same, they check if the
// neighbour of the actual ball has the same color
if((_ball[px, y]._icolor == color) && (_ball[px, y]._exists) && (px != x))
{
// make it inexistant
_ball[px, y]._exists = false;
// to animate it in the main form
_ball[px, y]._isDisappearing = true;
// save color of ball( so we can take the turn back)
_turn[_turn.GetLength(0) - 1]._itColor = color;
_X[_index] = px; // and save its position (for the same reason)
_Y[_index] = y;
_index++;
ret += CheckNextBall(px, y, color);
}
if((_ball[nx, y]._icolor == color) && (_ball[nx, y]._exists) && (nx != x))
{
_ball[nx, y]._exists = false;
_ball[nx, y]._isDisappearing = true;
_turn[_turn.GetLength(0) - 1]._itColor = color;
_X[_index] = nx;
_Y[_index] = y;
_index++;
ret += CheckNextBall(nx, y, color);
}
if((_ball[x, py]._icolor == color) && (_ball[x, py]._exists) && (py != y))
{
_ball[x, py]._exists = false;
_ball[x, py]._isDisappearing = true;
_turn[_turn.GetLength(0) - 1]._itColor = color;
_X[_index] = x;
_Y[_index] = py;
_index++;
ret += CheckNextBall(x, py, color);
}
if((_ball[x, ny]._icolor == color) && (_ball[x, ny]._exists) && (ny != y))
{
_ball[x, ny]._exists = false;
_ball[x, ny]._isDisappearing = true;
_turn[_turn.GetLength(0) - 1]._itColor = color;
_X[_index] = x;
_Y[_index] = ny;
_index++;
ret += CheckNextBall(x, ny, color);
}
return ret;
}
This function checks the color of the four neighboring balls, and if they have the same color, sets its state to disappearing and checks the neighbors for their colors. It also saves the information about color and position in the AnimationThis is done in the /// <summary>
/// Balls Disappear or appear
/// </summary>
/// <param name="appear"> if they are appearing or disappearing</param>
public void MakeBallsAppear(bool appear)
{
if(!appear)
{
PlayWav(1); // play a sound
// redraw all balls, but animate only thosse with appear state
// this occours when balls that are clicked disappear
for(int i = 0; i < 12; i++)
{
_var = i;
Thread.Sleep(10); // sleep 10 ms between 2 frames
this.pictureBox1.Refresh();
}
_var = 0;
}
else
{
PlayWav(8);
// same thng here too, but balls reappaer when
// player takes a move back
for(int i = 11; i >= 0; i--)
{
_var = i;
Thread.Sleep(10);
this.pictureBox1.Refresh();
}
}
}
The /// <summary>
/// here all the drawing takes place. for each
/// frame, this event is called once,
/// whith a different _var, _posx or _posy
/// if an animation takes place
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
for(int i = 0; i < 8; i++)
{
for(int j = 0; j < 12; j++)
{
if(engine._ball[i, j]._exists)
{
g.DrawImage(_bmp[engine._ball[i, j]._icolor],
(i * 24) - _posx * engine._ball[i, j]._yvel,
(j * 24) + _posy * engine._ball[i, j]._vel,
24, 24);
}
if(engine._ball[i, j]._isDisappearing)
{
g.DrawImage(_bmp[engine._ball[i, j]._icolor],
(i * 24) + _var, (j * 24) + _var,
24 - (_var*2), 24 - (_var*2));
}
}
}
if((_gameover) && (!_gamewon))
{
// shadow of the text
g.DrawString("GAME OVER", new Font("Arial", 20),
System.Drawing.Brushes.Black,
new Point(10, 122));
g.DrawString("GAME OVER", new Font("Arial", 20),
System.Drawing.Brushes.LightBlue,
new Point(8, 120));
}
if(_gamewon)
{
// shadow of the text
g.DrawString("GAME WON", new Font("Arial", 20,
System.Drawing.FontStyle.Bold),
System.Drawing.Brushes.Black,
new Point(12, 122));
g.DrawString("GAME WON", new Font("Arial", 20,
System.Drawing.FontStyle.Bold),
System.Drawing.Brushes.Yellow,
new Point(10, 120));
}
}
SoundsThe [DllImport("winmm.dll")]
private static extern bool PlaySound( string lpszName,
int hModule, int dwFlags );
private void PlayWav(int play)
{
if(checkBox1.Checked)
{
string myFile = ".\\Sounds\\default.wav";
switch(play)
{
case 1:
myFile = ".\\Sounds\\BallDisappear.wav";
break;
case 2:
myFile = ".\\Sounds\\BallDown.wav";
break;
case 3:
myFile = ".\\Sounds\\lost.wav";
break;
case 4:
myFile = ".\\Sounds\\newgame.wav";
break;
case 5:
myFile = ".\\Sounds\\ColumnDis.wav";
break;
case 6:
myFile = ".\\Sounds\\ColumnAppear.wav";
break;
case 7:
myFile = ".\\Sounds\\BallUp.wav";
break;
case 8:
myFile = ".\\Sounds\\BallAppear.wav";
break;
case 9:
myFile = ".\\Sounds\\Won.wav";
break;
case 10:
myFile = ".\\Sounds\\illegal.wav";
break;
default:
break;
}
PlaySound(myFile, 0, 0x0003); // Play the sound
}
}
The WAV files have to be in the subfolder Sounds of the folder containing Clickmania.exe. I have used some sounds of the game Half Life 2. If you want to use your own sounds, you can upload them to the sounds folder and rename them. ScoresAll scores are stored in a binary file called Scores.sco in the same folder as the exe. If this file does not exist, it will be generated when the game is run. This file also contains the name of the last user who reached the top 10, so he shouldn't reenter his name if he reaches the top10 again. To view the high scores, I have used the private void PopulateListbox()
{
string name, score;
listView1.Items.Clear(); // remove items in listview
ListViewItem [] items = new ListViewItem[10];
//DateTime date;
Color textColor = new Color();
Font font;
for(int i = 0; i < 10; i++)
{
name = (i + 1).ToString() + ":" +
" " + _MF._stScores[i]._sNames;
score = _MF._stScores[i]._iScores.ToString();
if (i == 9)name = (i + 1).ToString() + ":" +
" " + _MF._stScores[i]._sNames;
textColor = _MF._stScores[i]._SballsLeft == 0 ?
System.Drawing.Color.Blue : System.Drawing.Color.Black;
textColor = (_MF._stScores[i]._sTakeBacks == 0 &&
_MF._stScores[i]._SballsLeft == 0) ?
Color.DarkRed : textColor;
font = (textColor == Color.Black) ?
new Font("Arial", 8) : new Font("Fixedsys", 8,
(_MF._stScores[i]._sTakeBacks == 0) ?
FontStyle.Italic : FontStyle.Regular);
items[i] = new ListViewItem(new string[] {name, score,
_MF._stScores[i]._Date,
_MF._stScores[i]._Stime.ToString(),
_MF._stScores[i]._SbestMove.ToString(),
_MF._stScores[i]._SballsLeft.ToString(),
_MF._stScores[i]._sTakeBacks.ToString()},
-1, textColor, Color.White, font);
listView1.Items.Add(items[i]);
}
}
Information is shown in a different color according to how the game was played, for example if the player left no balls or if he took no moves back. About the programThis is one of my first C# implementations, so be indulgent if my code seems junk at some places.
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||