SFML и C++ Уроки \ Разработка игр › Форумы › SFML System, Window и другие вопросы › Проблемсы с реализацией выноса классов в файлы
В этой теме 13 ответов, 4 участника, последнее обновление MatrixDeity 8 года/лет, 3 мес. назад.
-
АвторСообщения
-
Всем привет. Знаю, что подобная тема уже была, но мне не помогла она, поэтому создал еще одну похожую, сори.
Решил вынести классы в отдельные файлы.
Возникла проблема с подключением map.h
error C2065: ‘TileMap’ : undeclared identifier
Вот как я подключаю файлыplayer.h
#include <SFML/Graphics.hpp>
using namespace sf;
player.cpp
#include “player.h”
и дальше все функции.main.cpp
#include <SFML/Graphics.hpp>
#include “map.h”
#include “view.h”
#include “player.h”
using namespace sf;Если полключать map.h в player.h, а в main.cpp убрать, то fatal error LNK1169: one or more multiply defined symbols found
Привет. Хорошо бы увидеть полный листинг. Чтобы иметь полную картину мира, так сказать
P.S. Пожалуйста, используй теги форматирования для оформления кода. Это значительно повышает удобочитаемость поста.
C++1234567891011121314151617181920212223242526272829#include <SFML/Graphics.hpp>using namespace sf;class Player{private: float x, y;public:float w, h, dx, dy, speed;int dir;String File;Image image;Texture texture;Sprite sprite;Player(String F, float X, float Y, float W, float H);void update(float time);void interactionWithMap();float Player::getplayercoordinateX(){return x;}float Player::getplayercoordinateY(){return y;}};C++123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566#include "player.h"Player::Player(String F, float X, float Y, float W, float H){File = F;dx = 0;dy = 0;speed = 0;y = 0;w = W; h = H;image.loadFromFile("images/" + File);image.createMaskFromColor(Color(252, 0, 252));texture.loadFromImage(image);sprite.setTexture(texture);x = X; y = Y;sprite.setTextureRect(IntRect(0, 0, w, h));}void Player::update(float time){switch (dir){case 0: dx = speed; dy = 0; break;case 1: dx = -speed; dy = 0; break;case 2: dx = 0; dy = speed; break;case 3: dx = 0; dy = -speed; break;}x += dx*time;y += dy*time;speed = 0;sprite.setPosition(x,y);interactionWithMap();//вызываем функцию, отвечающую за взаимодействие с картой}void Player::interactionWithMap()//ф-ция взаимодействия с картой{for (int i = y / 32; i < (y + h) / 32; i++)//проходимся по всей карте, то есть по всем квадратикам размера 32*32, которые мы окрашивали в 9 уроке. про условия читайте ниже.for (int j = x / 32; j<(x + w) / 32; j++)//икс делим на 32, тем самым получаем левый квадратик, с которым персонаж соприкасается. (он ведь больше размера 32*32, поэтому может одновременно стоять на нескольких квадратах). А j<(x + w) / 32 - условие ограничения координат по иксу. то есть координата самого правого квадрата, который соприкасается с персонажем. таким образом идем в цикле слева направо по иксу, проходя по от левого квадрата (соприкасающегося с героем), до правого квадрата (соприкасающегося с героем){if (TileMap[i][j] == '0')//если наш квадратик соответствует символу 0 (стена), то проверяем "направление скорости" персонажа:{if (dy > 0)//если мы шли вниз,{y = i * 32 - h;//то стопорим координату игрек персонажа. сначала получаем координату нашего квадратика на карте(стены) и затем вычитаем из высоты спрайта персонажа.}if (dy < 0){y = i * 32 + 32;//аналогично с ходьбой вверх. dy<0, значит мы идем вверх (вспоминаем координаты паинта)}if (dx > 0){x = j * 32 - w;//если идем вправо, то координата Х равна стена (символ 0) минус ширина персонажа}if (dx < 0){x = j * 32 + 32;//аналогично идем влево}}if (TileMap[i][j] == 's') { //если символ равен 's' (камень)x = 300; y = 300;//какое то действие... например телепортация герояTileMap[i][j] = ' ';//убираем камень, типа взяли бонус. можем и не убирать, кстати.}}}C++123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111#include <SFML/Graphics.hpp>#include "map.h"#include "view.h"#include "player.h"using namespace sf;int main(){RenderWindow window(sf::VideoMode(640, 480), "Lesson 11. kychka-pc.ru");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("mage.png", 250, 250, 34.0, 50.0);float CurrentFrame = 0;Clock clock;char atkPosition = 's';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();}float coordinatePlayerX, coordinatePlayerY = 0;coordinatePlayerX = p.getplayercoordinateX();coordinatePlayerY = p.getplayercoordinateY();///////////////////////////////////////////Управление персонажем с анимацией////////////////////////////////////////////////////////////////////////if (Keyboard::isKeyPressed(Keyboard::A)){p.dir = 1; p.speed = 0.1;atkPosition = 'l';CurrentFrame += 0.005*time;if (CurrentFrame > 4) CurrentFrame -= 4;p.sprite.setTextureRect(IntRect(64 * int(CurrentFrame)+12, 384, 34, 38));}if (Keyboard::isKeyPressed(Keyboard::D)){p.dir = 0; p.speed = 0.1;atkPosition = 'r';CurrentFrame += 0.005*time;if (CurrentFrame > 4) CurrentFrame -= 4;p.sprite.setTextureRect(IntRect(64 * int(CurrentFrame), 128, 34, 38));}if (Keyboard::isKeyPressed(Keyboard::W)){p.dir = 3; p.speed = 0.1;atkPosition = 't';CurrentFrame += 0.005*time;if (CurrentFrame > 4) CurrentFrame -= 4;p.sprite.setTextureRect(IntRect(64 * int(CurrentFrame), 0, 32, 50));}if (Keyboard::isKeyPressed(Keyboard::S)){p.dir = 2; p.speed = 0.1;atkPosition = 'd';CurrentFrame += 0.005*time;if (CurrentFrame > 4) CurrentFrame -= 4;p.sprite.setTextureRect(IntRect(64 * int(CurrentFrame), 255, 32, 38));}if((Keyboard::isKeyPressed(Keyboard::Space)&&(atkPosition == 'r'))){p.sprite.setTextureRect(IntRect(450, 192, 43, 40));}if((Keyboard::isKeyPressed(Keyboard::Space)&&(atkPosition == 'l'))){p.sprite.setTextureRect(IntRect(450, 320, 43, 40));}if((Keyboard::isKeyPressed(Keyboard::Space)&&(atkPosition == 't'))){p.sprite.setTextureRect(IntRect(385, 0, 32, 50));}if((Keyboard::isKeyPressed(Keyboard::Space)&&(atkPosition == 'd'))){p.sprite.setTextureRect(IntRect(450, 255, 34, 45));}getplayercoordinateforview(coordinatePlayerX, coordinatePlayerY);p.update(time);window.setView(view);window.clear();/////////////////////////////Рисуем карту/////////////////////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));s_map.setPosition(j * 32, i * 32);window.draw(s_map);}window.draw(p.sprite);window.display();}return 0;}Во-первых, при описании методов внутри тела класса, имя класса не нужно добавлять перед именем метода. Т.е.:
C++12345678910111213141516171819202122232425class Player{private: float x, y;public:float w, h, dx, dy, speed;int dir;String File;Image image;Texture texture;Sprite sprite;Player(String F, float X, float Y, float W, float H);void update(float time);void interactionWithMap();float getplayercoordinateX() // Убрал Player::{return x;}float getplayercoordinateY() // Тут тоже{return y;}};Во-вторых, проблема возникла из-за того, что переменная TileMap не видна в модулях player.h и player.cpp, где происходит обращение к ней. Так что вариант добавить #include “map.h” в player.h правильный. Очевидно, что проблема в map.h. Напиши его содержимое.
C++12345678910111213141516171819202122232425262728293031#include <SFML\Graphics.hpp>const int HEIGHT_MAP = 25;//размер карты высотаconst int WIDTH_MAP = 40;//размер карты ширинаsf::String TileMap[HEIGHT_MAP] = {"0000000000000000000000000000000000000000","0 0","0 s 0","0 0","0 0","0 0","0 0","0 0","0 0","0 0","0 0","0 0","0 0","0 0","0 0","0 0","0 0","0 0","0 0","0 0","0 0","0 0","0 0","0 0","0000000000000000000000000000000000000000",};Ок.
вижу неправильный слеш в объявлении sfml \ Graphics
Да, слеш в другую сторону. Иначе имя библиотеки будет указано неверно, и, как следствие, тип String будет неизвестен компилятору. Отсюда и ошибка, мол, непонятно что за объект TileMap.
Попробуй исправить ошибки, которые уже были указаны в топике. Потом отпишись, скомпилировалось ли
Исправил, ошибка такая же, указывает на 64 строку в player.cpp, а вообще, мне такой слеш ставит студия. Когда приписываешь скобку, а потом курсором выбираешь там из выпадающего списка.
Так, в общем, я не поленился и закодил то же, что и у тебя.
На счет слеша: да, я ошибся – в map.h слеш правильный, а вот в main.cpp и player.h – нет (поменяй на обратный, по аналогии с map.h, т.е. правильно: SFML\Graphics.hpp).
А вот с TileMap не совсем все ясно. Я решил проблему объявив массив статическим, и все заработало:
map.h
C++12345678910111213141516171819202122232425262728293031323334#pragma once#include <SFML\Graphics.hpp>const int HEIGHT_MAP = 25;const int WIDTH_MAP = 40;static sf::String TileMap[HEIGHT_MAP] = {"0000000000000000000000000000000000000000","0 0","0 s 0","0 0","0 0","0 0","0 0","0 0","0 0","0 0","0 0","0 0","0 0","0 0","0 0","0 0","0 0","0 0","0 0","0 0","0 0","0 0","0 0","0 0","0000000000000000000000000000000000000000",};А у автора урока этот код точно без static работает? Интересно, что Павел скажет
Да, работает. Это глобальная переменная, объявленная вне.
Это странно. Следуя этому коду можно обнаружить многократное объявление TileMap (сначала в main.cpp, а потом в player.cpp). Давайте рассмотрим схему проекта:
Есть два исходника: main.cpp и player.cpp. В оба подключаем player.h, который содержит map.h. таким образом видим, что map.h подставляется в ОБА cpp. Следовательно, компилятор видит, что переменная TileMap объявляется дважды и говорит нам об этом.
нужна конструкция которая делает включение в фаил только один раз. забыл как называется…
http://cppstudio.com/post/443/
<code class=”c comments”>// структура препроцессорной обёртки<code class=”c preprocessor”>#ifndef /*ИМЯ ЗАГОЛОВОЧНОГО ФАЙЛА_H*/<code class=”c preprocessor”>#define /*ИМЯ ЗАГОЛОВОЧНОГО ФАЙЛА_H*/<code class=”c comments”>// определение класса<code class=”c preprocessor”>#endif /*ИМЯ ЗАГОЛОВОЧНОГО ФАЙЛА_H*/С использованием препроцессорной обёртки, попытки подключения одного и того же файла, ошибки переопределения не вызовут. Этот же приём применяется и для предотвращения многократного определения функций, только если функции вынесены в отдельный файл.
вот. нашел. надеюсь поможет)
Использование include guard или pragma once тут не поможет, к сожалению. Хотя они все равно желательны во всех хэдерах.
-
АвторСообщения
Для ответа в этой теме необходимо авторизоваться.