SFML и C++ Уроки \ Разработка игр › Форумы › SFML Graphics › Проблема взаимодействия персонажа с картой.
Помечено: Взаимодействие персонажа с картой, гравитация, прыжок
В этой теме 7 ответов, 3 участника, последнее обновление Nodira 7 года/лет, 6 мес. назад.
-
АвторСообщения
-
Здравствуйте! Уже какой день не получается исправить некорректное взаимодействие с картой, поэтому обращаюсь к вам за помощью. Пожалуйста, помогите!
Я делаю платформер. Вот в чём проблема: персонаж при падении на землю, проваливается сквозь тайл земли и выталкивается наружу снова и снова.
Создаю класс в отдельном .cpp файле, а описываю его в файле .h.
Код по началу был другим, но и с ним были проблемы похожего рода, потом я попробовал в точности воспроизвести Ваш код:
C++123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106Player::Player(String file, IntRect r){speed = 0;isMove = false;this->file = file;onGround = false;image.loadFromFile("images/" + file);texture.loadFromImage(image);sprite.setTexture(texture);this->rect.left = 100;this->rect.top = 400;this->rect.width = r.width;this->rect.height = r.height;sprite.setTextureRect(IntRect(r.left, r.top, rect.width, rect.height));sprite.setPosition(rect.left, rect.top);}void Player::update(float time, String* TileMap){control();switch (state){case right: dx = speed; break;case left: dx = -speed; break;case up: break;case down: break;case jump: break;case stay: break;}rect.left += dx*time;checkCollisionWithMap(dx, 0,TileMap);rect.top += dy*time;checkCollisionWithMap(0, dy,TileMap);if (!isMove){speed = 0;}sprite.setPosition(rect.left,rect.top);dy = dy + 0.0015*time;}void Player::checkCollisionWithMap(float dx,float dy,String* TileMap){for (int i = rect.top / 70; i < (rect.top + rect.height) / 70; i++)for (int j = rect.left / 70; j < (rect.left +rect.width) / 70; j++){if (TileMap[i][j] == 'b'){if (dy > 0){rect.top = i * 70 - rect.height;dy = 0;onGround = true;}if (dy < 0){rect.top = i * 70 +70;dy = 0;}if (dx > 0){rect.left = j * 70 - rect.width;}if (dx < 0){rect.left = j * 70 + 70;}}}}void Player::control(){if (Keyboard::isKeyPressed(Keyboard::Left)) {state = left;speed = 0.1;//currentFrame += 0.005*time;//if (currentFrame > 3) currentFrame -= 3;//p.sprite.setTextureRect(IntRect(96 * int(currentFrame), 135, 96, 54));}if (Keyboard::isKeyPressed(Keyboard::Right)) {state = right;speed = 0.4;// currentFrame += 0.005*time;// if (currentFrame > 3) currentFrame -= 3;// p.sprite.setTextureRect(IntRect(96 * int(currentFrame), 232, 96, 54));}if ((Keyboard::isKeyPressed(Keyboard::Up)) && (onGround)) {state = jump; dy = -0.5; onGround = false;//то состояние равно прыжок,прыгнули и сообщили, что мы не на земле//currentFrame += 0.005*time;//if (currentFrame > 3) currentFrame -= 3;//p.sprite.setTextureRect(IntRect(96 * int(currentFrame), 307, 96, 96));}if (Keyboard::isKeyPressed(Keyboard::Down)) {state = down;speed = 0.4;//currentFrame += 0.005*time;//if (currentFrame > 3) currentFrame -= 3;//p.sprite.setTextureRect(IntRect(96 * int(currentFrame), 0, 96, 96));}}к сожалению сейчас времени совсем нет, завалило делами даже на форуме посидеть не могу( посмотрите урок про пересечение прямоугольников (25ый вроде). и ещё посмотрите урок 11.
у вас здесь вижу вроде всё норм, единственно подозрение вызывает строка после (if Dy </>0), где вы сначала прибавляете +70, а затем почему то в аналогичном случае вычитаете height. возможно от этого одно значение больше другого и происходит бесконечное плавание вверх вниз.
Спасибо, что находите хоть какое-то время, чтобы ответить! Я попробовал вывести Dy и оказывается, что он постоянно растёт, т.е. персонаж постоянно проваливается. Но пока не смог исправить эту проблему:(
персонаж и должен постоянно проваливаться. он же притягивается к земле типа. главное вытолкнуть его правильно и убрать проваливание когда он на земле уже..
Уже догнал… А в чём ещё проблема может быть? Может ли размер тайла земли играть какую-то роль?
Да, конечно. Нужно вычитать размер тайла земли, чтобы вытолкнуть. если есть где то setOrigin – попробуйте его пока что убрать везде и сделать без него
Доброго времени суток. У меня проблема при взаимодействии с картой. точнее когда я, в метод update добавляю метод interactionWithMap(), то у меня при запуске появляется окно и все норм, но как только я нажимаю какую либо из клавиш, появляется какая то непонятная штука
Посмотрите пожалуйста, а то я не могу найти причину:
#include <SFML/Graphics.hpp>
#include “map.h”
#include”view.h”
using namespace sf;
//класс игрока//
class Player { // класс Игрока
private: float x, y = 0;
public:
bool onGround, life;
float w, h, dx, dy, speed; //координаты игрока х и у, высота ширина, ускорение (по х и по у), сама скорость
int dir; //направление (direction) движения игрока
String File; //файл с расширением
Image image;//сфмл изображение
Texture texture;//сфмл текстура
Sprite sprite;//сфмл спрайт
Player(String F, float X, float Y, float W, float H) { //Конструктор с параметрами(формальными) для класса Player. При создании объекта класса мы будем задавать имя файла, координату Х и У, ширину и высоту
dx = 0; dy = 0; speed = 0; dir = 0;
File = F;//имя файла+расширение
w = W; h = H;//высота и ширина
image.loadFromFile(“images/” + File);//запихиваем в image наше изображение вместо File мы передадим то, что пропишем при создании объекта. В нашем случае “hero.png” и получится запись идентичная image.loadFromFile(“images/hero/png”);
image.createMaskFromColor(Color(41, 33, 59));//убираем ненужный темно-синий цвет, эта тень мне показалась не красивой.
texture.loadFromImage(image);//закидываем наше изображение в текстуру
sprite.setTexture(texture);//заливаем спрайт текстурой
x = X; y = Y;//координата появления спрайта
sprite.setTextureRect(IntRect(0, 0, w, h));
sprite.setTextureRect(IntRect(0, 0, w, h)); //Задаем спрайту один прямоугольник для вывода одного льва, а не кучи львов сразу. IntRect – приведение типов
}
void update(float time) //функция “оживления” объекта класса. update – обновление. принимает в себя время SFML , вследствие чего работает бесконечно, давая персонажу движение.
{
switch (dir)//реализуем поведение в зависимости от направления. (каждая цифра соответствует направлению)
{
case 0: dx = speed; dy = 0; break;//по иксу задаем положительную скорость, по игреку зануляем. получаем, что персонаж идет только вправо
case 1: dx = -speed; dy = 0; break;//по иксу задаем отрицательную скорость, по игреку зануляем. получается, что персонаж идет только влево
case 2: dx = 0; dy = -speed; break;//по иксу задаем нулевое значение, по игреку отрицательное. получается, что персонаж идет только вверх
}x += dx*time;// наше ускорение на время получаем смещение координат и как следствие движение
y += dy*time;//аналогично по игрекуspeed = 0;//зануляем скорость, чтобы персонаж остановился.
sprite.setPosition(x, y); //выводим спрайт в позицию x y , посередине. бесконечно выводим в этой функции, иначе бы наш спрайт стоял на месте.
interactionWithMap();//вызываем функцию, отвечающую за взаимодействие с картой
}float getplayercoordinateX() { //этим методом будем забирать координату Х
return x;
}
float getplayercoordinateY() { //этим методом будем забирать координату Y
return y;
}
void interactionWithMap()//ф-ция взаимодействия с картой
{for (int k = y / 32; k < (y + h)/32; k++)//проходимся по тайликам, контактирующим с игроком, то есть по всем квадратикам размера 32*32, которые мы окрашивали в 9 уроке. про условия читайте ниже.
for (int l = x / 32; l < (x + w)/32; l++)//икс делим на 32, тем самым получаем левый квадратик, с которым персонаж соприкасается. (он ведь больше размера 32*32, поэтому может одновременно стоять на нескольких квадратах). А j<(x + w) / 32 – условие ограничения координат по иксу. то есть координата самого правого квадрата, который соприкасается с персонажем. таким образом идем в цикле слева направо по иксу, проходя по от левого квадрата (соприкасающегося с героем), до правого квадрата (соприкасающегося с героем)
{
if (TileMap[k][l] == ‘0’)//если наш квадратик соответствует символу 0 (стена), то проверяем “направление скорости” персонажа:
{
if (dy>0)//если мы шли вниз,
{
y = k * 32 – h;//то стопорим координату игрек персонажа. сначала получаем координату нашего квадратика на карте(стены) и затем вычитаем из высоты спрайта персонажа.
}
if (dy<0)
{
y = k * 32 + 32;//аналогично с ходьбой вверх. dy<0, значит мы идем вверх (вспоминаем координаты паинта)
}
if (dx>0)
{
x = l * 32 – w;//если идем вправо, то координата Х равна стена (символ 0) минус ширина персонажа
}
if (dx < 0)
{
x = l * 32 + 32;//аналогично идем влево
}
}}
}
};
int main() {RenderWindow window(VideoMode(640, 480), “SFML Application”);
view.reset(sf::FloatRect(0, 0, 640, 480));//размер “вида” камеры при создании объекта вида камеры. (потом можем менять как хотим) Что то типа инициализации.
Image map_image;//объект изображения для карты
map_image.loadFromFile(“images/map.png”);//загружаем файл для карты
Texture map;//текстура карты
map.loadFromImage(map_image);//заряжаем текстуру картинкой
Sprite s_map;//создаём спрайт для карты
s_map.setTexture(map);//заливаем текстуру спрайтом
Player p(“hero.png”, 30, 388, 64.0, 64.0);//создаем объект p класса player,задаем “hero.png” как имя файла+расширение, далее координата Х,У, ширина, высота
float CurrentFrame = 0;//хранит текущий кадр
Clock clock;while (window.isOpen()) {
float time = clock.getElapsedTime().asMicroseconds();
clock.restart();
time = time / 800;
Event event;
while (window.pollEvent(event)) {
if (event.type == Event::Closed)
window.close();
}
if (Keyboard::isKeyPressed(Keyboard::Left)) {
p.dir = 1; p.speed = 0.2;//dir =1 – направление вверх, speed =0.2 – скорость движения. Заметьте – время мы уже здесь ни на что не умножаем и нигде не используем каждый раз
CurrentFrame += 0.008*time;
if (CurrentFrame > 8) CurrentFrame -= 8;
p.sprite.setTextureRect(IntRect(64 * int(CurrentFrame), 128, 64, 64)); //через объект p класса player меняем спрайт, делая анимацию (используя оператор точку)
}
if ((Keyboard::isKeyPressed(Keyboard::Right) || (Keyboard::isKeyPressed(Keyboard::D)))) {
p.dir = 0; p.speed = 0.2;//направление вправо, см выше
CurrentFrame += 0.008*time;
if (CurrentFrame > 8) CurrentFrame -= 8;
p.sprite.setTextureRect(IntRect(64 * int(CurrentFrame), 0, 64, 64)); //через объект p класса player меняем спрайт, делая анимацию (используя оператор точку)
}
getPlayerCoorinateForView(p.getplayercoordinateX(), p.getplayercoordinateY());//передаем координаты игрока в функцию управления камерой
p.update(time);//оживляем объект p класса Player с помощью времени sfml, передавая время в качестве параметра функции update. благодаря этому персонаж может двигаться
//viewmap(time);//функция скроллинга карты, передаем ей время sfml
window.setView(view);//”оживляем” камеру в окне sfml
window.clear();
/////////////////////////////Рисуем карту/////////////////////
for (int i = 0; i < H_MAP; i++)
for (int j = 0; j < W_MAP; j++)
{
if (TileMap[i][j] == ‘ ‘) s_map.setTextureRect(IntRect(32, 0, 32, 32)); //если встретили символ пробел, то рисуем 1й квадратик
if (TileMap[i][j] == ‘s’) s_map.setTextureRect(IntRect(0, 0, 32, 32));//если встретили символ s, то рисуем 2й квадратик
if ((TileMap[i][j] == ‘0’)) s_map.setTextureRect(IntRect(64, 0, 32, 32));//если встретили символ 0, то рисуем 3й квадратик
s_map.setPosition(j * 32, i * 32);//по сути раскидывает квадратики, превращая в карту. то есть задает каждому из них позицию. если убрать, то вся карта нарисуется в одном квадрате 32*32 и мы увидим один квадратwindow.draw(s_map);//рисуем квадратики на экран
}
window.draw(p.sprite);//рисуем спрайт объекта p класса player
window.display();
}}
Вложения:
You must be logged in to view attached files.C++123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136#include <SFML/Graphics.hpp>#include "map.h"#include"view.h"using namespace sf;//класс игрока//class Player { // класс Игрокаprivate: float x, y = 0;public:bool onGround, life;float w, h, dx, dy, speed; //координаты игрока х и у, высота ширина, ускорение (по х и по у), сама скоростьint dir; //направление (direction) движения игрокаString File; //файл с расширениемImage image;//сфмл изображениеTexture texture;//сфмл текстураSprite sprite;//сфмл спрайтPlayer(String F, float X, float Y, float W, float H) { //Конструктор с параметрами(формальными) для класса Player. При создании объекта класса мы будем задавать имя файла, координату Х и У, ширину и высотуdx = 0; dy = 0; speed = 0; dir = 0;File = F;//имя файла+расширениеw = W; h = H;//высота и ширинаimage.loadFromFile("images/" + File);//запихиваем в image наше изображение вместо File мы передадим то, что пропишем при создании объекта. В нашем случае "hero.png" и получится запись идентичная image.loadFromFile("images/hero/png");image.createMaskFromColor(Color(41, 33, 59));//убираем ненужный темно-синий цвет, эта тень мне показалась не красивой.texture.loadFromImage(image);//закидываем наше изображение в текстуруsprite.setTexture(texture);//заливаем спрайт текстуройx = X; y = Y;//координата появления спрайтаsprite.setTextureRect(IntRect(0, 0, w, h));sprite.setTextureRect(IntRect(0, 0, w, h)); //Задаем спрайту один прямоугольник для вывода одного льва, а не кучи львов сразу. IntRect - приведение типов}void update(float time) //функция "оживления" объекта класса. update - обновление. принимает в себя время SFML , вследствие чего работает бесконечно, давая персонажу движение.{switch (dir)//реализуем поведение в зависимости от направления. (каждая цифра соответствует направлению){case 0: dx = speed; dy = 0; break;//по иксу задаем положительную скорость, по игреку зануляем. получаем, что персонаж идет только вправоcase 1: dx = -speed; dy = 0; break;//по иксу задаем отрицательную скорость, по игреку зануляем. получается, что персонаж идет только влевоcase 2: dx = 0; dy = -speed; break;//по иксу задаем нулевое значение, по игреку отрицательное. получается, что персонаж идет только вверх}x += dx*time;// наше ускорение на время получаем смещение координат и как следствие движениеy += dy*time;//аналогично по игрекуspeed = 0;//зануляем скорость, чтобы персонаж остановился.sprite.setPosition(x, y); //выводим спрайт в позицию x y , посередине. бесконечно выводим в этой функции, иначе бы наш спрайт стоял на месте.interactionWithMap();//вызываем функцию, отвечающую за взаимодействие с картой}float getplayercoordinateX() { //этим методом будем забирать координату Хreturn x;}float getplayercoordinateY() { //этим методом будем забирать координату Yreturn y;}void interactionWithMap()//ф-ция взаимодействия с картой{for (int k = y / 32; k < (y + h)/32; k++)//проходимся по тайликам, контактирующим с игроком, то есть по всем квадратикам размера 32*32, которые мы окрашивали в 9 уроке. про условия читайте ниже.for (int l = x / 32; l < (x + w)/32; l++)//икс делим на 32, тем самым получаем левый квадратик, с которым персонаж соприкасается. (он ведь больше размера 32*32, поэтому может одновременно стоять на нескольких квадратах). А j<(x + w) / 32 - условие ограничения координат по иксу. то есть координата самого правого квадрата, который соприкасается с персонажем. таким образом идем в цикле слева направо по иксу, проходя по от левого квадрата (соприкасающегося с героем), до правого квадрата (соприкасающегося с героем){if (TileMap[k][l] == '0')//если наш квадратик соответствует символу 0 (стена), то проверяем "направление скорости" персонажа:{if (dy>0)//если мы шли вниз,{y = k * 32 - h;//то стопорим координату игрек персонажа. сначала получаем координату нашего квадратика на карте(стены) и затем вычитаем из высоты спрайта персонажа.}if (dy<0){y = k * 32 + 32;//аналогично с ходьбой вверх. dy<0, значит мы идем вверх (вспоминаем координаты паинта)}if (dx>0){x = l * 32 - w;//если идем вправо, то координата Х равна стена (символ 0) минус ширина персонажа}if (dx < 0){x = l * 32 + 32;//аналогично идем влево}}}}};int main() {RenderWindow window(VideoMode(640, 480), "SFML Application");view.reset(sf::FloatRect(0, 0, 640, 480));//размер "вида" камеры при создании объекта вида камеры. (потом можем менять как хотим) Что то типа инициализации.Image map_image;//объект изображения для картыmap_image.loadFromFile("images/map.png");//загружаем файл для картыTexture map;//текстура картыmap.loadFromImage(map_image);//заряжаем текстуру картинкойSprite s_map;//создаём спрайт для картыs_map.setTexture(map);//заливаем текстуру спрайтомPlayer p("hero.png", 30, 388, 64.0, 64.0);//создаем объект p класса player,задаем "hero.png" как имя файла+расширение, далее координата Х,У, ширина, высотаfloat CurrentFrame = 0;//хранит текущий кадрClock clock;while (window.isOpen()) {float time = clock.getElapsedTime().asMicroseconds();clock.restart();time = time / 800;Event event;while (window.pollEvent(event)) {if (event.type == Event::Closed)window.close();}if (Keyboard::isKeyPressed(Keyboard::Left)) {p.dir = 1; p.speed = 0.2;//dir =1 - направление вверх, speed =0.2 - скорость движения. Заметьте - время мы уже здесь ни на что не умножаем и нигде не используем каждый разCurrentFrame += 0.008*time;if (CurrentFrame > 8) CurrentFrame -= 8;p.sprite.setTextureRect(IntRect(64 * int(CurrentFrame), 128, 64, 64)); //через объект p класса player меняем спрайт, делая анимацию (используя оператор точку)}if ((Keyboard::isKeyPressed(Keyboard::Right) || (Keyboard::isKeyPressed(Keyboard::D)))) {p.dir = 0; p.speed = 0.2;//направление вправо, см вышеCurrentFrame += 0.008*time;if (CurrentFrame > 8) CurrentFrame -= 8;p.sprite.setTextureRect(IntRect(64 * int(CurrentFrame), 0, 64, 64)); //через объект p класса player меняем спрайт, делая анимацию (используя оператор точку)}getPlayerCoorinateForView(p.getplayercoordinateX(), p.getplayercoordinateY());//передаем координаты игрока в функцию управления камеройp.update(time);//оживляем объект p класса Player с помощью времени sfml, передавая время в качестве параметра функции update. благодаря этому персонаж может двигаться//viewmap(time);//функция скроллинга карты, передаем ей время sfmlwindow.setView(view);//"оживляем" камеру в окне sfmlwindow.clear();/////////////////////////////Рисуем карту/////////////////////for (int i = 0; i < H_MAP; i++)for (int j = 0; j < W_MAP; j++){if (TileMap[i][j] == ' ') s_map.setTextureRect(IntRect(32, 0, 32, 32)); //если встретили символ пробел, то рисуем 1й квадратикif (TileMap[i][j] == 's') s_map.setTextureRect(IntRect(0, 0, 32, 32));//если встретили символ s, то рисуем 2й квадратикif ((TileMap[i][j] == '0')) s_map.setTextureRect(IntRect(64, 0, 32, 32));//если встретили символ 0, то рисуем 3й квадратикs_map.setPosition(j * 32, i * 32);//по сути раскидывает квадратики, превращая в карту. то есть задает каждому из них позицию. если убрать, то вся карта нарисуется в одном квадрате 32*32 и мы увидим один квадратwindow.draw(s_map);//рисуем квадратики на экран}window.draw(p.sprite);//рисуем спрайт объекта p класса playerwindow.display();}} -
АвторСообщения
Для ответа в этой теме необходимо авторизоваться.