Пишем игры на... VBA (Pt. 1)

Пишем игры на... VBA (Pt. 1)

Вступление

Все мы знакомы с Excel. Вот и я, душный банковский служащий, что-то да слышал об этом инструменте.

Одни люди умеют лишь форматировать ячейки, другие создают клоны Doom, используя встроенный в Microsoft Office язык программирования.

Я не претендую на второй тип, но и к первым себя не отношу. Для меня Visual Basic for Applications, или VBA, стал тем первым ЯП, который заинтересовал и позволил уверовать в свои силы. Путешествие в этот интересный мир началось с автоматической записи макросов и разбора созданного программой кода.

Как начинающему программисту-самоучке, мне приходилось активно шерстить интернет в поисках решения возникающих проблем. Огромное коммьюнити, большое количество тематических сайтов и, на крайний случай, справка Microsoft помогали в решении практически любой проблемы.

Однако наступил такой момент, когда все рабочие задачи оказались решены и автоматизированы, а внутреннему программисту (ха-ха) все ещё хотелось чего-то большего.

Тогда меня осенило! В Excel ведь можно делать игры. Первая же найденная статья от программиста, который в армии клепал на основе VBA разные вещи, подтвердила мою теорию.

Эта серия статей не была задумана как обучающая (ведь я сам ещё учусь), но если что-нибудь изложенное будет полезно начинающим программистам, которые только погружаются в мир Visual Basic, то считайте, что я пополнил интернет одной полезной ссылкой.

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

К тому же где, как не в комментариях, мне доходчиво и культурным языком укажут на допущенные ошибки?

Я постараюсь объяснять все подробно, но без углубления в простейшие истины. Не буду рассказывать, что такое переменные, циклы или массивы. Всю эту информацию, изложенную доступным языком, можно найти в интернете.

В этих статьях я буду описывать процесс создания игр в Excel, начиная с простейших пятнашек и заканчивая глобальной RPG. Это позволит мне больше углубиться в тему, а вам поглумиться над неумехой-программистом. Или, в лучшем случае, забрать готовое решение на работу и скрывать безделье за мнимой подготовкой отчета. Ссылки на все исходные материалы будут в свободном доступе.

Темы первых статей цикла уже определены. В этой статье я расскажу, как сделать пятнашки в Excel. В следующий раз разберу создание игры в стиле Toon Blast.

Если вам, к моему удивлению, будет интересен такой формат, то пишите в комментариях, какого типа игры вы бы хотели, чтобы я реализовал на базе Excel.

Итак, хватит грофомании! ПЯТНАШКИ.

Первым делом...

Необходимо определиться с игровым полем. В данном случае все просто. Что есть пятнашки? Диапазон размером 4 на 4 ячейки, что в сумме даёт 16 ячеек. Переименовываем первый лист (по желанию), приводим нужные ячейки к квадратной форме и декорируем, как душе угодно. Получается примерно следующее:

Пишем игры на... VBA (Pt. 1)

Формирование игрового поля

На этом этапе необходимо в случайном порядке расположить числа от 1 до 15, а также пустое поле на выбранном диапазоне. Для этого открываем окно редактирования кода (Alt + F11), добавляем простой модуль (при желании и его можно переименовать для красоты).

Создаем публичную переменную rngPlayField, которая будет хранить координаты нашего игрового поля, и в методе initializeField() пишем следующий код:

Public rngPlayField As Range Sub initializeField() Dim collNumbers As New Collection, countNumbers As Byte, rndNumber As Byte, fndCells As Range Set rngPlayField = Sheets("MAIN").Range("B2:E5") 'игровой диапазон For countNumbers = 1 To 16 'формируем коллекцию чисел collNumbers.Add countNumbers Next For Each fndCells In rngPlayField.Cells 'цикл для каждого элемента на игровом поле rndNumber = Application.WorksheetFunction.RandBetween(1, collNumbers.Count) 'случайное число от 1 до количества элементов в коллекции fndCells.Value = collNumbers.Item(rndNumber) 'значение ячейки = случайное число из коллекции collNumbers.Remove rndNumber 'удаляем использованное число из коллекции Next Call decorateField 'вызываем декорирование поля End Sub

