How to Create a horizontal version of the Mario-like platform action game continued, a class of Mario
Welcome back. In the previous article, we talked about the gravity environment simulation in the physical engine and the collision between the protagonist Koala and the wall on the ground. I believe everyone has a certain understanding of the physical simulation in the 2D world, now let's talk about how to make the koala work!
Let the koala go!
It is very easy to control the koala movement here. It only has two capabilities: Forward and jump (I added the koala Backward function in the source code, it is recommended that you add several virtual buttons to implement richer features.) If you press the left half of the screen, the koala will move forward, hold down the right side of the koala and it will jump up (the original text sets the koala will not go back -_-).
We need to add two member variables in Player. h:
Bool _ forwardMarch; // whether to move forward
Bool _ mightAsWellJump; // Can I skip?
Set them to false in the init method of player. cpp or in the constructor.
Add touch to the GameLevelLayer class and add the following to the. h file:
virtual void registerWithTouchDispatcher();void ccTouchesBegan(cocos2d::CCSet *pTouches, cocos2d::CCEvent *pEvent);void ccTouchesMoved(cocos2d::CCSet *pTouches, cocos2d::CCEvent *pEvent);void ccTouchesEnded(cocos2d::CCSet *pTouches, cocos2d::CCEvent *pEvent);
Add it to the init of GameLevelLayer. cpp (after the map code is loaded)
SetTouchEnabled (true); // you can set the value to be touchable.
Then write the registration touch Method
Void GameLevelLayer: Register () {CCDirector * pDirector = CCDirector: sharedDirector (); pDirector-> getTouchDispatcher ()-> addStandardDelegate (this, 0); // register multiple touch}
Now, let's take a look at the three touch events!
Void GameLevelLayer: ccTouchesBegan (cocos2d: CCSet * pTouches, cocos2d: CCEvent * pEvent) {CCSetIterator iter = pTouches-> begin (); for (; iter! = PTouches-> end (); iter ++) {CCTouch * pTouch = (CCTouch *) (* iter); CCPoint touchLocation = this-> convertTouchToNodeSpace (pTouch ); // convert the touch point location to the local coordinate if (touchLocation. x> 240) // On the rightmost side of the screen, the hop is {_ player-> bMightAsWellJump = true;} else {_ player-> bForwardMarch = true ;}} void GameLevelLayer:: ccTouchesEnded (cocos2d: CCSet * pTouches, cocos2d: CCEvent * pEvent) {_ player-> bForwardMarch = false; // when you release the button, set to non-Skip or forward status _ player-> bMightAsWellJump = false ;}
The code is clear at a glance. When ccTouchesBegan is used, it determines whether the koala status is forward or jump based on the position where the player presses the button. When the buttons are released, the two status variables are reset to false.
The real role movement is carried out in the player update method. Check the Code:
Void Player: update (float delta) {CCPoint gravity = ccp (0.f,-0000.f); // koala drops by 450 units per second (CCPoint gravityStep = ccpMult (gravity, delta ); // calculate the specific decrease in the delta time under the influence of gravity, that is, the whereabouts of the dt time CCPoint forwardMove = ccp (800.f, 0.f); // the forward speed, which advances by 800 per second. fCCPoint forwardStep = ccpMult (forwardMove, delta); // 1this-> _ velocity = ccpAdd (this-> _ velocity, gravityStep ); // current speed = Current Speed + gravity acceleration this-> _ velocity = ccp (this-> _ velocity. x * 0.9f, this-> _ velocity. y); // 2if (this-> bForwardMarch) {this-> _ velocity = ccpAdd (this-> _ velocity, forwardStep ); // Add the forward velocity vector to the current velocity} // 3 CCPoint minMovement = ccp (-120.f,-350.f); CCPoint maxMovement = ccp (1200000f, 250.f ); this-> _ velocity = ccpClamp (this-> _ velocity, minMovement, maxMovement); // 4 CCPoint stepVelocity = ccpMult (this-> _ velocity, delta ); // calculate how much the protagonist moves at this speed. this-> _ desiredPosition = ccpAdd (this-> getPosition (), stepVelocity ); // the desired location = current location + current speed}
Let's take a closer look at the new section:
Now, we can see that our leading role, koalas, is now ready to go!
Let the koala jump!
Jumping is the most obvious feature in an action game. We want the role to be smooth and lifelike. Now let's implement it.
In the update method of the player class, add the following code before the if (this-> _ forwardMarch) Statement:
CCPoint jumpForce = ccp(0.f, 310.f);if(this->_mightAsWellJump && this->_onGround){ this->_velocity = ccpAdd(this->_velocity, jumpForce);}
If you add an upward force, the role can jump up.
If you stop here, you will get an old-fashioned elegant jump, that is, each jump is the same height, and each time you give the player the same force, then let's wait for gravity to pull you back to the ground.
There seems to be nothing wrong with this. If you don't have high requirements, but you should carefully observe various popular platform games, such as super mario, sonick, Rockman, and water soul fighters, it seems that players can control the height of the jump by pressing the keys to achieve more flexible results. How is that done?
In fact, this effect is also very simple. The so-called strength of a player's buttons is only a long time for pressing keys. The duration is long, that is, the time for applying the jump force. Of course, the jump is high, if the player is not strong on the road, of course, it will jump to half of the chain, but the player has the illusion that if you want to jump high, press the jump key. If you don't want to jump, release the key is -_-
CCPoint jumpForce = ccp (0.f, 310.f ); // upward jump force float jumpCutOff = 150.f; // if (this-> bMightAsWellJump & this-> onGround) // if the current player presses the jump key and {this-> _ velocity = ccpAdd (this-> _ velocity, jumpForce) on the ground ); // The hop is added with an upward speed} else if (! This-> bMightAsWellJump & this-> _ velocity. y> jumpCutOff) // The player does not hold down the jump key and the upward speed has exceeded the set value, the jump speed is limited to {this-> _ velocity = ccp (this-> _ velocity. x, jumpCutOff );}
The annotations are very clear, so there will be no more explanations.
Now, let's build and run our game. Let's take a look. Our koala can go up and down freely.
The jump is very happy, but the tragedy is that it jumps out of the screen on the far right and cannot be seen.
To solve this problem, this problem is actually the Point of view follow. On cocos2dx, there is a standard algorithm to solve this problem, and the code is pasted:
Void GameLevelLayer: setViewpointCenter (cocos2d: CCPoint pos) {CCSize winSize = CCDirector: sharedDirector ()-> getWinSize (); // The role cannot exceed the half-screen int x = MAX (pos. x, winSize. width/2); int y = MAX (pos. y, winSize. height/2); // The role cannot run out of the screen x = MIN (x, (_ map-> getMapSize (). width * _ map-> getTileSize (). width)-winSize. width/2); y = MIN (y, (_ map-> getMapSize (). height * _ map-> getTileSize (). height)-winSize. height/2); CCPoint actualPosition = ccp (x, y); CCPoint centerOfView = ccp (winSize. width/2, winSize. height/2); CCPoint viewPoint = ccpSub (centerOfView, actualPosition); // set the map position _ map-> setPosition (viewPoint );}
The method parameter is the current position of the player koala. This method can be used not only to follow the leading role, but also to follow the leading role. The principle of this method has been mentioned in many blogs. The principle is that the map is moving in the back direction with the player. You can refer to other articles to explain its principle, however, I don't want to say more here. I don't want to say a sentence or two clearly. We just need to use this method.
All we need to do is call the update method in the GameLevelLayer class. You can put it before the end of the update method, as shown below:
This-> setViewpointCenter (_ player-> getPosition ());
Now let's run the build again. This time our koala will no longer run out of the screen!
Try the injury!
Now we can get started with the game clearance and GameOver functions.
There is a hazards layer in the map, which contains some objects that will be hung when a Koala hits. In essence, it also involves collision detection. Check the Code:
Void GameLevelLayer: handleHazardCollisions (Player * player) {CCArray * tiles = this-> getSurroundingTilesAtPosition (player-> getPosition (), _ hazards); CCDictionary * dic = NULL; CCObject * obj = NULL; float x = 0.f; float y = 0.f; int gid = 0; CCARRAY_FOREACH (tiles, obj) {dic = (CCDictionary *) obj; x = dic-> valueForKey ("x")-> floatValue (); y = dic-> valueForKey ("y")-> floatValue (); CCRect tileRect = CCRectMake (x, y, _ map-> getTileSize (). width, _ map-> getTileSize (). height); CCRect pRect = player-> collisionBoundingBox (); gid = dic-> valueForKey ("gid")-> intValue (); if (gid & tileRect. intersectsRect (pRect) // if there is a dingtalk and the player is in conflict with it, gameOver {this-> gameOver (false );}}}
The code looks familiar to me. You did not remember it. In fact, it was copied from the checkAndResolveCollisions method, but it didn't tell so many cases and the processing method was simple. It just called the gameOver method, if the Boolean value of gameOver is true, the game wins. If it is false, the game fails.
_ Hazards in the GameLevelLayer class is also a member variable of the CCTMXLayer * map layer type. initialize it in the init method of this class:
_ Hazards = _ map-> layerNamed ("hazards ");
Call this method in the update method. The update method is like this:
void GameLevelLayer::update(float delta){_player->update(delta);this->handleHazardCollisions(_player);this->checkForAndResolveCollisions(_player);this->setViewpointCenter(_player->getPosition());}
Now we have implemented the gameOver method. When a player jumps to the harads layer, we call this method to end the game, or when the player ends, I also call this method, this method will display a restart button, and print some information on the screen, telling the player that you are dead or you have won -_-
void GameLevelLayer::gameOver(bool bWon){bGameOver = true;CCString* gameText;if (bWon){gameText = CCString::create("You Won!");}elsegameText = CCString::create("You have Died!");CCLabelTTF* diedLabel = CCLabelTTF::create(gameText->getCString(), "Marker Felt", 40);diedLabel->setPosition(ccp(240, 200));CCMoveBy *slideIn = CCMoveBy::create(1.f, ccp(0, 250));CCMenuItemImage* replay = CCMenuItemImage::create("replay.png","replay.png","replay.png", this, menu_selector(GameLevelLayer::restartGame));CCArray *menuItems = CCArray::create();menuItems->addObject(replay);CCMenu *menu = CCMenu::create();menu->addChild(replay);menu->setPosition(ccp(240, -100));this->addChild(menu);this->addChild(diedLabel);menu->runAction(slideIn);}
The variable bGameOver at the beginning of the method is also a new member variable defined by the GameLevelLayer class. It must be initialized to false in init. This variable indicates whether the game ends. The function of this variable is to judge at the beginning of the update method. If bGameOver = true, return directly and do not do anything. As follows:
void GameLevelLayer::update(float delta){if(bGameOver)return;_player->update(delta);this->handleHazardCollisions(_player);this->checkForAndResolveCollisions(_player);this->setViewpointCenter(_player->getPosition());}
Now you can compile and run it again. If you try it on the dingtalk board, a cup of cake will come to an end...
Do not try too much. be careful that the animal protection organization will come to your home (-_-is this so-called American humor ?)
Fixed a fatal BUG.
Do you remember the previous occurrence when the koala fell out of the map? There are some pitfalls in the game map. Our intention is that the game will end when the koala falls, but in fact the program has collapsed. All those with TILED maps will have this problem. As long as the role goes out of the map border game, it will collapse. This problem has plagued many people, including me. In fact, solving this BUG is very simple. The key lies in the tileGIDAt method.
When you open the source code implementation, you will know that it has a CCASSERT macro, and an exception will be thrown when a map is identified. Originally, this method is to take the gid of a certain tile on the map. If you get all the gids of a certain tile on the map, you have to leave an exception. Here we add a judgment to the getSurroundingTilesAtPosition method. Note that before calling the tileGIDAt method, we should not let the koala out of the MAP:
if (tilePos.y > (_map->getMapSize().height-1)){this->gameOver(false);return NULL;}
In the checkForAndResolveCollisions method, you can call the getSurroundingTilesAtPosition method. If it returns an empty array, it is not good. Therefore, you need to modify the checkForAndResolveCollisions method, after the CCArray * tiles = this-> getSurroundingTilesAtPosition (player-> getPosition (), _ Wils); sentence, add
If (bGameOver) // The player may fall into the trap and will not process {return ;}
Compile the program and run it again. Try the koala into the trap and find that the program is no longer DOWN!
Winner!
At last, we processed the progress of the koala's victory when it reached the final point.
Here we are simple, just judge how far the koala goes to the right to win
void GameLevelLayer::checkForWin(){if (_player->getPositionX()>3130.0){this->gameOver(true);}}
In real games, we usually place an object at the end, such as the door, the castle flag or something. When the main character encounters it, it will win and enter the next level. If you are interested, try it yourself!
This method should also be put in the update method of GameLevelLayer, as follows:
void GameLevelLayer::update(float delta){if(bGameOver)return;_player->update(delta);this->handleHazardCollisions(_player);this->checkForWin();this->checkForAndResolveCollisions(_player);this->setViewpointCenter(_player->getPosition());}
Run it and try it at the end:
Add sound effects
After victory, there will be no music. A silent world will be desperate. Let's add some sound effects!
Add the following header files to GameLevelLayer. cpp and player. cpp:
# Include "SimpleAudioEngine. h"
Using namespace CocosDenshion;
Add the following to the init method of GamelevelLayer:
SimpleAudioEngine: audio engine ()-> playBackgroundMusic ("level1.mp3 ");
In the update method of player. cpp, the following audio effects are added to the position where the player jumps:
If (this-> bMightAsWellJump & this-> onGround) // if the current player presses the jump key and {this-> _ velocity = ccpAdd (this-> _ velocity, jumpForce); // The jump is to add an upward speed SimpleAudioEngine: sharedEngine ()-> playEffect ("jump.wav ");}
Add the following sound effects to the gameOver method of GameLevelLayer. cpp:
Void GameLevelLayer: gameOver (bool bWon) {if (bGameOver) // do not call {return;} bGameOver = true repeatedly; SimpleAudioEngine: sharedEngine () -> playEffect ("hurt.wav ");
Compile and run the program. Try a music game you have created!
Source code: Download
What else can we do?
What else do you have to do? There are also monsters, AI, checkpoints, role state machines, and so on. These are all in the three-piece ios game Platformer Start Kit. As mentioned above, this tutorial is just a prelude to this Start Kit. What is the real Platformer game?
After completing this tutorial, you can learn
How is it? The question about copyright cannot be disclosed here. If you are interested, you can check the horizontal version of the platform game source code.
In addition, one of the three well-known horizontal Combat Games
Beat 'em Up Game Starter KitIt's also wonderful! The source code tutorials published on the Internet are less than 1/20 -_-
If you are interested, please refer to the following link: horizontal combat game source code: