Суббота, 27 Апреля 2024, 03:57

Приветствую Вас Гость

[ Новые сообщения · Игроделы · Правила · Поиск ]
  • Страница 1 из 2
  • 1
  • 2
  • »
Форум игроделов » Программирование » C/C++ » [Урок]Генератор карт для платформера
[Урок]Генератор карт для платформера
SaiteiДата: Понедельник, 04 Августа 2014, 19:10 | Сообщение # 1
старожил
Сейчас нет на сайте
Доброго времени суток, уважаемые читатели.
Сегодня я хотел бы научить вас писать генераторы карт. На самом деле данный метод несколько шаманский, ибо придумывался самостоятельно.
Выражаю огромную благодарность пользователю Fade, который 2-3 года назад натолкнул на замечательные идеи на сей счёт, поделившись своими наработками.
Я понятия не имею, как генерировать карты с помощью шумов Перлина. Поэтому если кто-то знает как такое сделать - пожалуйста, поделитесь опытом.

Заранее приношу извинения за свой стиль программирования, вполне возможно, что он не каждому придется по душе.

Начало начал

Первым делом надо определиться с помощью чего мы будем выводить графику. На самом деле тут особых плюшек и не требуется, можно и самостоятельно через DirectX/OpenGL загрузить и вывести на экран спрайты. Но, увы, я очень ленив, поэтому воспользуемся SFML(Simple and Fast Multimedia Library). Качаем с этого сайта.

После того, как вы скачали SFML, нам необходимо создать С++ проект. Я использую Visual Studio 2010.
Запускаем IDE -> Создать проект... -> Пустой проект (Empty project) (называйте как хотите на английском, расположение так же выбирайте самостоятельно) -> OK.
Далее нажимаем правой кнопкой мыши по решению "НазваниеВашегоПроекта" -> Добавить -> Создать элемент (Файл cpp, main.cpp (хотя название можно любое выбрать)).
Потом: Проект -> Свойства: "НазваниеВашегоПроекта"... -> Свойства конфигурации -> C/C++ -> Общие -> (в "Конфигурация" выберите "Все конфигурации") -> Дополнительные каталоги включаемых файлов: добавьте путь к папке SFML\include.
Теперь Компоновщик -> Общие -> Дополнительные каталоги библиотек: путь к папке SFML\lib.
В Компоновщик -> Ввод добавьте следующие библиотеки (они все нам не нужны, но добавьте на будущее, т.к. я надеюсь, что вы продолжите писать игру : ) ):
Цитата
sfml-graphics.lib
sfml-window.lib
sfml-system.lib
sfml-audio.lib

Что ж, со стороны IDE справились. Осталось скинуть динамические библиотеки (dll) в папку Release и Debug. Dll файлы находятся в SFML\bin.
Может быть, вы не смогли справиться с настройкой, поэтому вы можете либо посмотреть видеоурок на русском, либо прочитать это.
SFML так же можно запустить под Mac OS X и Linux. За подробностями обращайтесь к урокам на официальном сайте. Там же можно узнать как использовать все фичи SFML. Однако я не советую использовать загрузку и проигрывание звуков\музыки. Это коряво реализовано для Windows XP, могут быть проблемы. Придётся самостоятельно пилить звуковой движок или качать уже готовый.

Проверка работы SFML

Вставляем в main.cpp код, который так любезно предоставил официальный сайт библиотеки:
Код
#include <SFML/Graphics.hpp>

int main()
{
         sf::RenderWindow window(sf::VideoMode(200, 200), "SFML works!");
         sf::CircleShape shape(100.f);
         shape.setFillColor(sf::Color::Green);

         while (window.isOpen())
         {
             sf::Event event;
             while (window.pollEvent(event))
             {
                 if (event.type == sf::Event::Closed)
                     window.close();
             }

             window.clear();
             window.draw(shape);
             window.display();
         }

         return 0;
}

Запускаем. Через некоторое время должно появиться такое окно:

Если же появилась какая-то ошибка, то проверьте себя трижды или почитайте\посмотрите инструкцию по установки, которую я кидал выше (проект можно настроить для работы со статическими библиотеками, тогда нет нужды за собой таскать dll файлы).
Есть вероятность, что антивирус будет ругаться. Лично у меня стоит Avast и он, похоже, очень не любит мои программы... : )))
Для того, чтобы антивирус игнорировал и не проверял проект, надо добавить исключения (гуглим, я ж лентяй : ))) ).

Пилим код! Да простит меня инквизиция!
Заменяем весь код main.cpp на:
Код
int main()
{
return 0;
}

Этот код потом мы дополним.
Создадим базовый класс Game. Для этого нажмем правой кнопкой мыши по "НазваниеВашегоПроекта" и нажмём Добавить -> Класс -> Добавить. В "Имя класса" напишите "Game". Что ж, IDE любезно сгенерировала Game.h с описанием и Game.cpp с реализацией класса.
Теперь немного раздумий. Что есть класс Game? Этот класс - ядро всей игры. Он будет создавать окно игры и он будет иметь доступ к другим классам. То есть это - центральный мозг нашего чудо-юдо-генератора.
Вот так выглядит мой Game.h:
Код
#pragma once
#include <string>
#include <SFML/Window.hpp>
#include <SFML/Graphics.hpp>
#include <math.h>
#include <vector>
using namespace std;

class Game
{
      string Title;
      sf::RenderWindow *window;
      const int game_width;
      const int game_height;
public:
      Game(void); //Конструктор
      bool Init(); //Инициализация окон и прочих систем (файл будет дополняться)
      bool Load(); //Загрузка ресурсов игры      
      bool UnLoad(); //Удаление ресурсов игры из оперативной памяти
      void Loop(); //Главный цикл игры
      void Shutdown(); //Завершение работы игры
      ~Game(void); //Деструктор
};


Game.cpp:
Код
#include "Game.h"

Game::Game(void): game_width(640), game_height(480) //Не пугайтесь, это всего лишь инициализация rконстант
{
           
}

bool Game::Init()
{
      Title = "My Super Duper Omega Generator"; //Название создаваемого окна : )))))

      window = new sf::RenderWindow(sf::VideoMode(game_width, game_height), Title.c_str());
      if(!Load()) //Если произошла ошибка во время загрузки ресурсов игры нам необходимо незамедлительно завершить работу программы
      {
       return false;
      }
      else
      {
       //Log("Game resources load successfully"); <- Реализуйте сами : )
      }
      return true;
}

bool Game::Load()
{

      return true;
}
bool Game::UnLoad()
{
      //Log("Game resources unloaded");
      return true;
}

void Game::Loop()
{
      //Log("Running the game");
      while(window->isOpen())
      {
       sf::Event event;
       while(window->pollEvent(event))
       {
        if(event.type == sf::Event::Closed) /*Если поступило событие "Закрытие окна" надо завершить работу приложения, т.е. освободить ресурсы и закрыть программу*/
        {
         Shutdown();
         window->close();
         break;
        }
       }
       if(sf::Keyboard::isKeyPressed(sf::Keyboard::Escape)) //Если нажали ESC - выход
       {
         Shutdown();
         window->close();
         break;
       }
       window->clear();
       window->display();
      }
}

void Game::Shutdown()
{
      UnLoad();
      //Log("The game shuts down\n");
}

Game::~Game(void)
{
}


Давайте проверим как работает то, что мы нашаманили?
Измените main.cpp:
Код
#include "Game.h"

int main()
{
      Game* game = new Game();
      if(game->Init())
      {
       game->Loop();
      }
         return 0;
}

и запустите.
Всего лишь окошко... =(
Пищаль, правда? Но не отчаивайтесь, всё впереди!

Графооон! Даёшь графооон! :D

Хм... Всё круто, окно есть. Но мы же пилим генератор карт, правильно? А для этого нам нужны, по меньшей мере, спрайты.
Сохраните эти спрайты в папках Release/Debug вашего проекта и назовите их land, grass и flowers соответственно:




Ого! Да у нас же будет мОйнкрУфт с травкой и цветочками!1 biggrin
Создайте класс Assets (в нём мы будем хранить текстуры (для хранения музыки и другой информации - дописывайте самостоятельно))

Assets.h:
Код
#pragma once
#include <map>
#include <string>
#include <SFML\Graphics.hpp>
using namespace std;

class Assets
{
      map<string,sf::Texture*>Textures;
public:
      Assets(void);
      sf::Texture& Get(const string& Key); //Получить текстуру со словесным ключем      
      void Push(const string& Key, const string& FilePath); //Положить текстуру с ключем      
      ~Assets(void);
};


Assets.cpp:
Код
#include "Assets.h"

Assets::Assets(void)
{
}

sf::Texture& Assets::Get(const string& Key)
{
      return *Textures[Key];
}

void Assets::Push(const string& Key, const string& FilePath)      
{
      sf::Texture* some = new sf::Texture; //Создать в программной куче место под текстуру
      some->loadFromFile(FilePath); //Загрузить текстуру (хочу заметить, что в FilePath вместо \ надо писать \\, т.к. \ - это специальный символ С\С++
      Textures[Key] = some; //Поместить указатель на текстуру с "пометкой" Key
}

Assets::~Assets(void)
{
      /*Домашнее задание: удаление всех текстур из памяти*/
}


Ну вот, класс написан.

Обновите Game.h:
Цитата
#pragma once
#include "Assets.h"
#include <string>
#include <SFML/Window.hpp>
#include <SFML/Graphics.hpp>
#include <math.h>
#include <vector>
using namespace std;

class Game
{
string Title;
sf::RenderWindow *window;
Assets *res; //Здесь будем хранить ресурсы игрули
const int game_width;
const int game_height;
public:
Game(void); //Конструктор
bool Init(); //Инициализация окон и прочих систем (файл будет дополняться)
bool Load(); //Загрузка ресурсов игры
bool UnLoad(); //Удаление ресурсов игры из оперативной памяти
void Loop(); //Главный цикл игры
void Shutdown(); //Завершение работы игры
~Game(void); //Деструктор
};


И Game.cpp:
Цитата
#include "Game.h"

Game::Game(void): game_width(640), game_height(480) //Не пугайтесь, это всего лишь инициализация переменных
{

}

bool Game::Init()
{
Title = "My Super Duper Omega Generator"; //Название создаваемого окна : )))))

window = new sf::RenderWindow(sf::VideoMode(game_width, game_height), Title.c_str());
if(!Load()) //Если произошла ошибка во время загрузки ресурсов игры нам необходимо незамедлительно завершить работу программы
{
return false;
}
else
{
//Log("Game resources load successfully"); <- Реализуйте сами : )
}
return true;
}

bool Game::Load()
{
res = new Assets();
return true;
}
bool Game::UnLoad()
{
delete res;
//Log("Game resources unloaded");
return true;
}

void Game::Loop()
{
//Log("Running the game");
while(window->isOpen())
{
sf::Event event;
while(window->pollEvent(event))
{
if(event.type == sf::Event::Closed) /*Если поступило событие "Закрытие окна" надо завершить работу приложения, т.е. освободить ресурсы и закрыть программу*/
{
Shutdown();
window->close();
break;
}
}
if(sf::Keyboard::isKeyPressed(sf::Keyboard::Escape)) //Если нажали ESC - выход
{
Shutdown();
window->close();
break;
}
window->clear();
window->display();
}
}

void Game::Shutdown()
{
UnLoad();
//Log("The game shuts down\n");
}

Game::~Game(void)
{
}

Теперь давайте напишем класс GameMap? Этот класс будет содержать информацию о расположении блоков.

GameMap.h:
Код
#pragma once
#include <vector>
#include "Assets.h"
using namespace std;

struct Block
{
      int x,y;
      int block_type;
};

class GameMap
{
public:
      vector<Block*>blocks;
      GameMap();
      ~GameMap(void);
};


GameMap.cpp:
Код
#include "GameMap.h"

GameMap::GameMap()
{

}

GameMap::~GameMap(void)
{
      for(int i = 0; i < blocks.size(); i++)
      {
       delete blocks[i];
      }
}


Всё вроде как хорошо, но что такое block_type? Это тип блока. Допустим, числу 0 соответствует блок земли, 1 - блоку травы и т.д.
Но согласитесь - помнить номера блоков не очень удобно. Для этого создадим block_types.h:
Код
#pragma once

enum
{
      LAND, GRASS, FLOWERS,
};


Надобно обновить класс Game:

Game.h:
Код
#pragma once
#include "Assets.h"
#include "block_types.h"
#include "GameMap.h"
#include <string>
#include <SFML/Window.hpp>
#include <SFML/Graphics.hpp>
#include <math.h>
#include <vector>
using namespace std;

class Game
{
      string Title;
      sf::RenderWindow *window;
      Assets *res; //Здесь будем хранить ресурсы игрули
      GameMap *map;
      const int game_width;
      const int game_height;
public:
      Game(void); //Конструктор
      bool Init(); //Инициализация окон и прочих систем (файл будет дополняться)
      bool Load(); //Загрузка ресурсов игры      
      bool UnLoad(); //Удаление ресурсов игры из оперативной памяти
      void Loop(); //Главный цикл игры
      void Shutdown(); //Завершение работы игры
      ~Game(void); //Деструктор
};


Game.cpp:
Код
#include "Game.h"

Game::Game(void): game_width(640), game_height(480) //Не пугайтесь, это всего лишь инициализация переменных
{
           
}

bool Game::Init()
{
      Title = "My Super Duper Omega Generator"; //Название создаваемого окна : )))))

      window = new sf::RenderWindow(sf::VideoMode(game_width, game_height), Title.c_str());
      if(!Load()) //Если произошла ошибка во время загрузки ресурсов игры нам необходимо незамедлительно завершить работу программы
      {
       return false;
      }
      else
      {
       //Log("Game resources load successfully"); <- Реализуйте сами : )
      }
      return true;
}

bool Game::Load()
{
      res = new Assets();
      res->Push("LAND","land.png");
      res->Push("GRASS","grass.png");
      res->Push("FLOWERS","flowers.png");

      map = new GameMap();
      return true;
}
bool Game::UnLoad()
{
      delete res;
      delete map;
      //Log("Game resources unloaded");
      return true;
}

void Game::Loop()
{
      //Log("Running the game");
      while(window->isOpen())
      {
       sf::Event event;
       while(window->pollEvent(event))
       {
        if(event.type == sf::Event::Closed) /*Если поступило событие "Закрытие окна" надо завершить работу приложения, т.е. освободить ресурсы и закрыть программу*/
        {
         Shutdown();
         window->close();
         break;
        }
       }
       if(sf::Keyboard::isKeyPressed(sf::Keyboard::Escape)) //Если нажали ESC - выход
       {
         Shutdown();
         window->close();
         break;
       }
       sf::Sprite some;
       for(int i = 0; i < map->blocks.size(); i++) //Рисуем карту
       {
        switch(map->blocks[i]->block_type)
        {
         case LAND:
         {
          some.setTexture(res->Get("LAND"));
          break;
         }
         case GRASS:
         {
          some.setTexture(res->Get("GRASS"));
          break;
         }
         case FLOWERS:
         {
          some.setTexture(res->Get("FLOWERS"));
          break;
         }
        }
        some.setPosition(map->blocks[i]->x, map->blocks[i]->y);
        window->draw(some);
       }
       window->clear();
       window->display();
      }
}

void Game::Shutdown()
{
      UnLoad();
      //Log("The game shuts down\n");
}

Game::~Game(void)
{
}


Я специально не помечал измененные места, ищите сами (:

Пишем генератор карт
Подошли к последнему и самому главному пункту - генерации карты. Попробуйте придумать самостоятельно генератор карт, возможно, у вас получится. Если же не получилось - читайте про тот способ, что использую я.
Итак. У нас есть блоки размером 32 на 32 пикселя. Будем считать, что один такой блок помещается в одну ячейку карты.
Имеем карту map_width на map_height (т.е. у нас map_width*map_height ячеек (вспомните формулу нахождения площади прямоугольника: S = a*b)).
Для того, чтобы "сгенерировать" карту всего лишь нужно псевдослучайным способом сгенерировать кривую на определенных высотах. Потом всё, что ниже этой кривой, заполнить блоками и сверху поставить цветочки. Оптимизации ради я буду сразу же генерировать столбцы карты.
Т.к. у нас уже есть класс GameMap, то мы будем отдавать объект этого класса генератору, чтобы он наполнял его информацией об иной карте.

Создайте класс MapGenerator.

MapGenerator.h:
Код
#pragma once
#include "block_types.h"
#include "GameMap.h"
#include <time.h>

class MapGenerator
{
      int h_min; //нижняя граница
      int h_max; //верхняя граница
public:
      MapGenerator(void);
      void Generate(vector<Block*>&blocks, const int map_width, const int map_height);
      ~MapGenerator(void);
};


MapGenerator.cpp:
Код
#include "MapGenerator.h"
#include <iostream>

MapGenerator::MapGenerator(void)
{
           
}

int Random(int a, int b)
{
      int result = a + rand()%(b-a+1);
      return result;
}

void MapGenerator::Generate(vector<Block*>&blocks, const int map_width, const int map_height)
{
      /*Удаляем старую карту*/
      for(int i = 0; i < blocks.size(); i++)
      {
       delete blocks[i];
      }
      blocks.clear();
      h_min = 1 + Random(4,5);
      h_max = map_height - 1 - Random(2,3);
      int height = Random(h_min, h_max);
      for(int x = 0; x < map_width; x++)
      {
       blocks.push_back(new Block());
       blocks[blocks.size() - 1]->block_type = GRASS;
       blocks[blocks.size() - 1]->x = x;
       blocks[blocks.size() - 1]->y = height;
       if(Random(-3,3) == 0)
       {
        blocks.push_back(new Block());
        blocks[blocks.size() - 1]->block_type = FLOWERS;
        blocks[blocks.size() - 1]->x = x;
        blocks[blocks.size() - 1]->y = height - 1;
       }

       for(int y = height+1; y < map_height; y++)
       {
        std::cout<<Random(-1,1)<<endl;
        blocks.push_back(new Block());
        blocks[blocks.size() - 1]->block_type = LAND;
        blocks[blocks.size() - 1]->x = x;
        blocks[blocks.size() - 1]->y = y;
       }

       height += Random(-1,1);
       if(height > h_max)
       {
        height -= 2;
       }
       else if(height < h_min)
       {
        height += 2;
       }
      }
}

MapGenerator::~MapGenerator(void)
{
}


Меняем Game.cpp:

Код
#include "Game.h"

Game::Game(void): game_width(640), game_height(480) //Не пугайтесь, это всего лишь инициализация переменных
{
         
}

bool Game::Init()
{
     Title = "My Super Duper Omega Generator"; //Название создаваемого окна : )))))

     window = new sf::RenderWindow(sf::VideoMode(game_width, game_height), Title.c_str());
     if(!Load()) //Если произошла ошибка во время загрузки ресурсов игры нам необходимо незамедлительно завершить работу программы
     {
      return false;
     }
     else
     {
      //Log("Game resources load successfully"); <- Реализуйте сами : )
     }
     return true;
}

bool Game::Load()
{
     res = new Assets();
     res->Push("LAND","land.png");
     res->Push("GRASS","grass.png");
     res->Push("FLOWERS","_flowers.png");     

     map = new GameMap();
     mapGen.Generate(map->blocks, (int)(game_width/32), (int)(game_height/32));
     return true;
}
bool Game::UnLoad()
{
     delete map;
     delete res;
     //Log("Game resources unloaded");
     return true;
}

void Game::Loop()
{
     //Log("Running the game");
     while(window->isOpen())
     {
      sf::Event event;
      while(window->pollEvent(event))
      {
       if(event.type == sf::Event::Closed) /*Если поступило событие "Закрытие окна" надо завершить работу приложения, т.е. освободить ресурсы и закрыть программу*/
       {
        Shutdown();
        window->close();
        break;
       }
      }
      if(sf::Keyboard::isKeyPressed(sf::Keyboard::Escape)) //Если нажали ESC - выход
      {
        Shutdown();
        window->close();
        break;
      }
      if(sf::Keyboard::isKeyPressed(sf::Keyboard::R))
      {
       mapGen.Generate(map->blocks, (int)(game_width/32), (int)(game_height/32));
      }

      sf::Sprite some;
      window->clear();
      for(int i = 0; i < map->blocks.size(); i++) //Рисуем карту
      {
       switch(map->blocks[i]->block_type)
       {
        case LAND:
        {
         some.setTexture(res->Get("LAND"));
         break;
        }
        case GRASS:
        {
         some.setTexture(res->Get("GRASS"));
         break;
        }
        case FLOWERS:
        {
         some.setTexture(res->Get("FLOWERS"));
         break;
        }
       }
       some.setPosition(map->blocks[i]->x*32, map->blocks[i]->y*32);
       window->draw(some);
      }
      window->display();
     }
}

void Game::Shutdown()
{
     UnLoad();
     //Log("The game shuts down\n");
}

Game::~Game(void)
{
}

и Game.h:
Код
#pragma once
#include "Assets.h"
#include "block_types.h"
#include "GameMap.h"
#include "MapGenerator.h"
#include <string>
#include <math.h>
#include <vector>
using namespace std;

class Game
{
  string Title;
  sf::RenderWindow *window;
  Assets *res; //Здесь будем хранить ресурсы игрули
  GameMap *map;
  MapGenerator mapGen;
  const int game_width;
  const int game_height;
public:
  Game(void); //Конструктор
  bool Init(); //Инициализация окон и прочих систем (файл будет дополняться)
  bool Load(); //Загрузка ресурсов игры  
  bool UnLoad(); //Удаление ресурсов игры из оперативной памяти
  void Loop(); //Главный цикл игры
  void Shutdown(); //Завершение работы игры
  ~Game(void); //Деструктор
};



ВНИМАНИЕ! Если вы подключали SFML точно так же, как и я, то для тестирования проекта используйте Release. В Debug будут ошибки (это связано с тем, что библиотеки подключены для релиза)


Сообщение отредактировал Saitei - Понедельник, 18 Августа 2014, 20:52
NotinДата: Понедельник, 04 Августа 2014, 19:36 | Сообщение # 2
заслуженный участник
Сейчас нет на сайте
Хоть сам и не пишу на С++, но думаю что урок будет очень полезен, кстати, почему не урок по написанию змейки! cool

Twitter
ВКонтакте
SaiteiДата: Понедельник, 04 Августа 2014, 19:50 | Сообщение # 3
старожил
Сейчас нет на сайте
Цитата Notin ()
Хоть сам и не пишу на С++, но думаю что урок будет очень полезен, кстати, почему не урок по написанию змейки! cool

Запросов не было) А надо? Я могу. Главное графику найти (чтобы змейка не такой унылой получилась)
NotinДата: Понедельник, 04 Августа 2014, 19:56 | Сообщение # 4
заслуженный участник
Сейчас нет на сайте
Saitei, да это ж шутка, тыж любишь змейки писать happy

Twitter
ВКонтакте
SaiteiДата: Понедельник, 04 Августа 2014, 21:47 | Сообщение # 5
старожил
Сейчас нет на сайте
Notin, не, просто на ассемблере надо было написать. В то время я даже не знал как её на С написать. Голову 2 часика ломал и в итоге написал прототип на С. Потом быстренько написал на ассемблере. Кроме того я ещё Х-О писал! :С
МаоДзедунДата: Понедельник, 04 Августа 2014, 22:16 | Сообщение # 6
почетный гость
Сейчас нет на сайте
Выложи на главной в статьи !!! Статьи реально хорошая


Знакомая попросила переустановить Windows, взял дистрибутив Ubuntu
Интеллигенция — самая необразованная часть общества.©Мао Дзэдун
Кто не побывал на Великой китайской стене, тот не может считаться китайцем.©Мао Дзэдун
SaiteiДата: Вторник, 05 Августа 2014, 15:50 | Сообщение # 7
старожил
Сейчас нет на сайте
МаоДзедун, по вашей просьбе так и сделаю

Сообщение отредактировал Saitei - Вторник, 05 Августа 2014, 15:50
romgermanДата: Вторник, 05 Августа 2014, 20:54 | Сообщение # 8
старожил
Сейчас нет на сайте
Зачем выкладывать на форуме, если есть раздел статей?
МаоДзедунДата: Вторник, 05 Августа 2014, 21:41 | Сообщение # 9
почетный гость
Сейчас нет на сайте
Аффтар жжот нипадецки


Знакомая попросила переустановить Windows, взял дистрибутив Ubuntu
Интеллигенция — самая необразованная часть общества.©Мао Дзэдун
Кто не побывал на Великой китайской стене, тот не может считаться китайцем.©Мао Дзэдун
SaiteiДата: Суббота, 09 Августа 2014, 11:39 | Сообщение # 10
старожил
Сейчас нет на сайте
По уроку у всех всё получилось? Кстати... Если кто-нибудь будет использовать много текстур, то std::map надо заменить хотя бы на std::vector.


Мм... К слову, ещё какие-нибудь уроки нужны?
vasua99Дата: Суббота, 09 Августа 2014, 21:54 | Сообщение # 11
GNU follower
Сейчас нет на сайте
а лучше вообще грузить все спрайты в один большой буфер, и сохранять смещения спрайтов)