Сперва метод создаёт коллекцию чисел от 1 до 16.

Я заметил особенность, что в VBA в большинстве случаев удобнее пользоваться коллекциями элементов, чем одномерными массивами. В моей практике были и такие случаи, когда приходилось использовать коллекции массивов (возможно, из-за моей некомпетентности).

InitializeField() запускает цикл For Each, который распространяется на каждую ячейку нашего диапазона. На первом этапе цикла программа рассчитывает случайное число от 1 до значения размера нашей коллекции (в настоящий момент 16) и помещает в первую ячейку диапазона число, хранящееся в коллекции под полученным случайным индексом.

Далее цикл удаляет из коллекции использованное число и переходит к следующей ячейке.

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

UPD. Уже в процессе написания статьи я выяснил, что не все комбинации, полученные таким образом, изначально решаемы. Тогда мной в ускоренном темпе был написан код, который сперва формирует правильное поле от 1 до 16, а затем в стиле песков времени разбирает пятнашки в течении 300 ходов (что мы собственно и делаем в реальной жизни, когда хотим "рестартнуть" игру). Направление движения "костяшки" рассчитывается случайным образом.

Sub createRightField() Dim countNumbers As Byte, fndCells As Range, countMoves As Integer, byteRndDir As Byte, fndRndRange As Range countNumbers = 1 For Each fndCells In rngPlayField.Cells 'заполняем поле значениями от 1 до 16 fndCells.Value = countNumbers countNumbers = countNumbers + 1 Next While countMoves < 300 byteRndDir = 3 * Rnd() + 1 ' 1 - up, 2 - down, 3 - left, 4 - right For Each fndRndRange In rngPlayField.Cells If fndRndRange.Value = 16 Then Select Case byteRndDir 'выбор направления Case 1 If fndRndRange.Offset(-1, 0) <> "" Then fndRndRange.Value = fndRndRange.Offset(-1, 0) fndRndRange.Offset(-1, 0) = 16 countMoves = countMoves + 1 End If Case 2 If fndRndRange.Offset(1, 0) <> "" Then fndRndRange.Value = fndRndRange.Offset(1, 0) fndRndRange.Offset(1, 0) = 16 countMoves = countMoves + 1 End If Case 3 If fndRndRange.Offset(0, -1) <> "" Then fndRndRange.Value = fndRndRange.Offset(0, -1) fndRndRange.Offset(0, -1) = 16 countMoves = countMoves + 1 End If Case 4 If fndRndRange.Offset(0, 1) <> "" Then fndRndRange.Value = fndRndRange.Offset(0, 1) fndRndRange.Offset(0, 1) = 16 countMoves = countMoves + 1 End If End Select End If Next Wend Call decorateField End Sub

Имитация движения "костяшек" по игровому полю

Сперва создаём обработчик события нажатия на определенную ячейку. Для этого в модуле листа, на котором расположено игровое поле, формируем метод Worksheet_SelectionChange и пишем в нем следующий код. Переменная Target при этом содержит адрес выбранной ячейки.

Private Sub Worksheet_SelectionChange(ByVal Target As Range) Set rngPlayField = Sheets("MAIN").Range("B2:E5") If Target.Cells.Count > 1 Then Exit Sub ElseIf Not Application.Intersect(rngPlayField, Target) Is Nothing Then 'считывает вхождение нажатой ячейки в игровое поле Call moveCells(Target) End If End Sub

При выборе диапазона ячеек (больше одной), а также если выбранная ячейка не относится к игровому полю, программа завершает работу.

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

В основном модуле в методе moveCells(byVal rngCell as range) пишем следующее:

