Wednesday, May 8, 2013

Bounce timing / easing function

Working on our next game the graphic designer designed one of the animations in such a way that I needed to write special timing function. There is big block falling from top of the screen, that bounces several times and stays still. I wanted to enrich my engine with function that will not only do this job, but will be also useful in future. I ended with flexible function that produces bounces based on given parameters and on the next lines I will describe it step by step.

Parameters & result

I will start with brief gallery of achieved results. The function takes four parameters:
•  duration of whole effect in seconds,
•  number of bounces (actually how much times the floor is touched),
•  elasticity,
•  whether to start the first bounce from floor or from top
You can call the function with all four parameters set but if you want for example bounce three times and stay still then you can omit elasticity (set to -1) and the function will calculate it for you. On the following pictures is what I am exactly writing about - I am asking to produce such a curve that it should take 3 seconds and 3 bounces and then the bouncing object will stay still. In first case I want to start it from top (for example something may fall from off screen region into visible area) and in the second case I want to make it jump from bottom:

In the first of following two I am saying, that I want 3 seconds again, elasticity 0.5 and start from top. I do not know how many bounces is necessary to leave the object on floor. But the function calculates for me it is 8 of them and calculates the speed of move to squeeze it into 3 seconds.
In the second I am setting the elasticity over 1 so I will overshot 0.0-1.0 output range and so I have to define the number of bounces else the function would try to squeeze infinite number of bounces into three seconds. It ends itself only when zero height is reached or when requested number of bounces is met.

Look at the header file below. You will see we are defining some variables, constants and functions. The constants will limit our function to 10 bounces as well as it defines epsilon error value that will be explained during implementation.
The variables holds the ones needed for whole function - as its duration, number of bounces, elasticity, calculated acceleration and so on. And it also holds values specific for every single bounce - its duration, initial velocity and height. Yes, the resulting function faces itself for programmer as single function but it is inside series of individual consecutive bounces.
The functions are simple getters (imagine you are calling with unknown number of bounces in initialization; you can ask then how many of them was calculated) and functions that set and return the height based on duration progress.

One remark: the function is taken from my cross-platform engine (you can read other posts regarding it on this blog) so do not get confused with specific namespaces. Rewrite them with yours or delete it.

#ifndef TIMINGBOUNCE_H_
#define TIMINGBOUNCE_H_

#include "../../System/system.h"

namespace SBC
{
namespace Engine
{

class TimingBounce
{
public:
static const u32 BOUNCES_MAX = 10;
static const f32 EPSILON;
static const f32 INTERNAL_HEIGHT;

public:
TimingBounce();
virtual ~TimingBounce();

public:
void initialize(f32 aDuration, s32 aBounces, f32 aElasticity = -1.0f, bool aHalveFirstBounce = true);
f32 getActual();

// getters
f32 getDuration();
f32 getDurationProgress();
s32 getBounces();
f32 getElasticity();
f32 getAcceleration();

private:
// duration of function
f32 mDuration;
// actual position in duration
f32 mDurationProgress;

// number of bounces
s32 mBounces;
// elasticity - how high is next amplitude
f32 mElasticity;
// acceleration for requested parameters
f32 mAcceleration;
// start from peek or from bottom
bool mHalveFirstBounce;

// duration of particular bounces
f32 mBounceDuration[BOUNCES_MAX];
// height of particular bounces
f32 mBounceHeight[BOUNCES_MAX];
// bounce velocity
f32 mBouceVelocity[BOUNCES_MAX];
};

} /* namespace Engine */
} /* namespace SBC */
#endif /* TIMINGBOUNCE_H_ */


Implementation

Next follows the implementation. It is cut into pieces and described and explained step by step:

We simply start with defining some of the constants. The EPSILON is error member and is set to 1, which is in most cases 1 pixel on the screen. The INTERNAL_HEIGHT is defined as 1000. The function inside calculates the height of bounces in range 0-1000 and this is then normalized into 0-1 before vales are returned to client.

Constructor simply sets initial values. Actually undefined elasticity (-1.0f) and zero number of bounces are together invalid parameters.

#include "TimingBounce.h"

#undef LOG_TAG
#define LOG_TAG  "TimingBounce"

