Writing the Level Creator


Although you could create a fancy 3D graphics version of the level creator for the Blockers game, one thing any good tool developer realizes is that tools need to be functional and written quickly. Why spend all the time making a fancy 3D graphics version, when you can simply use the WinForms controls to get the job done easily? To get started, load the Blockers solution file, right-click on the solution in Solution Explorer, and click Add New Project. Choose a WinForms project, and call it LevelCreator.

This step automatically generates some code, so open the form1.cs file in code view (not in designer view), and replace the automatically generated variables with these:

 private static readonly Color[] BoxColors = {Color.Black, Color.Red ,             Color.Blue, Color.Green, Color.Yellow, Color.SandyBrown,             Color.WhiteSmoke, Color.DarkGoldenrod, Color.CornflowerBlue,             Color.Pink         }; int playerIndex = 0; private const int SquareSize = 13; private System.ComponentModel.Container components = null; private static readonly string MediaPath =     ConfigurationSettings.AppSettings.Get("MediaPath"); private System.Windows.Forms.Label label1; private System.Windows.Forms.Label label2; private System.Windows.Forms.Label label3; private System.Windows.Forms.Label label4; private System.Windows.Forms.CheckBox chkWrap; private System.Windows.Forms.Label label5; private System.Windows.Forms.Label label6; private System.Windows.Forms.TextBox txtTime; private System.Windows.Forms.TextBox txtMoves; private System.Windows.Forms.NumericUpDown levelNumber; private System.Windows.Forms.Label label7; private System.Windows.Forms.Button btnSave; private System.Windows.Forms.Button[] levelButtons; private byte[] levelIndices; private System.Windows.Forms.Button[] mainButtons; private int[] mainColors; 

You'll notice that the box colors array matches the one you have in the Blockers game. If you change one, you need to make sure that you change them in both places. The same goes for the SquareSize constant. You'll also see a variable to maintain the starting location of the player (marked by an asterisk). Next, you'll see a series of controls that make up the user interface controls for the application.

