<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>camera &#8211; Joni Mikkola (c) 2024</title>
	<atom:link href="https://jonimikkola.com/tag/camera/feed/" rel="self" type="application/rss+xml" />
	<link>https://jonimikkola.com</link>
	<description></description>
	<lastBuildDate>Sun, 10 Jan 2016 12:40:18 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.1.10</generator>
	<item>
		<title>Hill Climb Racing &#8211; Deconstructed &#8211; Hud, checkpoints and tricks</title>
		<link>https://jonimikkola.com/hill-climb-racing-deconstructed-hud-checkpoints-tricks/</link>
					<comments>https://jonimikkola.com/hill-climb-racing-deconstructed-hud-checkpoints-tricks/#comments</comments>
		
		<dc:creator><![CDATA[jonimikkola]]></dc:creator>
		<pubDate>Sun, 10 Jan 2016 12:32:59 +0000</pubDate>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[box2d]]></category>
		<category><![CDATA[camera]]></category>
		<category><![CDATA[car]]></category>
		<category><![CDATA[checkpoint]]></category>
		<category><![CDATA[cocos2d-x]]></category>
		<category><![CDATA[hill climb racing]]></category>
		<category><![CDATA[layer]]></category>
		<category><![CDATA[listener]]></category>
		<category><![CDATA[math]]></category>
		<category><![CDATA[physics]]></category>
		<category><![CDATA[sprite]]></category>
		<guid isPermaLink="false">http://www.mikkolajoni.com/?p=712</guid>

					<description><![CDATA[Hi and sorry for delaying this chapter about HUD, checkpoints and tricks for so long, in this part we will go through how to identify tricks and checkpoints in the game using collision listeners etc. All aforementioned will be displayed on a HUD so user gets the feedback properly. Let&#8217;s start this chapter by creating ... <a title="Hill Climb Racing &#8211; Deconstructed &#8211; Hud, checkpoints and tricks" class="read-more" href="https://jonimikkola.com/hill-climb-racing-deconstructed-hud-checkpoints-tricks/" aria-label="More on Hill Climb Racing &#8211; Deconstructed &#8211; Hud, checkpoints and tricks">Read more</a>]]></description>
										<content:encoded><![CDATA[<p>Hi and sorry for delaying this chapter about HUD, checkpoints and tricks for so long,</p>
<p><a href="http://www.mikkolajoni.com/wp-content/uploads/2016/01/hcr.png"><img decoding="async" loading="lazy" src="http://www.mikkolajoni.com/wp-content/uploads/2016/01/hcr.png" alt="Preview" width="222" height="134" class="aligncenter size-full wp-image-840" /></a></p>
<p>in this part we will go through how to identify tricks and checkpoints in the game using collision listeners etc. All aforementioned will be displayed on a HUD so user gets the feedback properly.</p>
<p>Let&#8217;s start this chapter by creating HUD and after that we&#8217;ll implement all necessary functionality.</p>
<p><a href="#HUD">1. Hud</a><br />
<a href="#Car">2. Car</a><br />
<a href="#Checkpoints">3. Checkpoints</a><br />
<a href="#Fuel">4. Fuel cans and coins</a><br />
<a href="#Tricks">5. Listeners and tricks</a></p>
<p><a href="http://www.mikkolajoni.com/wp-content/uploads/2016/01/hcr_ch3.zip">Download resources</a></p>
<p><a name="HUD"></p>
<h2>1. HUD</h2>
<p></a></p>
<p>HUD is very essential part of a game providing necessary feedback for user interactions. It of course could be called with many other names, but term HUD is pretty well known among gamers.</p>
<p>Our HUD will eventually contain information for:<br />
1. Distance from the start<br />
2. Amount of fuel left<br />
3. Amount of money<br />
4. Airtime and flip reward feedback</p>
<p>Let&#8217;s add template code for the HUD. The method names are pretty much self explanatory.</p>
<p>Hud.h<br />
[code language=&#8221;cpp&#8221;]<br />
#ifndef __HUD_H__<br />
#define __HUD_H__</p>
<p>#include &quot;cocos2d.h&quot;</p>
<p>USING_NS_CC;</p>
<p>class Hud : public Layer  {<br />
private:<br />
    Label* _messageLabel;<br />
    Label* _moneyLabel;<br />
    Label* _nextCheckpointLabel;</p>
<p>    Sprite* _fuelTankSprite;<br />
    Sprite* _fuelTankBarSprite;<br />
    Sprite* _coinSprite;<br />
    Sprite* _distanceSprite;<br />
public:<br />
    void showMessage(std::string message);</p>
<p>    void setFuel(float fuel);<br />
    void setMoney(int money);<br />
    void setMeters(int nextCheckpont, int meters);</p>
<p>    virtual bool init();<br />
    CREATE_FUNC(Hud);<br />
};</p>
<p>#endif<br />
[/code]</p>
<p>Hud.cpp<br />
[code language=&#8221;cpp&#8221;]<br />
#include &quot;Hud.h&quot;</p>
<p>#include &quot;SimpleAudioEngine.h&quot;</p>
<p>USING_NS_CC;</p>
<p>bool Hud::init() {<br />
    if(!Layer::init()) {<br />
        return false;<br />
    }</p>
<p>    int fuelDistanceFromTop = 70;<br />
    int moneyDistanceFromTop = 120;<br />
    int distanceDistanceFromTop = 30;</p>
<p>    Size s = Director::getInstance()-&gt;getVisibleSize();</p>
<p>    _fuelTankSprite = Sprite::create(&quot;images/hud/fuel_can.png&quot;);<br />
    _fuelTankSprite-&gt;setPosition(Point(40, s.height &#8211; fuelDistanceFromTop));<br />
    addChild(_fuelTankSprite);</p>
<p>    _fuelTankBarSprite = Sprite::create(&quot;images/hud/fuel_can_bar.png&quot;);<br />
    _fuelTankBarSprite-&gt;setAnchorPoint(Point(0.0f, 0.5f));<br />
    _fuelTankBarSprite-&gt;setPosition(Point(90, s.height &#8211; fuelDistanceFromTop));<br />
    addChild(_fuelTankBarSprite);</p>
<p>    _coinSprite = Sprite::create(&quot;images/hud/coin.png&quot;);<br />
    _coinSprite-&gt;setPosition(Point(40, s.height &#8211; moneyDistanceFromTop));<br />
    addChild(_coinSprite);</p>
<p>    _distanceSprite = Sprite::create(&quot;images/hud/distance.png&quot;);<br />
    _distanceSprite-&gt;setPosition(Point(40, s.height &#8211; distanceDistanceFromTop));<br />
    addChild(_distanceSprite);</p>
<p>    _messageLabel = Label::createWithTTF(&quot;Checkpoint reached!&quot;, &quot;fonts/arial.ttf&quot;, 60);<br />
    _messageLabel-&gt;setRotation(10.0f);<br />
    _messageLabel-&gt;setScale(0.5f);<br />
    _messageLabel-&gt;setPosition(Point(s.width/2.0f + 100, s.height/2.0f + 50.0f));<br />
    _messageLabel-&gt;setOpacity(0);<br />
    addChild(_messageLabel);</p>
<p>    _moneyLabel = Label::createWithTTF(&quot;0&quot;, &quot;fonts/arial.ttf&quot;, 60);<br />
    _moneyLabel-&gt;setScale(0.4f);<br />
    _moneyLabel-&gt;setAnchorPoint(Point(0.0f, 0.5f));<br />
    _moneyLabel-&gt;setPosition(Point(90, s.height &#8211; moneyDistanceFromTop));<br />
    addChild(_moneyLabel);</p>
<p>    _nextCheckpointLabel = Label::createWithTTF(&quot;0/0&quot;, &quot;fonts/arial.ttf&quot;, 60);<br />
    _nextCheckpointLabel-&gt;setScale(0.4f);<br />
    _nextCheckpointLabel-&gt;setAnchorPoint(Point(0.0f, 0.5f));<br />
    _nextCheckpointLabel-&gt;setPosition(Point(90, s.height &#8211; distanceDistanceFromTop));<br />
    addChild(_nextCheckpointLabel);</p>
<p>    return true;<br />
}</p>
<p>void Hud::showMessage(std::string message) {<br />
    _messageLabel-&gt;setString(message);<br />
    Director::getInstance()-&gt;getActionManager()-&gt;removeAllActionsFromTarget(_messageLabel);<br />
    _messageLabel-&gt;setOpacity(255);<br />
    DelayTime* delay = DelayTime::create(0.5f);<br />
    FadeOut* fade = FadeOut::create(0.2f);<br />
    Sequence* seq = Sequence::create(delay, fade, NULL);<br />
    _messageLabel-&gt;runAction(seq);<br />
}</p>
<p>void Hud::setMeters(int nextCheckpont, int meters) {<br />
    __String* str = String::createWithFormat(&quot;%d/%d&quot;, meters, nextCheckpont);<br />
    _nextCheckpointLabel-&gt;setString(str-&gt;getCString());<br />
}</p>
<p>void Hud::setFuel(float value) {<br />
    const float barMaxWidth = 120;<br />
    value = clampf(value, 0.0f, 120.0f);<br />
    _fuelTankBarSprite-&gt;setScaleX((value * 1.2f) / barMaxWidth);<br />
}</p>
<p>void Hud::setMoney(int money) {<br />
    __String* str = String::createWithFormat(&quot;%d&quot;, money);<br />
    _moneyLabel-&gt;setString(str-&gt;getCString());<br />
}<br />
[/code]</p>
<p>Now we need to add the Hud layer as a child of our main layer by doing the following:</p>
<p>HelloWorldScene.h<br />
[code language=&#8221;cpp&#8221;]<br />
#include &quot;Hud.h&quot;<br />
class HelloWorld : public cocos2d::Layer {<br />
private:<br />
    &#8230;<br />
    Hud* _hud;<br />
[/code]</p>
<p>Initialize _hud variable and add is as a child just before end of bool HelloWorld::init so that HUD will be displayed on top of other layers. We could of course change the rendering order by changing z order, but adding layers in &#8216;correct&#8217; order suffices here.</p>
<p>HelloWorldScene.cpp<br />
[code language=&#8221;cpp&#8221;]<br />
    _hud = Hud::create();<br />
    addChild(_hud);<br />
[/code]</p>
<p>Now if you compile and run the code you should see distance, fuel and money indicators in top left corner. Now we have our HUD frame which we can utilize later on with methods showMessage, setMeters, setFuel and setMoney.</p>
<p><a name="Car"></p>
<h2>2. Car</h2>
<p></a></p>
<p>Next we need to add some new features in our car class. Since our HUD needs a way to display car fuel, we need to add fuel variable in the class along with getAngle method which we use later on to identify flips.</p>
<p>Position method is for getting distance later on.</p>
<p>Car.h<br />
[code language=&#8221;cpp&#8221;]<br />
class Car {<br />
private:<br />
    &#8230;<br />
    float _fuel;<br />
public:<br />
    &#8230;</p>
<p>    float getAngle() {<br />
        return _body-&gt;GetAngle();<br />
    }</p>
<p>    float getFuel() {<br />
        return _fuel;<br />
    }</p>
<p>    Point getPosition() {<br />
        return Point(_body-&gt;GetPosition().x * PTM_RATIO, _body-&gt;GetPosition().y * PTM_RATIO);<br />
    }</p>
<p>    void setFuel(float fuel) {<br />
        _fuel = fuel;<br />
    }<br />
[/code]</p>
<p>When fuel value is <= 0 the fuel bar is empty.

Car.cpp
[code language="cpp"]
void Car::update(float dt) {
...
   _fuel -= 0.1f;
}
[/code]

HelloWorldScene.cpp
[code language="cpp"]
bool HelloWorld::init() {
    ....
    _car = new Car();
    _car-&gt;init(_world, _terrain, _startPosition);
    _car-&gt;setFuel(100.0f);
    ...
}


void HelloWorld::update(float dt) {
   ...
   _hud-&gt;setFuel(_car-&gt;getFuel());
   _camera-&gt;update(dt);
   ...
[/code]

Now if you compile and run your car's fuel should be slowly decreasing on the top left corner.

<a name="Checkpoints"></p>
<h2>3. Checkpoints</h2>
<p></a></p>
<p>Now it&#8217;s time to add checkpoints functionality. Checkpoints are reached after certain amount of distance is driven. We can store distances in simple integer array. Also in order to know the distance, we need to add method for doing that which we later on compare with the checkpoint integer array.</p>
<p>Lets decide that 10 pixels equal on meter. Therefore in code below first checkpoints appears after 500 meters(5000 pixels).</p>
<p>Notice that we are adding car&#8217;s start position as a private variable _startPosition. We use this for calculating distance from start point forwards.</p>
<p>HelloWorldScene.h<br />
[code language=&#8221;cpp&#8221;]<br />
&#8230;<br />
private:<br />
    int _currentCheckpoint;<br />
    std::vector&lt;int&gt; _checkpoints;</p>
<p>    int getMeters();<br />
    void nextCheckpoint(); </p>
<p>    Point _startPosition;<br />
[/code]</p>
<p>HelloWorldScene.cpp<br />
[code language=&#8221;cpp&#8221;]<br />
bool HelloWorldScene::init() {<br />
    _startPosition = Point(400, 500);<br />
    _car = new Car();<br />
    _car-&gt;init(_world, _terrain, _startPosition);</p>
<p>    _currentCheckpoint = 0;<br />
    _checkpoints.push_back(500);<br />
    _checkpoints.push_back(1000);<br />
    _checkpoints.push_back(1700);<br />
    _checkpoints.push_back(2500);<br />
    _checkpoints.push_back(3500);<br />
    _checkpoints.push_back(5000);<br />
}</p>
<p>int HelloWorld::getMeters() {<br />
    const int meterEqualsPixels = 10;<br />
    int totalMeters = (_car-&gt;getPosition().x &#8211; _startPosition.x) / meterEqualsPixels;<br />
    //use std::max to avoid returning negative meters<br />
    return std::max(totalMeters, 0);<br />
}</p>
<p>void HelloWorld::nextCheckpoint() {<br />
   _currentCheckpoint++;<br />
   _hud-&gt;showMessage(&quot;Checkpoint reached!&quot;);<br />
}</p>
<p>void HelloWorld::update(float dt) {<br />
   &#8230;<br />
   //monitor whether player has passed previous checkpoint<br />
   int currentMeters = getMeters();<br />
   if(currentMeters &gt;= _checkpoints[_currentCheckpoint]) {<br />
        nextCheckpoint();<br />
   }</p>
<p>   _hud-&gt;setMeters(_checkpoints[_currentCheckpoint], getMeters());</p>
<p>   _camera-&gt;update(dt);<br />
   &#8230;<br />
}<br />
[/code]</p>
<p>Notice that the _currentCheckpoint variable is increased each time checkpoint is reached, which means that the if comparison will then start comparing against next position in _checkpoints array. Pretty straightforward.</p>
<p>Compile the code and take your car for a ride. After 5000 pixels you should get a &#8220;Checkpoint reached&#8221; displayed on HUD.</p>
<p><a name="Fuel"></p>
<h2>4. Fuel cans and coins</h2>
<p></a></p>
<p>It is now time to do some preliminary actions before diving into tricks. We need to be able to identify between sprite objects of different type. For that purpose add following code in config.h. Before compiling remove ItemType struct enum from Terrain.h!</p>
<p>Config.h<br />
[code language=&#8221;cpp&#8221;]<br />
&#8230;<br />
#define PTM_RATIO 10</p>
<p>struct ItemType {<br />
    enum type {<br />
        Coin,<br />
        Fuel,<br />
        Head,<br />
        Ground,<br />
        Car<br />
    };<br />
};<br />
&#8230;<br />
[/code]</p>
<p>Lets take those types into use. Add following code inside Car::init after sprites are initialized.</p>
<p>Car.cpp<br />
[code language=&#8221;cpp&#8221;]<br />
    _driverSprite-&gt;setTag(ItemType::Head);<br />
    _bodySprite-&gt;setTag(ItemType::Car);<br />
    _frontTireSprite-&gt;setTag(ItemType::Car);<br />
    _rearTireSprite-&gt;setTag(ItemType::Car);<br />
[/code]</p>
<p>Same thing with Terrain class&#8217;s fuel and coin sprites. Insert code</p>
<p>Terrain.h<br />
[code language=&#8221;cpp&#8221;]<br />
&#8230;<br />
private:<br />
   Node* _groundNode;<br />
[/code]</p>
<p>Terrain.cpp<br />
[code language=&#8221;cpp&#8221;]<br />
Terrain::Terrain() {<br />
   &#8230;<br />
   _groundNode = Node::create();<br />
   _groundNode-&gt;setTag(ItemType::Ground);<br />
   addChild(_groundNode);<br />
}</p>
<p>void TerrainG::initWorld(b2World* world) {<br />
   &#8230;<br />
   _body = world-&gt;CreateBody(&amp;bd);<br />
   //with _groundNode we can identify if collision is happening with ground (ItemType::Ground).<br />
   _body-&gt;SetUserData(_groundNode);<br />
}</p>
<p>void Terrain::generateItems(std::vector&lt;Sprite*&gt;&amp; sprites) {<br />
&#8230;<br />
   Sprite* coinSprite = Sprite::create(&quot;images/game/coin.png&quot;);<br />
   coinSprite-&gt;setTag(ItemType::Coin);<br />
&#8230;<br />
   Sprite* fuelCanSprite = Sprite::create(&quot;images/game/fuel_can.png&quot;);<br />
   fuelCanSprite-&gt;setTag(ItemType::Fuel);<br />
&#8230;<br />
}<br />
[/code]</p>
<p>Now we are able to identify collision between different kinds of objects and can more easily do some specific actions required.</p>
<p>Lets utilize HUD class when collecting money or fuel.<br />
HelloWorldScene.h<br />
[code language=&#8221;cpp&#8221;]<br />
private:<br />
&#8230;<br />
    int _money;</p>
<p>    //items<br />
    void playCollectAnim(Sprite* sprite);<br />
    void collectFuelTank(Sprite* sprite);<br />
    void collectMoney(Sprite* sprite);<br />
[/code]</p>
<p>HelloWorldScene.cpp<br />
[code language=&#8221;cpp&#8221;]<br />
    void HelloWorld::playCollectAnim(Sprite* sprite) {<br />
        sprite-&gt;runAction(MoveBy::create(0.5f, Point(0.0f, 40.0f)));<br />
        sprite-&gt;runAction(FadeOut::create(0.5f));<br />
    }</p>
<p>    void HelloWorld::collectFuelTank(Sprite *sprite) {<br />
        _car-&gt;setFuel(120);<br />
        playCollectAnim(sprite);<br />
    }</p>
<p>    void HelloWorld::collectMoney(Sprite *sprite) {<br />
        _money += 100;<br />
        _hud-&gt;setMoney(_money);<br />
        playCollectAnim(sprite);<br />
    }<br />
[/code]</p>
<p>Update HelloWorld::update code where collisions against fuel and coins are checked with following:<br />
[code language=&#8221;cpp&#8221;]<br />
    for(int i = 0; i &lt; _sprites.size(); ++i) {<br />
        if(_car-&gt;getSprite()-&gt;boundingBox().intersectsRect(_sprites[i]-&gt;boundingBox()) &amp;&amp; _sprites[i]-&gt;isVisible()) {<br />
            if(_sprites[i]-&gt;getTag() == ItemType::Coin) {<br />
                collectMoney(_sprites[i]);<br />
            } else if(_sprites[i]-&gt;getTag() == ItemType::Fuel) {<br />
                collectFuelTank(_sprites[i]);<br />
            }<br />
            _sprites[i]-&gt;setTag(-1);<br />
        }<br />
    }<br />
[/code]</p>
<p>Compile your code and collect some fuel cans and coins to see new features in action.</p>
<p><a name="Tricks"></p>
<h2>5. Tricks</h2>
<p></a></p>
<p>Now we get to more hairy part of this chapter. There is many ways to identify flips and airtimes and this is only one among them. </p>
<p>What we essentially need is a way to know whether the car is on the ground or in the air. If car is in the air we monitor if car&#8217;s angle changes as much as is needed for a complete flip.</p>
<p>In order to monitor collisions using Box2d, we need to start by creating MyContactListener class, which Box2d fills with collision information for us to utilize. So create new files MyContactListener.h accordingly.</p>
<p>MyContactListener.h<br />
[code language=&#8221;cpp&#8221;]<br />
#ifndef __MYCONTACTLISTENER_H__<br />
#define __MYCONTACTLISTENER_H__</p>
<p>#import &lt;Box2d/Box2D.h&gt;<br />
#import &lt;vector&gt;<br />
#import &lt;algorithm&gt;</p>
<p>#include &quot;cocos2d.h&quot;</p>
<p>USING_NS_CC;</p>
<p>struct MyContact {<br />
    b2Body *bodyA;<br />
    b2Body *bodyB;<br />
    float impulseA;<br />
    float impulseB;<br />
    bool operator==(const MyContact&amp; other) const<br />
    {<br />
        return (bodyA == other.bodyA) &amp;&amp; (bodyB == other.bodyB);<br />
    }</p>
<p>    bool hasCollision(int typeA, int typeB) {<br />
        bool collision = false;<br />
        if(bodyA &amp;&amp; bodyB) {<br />
            Node* aNode = (Node*)bodyA-&gt;GetUserData();<br />
            Node* bNode = (Node*)bodyB-&gt;GetUserData();</p>
<p>            if(aNode &amp;&amp; bNode) {<br />
                log(&quot;id %d&quot;, aNode-&gt;getTag());<br />
                if((aNode-&gt;getTag() == typeA &amp;&amp; bNode-&gt;getTag() == typeB) ||<br />
                   (bNode-&gt;getTag() == typeA &amp;&amp; aNode-&gt;getTag() == typeB)) {<br />
                    collision = true;<br />
                }<br />
            }<br />
        }</p>
<p>        return collision;<br />
    }<br />
};</p>
<p>class MyContactListener : public b2ContactListener {</p>
<p>public:<br />
    std::vector&lt;MyContact&gt; beginContacts;<br />
    std::vector&lt;MyContact&gt; endContacts;</p>
<p>    void BeginContact(b2Contact* contact) {<br />
        MyContact myContact = { contact-&gt;GetFixtureA()-&gt;GetBody(), contact-&gt;GetFixtureB()-&gt;GetBody() };<br />
        beginContacts.push_back(myContact);<br />
    }</p>
<p>    void EndContact(b2Contact* contact) {<br />
        MyContact myContact = { contact-&gt;GetFixtureA()-&gt;GetBody(), contact-&gt;GetFixtureB()-&gt;GetBody() };<br />
        endContacts.push_back(myContact);<br />
    }</p>
<p>};</p>
<p>#endif<br />
[/code]</p>
<p>Next thing we need is to utilize this class. Create _contactListener variable pointer in HelloWorldScene.h.</p>
<p>HelloWorldScene.h<br />
[code language=&#8221;cpp&#8221;]<br />
private:<br />
&#8230;<br />
   //tricks<br />
   float _aerialTimer;<br />
   float _lastRotation;<br />
   int _collisionPoints;<br />
   int _flips;</p>
<p>   MyContactListener* _contactListener;</p>
<p>   void flip();<br />
[/code]</p>
<p>HelloWorldScene.cpp<br />
[code language=&#8221;cpp&#8221;]<br />
bool HelloWorld::init() {<br />
    &#8230;<br />
    //add after Box2d&#8217;s _world variable is initialized<br />
    _contactListener = new MyContactListener();<br />
    _world-&gt;SetContactListener(_contactListener);</p>
<p>    //initialize variables<br />
    _aerialTimer = 0.0f;<br />
    _collisionPoints = 0;<br />
    _flips = 0;<br />
    _lastRotation = 0.0f;<br />
}</p>
<p>void HelloWorld::flip() {<br />
    _flips++;<br />
    _money += 1000;<br />
    _hud-&gt;setMoney(_money);<br />
    _hud-&gt;showMessage(&quot;Flip!&quot;);<br />
}</p>
<p>void HelloWorld::update(float dt) {<br />
&#8230;<br />
    std::vector&lt;MyContact&gt;&amp; beginContacts = _contactListener-&gt;beginContacts;<br />
    bool groundHit = false;<br />
    for(int i = 0; i &lt; beginContacts.size(); ++i) {<br />
       if(beginContacts[i].hasCollision(ItemType::Ground, ItemType::Car)) {<br />
            groundHit = true;<br />
            _aerialTimer = 0.0f;<br />
            _collisionPoints++;<br />
        }<br />
    }</p>
<p>    std::vector&lt;MyContact&gt;&amp; endContacts = _contactListener-&gt;endContacts;<br />
    for(int i = 0; i &lt; endContacts.size(); ++i) {<br />
        if(endContacts[i].hasCollision(ItemType::Ground, ItemType::Car)) {<br />
            _collisionPoints&#8211;;<br />
        }<br />
    }</p>
<p>    endContacts.clear();<br />
    beginContacts.clear();</p>
<p>    //in case where car is on the ground, there are 2 collision points because<br />
    //both tires are touching the ground<br />
    if(_collisionPoints == 0) {<br />
        _aerialTimer += dt;<br />
        //air score bonus after every second(1.0f)<br />
        if(_aerialTimer &gt;= 1.0f) {<br />
            _hud-&gt;showMessage(&quot;Air score +100!&quot;);<br />
            _aerialTimer = _aerialTimer &#8211; 1.0f;<br />
        }<br />
    }</p>
<p>    //compare new angle with last angle. If new angle is larger or smaller than PI*2<br />
    //it means car has rotated a full circle.<br />
    float angle = _car-&gt;getAngle();<br />
    if(!groundHit) {<br />
        if(angle &gt; _lastRotation + M_PI * 2) {<br />
            _lastRotation = _lastRotation + M_PI * 2;<br />
            flip();<br />
        } else if(angle &lt; _lastRotation &#8211; M_PI * 2) {<br />
            _lastRotation = _lastRotation &#8211; M_PI * 2;<br />
            flip();<br />
        }<br />
    }</p>
<p>    _camera-&gt;update(dt);<br />
&#8230;<br />
}</p>
<p>[/code]</p>
<p>Now if you hit compile and run you should start gaining some rewards by doing flips and airtimes.</p>
<p>I hope I didn&#8217;t miss anything relevant information with this chapter. Anyway if that was case, please don&#8217;t hesitate to comment. Thanks!</p>
]]></content:encoded>
					
					<wfw:commentRss>https://jonimikkola.com/hill-climb-racing-deconstructed-hud-checkpoints-tricks/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>Hill Climb Racing &#8211; Deconstructed &#8211; Car, controls, camera, coins and fuel cans</title>
		<link>https://jonimikkola.com/hill-climb-racing-deconstructed-car-controls-camera-coins-and-fuel-cans/</link>
					<comments>https://jonimikkola.com/hill-climb-racing-deconstructed-car-controls-camera-coins-and-fuel-cans/#comments</comments>
		
		<dc:creator><![CDATA[jonimikkola]]></dc:creator>
		<pubDate>Thu, 21 May 2015 14:10:27 +0000</pubDate>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[b2body]]></category>
		<category><![CDATA[box2d]]></category>
		<category><![CDATA[c++]]></category>
		<category><![CDATA[camera]]></category>
		<category><![CDATA[car]]></category>
		<category><![CDATA[climb]]></category>
		<category><![CDATA[cocos]]></category>
		<category><![CDATA[cocos2d-x]]></category>
		<category><![CDATA[collision]]></category>
		<category><![CDATA[controls]]></category>
		<category><![CDATA[development]]></category>
		<category><![CDATA[games]]></category>
		<category><![CDATA[hill]]></category>
		<category><![CDATA[multitouch]]></category>
		<category><![CDATA[physics]]></category>
		<category><![CDATA[racing]]></category>
		<category><![CDATA[tutorials]]></category>
		<category><![CDATA[vehicle]]></category>
		<category><![CDATA[zoom]]></category>
		<guid isPermaLink="false">http://www.mikkolajoni.com/?p=700</guid>

					<description><![CDATA[Hello, in the last chapter we went through on how to create terrain and now we will focus on creating four main things we now lack. We will start by creating car which can be driven with reverse and accelerate pedals. In order to keep the car centered on the screen, we will create camera ... <a title="Hill Climb Racing &#8211; Deconstructed &#8211; Car, controls, camera, coins and fuel cans" class="read-more" href="https://jonimikkola.com/hill-climb-racing-deconstructed-car-controls-camera-coins-and-fuel-cans/" aria-label="More on Hill Climb Racing &#8211; Deconstructed &#8211; Car, controls, camera, coins and fuel cans">Read more</a>]]></description>
										<content:encoded><![CDATA[<p>Hello,</p>
<p>in the <a href="http://www.mikkolajoni.com/2015/03/how-to-develop-hill-climb-racing-with-cocos2d-x-2/">last chapter</a> we went through on how to create terrain and now we will focus on creating four main things we now lack. We will start by creating car which can be driven with reverse and accelerate pedals. In order to keep the car centered on the screen, we will create camera that will act similarly to Hill Climb Racing which means it will zoom out while driving fast and zoom in when slowing down. The last thing we will go through is collecting coins and fuel cans. So we will have 4 simple concepts: Car, controls, camera, coins and fuel cans where each of these concepts one has some special features which we will go through.</p>
<p>I will again provide you with some template code, which we will start implementing as we go on and in the end you should be able to enjoy some fast driving while collecting dozens of coins and fuel cans.</p>
<p>What we have after this<br />
<iframe loading="lazy" width="420" height="315" src="https://www.youtube.com/embed/zLsWhhDXUqk?rel=0" frameborder="0" allowfullscreen></iframe></p>
<p><a href="#Car">1. Car</a><br />
<a href="#Controls">2. Controls</a><br />
<a href="#Camera">3. Camera</a><br />
<a href="#CoinsFuels">4. Coins and fuel cans</a></p>
<p><a href="http://karvis.kapsi.fi/hr_tutorial/hr_ch2.zip">Download resources</a></p>
<p><a name="Car"></p>
<h2>1. Car class</h3>
<p></a><br />
<a href="http://www.mikkolajoni.com/wp-content/uploads/2015/05/car.jpg"><img decoding="async" loading="lazy" src="http://www.mikkolajoni.com/wp-content/uploads/2015/05/car.jpg" alt="Car physics with Box2D" width="222" height="134" class="aligncenter size-full wp-image-840" /></a></p>
<p>We will start by creating car on our track. Because our car will be driven on the track which is made of Box2D body, the car will be made of Box2D body too, so the bodies can interact between each other. We will then add sprites on those bodies created and then update those sprite positions in update loop to make sprites follow Box2D bodies properly. </p>
<p>I won&#8217;t actually be going that much into detail on how to create car with Box2D bodies, because creating physics that provide well functioning car model is somewhat trial and error process. So the variables defined below just need to be tweaked long enough to have it work well enough for your purposes.<br />
See these two links if want to get some ideas:<br />
<a href="http://www.emanueleferonato.com/2009/04/06/two-ways-to-make-box2d-cars/" target="_blank">http://www.emanueleferonato.com/2009/04/06/two-ways-to-make-box2d-cars/</a><br />
<a href="http://www.iforce2d.net/b2dtut/" target="_blank">http://www.iforce2d.net/b2dtut/</a></p>
<p>First of all we need to create separate config.h file so that we can refer PTM_RATIO definition each time we need it.<br />
[code language=&#8221;cpp&#8221;]<br />
#ifndef __CONFIG_H__<br />
#define __CONFIG_H__</p>
<p>#define PTM_RATIO 10</p>
<p>#endif<br />
[/code]</p>
<p>Update Terrain.h code<br />
[code language=&#8221;cpp&#8221;]<br />
#include &quot;config.h&quot;<br />
//#define PTM_RATIO 32<br />
[/code]</p>
<p>Replace Terrain::update code with next code<br />
[code language=&#8221;cpp&#8221;]<br />
void Terrain::update(float dt) {<br />
    int start = abs((getPositionX()) / GroundStep);<br />
    updatePhysics(start, start + _maxPhysicsPoints);<br />
}<br />
[/code]</p>
<p>Lets now build the pickup.<br />
Car.h<br />
[code language=&#8221;cpp&#8221;]<br />
#ifndef __CAR_H__<br />
#define __CAR_H__</p>
<p>#include &lt;Box2d/Box2D.h&gt;<br />
#include &quot;cocos2d.h&quot;</p>
<p>USING_NS_CC;</p>
<p>class Car {<br />
private:<br />
    bool _accelerating;<br />
    bool _reversing;</p>
<p>    float _speed;<br />
    float _torque;</p>
<p>    Sprite* _bodySprite;<br />
    Sprite* _frontTireSprite;<br />
    Sprite* _driverSprite;<br />
    Sprite* _rearTireSprite;</p>
<p>    b2Body* _frontTireBody;<br />
    b2Body* _frontAxelTireBody;<br />
    b2Body* _rearTireBody;<br />
    b2Body* _rearAxelTireBody;<br />
    b2Body* _body;<br />
    b2Body* _headBody;</p>
<p>    b2RevoluteJoint* _rearTireRevoluteJoint;<br />
    b2PrismaticJoint* _rearTirePrismaticJoint;<br />
    b2RevoluteJoint* _frontTireRevoluteJoint;<br />
    b2PrismaticJoint* _frontTirePrismaticJoint;<br />
    b2RevoluteJoint* _neckRevoluteJoint;<br />
public:<br />
    Car();<br />
    ~Car();</p>
<p>    void init(b2World* world, Node* parent, Point pos);</p>
<p>    void accelerate(bool accelerating);<br />
    void reverse(bool reversing);</p>
<p>    //our camera class needs to get a target body which to follow<br />
    b2Body* getBody() {<br />
        return _body;<br />
    }</p>
<p>    //for collision detection<br />
    Sprite* getSprite() {<br />
        return _bodySprite;<br />
    }</p>
<p>    void update(float dt);<br />
};</p>
<p>#endif<br />
[/code]</p>
<p>Car.cpp<br />
[code language=&#8221;cpp&#8221;]<br />
#include &quot;config.h&quot;<br />
#include &quot;Car.h&quot;</p>
<p>USING_NS_CC;</p>
<p>Car::Car() {</p>
<p>}</p>
<p>Car::~Car() {</p>
<p>}</p>
<p>void Car::accelerate(bool accelerating) {<br />
    _accelerating = accelerating;<br />
}</p>
<p>void Car::reverse(bool reversing) {<br />
    _reversing = reversing;<br />
}</p>
<p>void Car::update(float dt) {<br />
    if(_accelerating) {<br />
        _rearTireRevoluteJoint-&gt;SetMotorSpeed(_speed);<br />
        _rearTireRevoluteJoint-&gt;SetMaxMotorTorque(_torque);<br />
        _frontTireRevoluteJoint-&gt;SetMotorSpeed(_speed);<br />
        _frontTireRevoluteJoint-&gt;SetMaxMotorTorque(_torque);<br />
    }</p>
<p>    if(_reversing) {<br />
        _rearTireRevoluteJoint-&gt;SetMotorSpeed(-_speed);<br />
        _rearTireRevoluteJoint-&gt;SetMaxMotorTorque(_torque);<br />
        _frontTireRevoluteJoint-&gt;SetMotorSpeed(-_speed);<br />
        _frontTireRevoluteJoint-&gt;SetMaxMotorTorque(_torque);<br />
    }</p>
<p>    if(!_reversing &amp;&amp; !_accelerating) {<br />
        _rearTireRevoluteJoint-&gt;SetMotorSpeed(0);<br />
        _rearTireRevoluteJoint-&gt;SetMaxMotorTorque(0);<br />
        _frontTireRevoluteJoint-&gt;SetMotorSpeed(0);<br />
        _frontTireRevoluteJoint-&gt;SetMaxMotorTorque(0);<br />
    }<br />
}</p>
<p>void Car::init(b2World* world, Node* parent, Point pos) {<br />
    _torque = 100.0f;<br />
    _speed = 400.0f;</p>
<p>    _frontTireSprite = Sprite::create(&quot;images/tire.png&quot;);<br />
    _rearTireSprite = Sprite::create(&quot;images/tire.png&quot;);<br />
    _bodySprite = Sprite::create(&quot;images/van.png&quot;);<br />
    _driverSprite = Sprite::create(&quot;images/head1.png&quot;);</p>
<p>    parent-&gt;addChild(_frontTireSprite);<br />
    parent-&gt;addChild(_rearTireSprite);<br />
    parent-&gt;addChild(_driverSprite);<br />
    parent-&gt;addChild(_bodySprite);</p>
<p>    b2BodyDef bodyDef;<br />
    bodyDef.position = b2Vec2(pos.x/PTM_RATIO, pos.y/PTM_RATIO);<br />
    bodyDef.type = b2_dynamicBody;</p>
<p>    _body = world-&gt;CreateBody(&amp;bodyDef);<br />
    _body-&gt;SetUserData(_bodySprite);<br />
    b2PolygonShape carShape;<br />
    carShape.SetAsBox(70.0f/PTM_RATIO, 10.0f/PTM_RATIO);</p>
<p>    b2FixtureDef carFixtureDef;<br />
    carFixtureDef.shape = &amp;carShape;<br />
    carFixtureDef.density = 0.1;<br />
    carFixtureDef.friction = 1.0f;<br />
    carFixtureDef.filter.categoryBits = 0x02;<br />
    carFixtureDef.restitution = 0.2;<br />
    carFixtureDef.filter.maskBits = 0x01;<br />
    _body-&gt;CreateFixture(&amp;carFixtureDef);</p>
<p>    b2BodyDef driverBodyDef;<br />
    b2Vec2 position = _body-&gt;GetPosition();<br />
    driverBodyDef.position.Set((position.x + 10.0f / PTM_RATIO),(position.y + 10.0f / PTM_RATIO));<br />
    driverBodyDef.type = b2_dynamicBody;</p>
<p>    _headBody = world-&gt;CreateBody(&amp;driverBodyDef);<br />
    _headBody-&gt;SetUserData(_driverSprite);<br />
    b2CircleShape headShape;<br />
    headShape.m_radius = 16.0f/PTM_RATIO;<br />
    b2FixtureDef headFixtureDef;<br />
    headFixtureDef.shape = &amp;headShape;<br />
    headFixtureDef.density = 0.1f;<br />
    headFixtureDef.friction = 1.0f;<br />
    headFixtureDef.filter.categoryBits = 0x02;<br />
    headFixtureDef.filter.maskBits = 0x01;<br />
    _headBody-&gt;CreateFixture(&amp;headFixtureDef);</p>
<p>    b2RevoluteJointDef headJointDef;<br />
    headJointDef.enableLimit = true;<br />
    headJointDef.lowerAngle = -0.1f;<br />
    headJointDef.upperAngle = 0.1f;<br />
    b2Vec2 neckPos = b2Vec2(_headBody-&gt;GetWorldCenter().x, _headBody-&gt;GetWorldCenter().y &#8211; 0.4f);<br />
    headJointDef.Initialize(_body, _headBody, neckPos);<br />
    _neckRevoluteJoint = (b2RevoluteJoint*)world-&gt;CreateJoint(&amp;headJointDef);</p>
<p>    b2BodyDef rearTireBodyDef;<br />
    rearTireBodyDef.position.Set((pos.x + -40.0f)/PTM_RATIO, (pos.y &#8211; 25.0f)/PTM_RATIO);<br />
    rearTireBodyDef.type = b2_dynamicBody;</p>
<p>    _rearTireBody = world-&gt;CreateBody(&amp;rearTireBodyDef);<br />
    _rearTireBody-&gt;SetUserData(_rearTireSprite);<br />
    b2CircleShape rearTireShape;<br />
    rearTireShape.m_radius = 16.0f/PTM_RATIO;</p>
<p>    b2FixtureDef rearTireFixtureDef;<br />
    rearTireFixtureDef.shape = &amp;rearTireShape;<br />
    rearTireFixtureDef.density = 0.15;<br />
    rearTireFixtureDef.friction = 1.0f;<br />
    rearTireFixtureDef.filter.categoryBits = 0x02;<br />
    rearTireFixtureDef.restitution = 0.2;</p>
<p>    _rearTireBody-&gt;CreateFixture(&amp;rearTireFixtureDef);<br />
    b2BodyDef rearAxelBodyDef;<br />
    rearAxelBodyDef.position.Set(_rearTireBody-&gt;GetWorldCenter().x, _rearTireBody-&gt;GetWorldCenter().y);<br />
    rearAxelBodyDef.type = b2_dynamicBody;<br />
    _rearAxelTireBody = world-&gt;CreateBody(&amp;rearAxelBodyDef);<br />
    b2PolygonShape rearAxelShape;<br />
    rearAxelShape.SetAsBox(6.0f/PTM_RATIO, 12.0f/PTM_RATIO, b2Vec2_zero, -0.5);<br />
    b2FixtureDef rearAxelFixtureDef;<br />
    rearAxelFixtureDef.shape = &amp;rearAxelShape;<br />
    rearAxelFixtureDef.density = 0.01f;<br />
    rearAxelFixtureDef.filter.categoryBits = 0x02;<br />
    rearAxelFixtureDef.filter.maskBits = 2;</p>
<p>    _rearAxelTireBody-&gt;CreateFixture(&amp;rearAxelFixtureDef);</p>
<p>    b2RevoluteJointDef rearWheelRevoluteJointDef;<br />
    rearWheelRevoluteJointDef.enableMotor = true;<br />
    rearWheelRevoluteJointDef.Initialize(_rearTireBody, _rearAxelTireBody, _rearTireBody-&gt;GetWorldCenter());<br />
    _rearTireRevoluteJoint = (b2RevoluteJoint*)world-&gt;CreateJoint(&amp;rearWheelRevoluteJointDef);</p>
<p>    b2PrismaticJointDef rearWheelPrismaticJointDef;<br />
    rearWheelPrismaticJointDef.enableLimit = true;<br />
    rearWheelPrismaticJointDef.enableMotor = true;<br />
    rearWheelPrismaticJointDef.lowerTranslation = -0.3f/PTM_RATIO;<br />
    rearWheelPrismaticJointDef.upperTranslation = 0.5f/PTM_RATIO;<br />
    rearWheelPrismaticJointDef.Initialize(_body, _rearAxelTireBody, b2Vec2(_rearAxelTireBody-&gt;GetWorldCenter().x, _rearAxelTireBody-&gt;GetWorldCenter().y), b2Vec2(0, 2));<br />
    _rearTirePrismaticJoint = (b2PrismaticJoint*)world-&gt;CreateJoint(&amp;rearWheelPrismaticJointDef);</p>
<p>    b2BodyDef frontTireBodyDef;<br />
    frontTireBodyDef.position.Set((pos.x + 49.0f)/PTM_RATIO, (pos.y &#8211; 24.0f)/PTM_RATIO);<br />
    frontTireBodyDef.type = b2_dynamicBody;</p>
<p>    _frontTireBody = world-&gt;CreateBody(&amp;frontTireBodyDef);<br />
    _frontTireBody-&gt;SetUserData(_frontTireSprite);<br />
    b2CircleShape frontTireShape;<br />
    frontTireShape.m_radius = 16.0f / PTM_RATIO;</p>
<p>    b2FixtureDef frontTireFixtureDef;<br />
    frontTireFixtureDef.shape = &amp;frontTireShape;<br />
    frontTireFixtureDef.density = 0.15;<br />
    frontTireFixtureDef.friction = 1.0;<br />
    frontTireFixtureDef.filter.categoryBits = 0x02;<br />
    frontTireFixtureDef.restitution = 0.2;<br />
    _frontTireBody-&gt;CreateFixture(&amp;frontTireFixtureDef);</p>
<p>    b2BodyDef frontAxelBodyDef;<br />
    frontAxelBodyDef.position.Set(_frontTireBody-&gt;GetWorldCenter().x, _frontTireBody-&gt;GetWorldCenter().y);<br />
    frontAxelBodyDef.type = b2_dynamicBody;<br />
    _frontAxelTireBody = world-&gt;CreateBody(&amp;frontAxelBodyDef);<br />
    b2PolygonShape frontAxelShape;<br />
    frontAxelShape.SetAsBox(6.0f/PTM_RATIO, 12.0f/PTM_RATIO, b2Vec2_zero, 0.5);<br />
    b2FixtureDef frontAxelFixtureDef;<br />
    frontAxelFixtureDef.shape = &amp;frontAxelShape;<br />
    frontAxelFixtureDef.density = 0.01f;<br />
    frontAxelFixtureDef.filter.categoryBits = 0x01;<br />
    frontAxelFixtureDef.filter.maskBits = 2;</p>
<p>    _frontAxelTireBody-&gt;CreateFixture(&amp;frontAxelFixtureDef);</p>
<p>    b2RevoluteJointDef frontWheelRevoluteJointDef;<br />
    frontWheelRevoluteJointDef.enableMotor = true;<br />
    frontWheelRevoluteJointDef.Initialize(_frontTireBody, _frontAxelTireBody, _frontTireBody-&gt;GetWorldCenter());<br />
    _frontTireRevoluteJoint = (b2RevoluteJoint*)world-&gt;CreateJoint(&amp;frontWheelRevoluteJointDef);</p>
<p>    b2PrismaticJointDef frontWheelPrismaticJointDef;<br />
    frontWheelPrismaticJointDef.enableLimit = true;<br />
    frontWheelPrismaticJointDef.enableMotor = true;<br />
    frontWheelPrismaticJointDef.lowerTranslation = -0.3f/PTM_RATIO;<br />
    frontWheelPrismaticJointDef.upperTranslation = 0.5f/PTM_RATIO;<br />
    frontWheelPrismaticJointDef.Initialize(_body, _frontAxelTireBody, b2Vec2(_frontAxelTireBody-&gt;GetWorldCenter().x, _frontAxelTireBody-&gt;GetWorldCenter().y), b2Vec2(0, 2));<br />
    _frontTirePrismaticJoint = (b2PrismaticJoint*)world-&gt;CreateJoint(&amp;frontWheelPrismaticJointDef);<br />
}<br />
[/code]</p>
<p>So the code above creates our car and it has accelerate and reverse methods, which we will later on connect to our controls. It has init method which takes b2World as a parameter, so that the bodies we created can be added to the physics world. The next parameter will be our Terrain layer where we will add each sprite(tires, car frame and head). The last parameter is position and we will be positioning our car on top of our track. </p>
<p>Lets now drop our car on the terrain. For some reason I still haven&#8217;t renamed HelloWorldScene to any other name, but we will now go with it. Add following code into HelloWorldScene.h.</p>
<p>HelloWorld.h<br />
[code language=&#8221;cpp&#8221;]<br />
&#8230;<br />
#include &quot;Car.h&quot;</p>
<p>class HelloWorld : public cocos2d::Layer {<br />
private:<br />
&#8230;<br />
    Car* _car;<br />
public:<br />
&#8230;<br />
    virtual void update(float dt);<br />
&#8230;<br />
[/code]</p>
<p>[code language=&#8221;cpp&#8221;]<br />
bool HelloWorld::init() {<br />
    &#8230;<br />
    const Point startPoint = Point(400, 500);<br />
    _car = new Car();<br />
    _car-&gt;init(_world, _terrain, startPoint);</p>
<p>    //When we call this method, our HelloWorld layer will start calling update method and we will there update sprite positions into body position<br />
    scheduleUpdate();<br />
    &#8230;<br />
}<br />
[/code]</p>
<p>Let&#8217;s now position sprites into Box2D body positions. Put this on bottom of HelloWorldScene.cpp.<br />
[code language=&#8221;cpp&#8221;]<br />
void HelloWorld::update(float dt) {<br />
    //keep Sprite positions in sync with Box2D&#8217;s bodies<br />
    for (b2Body * b = _world-&gt;GetBodyList(); b != NULL; b = b-&gt;GetNext()) {<br />
        if (b-&gt;GetUserData() != NULL) {<br />
            Node* node = (Node*)b-&gt;GetUserData();<br />
            node-&gt;setPosition(Point(b-&gt;GetPosition().x * PTM_RATIO, b-&gt;GetPosition().y * PTM_RATIO));<br />
            node-&gt;setRotation(-1 * CC_RADIANS_TO_DEGREES(b-&gt;GetAngle()));<br />
        }<br />
    }</p>
<p>    _car-&gt;update(dt);<br />
    _world-&gt;Step(dt, 10, 10);<br />
}<br />
[/code]</p>
<p>Now if you run the code, provided that you position your car properly(<em>startPoint</em> variable), the car should fall on the track. Because we need to make this more fun, lets add controls so we can interact with our car.</p>
<p><a name="Controls"></p>
<h2>2. Controls class</h3>
<p></a><br />
<a href="http://www.mikkolajoni.com/wp-content/uploads/2015/05/pedals.jpg"><img decoding="async" loading="lazy" src="http://www.mikkolajoni.com/wp-content/uploads/2015/05/pedals-300x63.jpg" alt="Pedals with multitouch support for controlling the car" width="300" height="63" class="aligncenter size-medium wp-image-841" srcset="https://jonimikkola.com/wp-content/uploads/2015/05/pedals-300x63.jpg 300w, https://jonimikkola.com/wp-content/uploads/2015/05/pedals.jpg 600w" sizes="(max-width: 300px) 100vw, 300px" /></a></p>
<p>So now we will continue on working our controls, which means reverse and accelerate pedals. We will develop controls with multitouch support, which means that pedals won&#8217;t lock in case we have more than one finger on the screen and for each of the touches we will be monitoring their positions whether on the reverse/accelerate pedal or not.</p>
<p>[code language=&#8221;cpp&#8221;]<br />
#ifndef __CONTROLS_H__<br />
#define __CONTROLS_H__</p>
<p>#include &lt;Box2d/Box2D.h&gt;<br />
#include &quot;cocos2d.h&quot;</p>
<p>USING_NS_CC;</p>
<p>class Controls : public Layer  {<br />
private:<br />
    bool _reversing;<br />
    bool _accelerating;</p>
<p>    Sprite* _accelerateSprite;<br />
    Sprite* _accelerateDownSprite;<br />
    Sprite* _brakeSprite;<br />
    Sprite* _brakeDownSprite;</p>
<p>    Touch* _brakeTouch;<br />
    Touch* _thrustTouch;</p>
<p>    void handleTouches(const std::vector&lt;Touch*&gt;&amp; touches, Event* event);</p>
<p>    void accelerate(bool accelerating);<br />
    void reverse(bool reversing);</p>
<p>    void onTouchesBegan(const std::vector&lt;Touch*&gt;&amp; touches, Event *event);<br />
    void onTouchesEnded(const std::vector&lt;Touch*&gt;&amp; touches, Event *event);<br />
    void onTouchesMoved(const std::vector&lt;Touch*&gt;&amp; touches, Event *event);<br />
public:<br />
    bool isAccelerating() {<br />
       return _accelerating;<br />
    }</p>
<p>    bool isReversing() {<br />
       return _reversing;<br />
    }</p>
<p>    virtual bool init();<br />
    CREATE_FUNC(Controls);<br />
};</p>
<p>#endif<br />
[/code]</p>
<p>Controls.cpp<br />
[code language=&#8221;cpp&#8221;]<br />
#include &quot;Controls.h&quot;</p>
<p>USING_NS_CC;</p>
<p>bool Controls::init() {<br />
    if(!Layer::init()) {<br />
        return false;<br />
    }</p>
<p>    _brakeTouch = nullptr;</p>
<p>    _reversing = false;<br />
    _accelerating = false;</p>
<p>    const Point leftPedalPosition = Point(100, 100);<br />
    const Point rightPedalPosition = Point(Director::getInstance()-&gt;getVisibleSize().width &#8211; 100, 100);</p>
<p>    _brakeSprite = Sprite::create(&quot;images/brake.png&quot;);<br />
    _brakeDownSprite = Sprite::create(&quot;images/brake_down.png&quot;);<br />
    _brakeSprite-&gt;setPosition(leftPedalPosition);<br />
    _brakeDownSprite-&gt;setPosition(leftPedalPosition);</p>
<p>    _accelerateSprite = Sprite::create(&quot;images/accelerate.png&quot;);<br />
    _accelerateDownSprite = Sprite::create(&quot;images/accelerate_down.png&quot;);<br />
    _accelerateSprite-&gt;setPosition(rightPedalPosition);<br />
    _accelerateDownSprite-&gt;setPosition(rightPedalPosition);</p>
<p>    addChild(_brakeSprite);<br />
    addChild(_brakeDownSprite);<br />
    addChild(_accelerateSprite);<br />
    addChild(_accelerateDownSprite);</p>
<p>    _brakeDownSprite-&gt;setVisible(false);<br />
    _accelerateDownSprite-&gt;setVisible(false);</p>
<p>    auto listener = EventListenerTouchAllAtOnce::create();<br />
    listener-&gt;onTouchesBegan = CC_CALLBACK_2(Controls::onTouchesBegan, this);<br />
    listener-&gt;onTouchesMoved = CC_CALLBACK_2(Controls::onTouchesMoved, this);<br />
    listener-&gt;onTouchesEnded = CC_CALLBACK_2(Controls::onTouchesEnded, this);<br />
    _eventDispatcher-&gt;addEventListenerWithSceneGraphPriority(listener, this);</p>
<p>    return true;<br />
}</p>
<p>void Controls::accelerate(bool accelerating) {<br />
    _accelerating = accelerating;<br />
    if(_accelerating) {<br />
        _accelerateDownSprite-&gt;setVisible(true);<br />
        _accelerateSprite-&gt;setVisible(false);<br />
    } else {<br />
        _accelerateDownSprite-&gt;setVisible(false);<br />
        _accelerateSprite-&gt;setVisible(true);<br />
    }<br />
}</p>
<p>void Controls::reverse(bool reversing) {<br />
    _reversing = reversing;<br />
    if(_reversing) {<br />
        _brakeDownSprite-&gt;setVisible(true);<br />
        _brakeSprite-&gt;setVisible(false);<br />
    } else {<br />
        _brakeDownSprite-&gt;setVisible(false);<br />
        _brakeSprite-&gt;setVisible(true);<br />
    }<br />
}</p>
<p>//all the incoming touches will go trough this method. &lt;em&gt;touches&lt;/em&gt; array contains all current touches<br />
//on the screen and therefore we be looping through each of those and checking if one of those touches<br />
//are on one or both of the pedals. Note that if one of those touch events has a code of //EventTouch::EventCode::ENDED, it means touch has ended and therefore no pedal is pressed.<br />
void Controls::handleTouches(const std::vector&lt;Touch*&gt; &amp;touches, Event* event) {<br />
    bool reversing = false;<br />
    bool accelerating = false;<br />
    for(int i = 0; i &lt; touches.size(); ++i) {<br />
        if(_brakeSprite-&gt;boundingBox().containsPoint(touches[i]-&gt;getLocation()) &amp;&amp; ((EventTouch*)event)-&gt;getEventCode() != EventTouch::EventCode::ENDED) {<br />
            reversing = true;<br />
        } else if(_accelerateSprite-&gt;boundingBox().containsPoint(touches[i]-&gt;getLocation()) &amp;&amp; ((EventTouch*)event)-&gt;getEventCode() != EventTouch::EventCode::ENDED) {<br />
            accelerating = true;<br />
        }<br />
    }<br />
    reverse(reversing);<br />
    accelerate(accelerating);<br />
}</p>
<p>void Controls::onTouchesBegan(const std::vector&lt;Touch*&gt;&amp; touches, Event* event) {<br />
    handleTouches(touches, event);<br />
}</p>
<p>void Controls::onTouchesEnded(const std::vector&lt;Touch*&gt;&amp; touches, Event* event) {<br />
    handleTouches(touches, event);<br />
}</p>
<p>void Controls::onTouchesMoved(const std::vector&lt;Touch*&gt;&amp; touches, Event* event) {<br />
    handleTouches(touches, event);<br />
}<br />
[/code]</p>
<p>Our Controls class has two &#8216;outputs&#8217;, <em>isAccelerating()</em> and <em>isReversing()</em>. We will connect these &#8216;outputs&#8217; to our car&#8217;s <em>accelerate</em> and <em>reverse</em> methods after we have first initialised Controls class. </p>
<p>HelloWorld.h<br />
[code language=&#8221;cpp&#8221;]<br />
&#8230;<br />
#include &quot;Controls.h&quot;</p>
<p>class HelloWorld : public cocos2d::Layer {<br />
private:<br />
&#8230;<br />
    Controls* _controls;<br />
}<br />
[/code]</p>
<p>HelloWorld.cpp<br />
[code language=&#8221;cpp&#8221;]<br />
bool HelloWorld::init() {<br />
    &#8230;<br />
    _controls = Controls::create();<br />
    _controls-&gt;setZOrder(1000.0f);<br />
    addChild(_controls);<br />
}<br />
[/code]</p>
<p>If you now run the code, you should se pedals on left and right of your screen which you can press down in whatever ways you want, but of course it won&#8217;t be moving our car because we haven&#8217;t connected controls to our car. Lets next monitor state of the Controls in <em>HelloWorld::update</em> method, where we can update car&#8217;s state according to Controls state.</p>
<p>[code language=&#8221;cpp&#8221;]<br />
void HelloWorld::update(float dt) {<br />
   &#8230;<br />
   _car-&gt;accelerate(_controls-&gt;isAccelerating());<br />
   _car-&gt;reverse(_controls-&gt;isReversing());<br />
}<br />
[/code]</p>
<p>Now by running the code you should be able to drive the car back and forward, however there is no camera yet to follow the car. Lets do that next.</p>
<p><a name="Camera"></p>
<h2>3. Camera class</h3>
<p></a><br />
Camera is pretty essential thing when game contains something that can move freely in the game area which is larger than the game window. This game would be pointless if there was no camera following the car. We will add little extra to the camera, by adding a feature that zooms in and out depending on the vehicle&#8217;s velocity.</p>
<p>Lets continue coding. We are using CarCamera name for the class so it won&#8217;t be mixed with Cocos2d-x&#8217;s Camera class. You could also use namespaces to solve this issue, but this will suit our purposes now.</p>
<p>CarCamera.h<br />
[code language=&#8221;cpp&#8221;]<br />
#ifndef __CAMERA_H__<br />
#define __CAMERA_H__</p>
<p>#include &lt;Box2D/Box2D.h&gt;<br />
#include &quot;cocos2d.h&quot;</p>
<p>#include &quot;config.h&quot;</p>
<p>USING_NS_CC;</p>
<p>class CarCamera {<br />
private:<br />
    //we need separate Layer for scaling<br />
    Layer* _scaleLayer;</p>
<p>    //this will be our Terrain node<br />
    Node* _node;</p>
<p>    const float MaxZoom = 1.5f;<br />
    const float MinZoom = 0.6f;<br />
    const float ZoomSpeed = 0.001f;</p>
<p>    float _targetScale;<br />
    float _zoomVelocity;</p>
<p>    b2Body* _targetBody;</p>
<p>    Point getOffset();<br />
public:<br />
    CarCamera(Layer* scaleLayer, Node* node);<br />
    ~CarCamera();</p>
<p>    void setTarget(b2Body* target);<br />
    void update(float dt);<br />
};</p>
<p>#endif<br />
[/code]</p>
<p>CarCamera.cpp<br />
[code language=&#8221;cpp&#8221;]<br />
#include &quot;CarCamera.h&quot;</p>
<p>USING_NS_CC;</p>
<p>CarCamera::CarCamera(Layer* scaleLayer, Node* node) : _scaleLayer(scaleLayer), _node(node), _targetBody(nullptr), _zoomVelocity(1.0f) {<br />
}</p>
<p>CarCamera::~CarCamera() {</p>
<p>}</p>
<p>Point CarCamera::getOffset() {<br />
    float offsetX = 0;<br />
    float offsetY = 0;<br />
    if(_targetBody) {<br />
        //convert box2d body position into pixel units and add offset to position to center of the screen<br />
        float halvedScreenWidth = Director::getInstance()-&gt;getVisibleSize().width / 2.0f;<br />
        float halvedScreenHeight = Director::getInstance()-&gt;getVisibleSize().height / 2.0f;<br />
        offsetX = -(_targetBody-&gt;GetPosition().x * PTM_RATIO) + halvedScreenWidth;<br />
        offsetY = -(_targetBody-&gt;GetPosition().y * PTM_RATIO) + halvedScreenHeight;<br />
    }<br />
    return Point(offsetX, fmin(0, offsetY));<br />
}</p>
<p>void CarCamera::setTarget(b2Body* targetBody) {<br />
    _targetBody = targetBody;<br />
}</p>
<p>void CarCamera::update(float dt) {<br />
    Point targetOffset = getOffset();<br />
    _node-&gt;setPosition(targetOffset);</p>
<p>    //if we have set a target body for the camera, it&#8217;ll start following that body<br />
    if(_targetBody) {<br />
        float velocity = fabs(_targetBody-&gt;GetLinearVelocity().Length());<br />
        float zoom = (velocity / 60.0f);</p>
<p>        _zoomVelocity = MaxZoom &#8211; zoom;</p>
<p>        if(_zoomVelocity &gt; _scaleLayer-&gt;getScale()) {<br />
            _zoomVelocity = _scaleLayer-&gt;getScale() + ZoomSpeed;<br />
        } else if(_zoomVelocity &lt; _scaleLayer-&gt;getScale()) {<br />
            _zoomVelocity = _scaleLayer-&gt;getScale() + -ZoomSpeed;<br />
        }</p>
<p>        _zoomVelocity = clampf(_zoomVelocity, MinZoom, MaxZoom);<br />
        _scaleLayer-&gt;setScale(_zoomVelocity);<br />
    }<br />
}<br />
[/code]</p>
<p>Now we have set up the camera, now we need to take it into use and what it needs is to do an initialisation and updating. Therefore:</p>
<p>HelloWorldScene.h<br />
[code language=&#8221;cpp&#8221;]<br />
&#8230;<br />
#include &quot;CarCamera.h&quot;</p>
<p>class HelloWorld : public cocos2d::Layer {<br />
private:<br />
    &#8230;<br />
    CarCamera* _camera;<br />
    Layer* _scaleLayer;<br />
[/code]</p>
<p>Note that <em>_terrain</em> node is added as a child to <em>_scaleLayer</em>, so comment out code <em>addChild(_terrain)</em>. Scale layer is a little trick used to handle zoom in out without messing too much with the rendering of the terrain.</p>
<p>[code language=&#8221;cpp&#8221;]<br />
bool HelloWorld::init() {<br />
    &#8230;<br />
    _scaleLayer = Layer::create();<br />
    addChild(_scaleLayer);</p>
<p>    //addChild(_terrain);<br />
    _terrain = Terrain::create();<br />
    _scaleLayer-&gt;addChild(_terrain);</p>
<p>    _camera = new CarCamera(_scaleLayer, _terrain);<br />
    _camera-&gt;setTarget(_car-&gt;getBody());<br />
[/code]</p>
<p>[code language=&#8221;cpp&#8221;]<br />
void HelloWorld::update(float dt) {<br />
    &#8230;<br />
    _camera-&gt;update(dt);<br />
[/code]</p>
<p>Now if you run the code, camera should follow the pickup wherever it goes. Notice, that if you drive fast enough, rendering of the terrain should not cover entire screen. This can be exercise for you to solve. If it turns out to be too hard to solve, feel free to comment. <img src="https://s.w.org/images/core/emoji/14.0.0/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<p>What is left for this chapter, is to create coins and fuel cans to be collected.</p>
<p><a name="CoinsFuels"></p>
<h2>4. Coins and fuel cans</h3>
<p></a><br />
<a href="http://www.mikkolajoni.com/wp-content/uploads/2015/05/coins.jpg"><img decoding="async" loading="lazy" src="http://www.mikkolajoni.com/wp-content/uploads/2015/05/coins-300x136.jpg" alt="Coins and fuel cans can be collected." width="300" height="136" class="aligncenter size-medium wp-image-842" srcset="https://jonimikkola.com/wp-content/uploads/2015/05/coins-300x136.jpg 300w, https://jonimikkola.com/wp-content/uploads/2015/05/coins.jpg 418w" sizes="(max-width: 300px) 100vw, 300px" /></a></p>
<p>So as already stated, this is last part of this chapter. We will create coin and fuel can sprites on top of the track properly separated in distance from each other. In addition to that we will add simple collision detection so we can identify if car hits those sprites. Fuel actually does not ever end nor coins does not actually sum up in total money, but those issues will be resolved in next chapters. Well, lets keep going.</p>
<p>Insert following code strip in <em>Terrain.h</em> just below <em>#define</em> fields. With this we will identify collisions between each of these types.<br />
[code language=&#8221;cpp&#8221;]<br />
struct ItemType {<br />
    enum type {<br />
        Coin,<br />
        Fuel,<br />
        Car<br />
    };<br />
};<br />
[/code]</p>
<p>Also add this method in <em>Terrain.h.</em><br />
[code language=&#8221;cpp&#8221;]<br />
class Terrain : public Node {<br />
public:<br />
    &#8230;<br />
    void generateItems(std::vector&lt;Sprite*&gt;&amp; sprites);<br />
[/code]</p>
<p>Terrain.cpp<br />
[code language=&#8221;cpp&#8221;]<br />
void Terrain::generateItems(std::vector&lt;Sprite*&gt;&amp; sprites) {<br />
    //this is spacing between 5 sets of coins. If GroundStep definition in Terrain.h equals 20 for example<br />
    //coinSpacing would mean 100 * 20 = 2000 pixels and the same goes for positioning coins and fuel cans below<br />
    int coinSpacing = 100;<br />
    int coinAmount = 5;<br />
    Point distanceFromGround = Point(0.0f, 30.0f);</p>
<p>    int fuelIndex = 0;<br />
    for(int i = coinSpacing; i &lt; MaxXYPoints; i += coinSpacing) {<br />
        fuelIndex++;<br />
        for(int j = 0; j &lt; coinAmount; ++j) {<br />
            Sprite* coinSprite = Sprite::create(&quot;images/coin.png&quot;);<br />
            coinSprite-&gt;setTag(ItemType::Coin);<br />
            coinSprite-&gt;setPosition(_xyPoints[i + j * 3] + distanceFromGround);<br />
            addChild(coinSprite);<br />
            sprites.push_back(coinSprite);</p>
<p>            //in every 5th set of 5 coins a fuel can is positioned<br />
            if(j == coinAmount &#8211; 1 &amp;&amp; fuelIndex &gt; 4) {<br />
                Sprite* fuelCanSprite = Sprite::create(&quot;images/fuel_can.png&quot;);<br />
                fuelCanSprite-&gt;setPosition(_xyPoints[i + j * 3] + distanceFromGround);<br />
                fuelCanSprite-&gt;setTag(ItemType::Fuel);<br />
                addChild(fuelCanSprite);<br />
                sprites.push_back(fuelCanSprite);<br />
                fuelIndex = 0;<br />
            }<br />
        }<br />
    }<br />
}<br />
[/code]</p>
<p>If you noticed, <em>generateItems</em> method takes a vector as a reference and that is because we will monitor collisions between coins and fuel cans in HelloWorld class.</p>
<p>HelloWorldScene.h<br />
[code language=&#8221;cpp&#8221;]<br />
class HelloWorld : public cocos2d::Layer {<br />
private:<br />
    &#8230;<br />
    std::vector&lt;Sprite*&gt; _sprites;<br />
[/code]</p>
<p>Now the only thing to do, is to call <em>generateItems</em> method to populate the track and then monitor collisions in <em>HelloWorld::update</em>.</p>
<p>HelloWorldScene.cpp<br />
[code language=&#8221;cpp&#8221;]<br />
bool HelloWorld::init() {<br />
    &#8230;<br />
    _terrain-&gt;generateItems(_sprites);<br />
}</p>
<p>void Game::update(float dt) {<br />
    &#8230;<br />
    for(int i = 0; i &lt; _sprites.size(); ++i) {<br />
    if(_car-&gt;getSprite()-&gt;boundingBox().intersectsRect(_sprites[i]-&gt;boundingBox()) &amp;&amp; _sprites[i]-&gt;isVisible()) {<br />
        if(_sprites[i]-&gt;getTag() == ItemType::Coin || _sprites[i]-&gt;getTag() == ItemType::Fuel) {<br />
            _sprites[i]-&gt;runAction(FadeTo::create(1.0f, 0));<br />
            _sprites[i]-&gt;runAction(MoveBy::create(1.0f, Point(0, 50.0f)));<br />
            _sprites[i]-&gt;setTag(-1);<br />
        }<br />
    }<br />
}<br />
[/code]</p>
<p>By now you should be able to run the code and drive the pickup while collecting money as the camera follows you from a distance depending of the vehicle&#8217;s velocity.</p>
<p>In case you&#8217;ve come this far and have some questions related to this chapter, feel free to ask. Hopefully I was able to cover these topics well enough for you to have better understanding of these concepts.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://jonimikkola.com/hill-climb-racing-deconstructed-car-controls-camera-coins-and-fuel-cans/feed/</wfw:commentRss>
			<slash:comments>9</slash:comments>
		
		
			</item>
	</channel>
</rss>
