Журнал о программированнии на языках Blitz3D, BlitzPlus, BlitzMax

Создаём свой Crimsonland на BlitzMax

Материал из Blitz Et Cetera

Перейти к: навигация, поиск

Содержание

Инициализация мира

Вначале давайте подумаем, о чем будет наша игра. Сделаем давайте мы безумную экшн-мясо-кровища-шутер. По экрану будет бегать чел. Чел этот будет из автомата или из дробовика крошить монстров. Будут декалитры крови. Круто? ;)

Ну, начнем писать код. Создадим новый файл - file => new. Создали? сохраним его в папку Crimsonland2 (предварительно создав ее ;) ).

Для начала нужно задать разрешение экрана. Хранить значения мы будет в константах height, width. Для теста возьмем 1024х768. Пишем:

'ЧАСТЬ 1. Инициализация мира.


'======== КОНСТАНТЫ
Const width=1024 , height=768 'Константы расширения
'======== УСТАНОВКА ИГРЫ

Graphics width,height,32    'Ставим настройки.

Все. Теперь игра уже представляет собой что-то. Круто? думаю ,пока нет. ;)

Загрузка медиа

Так. для начала сохраните у себя на компе эти картинки:

Под названием 1.jpg и переместим в папку игры : CrimsonLand2/media/1.jpg . Это у нас будет текстура земли в игре.

Под названием 1.png и переместим в папку игры : CrimsonLand2/media/1.png . Главный герой.

Под названием blood.png и переместим в папку игры : CrimsonLand2/media/blood.png . Кровища для игры.

Под названием bulletPistol.png и переместим в папку игры : CrimsonLand2/media/bulletPistol.png. Пуля.

Под названием button.png и переместим в папку игры : CrimsonLand2/media/Button/button.png. Кнопка.

Под названием buttondown.png и переместим в папку игры : CrimsonLand2/media/Button/buttondown.png. Когда наводим указатель на кнопку.

Под названием buttonexit.png и переместим в папку игры : CrimsonLand2/media/Button/buttonexit.png.

Под названием buttonexitdown.png и переместим в папку игры : CrimsonLand2/media/Button/buttonexitdown.png. Когда наводим указатель на кнопку.

Под названием monster.png и переместим в папку игры : CrimsonLand2/media/monster/monster.png .

Так, все медиа мы вроде загрузили. Сейчас напишем код ,который бы все это загружал...

Global Fonimage = LoadImage ("Media/1.jpg")   'Загружаем текстуру для игры.


Global BulletImage = LoadImage ("Media/bulletPistol.png")    'Загружаем картинку пули.
MidHandleImage BulletImage 'Координаты картинки монстра по центру.

Global PlayerImage = LoadAnimImage ("Media/1.png",68,40,0,8) 'Загружаем картинку для главного игрока.
MidHandleImage PlayerImage 'Делаем координаты картинки по центру.

Global MonsterImage = LoadAnimImage ("Media/Monster/1.png",100,50,0,20) 'Загрузка картинки монстра.
MidHandleImage MonsterImage 'Координаты картинки монстра по центру.

Global BloodImage = LoadImage ("Media/Blood.png")
MidHandleImage BloodImage

Global Button = LoadImage ("Media/Button/button.png")   'Кнопка для начала новый игры.
Global ButtonDown = LoadImage ("Media/Button/buttondown.png")
MidHandleImage Button
MidHandleImage ButtonDown


Global ButtonExit =  LoadImage ("Media/Button/buttonexit.png")   'Кнопка для начала новый игры.
Global ButtonExitDown = LoadImage ("Media/Button/buttonexitdown.png")
MidHandleImage ButtonExit
MidHandleImage ButtonExitDown


Global SMonster:TList=New TList
Global SPlayer:TList=New TList 'Листы для объектов.
Global SBullet:TList=New TList

Global SBlood:TList=New TList
  • Global - объявляет новую переменную.
  • LoadImage - загружает картинку.
  • MidHandleImage - координаты картинки по центру.
  • TList - листы, списки, в которых будут содержатся данные об наших объектах.

Загрузили в нашу игру мы медиа. Что дальше?

Создания типов, переменных, функций, необходимых для работы игры

А дальше у нас идет создание необходимого.

Global TipGame 'Для того, чтобы можно было понять, что сейчас происходит. 0- меню. 1- игра.