namespace SBC
{
namespace Engine
{

using namespace SBC::System::MathUtils;

const f32 TimingBounce::EPSILON = 1.0f;
const f32 TimingBounce::INTERNAL_HEIGHT = 1000.0f;

//------------------------------------------------------------------------
TimingBounce::TimingBounce()
{
mDuration = 0.0f;
mDurationProgress = 0.0f;
mBounces = 0;
mElasticity = -1.0f;
mAcceleration = 0.0f;
mHalveFirstBounce = false;
}

//------------------------------------------------------------------------
TimingBounce::~TimingBounce()
{
}


Now comes the initialize function where most of the fun takes place:

//------------------------------------------------------------------------
void TimingBounce::initialize(f32 aDuration, s32 aBounces, f32 aElasticity, bool aHalveFirstBounce)
{
// check parameters validity
if (aBounces <= 0 && aElasticity < 0.0f)
{
LOGE("Invalid parameters (aBounces = %i, aElasticity = %f)", aBounces, aElasticity);
return;
}
{
LOGE("Duration cannot be less than zero");
return;
}


First we check whether input parameters are correct. Either one of aBounces or elasticity must be defined (bounces higher than zero and / or elasticity also higher than 0).

 // calculate missing parameters
// if defined bounces but not elasticity
if (aBounces > 0 && aElasticity < 0.0f)
{
aElasticity = Math::pow(EPSILON / INTERNAL_HEIGHT, 1.0f / aBounces);

}
// if defined elasticity but not bounces
else if (aElasticity > 0.0f && aBounces <= 0)
{
if (aElasticity >= 1.0f)
{
LOGE("Elasticity must be less than 1");
return;
}

// EPSILON = aElasticity ^ aBounces ... aBounces = log_aElasticity EPSILON = ln EPSILON / ln aElasticity
aBounces = Math::log(EPSILON / INTERNAL_HEIGHT) / Math::log(aElasticity);
}


If we know the number of bounces and elasticity is unknown we have to calculate it. It will have such a value that after requested number of bounces the potential next bounce would had its height less or equal to EPSILON. It comes from calculation:

$\text{EPSILON}=\text{INTERNAL_HEIGHT}\ast {\mathrm{elasticity}}^{\mathit{bounces}}$
$\mathit{elasticity}={\mathit{EPSILON}/\text{INTERNAL_HEIGHT}}^{1/\mathit{bounces}}$

In second case the unknown are the bounces so the calculation is:

$\mathit{bounces}=\frac{\mathrm{log}\left(\text{EPSILON}/\text{INTERNAL_HEIGHT}\right)}{\mathrm{log}\left(\mathit{elasticity}\right)}$

Now when we know the parameters we can save it:

 // store parameters
mDurationProgress = 0.0f;
mBounces = aBounces;
mElasticity = aElasticity;
mHalveFirstBounce = aHalveFirstBounce;


But with the parameter above we still do not know how much time the function will take. We request some time but we do not know the speed. So, we have to calculate it. As the whole function is not a single function but internally it is sequence of functions we will choose some random speed to calculate how much time each bounce takes and calculate the total time
Each bounce takes 2 times the result of:

$\mathit{height}=\frac{1}{2}\mathit{acceleration}\ast {\mathit{time}}^{2}$

$\mathit{time}=\sqrt{\frac{2\ast \mathit{height}}{\mathit{acceleration}}}$

Two times because we have to reach the top of bounce and then the same time it takes to fall down.

 // get "some" acceleration and calculate time for bounces
f32 acceleration = INTERNAL_HEIGHT / 1000.0f;
f32 totalDuration = 0.0f;
f32 height = INTERNAL_HEIGHT;
for (s32 i = 0; i < mBounces; i++)
{
// s = 1/2 a * t^2 ... 2s / a = t^2 ... sqrt(2s / a) = t
f32 duration = Math::sqrt(2 * height / acceleration) * 2;

if (mHalveFirstBounce && i == 0)
duration /= 2;

mBounceDuration[i] = duration;
mBounceHeight[i] = height;

totalDuration += duration;
height *= mElasticity;
}


Let's say that the total duration resulted in 340 seconds with some initial velocity. This is more than 100 times more than we requested. But as we have the time ratio between the bounces we can adjust it to our requested time:

 // adjust total duration to fit requested duration
mDuration = 0.0f;
for (s32 i = 0; i < mBounces; i++)
{
f32 duration = mBounceDuration[i] * aDuration / totalDuration;
mBounceDuration[i] = duration;
// sum up to avoid imprecision
mDuration += duration;
}


Now, when we are in requested time limit, we have to calculate the acceleration that will help us to achieve it (again the same formula is used but the unknown is the acceleration this time):

 // calculate new acceleration
f32 firstHalfBounceDuration = mHalveFirstBounce ? mBounceDuration[0] : mBounceDuration[0] / 2;
// s = 1/2 a * t^2 ... 2s / t^2 = a
mAcceleration = (2.0f * INTERNAL_HEIGHT) / (firstHalfBounceDuration * firstHalfBounceDuration);


Finally we can calculate the parameters for each bounce:

 // calculate initial bounce velocities
for (s32 i = 0; i < mBounces; i++)
{
// v = v0 + at ... on the top of bounce the v equals zero => v0 = -at
// if bounce starts halved (on top) than its initial velocity is zero
// halve duration of each bounce (as it contains the way up and down)
if (i == 0 && aHalveFirstBounce)
mBouceVelocity[i] = 0.0f;
else
mBouceVelocity[i] = mBounceDuration[i] / 2.0f * mAcceleration;
}

// change the sign of acceleration to point downwards
mAcceleration = -mAcceleration;


The debug output is now commented out:

 // debug output
/*
LOGD("Bounces: %i, Elasticity: %f, Acceleration: %f, Duration: %f, HalveFirstBounce %s",
mBounces, mElasticity, mAcceleration, mDuration, mHalveFirstBounce ? "true" : "false");
for (s32 i = 0; i < mBounces; i++)
{
LOGD("Bounce %i: height = %f, duration = %f, velocity = %f",
i, mBounceHeight[i], mBounceDuration[i], mBouceVelocity[i]);
}
*/
}


The function is initialized now so we can start using it. There are three function - one tracks current position within requested time and is called tick(). Its parameter is time elapsed from last frame so you can feed it with your game loop timing steps. The second takes the value for current position and the last one returns value from any requested position. this one is the most important one and it is the place where things happens:

//------------------------------------------------------------------------
{

// return actual value
return getAt(mDurationProgress);
}

//------------------------------------------------------------------------
f32 TimingBounce::getActual()
{
return getAt(mDurationProgress);
}

//------------------------------------------------------------------------
{
// check time bounds


After check of bounds we have to find index of the bounce we are currently in:

 s32 index = 0;
f32 totalDuration = 0.0f;

// get index to particular bounce
while(index < mBounces && aDurationProgress > totalDuration + mBounceDuration[index])
{
totalDuration += mBounceDuration[index];
++ index;
}

// get duration within bounce (if not the first one)


and then we can calculate the height in range 0 - INTERNAL_HEIGHT and normalize it to 0-1:

 f32 height = 0.0f;
if (index == 0 && mHalveFirstBounce)
{
// height = height + 1/2 * mAcceleration * aDurationProgress^2

}
else
{
// height = mBounceVelocity * aDurationProgress + 1/2 * mAcceleration * aDurationProgress^2
// height = aDurationProgress * (mBounceVelocity + 1/2 * mAcceleration * aDurationProgress)
}

return height / INTERNAL_HEIGHT;
}


For completeness here are also getter functions:

//------------------------------------------------------------------------
f32 TimingBounce::getDuration()
{
return mDuration;
}

//------------------------------------------------------------------------
f32 TimingBounce::getDurationProgress()
{
return mDurationProgress;
}

//------------------------------------------------------------------------
s32 TimingBounce::getBounces()
{
return mBounces;
}

//------------------------------------------------------------------------
f32 TimingBounce::getElasticity()
{
return mElasticity;
}

//------------------------------------------------------------------------
f32 TimingBounce::getAcceleration()
{
return mAcceleration;
}

} /* namespace Engine */
} /* namespace SBC */


