На прошлом уроке мы разбирались со “временем SFML “, значит теперь самое время разобраться с анимацией. Как я уже говорил – анимация это быстрое переключение прямоугольников с картинками. Для начала слегка почистим код с предыдущего урока, удалив
ненужный нам таймер телепортации героя – удалите строки с этой переменной и все, что с ней связано.
Видеоверсия sfml урок 7
Если вы не особо поняли как работать с координатами в том же паинте – посмотрите предыдущие уроки ( 3,4 ). Здесь координат будет не мало, вам надо научиться их правильно извлекать.
Вернемся к созданию анимации. Нам нужна переменная, которая будет передвигать кадры нашей анимации благодаря времени, она будет хранить в себе текущий кадр. Объявим еще в ф-ции main:
float CurrentFrame=0;//хранит текущий кадр
Для начала я распишу на примере кнопки влево и разделив всё построчно с комментариями.
Напомню – у меня персонаж лев:
И я хочу сделать анимацию “влево”, значит мне нужно пройтись по второму ряду льва и делать это “пока нажата клавиша влево”
В функции пока открыто окно, где у меня было управление персонажем, напишем вот такую строчку для клавиши “влево”, предварительно стерев предыдущую. Смотрим комментарии:
1 2 3 4 5 6 |
if ((Keyboard::isKeyPressed(Keyboard::Left) || (Keyboard::isKeyPressed(Keyboard::A)))) { //если нажата клавиша стрелка влево или англ буква А CurrentFrame += 0.005*time; //служит для прохождения по "кадрам". переменная доходит до трех суммируя произведение времени и скорости. изменив 0.005 можно изменить скорость анимации if (CurrentFrame > 3) CurrentFrame -= 3; // если пришли к третьему кадру - откидываемся назад. herosprite.setTextureRect(IntRect(96 * int(CurrentFrame), 96, 96, 96)); //проходимся по координатам Х. получается начинаем рисование с координаты Х равной 0,96,96*2, и опять 0 herosprite.move(-0.1*time, 0);//происходит само движение персонажа влево } |
Аналогично поступаем для остальных клавиш:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
if ((Keyboard::isKeyPressed(Keyboard::Right) || (Keyboard::isKeyPressed(Keyboard::D)))) { CurrentFrame += 0.005*time; //служит для прохождения по "кадрам". переменная доходит до трех суммируя произведение времени и скорости. изменив 0.005 можно изменить скорость анимации if (CurrentFrame > 3) CurrentFrame -= 3; //если пришли к третьему кадру - откидываемся назад. herosprite.setTextureRect(IntRect(96 * int(CurrentFrame), 192, 96, 96)); //проходимся по координатам Х. получается 0, 96,96*2 и опять 0 herosprite.move(0.1*time, 0);//происходит само движение персонажа вправо } if ((Keyboard::isKeyPressed(Keyboard::Up) || (Keyboard::isKeyPressed(Keyboard::W)))) { CurrentFrame += 0.005*time; //служит для прохождения по "кадрам". переменная доходит до трех суммируя произведение времени и скорости. изменив 0.005 можно изменить скорость анимации if (CurrentFrame > 3) CurrentFrame -= 3; // если пришли к третьему кадру - откидываемся назад. herosprite.setTextureRect(IntRect(96 * int(CurrentFrame), 288, 96, 96)); //проходимся по координатам Х. получается 0,96,96*2, и опять 0 herosprite.move(0, -0.1*time);//происходит само движение персонажа вверх } if ((Keyboard::isKeyPressed(Keyboard::Down) || (Keyboard::isKeyPressed(Keyboard::S)))) { CurrentFrame += 0.005*time; //служит для прохождения по "кадрам". переменная доходит до трех суммируя произведение времени и скорости. изменив 0.005 можно изменить скорость анимации if (CurrentFrame > 3) CurrentFrame -= 3; //если пришли к третьему кадру - откидываемся назад. herosprite.setTextureRect(IntRect(96 * int(CurrentFrame), 0, 96, 96)); //проходимся по координатам Х. получается 0,96,96*2,и опять 0 herosprite.move(0, 0.1*time);//происходит само движение персонажа вниз } |
Когда поймете что к чему – скомпонуйте для удобства и компактности код. Хочется отметить , что лучше использовать отдельный класс анимации и отделять ему отдельный заголовочный файл – это поможет увеличить читаемость кода и убрать лишнюю кашу, поскольку в реальных играх количество строк кода будет зашкаливать Ну об этом поговорим как-нибудь.
В моем случае на картинке есть движение льва и влево и вправо. Но что делать, если есть только влево ? Можно в редакторе (хоть в паинте) добавить вручную вправо (путем “отразить по горизонтали” и получится как у меня). Но это не лучший вариант. Лучше всего отразить с помощью кода. Есть несколько способов.
1 |
herosprite.setScale(-1, 1); //отразим по горизонтали |
setScale(-1, 1);
этой функцией можно менять размер спрайта. Если напишите setScale(2, 2); то увидите какой большой лвёнок получился. Если бы у нас марио съел гриб – можно было бы использовать Так вот этой функцией можно отразить спрайт, задав отрицательную координату. По х – отразите по горизонтали, по у – по вертикали.
Еще один способ отразить изображение:
1 |
herosprite.setTextureRect(IntRect(96 * int(CurrentFrame)+96, 96, -96, 96)); |
Итак – текущий кадр у нас +96 то есть правый край и координата -96 означает, что мы идем наоборот справа налево, таким образом рисуя наоборот. Получается, что мы делаем инверсию и отражаем по горизонтали. Аналогичным способом можно начудить по Y.
Или вот например. Если у вас танк в виде квадрата с палкой (дуло), то вам достаточно будет одного его изображения в одном состоянии и можете использовать повороты:
1 |
herosprite.setRotation(90); //повернули спрайт на 90 градусов |
То есть не обязательно рисовать несколько состояний персонажа в редакторе. Можно обойти всё кодом и изображение будет меньше “весить”.
В будущем создадим класс анимаций. Пока поживем так. Заметьте, что наш персонаж может двигаться по диагонали и к тому же делает он это быстрее, а всё потому, что выполняется сразу два условия и его скорости суммируются. Как это обойти? Сделать четыре направления для персонажа. Ну или исключить нажатие другой клавиши,. Первый вариант лучше. Рассмотрим эти вещи в следующем уроке. Счастливо
Листинг урока:
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 |
#include <SFML/Graphics.hpp> #include <iostream> using namespace sf; int main() { RenderWindow window(sf::VideoMode(640, 480), "Lesson 7. kychka-pc.ru"); Texture herotexture; herotexture.loadFromFile("images/hero.png"); Sprite herosprite; herosprite.setTexture(herotexture); herosprite.setTextureRect(IntRect(0, 192, 96, 96)); herosprite.setPosition(250, 250); float CurrentFrame = 0;//хранит текущий кадр Clock clock; while (window.isOpen()) { float time = clock.getElapsedTime().asMicroseconds(); clock.restart(); time = time / 800; sf::Event event; while (window.pollEvent(event)) { if (event.type == sf::Event::Closed) window.close(); } ///////////////////////////////////////////Управление персонажем с анимацией//////////////////////////////////////////////////////////////////////// if ((Keyboard::isKeyPressed(Keyboard::Left) || (Keyboard::isKeyPressed(Keyboard::A)))) { //если нажата клавиша стрелка влево или англ буква А CurrentFrame += 0.005*time; //служит для прохождения по "кадрам". переменная доходит до трех суммируя произведение времени и скорости. изменив 0.005 можно изменить скорость анимации if (CurrentFrame > 3) CurrentFrame -= 3; //проходимся по кадрам с первого по третий включительно. если пришли к третьему кадру - откидываемся назад. herosprite.setTextureRect(IntRect(96 * int(CurrentFrame), 96, 96, 96)); //проходимся по координатам Х. получается 96,96*2,96*3 и опять 96 herosprite.move(-0.1*time, 0);//происходит само движение персонажа влево } if ((Keyboard::isKeyPressed(Keyboard::Right) || (Keyboard::isKeyPressed(Keyboard::D)))) { CurrentFrame += 0.005*time; //служит для прохождения по "кадрам". переменная доходит до трех суммируя произведение времени и скорости. изменив 0.005 можно изменить скорость анимации if (CurrentFrame > 3) CurrentFrame -= 3; //проходимся по кадрам с первого по третий включительно. если пришли к третьему кадру - откидываемся назад. herosprite.setTextureRect(IntRect(96 * int(CurrentFrame), 192, 96, 96)); //проходимся по координатам Х. получается 96,96*2,96*3 и опять 96 herosprite.move(0.1*time, 0);//происходит само движение персонажа вправо } if ((Keyboard::isKeyPressed(Keyboard::Up) || (Keyboard::isKeyPressed(Keyboard::W)))) { CurrentFrame += 0.005*time; //служит для прохождения по "кадрам". переменная доходит до трех суммируя произведение времени и скорости. изменив 0.005 можно изменить скорость анимации if (CurrentFrame > 3) CurrentFrame -= 3; //проходимся по кадрам с первого по третий включительно. если пришли к третьему кадру - откидываемся назад. herosprite.setTextureRect(IntRect(96 * int(CurrentFrame), 288, 96, 96)); //проходимся по координатам Х. получается 96,96*2,96*3 и опять 96 herosprite.move(0, -0.1*time);//происходит само движение персонажа вверх } if ((Keyboard::isKeyPressed(Keyboard::Down) || (Keyboard::isKeyPressed(Keyboard::S)))) { CurrentFrame += 0.005*time; //служит для прохождения по "кадрам". переменная доходит до трех суммируя произведение времени и скорости. изменив 0.005 можно изменить скорость анимации if (CurrentFrame > 3) CurrentFrame -= 3; //проходимся по кадрам с первого по третий включительно. если пришли к третьему кадру - откидываемся назад. herosprite.setTextureRect(IntRect(96 * int(CurrentFrame), 0, 96, 96)); //проходимся по координатам Х. получается 96,96*2,96*3 и опять 96 herosprite.move(0, 0.1*time);//происходит само движение персонажа вниз } window.clear(); window.draw(herosprite); window.display(); } return 0; } |
спасибо за уроки.
но не очень понял эту строку
"
первое 96 это на сколько передвигается прямоугольник. а остальные?
первая цифра 96 – передвижение прямоугольника по иксу. вторая 96 – координата игрек. третья – ширина прямоугольника, четвертая цифра – его высота.
Разобрался ,спасибо
Хочу сделать платформер, можете выложить урок по гравитации? чтоб персонаж прыгал
конечно, такой урок будет. у меня по плану пара других уроков. но скорее всего на выходных этой недели, или на следующих я сделаю такой урок
Ждем))
Добрый день, Павел!
Осваиваю SFML с помощью твоих уроков. Все просто, понятно и очень подробно описано. Большое спасибо за эти статьи!
Однако есть некоторые проблемы с ротацией спрайтов. Был удивлен, что метод setRotation() поворачивает спрайт относительно верхнего левого угла, а не относительно центра изображения. Таким образом, картинка визуально при повороте вроде как “перепрыгивает” на новые координаты. Есть ли красивый способ решить эту проблему? Ниже приведен мой код.
P.S. Еще раз хочу поблагодарить за великолепные уроки 😉
Привет, спасибо!
Подобный вопрос уже обсуждался на форуме : https://kychka-pc.ru/Forum/topic/povorot
так же в будущих уроках это будет не раз затронуто и ты столкнешься с этим снова.
Пиши на форум вопросы:)
Спасибо, разобрался
Также помимо форума нашел инфу в 19-ом уроке. Впредь буду внимательнее
Отчасти и я виноват – на сайте еще хреновый поиск он плохо работает.) а искать пытаться инфу в поздних уроках , в которых нет на это намёка – неудобно.
Спасибо большое! Уроки просто восхитительны! Только вот возник такой вопрос. При нажатии кнопки “Вправо” мой персонаж, начинает двигаться вправо, при нажатии “Влево” соответственно влево. После того, как отпускаем клавишу, то персонаж остаётся в том спрайте, на каком он остановился. А как, например, сделать условие, “Если не нажато ничего, то вставляется определённый спрайт”?
спасибо:)
у нас вопросы на форуме:)
Можно сделать условие : “Если не нажата клавиша влево & Не нажата вверх & вниз & вправо, то спрайт стоящего персонажа”.