SFML и C++ Уроки \ Разработка игр › Форумы › SFML Graphics › Анимация , класс Анимации
Помечено: Полный код
В этой теме 12 ответов, 5 участников, последнее обновление Бек 8 года/лет, 4 мес. назад.
-
АвторСообщения
-
Прошу прощения за поток вопросов
В 20-ом уроке Павел временно вырезает анимацию из игры, но я, пробежавшись до 26 урока, заметил, что анимацию пока что не вернули. Я решил попробовать сделать анимацию сам, но получилось очень плохо:
C++123456789101112131415void Animation(float time){CurrentFrame += 0.005*time;if (CurrentFrame > 12) CurrentFrame -= 12;switch (state){case right: sprite.setTextureRect(IntRect(32*int(CurrentFrame), 64, 28, 28)); break;case left: sprite.setTextureRect(IntRect(32*int(CurrentFrame)+32-8, 64, -28, 28)); break;case up: break;case down: break;case jump: break;case stay: sprite.setTextureRect(IntRect(0, 64, 28, 28)); break;}}Собственно, подскажите как лучше всего сделать. Я думаю, что надо делать class Animation и в конструкторе передавать время, ввести приватную переменную CurrentFrame и с помощью switch’a прослеживать состояния игрока, изменяя анимацию. Буду благодарен любой помощи.
Да, идея верная. Можно так сделать.
Только в конструкторе время передается единожды при создании объекта класса Менеджер Анимации, поэтому тебе нужно время передавать не в конструкторе, а в функции.
Делаешь класс Менеджер Анимации, в нем функции, которые меняют картинки в зависимости от состояния. Помимо этого будет функция update, которая и принимает время. Объект класса менеджер анимации создаешь в int main и передаешь его по ссылке в конструктор игрока например (или другого класса, анимацию которого хочешь сделать). А когда передашь – можешь работать внутри класса , вызывая нужную анимацию в зависимости от состояния персонажа, написав что то типа animation.setAnimation(“jump”); или animation.jump(); (это если у тебя для каждой анимации своя ф-ция в менеджере). 1й вар-т красивее.
и в конце ф-ции update анимируемого класса вызываешь update анимации, как раз таки и передавая то самое время time. (которое приходит в ф-цию update анимируемого объекта).
Такой урок планируется в скором будущем, пока что я код не писал. И сейчас на очереди урок про платформу, меню, стрельбу.В общем не стал себе на ночь голову дурить. Сделал пока что так:
C++123456789// Написал почти в самом конце функции update() класса Player// Переменную CurrentFrame занёс в private: класса PlayerCurrentFrame += 0.005 * time;if (CurrentFrame > 12) CurrentFrame -= 12;if (dx > 0) sprite.setTextureRect(IntRect(32*int(CurrentFrame), 64, 28, 28));// Hе использовал switch(state), т.к. после отжатия кнопки// анимация не останавливаласьif (dx < 0) sprite.setTextureRect(IntRect(32*int(CurrentFrame)+32-8, 64, -28, 28));Завтра доработаю class Animation и сюда выложу.
Видимо не доработал )
Или интернет не оплатил)
Что бы не создавать новую тему, напишу здесь.
По одному из сторонних уроков создал класс анимации, который загружает всю анимацию персонажа из XML-файла.
C++123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143#ifndef ANIM_H#define ANIM_H#include "TinyXML/tinyxml.h"#include <SFML/Graphics.hpp>using namespace sf;class Animation{public:std::vector<IntRect> frames, frames_flip;float currentFrame, speed;bool loop, flip, isPlaying;Sprite sprite;Animation(){currentFrame = 0;isPlaying=true;flip=false;}void tick(float time){if (!isPlaying) return;currentFrame += speed * time;if (currentFrame > frames.size()) { currentFrame -= frames.size();if (!loop) {isPlaying=false; return;}}int i = currentFrame;sprite.setTextureRect( frames[i] );if (flip) sprite.setTextureRect( frames_flip[i] );}};class AnimationManager{public:String currentAnim;std::map<String, Animation> animList;AnimationManager(){}~AnimationManager(){ animList.clear();}void create(String name, Texture &texture, int x, int y, int w, int h, int count, float speed, int step=0, bool Loop=true){Animation a;a.speed = speed;a.loop = Loop;a.sprite.setTexture(texture);a.sprite.setOrigin(0,h);for (int i=0;i<count;i++){a.frames.push_back( IntRect(x+i*step, y, w, h) );a.frames_flip.push_back( IntRect(x+i*step+w, y, -w, h) );}animList[name] = a;currentAnim = name;}//загрузка из файла XMLvoid loadFromXML(std::string fileName,Texture &t){TiXmlDocument animFile(fileName.c_str());animFile.LoadFile();TiXmlElement *head;head = animFile.FirstChildElement("sprites");TiXmlElement *animElement;animElement = head->FirstChildElement("animation");while(animElement){Animation anim;currentAnim = animElement->Attribute("title");int delay = atoi(animElement->Attribute("delay"));anim.speed = 1.0/delay; anim.sprite.setTexture(t);TiXmlElement *cut;cut = animElement->FirstChildElement("cut");while (cut){int x = atoi(cut->Attribute("x"));int y = atoi(cut->Attribute("y"));int w = atoi(cut->Attribute("w"));int h = atoi(cut->Attribute("h"));anim.frames.push_back( IntRect(x,y,w,h) );anim.frames_flip.push_back( IntRect(x+w,y,-w,h) );cut = cut->NextSiblingElement("cut");}anim.sprite.setOrigin(0,anim.frames[0].height);animList[currentAnim] = anim;animElement = animElement->NextSiblingElement("animation");}}void set(String name){currentAnim = name;animList[currentAnim].flip=0;}void draw(RenderWindow &window,int x=0, int y=0){animList[currentAnim].sprite.setPosition(x,y);window.draw( animList[currentAnim].sprite );}void flip(bool b=1) {animList[currentAnim].flip = b;}void tick(float time) {animList[currentAnim].tick(time);}void pause() {animList[currentAnim].isPlaying=false;}void play() {animList[currentAnim].isPlaying=true;}void play(String name) {animList[name].isPlaying=true;}bool isPlaying() {return animList[currentAnim].isPlaying;}float getH() {return animList[currentAnim].frames[0].height;}float getW() {return animList[currentAnim].frames[0].width;}};#endif ANIM_HВ классе int main() прописал функцию вызова
C++12AnimationManager anim;anim.loadFromXML("files/hero_anim.xml",_);но не как не могу понять что нужно прописать во втором аргументе(где знак подчеркивания). Пробовал напрямую обратится к Player p…., но выдает ошибки. Каким образом можно реализовать эту функцию что бы работало все корректно?
ЗЫ: код практически такой, как в 20м уроке.судя по коду нужно передать туда текстуру по ссылке
Насколько я понял передавать картинку не получится, а нужно именно спрайт?
C++12Image heroImage;heroImage.loadFromFile("files/hero.png");C++1234Texture heroImage;heroImage.loadFromFile("files/hero.png");AnimationManager anim;anim.loadFromXML("files/hero_anim.xml",heroImage);И наверно прийдется делать как во втором варианте и не много изменять класс Player под работу с текстурой, а не изображением.
Поправьте меня, если я не прав.
нужно передавать именно текстуру как во 2 ом вар-те. но мы можем загрузить в текстуру сам Image объект. посмотри урок вывод картинки на экран (3-й вроде) – там как раз двумя вариантами грузили картинки
Реееебятттта, ну ввывод какой? пишите правильный ответ
я остановился 14 уроке, дальше пролистал и заметил что все поменялось, хочу оставить игру квестом! КАк теперь быть, как загрузить туда карту, не преврашая игру в платформер? И там управление анимаций отсуствует! Помогите!
решил как-то! Воовшем вот так,
в class Entity написал
float CurrentFrame = 0;//хранит текущий кадр
потом вот так
Player(Image &image, String Name, Level &lev, float X, float Y, int W, int H) :Entity(image, Name, X, Y, W, H){
playerScore = 0; state = stay; obj = lev.GetAllObjects();//инициализируем.получаем все объекты для взаимодействия персонажа с картой
if (name == “Player1″){
sprite.setTextureRect(IntRect(32, 0, 32, 32)); здесь дал координату для одного кадраПОтом вот
void control(){
if (Keyboard::isKeyPressed){
if (Keyboard::isKeyPressed(Keyboard::Left)) {
state = left; speed = 0.1;
CurrentFrame += 0.005 * 1, 1;
if (CurrentFrame > 3) CurrentFrame -= 3;
sprite.setTextureRect(IntRect(32 * int(CurrentFrame), 32, 32, 32));}
if (Keyboard::isKeyPressed(Keyboard::Right)) {
state = right; speed = 0.1;
CurrentFrame += 0.005*1,1;
if (CurrentFrame > 3) CurrentFrame -= 3;
sprite.setTextureRect(IntRect(32 * int(CurrentFrame), 64, 32, 32));
}if ((Keyboard::isKeyPressed(Keyboard::Up)) && (onGround)) {
state = jump; dy = -0.5; onGround = false ;}
if (Keyboard::isKeyPressed(Keyboard::Down)) {
state = down;
sprite.setTextureRect(IntRect(32, 0, 32, 32));
}
}
}Все анимация работает!
-
C++123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268#include <SFML/Graphics.hpp>//#include "map.h"#include "view.h"#include <iostream>#include <sstream>#include "mission.h"#include "iostream"#include "level.h"#include <vector>#include <list>#include "TinyXML/tinyxml.h"using namespace sf;////////////////////////////////////Общий класс-родитель//////////////////////////class Entity {public:std::vector<Object> obj;//вектор объектов картыfloat dx, dy, x, y, speed, moveTimer;int w, h, health;bool life, isMove, onGround;float CurrentFrame = 0;//хранит текущий кадрTexture texture;Sprite sprite;String name;Entity(Image &image, String Name, float X, float Y, int W, int H){x = X; y = Y; w = W; h = H; name = Name; moveTimer = 0;speed = 0; health = 100; dx = 0; dy = 0;life = true; onGround = false; isMove = false;texture.loadFromImage(image);sprite.setTexture(texture);sprite.setOrigin(w / 2, h / 2);}FloatRect getRect(){//ф-ция получения прямоугольника. его коорд,размеры (шир,высот).return FloatRect(x, y, w, h);//эта ф-ция нужна для проверки столкновений}};////////////////////////////////////////////////////КЛАСС ИГРОКА////////////////////////class Player :public Entity {public:enum { left, right, up, down, jump, stay } state;int playerScore;Player(Image &image, String Name, Level &lev, float X, float Y, int W, int H) :Entity(image, Name, X, Y, W, H){playerScore = 0; state = stay; obj = lev.GetAllObjects();//инициализируем.получаем все объекты для взаимодействия персонажа с картойif (name == "Player1"){sprite.setTextureRect(IntRect(32, 0, 32, 32));}}void control(){if (Keyboard::isKeyPressed){if (Keyboard::isKeyPressed(Keyboard::Left)) {state = left; speed = 0.1;CurrentFrame += 0.005 * 1, 1;if (CurrentFrame > 3) CurrentFrame -= 3;sprite.setTextureRect(IntRect(32 * int(CurrentFrame), 32, 32, 32));}if (Keyboard::isKeyPressed(Keyboard::Right)) {state = right; speed = 0.1;CurrentFrame += 0.005*1,1;if (CurrentFrame > 3) CurrentFrame -= 3;sprite.setTextureRect(IntRect(32 * int(CurrentFrame), 64, 32, 32));}if ((Keyboard::isKeyPressed(Keyboard::Up)) && (onGround)) {state = jump; dy = -0.5; onGround = false ;}if (Keyboard::isKeyPressed(Keyboard::Down)) {state = down;sprite.setTextureRect(IntRect(32, 0, 32, 32));}}}void checkCollisionWithMap(float Dx, float Dy){/*вариант взаимодействия со старой картой.будет удален на след урокеfor (int i = y / 32; i < (y + h) / 32; i++)//проходимся по элементам картыfor (int j = x / 32; j<(x + w) / 32; j++){if (TileMap[i][j] == '0'){if (Dy>0){ y = i * 32 - h; dy = 0; onGround = true; }if (Dy<0){ y = i * 32 + 32; dy = 0; }if (Dx>0){ x = j * 32 - w; }if (Dx<0){ x = j * 32 + 32; }}}*/for (int i = 0; i<obj.size(); i++)//проходимся по объектамif (getRect().intersects(obj[i].rect))//проверяем пересечение игрока с объектом{if (obj[i].name == "solid")//если встретили препятствие{if (Dy>0) { y = obj[i].rect.top - h; dy = 0; onGround = true; }if (Dy<0) { y = obj[i].rect.top + obj[i].rect.height; dy = 0; }if (Dx>0) { x = obj[i].rect.left - w; }if (Dx<0) { x = obj[i].rect.left + obj[i].rect.width; }}}}void update(float time){control();switch (state){case right:dx = speed; break;case left:dx = -speed; break;case up: break;case down: dx = 0; break;case stay: break;}x += dx*time;checkCollisionWithMap(dx, 0);y += dy*time;checkCollisionWithMap(0, dy);sprite.setPosition(x + w / 2, y + h / 2);if (health <= 0){ life = false; }if (!isMove){ speed = 0; }setPlayerCoordinateForView(x, y);if (life) { setPlayerCoordinateForView(x, y); }dy = dy + 0.0015*time;}};class Enemy :public Entity{public:Enemy(Image &image, String Name, Level &lvl, float X, float Y, int W, int H) :Entity(image, Name, X, Y, W, H){obj = lvl.GetObjects("solid");//инициализируем.получаем нужные объекты для взаимодействия врага с картойif (name == "EasyEnemy"){sprite.setTextureRect(IntRect(0, 0, w, h));dx = 0.1;}}void checkCollisionWithMap(float Dx, float Dy){/*вариант взаимодействия со старой картойfor (int i = y / 32; i < (y + h) / 32; i++)for (int j = x / 32; j<(x + w) / 32; j++){if (TileMap[i][j] == '0'){if (Dy>0){ y = i * 32 - h; }if (Dy<0){ y = i * 32 + 32; }if (Dx>0){ x = j * 32 - w; dx = -0.1; sprite.scale(-1, 1); }if (Dx<0){ x = j * 32 + 32; dx = 0.1; sprite.scale(-1, 1); }}}*/for (int i = 0; i<obj.size(); i++)//проходимся по объектамif (getRect().intersects(obj[i].rect))//проверяем пересечение игрока с объектом{//if (obj[i].name == "solid"){//если встретили препятствие (объект с именем solid)if (Dy>0) { y = obj[i].rect.top - h; dy = 0; onGround = true; }if (Dy<0) { y = obj[i].rect.top + obj[i].rect.height; dy = 0; }if (Dx>0) { x = obj[i].rect.left - w; dx = -0.1; sprite.scale(-1, 1); }if (Dx<0) { x = obj[i].rect.left + obj[i].rect.width; dx = 0.1; sprite.scale(-1, 1); }//}}}void update(float time){if (name == "EasyEnemy"){//moveTimer += time;if (moveTimer>3000){ dx *= -1; moveTimer = 0; }//меняет направление примерно каждые 3 секcheckCollisionWithMap(dx, 0);x += dx*time;sprite.setPosition(x + w / 2, y + h / 2);if (health <= 0){ life = false; }}}};int main(){RenderWindow window(VideoMode(640, 480), "Lesson 22. kychka-pc.ru");view.reset(FloatRect(0, 0, 640, 480));Level lvl;//создали экземпляр класса уровеньlvl.LoadFromFile("map.tmx");//загрузили в него карту, внутри класса с помощью методов он ее обработает.Image quest_image;quest_image.loadFromFile("images/missionbg.jpg");quest_image.createMaskFromColor(Color(0, 0, 0));Texture quest_texture;quest_texture.loadFromImage(quest_image);Sprite s_quest;s_quest.setTexture(quest_texture);s_quest.setTextureRect(IntRect(0, 0, 340, 510)); //приведение типов, размеры картинки исходныеs_quest.setScale(0.6f, 0.6f);//чуть уменьшили картинку, => размер стал меньше/*Старый вариант карты, больше не пригодитсяImage map_image;map_image.loadFromFile("images/map.png");Texture map;map.loadFromImage(map_image);Sprite s_map;s_map.setTexture(map);*/Image heroImage;heroImage.loadFromFile("images/hero.png");Sprite herosprite;bool showMissionText = true;//логическая переменная, отвечающая за появление текста миссии на экранеImage easyEnemyImage;easyEnemyImage.loadFromFile("images/shamaich.png");easyEnemyImage.createMaskFromColor(Color(255, 0, 0));Object player = lvl.GetObject("player");//объект игрока на нашей карте.задаем координаты игроку в начале при помощи негоObject easyEnemyObject = lvl.GetObject("easyEnemy");//объект легкого врага на нашей карте.задаем координаты игроку в начале при помощи негоPlayer p(heroImage, "Player1", lvl, player.rect.left, player.rect.top, 40, 30);//передаем координаты прямоугольника player из карты в координаты нашего игрокаEnemy easyEnemy(easyEnemyImage, "EasyEnemy", lvl, easyEnemyObject.rect.left, easyEnemyObject.rect.top, 200, 97);//передаем координаты прямоугольника easyEnemy из карты в координаты нашего врагаClock clock;while (window.isOpen()){float time = clock.getElapsedTime().asMicroseconds();clock.restart();time = time / 800;Event event;while (window.pollEvent(event)){if (event.type == sf::Event::Closed)window.close();}p.update(time);easyEnemy.update(time);window.setView(view);window.clear(Color(77, 83, 140));lvl.Draw(window);//рисуем новую карту/*рисование старой карты будет удалено на след урокеfor (int i = 0; i < HEIGHT_MAP; i++)for (int j = 0; j < WIDTH_MAP; j++){if (TileMap[i][j] == ' ') s_map.setTextureRect(IntRect(0, 0, 32, 32));if (TileMap[i][j] == 's') s_map.setTextureRect(IntRect(32, 0, 32, 32));if ((TileMap[i][j] == '0')) s_map.setTextureRect(IntRect(64, 0, 32, 32));if ((TileMap[i][j] == 'f')) s_map.setTextureRect(IntRect(96, 0, 32, 32));if ((TileMap[i][j] == 'h')) s_map.setTextureRect(IntRect(128, 0, 32, 32));s_map.setPosition(j * 32, i * 32);window.draw(s_map);}*/window.draw(easyEnemy.sprite);window.draw(p.sprite);window.display();}return 0;}
<!–more–>
-
-
АвторСообщения
Для ответа в этой теме необходимо авторизоваться.