Цикл уроков С++ с нуля до игр


Данный курс лекций и упражнений расчитан на проход с примерно азов до полного понимание кода игры астеройды www.youtube.com/watch?v=rWaSo2usU4A
 
Это темы:
1. начальные знания (переменные, условия, циклы)
2. ссылки, указатели, динамическая память
3. контейнеры vector, list, map
4. классы C++
5. наследование, полиморфизм
+ всяко разно про SFML и геометрию 

 

1. Если опыта прораммирования еще нет, лучше начать с общих знаний https://www.youtube.com/watch?v=P2HHpZgOgXk , потом посмотреть как это выглядит в с++ (переменные, условия, циклы - примерно будет то же самое, просто разный синтаксис). Из книг рекомендовал бы Дейтел "как программировать на C++" 5-е малое издание, а также вкусную книгу "85 нетривиальных задач по C++" (потренировать программистское мышление:)

2. Вторая тема - https://www.youtube.com/watch?v=goYuEJumeOA . Под видео есть ссылки на скачку - это продолжение. В принципе там же разговор на третью тему переходит

вот такие 3 упражнения, первые 2 устные
1. что выведет программа

double a[3];
cout<<sizeof(a)<<endl;
2. что выведет программа
int a[3] = {7,23,18};
int *p = a;
cout<< *(p+2)+2 <<endl;
3. создать функцию принимающую n, и возвращающую массив размером n заполненный случайно. В main вывести на экран например третий элемент массива.
 

3. Третья тема - в этом видео https://www.youtube.com/watch?v=KNVPFVG49Oc с 6-ой по 23-юю минуту разбор vector, list, map

Устные упражнения:
* В чем отличие vector от list?
* что такое итератор? что появилось в с++11 по этому поводу?
* Допустим у нас есть текстовый файл, и стоит задача найти самое часто встречающееся слово. Какую структуру лучше использовать?
* как создать двумерный массив с помощью vector?
* какую структуру использовать для реализации телефонного справочника?
* как удалить элемент с list?
 
Упражнение:
Создать класс ученик, у которого есть фамилия, возраст и оценки (количество оценок может быть разное).  Закинуть 50 случайных учеников. Удалить всех со средним баллом меньше 3. Вывести оставшихся по старшинству и их количество.

4. Классы С++ https://www.youtube.com/watch?v=CF5YMeW0SSw

5. Наследование, полиморфизм https://www.youtube.com/watch?v=UUTsxdEeU3I

Упражнение:
Моделирования забега. Черепаха, лягушка и кролик. У всех есть позиция pos, метод go() и info(). Черепаха за шаг перемещается случайно от 0 до 3. Кролик от -2 до 5. Лягушка с вероятностью 1/4 прыгает на 7. 
info() для черепахи это вывод на экран "черепаха - " и позиция. Другие аналогично.
Взять по 5 животных каждого вида, и запустить на 10 шагов. Вывести на экран результаты забега (по убыванию)
 
6. Далее переходим к цели - разбор кода игры астеройды https://yadi.sk/d/feCghyH4ropx9 . Также в видео начальный разбор библиотеки SFML
 
 

Для самопроверки, публикуем переписку с одним из юзеров:

[10:26:45] Trinli: вот такие 3 упражнения, первые 2 устные
 
1. что выведет программа
double a[3];
cout<<sizeof(a)<<endl;
 
2. что выведет программа
int a[3] = {7,23,18};
int *p = a;
cout<< *(p+2)+2 <<endl;
 
3. создать функцию принимающую n, и возвращающую массив размером n заполненный случайно. В main вывести на экран например её третий элемент.
[10:27:14] Deonisiu: 1) 24 байта
[10:27:26] Trinli: ок
[10:29:25] Deonisiu: 2) 20
[10:29:38] Trinli: верно!
[10:30:12] Deonisiu: А третье мне надо написать прогу полностью как я понял ?
[10:30:17] Trinli: да
[10:30:36] Deonisiu: Там обязательно нужно использовать функцию rand ?
[10:30:53] Trinli: нет, можно от 1 до n заполнить
[10:40:28] Deonisiu: Как то так
[10:40:29] Deonisiu: #include <iostream>
 
using namespace std;
 
