Пишем свою игру : Эльфостроение : Форум |
mmcorp: |
20.09.10, 06:23 |
Пишем свою игру: Введение В данной статье я бы хотел показать программирование/разработку игр на эльфах с помощью классов на примере всем известной игры "Snake", эта статья не для изучения использования различных функций в библиотеке. Сообщения в этой теме будут добавляться по мере написания, готовые исходники игры вы можете скачать в аттаче (исходники последней версии вы можете скачать тут), и разобраться самостоятельно если не хотите ждать продолжения. Для прочтения данной статьи вам нужно как минимум: прочитать: http://ru.wikipedia.org/wiki/Парадигма_программирования http://ru.wikipedia.org/wiki/Объектно-ориентированное_программирование другие уроки в этой теме знать азы Си/Си++ просмотреть и что-то там понять: http://perk11.info/svn/SE/classlib/cl.cpp http://perk11.info/svn/SE/classlib/cl.h Для того, чтобы начать программировать, нам нужно выделить обособленные друг от друга элементы игры, подумать как и где их выводить, а также примерно представить: как они будут взаимодействовать между собой и с пользователем. В нашем примере я выделил 2 объекта: 1) Змейка (я ее представил в виде направленного набора квадратиков (частей,фрагментов), которые будут в процессе движения чередовать свои координаты от 1-ых к последним, также к ней могут добавляться новые части) 2) Еда (квадратик, который будет появляться на некоторое время вне занятых участках поля и который змейка может съесть) Разработка Класс "Змейка" Итак, начнем описание объекта "Змейка", для этого нужно выделить все ее свойства, а также методы для задания/изменения этих свойств. Код: class snake { }; Подумаем, что из себя представляет часть змейки. Я ее представил в виде набора координат (x;y), то есть чтобы описать фрагмент змейки мы можем воспользоваться типом записи (структурой) XY: тык: Хранить координаты всех частей, на мой взгляд лучше в динамическом листе->добавляем поле LIST*list в класс Код: class snake { LIST*list; }; Змейка может двигаться в разных направлениях->добавляем поле char _way для хранения ее направления Также змейкой можно управлять (т.е. изменять ее свойство _way), для этого добавляем метод void setway(char way), Код: class snake { LIST*list; char _way; public: void setway(char way); }; директива public означает, что Продолжим: Змейка движется->добавляем метод void move() Змейке "важно знать до каких пор ей можно ползти"->добавляем свойства int _mx,_my для хранения максимальных координат К змейке могут добавляться новые части->добавляем метод void add(int x,int y) Змейку нужно вывести на экран->добавляем метод void out() (его мы оставим пустым, так как пока мы еще не решили как будем все это выводить) Змейка не может пересечь сама себя->добавляем метод bool check() для проверки общих координат в фрагментах Во время игры нам понадобятся координаты головы и хвоста, чтобы фиксировать когда еда будет съедена и когда добавиться в конец->добавляем методы XY*getlast(); XY*getfirst(); само собой нам нужно как то задать начальные свойства змейки: координаты головы, ее направление, кол-во частей, максимальные координаты. Для этого добавим конструктор с перечисленными выше параметрами, ну а раз есть конструктор, то должен быть и деструктор у нас должно получиться что-то наподобие этого: тык: итак, сразу определимся, что в свойстве _way 0 означает вверх 1 - вправо 2 - вниз 3 - влево начинаем описывать методы (подробно комментировать я их не буду, для чтения этой статьи, вы уже сами должны представлять алгоритмы для описания нужного вами действия) тык: (c) MoneyMasteR aka mmcorp |
mmcorp: |
20.09.10, 07:13 |
Класс "Еда" Опишем цвета с помощью перечесления тык: Приступим к описанию объекта "Еда" Код: class food { }; У "Еды" должны быть координаты->добавляем поля int _x,_y; "Еда" существыет не вечно->добавляем поле int timeleft; "Еда" может быть съедена, а может и нет->добавляем поле bool state; ну и прикрутим цвет для еды->добавляем поле COLOR _col; В процессе игры нам придется воспользоваться свойствами: _x,_y,timeleft,state,_col - поэтому добавляем методы: int getx(); int gety(); bool getstate(); int gettime(); int getcol(); Время, которое "Еда" существует должно уменьшаться->добавляем метод void timedown(int dtime); "Еду" может скушать "Змейка"->добавляем метод void eated(); Также нужен конструктор, который определит свойства объекта food(int x,int y,COLOR col);, добавим также деструктор ~food();. Он будет пустым И метод вывода void out(); Получается вот это: тык: Описываем методы: понадобятся два файла для компиляции: http://perk11.info/svn/SE/tictactoe/rand.c http://perk11.info/svn/SE/tictactoe/rand.h тык: (с) MoneyMasteR aka mmcorp |
mmcorp: |
20.09.10, 11:05 |
Класс "Дисплей" Теперь пришло время подумать, а как выводить наши объекты. Мне хочется максимально приблизить вид игры к тетрису. Для этого я сделаю класс экран тетриса, через который мы будем выводить, что нам потребуется. Итак, дисплей состоит из пикселей, поэтому сначала нам нужно описать его. У пикселя должен быть свой цвет, а также статус вкл/выкл: тык: Для того, чтобы наш дисплей мог быть любого размера, я размещу пиксели в динамической памяти, сделав что-то наподобие двумерного массива (на самом деле указатель на массив указателей на массив пикселей) Поэтому добавляем поле PXL**_display; Так же нам надо хранить размеры экрана, а также размер пикселя в экранных пикселях телефона->добавляем поля: int _x,_y; int _pixel; Также я хочу хранить изображения 8 пикселей разного цвета->добавляем поле GC*gc_pixel[8]; Для того, чтобы вкл/выкл пиксель и установить цвет добавляем метод void set(int x, int y, COLOR col,bool state); добавляем метод void clear(); , чтобы отключить все пиксели также нам нужно выводить свой дисплей на нужный нам GC -> void out(GC*gc); добавляем конструктор для установки размеров, а также инициализации изображения 8 пикселей ну и деструктор, чтобы подчистить за собой. Я бы также добавил функцию получения цвета по его номеру int getColor(COLOR col); Должно получиться: тык: Описываем методы: тык: Теперь опишим наши методы out в классах snake и food Для этого меняем параметры этого метода, должно получиться: тык: Описываем метод out для класса snake: тык: Описываем метод out для класса food: тык: (с) MoneyMasteR aka mmcorp |
mmcorp: |
20.09.10, 21:37 |
Класс "Игра" Опишем сам класс игры (CGame) итак в игре учавствуют: "Змейка", динамический лист с "Едой", дисплей. выделим некоторые состояния игры: "Конец игры", "Пауза" - а также методы возвращающие их значения. bool pause; bool isgameover; bool IsGameOver(); bool IsPause(); еще нужно добавить метод устанавливающий паузу void SetPause(); Игре нужно обновляться через нужное ей время для этого добавляем поле и метод возвращающий его значение int time; int GetRefreshTime(); В играх можно набирать очки int score; int GetScore(); На игру влияют нажатия клавиш void OnKey(int key,int mode); Игру нужно обновлять void OnMove(); Игру нужно выводить на экран void OnDraw(GC*gc); Добавляем конструктор для инициализации игры и деструктор для подчищения за собой. Должно получиться: тык: Описываем методы: тык: (c) MoneyMasteR aka mmcorp |
mmcorp: |
20.09.10, 22:09 |
Делаем свой DispObj И так мы создали объект игры, но как пользователь будет взаимодействовать с ним? Ответ - через DispObj. В cl.h уже есть описанный класс CDispObjT, мы создадим свой объект на основе этого, переопределив нужные нам методы, а именно: onDraw, onKey, onRefresh, onDestroy, onCreate, getName Добавить поля CGame*game и bool isondraw получаем: тык: Описываем методы: тык: (c) MoneyMasteR aka mmcorp |
mmcorp: |
20.09.10, 23:15 |
Создаем нашу книгу Опишем нашу книгу (CMyBook), она будет наследовать все свои свойства от класса CBook в cl.h Добавим страницу книги base_page, и методы для нее static int ShowAuthorInfo(CBookBase**bm_book,CMyBook*mbk); static int TerminateElf(CBookBase**bm_book,CMyBook*mbk); Добавляем CGuiBase*gui; Добавляем конструктор и деструктор тык: Описываем наши методы и пагу тык: ну и опишем функцию int main(); Код: int main() { new CMyBook(); return 0; }; Первые результаты итак у меня получилось 16 файлов в папке с проектом: CGame.c CGame.h CGameDisp.c CGameDisp.h CMyBook.c CMyBook.h display.c display.h food.c food.h gametypes.h main.c rand.c rand.h snake.c snake.h в проект также надо включить файл cl.cpp. Все лежит в аттаче этого поста. если проект не компилируется, попробуйте убрать строчку #include "..\deleaker\mem2.h" из cl.cpp Эльф готов. На разработку эльфа ушло от силы 2 часа, на урок весь день Ну и на последок хотел бы вам дать что-то наподобие задания: 1). Исправить баг типа: змейка идет вверх, я жму влево а потом вниз и происходит конец игры 2). Добавить ускорение на джойстик/5 3). Добавить вывод очков 4). Изменить появление еды так, чтобы она не могла появляться на змейке и на другой еде этих недочетов нет в исходниках из первого поста) можете посмотреть как там все это реализовано Вопросы/предложение/выполнение задания можете оставлять в этой теме Удачи в программировании! (с) MoneyMasteR aka mmcorp Прикрепленный к сообщению файл: snake.zip (10.83 kb; 14 hits) Скачать файл snake.elf (10.13 kb; 35 hits) Скачать файл |
mmcorp: |
21.09.10, 04:39 |
Оптимизация/улучшения/исправления Закрытие книги при нажатии кнопки Итак продолжим улучшать игру. В этом сообщении я буду добавлять/исправлять что-то в игре, и описывать. Чтобы добавить выход по долгом нажатию на "С" нужно: изменить base_page в CMyBook.c было: стало: тык: Появление "Еды" только в пустых местах Чтобы добавить проверку на занятость данного места поля добавляем метод bool in(int x, int y) в объект snake (snake.h) и описываем его (snake.c): тык: тык: тык: Добавление ускорения добавляем ускорение на джойстик/5 Для этого добавляем поле bool isfast в CGame, модифицируем методы GetRefreshTime,OnKey и конструктор объекта CGame: тык: Исправление бага с направлением "Змейки" Исправляем баг: MoneyMasteR писал: 1). Исправить баг типа: змейка идет вверх, я жму влево а потом вниз и происходит конец игры И так, это происходит из-за того, что за одно движение направление змейки может меняться несколько раз, надо сделать так, чтобы направление менялось только один раз, перед самим движением, для этого нам потребуется в класс snake добавить поле char _nextway куда будем заносить следующее направление, 0xFF будет значить, что нового направления не было. Менять _way на _nextway мы будем в методе move() объекта snake перед перестановкой координат, так же в конструктор нужно добавить начальное значение _nextway, и переделать метод setway(): тык: Добавляем вывод очков на экран Для вывода очков я хочу использовать стандартные изображения цифорок из прошивки телефона: MIDI_COMP_BL_NBR_0_ICN MIDI_COMP_BL_NBR_1_ICN MIDI_COMP_BL_NBR_2_ICN MIDI_COMP_BL_NBR_3_ICN MIDI_COMP_BL_NBR_4_ICN MIDI_COMP_BL_NBR_5_ICN MIDI_COMP_BL_NBR_6_ICN MIDI_COMP_BL_NBR_7_ICN MIDI_COMP_BL_NBR_8_ICN MIDI_COMP_BL_NBR_9_ICN Добавляем в объект CGameDisp: 1) поле wchar_t numimage[10] здесь будем хранить ид этих изображений 2) методы: void DrawNumeral(GC*gc,int &x,int &y,int numeral);//выводит цифру void DrawNumber(GC*gc,int x,int y,int number);//выводит число 3) изменяем метод onCreate, чтобы инициализировать поле numimage 4) изменяем метод onDraw тык: исходники последней версии тут или прикреплены к сообщению |
URL этой темы: https://mobilefree.justdanpo.ru/newbb_plus/viewtopic.php?topic_id=5074 © 2005-2018 supertrubka.org |