Опубликован: 24.03.2009 | Доступ: свободный | Студентов: 2286 / 115 | Оценка: 4.24 / 3.93 | Длительность: 17:47:00
Лекция 3:

Использование Visual Studio с Silverlight 2

< Лекция 2 || Лекция 3: 12345 || Лекция 4 >
Перемешиваем фрагменты

Фрагменты головоломки перемешиваются с помощью довольно простого алгоритма перемешивания, который перебирает массив 100 раз, выбирая два случайных элемента каждый раз; если эти два элемента разные, он меняет местами их содержимое. В конце он загружает в последний элемент значение -1, чтобы обозначить пустой квадрат.

Здесь представлен алгоритм перемешивания:

void shuffle() 
{ // Инициализируем игровое поле 
 for (int n = 0; n < 15; n ++)  { 
  board[n] = n; 
 }
 Random rand = new Random(System.DateTime.Now.Second); 
 for (int n = 0; n < 100; n++) { 
   int n1 = rand.Next(15); 
   int n2 = rand.Next(15); 
   if (n1 != n2) { 
     int tmp = board[n1]; 
     board[n1] = board[n2]; 
     board[n2] = tmp; 
    } 
  } 
 board[15] = -1;
}

Следующий шаг после того, как фрагменты перемешаны, - отрисовка игрового поля.

Отрисовка игрового поля

На данный момент мы имеем все фрагменты головоломки, определенные как элементы Image в элементе Canvas, и массив целых чисел, значение индекса n которого соответствует фрагменту, отображаемому в данном месте. Вы также перемешали этот массив целых чисел, поэтому пришло время отрисовать игровое поле. Это делается следующим образом:

void drawBoard() { 
  int nx = 0; 
  int ny = 0;
  for (int n = 0; n < 15; n ++) { 
    nx = n / 4; 
    ny = n % 4; 
    if(board[n]> = 0) { 
      cI[board[n]].SetValue(Canvas.TopProperty, Convert.toDouble(ny * 100)); 
      cI[board[n]].SetValue(Canvas.LeftProperty, Convert.toDouble(nx * 100));
    }
  } 
}

В данном случае выполняется цикл с изменением индекса от 0 до 14 (в головоломке 15 фрагментов) и вычисление координат x,y для каждого блока в сетке 4 х 4. Значение x -это просто результат целочисленного деления индекса массива на 4, и значение y -это модуль индекса, деленного на 4. Умножаем эти значения на 100 и получаем необходимые координаты для отрисовки элемента Canvas.

Теперь мы имеем полностью инициализированную игру. Рисунок располагается справа, а игровое поле с перемешанными фрагментами рисунка - слева.

Обработка действий пользователя

Теперь необходимо заняться обработкой действий пользователя. В играх подобного рода предполагается, что пользователь будет щелкать фрагмент изображения, и если этот фрагмент находится рядом с пустой областью, он плавно переместится в эту область, оставляя пустым занимаемое до этого место. Итак, мы должны обработать щелчки по элементу Canvas, содержащего фрагмент изображения. Если вернуться к инициализации фрагментов, можно вспомнить такую строку:

cI[nx].MouseLeftButtonDown += new MouseButtonEventHandler(Page  MouseLeftButtonDown);

Здесь описывается, что по щелчку Canvas будет запускаться обработчик события

PageMouseLeftButtonDown. Этот обработчик события задан для каждого из блоков Canvas.

Код этого обработчика события состоит из двух разделов. Первый определяет, который из Canvas сформировал событие и положение этого Canvas на игровом поле:

void PageMouseLeftButtonDown(object sender, MouseButtonEventArgs e) { 
 Canvas c = sender as Canvas; 
 int nCanvasID = -1; 
 int nBoardLoc = -1; 
 int nEmptyLoc = -1; 
 for (int i = 0; i < 16; i++) { 
  if (c == cI[i]) { nCanvasID = i; break; } 
 }
 for (int i = 0; i < 16; i++) { 
  if (board[i] == nCanvasID) {
    nBoardLoc = i; 
  }
  else if (board[i] == -1) 
  { nEmptyLoc = i; } 
 }

Итак, например, вы щелкнули фрагмент, представляющий верхний левый угол полного изображения (блок 0), но в настоящий момент он находится в нижнем левом углу игрового поля (позиция 12). При возникновении события щелчка, выполняется перебор всего массива элементов Canvas, представляющих фрагменты головоломки, до тех пор, пока не будет найден фрагмент, соответствующий Canvas, по которому было произведен щелчок. И здесь вы можете получить индекс этого элемента в массиве, сохранив его в переменной nCanvasID (в нашем гипотетическом примере он равнялся бы 0). После этого, просканировав игровое поле, можно определить местоположение элемента 0 на нем и присвоить значение его позиции переменной nBoardLoc (которое в нашем гипотетическом примере равно 12). И, находясь в этой позиции, найдите местоположение пустой ячейки на игровом поле и сохраните его в nEmptyLoc.

Вторая часть кода проверяет возможность перемещения фрагмента, и, если перемещение возможно, перемещает фрагмент в пустую ячейку и соответствующим образом обновляет игровое поле.

// Проверяем возможность перемещения 
if ((nBoardLoc == nEmptyLoc + 1) ||
   (nBoardLoc == nEmptyLoc - 1)  ||
   (nBoardLoc == nEmptyLoc + 4) ||
   (nBoardLoc == nEmptyLoc - 4)) {
  int nx = nEmptyLoc/4;
  int ny = nEmptyLoc%4;
  cI[nCanvasID].SetValue(Convert.toDouble(Canvas.TopProperty, ny * 100)); 
  cI[nCanvasID].SetValue(Convert.toDouble(Canvas.LeftProperty, nx * 100));
  board[nEmptyLoc] = nCanvasID; board[nBoardLoc] = -1;
  checkWinner(); 
}
else {
 // не делаем ничего }
}

Для этого сначала проверим местоположение пустой ячейки относительно положения блока, по которому был произведен щелчок. Если они располагаются непосредственно рядом друг с другом (над, под, слева или справа), перемещение возможно. Поскольку игровое поле 4x4 является одномерным массивом, сделать это несложно. Позиции элементов, располагающихся слева и справа от текущего элемента, отличаются от текущей на -1 и +1, соответственно, и позиции элементов сверху и снизу - на -4 и +4, соответственно. Так что если результат проверки фрагментов указывает на пустую ячейку, вы знаете, что можете двигаться.

Чтобы выполнить перемещение, необходимо получить координаты x и y относительно положения пустого фрагмента и задать их как координаты блока, по которому был произведен щелчок. Затем присвоить позиции поля, на которой до этого находилась пустая ячейка, значение Canvas, по которому был произведен щелчок, и позиции поля, на которой до этого располагался Canvas, по которому был произведен щелчок, значение -1, указывая на то, что теперь эта ячейка пуста.

Наконец, вызовем функцию checkWinner(), чтобы проверить успешность решения головоломки.

Проверка условия выигрыша

Функция checkWinner (Проверка победителя) проверяет успешность решения головоломки. Опять же, все очень просто. Головоломка решена, если каждый n -ный элемент игрового поля равен n, т.е. Canvas 0 имеет индекс 0, Canvas1 - индекс 1 и т.д.

Самый эффективный способ сделать это - предположить, что головоломка решена успешно, и проска-нировать игровое поле в поисках несоответствия board[n] значению n, что будет свидетельствовать о неверном решении и приводить к завершению цикла. Если все индексы совпадают, у нас есть победитель!

void checkWinner() {
  bool bCompleted = true; 
  for (int n = 0; n < 15; n ++) {
    if (n != board[n]) {
      bCompleted = false; break; } 
  }
  if (bCompleted) {
  // Игрок выиграл....сделайте для него что-нибудь приятное. 
  } 
}

Теперь вы получили все элементы простой игры-головоломки с перемещением фрагментов, написанной на С# для Silverlight 2. И для этого понадобилось всего лишь 100 строк кода. По ходу чтения этого курса данный пример можно расширять, добавляя анимацию перемещения блоков, сохранение лучших результатов, возможность загрузки изображений в приложение и т.д. Нет пределов совершенству!

Заключение

В данной лекции мы рассмотрели Visual Studio 2008 и предлагаемые ею различные инструменты и шаблоны для разработки приложений Silverlight с использованием языков программирования .NET. Был детально разобран проект, созданный на базе стандартных шаблонов Silverlight, рассмотрены все его файлы и то, как он используется для разработки и развертывания приложения Silverlight. После этого мы применили теорию на практике и создали полнофункциональную игру-головоломку с перемещением фрагментов, используя XAML и C#. Это позволило почувствовать возможности Silverlight 2, но мы лишь слегка прикоснулись к ним. В части 2 данного курса, "Программирование в Silverlight 2", будут более глубоко рассмотрены функциональные возможности, доступные в .NET Framework, включая создание собственных элементов управления, работу с сетью и обмен информацией, данные и XML, динамические языки программирования и серверные элементы управления ASP.NET.

< Лекция 2 || Лекция 3: 12345 || Лекция 4 >