Форум программистов, компьютерный форум CyberForum.ru

Игра Арканоид - C++

Восстановить пароль Регистрация
 
obivan
Падаван С++
 Аватар для obivan
172 / 158 / 41
Регистрация: 11.11.2014
Сообщений: 590
Завершенные тесты: 1
06.10.2015, 13:26     Игра Арканоид #1
Добрый день дорогие кибер форучане, сделал попытку написать игру арканоид, вроде вот и получилось, но вот незадача, выйглядит это как то недобросовестно и ужасно со взгляда ООП, я создал подобную тему в разделе OpenGl, но не наказывайте меня модераторы, все вопросы там относяться по большей мере к графической части, а от вас хотелось бы услышать критику в стороны реализации на ООП, что улучшить, что переделать или вообще снести к чертям этот код и все заново, но уже с более четкими задачами. Оставляю код здесь на расмотрение, накидал пару коментов, они из разряда капитан очевидность, но может будут кому то нужны, жду ваших сообщений
Кликните здесь для просмотра всего текста
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
#include <GL\glew.h>
#include <GL\freeglut.h>
#include <iostream>
 
class Object { // класс обьект, являеться классом родителем для классов елементы и мячь, его свойтса это координаты центра любого обьекта
private:
    int x;
    int y;
public:
    Object() : x(0), y(0){}
    Object(int x, int y) : x(x), y(y){}
 
    const int getX(void) const { return x; }
    const int getY(void) const { return y; }
    void setX(const int x) { this->x = x; }
    void setY(const int y) { this->y = y; }
    void setXY(int x, int y) { setX(x); setY(y); }
};
class Elems : public Object { // класс элементы
private:
    Object coords[4]; // координаты вержин блоков
public:
    Elems() : Object() {
        setCoords();
    }
    Elems(int x, int y) : Object(x, y){
        setCoords();
    }
 
    // набо get`ов для координат
    const int getLeftBX(void) const { return coords[0].getX(); }//левая часть координаты низа
    const int getLeftBY(void) const { return coords[0].getY(); }
    const int getLeftTX(void) const { return coords[3].getX(); }//левая часть координаты верха
    const int getLeftTY(void) const { return coords[3].getY(); }
 
    const int getRightBX(void) const { return coords[1].getX(); }//правая часть координаты низа
    const int getRightBY(void) const { return coords[1].getY(); }
    const int getRightTX(void) const { return coords[2].getX(); }//правая часть координаты верха
    const int getRightTY(void) const { return coords[2].getY(); }
 
    void setCoords(void) {//устанавливаем координаты относительно центра
        int x = getX(), y = getY();
        coords[0].setXY(x - 25, y - 5);
        coords[1].setXY(x + 25, y - 5);
        coords[2].setXY(x + 25, y + 5);
        coords[3].setXY(x - 25, y + 5);
    }
    void draw(int Red, int Green, int Blue) { // функция вывода
        setCoords();
        glColor3d(Red, Green, Blue);
        glBegin(GL_POLYGON);
        glVertex2d(coords[0].getX(), coords[0].getY());
        glVertex2d(coords[1].getX(), coords[1].getY());
        glVertex2d(coords[2].getX(), coords[2].getY());
        glVertex2d(coords[3].getX(), coords[3].getY());
        glEnd();
    }
};
class Ball : public Object {//класс мячик
private :
    unsigned int Radius;
public:
    Ball() : Object(), Radius(5) {}
    Ball(int x, int y) : Object(x, y), Radius(5) {}
    const unsigned int getRadius(void) const { return Radius; }
    void setRadius(const unsigned int Radius) { this->Radius = Radius; }
 