int* myFunc(int n) {
 int *ptr;
 ptr = new int[n];
 for (int i = 0; i < n; i++) {
  ptr[i] = i;
 }
 ptr[2] = 256;
 return ptr;
}
 
void main() {
 int n;
 int *ptr;
 
 cin >> n;
 
 ptr = myFunc(n);
 
 for (int i = 0; i < n; i++) {
  cout << ptr[i] << " ";
 }
 
 cout << endl << "ptr[2] = " << ptr[2] << endl;
 system("pause");
}
 
[10:41:27] Trinli: совершенно верно))
[10:41:59] Trinli: только в конце delete [] ptr;
[10:42:09] Trinli: чтоб без утечек
[10:42:31] Trinli: ну что ж, второй пункт можно мысленно вычеркнуть)
[10:42:46] Deonisiu: Эм.. утечки тут не будет т.к. прога больше ничего не делает, но написать надо было
[10:42:58] Deonisiu: Забыл про это
 
[11:02:14] Deonisiu: Как лучше изучать программирование ? Много книг читать по программированию, или больше практики, или и то и другое ? Работать над реальными проектами, или в основном над мелкими примерами из книг и т.п. ?
[11:02:51] Deonisiu: Просто хочется понять правильный вектор направления для быстрого и продуктивного развития в программировании
[11:08:39] Trinli: чтобы всякие сложные понятия программирования не были абстракцией, а реально удобным инструментом решающим ту или иную задачу - нужно на эти задачи выйти. И увидеть, что обычные средствами не обойтись. 
К примеру создание своего движка сайта или создание игры (скажем платформер со всеми плюшками), или даже те же астеройды
 
 
[10:42:38] Trinli: Устные:
* В чем отличие vector от list?
* что такое итератор? что появилось в с++11 по этому поводу?
* Допустим у нас есть текстовый файл, и стоит задача найти самое часто встречающееся слово. Какую структуру лучше использовать?
* как создать двумерный массив с помощью vector?
* какую структуру использовать для реализации телефонного справочника?
* как удалить элемент с list?
 
Упражнение:
Создать класс ученик, у которого есть фамилия, возраст и оценки (количество оценок может быть разное).  Закинуть 50 случайных учеников. Удалить всех со средним баллом меньше 3. Вывести оставшихся по старшинству и их количество.
[10:45:16] Deonisiu: Ну как я понял основные отличия vector от list это то что в векторе можно обратиться к любому элементу а в листе нет, а в листе можно делать сортировку, вставлять в начало и удалить все элементы с одним значением.
[10:46:07] Deonisiu: Мне показалось что это основные моменты
[10:47:23] Trinli: сортировку, вставку в начало и удаление можно делать и в vector - но это будет гордаздо дольше - это основное отличие))
[10:48:51] Trinli: то есть вектор в памяти расположен подряд, поэтому быстрое обращение за 1 операцию. Лист разбросан и доступ от каждого до следующего по ссылке, поэтому добираться до элемента медленней
[10:49:11] Trinli: но удалить-вставить быстро
[10:49:50] Trinli: чтоб удалить элемент из вектора придется сдвигать всех кто после него на 1 назад
[10:50:14] Trinli: если элементов тысячи, то это трабл
[10:51:26] Deonisiu: Я если честно раньше не сталкивался с этими структурами. Поэтому пока ещё не особо понял всю суть. Но в общих чертах вроде пока понятно. Не хватает наверно практики
[10:52:30] Trinli: да, с практикой приходит
[10:52:34] Deonisiu: Как я понял vector-ом удобно пользоваться как стеком LIFO ?
[10:52:50] Trinli: да
[10:53:27] Deonisiu: Тобишь создавать и удалять элементы только с конца, а не где то в середине
[10:54:52] Deonisiu: Итератор - речь идет о итераторе который создается например для list?
[10:56:42] Deonisiu: Думаю можно точно сказать что это объект позволяющий перебирать все элементы коллекции
[10:56:54] Trinli: верно!
[10:57:36] Trinli: как пройтись например по list понял?
[10:58:40] Deonisiu: Создаем итератор list<int>::iterator itr;
[10:59:18] Trinli: в с++11 вместо громоздкой конструкции которая раньше была можно пройтись for(i:mylist) {...}
[10:59:24] Deonisiu: И используем for(itr=listName.begin(); itr<listName.end(); ir++)
[10:59:45] Deonisiu: itr++
[11:00:19] Trinli: вот вместо этого можно for(auto i: listName) {...} в с++11
[11:00:40] Trinli: что, конечно, приятней для глазу;)
[11:01:03] Deonisiu: В I записывается каждое значение по очереди ?
[11:01:10] Trinli: да
[11:01:39] Deonisiu: Работает только для list или есть ещё другие коллекции ?
[11:01:45] Trinli: для любых
[11:02:20] Deonisiu: Надо будет почитать про это
[11:02:59] Trinli: попробуй 3-ий вопрос ответить
[11:03:07] Deonisiu: [14:42:48] Trinli:  Допустим у нас есть текстовый файл, и стоит задача найти самое часто встречающееся слово. Какую структуру лучше использовать?
 