Global Level = 1000 'Уровень
Global time

Global a

Объявляем переменные.

'======== ДЛЯ УПРАВЛЕНИЯ СПИСКАМИ  

Function UpdateEntities( list:TList )
        For Local entity:TEntity=EachIn list
                entity.Update
        Next
End Function

'======== ТИП ДЛЯ РАБОТЫ С ДРУГИМИ ТИПАМИ
'Чтобы можно было работать с другими типами, необходим этот...
Type TEntity
        Field link:TLink
        Method remove()
                link.remove
                Sila:-1
        End Method
        Method AddLast( list:TList )
                link=list.AddLast( Self )
        End Method
        Method Update() Abstract

End Type

Можно сказать, функция для обновления объектов в списке, а тип для работы функции... вроде...

Function Dn_DrawImage (image, x#, y#, rotat#,level# ) 'Рисуем картинку через функцию.

        SetBlend Alphablend
        SetRotation rotat
        SetAlpha level
        DrawImage image, x, y
End Function

Function Dn_DrawAnimImage (image, x#, y#, rotat#,level#, f ) 'Рисуем картинку через функцию.
        SetBlend Alphablend
        SetRotation rotat
        SetAlpha level
        DrawImage image, x, y, f

End Function

Две функции для отрисовки изображений.

  • Dn_DrawImage - для обычных картинок.
    • image
    • x - положение
    • y - положение
    • rotat - поворот картинки
    • level - прозрачность.
  • Dn_DrawAnimImage - для анимационных картинок.
    • f - номер кадра.


'======== ОПРЕДЕЛИТЬ УГОЛ МЕЖДУ ТОЧКАМИ    

Function Angle!(x0!,y0!,x1!,y1!)
        Return ATan2(y1-y0,x1-x0)

End Function

Функция для определения угла между двумя точками.

Создания типов для игры

В этой части мы сделаем все необходимые типы для игры:

  1. Герой
  2. Монстр
  3. Пуля
  4. Кровь

Так. Приступим сразу к коду:

Type TPlayer Extends TEntity 'Объявляем тип.

Field X#,Y#                                'Положение игрока.
Field Life                               'Жизнь.
Field Ang                                'Поворот.
Field frame , animnext                   'Для анимации.
Field Weapon                             'Какое оружие. 0 - автомат. 1 - дробовик
Field Shot                               'Когда был произведен предыдузий выстрел.   
       
        Method Update() 'Цикл, где будут проходить все действия для...
   ' пока здесь ничего не пишем, но сделаем метку
        End Method
       
       
       
        Function CreatePlayer:TPlayer( ) 'Функция для создания игрока.
        Local Player:TPlayer=New TPlayer 'Создаем игрока.
        Player.x = width*0.5 'Делаем, чтобы положение игрока было точно в центре.
        Player.y = height*0.5
        Player.life = 100    '100 жизней для начала.
        Player.AddLast SPlayer 'Добавляем объект в список.
        Return Player 'Возвращаем объект.
        End Function
       

End Type

Type - объявляет тип. Extends - "привязка". В нашем случае к типу TEntity. Помните, мы такой создали? Field - знаения для типа. Function CreatePlayer:TPlayer( ) - Функция для создания объекта - игрока. TPlayer означает, к какому типу относится функция.

Так же у нас для монстра, пули и крови. Только у поля значения другие и названия. )

Для того, чтобы создать игрока, достаточно ввести TPlayer.CreatePlayer (), но мы это напишем потом.

Type TMonster Extends TEntity 'Объявляем тип.

Field X#,Y#                                'Положение объекта.
Field Life                               'Жизнь.
Field Ang                                'Поворот.
Field frame , animnext                                        'Для анимации.

        Method Update() 'Цикл, где будут проходить все действия для монстра.
   ' пока здесь ничего не пишем, но сделаем метку
        End Method

        Function CreateMonster:Tmonster(x#,y#) 'Функция для создания монстра.
        Local monster:TMonster=New TMonster 'Создаем игрока.
        Monster.x = x
        Monster.y = y
        Monster.life = 100    '100 жизней для начала.
        Monster.AddLast SMonster 'Добавляем объект в список
        Return Monster 'Возвращаем объект.
        End Function
       

End Type

Чтобы создать монстра, надо написать:

TMonster.CreateMonster(х, у)

Х, У - положение монстра. Если мы хотим, чтобы у монстров разные жизни были, или еще что-нибудь, то нужно заместо:

Function CreateMonster:Tmonster(x#,y#)
Monster.life = 100

писать

Function CreateMonster:Tmonster(x#,y#, life)
Monster.life = life

То есть при создании нового монстра мы будем указывать еще и его количество жизней, но в моем туториале мы не будем делать этого. )))

Type TBullet Extends TEntity 'Объявляем тип.

Field X#,Y#                              'Положение пули.
Field Ang                                'Поворот.
Field speed
                             
        Method Update() 'Цикл, где будут проходить все действия для пули.
   ' пока здесь ничего не пишем, но сделаем метку
        End Method

        Function CreateBullet:TBullet(x#,y#,ang#, speed) 'Функция для создания пули.
        Local Bullet:TBullet=New TBullet 'Создаем игрока.
        Bullet.x# = x#
        Bullet.y# = y#
        Bullet.ang = ang
        Bullet.speed = speed
        Bullet.AddLast SBullet 'Добавляем объект в список
        Return Bullet 'Возвращаем объект.
        End Function
       

End Type

Создаем пулю. Чтобы создать, нужно написать:

TBullet.CreateBullet (x,y,поворот пули, скорость в px)

Правда, делать мы это будем в методе обновлении игрока.

Type TBlood Extends TEntity 'Объявляем тип.

Field X#,Y#                                'Положение пули.
Field Ang                                'Поворот.
Field Alpha#                             'Прозрачность крови.
Field AlphaM#                            'Как быстро будет кровь становится прозрачнее

        Method Update() 'Цикл, где будут проходить все действия для крови.
   ' пока здесь ничего не пишем, но сделаем метку
        Function CreateBlood:TBlood(x#,y#,m#) 'Функция для создания крови.
        Local Blood:TBlood=New TBlood 'Создаем игрока.
        Blood.x = x
        Blood.y = y
        Blood.alpham = m
        Blood.alpha = 1
        Blood.ang = Rand (360)
        Blood.AddLast SBlood 'Добавляем объект в список
        Return Blood 'Возвращаем объект.
        End Function
       

End Type

Типы все похожи ,не правда ли? То же самое и с кровью. Только мы добавили несколько новых значений. Значение alpham, alpha. Ang у нас будет каждый раз разное. Чтобы кровь была не похоже друг на друга, каждый раз рисуем ее в случайном повороте.

Alpha - при зарисовке будет указываться, на сколько прозрачной будет кровь. Так же будет как "счетчик". Постепенно это значение будет уменьшатся на alpham, и когда alpha будет меньше или равна 0 ,то удаляем кровь.

Хе. Вы почти создали основу , "каркас" вашей игры. Осталось не так уж и много...

Правда посмотреть пока не на что...

Создание обновления для объектов

Без обновления объектов у нас игра просто не будет работать. Так давай те же напишем его! ;)

Главный герой

'Движение игрока.

        If KeyDown(Key_W) Then Y = Y - 3 ; If animnext < MilliSecs () Then animnext=MilliSecs()+50 ; frame:+1
        If KeyDown(Key_S) Then Y = Y + 3 ; If animnext < MilliSecs () Then animnext=MilliSecs()+50 ; frame:+1
        If KeyDown(Key_A) Then X = X - 3 ; If animnext < MilliSecs () Then animnext=MilliSecs()+50 ; frame:+1
        If KeyDown(Key_D) Then X = X + 3 ; If animnext < MilliSecs () Then animnext=MilliSecs()+50 ; frame:+1
       
        Ang = Angle(x,y,MouseX(),MouseY())'Определяем поворот игрока.
       
        If frame=8 Then frame=0
        Dn_DrawAnimImage (playerImage, x, y, ang,1,frame )
       
        If shot < MilliSecs () And MouseDown (1) Then
       
        If weapon = 0 Then shot = MilliSecs () +  50  ; TBullet.CreateBullet (x,y,ang+Rand(-6,6),30)
        If weapon = 1 Then shot = MilliSecs () +  500 ; For i = 0 To 10 ; TBullet.CreateBullet (x,y,ang+Rand(-40,40),15) ; Next
        End If
       
        If KeyHit (key_f1) Then weapon=0
        If KeyHit (key_f2) Then weapon=1
       
        SetRotation 0

        DrawText life ,0 , 0

Так. найдите в коде обновление игрока / type TPlayer => Method Update ()

Там мы оставляли метку. Убираем ее и пишем вышенаписанное .)))

