This tutorial is based on the Child Dragon Mountain Man translation cocos2d iPhone tutorial, rewrite with cocos2d-x for xNa engine, plus some of my processing and production. In the tutorial, most of the text images are from the original author and the author of the translation, Long Shan, and many others are my own understandings and processing. I would like to thank the original author for his tutorial and the translation of Zilong shangren. This tutorial is only for learning and communication purposes. Do not conduct commercial communications.
IPhone tutorial address: http://www.cnblogs.com/zilongshanren/archive/2011/04/11/2012852.html
IPhone tutorial original address: http://www.raywenderlich.com/1163/how-to-make-a-tile-based-game-with-cocos2d
Program:
This tutorial is part 2 of "Create a tile-Based Map game with cocos2d-x. In the previous tutorial, we created a simple tiled map-based game with a ninja searching for delicious food in the desert!
In the first part of the tutorial, we introduced how to create a map based on tiled, how to add the map to the game, how to scroll the map to follow the main character movement, and how to use the Object layer.
In this part of the tutorial, we will introduce how to create a collision area in the map and how to use the tile attribute, if you collect game items and dynamically modify maps, How can you ensure that your ninja will not eat too much!
Therefore, let's continue with what we learned in the previous tutorial and make it more like a real game!
Tiled map and Collision Detection
You may have noticed that our ninja can pass through walls and obstacles without blocking. He is a ninja, but even the real ninja, he is not so powerful!
Therefore, we need to find a way to mark some tile as "collision-able", so that we can prevent players from passing through those points. There are many ways to do this (including using the Object layer), but I want to show you a new technology. I think it is more efficient and also a good learning exercise-using a meta layer and layer attributes.
Let's get started! Start tiled again, click "layer \ add layer", name it "meta", and select "OK. We will add some fake tile in this layer to represent some "Special tile ".
So now we need to add our special tile. Click "map \ new image block ..htm", find mate_tiles.png under your resourcesfolder, and select open. Click OK.
In this case, you can see a new label in the graph block area. Open it, and you will see two tile: one red and one green.
There is nothing special about these tile-I just made a simple image that contains a red and a green translucent tile. Next, we regard the Red Tile as "collision-able" (we will use green later), and then draw our scenario as appropriate.
Therefore, make sure that the meta layer is selected, select the stamp tool, select the Red Tile, and then paint all the maps you don't want ninja to pass. When you finish, it should look like the following figure:
Next, we can set the tile attribute. In this way, we can identify in the code that this tile is "collision-able (worn out )". Right-click the graph block and click its attributes. As shown in.
Then add the colretriable attribute and set it to true. Click OK. Save the map and update it to the project's resource folder (if you did not directly edit the tmx file in the project ).
Then add a variable declaration to the tilemaplayer class;
CCTMXLayer meta = null;
Add the following code after the loading background of the init method:
this.meta = tileMap.layerNamed("Meta");
meta.visible = false;
And add a method:
CCPoint tileCoordForPosition(CCPoint position) { int x = Convert.ToInt32(position.x / tileMap.TileSize.width); int y = Convert.ToInt32((tileMap.TileSize.height * tileMap.MapSize.height - position.y) / tileMap.TileSize.height); return new CCPoint(x, y); }
Okay, let's stop for a while. As before, I declare a member variable in the meta layer and load a reference from the tile map. Note: we regard this word as invisible because we do not want to see these objects. They exist only to illustrate that the areas can be collided.
Next, we will add a new help method, which can help us convert x and y into "tile coordinates". Each tile has a coordinate starting from (0, 0) in the upper left corner to (31, 31) in the lower right corner ). (In this example, the map size is 32 × 32)
The screenshot above is the tiled interface of java version. The current map version does not support displaying coordinates. You can display the square. However, I just want to explain the principle. In any case, the tile coordinates instead of the x and y coordinates will be used for some functions we will use. Therefore, we need to convert x and y into tile coordinates. This is exactly what the function needs to do.
It is very easy to obtain the x coordinate -- we only need to divide it by the width of a tile. To get the y coordinate, we have to flip something because () in cocos2d is in the lower left corner, not in the upper left corner.
Next, replace setPlayerPosition with the following content:
void setPlayerPosition(CCPoint position) { CCPoint tileCoord = this.tileCoordForPosition(position); int tileGid = meta.tileGIDAt(tileCoord); if (tileGid != 0) { Dictionary<string, string> properties = tileMap.propertiesForGID(tileGid); if (properties != null) { string collision = String.Empty, temp; if (properties.TryGetValue("Collidable", out temp)) { collision = temp; } if (collision.Length > 0 && collision.Equals("True")) { return; } } } player.position = position; }
Here, we convert the x and y coordinates of players into tile coordinates. Then, we use the tileGIDAt method in the meta layer to obtain the GID of the specified position.
By the way, what is GID? GID indicates the unique identifier of the world (my personal opinion ). However, in this example, I think it is only an identifier of the tile we use. It can be the Red area we want to move.
When we use GID to find the attribute of the specified tile. It returns an attribute dictionary, so we can traverse it to see if there is a "collision-able" object set to "true", or gij is just like that. Compile and run the project, so no player location is set yet.
That's all! Compile and run the program. It will be displayed to you. Now you cannot use the Red tile:
PS: I found some problems during the tutorial. It is the problem of map accuracy. View
Here, if the coordinates of my player are close to the grid, the tilecoordforposition function is used for precision calculation during collision determination. If it is close to the following, it will be rounded off during calculation. Then, we will find that the coordinates of the obstacle have risen by a row. So I set the player frame to be slightly closer to the above line. This grid can be displayed in "View-display grid.
Dynamically modify tiled Map
At present, our ninja has had an interesting adventure, but the world is a little boring. And it's easy to do without tasks! In addition, our ninja looks greedy and the background will move as players move. Therefore, let's create something for ninja to play!
To make it feasible, I will have to create a foreground layer so that users can collect things. In this case, we only delete the unused tile from the foreground layer (when the tile is picked up by the play corner). In this process, the background will move.
Therefore, open the map, select "layer \ add layer...", name this layer "foreground", and then select OK. Make sure that the foreground layer is selected, and add a pair of items that can be picked up in the game. I like to place something to watermelon or something. Place something at will.
Now, we need to mark these tile as traceable. Similarly, we can refer to how we mark the tile as collision-able. Select the meta layer and convert it to meta_tiles. Now we need to make these tile available for pickup.
Next, we need to add a property for the tile so that it can be marked as a pick-up. Click the green tile in the graph Block tab, right-click and select "attribute... ", add a new property named" collectable ", and set the value to" true ".
Returns the tilemaplayer class and adds a variable declaration;
CCTMXLayer foreground = null;
At the same time, modify the tilemaplayer class accordingly:
// Add foreground = tilemap after the init method and loadingbackground. layernamed ("foreground"); // Add string collectable = string after the if statement returned when the setplayerposition method is used to determine the wall. empty; If (properties. trygetvalue ("collectable", out temp) {collectable = temp;} If (collectable. length> 0 & collectable. equals ("true") {meta. removetileat (tilecoord); ccsprite sprite = foreground. tileat (tilecoord); If (Sprite! = NULL) Sprite. Visible = false ;}
Here is a common method used to save the foreground layer handle. The difference is that we test whether the tile that players are moving towards contains the "collectable" attribute. If yes, we will use the removetileat method to remove tile from the Mata layer and foreground layer. Compile and run the project. Now you can try watermelon!
Create a splitter
Ninja is very happy to eat watermelon, but as a game player, we want to know how many watermelons we have eaten. You know, we don't want to make him fat.
Generally, we add a label on the layer. However, wait a moment: we are constantly moving this layer. In this case, the label will not be visible. What should we do?
This is a very good opportunity. If we use multiple layers in a single scenario, this is the challenge we are facing now. We will keep the TileMapLayer layer, but we will add another TileMapHud layer to display our label. (Hud means Heads up display. You can google the frequently used technologies in the game)
Of course, the two layers need to be linked in one way-the Hud layer should know when the ninja eats a watermelon. There are many ways to make two different layers communicate with each other, but I will only introduce the simplest. We store a HelloWorldHud layer handle in the HelloWorld layer. In this way, when a ninja eats a watermelon, it can call a method of the Hud layer for notification.
Therefore, add the following code in TileMapScene. cs:
class TileMapHud : CCLayer { CCLabelTTF label; public override bool init() { if (!base.init()) return false; CCSize winSize = CCDirector.sharedDirector().getWinSize(); label = CCLabelTTF.labelWithString("0", "Arial", 18); label.Color = new ccColor3B(0, 0, 0); int margin = 10; label.position = new CCPoint(winSize.width - label.contentSize.width / 2 - margin, label.contentSize.height / 2 + margin); this.addChild(label); return true; } public void numCollectedChanged(int numCollected) { label.setString(numCollected.ToString()); } public static new TileMapHud node() { TileMapHud layer = new TileMapHud(); if (layer.init()) return layer; return null; } }
Everything is clear. The second layer is derived from the CCLayer, but a label is added in the lower right corner of the CCLayer. Now, you need to add a declaration in the TileMapLayer class as a reference to the Hud layer and add such a method for initialization of the TileMapLayer.
int numCollected = 0; TileMapHud hud = null; public static TileMapLayer initWithHudLayer(TileMapHud hud) { TileMapLayer ret = new TileMapLayer(); if (ret.init()) { ret.hud = hud; return ret; } return null; }
Modify the TileMapScene constructor as follows:
public TileMapScene() { TileMapHud hud = TileMapHud.node(); this.addChild(TileMapLayer.initWithHudLayer(hud)); this.addChild(hud); }
Modify the setPlayerPosition method. When tile contains the "Collectable" attribute and the attribute is True.
this.numCollected++; hud.numCollectedChanged(numCollected);
Compile and run the program. If everything is OK, you will see the Label for counting the watermelon food by ninja in the lower right corner of the screen.
Sound Effects and music
Without cool sound effects and background music, this cannot be regarded as a complete game tutorial.
It is very easy to add sound effects and music. Add CocosDenshion. dll references first, and modify the following in the TileMapLayer class:
// Add the following to Init: // Effect preload SimpleAudioEngine. sharedEngine (). preloadEffect (@ "Resources/hit"); SimpleAudioEngine. sharedEngine (). preloadEffect (@ "Resources/move"); SimpleAudioEngine. sharedEngine (). preloadEffect (@ "Resources/pickup"); SimpleAudioEngine. sharedEngine (). playBackgroundMusic (@ "Resources/TilesMap", true); // In case for colelizable tile SimpleAudioEngine. sharedEngine (). playEffect (@ "Resources/hit"); // In case of collectable tile SimpleAudioEngine. sharedEngine (). playEffect (@ "Resources/pickup"); // Right before setting player position SimpleAudioEngine. sharedEngine (). playEffect ("Resources/move ");
Now, our ninja can eat it all!
Where to go?
Through this tutorial, you should have a good understanding of the use of tiled map in cocos2d. Here is the complete source code for this tutorial (http://dl.dbank.com/c02vzbkoyl ).
Next, go on to Part 3 <cocos2d-x for wp7> Create a Tile map-based game with cocos2d-x: Join enemies and battles (3)
If you have read this tutorial and have any good comments or suggestions, you can speak freely. Thank you!