<<< 
[11:03:39] Deonisiu: Я так понимаю что тут нужно использовать map
[11:03:44] Trinli: верно!
[11:04:01] Deonisiu: Хотя я если честно не совсем понимаю как это сделать =)
[11:05:42] Trinli: map будет строка-число. Просто читаем слова и если в map его еще нет кладем с нулем, иначе инкрементруем)
[11:09:48] Trinli: Кстати в том видео про хаффмана используется map для подсчета букв
[11:36:47] Trinli: * какую структуру использовать для реализации телефонного справочника?
[11:37:04] Trinli: в догонку про справочник - очевидно какая структура?)
[11:37:20] Deonisiu: Тоже map
[11:37:37] Trinli: но здесь он эффективен по еще одной причине - какой?
[11:38:16] Deonisiu: Блин для этого надо хорошо знать методы map =)
[11:38:33] Trinli: нет) самая суть map
[11:39:01] Deonisiu: Ассоциативный массив ?
[11:39:10] Trinli: быстрый доступ
[11:39:25] Trinli: по ключу, который в данном случае номер телефона
[11:42:13] Trinli: в map быстрый доступ по ключу (внутри сортируется специальным образом)
[11:43:21] Deonisiu: Полезная структура. А я о них даже не знал.
[11:45:50] Trinli: есть еще пару структур всплывающих иногда и реально решающих ситуацию в пару строк вместо громождения огорода. Вроде множества, хэш-таблицы, кольцевого буфера и т.д. Т.е. разные структуры показывают эффективность на разных задачах, полезно знать что они существуют
[11:46:45] Deonisiu: Видимо у меня не было этих разных задач, да и структур я не знал никаких до этого
[11:47:30] Deonisiu: А двумерный массив обычного вида может быть динамическим ?
[11:47:50] Trinli: да, конечно. Сколько угодно мерные)
[11:48:17] Deonisiu: Тогда у меня вопрос для чего нужен двумерный массив vector ?
[11:50:53] Trinli: дело в том, что обычный массив С++ никак не отлавливает то, что мы вышли за пределы - будешь стрелять по случайным местам памяти и комп рухнет. Вектор отлавливает. Плюс - размер вектора можно по ходу дела менять, что весьма важно. (Например по мере надобности добавлять элемент в конец, или удалять)
[11:51:53] Trinli: плюс куча плюшек связанных с контейнерами
[11:53:25] Deonisiu: Получается vector удобней в использовании чем массив ?
[11:55:02] Deonisiu: Или не всегда ?
[11:55:28] Trinli: иногда обычным массивом не обойтись
[11:58:02] Trinli: в обычный массив как положить например слова из файла?
[11:58:17] Trinli: заранее не известно сколько их будет
[11:59:01] Deonisiu: Для меня раньше такие задачи в с++ были невозможны в моем представлении =)
[11:59:07] Trinli: )))))
[11:59:18] Deonisiu: Всегда казалось что в с++ чего то нехватает
[11:59:22] Deonisiu: Оказалось знаний
[12:00:20] Deonisiu: vector в виде двумерного массива это вектор в векторе ?
[12:00:25] Trinli: да
[12:00:50] Trinli: как будет выглядеть как ты думаешь?
[12:01:38] Deonisiu: vector<vector<int>>
[12:02:40] Trinli: верно! вот только  vector<vector<int> > - пробел между последними двумя знаками. Иначе компилятор думает что это >>
[12:03:06] Deonisiu: Ах да
[12:03:49] Trinli: хорошо)) последние упражнение
[12:04:08] Deonisiu: У меня вопрос по программе
[12:04:38] Deonisiu: В этой программе лучше всего использовать все 3 структуры которые мы обсуждали ?
[12:05:47] Trinli: map тут не прикрутить. А вектор и лист, да
[12:06:10] Trinli: вектор сделай оценки. А список учеников - лист
[12:06:28] Deonisiu: Эх.. не надо было подсказывать =)
[12:06:45] Trinli: )))
 