    void draw(int Red, int Green, int Blue) {// функция отрисовки мяча, вот тут возникают некоторые проблемы 
        glColor3d(Red, Green, Blue);
        glBegin(GL_POINTS);//с отрисовкой закрашеного мяча, пока что сделал только контур, проблемы возникают после того как я уменьшил
        int BallX, BallY, x = getX() , y = getY();//координатное соотнощение в самом начале делал для окна 600, 480 отношение по Х -300 до 300
        for (float angle = 0.3; angle < 6.4; angle += 0.1) {//по У -240 до 240 но потом решил уменьшить масшатб координатной сетки в 2 раза
            BallX = Radius * cosf(angle);// и из за этого не получаеться нормально нарисовать закрашеный мячь
            BallY = Radius * sinf(angle);//но это не така большая проблемма тк хочу загрузить текстуру
            glVertex2d(BallX + x, BallY + y);
        }
        glEnd();
    }
};
class Field {// класс поле являеться неким классом обработчиком
private:
    unsigned int Count;//кол-во блоков
    bool BallState;// состояние мяча (в движении или в начальной точке)
    const int Speed = 3;//скорость, но используеться коряво
    int velocity[2];//вектор для направления движения мяча
    //по поводу вектора: если он 1, 1 то двигаемся в правых верхний угол, если -1, 1 то в левый верхний
    // 1, - 1 в правый нижний, -1, -1 в левый нижний
 
    Elems ** Blocks;//блоки
    Elems Player;
    Ball Ball;
 
    void createField(void) {//создаем  поле из блоков,
        Count = 16;
        Blocks = new Elems*[Count];
 
        for (int i(0), x = -105, y = 100; i < Count; i++, x += 70) {
            Blocks[i] = new Elems();
            Blocks[i]->setXY(x, y);
            if (i % 4 == 3) { x -= 280; y -= 25; }
        }
    }
public:
 
    Field() : Player(0, -100), Ball(Player.getX(), Player.getY() + 10), BallState(false) {//конструктор с нач значениями
        createField();//у мяча координаты как и у игрока в начал с сдвигом на 10 по Y
    }
 
    const int getPlayerX(void) const { return Player.getX(); }//центральный X блока игронка
    const int getVelocityX(void) const { return velocity[0]; }//считываем вектор
    const int getVelocityY(void) const { return velocity[0]; }
 
    void setBallState(bool BallState) { this->BallState = BallState; }//задаем состояние мяча
    void setVelocityX(int x) { velocity[0] = x; }//задаем вектор
    void setVelocityY(int y) { velocity[1] = y; }
 
    void draw(void) {// рисуем поле
        for (int i(0); i < Count; i++)
            Blocks[i]->draw(255, 255, 0);
        Player.draw(255, 0, 0);
        Ball.draw(0, 0, 255);
    }
    void PlayerMove(unsigned char key) {//обработчик перемещения, из keyboard передаеться значение
        if (key == char(100))//если a двигаемся в лево
            Player.setX(Player.getX() + 5);
        else if (key == char(97))//если d вправо
            Player.setX(Player.getX() - 5);
        if (BallState == false) Ball.setX(Player.getX());// если мяч не запушен то следует за нами
    }
    void Dell(int i) {// импровизированая функция удаления, лучше не придумал, были проблемы с освобождением памяти поэтому пока так
        for (int j(i); j < Count; j++) {
            Blocks[j] = Blocks[j + 1];
        }
        Count--;
    }
    void BallTick(void) {//функция перемещения мяча
        for (int j(0); j < Speed; j++) { // цикл по Speed(для более плавной и быстрой анимации, как я думал ...)
            int BallX = Ball.getX(), BallY = Ball.getY(), BallRadius = Ball.getRadius();// запомнили поля мяча
            Ball.setX(BallX + velocity[0]);//перемещение по вектору
            Ball.setY(BallY + velocity[1]);
 
            //проверяем стенки окна на столкновение
            if (BallX < -150) velocity[0] = 1;//левая
            else if (BallX > 150) velocity[0] = -1;//правая
            if (BallY > 120) velocity[1] = -1;//верх
            else if (BallY < -120) velocity[1] = 1;//низ, при попадании в низ желательно сделать проигрыш но пока так
            //условие попеды по уничтожыным блокам тоже пока что отсутвует 
 
            //проверка на отражение с блоком игрока, решил сделтать только по верхней части блока, потому что друкое не имеет смысла
            if (BallY == Player.getY() + 2 && BallX > Player.getLeftTX() - BallRadius && BallX < Player.getRightTX() + BallRadius)
                velocity[1] = 1;
 
            for (int i(0); i < Count; i++) {//перебор всех блоков
                if (Blocks[i]->getLeftBY() == BallY && BallX > Blocks[i]->getLeftBX() - 2 && BallX < Blocks[i]->getRightBX() + 2) {
                    Dell(i); velocity[1] = -1;//проверяем низ блоков
                    break;
                }else if (Blocks[i]->getLeftTY() == BallY && BallX > Blocks[i]->getLeftTX() - 2 && BallX < Blocks[i]->getRightTX() + 2) {
                    Dell(i); velocity[1] = 1;//верх блоков
                    break;
                }
 
                if (Blocks[i]->getLeftBX() == BallX && BallY > Blocks[i]->getLeftBY() + 1 && BallY < Blocks[i]->getLeftTY() - 1) {
                    Dell(i); velocity[0] = -1;//левую грань
                    break;
                }
                else if (Blocks[i]->getRightBX() == BallX && BallY > Blocks[i]->getRightBY() + 1 && BallY < Blocks[i]->getRightTY() - 1) {
                    Dell(i); velocity[0] = 1;//правую грань
                    break;
                }
            }
        }
    }
}Field;
 