Usage

To demonstrate the use our bouncing function all you have to do for example is something like this (this will produce the output you have seen on the first graph):

 TimingBounce b;
b.initialize(3.0f, 3, -1.0f, true);

for (f32 i = 0.0f; i < 3.0f; i = i + 0.01f)
LOGD("height: %f", b.tick(0.01f));


Conclusion

So, we created bouncing function that is flexible enough. It also hides all its details (series of functions) inside and the user just initializes it with desired values. You can download the source here.

Sunday, March 10, 2013

Shards - Work In Progress I

This is short info on our upcoming game - Shards - we are working on. The game will be clone of well known arcade classic with glassy look and very nice fractal backgrounds. The music in video is just a placeholder I found on the web - it sounds to make me sure the sound engine works.

Making the game we are using these technologies:
• box2D for physics,
• notice the usage of our universal box2D debug draw that works for Open GL ES 1.x and 2.0,
• our cross platform engine -  the video is taken from desktop Windows while the game runs on Android as well,
• PicOpt - our sprite atlas creation tool that is free to download,
• for Android NDK: textures are loaded through this way, while sound (not too much of them in the game presently) is coded like this,

Tuesday, March 5, 2013

previous parts

Finally, we are making ad manager to handle multiple networks, so it is not wise to rely only on one network.
If you intent to use Leadbolt you can register through this link. If you do so, I will get some small reward from them :-)