Sub moveCells(ByVal rngCell As Range) 'процедура нажатия на ячейку с числом Dim checkCells As Integer For checkCells = -1 To 1 Step 2 'цикл для проверки ячеек слева/справа и сверху/снизу от выбранной ячейки If rngCell.Offset(checkCells, 0).Value = 16 And Not Application.Intersect(rngPlayField, rngCell.Offset(checkCells, 0)) Is Nothing Then 'проверка сверху/снизу rngCell.Offset(checkCells, 0).Value = rngCell.Value rngCell.Value = 16 ElseIf rngCell.Offset(0, checkCells).Value = 16 And Not Application.Intersect(rngPlayField, rngCell.Offset(0, checkCells)) Is Nothing Then 'проверка слева/справа rngCell.Offset(0, checkCells).Value = rngCell.Value rngCell.Value = 16 End If Next Call decorateField ' вызываем декорирование поля

Цикл проверяет, есть ли слева, справа, снизу или сверху от нажатой ячейки пустое поле (его имитирует число 16) и считывает вхождение смещенной ячейки в диапазон игрового поля. Если условия выполнены, то программа просто меняет числа местами.

Декорирование игрового поля и проверка победы в методе decorateField()

Создаём в рабочей книге второй лист и размещаем на нем победный вариант. Пример:

Пишем игры на... VBA (Pt. 1)

Далее пишем следующий код:

Sub decorateField() 'декорирует и проверяет на победу Dim fndZero As Range, rngRightData As Range, countRows As Byte, countColumns As Byte, countRight As Integer Set rngRightData = Sheets("DATA").Range("A1:D4") 'диапазон с правильными значениями For countRows = 1 To 4 For countColumns = 1 To 4 If rngPlayField.Cells(countRows, countColumns) = rngRightData.Cells(countRows, countColumns) rngPlayField.Cells(countRows, countColumns) <> 16 Then 'декорирование правильной позиции With rngPlayField.Cells(countRows, countColumns) .Interior.Color = RGB(0, 150, 0) 'vbGreen показался мне слишком ярким .Font.Color = vbWhite End With countRight = countRight + 1 Else With rngPlayField.Cells(countRows, countColumns) .Interior.Color = xlNone .Font.Color = vbBlack End With End If Next Next For Each fndZero In rngPlayField 'цвет шрифта для нуля If fndZero = 16 Then fndZero.Font.Color = vbWhite End If Next If countRight = 15 Then 'проверка победы MsgBox "Победа!", vbExclamation, "Победа" End If End Sub

Данная программа с помощью цикла проверяет значение каждой ячейки игрового поля на соответствие аналогичной ячейке поля с победной расстановкой значений и закрашивает правильные варианты в зелёный цвет. И наоборот. Далее метод проверяет поле на наличие числа 16 (которое имитирует пустую "костяшку", не забываем) и устанавливает для шрифта белый цвет. Последний момент: проверка победы. При каждом совпадении каких-либо чисел с правильной позицией увеличивается переменная countRight, и когда ее значение станет равно 15, игра сообщит о победе.

ЭПИЛОГ

В итоге должно получиться примерно следующее:

Пишем игры на... VBA (Pt. 1)

Вот таким нехитрым образом можно создать себе развлечение на случай скучных рабочих дней. А тем временем на ум приходит следующая картинка:

Пишем игры на... VBA (Pt. 1)

При желании в игру можно добавить подсчет ходов, рейтинговую таблицу или график динамики побед.

Сейчас же я считаю, что на этом можно остановиться.

Следующая статья выйдет When it's done. Скорее всего в течение недели.

3232
19 комментариев

Это очень подлый поступок - вставить картинку с троллейбусом сразу в статью

36
Ответить

пусть нахуй сам тогда и комментирует и обсирает свой код

6
Ответить

Никто не покармадрочит зато. Хотя...

1
Ответить

Комментарий недоступен

2
Ответить

Да, все так. Если вырезаешь часть кода, это обычно не нравится редактору. Но я делал это в 10 версии, может в более новых и пофиксили.

1
Ответить

Абстрагируясь от вопросов «зачем», комменты можно (я бы сказал, нужно) писать перед кодом, к которому коммент предназначается. А длинные условия переносить на новую строку.

2
Ответить