КАК
Я РАЗРАБАТЫВАЛ ИГРУ "СТРЕЛОК 50"
Половый
Александр
ПРОЛОГ
По своей профессии я
инженер-конструктор. VB мне нужен в основном для
разработки расчетных программ, решающих мои прикладные задачи, т.е. основным
заказчиком моих программ являюсь я сам, и разумеется мне за них никто не
платит.
Поэтому меня можно отнести скорее
к программистам-любителям, у которых есть E‑mail только на бесплатном
сервере Polovy@mail.ru. Но, честно говоря,
мне кажется, не этот признак должен определять любитель ты или профессионал.
В свободное от основной работы
время я программирую "для души", для напряжения своего ума, для
самовыражения, так сказать.
Часто бывает, что я берусь за
программу для того, чтобы доказать (скорее самому себе), что на VB можно
сделать все и даже больше. Я вижу чью-то разработку и говорю себе "я это
сделаю на VB!". Например, так я поступил с "Виртуальной
линейкой" (Ruler.exe), ее я сделал после того, как увидел программу Cool Ruler (www.fabsoft.com). Если Вам интересно, сравните их. У еще одной моей
программы RulerR.exe вообще аналогов нет, ведь она вращается!
Так вот игру "Стрелок
50" я задумал после того, как поиграл в одну из "стрелялок".
"Это просто" сказал я
себе и принялся за работу.
"СТРЕЛЯЛКА" ПО
ПРОИЗВОЛЬНО ДВИЖУЩИМСЯ ЦЕЛЯМ
Итак, что нам нужно? Прежде всего
я определился, что моя программа работает в двух режимах:
1) Режим "ИГРА" FGamePlay = True, когда я как бешенный должен
носиться за мишенями по всему окну и уничтожить как можно больше мишеней.
2) Режим "СТАНДАРТНАЯ ПРОГРАММА"
FGamePlay = False, в котором моя игра
выполняет функции любой другой программы: открывается, закрывается, меняет
свои размеры, может быть поверх остальных окон, включает/выключает звуки и сохраняет
эти параметры в INI-файле.
Сначала поговорим о режиме
"ИГРА".
СТРЕЛЬБА
Стрельба должна быть похожа на
настоящую, для этого курсор мыши превращаем в перекрестие:
…
Me.MousePointer = 99
Me.MouseIcon = Image2.Picture
…
В Image2.Picture я поместил файл курсора в виде
перекрестия. Дефицита в подобных файлах сейчас нет, а кое-что можно и самому
нарисовать, например в ImageEdit.exe.
Выстрел должен звучать:
…
Dim PW
As Integer
…
If MnuGameSound.Checked = True Then
PW = sndPlaySound(SW(1), SND_ASYNC Or SND_NODEFAULT)
End If
…
Флаг SND_ASYNC – прерывает
предыдущий звук и проигрывает текущий
Флаг SND_NODEFAULT – не позволяет системе проигрывать звук по
умолчанию, если не найден текущий звук
Путь к файлу со звуком выстрела
1.wav хранится в переменной SW(1):
…
SW(1) = App.Path & "\1.WAV"
…
Не забываем в Module1.bas указать функцию
sndPlaySound:
…
Public Declare Function sndPlaySound Lib
"winmm.dll" Alias "sndPlaySoundA" (ByVal lpszSoundName As
String, ByVal uFlags As Long) As Long
Global Const SND_ASYNC = 1
Global Const SND_SYNC = 0
Global Const SND_NODEFAULT = &H2
…
Если прицел наведен и выстрел
сделан, то обязательно появится пулевое отверстие:
…
If
Button = 1 Then
…
IShoot = IShoot + 1
…
Load Image4(IShoot)
Image4(IShoot).Picture = Image4(0).Picture
Image4(IShoot).Left = X - Image4(IShoot).Width / 2
Image4(IShoot).Top = Y - Image4(IShoot).Height / 2
Image4(IShoot).Visible = True
…
End If
…
Реагируем только на левую кнопку
мыши Button = 1. С каждым выстрелом количество
отверстий IShoot увеличивается на
1. Получается массив объектов Image4, "клонированных"
из Image4(0).Picture.
Процессы создания отверстий
помещаем в события MouseDown всех объктов,
находящихся на форме – это сама форма, метки на ней, мишени да и сами картинки
с пулевыми отверстиями.
Если событие произошло на мишени Image1 –
то попадание:
…
Private Sub Image1_MouseDown(Index As Integer, Button As
Integer, Shift As Integer, X As Single, Y As Single)
…
CurNShoots = CurNShoots + 1 'общее количество выстрелов
…
CurNTargetsTerm = CurNTargetsTerm + 1 'мишень унитожена
RatCurNTargetsTerm = RatCurNTargetsTerm + 1 * ScaleKoef
'стоимость мишени
FlagTargetTerm(Index) = True ' цель уничтожена
…
End Sub
…
Флаг FlagTargetTerm показывает: уничтожена мишень или
нет.
На остальных объектах – промах,
например на форме:
…
Private Sub Form_MouseDown(Button As Integer, Shift As
Integer, X As Single, Y As Single)
…
CurNShoots = CurNShoots + 1
'общее количество выстрелов
…
CurNMiss = CurNMiss + 1 ' число промахов
RatCurNMiss = RatCurNMiss + 1 * ScaleKoef ' стоимость
промаха
…
End Sub
…
Со временем на форме остается
слишком много пулевых отверстий, поэтому периодически, скажем 1 раз в 3
секунды, проводим их "зачистку":
…
'------------------------------------------------------
'удалить
пулевые отверстия
'------------------------------------------------------
Private Sub Timer4_Timer()
If FGamePlay = True Then
ClearHoles 'очищаем отверстия от пуль
Else ' игра окончена
Timer4.Enabled = False
End If
End Sub
…
Private Sub ClearHoles()
Dim I As Integer
For I = Image4.Count - 1 To 1 Step -1
Unload Image4(I)
Next I
IShoot = 0
End Sub
'------------------------------------------------------
'удалить
пулевые отверстия
'------------------------------------------------------
…
МИШЕНИ
Стрелять мы научились, теперь
"посоздаем" мишени. Пожалуй, это самое интересное в моей программе.
Чем любая игра похожа на жизнь?
Непредсказуемостью.
За непредсказуемость у нас
отвечает генератор случайных чисел в интервале от Nmin до Nmax:
…
Private Function NRnd(Nmin As Integer, Nmax As Integer) As
Integer
Randomize
NRnd = Int((Nmax - Nmin + 1) * Rnd) + Nmin 'произвольный номер
End
Function
…
и генератор случайного
направления (знака "+" или "–") :
…
Private Function NSgnRnd() As Integer
Randomize
If Int(2 * Rnd) = 0 Then NSgnRnd = 1 Else NSgnRnd = -1
End
Function
…
Вот этими базовыми функциями я и
напичкал процессы создания и движения мишеней. Процессы эти сложные, так что
без таймеров не обойтись Timer1 отвечает за "движение",
а Timer2 за "рождение" мишени.
РОЖДЕНИЕ МИШЕНИ
Все мишени я отсортировал по типу
движения TypeTarget:
…
Select Case TypeTarget(I) ' тип движения цели
Case 0 ' не подвижен
…
Case 1 'горизонтальное без
смены направления
…
Case 2 'горизонтальное со сменой направления
…
Case 3 'вертикальное без смены направления
…
Case 4 'вертикальное со сменой направления
…
Case 5,
6 ' под любым углом без смены направления
…
Case 7,
8 ' под любым углом со сменой направления
…
Case 9 To 12 ' по окружности
…
End Select
…
Тип движения цели генерируется
так:
…
TypeTarget(ITarget)
= NRnd(0, 12) ' случайный тип движения цели
…
Чтобы управлять долей вероятности
создания того или иного типа мишени, я на каждый тип отвел разное количество
допустимых значений ITarget. Так для
неподвижной мишени ITarget=0, а для
двигающейся по окружности ITarget=9, 10, 11, 12,
что означает вероятность возникновения последней мишени в 4 раза выше, чем
первой, а этого мне и хотелось.
У каждой мишени есть свои
характеристики: внешний вид, скорость (точнее шаг движения по горизонтали и
вертикали, направление и т.п. (изменять размер мишеней я не захотел). Все эти
характеристики присваиваются мишени в момент ее рождения:
…
Private Sub Timer2_Timer()
…
ITarget = Image1.Count 'новая цель
…
Load Image1(ITarget) ' загружаем цель
…
ReDim Preserve TypeTarget(1 To ITarget) 'создаем тип двжения
ReDim Preserve FlagTargetTerm(1 To ITarget)
…
ReDim Preserve LeftStepTarget(1 To ITarget) 'шаг по
горизонтали
ReDim Preserve TopStepTarget(1 To ITarget) 'шаг по виртекали
…
' движение по окружности
ReDim Preserve AlfaTargetR(1 To ITarget)
ReDim Preserve AlfaStepTargetR(1 To ITarget)
ReDim Preserve RStepTargetR(1 To ITarget)
ReDim Preserve XrStepTargetR(1 To ITarget)
ReDim Preserve YrStepTargetR(1 To ITarget)
…
End Sub
…
Оператор ReDim изменяет размер массива, а параметр Preserve заставляет сохранять информацию, хранящуюся в
массиве перед изменением.
Массивы задаются на уровне формы
без задания размерности:
…
Dim TypeTarget() As Integer ' тип движения цели
Dim ITarget As Integer ' количество целей
Dim FlagTargetTerm() As Boolean ' цель уничтожена?
…
'прямолинейное движение
Dim LeftStepTarget() As Single ' шаг перемещения
Dim TopStepTarget() As Single ' шаг перемещения
…
'
движение по окружности
Dim
AlfaTargetR() As Single ' угол перемещения
Dim AlfaStepTargetR() As Single ' шаг угла перемещения
Dim RStepTargetR() As Single ' шаг перемещения
Dim XrStepTargetR() As Single ' шаг перемещения
Dim YrStepTargetR() As Single ' шаг перемещения
…
Ну и разумеется, значения
присваиваем этим характеристикам с максимальной непредсказуемостью (в пределах
разумного конечно):
…
AlfaTargetR(ITarget) = NRnd(0, 360) / 60
AlfaStepTargetR(ITarget) = NRnd(5, 15) / 100 * NSgnRnd
RStepTargetR(ITarget) = (Me.ScaleWidth + Me.ScaleHeight) /
6 * NRnd(1, 100) / 100
XrStepTargetR(ITarget) = Me.ScaleWidth * NRnd(1, 100) / 100
YrStepTargetR(ITarget) = Me.ScaleHeight * NRnd(1, 100) /
100
…
'случайный шаг и направление
LeftStepTarget(ITarget) = NRnd(50, 150) * NSgnRnd
TopStepTarget(ITarget) = NRnd(50, 150) * NSgnRnd
…
FlagTargetTerm(ITarget) = False ' цель не уничтожена
…
'случайная
картинка цели
I = NRnd(0, 9)
Image1(ITarget).Picture = Image3(I).Picture
…
'размеры
Image1(ITarget).Width = 32 * Screen.TwipsPerPixelX
Image1(ITarget).Height = 32 * Screen.TwipsPerPixelY
…
В качестве картинок я выбрал
иконки старого доброго Windows (иногда так охота "расстрелять" его!).
Чтобы мишень была выше других
объектов на форме и в нее можно было стрельнуть, ее надо "поднять":
…
Image1(ITarget).Visible = True
Image1(ITarget).ZOrder
…
Ну и чтобы было веселее, сделаем
звук "рождения " мишени:
…
PW = sndPlaySound(SW(2), SND_ASYNC Or SND_NODEFAULT)
…
Для разнообразия управляем
интервалами между созданиями мишеней, чтобы это было неравномерно:
…
'меняем
интервал создания новой мишени от 0.1 до 1 сек
Timer2.Interval = NRnd(100, 1000)
…
Чтобы в игре одновременно
существовало не более 10 мишеней я поставил условие:
…
If
Image1.Count - 1 < 10 Then 'больше 10 одновременно нельзя
…
'Создание
новой мишени
…
End If
…
Уничтоженные мишени на самом деле
просто делаются невидимыми Visible=False
(см. ниже), поэтому как только появляется такая "вакансия", старая
убитая невидимая мишень становится новой видимой:
…
'используем
удаленную мишень для новой
For I =
1 To Image1.Count - 1 ' обрабатываем все картинки
If Image1(I).Visible = False Then ' цель не видна
ITarget = I
Exit For
End If
Next I
…
Вот все это хозяйство оформляем в
событии Timer2_Timer.
ДВИЖЕНИЕ МИШЕНИ
Я поставил интервал Timer1,
как мне кажется, на самый короткий промежуток времени Timer1.Interval=50.
Выше - медленно, ниже - система не успевает отработать.
В движении мишени ничего
особенного нет, ну разве что движение по окружности прикольное. Если хотите
добавьте сюда "тряску" мишени вокруг основной траектории с помощью
того же Rnd, но я посчитал, что это слишком.
…
Private Sub Timer1_Timer()
…
If FGamePlay = True Then 'игра идет
…
For I = 1 To Image1.Count - 1 ' обрабатываем все картинки
…
If FlagTargetTerm(I) = False Then ' цель не поражена
…
Select Case TypeTarget(I) ' тип движения цели
Case 0 ' не подвижен
…
Case 1 'горизонтальное без
смены направления
Image1(I).Left = Image1(I).Left + LeftStepTarget(I)
If Image1(I).Left > Me.ScaleWidth Then Image1(I).Left =
0
If Image1(I).Left < -Image1(I).Width Then Image1(I).Left
= Me.ScaleWidth
If Image1(I).Top >= Me.ScaleHeight - Image1(I).Height
Then Image1(I).Top = Me.ScaleHeight - Image1(I).Height
…
Case 7,
8 ' под любым углом со сменой направления
Image1(I).Left = Image1(I).Left + LeftStepTarget(I)
If Image1(I).Left > Me.ScaleWidth Then LeftStepTarget(I)
= -LeftStepTarget(I)
If Image1(I).Left < -Image1(I).Width Then
LeftStepTarget(I) = -LeftStepTarget(I)
Image1(I).Top = Image1(I).Top + TopStepTarget(I)
If Image1(I).Top > Me.ScaleHeight Then TopStepTarget(I)
= -TopStepTarget(I)
If Image1(I).Top < -Image1(I).Height Then
TopStepTarget(I) = -TopStepTarget(I)
Case 9 To 12 ' по окружности
AlfaTargetR(I) = AlfaTargetR(I) + AlfaStepTargetR(I)
Image1(I).Left = XrStepTargetR(I) + RStepTargetR(I) *
Cos(AlfaTargetR(I))
Image1(I).Top = YrStepTargetR(I) + RStepTargetR(I) *
Sin(AlfaTargetR(I))
End Select
…
End If
'-----------------------------------------------------
Next I
'-----------------------------------------------------
Else ' игра окончена
Timer1.Enabled = False
End If
End Sub
…
Это если цель не поражена, иначе
делаем "гибель цели", например, сначала уменьшаем ее, а затем делаем
невидимой:
…
Else 'цель поражена
If Image1(I).Visible = True Then
ImageWidth = Image1(I).Width - 5 * Screen.TwipsPerPixelX
ImageHeight = Image1(I).Height - 5 * Screen.TwipsPerPixelY
If ImageWidth < 0 Or ImageHeight < 0 Then
ImageWidth = 0
ImageHeight = 0
Image1(I).Visible = False
End If
Image1(I).Width = ImageWidth
Image1(I).Height = ImageHeight
End If
End If
…
Вот собственно и все: мишени
летают, а мы по ним стреляем.
Осталось за немногим:
ПРАВИЛА ИГРЫ
Для разнообразия я ввел три
варианта завершения игры FlagGameType
(именно поэтому не просто "Стрелок", а "Стрелок 50"):
0) после совершения игроком 50
выстрелов с момента начала игры (не важно были попадания или промахи);
1) после уничтожения 50 мишеней
(целей).
2) по истечении 50 секунд с
момента начала игры;
За выстрелами следят все объекты,
имеющиеся на форме во время игры, для формы выглядит это так:
…
Private Sub Form_MouseDown(Button As Integer, Shift As
Integer, X As Single, Y As Single)
…
'окончание игры
If FlagGameType = 0 And CurNShoots = MaxNShoots Then
MnuCancel1_Click
End If
…
End Sub
…
По аналогии не забываем про:
Image1_MouseDown – выстрел по мишени
Image4_MouseDown – выстрел по пулевому отверстию
Label1_MouseDown, Label2_MouseDown – выстрел по меткам с
информацией
Из всех выстрелов попаданиями
считаюся только Image1_MouseDown.
Когда вы сгенерируете это событие 50 раз, игра закончится по сценарию 1.
…
'---------------------------------------------------
'окончание игры
If FlagGameType = 1 And CurNTargetsTerm = MaxNTargetsTerm …
Then
MnuCancel1_Click
End If
End If
End If
'-----------------------------------------------------
…
За временем следит Timer3:
…
Private Sub Timer3_Timer()
If FGamePlay = True Then
CurNTimes = CurNTimes + 1
ITimes = ITimes + 1
…
If FlagGameType = 2 And CurNTimes = MaxNTimes Then
MnuCancel1_Click
Exit Sub
End If
…
Else ' игра окончена
Timer3.Enabled = False
End If
End Sub
…
Во всех трех случаях запускается
некая MnuCancel1_Click,
где и описывается завершение текущей игры:
…
Private Sub MnuCancel1_Click()
'-----------------------------------------------------
Dim PW
As Integer 'звучание звукового файла
'-----------------------------------------------------
FGamePlay = False
Me.MousePointer = vbDefault
…
End Sub
…
Как только FGamePlay = False , все таймеры выключаются, а
объекты не реагируют на щелчки мыши.
РЕЙТИНГ
Человек всегда сравнивает себя с
другими и с самим собой.
Формулы для расчета очков могут
быть какими угодно – это право автора. Немного подумав, я изобрел формулу
рейтинга игрока, учитывающую, как мне кажется, все основные факторы игры:
…
Rating = (RatCurNTargetsTerm - RatCurNMiss) / CurNTimes *
1000
…
Итак, рейтинг равен разности
между стоимостями попаданий и промахов поделенной на пройденное время (последующее
умножение рейтинга на 1000 введено мною для получения "удобочитаемых"
чисел Integer).
Для всех трех сценариев игры
формула в целом справедлива.
Стоимости попаданий и промахов я
придумал для учета размеров формы во время игры. Логика примерно такая: на
форме меньшего размера мишени расположены кучнее и попасть в них легче, чем на
форме большего размера, поэтому "стоимость" попадания на большой
форме должна быть выше:
…
RatCurNTargetsTerm = RatCurNTargetsTerm+1 * ScaleKoef
'стоимость попадания
…
аналогично с промахом:
…
RatCurNMiss = RatCurNMiss + 1 * ScaleKoef
'стоимость попадания
…
Непосредственно масштабный коэффициент
я рассчитал после нескольких десятков "битв" на формах с разными
размерами. Его определение помещено в событии Form_Resize:
…
ScaleKoef = ((Me.Width ^ 2 + Me.Height ^ 2) ^ 0.5 /
(MinMeWidth ^ 2 + MinMeHeight ^ 2) ^ 0.5) ^ (1 / 4)
…
В каждом событии, которое
приводит к изменению рейтинга, запускается обновление информации для игрока:
…
Private Sub UpdateLabel()
If CurNTimes > 0 Then Rating = (RatCurNTargetsTerm - RatCurNMiss)
/ CurNTimes * ScaleKoef * 1000
Label2(0).Caption = CurNShoots
Label2(1).Caption = CurNTargetsTerm
Label2(2).Caption = CurNMiss
Label2(3).Caption = CurNTimes
Label2(4).Caption = Rating
End Sub
…
По завершении каждой игры
обновляется таблица лидеров:
…
Private Sub UpdateLiders()
…
Dim I As Integer
Dim OldLiderRat As Integer
Dim OldLiderName As String
'---------------------------------------------------
For I = 10 To 1 Step -1
'---------------------------------------------------
If Rating > LiderRat(I) Then 'если рез больше следующего
CurPosition = I
OldLiderName = LiderName(I)
OldLiderRat = LiderRat(I)
LiderName(I) = CurName
LiderRat(I) = Rating
'---------------------------------------------------
If I < 10 Then
LiderName(I + 1) = OldLiderName
LiderRat(I + 1) = OldLiderRat
End If
'---------------------------------------------------
Else
'если результат меньше следующего
Exit
For
End If
'---------------------------------------------------
Next I
'---------------------------------------------------
For I = 1 To 10
Text1(I).Text = LiderName(I)
Label4(I).Caption = LiderRat(I)
'---------------------------------------------------
If I = CurPosition Then 'красим в цвета
Text1(I).BackColor = vbGreen
Label4(I).BackColor = vbGreen
Label3(I).BackColor = vbGreen
Text1(I).Locked = False
Else
Text1(I).BackColor = vbWhite
Label4(I).BackColor = vbWhite
Label3(I).BackColor = vbWhite
Text1(I).Locked = True
End If
'---------------------------------------------------
Next I
'---------------------------------------------------
End Sub
…
Таким образом мы рассмотрели
основные функции программы в режиме "Игра".
СТАНДАРТНАЯ ПРОГРАММА
Функции в этом режиме FGamePlay = False присущи практически всем
программам.
Обратите внимание на возможность
паузы во время игры. Чтобы вызватьь ее при нажатии кнопки Esc
я использовал кнопку Command1, пряча ее при загрузке формы за ее пределы. Также
пауза срабатывает при сворачивании формы Form_Resize,
через 10 мин бездействия Timer3.timer
и по правой кнопке мыши:
…
If Button = 2 Then 'правая кнопка
If FGamePlay = False Then
Me.PopupMenu mnuGame 'всплывающее меню
Else
Command1_Click 'пауза
End If
End If
…
Два слова о работе с INI-файлом.
Конечно, приведенный в программе
вариант далеко не лучший, но он простой понятный и работает, к тому же я
стараюсь минимально привлекать в программу dll‑библиотеки, но это, как
говорится, мои проблемы.
Основная идея: читаем праметры
через OpenINI, если файла нет запускаем параметры по
умолчанию DefaultINI, приводим их в действие в LoadINI, сохраняем параметры в
SaveINI перед выходом из программы.
Я использую такой вариант:
…
Private Sub Form_Unload(Cancel As Integer)
…
SaveINI
…
For I = Forms.Count - 1 To 0 Step -1
Unload
Forms(I) 'выгружаются все формы программы
Next I
End Sub
…
С недавних пор в свои программы я
всегда включаю процедуру:
…
Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode
As Integer)
Unload
Me
End Sub
…
Она позволяет корректно завершить
работу программы (удаляются временные файлы и т.п.) при жестком закрытии
программы, например, при выходе из текущего сеанса Windows.
Мне очень нравится решение одного
из программистов (стыдно - не помню имени) по контролю минимально допустимой
ширины и высоты формы при изменении ее размеров:
…
Private Sub Form_Resize()
…
If Me.Width < MinMeWidth Then
Me.Enabled = False
Me.Width = MinMeWidth
Me.Enabled = True
End If
If Me.Height < MinMeHeight Then
Me.Enabled = False
Me.Height = MinMeHeight
Me.Enabled = True
End If
…
End Sub
…
Функция "Поверх остальных
окон" стала стандартной для современных программ.
…
'-----------------------------------------------------
'поверх
остальных окон
'-----------------------------------------------------
Private Sub MnuGameForvard_Click()
If MnuGameForvard.Checked = False Then
MnuGameForvard.Checked = True
Else
MnuGameForvard.Checked = False
End If
MeForvard
End Sub
Private Sub MeForvard()
If MnuGameForvard.Checked = True Then
SetWindowPos Me.hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE
Or SWP_NOSIZE
Else
SetWindowPos Me.hWnd, HWND_NOTOPMOST, 0, 0, 0, 0,
SWP_NOMOVE Or SWP_NOSIZE
End If
End Sub
'-----------------------------------------------------
'поверх
остальных окон
'-----------------------------------------------------
…
И не забудте в Module1.bas:
…
'------------------------------------------
'всегда
впереди
'------------------------------------------
Public Const HWND_TOPMOST = -1
Public Const HWND_NOTOPMOST = -2
Public Const SWP_NOACTIVATE = &H10
Public Const SWP_SHOWWINDOW = &H40
Public Const SWP_NOMOVE = &H2
Public Const SWP_NOSIZE = &H1
Declare Sub SetWindowPos Lib "user32" (ByVal hWnd
As Long, ByVal hWndInsertAfter As Long, ByVal X As Long, ByVal Y As Long, ByVal
cx As Long, ByVal cy As Long, ByVal wFlags As Long)
'------------------------------------------
'всегда
впереди
'------------------------------------------
…
Практически у всех моих программ
используется одна и та же форма "О программе" Form7. Так сказать "на поток" поставил.
Не забудьте и для этого окна
менять режим "Поверх остальных окон", а то оно появится сзади
основной формы и тяжело Вам будет до нее добраться.
Мир VB огромен и необъятен,
поэтому закругляюсь.
ЭПИЛОГ
Неделя, в течение которой я
разрабатывал эту игру, пролетела как одно мгновение. Это не передаваемое
состояние творчества, когда ты просыпаешься в 3 часа ночи и скорее запускаешь
компьютер, чтобы воплотить в программу мелькнувшую в голове мысль.
Совершенствуйте себя и свои
программы!
Именно для этого я написал эту
статью.
С уважением,
Половый
Александр
Декабрь
2002 г.