Упражнение:
Моделирования забега. Черепаха, лягушка и кролик. У всех есть позиция pos, метод go() и info(). Черепаха за шаг перемещается случайно от 0 до 3. Кролик от -2 до 5. Лягушка с вероятностью 1/4 прыгает на 7. 
info() для черепахи это вывод на экран "черепаха - " и позиция. Другие аналогично.
Взять по 5 животных каждого вида, и запустить на 10 шагов. Вывести на экран результаты забега (по убыванию)
[12:11:42] Deonisiu: Вероятность 1/4 делается через rand() ?
[12:11:51] Trinli: да
 
[16:50:37]  Пользователь Deonisiu отправил файл пользователю Classes.txt.
[17:29:07] Trinli: Отлично, молодец))) Разные нюансы есть там здесь, но в целом код работает и понимание есть))
Это именно случай когда задействовать list или vector можно чтоб убрать ANIM_COUNT 30 (например, пользователь задает количество, или сами появляются в игре)
Можешь глянуть эту решение - 
#include <iostream>
#include <vector>
#include <time.h>
using namespace std;
 
class Animal
{
  public:
  int pos;
  Animal(){pos=0;}
  virtual void go(){};
  virtual void info(){};
};
 
class Turtle: public Animal
{
  public:
  void go(){pos+=rand()%3;}
  void info(){cout<<"turtle - "<<pos<<endl;}
};
 
class Rabbit: public Animal
{
  public:
  void go(){pos+=-2+rand()%8;}
  void info(){cout<<"rabbit - "<<pos<<endl;}
};
 
class Toad: public Animal
{
  public:
  void go(){if (rand()%4==0) pos+=7;}
  void info(){cout<<"toad - "<<pos<<endl;}
};
 
 
int main()
{
   srand(time(0));
 
    vector<Animal*> animals;
 
    for(int i=0;i<5;i++){
    animals.push_back(new Turtle());
    animals.push_back(new Toad());
    animals.push_back(new Rabbit());
    }
 
    for(int i=0;i<10;i++)
     for(auto a:animals) a->go();
 
    for(int i=0;i<animals.size();i++)
     for(int j=0;j<animals.size();j++)
      if (animals[i]->pos > animals[j]->pos) swap(animals[i],animals[j]);
 
    for(auto a:animals) a->info();
 
    return 0;
}
[17:29:35] Trinli: обрати внимание на сортировку
[17:30:19] Trinli: swap меняет местами ссылки - и нет проблем
[17:30:41] Trinli: так же можно было сортирануть учеников из прошлой задачи
 
[17:43:11] Deonisiu: Мне пока тяжко дается написание. Хоть и есть понимание сам алгоритм в голове долго строится. Трачу по 2-3 часа на одну задачу
[17:43:36] Deonisiu: Плюс не всегда получается реализовать то что в голове было =)
[17:43:43] Trinli: это нормально
[17:43:47] Trinli: просто опыт
[17:43:56] Trinli: пару задач и все
[17:44:32] Trinli: потом применяешь готовые наработки или строишь подобные им
[17:45:46] Deonisiu: Звучит красиво =)
[17:46:32] Trinli: Потом уже как нибудь глянь вот это видео https://www.youtube.com/watch?v=T6o5OlgsCew там все по полной программе - и map и list и полиморфизм и плюшки вроде xml
 
[10:27:39] Deonisiu: Видео которое про разбор SFML
[10:28:08] Deonisiu: Не все понял, поэтому список вопросов не полный, но вот что первое пришло в голову
[10:28:19] Deonisiu: 1) Почему в цикле while(window.pollevent(e)) программа считывает еденичные нажатия, а за пределами цикла бесконечно много нажатий одной кнопки ?
 
2) Не совсем понял логику пересечения персонажа с платформой
   Я так понимаю что не для каждой игры столкновение радиусов подходит ?
 
