How to create a tile-based game using Cocos2d-x? This tutorial will teach you how to use Cocos2d-x to build a tile-based game based on tile maps, as well as Tiled map editor. Although we are referring to the younger generation's favorite video games like "Tanks vs. Robots" and "Adventure Island," most of them were developed using tile-based map designs. In this tutorial, we will be creating a simple Ninja character who eats西瓜 in the desert. Let's start by understanding some basics about using Tile layers in Cocos2d-x. Firstly, let's create the overall project structure. We will call our project "TileGame." Next, download the necessary resources, which include:
- Some audio files for sound effects, such as GarageBand tracks for background music.
- A tile set called "tmw_desert_spacing.png" that we have already extracted from the downloaded resource file.
After downloading these resources, extract and import the tile set into your project by clicking on "New Tileset..." under the "Map" menu in the Tile Manager panel. Select "tmw_desert_spacing.png" and make sure the tile size is set to 50x50. Set the tile spacing and margin values as shown in the following example:
margin: 1;
space: 1;
Select the desired location to place buildings in the map, ensuring that they are within the tile boundaries.
Now, proceed to prepare the Map class to handle creating and managing tile maps. Open the HelloWorldScene.h
file, locate the init()
function, replace its implementation with the code below:
bool HelloWorld::init() { if (!Layer::init()) { return false; } std::string file = "TileMap.tmx"; auto str = String::createWithContentsOfFile(FileUtils::getInstance()->-fullPathForFilename(file.c_str())); TileMap *tileMap = TMXTiledMap::createWithXML(str->getString(), "default"); Background *background = tileMap->getLayerNamed("Background"); background->addChild(tileMap, -1); return true; }
In this example, we create a new TMXTiledMap object, convert the XML string from a static string to an instance variable called str
, pass the tile file name ("default"
), and initialize the map's properties, including the Background
layer.
Next, load the tile map into the scene by calling the addChild()
method on the HelloWorldScene
:
auto HelloWorldScene::addChild(TileMap *tileMap, int index) : Layer(index) { auto context = tileMap->getContext(); context->setLayerType(GL_LINE_LIST); } auto HelloWorldScene::addChild(BasicMap *basicMap, int index) : Layer(index) { auto context = basicMap->getContext(); context->setLayerType(GL_LINE_LIST); } Layer *HelloWorldScene::addChild(TileSet *tileSet, int index) : Layer(index) { if (index < 0 || index >= tileSet->getNumberOfTiles()) { throw std::runtime_error("Invalid tile index."); } auto contexts = tileSet->getTileInfo() ->getContexts() ->getGameObjectsList(); std::vector<std::unique_ptr<Object>> objects; for (const auto &context : contexts) { objects.push_back(std::make_unique<Object>(context)); } for (int i = 0; i < objects.size(); i++) { addObject(objects[i], TileSet::tileTypeToTextureIndex(i)); } return nullptr; } auto HelloWorldScene::addObject(Object *object, TextureIndex textureIndex) { if (object == nullptr) { throw std::invalid_argument("Invalid object."); } auto textures = object->getTextureInfo() ->getTextures() ->getGameObjectsList(); if (textureIndex >= textures.size()) { throw std::runtime_error("Invalid texture index."); } textures[textureIndex]->bind(object); return object; }
The addChild()
function adds the child tile set or object to the current scene, depending on their respective layers. The BasicMap
and TileSet
classes handle loading and displaying individual tiles.
Here's how you can further modify the 'addObject()' function to handle collision detection between the Ninja character and the tiles:
auto HelloWorldScene::addObject(Object *object, TextureIndex textureIndex) { if (object == nullptr) { throw std::invalid_argument("Invalid object."); } auto textures = object->getTextureInfo() ->getTextures() ->getGameObjectsList(); if (textureIndex >= textures.size()) { throw std::runtime_error("Invalid texture index."); } textures[textureIndex]->bind(object); return object; } void HelloWorldScene::update(float delta) { // Update the Ninja character and tile positions for (int i = 0; i < BasicMap::objects.size(); i++) { BasicMap *basicMap = BasicMap::objects[i]; const auto &tileData = basicMap->getTileInfo() ->getTileAtPosition(*basicMap, basicMap->width, basicMap->height) ->getData(); Object *selectedObject = nullptr; for (const auto &context : tiledMap.getLayer().getContexts()) { for (const auto &tile : tileData.tileList) { if (tile.getSurfaceId() == object->getSurfaceId()) { selectedObject = std::dynamic_pointer_cast<Object>(context->createChild(&TileSet::nullHandle)); break; } } } if (selectedObject != nullptr) { applyCollision(selectedObject); } } tiledMap.render(delta); }
In this updated version, we iterate through the BasicMap
objects and find the selected object based on its surface ID. If a match is found, we apply a collision check using the applyCollision()
function, which updates the position of the selected object within the tile.
The render()
function is responsible for updating the visual representation of the map by rendering each tile. It iterates over all tiles and calls the appropriate drawSurface()
functions, modifying the tile data as needed during collision detection.
Finally, remember that Cocos2d-x does not directly support collision detection for tile maps. However, you can utilize the Group
and Container
classes to group and manage objects in your scene, allowing for more complex collision handling through the collisionDetection()
methods provided by these classes.
By combining the use of TCustomizeLayer
or TCustomizedGroup
for object placement and arrangement, along with the tmx
format for tile maps, you can create a tile-based game that simulates a desert environment with Ninja characters eating西瓜. With this tutorial, you should now have a solid foundation for building your own tile-based SLG game in Cocos2d-x. Feel free to explore more features, textures, and animations to enhance the gameplay experience!