This should be no problem. Leadbolt has very good and simple manual. It is located under App Developer SDKs in HELP/FAQS section. Actually it is the same as AdMob integration - copy .jar with SDK into libs folder and then adjust Build Path in Project Properties (right click on project name in Eclipse). Then Add JARs...

As for previous ad networks we have to adjust manifest file with permissions. Leadbolt has three required permissions: INTERNET, ACCESS_NETWORK_STATE and READ_PHONE_STATE. The first two we already have from times of AdMob implementation. There are three optional permissions: ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION and ACCESS_ LOCATION_EXTRA_COMMANDS. The coarse location we already allowed for Madvertise so we adjust the manifest like this:

  <!-- additional LeadBolt permissions -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS"/>


While most of the other networks has some way how to set refresh rate for Leadbolt you have to do it by yourself. So in the file with constants (SBCAdsConstatnts.java) we will define these lines:

 // Leadbolt
public static final long LEADBOLT_REFRESH_RATE = 1000 * 90 * 1;

The first line contains the ID your ad unit is assigned by Leadbolt. The second line defines refresh rate in millis - here 1,5 minute.

Now let's start implementation. As usual the imports and fields come first:

package com.sbcgames.sbcads;import com.pad.android.iappad.AdController;import com.pad.android.listener.AdListener;import com.sbcgames.sbcengine.SBCEngine;import android.util.Log;import android.view.KeyEvent;import android.view.ViewGroup;public class LeadboltSystem implements SBCAdSystem, AdListener
{
private final String SYSTEM_NAME = "LEADBOLT ";
private final ViewGroup mLayout;
private Runnable mR = null;
private SBCEngine mSBCEngine = null;

The constructor is very simple:

 // ------------------------------------------------------------------------
{
mSBCEngine = aSBCEngine;
mLayout = aLayout;
}

Next follows implementation of our SBCAdSystem interface:

 //------------------------------------------------------------------------
//------------------------------------------------------------------------
@Override
public void start()
{

mR = new Runnable()
{
public void run()
{

{
stop();
destroy();
}

}
};

mLayout.postDelayed(mR, 0);
}

Here is problem that I encountered: to refresh ad, calling to destroyAd() (called in destroy() method) and then calling to loadAd() should be enough. But the refreshing in this way did not work for me. So I am destroying the mAdView (the AdController object) every time and creating new one. I posted question to forum and I got reply from user called jay-z. While the above method works, I will try his code and update this article accordingly if it will work for me too.

Here is implementation of next methods - nothing special here:

 //------------------------------------------------------------------------
@Override
public void stop()
{
if (mR != null)
{
mLayout.removeCallbacks(mR);
}
}

//------------------------------------------------------------------------
@Override
public void destroy()
{
{
}
}

//------------------------------------------------------------------------
@Override
{
mListener = aListener;
}

In last two parts when implementing AdMob and Madvertise we left our keyDown implementation empty. For Leadbolt we have to handle back key. This is the reason why we have this method in interface. Of course, to avoid implementation in every ad system we could create abstract class between interface and concrete implementation (that will do nothing) and override it only when we need specialised behavior - I will leave this small change on your choice:

 //------------------------------------------------------------------------
@Override
public boolean keyDown(final int aKeyCode, final KeyEvent aEvent)
{
if (aKeyCode == KeyEvent.KEYCODE_BACK)
{
{
Log.d("AdsSystem", SYSTEM_NAME + "back key pressed");
return true;
}
}

return false;
}

 //------------------------------------------------------------------------
//------------------------------------------------------------------------
@Override
{
stop();
mSBCEngine.sendMessage(SBCEngine.MESSAGE_OUT_PAUSE, 0, 0);
}

//------------------------------------------------------------------------
@Override
{
}

//------------------------------------------------------------------------
@Override
{
}

//------------------------------------------------------------------------
@Override
{
}

//------------------------------------------------------------------------
@Override
{
mListener.onFailed(this);
}

//------------------------------------------------------------------------
@Override
{
}

//------------------------------------------------------------------------
@Override
{
}

//------------------------------------------------------------------------
@Override
{
}

//------------------------------------------------------------------------
@Override
{
}

//------------------------------------------------------------------------
@Override
{
}}

