Использование массивов при программировании игр
Идеология. Есть ли польза от массивов при программировании игр? Вопрос праздный. Массивы необходимы и для шахмат, и для шашек, и для морского боя, и для крестиков-ноликов, и для многих других игр, в особенности для тех, где игра проходит на прямоугольном поле, расчерченном на квадраты. Возьмем для примера игру против компьютера в крестики-нолики на поле размером 3 на 3. Компьютеру приходится здесь рисовать на экране большие клетки, а в них – нолики (кружочки) после ваших ходов и крестики (пересекающиеся косые линии) после своих. Но этого умения недостаточно. Компьютеру ведь еще надо соображать, куда ставить крестики. А для этого нужно как минимум знать, где уже стоят крестики и нолики. А откуда он это знает? Если знание об этом хранится только на экране, то это очень неудобно, так как анализировать информацию о пикселях экрана трудно. Гораздо разумнее заранее организовать массив Dim a (3, 3) As Integer и записывать туда в нужные места нолики после ходов человека и, скажем, единички после ходов компьютера. Сразу же после записи в элемент массива нуля или единицы программа должна рисовать в соответствующем месте экрана кружок или крестик. Мыслить компьютер мог бы при помощи примерно таких операторов – If a(1,1)=0 And a(1,2)=0 Then a(1,3)=1 Это очевидный защитный ход компьютера – на два кружочка в ряду он ставит в тот же ряд крестик. Итак, сделаем вывод, что массив в памяти компьютера и поле для игры на экране в любой момент времени соответствуют друг другу, но компьютеру удобнее глядеть не на экран, а в память. Проиллюстрируем идею использования массивов в играх подробнее, на специально придуманном примитивном примере (типа морского боя, но гораздо проще). Задание на создание игры: Играют друг против друга два человека на квадратном поле размером 2 на 2:
Компьютер в игре участвует только как судья, а не как игрок. Правила: Сначала первый игрок тайком от второго сообщает компьютеру, в каких двух клетках находятся его одноклеточные корабли (например, в правой верхней и правой нижней). Затем второй сообщает компьютеру, в какие клетки он производит два выстрела (тоже, конечно, наугад – например, в левую верхнюю и правую нижнюю). Если подбиты оба корабля – он выиграл, если подбит один – ничья, если ни одного – выиграл первый игрок. Сначала запрограммируем эту игру без графики, а потом с графикой. Поле боя должно быть показано на экране только один раз – после двух выстрелов, причем, если без графики, то в виде распечатки из 4 букв: М к О х Здесь я использовал такие обозначения: о - корабля здесь нет и сюда не стреляли к - неподбитый корабль х - подбитый корабль м - мимо (стреляли и промахнулись) Других вариантов быть не может. Вы видите, что приведенная распечатка отражает результат расстановки кораблей и выстрелов, описанных мной в качестве примера. Вот программа без графики: Dim a(2, 2) As String 'Поле боя Dim i, j, Подбито As Integer
'Главная процедура: Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click a(1, 1) = "о" : a(1, 2) = "о" 'Поначалу на поле боя кораблей нет a(2, 1) = "о" : a(2, 2) = "о" Устанавливаем_корабль(1) Устанавливаем_корабль(2) Подбито = 0 'Пока не стреляли Выстрел(1) Выстрел(2) Показываем_поле_боя() Debug.WriteLine(Подбито) 'Показываем исход битвы - количество подбитых кораблей End Sub
Sub Устанавливаем_корабль(ByVal Номер_корабля As Integer) i = InputBox("Первый игрок, назовите номер строки для корабля " & Номер_корабля) j = InputBox("Первый игрок, назовите номер столбца для корабля " & Номер_корабля) a(i, j) = "к" End Sub
Sub Выстрел(ByVal Номер_выстрела As Integer) i = InputBox("Второй игрок, назовите номер строки для выстрела " & Номер_выстрела) j = InputBox("Второй игрок, назовите номер столбца для выстрела " & Номер_выстрела) If a(i, j) = "к" Then 'Если попал, то a(i, j) = "х" 'ставим крестик Подбито = Подбито + 1 'и увеличиваем счетчик подбитых кораблей ElseIf a(i, j) = "о" Then 'иначе если промахнулся, то a(i, j) = "м" 'ставим м End If End Sub
Sub Показываем_поле_боя() For i = 1 To 2 For j = 1 To 2 Debug.Write(a(i, j)) Next j Debug.WriteLine("") Next i End Sub Пояснения: Вы видите, что в качестве массива, представляющего поле для игры, я выбрал строковый массив Dim a(2, 2) As String 'Поле боя Теперь прочтите главную процедуру. Убедитесь, что она правильно отражает основной порядок действий в процессе игры. Затем разберитесь в процедурах Устанавливаем_корабль и Выстрел. Они с параметрами. Наконец, разберитесь в процедуре Показываем_поле_боя. Программа не объявляет итогов боя, а всего лишь печатает количество подбитых кораблей. Определение и объявление победителя оставляю вам. Обратите внимание, что игра мгновенно переделывается из игры на поле 2 на 2 в игру на поле, скажем, 30 на 30, простой заменой числа 2 в тексте программы на число 30. Этой возможностью мы наслаждаемся только благодаря использованию массива! В этом случае, конечно, придется в цикле записать во все клеточки поля букву «о». А если мы хотим при этом иметь больше двух кораблей и двух выстрелов, нам придется в главной процедуре обратиться в цикле к процедуре Устанавливаем_корабль и в цикле к процедуре Выстрел, что очень просто. Если бы мы пренебрегли секретностью, то могли бы показывать поле боя после каждого хода игроков. Для этого достаточно в конец процедур Устанавливаем_корабль и Выстрел включить строку Показываем_поле_боя()
Программа с графикой. Придумаем для простоты такую графику. Поле состоит из 4 цветных квадратов. Вот возможные цвета: Голубой квадрат - корабля здесь нет и сюда не стреляли Серый квадрат - неподбитый корабль Красный квадрат - подбитый корабль Зеленый квадрат - мимо (стреляли и промахнулись) Замечательно, что от добавлении графики программа абсолютно не изменится за исключением единственной процедуры Показываем_поле_боя. Причем и в ней-то вся структура цикла останется неизменной. По большому счету выкинем только строку Debug.WriteLine("") как нужную только для текстового вывода, а строку, печатающую очередную букву из четырех: Debug.Write(a(i, j)) заменим фрагментом, рисующим очередной квадрат из четырех. Вот новая процедура Показываем_поле_боя: Sub Показываем_поле_боя() Dim Размер As Integer = 100 'Размер квадрата Dim Гр As Graphics = Me.CreateGraphics Dim Кисть_для_воды As New SolidBrush(Color.LightBlue) Dim Кисть_для_корабля As New SolidBrush(Color.Gray) Dim Кисть_для_попадания As New SolidBrush(Color.Red) Dim Кисть_для_промаха As New SolidBrush(Color.Green) Dim Кисть As SolidBrush 'Текущая кисть для квадрата Гр.Clear(Color.White) 'Стираем поле, нарисованное после предыдущего хода For i = 1 To 2 For j = 1 To 2 Select Case a(i, j) 'Выбираем кисть для очередного квадрата Case "о" : Кисть = Кисть_для_воды Case "к" : Кисть = Кисть_для_корабля Case "х" : Кисть = Кисть_для_попадания Case "м" : Кисть = Кисть_для_промаха End Select 'Рисуем очередной квадрат: Гр.FillRectangle(Кисть, Размер * j, Размер * i, Размер, Размер) Next j Next i End Sub Пояснения: Предположим, наша процедура работает после каждого хода игроков. Мы могли бы написать ее так, чтобы после каждого очередного хода (поставили корабль или выстрелили) компьютер перерисовывал только тот квадрат, о котором шла речь. Остальные ведь остались неизменными – чего их перерисовывать? В этом случае компьютеру пришлось бы «меньше трудиться». Но я пошел по более простому и универсальному пути – после каждого хода все поле со всеми квадратами стирается и рисуется заново согласно содержимому массива a. Кстати, строка Гр.Clear(Color.White) 'Стираем поле, нарисованное после предыдущего хода в нашем случае излишня. Ведь мы все равно заново перерисовываем все квадраты поля, поэтому предварительно стирать их не имеет смысла. Крестики-нолики 3х3 – советы. В принципе вы уже готовы к программированию игры против компьютера в обычные крестики-нолики 3х3. Всю техническую сторону дела мы прошли. Остается логика, то есть объяснение компьютеру, куда ставить крестики. И вот с логикой-то у нас будет проблема. Вы скажете: Какая проблема? – ведь клеточек всего 9 штук! Один из возможных операторов уже написан: If a(1,1)=0 And a(1,2)=0 Then a(1,3)=1 Напишу еще пару десятков подобных операторов – и дело с концом! – А пару сотен не хотите?! Вы только попробуйте перебрать все возможные варианты расстановки крестиков, ноликов и пустых клеток! Их вообще несколько тысяч. В этом случае, чтобы сократить программу, нужно применять в качестве индексов переменные величины и ломать голову над тем, какие писать процедуры, ветвления и циклы. Поэтому любителям игр рекомендую для тренировки запрограммировать крестики-нолики не против компьютера, а как игру человека с человеком, где компьютер – лишь судья. Если получится, вот тогда можно замахнуться и на большее. Но и здесь идите постепенно. Рекомендую написать большую процедуру для правильной простановки крестика в произвольном одномерном массиве из трех клеток. У этой процедуры будет три параметра – по числу клеток. Затем заметьте, что в реальной игре 3х3 вас интересует только 8 рядов: 3 по горизонтали, 3 по вертикали и 2 по диагонали. Значит у вас будет 8 обращений к этой процедуре. Дальше думайте сами. Все это совсем не просто. Массивы как объекты Оказывается, массив – это объект. Объект класса Arrayпространства имен System. Как?! – скажете вы, – мы до сих пор прекрасно работали с массивами и, как говорится, «ни сном, ни духом»! Мы нигде не писали New, не пользовались свойствами и методами массивов. – Что ж, верно, многим программистам вполне можно работать с массивами и не подозревать, что это объекты. Авторы VB замаскировали этот факт (как мне кажется), чтобы не пугать программистов, переходящих с Visual Basic 6.0 на VB. Массивы-объекты рождаются в вашей программе «нечувствительно» для вас безо всякого New. И все же, вот как можно создать массив при помощи New: Dim a() As Integer = New Integer() {8, 1, 4, 3} Нам будут полезны некоторые свойства и методы массивов (см. процедуру): Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click Dim a() As Integer = {80, 60, 50, 90, 40, 20, 50, 70} Dim t(,) As Integer = {{99, 99, 99, 99, 99}, {99, -8, -14, -19, -18}, {99, 25, 28, 26, 20}, {99, 11, 18, 20, 25}} Debug.WriteLine(a.Length) 'Длина массива a (число элементов) = 8 Debug.WriteLine(t.Length) 'Длина массива t (число элементов) = 20 Debug.WriteLine(t.GetUpperBound(0)) 'Число строк (макс. индекс первого измерения) - 1 = 3 Debug.WriteLine(t.GetUpperBound(1)) 'Число столбцов (макс. индекс второго измерения) - 1 = 4 'Ищется первое вхождение числа 50 в одномерный массив a и находится его индекс (2): Debug.WriteLine(Array.IndexOf(a, 50)) 'Ищется последнее вхождение числа 50 в одномерный массив a и находится его индекс (6): Debug.WriteLine(Array.LastIndexOf(a, 50)) Debug.WriteLine(Array.IndexOf(a, 55)) 'Ищется число 55 в массиве a и не находится (-1) Array.Reverse(a) 'Все элементы массива a меняют порядок на обратный = {70, 50, 20, 40, 90, 50, 60, 80} Array.Sort(a) 'Все элементы массива a сортируются по возрастанию = {20, 40, 50, 50, 60, 70, 80, 90} Array.Clear(a, 4, 3) 'Обнуляется 3 элемента массива a, начиная с индекса 4= {20, 40, 50, 50, 0, 0, 0, 90} End Sub Из приведенных методов некоторые имеют несколько вариантов, которые я здесь не привожу. Замечание. Учитывая, что массив – это объект, я призываю вас до поры не присваивать массив массиву целиком, без индексов, например, вот так: Dim a() As Integer = {8, 1, 5, 2} Dim b() As Integer b = a Присвоение, конечно, состоится, но совсем не такое, как вы ждали. Оно может привести к неожиданным для начинающих последствиям. Вот к каким, например. Продолжу фрагмент: a(2) = 99 Debug.WriteLine(b(2)) Напечатается 99, а не 5, потому что массив – это объект. Почему? Расскажу позднее, в 27.2. Массивы как параметры До этого момента параметр процедуры или функции был для нас каким-то одним данным: это или одно число, или одна строка, или один объект. Но параметр может быть и массивом. Задача: Имеется два массива, по три числа в каждом. Напечатать сумму элементов каждого массива. Использовать функцию sum, единственным параметром которой является суммируемый массив. Программа: Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click Dim a() As Integer = {4, 10, 20} Dim b() As Integer = {100, 40, 50} Debug.WriteLine(sum(a)) Debug.WriteLine(sum(b)) End Sub
Function sum(ByVal c() As Integer) As Integer sum = c(0) + c(1) + c(2) End Function Выполните программу в пошаговом режиме, глядя в окно Locals. Понаблюдайте, как массив c принимает в себя значения элементов массивов a и b. Задание 105. В школе два класса. В каждом – два-три десятка учеников. Каждый ученик получил отметку на экзамене по физике. Определить, какой из двух классов учится ровнее (будем считать, что ровнее учится тот класс, в котором разница между самой высокой и самой низкой отметкой меньше). Указание: Создать функции Минимум(c), Максимум(c) и Разница(c). Задание 106. На двух метеостанциях (A и B) в течение года измерялась температура. Соответственно созданы два массива чисел длиной 365. Затем оказалось, что на обеих станциях термометры были испорчены: на станции A термометр все время показывал температуру на 2 градуса выше настоящей, а на станции B – на 3 градуса ниже. Написать процедуру с двумя параметрами, которая исправляет один произвольный массив и с ее помощью исправить оба массива. Один параметр процедуры – величина поправки, другой – массив температур.
Популярное: Почему люди поддаются рекламе?: Только не надо искать ответы в качестве или количестве рекламы... Почему двоичная система счисления так распространена?: Каждая цифра должна быть как-то представлена на физическом носителе... Организация как механизм и форма жизни коллектива: Организация не сможет достичь поставленных целей без соответствующей внутренней... Как построить свою речь (словесное оформление):
При подготовке публичного выступления перед оратором возникает вопрос, как лучше словесно оформить свою... ©2015-2024 megaobuchalka.ru Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. (1330)
|
Почему 1285321 студент выбрали МегаОбучалку... Система поиска информации Мобильная версия сайта Удобная навигация Нет шокирующей рекламы |