void Display(void) { // функция дисплея
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    Field.draw();
    Field.BallTick();
    glFlush();
}
void keyboard(unsigned char key, int a, int b) {
    switch (key) {// клавиатура, управление на кнопках a, d, чтобы выстрелить мячиком нажмите 1
    case 100:
        if (Field.getPlayerX() < 125)
            Field.PlayerMove(key);
        break;
    case 97:
        if (Field.getPlayerX() > -125)
            Field.PlayerMove(key);
        break;
    case 49:
        Field.setBallState(true);
        if (Field.getPlayerX() >= 0){ Field.setVelocityX(1); Field.setVelocityY(1); }
        else { Field.setVelocityX(-1); Field.setVelocityY(1); }
    break;
    }
}
void Timer(int = 0) { // функция таймер
    Display();
    glutTimerFunc(50, Timer, 0);
}
int main(int argc, char* argv[]) { //в мейне делаем базовые настройки opengl
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA | GLUT_DEPTH);
    glutInitWindowSize(600, 480);
    glutCreateWindow("Game");
    gluOrtho2D(-150, 150, -120, 120);
    glutDisplayFunc(Display);
 
    GLenum err = glewInit();
    if (GLEW_OK != err) {
        fprintf(stderr, "GLEW error");
        return 1;
    }
    glutKeyboardFunc(keyboard);
    glutTimerFunc(50, Timer, 0);
    glutMainLoop();
    return 0;
}
Лучшие ответы (1)
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
06.10.2015, 13:26     Игра Арканоид
Посмотрите здесь:

C++ Арканоид
Delphi Арканоид
C++ Арканоид
Delphi Игра Арканоид - как создать кубики на форме
C# Игра "Арканоид". Сообщения пользователям
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
UltraPenguin
222 / 88 / 22
Регистрация: 20.03.2014
Сообщений: 296
Завершенные тесты: 1
06.10.2015, 14:43     Игра Арканоид #2
Эх) Выкидывать не надо) Но переделать можно многое. Пока редактирую можете подумать на темы:
1. коллизии имен переменных и типов;
2. преимущества передачи параметров по ссылке/константной ссылке;
3. полиморфизм и его применимость в контексте вашей задачи.

Добавлено через 1 минуту
4. согласованность именования переменных, типов и функций в расках проекта.
obivan
Падаван С++
 Аватар для obivan