Добавлено (09.08.2014, 21:54)
---------------------------------------------
biggrin


Жизнь игра, и мы в ней пешки... А я кушаю пельмешки)
SaiteiДата: Суббота, 09 Августа 2014, 22:54 | Сообщение # 12
старожил
Сейчас нет на сайте
Цитата vasua99 ()
а лучше вообще грузить все спрайты в один большой буфер, и сохранять смещения спрайтов)

Например?
vasua99Дата: Суббота, 09 Августа 2014, 23:46 | Сообщение # 13
GNU follower
Сейчас нет на сайте
Что то типо такого smile
Код

typedef struct Sprite {
     size_t offset;
     int size_x;
     int size_y;
     // etc
} Sprite;

std::list<Sprite *> LoadSprites(uint8_t *buf, size_t bufSize, char filenames[], int count)
{
      
     size_t currOffset = 0;
     size_t readed;
     int size_x;
     int size_y;
     std::list<Sprite *> sprites;
      
     for (int i = 0; i < count; i++) {
         FILE *sprite_file = fopen(filenames[i], "r");
         // for example - sprite is a file with sizes  
         // in first line and "raw rgb colors" at others
         if (fscanf(sprite_file, "%d %d\n", &size_x, &size_y) == 2 && size_x > 0 && size_y > 0) {
              
             if (currOffset + size_x * size_y >= bufSize) {
                 fprintf(stderr, "ERROR: Failed load sprites - not enough memory\n");
                 return sprites;
             }
              
             readed = 0;
             for (int j = 0; j < size_x * size_y; j++, currOffset++, readed++) {
                 int ch = fgetc(sprite_file);
                 if (ch != EOF)
                     buf[currOffset] = getchar();
                 else
                     break;
             }
              
             if (readed == size_x * size_y) {
                 Sprite *sprite = { currOffset, size_x, size_y };
                 sprites.push_back(sprite);
                 continue;
             }
             else {
                 currOffset -= readed;
             }
         }
         fprintf(stderr, "ERORR: Failed load sprite \"%s\"\n", filenames[i]);
     }
      
     return sprites;
}


