Тайловая игра на BlitzMax, часть 2 (3) Зумминг
Видели движок Матвея к игре про злобных колобков? Просто офигительный эффект приближения и удаления камеры, не правда ли? Признаюсь, меня он очень впечатлил, и я захотел сделать такой же. Сейчас мы с вами займемся его реализацией. Но для начала, уже по традиции, я вам расскажу какие функции в Бмаксе отвечают за увеличение/уменьшение масштаба. SetScale(Xscale,Yscale) – это установка коэффициента масштаба, и GetScale(Xscale, Yscale) - чтение текущего коэффициента масштаба. Как вы могли уже заметить, у этих функций 2 параметра. Первый отвечает за масштабирование по оси Х, а второй по оси У. Это значит, что мы можем масштабировать объект непропорционально, то есть без соблюдения пропорций, на каждую ось - свой коффициент. Эти коэффициенты имеют тип float, то есть - это вещественные числа. При коэффициенте 1.0 не будет ни увеличения, ни уменьшения масштаба, при 2.0 будет увеличение в 2 раза, при 0.5 уменьшение в 2 раза (0.5 = ½). Проведем еще один маленький тестик. Как говорится “лучше 1 раз увидеть, чем 100 раз услышать” : SuperStrict
SuperStrict
'========== Параметры графики =========== Global ScrWidth:Int = 640 Global ScrHeight:Int = 480 '===================================
AppTitle = "Test 004"
' инициализация графики Graphics(ScrWidth, ScrHeight)
Cls() SetScale(1.0 , 1.0)'один к одному SetColor(0,0,255) DrawRect(50,140,100,100)
SetScale(0.5 , 0.5)' в два раза меньше SetColor(255,0,0) DrawRect(250,140,100,100)
SetScale(2.0 , 2.0)' в два раза больше SetColor(0,255,0) DrawRect(400,140,100,100)
Flip() WaitKey()
End
Как видите (по коду программы), все три квадрата имеют одинаковые размеры - 100 на 100. Но в связи с тем, что перед их отрисовкой стоят функции масштабирования(SetScale), они выводятся разного размера. Один (красный) в 2 раза меньше – 0.5, а другой (зеленый) в 2 раза больше 2.0. Поэкспериментируйте с кодом, задавая разный масштаб квадратов для каждой оси.
Ну теперь все вроде понятно с зумингом, нам надо просто создать переменную масштаба Zoom:Float , которая будет хранить текущий масштаб карты, и прицепить изменение этой переменной на кнопки. Вот так:
If( KeyDown(KEY_A) ) Then Zoom :+ 0.01 If( KeyDown(KEY_Z) ) Then Zoom :- 0.01
а перед отрисовкой всех объектов впиндюрим SetScale( Zoom , Zoom ). Да, это все так. Но не забывайте, что при введении масштаба, мы вводим как бы третье измерение для нашей карты, а это надо учитывать. Ведь чем меньше масштаб карты тем плотнее тайлы друг к другу, соответственно их координаты подвержены влиянию коэффициента зума. Проходим по всем функциям отрисовки объектов и домножаем x и y координаты на наш коэффициент масштаба, вот так: x * Zoom, y * Zoom
Весь листинг программы здесь:
SuperStrict
'========== Параметры графики =========== Global ScrWidth:Int = 640 Global ScrHeight:Int = 480 '===================================
Global CameraX:Int = 0 Global CameraY:Int = 0 Global Zoom:Float = 1.0
Global ALLGameObjList : TList = CreateList()
Const TILESIZE:Int = 32
Type TBase Field x:Int Field y:Int Field Width:Byte Field Height:Byte Method update() Abstract Method draw() Abstract End Type
Type TTile Extends TBase Field xTile:Int Field yTile:Int Field Walkable:Byte Function Create(sx:Int, sy:Int, WB:Byte) Local TT:TTile = New TTile TT.width = TiLESIZE TT.height = TiLESIZE TT.xTile = sx TT.yTile = sy TT.x = TT.xTile * TT.width TT.y = TT.yTile * TT.height TT.Walkable = WB
ListAddLast(ALLGameObjList , TT) End Function
Method update() If walkable 'not walkable SetColor(255 , 0 , 0) 'red Else ' walkable SetColor(0 , 255 , 0) 'green End If End Method
Method Draw() DrawRect(x * Zoom, y * Zoom, Width, Height) End Method
End Type
Type TLevel Field Width : Byte Field Height : Byte
Field Map:Int[,] Function Create:TLevel(MyMap:Int[],map_width:Int,map_height:Int) Local TM : TLevel = New TLevel TM.Width = map_width TM.Height = map_height
TM.map = New Int [TM.Width, TM.Height] TM.Load(myMap) Return TM End Function Method Load(arrMap:Int[]) For Local i:Int = 0 Until Height For Local j:Int = 0 Until Width Map[j , i] = arrMap[j + (i * Width)] If Map[j , i] TTile.Create(j , i , True) Else TTile.Create(j , i , False) End If Next Next End Method
Method Render() For Local CurObj:TBase = EachIn ALLGameObjList CurObj.Update() CurObj.Draw() Next End Method End Type
Type TBonus Extends TBase
Field points:Int Field miny:Int Field maxy:Int Field diry:Int Field speed:Int Field AnimDelay:Int Function Create(sx:Int,sy:Int,pts:Int) Local TB : TBonus = New TBonus TB.Width = 6 TB.Height = 16 TB.x = sx * TiLESIZE + ((TiLESIZE - TB.width) / 2) 'center it TB.y = sy * TiLESIZE + ((TiLESIZE - TB.height) / 2) 'center it TB.points = pts TB.miny = TB.y - 4 TB.maxy = TB.y + TB.Height + 4 TB.diry = -1 TB.speed = 1 TB.AnimDelay = 5 ListAddLast(ALLGameObjList , TB) End Function Method Update() If(Animdelay < 0)
If ( miny >= (y) Or maxy <= (y + height) ) diry = -diry End If y = y + speed * diry AnimDelay = 5
End If AnimDelay :- 1 End Method Method Draw() SetColor(255 , 255 , 0) DrawRect(x * Zoom, y * Zoom, Width, Height) End Method End Type
Type TMovingX Extends TBase Field mindir:Int Field maxdir:Int
Field dirx:Int Field speed:Int Function Create(xt:Int , yt:Int , mn:Int , mx:Int , d:Int) Local TMX:TMovingX = New TMovingX
TMX.x = xt * TILESIZE TMX.y = yt * TILESIZE
TMX.width = TILESIZE TMX.height = TILESIZE
TMX.speed = 1
TMX.dirx = d
TMX.mindir = (mn + xt) * TILESIZE TMX.maxdir = (mx + xt + 1) * TILESIZE
ListAddLast(ALLGameObjList , TMX) End Function
Method Update() If( mindir >= x Or maxdir <= (x + width) ) dirx = - dirx EndIf x :+ speed * dirx End Method Method Draw() SetColor(128 , 128 , 128) ' gray DrawRect(x * Zoom, y * Zoom, Width, Height) SetColor(255 , 255 , 255) ' white line DrawLine(x * Zoom, y * Zoom, (x * Zoom) + width - 1, y * Zoom) End Method
End Type Global LevMap:Int[] = [1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , .. 1 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 1 , 1 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 0 , 0 , 1 , .. 1 , 1 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 1 , .. 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 1 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , .. 1 , 0 , 1 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , .. 1 , 0 , 0 , 0 , 0 , 0 , 1 , 1 , 0 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , .. 1 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , .. 1 , 0 , 1 , 1 , 1 , 1 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 1 , 1 , 1 , .. 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 1 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 0 , 1 , .. 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1]
Global MyLevel : TLevel = New TLevel.Create(LevMap , 20 , 10)
Function Init_All_Bonuses() TBonus.Create(1 , 1 , 25) TBonus.Create(3 , 8 , 25) TBonus.Create(4 , 8 , 25) TBonus.Create(5 , 8 , 25) TBonus.Create(15 , 1 , 100) TBonus.Create(7 , 4 , 100) TBonus.Create(9 , 7 , 10) TBonus.Create(4 , 6 , 10) TBonus.Create(9 , 2 , 10) TBonus.Create(14 , 7 , 10) TBonus.Create(17 , 6 , 10) TBonus.Create(17 , 4 , 10) TBonus.Create(18 , 4 , 10) TBonus.Create(3 , 3 , 10) End Function
TMovingX.Create(13 , 3 , - 2 , 5 , 1)
'----------------------------- main loop ----------------------------- Graphics(ScrWidth, ScrHeight)
Init_All_Bonuses()
Repeat Cls
If( KeyDown(KEY_LEFT) ) Then CameraX :- 5 If( KeyDown(KEY_RIGHT) ) Then CameraX :+ 5 If( KeyDown(KEY_UP) ) Then CameraY :- 5 If( KeyDown(KEY_DOWN) ) Then CameraY :+ 5
If( KeyDown(KEY_A) ) Then Zoom :+ 0.01 If( KeyDown(KEY_Z) ) Then Zoom :- 0.01
SetOrigin( -CameraX , -CameraY ) SetScale( Zoom , Zoom ) MyLevel.Render() Flip Until KeyDown(KEY_ESCAPE) Or AppTerminate()
End
Здорово! Кстати заметьте, что как только коэффициент масштаба станет отрицательным, то карта снова начнет увеличиваться, но уже будет перевернута верхом вниз и правая сторона поменяется с левой. Это такая фича функции SetScale(). Если вы загрузите картинку и перед ее отображением напишите SetScale(-1.0 , 1.0) , то картинка нарисуется отраженной (инвертированной) по оси Х, если напишем SetScale(1.0 , -1.0) , то отразится по У. В нашем же случае, карта отразится сразу по обеим осям, так как мы изменяем их пропорционально и синхронно.
Выведение
Ну вот в общем-то и все на сегодня. Вы теперь знаете, что такое ViewPort и как им манипулировать в своих целях. Знаете как осуществляется скроллинг и зумминг карты. На основе этих данных вы смело можете писать уже что-то очень похожее на игру. Надеюсь я не сильно загрузил вашу голову, и в ней еще есть место для следующих туторов. ;) А ваше домашнее задание - создать карту побольше нашей, которую мы использовали для экспериментов, и ввести лимит на скроллинг и зуминг карты по ее размерам. Удачи! У вас все получится – я уверен.
Спасибо вам за внимание. И как всегда, обо всех ошибках в тексте и коде, сообщайте мне на почтовый ящик. Комменты тоже приветствуются. Чао.
Источник: http://blitzetc.blitzmax.ru/index.php/Тайловая_игра_на_BlitzMax%2C_часть_2 |