172 / 158 / 41
Регистрация: 11.11.2014
Сообщений: 590
Завершенные тесты: 1
06.10.2015, 15:11  [ТС]     Игра Арканоид #3
UltraPenguin, Выкидывать в плане код ?))) Если да то я за ним не гонюсь может кому поможет )))
Коллизия имен переменных и типов, я понял о чем вы к примеру есть класс Ball и переменная с таким же именем собственно как и Field, это понял учтем, по ссылке приимущество в том что не создаеться копия обьекта а работаем с исходным, но я новичек скажем так делал чтобы хотя бы чтото зароботало, по поводо полиморфизма его я думаю можно отнести к методу draw ? Но тут другой вопрос теперь мне сказали на форуме по opengl если мы привязываем вызов opengl в классе то это по сути уже и не ооп ))) Так что я стал подумывать о создании глобальных функций рисования или может я не прав.

Добавлено через 2 минуты
По поводу 4го пункта недопонял, обьясните пожалуйста

Добавлено через 5 минут
По поводу наследования от Object так подумал, не лучше ли будет пихнуть данные в protected ?

Добавлено через 16 минут
Еще вот сказали, что наследоваться от точки(Object) не лучшая идея, предложили точку сделать структурой и использовать композицию/агрегацию в других классах
UltraPenguin
222 / 88 / 22
Регистрация: 20.03.2014
Сообщений: 296
Завершенные тесты: 1
06.10.2015, 15:15     Игра Арканоид #4
Цитата Сообщение от obivan Посмотреть сообщение
но я новичек скажем так делал чтобы хотя бы чтото зароботало
Вы же просили критики и предложений) Предложение простое: там где можно использовать ссылку - нужно использовать ссылку.
Скажу сразу с OpenGL не работал, ее код трогать не буду.

По поводу 4го пункта: придерживайтесь единого стиля именования элементов кода хотя бы в рамках проекта. Подробнее про стиль кода например тут или отчасти у Тамики. Статей и ресурсов over9000
obivan
Падаван С++
 Аватар для obivan
172 / 158 / 41
Регистрация: 11.11.2014
Сообщений: 590
Завершенные тесты: 1
06.10.2015, 15:24  [ТС]     Игра Арканоид #5
UltraPenguin, услышал вас сейчас почитаю, по поводу ссылок, будет сделано и переделано

Добавлено через 1 минуту
А по поводу критики вы не думайте, что так меняю решения, фраза новичек это отмазка будем честны, лучше сразу буду учиться делать правильно, переучиваться сложнее.

Добавлено через 2 минуты
Преподователь в университете кстати начал поднимать тему, чистого и правильного написания кода с возможным рефакторингом, я думаю к его советам тоже стоит прислушиваться посерьезнее.
Enno
265 / 168 / 38
Регистрация: 25.08.2014
Сообщений: 1,088
Записей в блоге: 1
06.10.2015, 15:26     Игра Арканоид #6
Сам-то я не супер-кодер, но в добавление к предыдущим:
- хардкод размеров
- объекту добавить свойство существования, чтобы удалять было легче
- блоки в стандартный или самописный контейнер
- обработчики событий (столкновение с блоком, падом, проигрыш)
obivan
Падаван С++
 Аватар для obivan
172 / 158 / 41
Регистрация: 11.11.2014
Сообщений: 590
Завершенные тесты: 1
06.10.2015, 15:46  [ТС]     Игра Арканоид #7
Enno, по первому пункту чуть поподробнее, по поводу второго предлагаете сделать свойство bool, к примеру и по нему искать какой обьект удалять ? Если правильно понял, то отличная идея , по поводу контейнера тоже чуть чуть поподробнее, ну или я еще такого не знаю и буду учит тогда, обработчики событий, я так понял этот сумбур который происходит в классе Field, а именно в методе BallTick вынести как группу маленьких методов ?