Conclusion

This is all for Leadbolt implementation. This was also last part of this little series. We created simple but extensible ad manager. Now it is up to you to enhance it and add other ad networks.

Sunday, March 3, 2013

previous parts

Last time we already added permissions for INTERNET and ACCESS_NETWORK_STATE. Today we will add optional permissions for reading state of wifi and to roughly detect the location of the user.

    <!-- additional Madvertise permissions -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />


Next we need to adjust the manifest file with Madvertise activity. There is also place for token identifying your app. If you just want to test then put word "TestTokn" there. Do not forget to replace it when you are ready to publish.

        <activity android:name="de.madvertise.android.sdk.MadvertiseActivity" />

<!-- Setup your site token. For tests, use 'TestTokn' -->
<meta-data
android:value="TestTokn" />


package com.sbcgames.sbcads;

import com.sbcgames.sbcengine.SBCEngine;

import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;

{
private final String SYSTEM_NAME = "MADVERTISE ";
private ViewGroup mLayout;


 // ------------------------------------------------------------------------
{
mLayout = aLayout;

// new view - all defaults

}


 //------------------------------------------------------------------------
@Override
public void start()
{

}


Calling setFetchingAdsEnabled with true will launch our request for ad. With the result of this reqest we will be notified through MadvertiseViewCallbackListener methods.

Next we need methods to stop and destroy this ad system in case we are switching to another one.

 //------------------------------------------------------------------------
@Override
public void stop()
{
{
}
}

//------------------------------------------------------------------------
@Override
public void destroy()
{
{
}
mLayout.removeAllViews();
}


Finally there is method for setting the events listener and for processing keys. The later will again have no implementation for Madvertise:

 //------------------------------------------------------------------------
@Override
{
mListener = aListener;
}

//------------------------------------------------------------------------
@Override
public boolean keyDown(final int aKeyCode, final KeyEvent aEvent)
{
return false;
}


Now comes the implementation of the interface that is part of Madvertise SDK. Again it is very simple as most of the methods will just notify our listener (that means our ad manager that is implementing it) and the rest is more or less just some debug output to watch in LogCat if everything is going well.

 //------------------------------------------------------------------------
//------------------------------------------------------------------------
@Override
{

if (!succeed)
{
mListener.onFailed(this);
}
}

//------------------------------------------------------------------------
@Override
public void onError(Exception exception)
{

mListener.onFailed(this);
}

//------------------------------------------------------------------------
@Override
public void onIllegalHttpStatusCode(int statusCode, String message)
{
}

//------------------------------------------------------------------------
@Override
{
}

//------------------------------------------------------------------------
@Override
public void onApplicationPause()
{
}

//------------------------------------------------------------------------
@Override
public void onApplicationResume()
{
}


Tuesday, February 19, 2013

previous parts

In Part 2 we finished our SBCAdManager class. Now we have system that will choose the right ad network for us as well as check whether the network is alive (sending ads) and change to another one if the network is dead. But so far we did not implement any ad network. As was said in previous parts we will step by step implement AdMob, Madvertise and Leadbolt. The system we are developing in this series is working in our real games Deadly Abyss 2 and Mahjong Tris.

This time we will implement first of the ad networks - AdMob. (for clarity, I am using terms ad network and ad system interchangeably)

To provide common set of methods to control each of the implemented ad systems we will introduce interface class SBCAdSystem. In each of the ad systems in our ad engine we will implement the methods. Remember Part 1 and picture with all the files in com.sbcgames.sbcads package. The interface is in SBCAdSystem.java file:

package com.sbcgames.sbcads;

import android.view.KeyEvent;

{
public void start();
public void stop();
public void destroy();
public boolean keyDown(final int aKeyCode, final KeyEvent aEvent);
}


Every Ad network needs some permissions enabled. AdMob is not as hungry as other so internet access and network state reading is enough. Put the following lines into your manifest file:

    <!-- AdMob SDK requires Internet permission -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />


        <!-- AdMobActivity definition -->
<activity
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize" />


Next we will adjust our file with constants (file SBCAdsConstatns.java) . I am again referring to Part 1 where the file is listed. Bellow is only the line related to AdMob implementation. Replace the text of the constant with the ID you get from AdMob site after you register your app - click Manage Settings and you should see something like this:

 // AdMob


package com.sbcgames.sbcads;

import com.sbcgames.sbcengine.SBCEngine;

import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;

{
private final String SYSTEM_NAME = "ADMOB ";
private ViewGroup mLayout;


The class implements SBCAdSystem interface as well as AdListener interface. The AdListener interface is part of AdMob package and its implementation will work as callback for various events like when the ad is received or when the request for ad failed. Our interface is here to unify the different ad systems and to provide some common set of methods to start, stop and so on the system.

The constructor looks like this:

 // ------------------------------------------------------------------------
{
mLayout = aLayout;

// new view

}


We store the reference to aLayout which is "adsholder" element in our main.xml layout file. This file we created in first part. Next we create new ad view - this comes from AdMob SDK. We add listener to it - it is the AdListener interface implementation and finally we add this new view into prepared "adsholder" in layout.

Now the object is created but we have to ask for ad. This is done in start method:

 //------------------------------------------------------------------------
//------------------------------------------------------------------------
@Override
public void start()
{

// Create an ad request. Check logcat output for the hashed device ID to
// get test ads on a physical device.

}


We first set view to be visible. Then we create request for AdMob ad. In following lines we can add test devices. I highly recommend this as this will deliver test ads into your device so clicking on them will not violate ad company policy. You can add the emulator as well as real devices you are testing your apps on.
Finally we launch our request. You will be notified if it is or is not successful through AdListener interface implementation.

The following 5 methods are implementation of SBCAdSystem interface.

As we want to change from one ad network to another, we need a way how to stop and destroy the ad view:

 //------------------------------------------------------------------------
@Override
public void stop()
{
{
}
}

//------------------------------------------------------------------------
@Override
public void destroy()
{
mLayout.removeAllViews();
}


These two methods are the ones called from SBCAdManager from stopAndDestroyAdSystem() method. It will disable displaying the view and cancel pending requests for ads. And in destroy() method it will destroy the ad view and remove it from "adsholder", so it is empty again and ready to hold any other ad view.

Last two methods from SBCAdSystem Interface are setEventListener() and keyDown(). The later has no implementation for AdMob, so we just say that AdMob did not consumed the sent key. The first one sets listener for events of this system. The listener is SBCAdManager class itself - it will listen for events from each ad system and based on it it will terminate network, change to another and so on:

 //------------------------------------------------------------------------
@Override
{
mListener = aListener;
}

//------------------------------------------------------------------------
@Override
public boolean keyDown(final int aKeyCode, final KeyEvent aEvent)
{
return false;
}


The implementation of Adlistener, the interface that comes with AdMob SDK is easy. In most of the methods only some debug print is implemented. Events which are reported is ad request failure and success. It is reported to listener, which is SBCAdManger. When loading of ad fails it can then stop and destroy this ad network and start another.

 //------------------------------------------------------------------------
//------------------------------------------------------------------------
@Override
{
}

//------------------------------------------------------------------------
@Override
{

mListener.onFailed(this);
}

//------------------------------------------------------------------------
@Override
{
}

//------------------------------------------------------------------------
@Override
{
}

//------------------------------------------------------------------------
@Override