Жизнь игра, и мы в ней пешки... А я кушаю пельмешки)
-l33t-h4xx-Дата: Понедельник, 11 Августа 2014, 10:10 | Сообщение # 14
участник
Сейчас нет на сайте
Цитата vasua99 ()
std::list<Sprite *> LoadSprites(uint8_t *buf, size_t bufSize, char filenames[], int count)

Такой типичный сишный код: "Я не хочу выделять память, сделай это за меня."


Как правильно задавать вопросы

Сообщение отредактировал -l33t-h4xx- - Понедельник, 11 Августа 2014, 10:12
SaiteiДата: Понедельник, 11 Августа 2014, 14:23 | Сообщение # 15
старожил
Сейчас нет на сайте
Цитата -l33t-h4xx- ()
сишный код

Тоже заметил это. Но не считаю, что это плохо. Единственное - если человек пишет на С, то он должен писать именно на С, без С++. Если на С++ - то пусть пишет на С++. Си в некоторых местах выигрывает по сравнению с си++, имхо
vasua99Дата: Понедельник, 11 Августа 2014, 17:25 | Сообщение # 16
GNU follower
Сейчас нет на сайте
Ну скажем так из С++ тут только совсем капля STLa. Ну а про выделение памяти - писалось в 2 часа ночи, в полусонном бреду, спать хотелось.. по хорошему надо юзать библиотеку со структурами данных или написать самому)