Добавлено через 4 минуты
Обработчики проигрыша/выйгрыша сейчас накидаю
Enno
265 / 168 / 38
Регистрация: 25.08.2014
Сообщений: 1,088
Записей в блоге: 1
06.10.2015, 17:11     Игра Арканоид #8
Цитата Сообщение от obivan Посмотреть сообщение
по первому пункту чуть поподробнее
Размеры вынеси как define или переменными.
Цитата Сообщение от obivan Посмотреть сообщение
по поводу второго предлагаете сделать свойство bool, к примеру и по нему искать какой обьект удалять ?
Нет, по нему определять существует объект или нет, чтобы рассчитывать столкновения и отрисовку.
Цитата Сообщение от obivan Посмотреть сообщение
о поводу контейнера тоже чуть чуть поподробнее
Указатель на указатель это не лучшее решение. Попробуй использовать класс который представляет собой блоки и предоставляет основные функции, типа удаления/добавления, столкновения.
Цитата Сообщение от obivan Посмотреть сообщение
обработчики событий, я так понял этот сумбур который происходит в классе Field, а именно в методе BallTick вынести как группу маленьких методов ?
В местах где ты производишь отскок вызывай метод-обработчик, вроде OnEvent(int event_type), чтобы было легче изменять эти места. Например добавить звук или сообщение.

Добавлено через 4 минуты
Можно кстати сделать разные блоки, с разным количеством "жизней", а метод проверки отскока сделать параметризированным, вроде IsBumped(Object& ball, int bump_type). Во втором параметре указывать надо ли удалять блок при отскоке, а в блоке указывать число жизней. Число жизней показывать цветом можно или текстурой.
obivan
Падаван С++
 Аватар для obivan
172 / 158 / 41
Регистрация: 11.11.2014
Сообщений: 590
Завершенные тесты: 1
06.10.2015, 17:14  [ТС]     Игра Арканоид #9
Enno, Угу попробуем доделать такое, чуть позже залью код
Enno
265 / 168 / 38
Регистрация: 25.08.2014
Сообщений: 1,088
Записей в блоге: 1
06.10.2015, 17:14     Игра Арканоид #10
Также можно определить виртуальную функцию IsBumped для класса Object, а в производных классах переопределять её. например для круга будет одна формула, а для прямоугольника другая. Плюс все блоки можно объявить наследником класса Object и рассматривать их как один большой композитный и мутирующий от ударов Object.
obivan
Падаван С++
 Аватар для obivan
172 / 158 / 41
Регистрация: 11.11.2014
Сообщений: 590
Завершенные тесты: 1
06.10.2015, 18:09  [ТС]     Игра Арканоид #11
Enno, блин столько хороших идей, буду пробовать все реализовать, что вы сказали, если появяться вопросы напишу, и результаты буду сюда заливать
UltraPenguin
222 / 88 / 22
Регистрация: 20.03.2014
Сообщений: 296
Завершенные тесты: 1
06.10.2015, 18:49     Игра Арканоид #12
Сообщение было отмечено автором темы, экспертом или модератором как ответ
1) Не используйте "магических цифр". Любую константу старайтесь именовать. Облегчает чтение и понимание кода.
2) Классы на то и классы, что должны аккумулировать внутри себя логику поведения объекта и предоставлять интерфейс в виде функций для воздействия на объект. Поэтому я перенес некоторые переменные и логику из Field в нужные классы.
3) Про правило 1 класс - 2 файла не забываем. Так же сделаете сами.

Может что-то уже забыл... если вспомню дополню.

Что я сделал: отрефакторил код архитектурно.
Что я не сделал: не менял логику работы и вычислений (вроде бы).
Кое-что, помеченное TODO, я специально оставил для ваших рук.

Вот вам пластилин для дальнейшего творчества:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
namespace Constants
{
    const int coordsNum         = 4;
    const int leftBottomCoords  = 0;
    const int leftTopCoords     = 3;
    const int rightBottomCoords = 1;
    const int rightTopCoords    = 2;
 
    const int defaultRadius = 5;
    const int defaultSpeed  = 3;
 
    const int defaultHalfWidth  = 25;
    const int defaultHalfHeight = 5;
}
//==================================================================================================
typedef struct strControls
{
    //TODO: дать возможность переназначать клавиши
    unsigned char goLeft = 97;
    unsigned char goRight = 100;
    unsigned char shootBall = 49;
} Controls;
Controls controls;
//==================================================================================================
typedef struct strCoords
{
    int x;
    int y;
    strCoords() : x(0), y(0){}
    strCoords(int x, int y) : x(x), y(y) {}
    void setXY(const int& x, const int& y) { this->x = x; this->y = y;}
} Coords;
 
