Есть несколько способов добавить карту в нашу игру. Сегодня разберем самый простой для понимания. Итак, карта – это набор
ячеечек с картинками. Все эти ячейки имеют одинаковый размер и в совокупности дают картинку. Они таким же образом, как и наш герой, загружаются из одной картинки , но каждая из них имеет свой прямоугольник.
Видеоверсия: http://www.youtube.com/watch?v=_ZBePRNttPU
Рассмотрим на примере, я накидал простую картинку (тайлсет) для нашего урока. Вот он, скачивайте:
Итак – в данном случае три квадратика (прямоугольничка), но одна картинка, ну как всегда. Белый фон справа я оставил просто так для вашей и моей фантазии, что нибудь можете впихнуть(подрисовать справа) и поэкспериментировать. Каждый квадратик имеет размер 32*32. Значит нам надо в программе выбирать нужный квадратик в зависимости от условия. Давайте попробуем:
Создадим отдельный заголовочный файл для карты. Слева, в обозревателе решений, нажимаем правой кнопкой на пункт “Файлы исходного кода”, выбираем “добавить”, “создать элемент”, “Заголовочный файл.h”, называем его map.h жмем окей. Теперь в этом файле пишем:
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 |
#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, чтобы можно было воспользоваться строкой (работа со String ниже), затем объявили две переменные (константы) , которые отвечают за ширину высоту карты. Потом объявили массив строк и инициализировали его.
Здесь у нас встречается три символа = ‘ ‘ , ‘0’ , ‘s’ , то есть пробел, ноль, s. Когда мы будем рисовать карту (пока у нас три квадратика, поэтому три символа), мы будем спрашивать – если элемент массива равен ‘ ‘, то рисуем первый квадратик (коричневая плитка), если ‘s’, то рисуем камень и тд. Старайтесь называть осмысленно все переменные, чтобы в будущем быстрее понимать какая из них что делает. Символ ‘s’ , например, в данном случае потому, что у нас на этом месте будет камень (с англ. stone). ‘0’ можно было назвать ‘b’ от слова brick(кирпич), но вот тут у меня свое замороченное объяснение и я оставлю его при себе и 0 тоже оставлю))
Теперь идём в main.cpp файл и подключаем наш только что созданный заголовочный:
1 |
#include "map.h" //подключили код с картой |
В главной ф-ции main объявляем
1 2 3 4 5 6 |
Image map_image;//объект изображения для карты map_image.loadFromFile("images/map.png");//загружаем файл для карты Texture map;//текстура карты map.loadFromImage(map_image);//заряжаем текстуру картинкой Sprite s_map;//создаём спрайт для карты s_map.setTexture(map);//заливаем текстуру спрайтом |
И в конце нам остается все это дело нарисовать, пробежавшись по этому массиву. После window.clear(); пишем:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
/////////////////////////////Рисуем карту///////////////////// 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)); //если встретили символ пробел, то рисуем 1й квадратик if (TileMap[i][j] == 's') s_map.setTextureRect(IntRect(32, 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);//рисуем квадратики на экран } |
Карта нарисовалась. Можно запускать приложение. Камера наша стоит на месте и на следующем уроке мы поработаем с камерой, а потом заставим героя взаимодействовать с элементами карты (с камнем например) и с кирпичами (не дадим ему выбраться, сделаем столкновение).
Как вы видите все спрайты хорошо друг на друга накладываются и остается прозрачность. Посмотрите как лев и камень взаимодействуют графически:
Это говорит о том, что мы имеем довольно хорошую возможность создавать красивые карты. Все это делать вручную (как сегодня) можно, но для крутых проектов используются тайлсет редакторы и большое количество разнообразных тайлов (квадратиков). Это тема для урока в будущем, а пока посмотрите, какую красоту можно создать и импортировать в нашу игру:
Слюньки потекли, да?
Листинг урока:
main.cpp:
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 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
#include <SFML/Graphics.hpp> #include <iostream> #include "map.h" //подключили код с картой using namespace sf; ////////////////////////////////////////////////////КЛАСС ИГРОКА//////////////////////// class Player { public: float x, y, 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){ dx=0; dy=0; speed=0; dir = 0; File = F; w = W; h = H; image.loadFromFile("images/" + File); image.createMaskFromColor(Color(41, 33, 59)); texture.loadFromImage(image); sprite.setTexture(texture); x = X; y = Y; sprite.setTextureRect(IntRect(0, 0, w, h)); } void 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); } }; int main() { RenderWindow window(sf::VideoMode(640, 480), "Lesson 9. kychka-pc.ru"); 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", 250, 250, 96.0, 96.0); 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)))) { p.dir = 1; p.speed = 0.1; CurrentFrame += 0.005*time; if (CurrentFrame > 3) CurrentFrame -= 3; p.sprite.setTextureRect(IntRect(96 * int(CurrentFrame), 96, 96, 96)); } if ((Keyboard::isKeyPressed(Keyboard::Right) || (Keyboard::isKeyPressed(Keyboard::D)))) { p.dir = 0; p.speed = 0.1; CurrentFrame += 0.005*time; if (CurrentFrame > 3) CurrentFrame -= 3; p.sprite.setTextureRect(IntRect(96 * int(CurrentFrame), 192, 96, 96)); } if ((Keyboard::isKeyPressed(Keyboard::Up) || (Keyboard::isKeyPressed(Keyboard::W)))) { p.dir = 3; p.speed = 0.1; CurrentFrame += 0.005*time; if (CurrentFrame > 3) CurrentFrame -= 3; p.sprite.setTextureRect(IntRect(96 * int(CurrentFrame), 288, 96, 96)); } if ((Keyboard::isKeyPressed(Keyboard::Down) || (Keyboard::isKeyPressed(Keyboard::S)))) { p.dir = 2; p.speed = 0.1; CurrentFrame += 0.005*time; if (CurrentFrame > 3) CurrentFrame -= 3; p.sprite.setTextureRect(IntRect(96 * int(CurrentFrame), 0, 96, 96)); } p.update(time); 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)); //если встретили символ пробел, то рисуем 1й квадратик if (TileMap[i][j] == 's') s_map.setTextureRect(IntRect(32, 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); window.display(); } return 0; } |
map.h:
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 |
#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), постоянно выдает эту ошибку и не где нет толкового ответа, как ее исправить….
Какой-то глюк, не могу изображение просмотреть. Напиши пожалуйста на форум в соответствующий раздел – посмотрим
))
=)))
Так держать!
После просмотра 9 урока =>
Не получается скрин воткнуть. Нажимаю на IMG вставляю URL код картинки нажимаю отправить, отправляется пустое сообщение, что не так?
Screen
Вложение:
Это такой глюк на сайте) пользуйтесь пока вложениями) другой url не вечен, а так картинка останется на века)
Красота:) Что планируете сделать в игре?
Досмотрю те уроки, что есть к тому времени надеюсь выйдут уроки по гравитации и взаимодействию с врагами. После планирую написать красочный 2D платформер не слишком большой но с хорошей графикой и увлекательным сюжетом. Сейчас пишу сценарий игры.
Затем после выхода урока по работе с тайлсет редактором планирую сделать РПГ .
У меня есть очень хороший Тайлсет редактор для РПГ с помошью которого можно не только составлять карты но и вбивать логику игры, анимацию, квесты, и т.д. Практически без использования кода и 58 уроков к нему, а так же около 6 гигов различных текстур, тайлсетов, чарсетов и т.д.
Но им пока не пользуюсь так как у меня 2 цели:
1. Из небольших (3-4 уровня) но красочных игр с хорошей графикой, логикой и сюжетом составить портфолио.
2 Подтянуть С++.
Отличный план. Делитесь ехе-шниками потом, поиграем:) Быть может вы сделаете онлайн РПГ, ведь sfml это сделать позволяет достаточно не сложно:)
p.s. уроки по тому, что вы перечислили – как раз сейчас в разработке. Может за два дня сделаю.
Когда ввожу в main.cpp строку #include “map.h” и пытаюсь запустить, то выдает вот что получается:
1>—— Сборка начата: проект: testsfml, Конфигурация: Debug x64 ——
1> main.cpp
1>main.cpp(5): error C2059: синтаксическая ошибка: using
1>main.cpp(11): error C2146: синтаксическая ошибка: отсутствие “;” перед идентификатором “File”
1>main.cpp(11): error C4430: отсутствует спецификатор типа – предполагается int. Примечание. C++ не поддерживает int по умолчанию
1>main.cpp(12): error C2146: синтаксическая ошибка: отсутствие “;” перед идентификатором “image”
1>main.cpp(12): error C4430: отсутствует спецификатор типа – предполагается int. Примечание. C++ не поддерживает int по умолчанию
1>main.cpp(13): error C2146: синтаксическая ошибка: отсутствие “;” перед идентификатором “texture”
1>main.cpp(13): error C4430: отсутствует спецификатор типа – предполагается int. Примечание. C++ не поддерживает int по умолчанию
1>main.cpp(14): error C2146: синтаксическая ошибка: отсутствие “;” перед идентификатором “sprite”
1>main.cpp(14): error C4430: отсутствует спецификатор типа – предполагается int. Примечание. C++ не поддерживает int по умолчанию
1>main.cpp(15): error C2061: синтаксическая ошибка: идентификатор “String”
1>main.cpp(16): error C2065: File: необъявленный идентификатор
1>main.cpp(16): error C2065: F: необъявленный идентификатор
1>main.cpp(17): error C2065: W: необъявленный идентификатор
1>main.cpp(18): error C2065: H: необъявленный идентификатор
1>main.cpp(19): error C2065: image: необъявленный идентификатор
1>main.cpp(19): error C2228: выражение слева от “.loadFromFile” должно представлять класс, структуру или объединение
1> тип: unknown-type
1>main.cpp(19): error C2065: File: необъявленный идентификатор
1>main.cpp(20): error C2065: image: необъявленный идентификатор
1>main.cpp(20): error C2228: выражение слева от “.createMaskFromColor” должно представлять класс, структуру или объединение
1> тип: unknown-type
1>main.cpp(20): error C3861: Color: идентификатор не найден
1>main.cpp(21): error C2065: texture: необъявленный идентификатор
1>main.cpp(21): error C2228: выражение слева от “.loadFromImage” должно представлять класс, структуру или объединение
1> тип: unknown-type
1>main.cpp(21): error C2065: image: необъявленный идентификатор
1>main.cpp(22): error C2065: sprite: необъявленный идентификатор
1>main.cpp(22): error C2228: выражение слева от “.setTexture” должно представлять класс, структуру или объединение
1> тип: unknown-type
1>main.cpp(22): error C2065: texture: необъявленный идентификатор
1>main.cpp(23): error C2065: X: необъявленный идентификатор
1>main.cpp(23): error C2065: Y: необъявленный идентификатор
1>main.cpp(24): error C2065: sprite: необъявленный идентификатор
1>main.cpp(24): error C2228: выражение слева от “.setTextureRect” должно представлять класс, структуру или объединение
1> тип: unknown-type
1>main.cpp(24): error C3861: IntRect: идентификатор не найден
1>main.cpp(34): error C2065: sprite: необъявленный идентификатор
1>main.cpp(34): error C2228: выражение слева от “.setPosition” должно представлять класс, структуру или объединение
1> тип: unknown-type
1>main.cpp(38): error C2065: RenderWindow: необъявленный идентификатор
1>main.cpp(38): error C2146: синтаксическая ошибка: отсутствие “;” перед идентификатором “window”
1>main.cpp(38): error C3861: window: идентификатор не найден
1>main.cpp(40): error C2065: Image: необъявленный идентификатор
1>main.cpp(40): error C2146: синтаксическая ошибка: отсутствие “;” перед идентификатором “map_image”
1>main.cpp(40): error C2065: map_image: необъявленный идентификатор
1>main.cpp(41): error C2065: map_image: необъявленный идентификатор
1>main.cpp(41): error C2228: выражение слева от “.loadFromFile” должно представлять класс, структуру или объединение
1> тип: unknown-type
1>main.cpp(42): error C2065: Texture: необъявленный идентификатор
1>main.cpp(42): error C2146: синтаксическая ошибка: отсутствие “;” перед идентификатором “map”
1>main.cpp(42): error C2065: map: необъявленный идентификатор
1>main.cpp(43): error C2065: map: необъявленный идентификатор
1>main.cpp(43): error C2228: выражение слева от “.loadFromImage” должно представлять класс, структуру или объединение
1> тип: unknown-type
1>main.cpp(43): error C2065: map_image: необъявленный идентификатор
1>main.cpp(44): error C2065: Sprite: необъявленный идентификатор
1>main.cpp(44): error C2146: синтаксическая ошибка: отсутствие “;” перед идентификатором “s_map”
1>main.cpp(44): error C2065: s_map: необъявленный идентификатор
1>main.cpp(45): error C2065: s_map: необъявленный идентификатор
1>main.cpp(45): error C2228: выражение слева от “.setTexture” должно представлять класс, структуру или объединение
1> тип: unknown-type
1>main.cpp(45): error C2065: map: необъявленный идентификатор
1>main.cpp(47): error C2661: Player::Player: нет перегруженной функции, принимающей 5 аргументов
1>main.cpp(50): error C2065: Clock: необъявленный идентификатор
1>main.cpp(50): error C2146: синтаксическая ошибка: отсутствие “;” перед идентификатором “clock”
1>main.cpp(50): warning C4551: в вызове функции отсутствует список аргументов
1>main.cpp(52): error C2065: window: необъявленный идентификатор
1>main.cpp(52): error C2228: выражение слева от “.isOpen” должно представлять класс, структуру или объединение
1> тип: unknown-type
1>main.cpp(52): fatal error C1903: не удается восстановить после предыдущих ошибок; остановка компиляции
========== Сборка: успешно: 0, с ошибками: 1, без изменений: 0, пропущено: 0 ==========
Урок 8
line 27: sprite.setTexture(texture);//заливаем спрайт текстурой
Урок 9
line 57: s_map.setTexture(map);//заливаем текстуру спрайтом