If KeyDown(Key_W) Then Y = Y - 3 ; If animnext < MilliSecs () Then animnext=MilliSecs()+50 ; frame:+1

        If KeyDown(Key_S) Then Y = Y + 3 ; If animnext < MilliSecs () Then animnext=MilliSecs()+50 ; frame:+1
        If KeyDown(Key_A) Then X = X - 3 ; If animnext < MilliSecs () Then animnext=MilliSecs()+50 ; frame:+1

        If KeyDown(Key_D) Then X = X + 3 ; If animnext < MilliSecs () Then animnext=MilliSecs()+50 ; frame:+1

Перемещение главного игрока. То есть при нажатие на клавишу (W,A,S,D) , у нас изменяются координаты. И так же при нажатии на клавигу у нас происходит смена кадров.

X=X+3, где 3 - скорость. Изменить, если надо)

If animnext < MilliSecs () Then animnext=MilliSecs()+50 ; frame:+1 Если animnext < с.времени , то animnext = с.время+50мс , кадр = +1

Каждые 50 мс увеличиваем кадр на 1. А всего у нас кадров 8. => если покажется 9 кадр, несуществующий, будет ошибка. Ставим ограничение:

If frame=8 Then frame=0

(если кадр = 8 , то кадр = 0)

Примечание: отсчет кадров начинается с нуля => 8 кадр - несуществующий.

Dn_DrawAnimImage (playerImage, x, y, ang,1,frame )

Рисуем анимационную картинку - playerImage . В положении х игрока, у игрока, с поворотом ang. С прозрачностью 1 (непрозрачный) и кадром frame.

Ang = Angle(x,y,MouseX(),MouseY())'Определяем поворот игрока.

Определяем через нашу функцию поворот между точкой один (главный герой) и точкой два (курсор)

If shot < MilliSecs () And MouseDown (1) Then

       
        If weapon = 0 Then shot = MilliSecs () +  50  ; TBullet.CreateBullet (x,y,ang+Rand(-6,6),30)
        If weapon = 1 Then shot = MilliSecs () +  500 ; For i = 0 To 10 ; TBullet.CreateBullet (x,y,ang+Rand(-40,40),15) ; Next

        End If

Если shot меньше с.времени и нажата левая кнопка мыши, то:

  • если оружие 0 (автомат), то увеличиваем shot, и создаем пулю.
  • если оружие 1 (дробовик), то увеличиваем shot, и много (11) пуль создаем пулю.

11 потому-что начинается счет с 0 и идет до 10 - 11.

Счетчик

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

If KeyHit (key_f1) Then weapon=0
        If KeyHit (key_f2) Then weapon=1

Если нажимаем клавишу F1, то достаем автомат, или переменная weapon = 0

Если нажимаем клавишу F2, то достаем дробовик, или переменная weapon = 1

SetRotation 0
        DrawText life ,0 , 0

Рисуем жизни. )

Так. Главного героя сделали. Все. перерыв. Идите выпейте чего-нибудь, отдохните)))

Отдохнули? приступаем к монстрам.

Монстры

Method Update() 'Цикл, где будут проходить все действия для монстра.

        If life < 0 Then remove ; Return;       If Level>300 Then Level:-5
       
        For Player:TPlayer = EachIn SPlayer
                If x > player.x - 30 And x < player.x + 30 And y > player.y - 30 And y < player.y + 30 Then
                       
                        player.life:-1
                        TBlood.CreateBlood (player.x,player.y,0.01)
                End If         
        Ang = Angle(x,y,player.x,player.y)'Определяем поворот монстра
        Next

        x=x+(3.5*Cos(ang))
        y=y+(3.5*Sin(ang))

       
        If animnext < MilliSecs () Then animnext=MilliSecs()+50 ; frame:+1
        If frame=20 Then frame=0
        Dn_DrawAnimImage (monsterimage, x, y, ang,1,frame )
       

        End Method

Весь код на монстров.

If life < 0 Then If Level>300 Then Level:-5; remove ; Return

Если жизни меньше 0 ,то если у нас скорость появления монстров (Level) больше 300, о уменьшаем ее. (на деле же чем меньше скорость, тем чаще они появляются.) Удалям объект (через TEntity, по этому не подствечивается). Возвращаем все.

For Player:TPlayer = EachIn SPlayer

                If x > player.x - 30 And x < player.x + 30 And y > player.y - 30 And y < player.y + 30 Then
                       
                        player.life:-1
                        TBlood.CreateBlood (player.x,player.y,0.01)

        End If

Смотрим на всех героев главных (а их он у нас один, но можно сделать и больше, только как быть с управлением?)

Если наш монстр близко к герою :

If x > player.x - 30 And x < player.x + 30 And y > player.y - 30 And y < player.y + 30 Then

То :

player.life:-1
TBlood.CreateBlood (player.x,player.y,0.01)

Уменьшаем жизни героя и создаем кровь в позиции героя.

сейчас немного объясню, почему так пишем...

Смотрите, мы в методе игрока писали просто x , а здесь player.x, так? а х у нас это координата монстра. Дело в том, что если если мы используем значения объекта прямо в его типе, то писать player. и тому подобное не надо. А если мы вызываем значения объекта из другого типа (из монстра вызываем героя), то надо писать player. и тому подобное...

x=x+(3.5*Cos(ang))
        y=y+(3.5*Sin(ang))

Перемещаем монстра относительно его поворота. 3.5 - скорость его. Если надо, можно увеличить или уменьшить...

If animnext < MilliSecs () Then animnext=MilliSecs()+50 ; frame:+1

If frame=20 Then frame=0

Dn_DrawAnimImage (monsterimage, x, y, ang,1,frame )

Для анимации... думаю, уже не надо расписывать)

Пули

Method Update() 'Цикл, где будут проходить все действия для пули.

        'Если пуля вылетает за пределы карты - удаляем.
        If x<-100 Or x>=width+100 Or y>=height Or y<=-100  Then
        remove
        Return
        EndIf
       
        For Monster:TMonster = EachIn SMonster
                If x > monster.x - 25 And x < monster.x + 25 And y > monster.y - 25 And y < monster.y + 25 Then 
                        monster.life:-20
                        TBlood.CreateBlood (x,y,0.001)
                        remove
                        Return 
                End If         
        Next
       
       
        x=x+(speed*Cos(ang))
        y=y+(speed*Sin(ang))
       


        Dn_DrawImage bulletImage, x, y, ang,1

        End Method


If x<-100 Or x>=width+100 Or y>=height Or y<=-100  Then

remove
Return

EndIf

Если пуля вылетает за пределы экрана - удаляем.

For Monster:TMonster = EachIn SMonster

                If x > monster.x - 25 And x < monster.x + 25 And y > monster.y - 25 And y < monster.y + 25 Then 
                        monster.life:-20
                        TBlood.CreateBlood (x,y,0.001)
                        remove
                        Return 
                End If         

        Next

Обновляем монстров. Если пуля близко к монстру ,то удаляем пулю, добавляем крови.

x=x+(speed*Cos(ang))
y=y+(speed*Sin(ang))

Перемещаем пулю.

Dn_DrawImage bulletImage, x, y, ang,1

Рисуем.

Кровь

Method Update() 'Цикл, где будут проходить все действия для крови.

        If alpha < 0 Then  REmove ; Return
        alpha = alpha - alpham
        Dn_DrawImage (BloodImage, x, y, ang,alpha )

        End Method

Постепенно делаем кровь прозрачней. (уменьшаем alpha на alpham) Если прозрачность = 0 (т.е. совсем невидим), то удаляем

Так. можете себя поздравить. Вы полностью сделали каркас вашей игры.

Создание меню

Ни одна игра не обходится без меню. Так давайте же создадим меню и в нашей игре.

While  TipGame <> 1 'Цикл для меню. Пока переменная не равна 1, будет меню.

Cls

Dn_DrawImage (button, width*0.5, height*0.5, 0,1 )
Dn_DrawImage (buttonexit, width*0.5, height*0.5+50, 0,1 )

If MouseX()> (width*0.5)-((ImageWidth(Button))*0.5) And MouseX()< (width*0.5)+((ImageWidth(Button))*0.5) And MouseY()> (height*0.5)-((ImageHeight(Button))*0.5) And MouseY()< (height*0.5)+((ImageHeight(Button))*0.5) Then
'Вот такой мегааццкий код для коллизии кнопка - курсор.  
'(width*0.5)-((ImageWidth(Button))*0.5) - ширину * 0.5  - ширину кнопки * 0.5  -- определяем начальную х кнопки.
'(width*0.5)+((ImageWidth(Button))*0.5) - ширину * 0.5  + ширину кнопки * 0.5  -- определяем конечную х кнопки.
'и так же для высоты кнопки ;)
Dn_DrawImage (buttondown, width*0.5, height*0.5, 0,1 )
End If

If MouseX()> (width*0.5)-((ImageWidth(Button))*0.5) And MouseX()< (width*0.5)+((ImageWidth(Button))*0.5) And MouseY()> (height*0.5)-((ImageHeight(Button))*0.5) And MouseY()< (height*0.5)+((ImageHeight(Button))*0.5)And MouseDown (1) Then TipGame = 1

If MouseX()> (width*0.5)-((ImageWidth(Button))*0.5) And MouseX()< (width*0.5)+((ImageWidth(Button))*0.5) And MouseY()> (height*0.5)-((ImageHeight(Button))*0.5)+50 And MouseY()< (height*0.5)+((ImageHeight(Button))*0.5)+50 Then
Dn_DrawImage (buttonexitdown, width*0.5, height*0.5+50, 0,1 )
End If

If MouseX()> (width*0.5)-((ImageWidth(Button))*0.5) And MouseX()< (width*0.5)+((ImageWidth(Button))*0.5) And MouseY()> (height*0.5)-((ImageHeight(Button))*0.5)+50 And MouseY()< (height*0.5)+((ImageHeight(Button))*0.5)+50 And MouseDown (1) Then End

Flip

Wend

Тут все просто. Рисуем две кнопки. Если курсор находится в зоне кнопки, то рисуем поверх нее другую. Если нажимаем на курсор и курсор находится над кнопкой, то делаем то-то. В нашем случае изменяем переменную TipGame 1.

А цикл меню заверщается, если переменная становится не равна 0. то есть если мы нажимаем на "новая игра", то цикл меню завершается и продолжается выполнение кода...

Кстати, cls - очищает экран, flip - все рисует.

Цикл для игры

Так. Подошли мы к концу.

TPlayer.CreatePlayer ()


While Not KeyDown (KEy_Escape)
Cls
       
        TileImage Fonimage , 0 , 0

        UpdateEntities Sblood
        UpdateEntities Sbullet
        UpdateEntities Splayer
        UpdateEntities Smonster

       
        If time < MilliSecs () Then
        time = MilliSecs () + level
        a = Rand (1,4)
        Select a
        Case 1
        TMonster.CreateMonster(-50, Rand(height))
        Case 2
        TMonster.CreateMonster (width+50, Rand(height))
        Case 3
        TMonster.CreateMonster (Rand(width) , -50)
        Case 4
        TMonster.CreateMonster (Rand(width), height+50)
        End Select
        End If
Flip

Wend
TPlayer.CreatePlayer () - что это такое? ;)
TileImage Fonimage , 0 , 0 - Рисуем фон игры.
UpdateEntities Sblood

        UpdateEntities Sbullet
        UpdateEntities Splayer

        UpdateEntities Smonster

Обновляем все объекты. UpdateEntities - наша функция, она была в самом начале.

If time < MilliSecs () Then

        time = MilliSecs () + level
        a = Rand (1,4)
        Select a
        Case 1
        TMonster.CreateMonster(-50, Rand(height))
        Case 2
        TMonster.CreateMonster (width+50, Rand(height))
        Case 3
        TMonster.CreateMonster (Rand(width) , -50)
        Case 4
        TMonster.CreateMonster (Rand(width), height+50)
        End Select

        End If

Так. Если time меньше системного времени, то присваиваем переменной a случайное значение от 1 до 4.

  • Если равно 1 , то создаем монстра слева экрана.
  • Если 2, то справа.
  • Если 3, то вверху экрана.
  • Если 4, то снизу.

Все! нажимаем F5! Работает? если нет - код в студию ,будем выяснять. А так, все готово! Вы можете поздравить себя с созданием игры основы Crimsonland. Конечно, все аспекты типа уровней, спецэффетов и тому подобного описывать я не буду, но вы уже сделали основу. Уже легче.


Автор: DanFi (ICQ: 468-909-567)

Другие

Друзья