Пишем свою игру : Эльфостроение : Форум


 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

Прикрепленный к сообщению файл:

snake.zip snake.zip (9.95 kb; 39 hits) Скачать файл

 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 snake.zip (10.83 kb; 14 hits) Скачать файл
snake.elf snake.elf (10.13 kb; 35 hits) Скачать файл

 mmcorp:
21.09.10, 04:39
 Оптимизация/улучшения/исправления



Закрытие книги при нажатии кнопки
Итак продолжим улучшать игру. В этом сообщении я буду добавлять/исправлять что-то в игре, и описывать. Чтобы добавить выход по долгом нажатию на "С" нужно:
изменить base_page в CMyBook.c

было: 

стало: 
Модифицировать метод onKey в CGameDisp
тык: 



Появление "Еды" только в пустых местах
Чтобы добавить проверку на занятость данного места поля добавляем метод bool in(int x, int y) в объект snake (snake.h) и описываем его (snake.c):
тык: 
добавляем точно такой же метод в food (food.h) и опишем (food.c)
тык: 
теперь чуть модифицируем метод OnMove объекта CGame
тык: 



Добавление ускорения
добавляем ускорение на джойстик/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
тык: 


исходники последней версии тут или прикреплены к сообщению

Прикрепленный к сообщению файл:

snake.zip snake.zip (11.37 kb; 24 hits) Скачать файл

URL этой темы:
https://mobilefree.justdanpo.ru/newbb_plus/viewtopic.php?topic_id=5074

© 2005-2018 supertrubka.org