3) app.setFrameRateLimit(60) исключает необходимость такого куска кода:
 
Clock clock;
float time = clock.getElapsedTime().asMicroseconds();
clock.restart();
time = time/800; // Тобишь скорость игры
 
Или это в принципе разные вещи, для разных нужд ?
 
4) Где можно прочитать про классическое движение типа:
if(thrust)
 { dx+=cos(angle*DEGTORAD)*0.2;
   dy+=sin(angle*DEGTORAD)*0.2; }
else
 { dx*=0.99;
   dy*=0.99; }
И есть ли книжки про подобные алгоритмы ?
[10:44:15] Trinli: Ок. Хорошие вопросы.
1. второй способ просто плюшка sfml для удобства. Обычно в подобных библиотеках бывает только первый вариант. А второй можно реализовать через него самому: заводим логическую переменную, которая становится true при нажатии, и false на другом событии - отпускание клавиши. Дальше в коде пользуемся ей.
2. Это логика подробно описана здесь https://youtu.be/P2HHpZgOgXk?t=1h6m21s  - тут же про технику нажатия клавиш (перемотано с этого момента).
Да, столкновение радиусов не всегда, но во многих случаях этого достаточно. Если нужны супер сложные стокновения, то можно переложить эту работу на box2d
3. В общем да. Осчитывать время с помощью Clock все равно часто нужно бывает (таймеры и прочее), как например тетрис
4. Сам давно ищу чтоб в одном месте. Один раз наткнулся на очень вкусный сайт с демонстрациями прям в браузере и не могу теперь найти((( А так, в той же книге "85 нетривиальных задач C++", разные алгоритмы-подходы для игр тож собраны
[10:48:24] Deonisiu: Я правильно понимаю, если 2D игра вида сверху то вполне подходит типо столкновений в виде радиусов, а если мы будем делать марио то там уже нужно сталкивать прямоугольные объекты ?
[10:48:57] Trinli: да
[10:49:19] Trinli: сверху тож могут быть сложные столкновения, смотря какая идея
[10:49:57] Deonisiu: А box2d это физ. движок ?
[10:50:07] Trinli: да физ движок
[10:50:43] Deonisiu: Его можно спокойной прикрутить к SFML ?
[10:50:56] Trinli: да, видео есть на канале как это сделать
 
[11:00:11] Deonisiu: У тебя есть ещё задания ? Попрактиковаться
[11:06:25] Trinli: задачка сделать движение машинки. Провериться можешь с видео car racing.
И еще вот это попробуй
[11:06:54]  Пользователь Trinli отправил файл
[11:09:46] Deonisiu: Ух ты. Прикольный эффект
[11:10:37] Trinli: да, при том что очень простой - реализуешь движение одной окружности, - остальные просто идут друг за другом
[11:11:21] Deonisiu: Надо это еще как в голове представить в виде кода
[11:12:37] Trinli: если что, вот код для самопроверки
#include <SFML/Graphics.hpp>
#include <vector>
using namespace sf;
 
struct CIRCLE
{
  public:
  CircleShape shape;
  CIRCLE(float R)
  {
    shape.setRadius(R);
    shape.setOrigin(R,R);
    shape.setOutlineColor(Color::Green);
    shape.setOutlineThickness(1);
    shape.setFillColor(Color(255,255,255,0));
  }
 
  void move(Vector2f t)
  {
    Vector2f d = t - shape.getPosition();
    d/=5.f;
    shape.move(d);
  }
 
};
 
int main()
{
    RenderWindow app(VideoMode(800, 600), "SFML window");
 
    std::vector<CIRCLE*> v;
 
    for(int i=1;i<25;i++)
    v.push_back(new CIRCLE(i*5));
 
    while (app.isOpen())
    {
        Vector2i pos = Mouse::getPosition(app);
 
        Event event;
        while (app.pollEvent(event))
        {
            if (event.type == Event::Closed)
                app.close();
        }
 
        v[0]->move(Vector2f(pos));
 
        for(int i=1;i<v.size();i++)
          v[i]->move(Vector2f(v[i-1]->shape.getPosition()));
 
        app.clear();
 
        for(auto i:v)
          app.draw(i->shape);
 
        app.display();
    }
 
    return EXIT_SUCCESS;
}
 

Поделиться: