Один список
Блин, что-то мне не нравится. И не нравится вот что. При добавлении нового типа в игру. Надо не только его описать, но и создать список для хранения объектов этого типа. А потом еще крутить вертеть все эти списки по очереди, чтобы всё проапдейтить и нарисовать. Вот было бы здорово иметь ОДИН список, для объектов всех типов. А? Потом просто разок пройтись по этому списку в цикле и всё, красотаааа. И в BlitzMaxе есть такая возможность! Нам поможет механизм наследования. Так, что это вы сразу отворачиваетесь и краснеете? Кто не вкурил руководство от Wave, кто думал что обойдется без этого? Кто у нас слабое звено? Ладно, попытаюсь объяснить. Но начнем по порядку. Стираем на фиг, все списки для хранения тайлов, бонусов и платформ. И создаем один :
Global ALLGameObjList : TList = CreateList()
Вот так, гордо и пафосно, я назвал новый список, в котором будут содержаться все объекты. Соответственно в каждой функции создания нового объекта Create() надо добавлять созданный объект уже в этот список.
ListAddLast(ALLGameObjList , Tobj)
Сделали? Теперь все объекты будут в одном списке! А в циклах отрисовки бонусов и платформ, замените прошлые листы на наш супер-мега-пупер-лист. Для бонуса, это будет например так:
For Local CurBonus:TBonus = EachIn ALLGameObjList
CurBonus.Update() CurBonus.Draw() Next
Компилим: клево работает!!! Блин, а чего мы выиграли-то? 2 строчки кода (((. Хоть и список у нас теперь один, а так и осталось - для каждого типа свой цикл. Думаем дальше: Вот если бы все объекты были одного типа, то было бы достаточно прокрутить с ним список один раз. То есть сделать один единственный цикл! И нам в этом поможет наследство. Плотнее товарищи, не вижу улыбок на лицах. Наследование - это один из важнейших принципов ООП. И кстати самый благозвучный, остальные без пол-литра не выговоришь. Давайте посмотрим по внимательнее на наши типы, а точнее на их свойства (Field). У них всех есть одинаковые параметры: x, y, width, height. Предлагаю сделать еще один тип - базовый или ,если угодно, родительский. Куда мы их и вынесем :
Type TParent
Field x:int Field y:int Field width:int Field height:int End type
А для чего я это сделал? Представьте, как было бы здорово не писать каждый раз эти одинаковые параметры в новый тип. А взять их из отдельного места. Из другого типа. Или другими словами <унаследовать> их. Я не зря назвал тип <TParent>, то есть <родитель>. Ведь типы, которые от него наследуют параметры, будут его детьми. Это как одна дружная семья типов. И как это сделать, да очень просто. Вот, к примеру, как ваше имя - отчество? Ну, к примеру, зовут вас Иван Сергеевич. Это значит что ваше имя - Ваня, а имя вашего отца (родителя) - Сергей. Вы не поверите, но в Блице это ни чуть не сложнее чем в жизни:
Type MyName Extends FatherName 'Type Иван Extends Сергеевич
То есть пишется сначала имя наследника и через Extends имя родителя. И это означает, что вы, то есть ваш тип, теперь обладает теми же параметрами, что и родитель. Тут надо заметить, что параметры имеют только одинаковые названия, но данные в разных типах не одинаковы. Как говорят в Одессе, это две большие разницы. Ну, теория теорией, а я предпочитаю практику. Закомментируем во всех наших типах поля <x>, <y>, <width> и <height>. И давайте первым делом <усыновим> тип TTile, как вы уже догадались, это будет выглядеть так:
Type TTile Extends TParent
На примере этого типа <усыновите> и остальные(TBonus и TMovingX). Компильте скорее! Все идет. А для чего мы все это делали? Нет, конечно, хорошо, что мы вынесли общие для наших типов параметры в отдельный тип. Но не это самое главное, а то, что теперь все наши типы имеют одного предка, то есть это <дети> одного отца. И имя ему - <TParent>. Ну что, уловили полет мысли? Ага, мы будем крутить цикл с базовым типом, вот так:
For Local CurObj:TBase = EachIn ALLGameObjList
CurObj.Update() CurObj.Draw() Next
А уж он будет обращаться, ко всем своим родственникам в нашем супер-пупер-мега-списке. То есть он пройдет и по TTile и по TBonus и по TMovingX - объектам, если вы их, конечно, усыновили. Компилим:и : опс, не работает. А что пишет-то? Мол, нет у типа TParent метода Update(). Да согласен - нет. У этого типа вообще нет ни одного метода. Мы хотим, чтобы методы Update() и Draw() вызывались не базового типа, а его наследников, так если CurObj - это Bonus, то и вызывать надо Bonus.Update() и так далее. Аналогично дело обстоит и с методом Draw(). Мы-то знаем, что хотим, но BlitzMax не знает. Для того? чтобы он это понял, надо все-таки объявить эти два метода в нашем базовом типе, НО не писать никакого кода, а сделать их АБСТРАКТНЫМИ или по-другому виртуальными:
Method Update() Abstract
Method Draw() Abstract
Как раз это ключевое слово (Abstract) и подсказывает BlitzMax-у, чтобы он обращался не к методам предка, а к методам его производных типов. Это называется полиморфизм, второй из важнейших признаков ООП. Цитата: <Полиморфизм реализуется с помощью наследования классов (в BlitzMaxe - типов). Класс-потомок наследует сигнатуры методов класса-родителя, но реализация этих методов может быть другой, соответствующей специфике класса-потомка. Это называется переопределением метода.>(Wikipedia). То есть в зависимости от того, к какому типу принадлежит текущий объект, метод такого типа и вызывается - это как раз то, чего мы хотели. Ну, надеюсь, вы поняли. А если нет, то курите, други мои, руководство от Wave, а я пошел пивка попью. Ах, да, теперь все, компилим и радуемся. Вот исходник:
SuperStrict
Global ScrWidth : Int = 640 Global ScrHeight : Int = 480
'Global TileList:TList = CreateList() 'Global BonusList:TList = CreateList() 'Global MovingXList:TList = CreateList()
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 x:Int ' Field y:Int ' Field Width:Byte ' Field Height:Byte 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,y,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 CurTile:TTile = EachIn ALLGameObjList ' CurTile.Update() ' CurTile.Draw() ' Next ' ' For Local CurBonus:TBonus = EachIn ALLGameObjList ' CurBonus.Update() ' CurBonus.Draw() ' Next ' ' For Local CurMovingX:TMovingX = EachIn ALLGameObjList ' CurMovingX.Update() ' CurMovingX.Draw() ' Next For Local CurObj:TBase = EachIn ALLGameObjList CurObj.Update() CurObj.Draw() Next End Method End Type
Type TBonus Extends TBase ' Field x:Int ' Field y:Int ' Field width:Int ' Field height:Int 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,y,Width,Height) End Method End Type
Type TMovingX Extends TBase ' Field x:Int ' Field y:Int ' Field width:Int ' Field height:Int 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 , y , Width , Height) SetColor(255 , 255 , 255) ' white line DrawLine(x , y , x + width - 1 , y) 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
MyLevel.Render() Flip Until KeyDown(KEY_ESCAPE) Or AppTerminate()
End
Так, на протяжении этой главы, мы создавали себе трудности и сами же их героически преодолевали, в этом и заключается программирование.
Выведение
Прошу строго не судить, ведь это моя первая статья. В следующей статье (если она будет) мы поместим на наш уровень героя, которого научим бегать, прыгать, стрелять, запрыгивать и ездить на платформах, а так же собирать бонусы. Все пожелания, а также сообщения об опечатках и багах в коде, пишите мне на почтовый ящик или в комменты.
|