; StoreWordPosition(Word, PlacementIndex_X, PlacementIndex_Y, OrientationDecision); return true;}break;垂直/左下/右下方向
相同的逻辑适用于为这3个方向找到单词的良好布局。它们在矩阵位置和边界检查的增量/减量方面不同。
在所有的单词被放置在矩阵中之后,FillInTheGaps()方法用随机字母填充矩阵的其余部分。此时窗体打开并触发Paint()事件。在这个事件上,我们绘制最终显示为40×40像素矩形的线。然后我们将我们的字符矩阵映射到board上。
Pen pen = new Pen(Color.FromArgb(255, 0, 0, 0));ColourCells(ColouredRectangles, Color.LightBlue);if (FailedRectangles.Count > 0) ColourCells(FailedRectangles, Color.ForestGreen);// Draw horizontal lines.for (int i = 0; i <= GridSize; i++) e.Graphics.DrawLine(pen, 40, (i + 1) * 40, GridSize * 40 + 40, (i + 1) * 40);// Draw vertical lines.for (int i = 0; i <= GridSize; i++) e.Graphics.DrawLine(pen, (i + 1) * 40, 40, (i + 1) * 40, GridSize * 40 + 40);MapArrayToGameBoard();MapArrayToGameBoard()方法简单地把我们的字符矩阵放在board上。我们使用来自MSDN的绘图代码。这遍历矩阵中的所有字符,将它们放置在40×40矩形的中间,边距调整为10像素。
Graphics formGraphics = CreateGraphics();Font drawFont = new Font("Arial", 16);SolidBrush drawBrush = new SolidBrush(Color.Black);string CharacterToMap;for (int i = 0; i < GridSize; i++) for (int j = 0; j < GridSize; j++) { if (WORDS_IN_BOARD[i, j] != '\0') { CharacterToMap = "" + WORDS_IN_BOARD[i, j]; // "" is needed as a means for conversion of character to string. formGraphics.DrawString(CharacterToMap, drawFont, drawBrush, (i + 1) * 40 + 10, (j + 1) * 40 + 10); } }单词发现和有效性检查
鼠标点击位置和释放位置存储在点列表中。对鼠标按钮释放事件(GameBoard_MouseUp())调用CheckValidity()方法。同时,当用户在左键按下的同时拖动鼠标时,我们从起始位置绘制一条线到鼠标指针。这在GameBoard_MouseMove()事件中完成。
if (Points.Count > 1) Points.Pop();if (Points.Count > 0) Points.Push(e.Location);// Form top = X = Distance from top, left = Y = Distance from left.// However mouse location X = Distance from left, Y = Distance from top.// Need an adjustment to exact the location.Point TopLeft = new Point(Top, Left);Point DrawFrom = new Point(TopLeft.Y + Points.ToArray()[0].X + 10, TopLeft.X + Points.ToArray()[0].Y + 80);Point DrawTo = new Point(TopLeft.Y + Points.ToArray()[1].X + 10, TopLeft.X + Points.ToArray()[1].Y + 80);ControlPaint.DrawReversibleLine(DrawFrom, DrawTo, Color.Black); // draw new line单词的有效性在CheckValidity()方法中检查。它通过抓取所有的字母来制定单词,字母通过使用鼠标查看相应的字符矩阵来绘制。然后检查是否真的匹配单词列表中的单词。如果匹配,则通过将单元格着色为浅蓝色并使单词列表中的单词变灰来更新单元格。
以下是抓取行开始和结束位置的代码片段。首先它检查行是否落在边界之外。然后它制定单词并且存储矩阵的坐标。类似地,它检查垂直,左下和右下单词,并尝试相应地匹配。如果这真的匹配,那么我们通过AddCoordinates()方法将临时矩形存储在我们的ColouredRectangles点列表中。
if (Points.Count == 1) return; // This was a doble click, no dragging, hence return.int StartX = Points.ToArray()[1].X / 40; // Retrieve the starting position of the line.int StartY = Points.ToArray()[1].Y / 40;int EndX = Points.ToArray()[0].X / 40; // Retrieve the ending position of the line.int EndY = Points.ToArray()[0].Y / 40;if (StartX > GridSize || EndX > GridSize || StartY > GridSize || EndY > GridSize || // Boundary checks. StartX <= 0 || EndX <= 0 || StartY <= 0 || EndY <= 0){ StatusLabel.Text = "Nope!"; StatusTimer.Start(); return;}StringBuilder TheWordIntended = new StringBuilder();List<Point> TempRectangles = new List<Point>();TheWordIntended.Clear();if (StartY == EndY) // Horizontal line drawn. for (int i = StartX; i <= EndX; i++) { TheWordIntended.Append(WORDS_IN_BOARD[i - 1, StartY - 1].ToString()); TempRectangles.Add(new Point(i * 40, StartY * 40)); }3)计分:对于计分,我们有计分文件。如果缺少,则使用当前分数和类别创建一个。这里,再次,所有的分数被组合在一个大的管道分隔的字符串中,然后该字符串被加密并放入文件。我们有四个实体。
class ScoreEntity{ public string Category { get; set; } public string Scorer { get; set; } public int Score { get; set; } public DateTime ScoreTime { get; set; }............................最多允许一个类别14个分数。首先加载分数列表中的所有分数,然后获得当前分类分数的排序子集。在该子集中,检查当前分数是否大于任何可用的分数。如果是,则插入当前分数。之后,检查子集数是否超过14,如果超过了,就消除最后一个。所以最后的得分消失了,列表总是有14个分数。这在CheckAndSaveIfTopScore()方法中完成。
这里,再次,如果有人篡改得分文件,那么它只会开始一个新的得分。不允许篡改。
4)显示隐藏的单词:如果时间用完了,那么游戏用绿色显示单词。首先,获取玩家找不到的单词。可以是这样的
List<string> FailedWords = new List<string>();foreach (string Word in WORD_ARRAY) if (WORDS_FOUND.IndexOf(Word) == -1) FailedWords.Add(Word);然后,遍历这些失败的单词位置并制定相应的失败的矩阵。最后,它通过无效来调用窗体的paint方法。
foreach (string Word in FailedWords){ WordPosition Pos = WordPositions.Find(p => p.Word.Equals(Word)); if (Pos.Direction == Direction.Horizontal) // Horizontal word. for (int i = Pos.PlacementIndex_X + 1, j = Pos.PlacementIndex_Y + 1, k = 0; k < Pos.Word.Length; i++, k++) FailedRectangles.Add(new Point(i * 40, j * 40)); else if (Pos.Direction == Direction.Vertical) // Vertical word. for (int i = Pos.PlacementIndex_X + 1, j = Pos.PlacementIndex_Y + 1, k = 0; k < Pos.Word.Length; j++, k++) FailedRectangles.Add(new Point(i * 40, j * 40)); else if (Pos.Direction == Direction.DownLeft) // Down left word. for (int i = Pos.PlacementIndex_Y + 1, j = Pos.PlacementIndex_X + 1, k = 0; k < Pos.Word.Length; i--, j++, k++) FailedRectangles.Add(new Point(i * 40, j * 40)); else if (Pos.Direction == Direction.DownRight) // Down right word. for (int i = Pos.PlacementIndex_X + 1, j = Pos.PlacementIndex_Y + 1, k = 0; k < Pos.Word.Length; i++, j++, k++) FailedRectangles.Add(new Point(i * 40, j * 40));}Invalidate();5)作弊码:这是一件小事了。这工作在keyup事件上,这个事件抓取所有的击键到CheatCode变量。实际上,我们合并玩家在游戏窗口上输入的击键,并看看代码是否与我们的CHEAT_CODE(mambazamba)匹配。例如,如果玩家按下“m”和“a”,那么我们在CheatCode变量中将它们保持为’ma’(因为,ma仍然匹配cheatcode模式)。类似地,如果它匹配CHEAT_CODE的模式,则添加连续变量。然而,一旦它不能匹配模式(例如,’mambi’),则重新开始。
最后,如果匹配,则激活作弊码(将剩余时间提高到完整一天,即86,400秒),并应用惩罚。
CheatCode += e.KeyCode.ToString().ToUpper();if (CHEAT_CODE.IndexOf(CheatCode) == -1) // Cheat code didn't match with any part of the cheat code. CheatCode = ("" + e.KeyCode).ToUpper(); // Hence erase it to start over.else if (CheatCode.Equals(CHEAT_CODE) && WORDS_FOUND.Count != MAX_WORDS){ Clock.TimeLeft = 86400; // Cheat code applied, literally unlimited time. 86400 seconds equal 1 day. ScoreLabel.Text = "Score: 0"; StatusLabel.Text = "Cheated! Penalty applied!!"; StatusTimer.Start(); CurrentScore = 0; Invalidate();这里有趣的是,我们必须使用WordsListView的KeyUp事件而不是窗体。这是因为在加载游戏窗口后,列表框有焦点,而不是窗体。
环境免责声明这不是一个OOP项目,它遵循程序性编程,虽然有些地方应用了OOP并且将进程委托给类对象。项目是用RAD方法完成的,以便使事情进行。它需要重构。此外,我没有遵循任何命名约定。我个人的偏好是名字能够说明意图,在将鼠标悬停在名字上面的时候,你可以自动地了解类型。我的意思是,变量’TheWordIntended’根据我没有遵循的标准命名约定应该有一个像’strTheWordIntended’这样的名字。这些都是可以被重构的。
未来的工作有很多事情可以做——应用设计模式,OOP。作为软件开发的本质,重构是必须的。 兴趣点要强制重绘窗口,我们需要调用窗口的Invalidate()方法。也需要通过调整表单顶部和左侧位置来校正鼠标坐标。有趣的是,表单的坐标定义为:X为距离屏幕顶部的距离,Y为距离屏幕左侧的距离。但是,鼠标坐标用另一种方式定义:X为距离窗口左边的距离,Y作为距离窗口顶部的距离。因此,为了校准,我们需要仔细调整。
private void GameBoard_MouseMove(object sender, MouseEventArgs e){ try { if (e.Button == MouseButtons.Left) { if (Points.Count > 1) Points.Pop(); if (Points.Count > 0) Points.Push(e.Location); // Form top = X = Distance from top, left = Y = Distance from left. // However mouse location X = Distance from left, Y = Distance from top. // Need an adjustment to exact the location. Point TopLeft = new Point(Top, Left); Point DrawFrom = new Point(TopLeft.Y + Points.ToArray()[0].X + 10, TopLeft.X + Points.ToArray()[0].Y + 80); Point DrawTo = new Point(TopLeft.Y + Points.ToArray()[1].X + 10, TopLeft.X + Points.ToArray()[1].Y + 80); ControlPaint.DrawReversibleLine(DrawFrom, DrawTo, Color.Black); // draw new line } }瑕疵我发现了一个小问题,如果一台机器有多个监测的话。如果游戏在一个窗口中加载,并且它被移动到另一个窗口的话,则鼠标拖动在第一个窗口上保持疤痕标记。不必恐慌,它在游戏关闭后擦除。
bug到目前为止没有发现任何bug。如果你发现bug的话,欢迎留言。也欢迎提出你的意见。
概要这是一个字母拼图游戏,可预设单词,自定义单词词,对单个词类别计分。
参考文献
作者: 不二晨 时间: 2018-7-23 17:21
优秀,奈斯
作者: 摩西摩西OvO 时间: 2018-7-26 10:25

作者: 吴琼老师 时间: 2018-7-26 16:28
欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) |
黑马程序员IT技术论坛 X3.2 |