Жизнь игра, и мы в ней пешки... А я кушаю пельмешки)
PolarBearДата: Понедельник, 18 Августа 2014, 20:30 | Сообщение # 17
был не раз
Сейчас нет на сайте
Цитата Saitei ()
По уроку у всех всё получилось?

Увы, компилятор жалуется:

>"1>Game.cpp(33): error C2065: mapGen: необъявленный идентификатор" ну и соответственно:
>1>Game.cpp(33): error C2228: выражение слева от ".Generate" должно представлять класс, структуру или объединение
в строке:
>mapGen.Generate(map->blocks, (int)(game_width/32), (int)(game_height/32));

Объявлять, чтобы быть уверенным, что я ничего не сломаю (ибо я бом бом в крестах, как и во всём другом, лол) окромя переменных я ничего не умею. Прошу автора подсказать, где и как сиё объявить.
SaiteiДата: Понедельник, 18 Августа 2014, 20:53 | Сообщение # 18
старожил
Сейчас нет на сайте
Цитата PolarBear ()
>"1>Game.cpp(33): error C2065: PolarBear, pmapGen: необъявленный идентификатор" ну и соответственно:

Забыл последний апдейт Game.h выложить.
Просто после "private:" допиши "MapGenerator mapGen;".
PolarBearДата: Понедельник, 18 Августа 2014, 20:58 | Сообщение # 19
был не раз
Сейчас нет на сайте
Цитата Saitei ()
после private

А ведь его то тоже нет.

P.S. Всё, вижу.


Сообщение отредактировал PolarBear - Понедельник, 18 Августа 2014, 21:00
SaiteiДата: Понедельник, 18 Августа 2014, 21:00 | Сообщение # 20
старожил
Сейчас нет на сайте
Цитата PolarBear ()
А ведь его то тоже нет.

Значит перед "public:". Там private неявно находится.
Форум игроделов » Программирование » C/C++ » [Урок]Генератор карт для платформера
  • Страница 1 из 2
  • 1
  • 2
  • »
Поиск:

Все права сохранены. GcUp.ru © 2008-2024 Рейтинг