class Object {
private:
    Coords center;
    bool deleted; // Метка на удаление
    int red; //Не стоит постоянно передавать одни и те же параметры, сделаем цвет хранимым
    int green;
    int blue;
public:
    Object() : deleted(false){}
    Object(int x, int y) : center(x, y), deleted(false){}
 
    const bool isDeleted(void) const { return deleted; }
    void markForDelete(void) { deleted = true; } // Сеттер не нужен. То что помечено, должно быть удалено.
    const int getX(void) const { return center.x; }
    const int getY(void) const { return center.y; }
    void setX(const int& x) { this->center.x = x; }
    void setY(const int& y) { this->center.y = y; }
    void setXY(const int& x, const int& y) { this->center.setXY(x, y); }
    void setRGB(const int& Red, const int& Green, const int& Blue) { red = Red; green = Green; blue = Blue;}
    virtual void draw(void) = 0;
};
//==================================================================================================
class Elems : public Object
{
private:
    Coords coords[Constants::coordsNum];
public:
    Elems() : Object() {
        setCoords();
    }
    Elems(int x, int y) : Object(x, y){
        setCoords();
    }
 
    const int getLeftBX(void) const { return coords[Constants::leftBottomCoords].x; }
    const int getLeftBY(void) const { return coords[Constants::leftBottomCoords].y; }
    const int getLeftTX(void) const { return coords[Constants::leftTopCoords].x; }
    const int getLeftTY(void) const { return coords[Constants::leftTopCoords].y; }
 
    const int getRightBX(void) const { return coords[Constants::rightBottomCoords].x; }
    const int getRightBY(void) const { return coords[Constants::rightBottomCoords].y; }
    const int getRightTX(void) const { return coords[Constants::rightTopCoords].x; }
    const int getRightTY(void) const { return coords[Constants::rightTopCoords].y; }
 
    void setCoords(void) {
        int x = getX(), y = getY();
        coords[0].setXY(x - Constants::defaultHalfWidth, y - Constants::defaultHalfHeight);
        coords[1].setXY(x + Constants::defaultHalfWidth, y - Constants::defaultHalfHeight);
        coords[2].setXY(x + Constants::defaultHalfWidth, y + Constants::defaultHalfHeight);
        coords[3].setXY(x - Constants::defaultHalfWidth, y + Constants::defaultHalfHeight);
    }
 
    void ballCollisionOccured(void) // Теперь можно добавить разные типы блоков и по разному обрабатывать столкновения
    {
        markForDelete();
    }
 
    virtual void draw(void) {
        setCoords();
        glColor3d(red, green, blue);
        glBegin(GL_POLYGON);
        glVertex2d(coords[0].getX(), coords[0].getY());
        glVertex2d(coords[1].getX(), coords[1].getY());
        glVertex2d(coords[2].getX(), coords[2].getY());
        glVertex2d(coords[3].getX(), coords[3].getY());
        glEnd();
    }
};
//==================================================================================================
class Player : public Elems
{
    //TODO: добавить текущий счет и имя игрока
    //TODO: границы также можно проименовать
public:
    Player() : Elems() {}
    Player(int x, int y) : Elems(x, y) {}
    // Будем держать всю логику перемещений игрока в одном месте.
    void move(const int& dist)
    {
        int x = getX();
        if ((dist > 0 && x < 125) || (dist < 0 && x > -125))
            setX(x + dist);
    }
};
//==================================================================================================
class Ball : public Object
{
private :
    unsigned int Radius;
    int velocity[2];
public:
    Ball() : Object(), Radius(Constants::defaultRadius) {}
    Ball(int x, int y) : Object(x, y), Radius(Constants::defaultRadius) {}
 
    const unsigned int getRadius(void) const { return Radius; }
    void setRadius(const unsigned int& Radius) { this->Radius = Radius; }
 
    void setVelocityX(const int& x) { velocity[0] = x; }
    void setVelocityY(const int& y) { velocity[1] = y; }
 