Next, replace the constructor with the following one:

 public Form1() {          //          // Required for Windows Form Designer support          //          InitializeComponent();     // Create the main buttons     mainButtons = new Button[4];     mainColors = new int[4];     for (int i = 0; i < 4; i++)     {         mainButtons[i] = new Button();         mainButtons[i].Tag = i;         mainButtons[i].BackColor = System.Drawing.Color.Black;         mainButtons[i].Location = new System.Drawing.Point(16 + (72 * i), 40);         mainButtons[i].Size = new System.Drawing.Size(24, 24);         mainButtons[i].TabStop = false;         mainButtons[i].Click +=new EventHandler(OnMainButtonClick);         mainColors[i] = 0;         this.Controls.Add(mainButtons[i]);     }     levelButtons = new Button[SquareSize * SquareSize];     levelIndices = new byte[levelButtons.Length];     int index = 0;     this.SuspendLayout();     Font f = new Font(this.Font.Name, 10.0f);     for (int j = 0; j < SquareSize; j++)     {         for (int i = 0; i < SquareSize; i++)         {             levelButtons[index] = new Button();             levelButtons[index].Tag = index;             levelButtons[index].Font = f;             levelButtons[index].BackColor = System.Drawing.Color.Black;             levelButtons[index].Location = new System.Drawing.Point(2 +                                            (22 * i), 80 + (22 * j));             levelButtons[index].Size = new System.Drawing.Size(20, 20);             levelButtons[index].TabStop = false;             levelButtons[index].Click +=new EventHandler(OnLevelButtonClick);             levelIndices[index] = 0;             this.Controls.Add(levelButtons[index]);             index++;         }     }     this.ResumeLayout(true); } 

After calling the InitializeComponent method (which you'll see in a moment), this code creates two sets of buttons: one set of four at the top, which are the level colors, and then a 13x13 array of buttons that make up the level itself. Each block in the level is one of the four main buttons or black, signifying that it's empty and doesn't exist in the level. When you choose the colors of the main level buttons, any corresponding button in the level itself should change colors. Before you define the event handlers you've used in the constructor, here is the InitializeComponent method:

 private void InitializeComponent() {     this.label1 = new System.Windows.Forms.Label();     this.label2 = new System.Windows.Forms.Label();     this.label3 = new System.Windows.Forms.Label();     this.label4 = new System.Windows.Forms.Label();     this.chkWrap = new System.Windows.Forms.CheckBox();     this.label5 = new System.Windows.Forms.Label();     this.label6 = new System.Windows.Forms.Label();     this.txtTime = new System.Windows.Forms.TextBox();     this.txtMoves = new System.Windows.Forms.TextBox();     this.levelNumber = new System.Windows.Forms.NumericUpDown();     this.label7 = new System.Windows.Forms.Label();     this.btnSave = new System.Windows.Forms.Button();     ((System.ComponentModel.ISupportInitialize)(this.levelNumber)).BeginInit();     this.SuspendLayout();     this.label1.Location = new System.Drawing.Point(8, 8);     this.label1.Size = new System.Drawing.Size(64, 23);     this.label1.TabIndex = 0;     this.label1.Text = "Color 1";     this.label2.Location = new System.Drawing.Point(80, 8);     this.label2.Size = new System.Drawing.Size(64, 23);     this.label2.TabIndex = 1;     this.label2.Text = "Color 2";     this.label3.Location = new System.Drawing.Point(160, 8);     this.label3.Size = new System.Drawing.Size(64, 23);     this.label3.TabIndex = 2;     this.label3.Text = "Color 3";     this.label4.Location = new System.Drawing.Point(232, 8);     this.label4.Size = new System.Drawing.Size(64, 23);     this.label4.TabIndex = 3;     this.label4.Text = "Color 4";     this.chkWrap.Location = new System.Drawing.Point(296, 40);     this.chkWrap.Size = new System.Drawing.Size(120, 16);     this.chkWrap.TabIndex = 4;     this.chkWrap.Text = "Wrap Colors?";     this.label5.Location = new System.Drawing.Point(296, 72);     this.label5.Size = new System.Drawing.Size(104, 23);     this.label5.TabIndex = 9;     this.label5.Text = "Number Seconds";     this.label6.Location = new System.Drawing.Point(296, 136);     this.label6.Size = new System.Drawing.Size(104, 23);     this.label6.TabIndex = 10;     this.label6.Text = "Max Moves";     this.txtTime.Location = new System.Drawing.Point(296, 96);     this.txtTime.Size = new System.Drawing.Size(96, 20);     this.txtTime.TabIndex = 11;     this.txtTime.Text = "180.0";     this.txtMoves.Location = new System.Drawing.Point(296, 168);     this.txtMoves.Size = new System.Drawing.Size(96, 20);     this.txtMoves.TabIndex = 12;     this.txtMoves.Text = "500";     this.levelNumber.Location = new System.Drawing.Point(296, 232);     this.levelNumber.Minimum = new System.Decimal(new int[] {         1, 0, 0, 0});     this.levelNumber.Size = new System.Drawing.Size(104, 20);     this.levelNumber.TabIndex = 13;     this.levelNumber.Value = new System.Decimal(new int[] {        1, 0, 0, 0});     this.label7.Location = new System.Drawing.Point(296, 208);     this.label7.Size = new System.Drawing.Size(104, 23);     this.label7.TabIndex = 14;     this.label7.Text = "Level Number";     this.btnSave.Location = new System.Drawing.Point(304, 320);     this.btnSave.Size = new System.Drawing.Size(104, 24);     this.btnSave.TabIndex = 15;     this.btnSave.Text = "Save Level";     this.btnSave.Click += new System.EventHandler(this.OnSaveLevel);     this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);     this.ClientSize = new System.Drawing.Size(424, 382);     this.Controls.Add(this.btnSave);     this.Controls.Add(this.label7);     this.Controls.Add(this.levelNumber);     this.Controls.Add(this.txtMoves);     this.Controls.Add(this.txtTime);     this.Controls.Add(this.label6);     this.Controls.Add(this.label5);     this.Controls.Add(this.chkWrap);     this.Controls.Add(this.label4);     this.Controls.Add(this.label3);     this.Controls.Add(this.label2);     this.Controls.Add(this.label1);     this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.Fixed3D;     this.MaximizeBox = false;     this.MinimizeBox = false;     this.Name = "Form1";     this.Text = "Blockers Level Creator";     ((System.ComponentModel.ISupportInitialize)(this.levelNumber)).EndInit();     this.ResumeLayout(false); } 

With that code, you should be able to view the window in the designer now. It should look something like Figure A.1.

Figure A.1. Design view.


Now you can add the event handlers you declared in the constructor:

 private void OnMainButtonClick(object sender, System.EventArgs e) {     Button b = sender as Button;     int index = (int)b.Tag;     mainColors[index]++;     if (mainColors[index] >= BoxColors.Length)         mainColors[index] = 0;     ((Button)sender).BackColor = BoxColors[mainColors[index]];     UpdateBoxColors(); } private void OnLevelButtonClick(object sender, EventArgs e) {     Button b = sender as Button;     int index = (int)b.Tag;     if ((Form.MouseButtons & MouseButtons.Right) == MouseButtons.Right)     {         playerIndex = index;     }     levelIndices[index]++;     if (levelIndices[index] >= 5)         levelIndices[index] = 0;     UpdateBoxColors(); } 

In each of these methods, you take the button and update its color to the next color in the array. In the level buttons, using the right mouse button also has the effect of moving the player's start position to that point. You'll notice each of the methods also calls this UpdateBoxColors method, as follows:

 private void UpdateBoxColors() {     for(int i = 0; i < levelButtons.Length; i++)     {         if (levelIndices[i] < 4)         {             levelButtons[i].BackColor = mainButtons[levelIndices[i]].BackColor;         }         else         {             levelButtons[i].BackColor = System.Drawing.Color.Black;         }         if (playerIndex == i)         {             if (levelButtons[i].BackColor == System.Drawing.Color.Black)             {                 playerIndex++;             }             else             {                 levelButtons[i].Text = "*";             }         }         else         {             levelButtons[i].Text = string.Empty;         }     } } 

Finally, all you need to do is write the code to save the level out to a file. This code should look familiar because it is basically the inverse of the loading code you wrote for Blockers:

 private void OnSaveLevel(object sender, System.EventArgs e) {     FileStream stm = File.Create(MediaPath + string.Format("Level{0}.lvl",                                                  (int)levelNumber.Value));     float maxTime = float.Parse(txtTime.Text);     int maxMoves = int.Parse(txtMoves.Text);     stm.Write(BitConverter.GetBytes(maxTime), 0, sizeof(float));     stm.Write(BitConverter.GetBytes(maxMoves), 0, sizeof(int));     int numColors = 0;     for (int i = 0; i < 4; i++)     {         if (mainColors[i] > 0)             numColors++;         else             break;     }     if (numColors < 2)         throw new InvalidOperationException("You need more colors defined.");     BlockColor[] colors = new BlockColor[numColors];     int numberColors = colors.Length;     stm.Write(BitConverter.GetBytes(numberColors), 0, sizeof(int));     for (int i = 0; i < colors.Length; i++)     {         colors[i] = (BlockColor)(mainColors[i] - 1);         stm.WriteByte((byte)colors[i]);     }     BlockColor final = colors[colors.Length - 1];     stm.WriteByte((byte)final);     stm.Write(BitConverter.GetBytes(chkWrap.Checked), 0, sizeof(bool));     int blockIndex = 0;     for (int j = 0; j < SquareSize; j++)     {         for (int i = 0; i < SquareSize; i++)         {             if (levelIndices[blockIndex] < numColors)             {                 stm.WriteByte(levelIndices[blockIndex]);             }             else             {                 stm.WriteByte((byte)0xff);             }             blockIndex++;         }     }     stm.Write(BitConverter.GetBytes(playerIndex), 0, 4);     stm.Close(); } 

Because this code uses the sizeof keyword, you are required to turn on unsafe code. With that, you're free to make as many levels as you want for Blockers. See Figure A.2 for an example of the final level creator in action.

Figure A.2. The level creator in action.


Have fun!



Beginning 3D Game Programming
Beginning 3D Game Programming
ISBN: 0672326612
EAN: 2147483647
Year: 2003
Pages: 191
Authors: Tom Miller

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net