    virtual void draw(void)
    {
        glColor3d(red, green, blue);
        glBegin(GL_POINTS);
        int BallX, BallY, x = getX() , y = getY();
        for (float angle = 0.3; angle < 6.4; angle += 0.1) {
            BallX = Radius * cosf(angle);
            BallY = Radius * sinf(angle);
            glVertex2d(BallX + x, BallY + y);
        }
        glEnd();
    }
 
    void checkCollisionWithPlayer(const Player* player)
    {
        if (getY() == player->getY() + 2 &&
            getX() > player->getLeftTX() - Radius &&
            getX() < player->getRightTX() + Radius)
            velocity[1] = 1;
    }
 
    bool checkCollisionWithBlock(const Elems* block)
    {
        int x = getX(), y = getY();
        if (block->getLeftBY() == y &&
            x > block->getLeftBX() - 2 &&
            x < block->getRightBX() + 2)
        {
            velocity[1] = -1;
            return true;
        }
 
        if (block->getLeftTY() == y &&
            x > block->getLeftTX() - 2 &&
            x < block->getRightTX() + 2)
        {
            velocity[1] = 1;
            return true;
        }
 
        if (block->getLeftBX() == x &&
            y > block->getLeftBY() + 1 &&
            y < block->getLeftTY() - 1)
        {
            velocity[0] = -1;
            return true;
        }
 
        if (block->getRightBX() == x &&
            y > block->getRightBY() + 1 &&
            y < block->getRightTY() - 1)
        {
            velocity[0] = 1;
            return true;
        }
        return false;
    }
 
    void move(void)
    {
        setX(getX() + velocity[0]);
        setY(getY() + velocity[1]);
        if (getX() < -150)
            velocity[0] = 1;
        else
        {
            if (getX() > 150) // Внес внутри блока чтобы проверка не срабатывала вхолостую
                velocity[0] = -1;
        }
 
        if (getY() > 120)
            velocity[1] = -1;
        else
        {
            if (getY() < -120) // Внес внутри блока чтобы проверка не срабатывала вхолостую
                velocity[1] = 1;
        }
    }
};
//==================================================================================================
class Field {
private:
    bool ballState;
    int speed = Constants::defaultSpeed;
 
    std::vector<Elems*> blocks;
    Player player;
    Ball ball;
 
    void createField(void) {
        //TODO: сделать сохранение/загрузку уровней
        unsigned int BlocksNum = 16;
        Elems* block;
        for (int i = 0, x = -105, y = 100; i < BlocksNum; ++i, x += 70)
        {
            block = new Elems;
            block->setXY(x, y);
            blocks.push_back(block);
 
            if (i % 4 == 3)
            {
                x -= 280;
                y -= 25;
            }
        }
    }
public:
 
    Field() :
        player(0, -100), ball(player.getX(), player.getY() + 10), ballState(false)
    {
        player.setRGB(255, 0, 0); //TODO: этого кода можно избежать, используя конструкторы
        ball.setRGB(0, 0, 255);
        createField();
        int num = blocks.size();
        for (int i = 0; i < num; ++i)
            blocks[i]->setRGB(255, 255, 0);
    }
    ~Field()
    {
        if (!blocks.empty())
        {
            for (int i = 0; i < blocks.size(); ++i)
                delete blocks[i];
        }
    }
 
    void setBallState(const bool& ballState) { this->ballState = ballState; }
 
    void draw(void)
    {
        int num = blocks.size();
        for (int i = 0; i < num; ++i)
            blocks[i]->draw();
        player.draw();
        ball.draw();
    }
 
    void playerMove(const unsigned char& key)
    {
        if (key == controls.goRight)
            player.move(5);
        else // Убрал лишнюю проверку, у нас и так либо одна клавиша либо другая за счет keyboard()
            player.move(-5);
        if (ballState == false)
        {
            ball.setX(player.getX());
            if (player.getX() < 0)
            {
                ball.setVelocityX(-1);
                ball.setVelocityY(1);
            }
            else
            {
                ball.setVelocityX(1);
                ball.setVelocityY(1);
            }
        }
    }
 
    void ballTick(void)
    {
        for (int j = 0; j < speed; ++j)
        {
            ball.move();
 
            ball.checkCollisionWithPlayer(&player);
 
            int num = blocks.size();
            for (int i = 0; i < num; ++i)//перебор всех блоков
                if (ball.checkCollisionWithBlock(blocks[i]))
                    blocks[i]->ballCollisionOccured();
 
            for (int i = 0; i < blocks.size(); ++i)
                if (blocks[i]->isDeleted())
                    delete blocks[i];
 
        }
    }
} field;
//==================================================================================================
void displayGame(void)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    field.draw();
    field.ballTick();
    glFlush();
}
//==================================================================================================
void keyboard(unsigned char key, int, int)
{
    if (key == controls.goRight || key == controls.goLeft)
    {
        field.playerMove(key);
        return;
    }
 
    if (key == controls.shootBall)
        field.setBallState(true);
}
obivan
Падаван С++
 Аватар для obivan
172 / 158 / 41
Регистрация: 11.11.2014
Сообщений: 590
Завершенные тесты: 1
06.10.2015, 19:08  [ТС]     Игра Арканоид #13
UltraPenguin, Пока есть желание и рвение, буду продолжать делать, по выше указанным пунктам, сегодня засяду и наверное завтра и после завтра попробую все реализовать, еще хочу добавить менюшку, но это я все сам, я вам выложу обязательно на суд Огромное спасибо, вы очень помогаете, хотя это я для души делаю

Добавлено через 3 минуты
Еще накидаю пример загрузки разных уровней, появилась тут идейка, но надо пока самому разобраться, вообщем не пропадайте далеко
Kerry_Jr
Модератор
 Аватар для Kerry_Jr
1855 / 1651 / 575
Регистрация: 14.05.2014
Сообщений: 4,737
Записей в блоге: 1
Завершенные тесты: 5
06.10.2015, 19:18     Игра Арканоид #14
Цитата Сообщение от UltraPenguin Посмотреть сообщение
C++
1
2
3
4
5
6
7
8
typedef struct strControls
{
    //TODO: дать возможность переназначать клавиши
    unsigned char goLeft = 97;
    unsigned char goRight = 100;
    unsigned char shootBall = 49;
} Controls;
Controls controls;
наследие Си? Зачем структуру тайпдефить?
UltraPenguin
222 / 88 / 22
Регистрация: 20.03.2014
Сообщений: 296
Завершенные тесты: 1
06.10.2015, 19:22     Игра Арканоид #15
Цитата Сообщение от Kerry_Jr Посмотреть сообщение
наследие Си? Зачем структуру тайпдефить?
Ахах) да наверно оттуда) Привычка видимо.
obivan
Падаван С++
 Аватар для obivan
172 / 158 / 41
Регистрация: 11.11.2014
Сообщений: 590
Завершенные тесты: 1
06.10.2015, 19:25  [ТС]     Игра Арканоид #16
Kerry_Jr, Если память не изменят в си при обявлении переменной мы пишем
C
1
strcut Example Object;
typedef же позволяет нам писать
C++
1
Example Object;
И передача в функцию также упрощаеться, но точно не уверен когда синтаксис был упрощен, и ключевое слово struct отпало, а это может остаться как привычка
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
06.10.2015, 20:11     Игра Арканоид
Еще ссылки по теме:

Простая игра-арканоид Delphi
OpenGL Игра Арканоид
C# арканоид

Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
Kerry_Jr
Модератор
 Аватар для Kerry_Jr
1855 / 1651 / 575
Регистрация: 14.05.2014
Сообщений: 4,737
Записей в блоге: 1
Завершенные тесты: 5
06.10.2015, 20:11     Игра Арканоид #17
obivan, я это прекрасно знаю, поэтому и спросил, зачем в С++ использовать устаревшие (для С++) вещи.
Yandex
Объявления
06.10.2015, 20:11     Игра Арканоид
Ответ Создать тему
Опции темы

Текущее время: 23:43. Часовой пояс GMT +3.
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2016, vBulletin Solutions, Inc.
Рейтинг@Mail.ru