Saturday, August 29, 2015

Interlude 15 - Site Update (Last update 6/01/17)

Over the years, Codea has continued to grow and evolve. As of this post, the current version is 2.3.1(47). With each new build, features are added and some old ways of doing things are dropped (deprecated if you want to get fancy).

This means that tutorials also need to be updated otherwise they become more of a problem than a solution. For the past year or so, I have been focussed on other activities but the time has come to do a major upgrade to the Codea Tutorials site. I will start by upgrading and retesting all of the tutorials against the latest version of Codea.

As each tutorial is updated, I will note this at the top and indicate the latest version of Codea it has been tested against.

In addition to updating all of the screenshots and adding figure numbers, I have added a lot of additional material and tried to make the tutorials clearer. All the code examples are also now correctly formatted using the online markup tool.

Finally, there are some new tutorials coming which will show how to build the classic lunar lander game in Codea.

As always, your comments and suggestions are welcome. Leave a comment here or contact me via the Reefwing Software site.

Status:

- Tutorial 1 updated                   - Interlude 7 updated
- Tutorial 2 updated                   - Interlude 8 updated
- Interlude 1 updated                 - Interlude 9 updated
- Tutorial 3 updated
- Interlude 2 updated
- Interlude 3 updated
- Interlude 4 updated
- Tutorial 4 updated
- Interlude 5 updated
- Tutorial 5 updated
- Interlude 6 updated
- Tutorial 6 updated

Friday, July 5, 2013

Tutorial 31 - Codea Collision Detection



31.1 Overview


Collision detection is a common requirement in game or simulator programming. It involves determining whether two or more objects have intersected. We will only be examining this problem in 2D (two dimensions) for this tutorial.

There are two main approaches to detecting collisions, discrete and continuous. In the discrete case, we advance the physical model by a small time step, and then check if any of our objects are intersecting, or are so close to each other that for all intents and purposes we consider them intersecting. For the continuous case, we write a collision detection algorithm which will be able to predict the trajectories of the physical bodies. The instants of collision are calculated, and the physical bodies never actually have to collide for us to detect the collision. 

The continuous approach is more accurate but is also more difficult. So for this tutorial, we will develop a few different discrete detection algorithms. Discrete detection will get the job done in many situations and is well suited to the Codea draw loop architecture. As long as your time steps are small enough, there aren't too many objects and the objects are large enough, you shouldn't miss any collisions.

Note that collision detection can chew up a lot of CPU time. If n is the number of objects on the screen that we need to test, then the:

Number of collision detection tests = (n² - n) / 2

Thus the number of tests increases exponentially with the number of objects being modeled. Consequently, if you have more than a few objects, you will want to look at optimising your tests.

31.2 Bounding Box Detection


If we have two rectangular objects with origins at (x1, y1) and (x2, y2) and dimensions (i.e. width and heights) of (w1, h1) and (w2, h2), then we can use the following function to determine whether the two rectangles are overlapping at a particular point in time.

function CollisionDetected(x1,  y1,  w1,  h1,  x2,  y2,  w2,  h2) 

  Collision = false

 
  if (y2 >= y1 and y1 + h1 >= y2) or (y2 + h2 >= y1 and y1 + h1 >= y2 + h2) 
    or (y1 >= y2 and y2 + h2 >= y1) 
        or (y1 + h1 >= y2 and y2 + h2 >= y1 + h1) then
     if x2 >= x1 and x1 + w1 >= x2 then          -- corner 1
        Collision = true
     end
     if x2 + w2 >= x1 and x1 + w1 >= x2 + w2 then  -- corner 2
        Collision = true 
     end
     if x1 >= x2 and x2 + w2 >= x1 then           -- corner 3
       Collision = true 
     end
     if x1 + w1 >= x2 and x2 + w2 >= x1 + w1 then  -- corner 4
       Collision = true
     end
  end

  return Collision -- return whether or not a collision is detected 


end
 


A faster version (about 10%) of the same function (albeit a bit more difficult to follow) is:

function CollisionDetected(x1,  y1,  w1,  h1,  x2,  y2,  w2,  h2) 
return not ((y1+h1 < y2) or (y1 > y2+h2) or (x1 > x2+w2) or (x1+w1 < x2))
end

And here is another version:

function CollisionDetected(x1,  y1,  w1,  h1,  x2,  y2,  w2,  h2) 
    local ax2, bx2, ay2, by2 = x1 + w1, x2 + w2, y1 + h1, y2 + h2
    return ax2 > x2 and bx2 > x1 and ay2 > y2 and by2 > y2
end

We will leave it as an exercise for the reader to demonstrate that these functions are all logically equivalent.


31.3 Bounding Circle Detection


The simplest method to detect circles colliding is to check whether the circles are overlapping. This can be done by calculating the distance between the centers of the two circles and seeing if it is less than or equal to the sum of the radii of the circles. If it is then a collision has occurred. 

function CollisionDetected(x1, y1, r1, x2, y2, r2)

    local dx = x2 - x1

    local dy = y2 - y1

    return math.sqrt(dx^2 + dy^2) <= r1 + r2


end


The square root in the distance formula is unnecessary because the inequality will still hold if we square both sides. Removing the square root will increase your collision detection speed, especially if you are checking for many collisions, because this is a relatively expensive calculation. 

function CollisionDetected(x1, y1, r1, x2, y2, r2)

    local dx = x2 - x1

    local dy = y2 - y1

    return (dx^2 + dy^2) <= (r1 + r2)^2


end





31.3 Box2D Collision Detection


If you don't want to roll your own collision detection, then you can utilise the built in collision detection functionality of Codea, which is provided courtesy of its Box2D implementation. To use this, the bodies which are colliding need to be created using the physics.body() function.  The downside of using this approach is that physics bodies can only have the following shapes (the property is called shapeType):
  • Circle; 
  • Polygon; 
  • Chain; or 
  • Edge (usually used for the ground or walls).

So for pixel perfect collisions you will need to use another approach if your sprites can't be outlined using one of the shapes above. In many cases however, you can approximate the sprite shape using these.

In addition to the shape type, we need to define the body type for our physics object. There are three options:
  1. Dynamic - these objects move under the influence of collisions, forces, joints and gravity (we will use this type for our asteroids);
  2. Static - these objects are not supposed to move and are unaffected by collisions and forces (e.g. the ground or walls); and 
  3. Kinematic - these objects can move by setting their linear velocity but like static bodies are unaffected by collisions and forces (we will use this type for our ship, if the ship collides with an asteroid we want it to explode not bounce away). 
To demonstrate how easy this is to implement we will create a simple Asteroids game with a ship in the center of the screen and randomly generated asteroids, which will destroy our ship if we detect a collision.

While Box2D will model the movement of our physics bodies for us, it is our responsibility to render (i.e. draw) representations of these bodies on the screen. To do this we will use a simplified version of the PhysicsDebugDraw class (called PhysicsDraw) which is included with the Physics Lab example. A copy of this simplified class is included at the end of the tutorial.

function setup()

    physics.gravity(0.0, 0.0)
    physicsDraw = PhysicsDraw()
    ship = createShip(WIDTH/2, HEIGHT/2)
    physicsDraw:addBody(ship)
    asteroidTimer = 0

end


The physics.gravity function allows us to set the gravity for our game for the x and y axis respectively. The units are pixels per second squared. We have set our program gravity to zero since we are in space. The createShip function will create a new physics body which we add to the table of bodies in PhysicsDraw. Every draw cycle, PhysicsDraw will render all the physics bodies at their current location.

function createShip(x, y, w, h)

   -- (x, y) are the centre co-ordinates of the ship.
   -- (w, h) define the width and height of the box containing the ship,
   -- if not specified they are 50 and 35 respectively.

   local width = w or 50
   local height = h or 35
   local shipBody = physics.body(POLYGON, 
                                  vec2(0, height), vec2(width, height/2), 
                                  vec2(0, 0), vec2(height/2, height/2), 
                                  vec2(0, height))

   shipBody.x = x
   shipBody.y = y
   shipBody.type = KINEMATIC
   shipBody.sleepingAllowed = false
   shipBody.info = "ship"
   shipBody.interpolate = true

   return shipBody


end

Next up we need functions to create our asteroids. We will use the same function (createRandPoly) to generate the debris field for when our ship collides with an asteroid and explodes.

function createRandPoly(x, y, s1, s2)

   -- s1 and s2 define the range of length for the poly sides
   -- count defines the number of sides of the poly

   local minLength = s1 or 1
   local maxLength = s2 or 5
   local count = math.random(5,10)
   local r = math.random(minLength, maxLength)
   local a = 0
   local d = 2 * math.pi / count
   local points = {}

   for i = 1,count do
       local v = vec2(r,0):rotate(a) 
                     + vec2(math.random(-10,10), math.random(-10,10))
       a = a + d
       table.insert(points, v)
   end

   local poly = physics.body(POLYGON, unpack(points))

   poly.x = x
   poly.y = y
   poly.type = DYNAMIC
   poly.sleepingAllowed = false
   poly.restitution = 0.5
   poly.info = "poly"
   physicsDraw:addBody(poly)

   return poly

end

function createAsteroid(x, y)

   return createRandPoly(x, y, 25, 65)


end

The asteroids are placed off the screen in random locations and accelerated towards the screen where eventually one of them will collide with our ship. Inside the draw() function we create a new asteroid every second.

function placeRandomAsteroid()

   -- Generate (x, y) co-ordinates which are
   -- initially off the screen.

   local x = math.random(WIDTH)

   if x < WIDTH/2 then
       x = x - WIDTH
   else
       x = x + WIDTH
   end

   local y = math.random(HEIGHT)

   if y < HEIGHT/2 then
       y = y - HEIGHT
   else
       y = y + HEIGHT
   end

   -- Create an asteroid at the new co-ordinates

   local asteroid = createAsteroid(x, y)

   -- Give the asteroid a linear velocity
   -- towards the screen (and our ship)

   local dX, dY = math.random(50, 100), math.random(50, 100)

   if x > WIDTH then
       dX = -dX
   end

   if y > HEIGHT then
       dY = -dY
   end

   asteroid.linearVelocity = vec2(dX, dY)

end

function createExplosionAt(x, y)

   -- Creates 6 small polygon moving out from (x, y)
   -- to simulate our ship exploding.
   --
   -- the body.info field is set to debris so that we
   -- can render it the same colour as our ship (i.e. to
   -- distinguish them from the asteroid polygons.

   randPoly1 = createRandPoly(x, y)
   randPoly1:applyForce(vec2(50,50))
   randPoly1.info = "debris"
   randPoly2 = createRandPoly(x, y)
   randPoly2:applyForce(vec2(50,50))
   randPoly2.info = "debris"
   randPoly3 = createRandPoly(x, y)
   randPoly3:applyForce(vec2(50,50))
   randPoly3.info = "debris"
   randPoly4 = createRandPoly(x, y)
   randPoly4:applyForce(vec2(-50,50))
   randPoly4.info = "debris"
   randPoly5 = createRandPoly(x, y)
   randPoly5:applyForce(vec2(-50,50))
   randPoly5.info = "debris"
   randPoly6 = createRandPoly(x, y)
   randPoly6:applyForce(vec2(-50,50))     
   randPoly6.info = "debris"   

end

-- This function gets called once every frame

function draw()

   -- This sets a dark background color 

   background(40, 40, 50)

   -- Generate a random asteroid every second

   asteroidTimer = asteroidTimer + DeltaTime

   if asteroidTimer > 1.0 then
       placeRandomAsteroid()
       asteroidTimer = 0
   end

   -- Do your drawing here

   physicsDraw:draw()

end

-- Collision Detection
--
-- This is simply a matter of checking whether one of the two
-- colliding bodies are our ship.

function collide(contact)

   if contact.bodyA == ship or contact.bodyB == ship then
       createExplosionAt(ship.x, ship.y)
       ship.info = "destroyed"
       ship = nil
   end


end

Collision detection is then just a matter of calling the built in collide() function and checking whether one of the two colliding bodies is our ship. Included below is the simplified PhysicsDebugDraw class which is responsible for rendering our physics bodies each frame.

--# PhysicsDraw

PhysicsDraw = class()

-- Simplified Physics Debug Draw class from the Physics Lab
-- example in Codea.

function PhysicsDraw:init()
   self.bodies = {}
   self.contacts = {}
end

function PhysicsDraw:addBody(body)
   table.insert(self.bodies,body)
end

function PhysicsDraw:draw()

   pushStyle()
   smooth()
   noFill()

   for i,body in ipairs(self.bodies) do

       pushMatrix()
       translate(body.x, body.y)
       rotate(body.angle)

       if body.type == STATIC then
           stroke(255,255,255,255)
       elseif body.type == DYNAMIC and body.info ~= "debris" then
           stroke(150,255,150,255)
       else
           stroke(150,150,255,255)
       end

       if body.shapeType == POLYGON and body.info ~= "destroyed" then
           strokeWidth(3.0)
           local points = body.points
           for j = 1,#points do
               a = points[j]
               b = points[(j % #points)+1]
               line(a.x, a.y, b.x, b.y)
           end
       elseif body.shapeType == CHAIN or body.shapeType == EDGE then
           strokeWidth(3.0)
           local points = body.points
           for j = 1,#points-1 do
               a = points[j]
               b = points[j+1]
               line(a.x, a.y, b.x, b.y)
           end      
       elseif body.shapeType == CIRCLE then
           strokeWidth(3.0)
           line(0,0,body.radius-3,0)            
           ellipse(0,0,body.radius*2)
       end

       popMatrix()
   end 

   stroke(255, 0, 0, 255)
   fill(255, 0, 0, 255)

   for k,v in pairs(self.contacts) do
       for m,n in ipairs(v.points) do
           ellipse(n.x, n.y, 10, 10)
       end
   end

   popStyle()
end

function PhysicsDraw:collide(contact)
   if contact.state == BEGAN then
       self.contacts[contact.id] = contact
       sound(SOUND_HIT, 2643)
   elseif contact.state == MOVING then
       self.contacts[contact.id] = contact
   elseif contact.state == ENDED then
       self.contacts[contact.id] = nil
   end

end

A complete download of the code presented in this tutorial is available from our Gist repository.

Thursday, April 25, 2013

Tutorial 30 - Codea v1.5.2: Objective C Add On, Game Center

Figure 0. iOS Dev Center


30.1 Overview


CodeaAddon is an experimental protocol for adding native extensions to exported Codea projects. You must deal with Lua directly to register your functions and globals. This protocol should be considered alpha and is subject to change in future Codea releases.As with the previous tutorial on iAds, we have provided a lot of background on Game Center in these tutorials:
Have a look at the above to refresh your knowledge on what is required from an iTunes Connect perspective as we will only cover that area briefly in this tutorial.


Figure 1. Find out the Bundle Identifier for your App.


30.2 Registering your Application in iTunes Connect


In order to test your app's Game Center functionality you need to register an app ID associated with this app which enables Game Center. The association is done via the bundle identifier of your app (Figure 1). Click on the app title in the top left of the project navigator screen in Xcode to bring up this screen. Make sure that you have selected the "Summary" tab. Write down the bundle identifier shown, we will need this shortly.

Figure 2. Set up a new App ID for your app.

Log into your apple developer account, go to the iOS Dev Center (Figure 0) and select the Certificates, Identifiers & Profiles link on the right hand side of the page. Select a name for your app ID (pick something you can remember e.g. we used AudioDemoAppID) and fill in the App ID Suffix bundle identifier. This is the critical step which will link your app to the configuration in iTunes Connect. Use the bundle identifier that you wrote down earlier. Click "confirm" and then "submit".

Figure 3. Associate your bundle identifier with the App ID.

Head back to the iOS Dev Center (Figure 0) and this time click on iTunes Connect link in the top right of the page. You will be asked to sign in again, do so and then click on the Manage your Apps link (Figure 4).

Figure 4. iTunes Connect

On the Manage your Apps page (Figure 5), click on the "Add New App" button at the top left of the screen. Click on iOS App on the next screen.

Figure 5. Manage your Apps

Fill in the app name, SKU number (this can be any unique identifier, we normally use the date), and then select the App ID that you just created for the bundle identifier (Figure 6). Click "Continue" when you are done.

Figure 6. iTunes Connect App Information.

Fill in all the meta data (have a look at the earlier tutorials if you get stuck) and save the configuration. When you are done (Figure 7), we can enable Game Center for your app. Click on the "Manage Game Center" button on the right hand side of the page (Figure 7).

Figure 7. Metadata addition complete.

Click on the button that says "Enable for Single Game" and then either configure some achievements and leader boards or just click "Done". We can now test our exported app once we have included the Game Center add on.

Figure 8. Adding the GameKit Framework.


30.3 Add the GameKit Framework to Your App


Fire up Xcode and load the exported version of your Codea application (See Tutorial 27). Click on the imported project file at the top of the project navigator then in the Build Phases tab, scroll down to the link binary with libraries area and select the drop down arrow. Click on the "+" button below your existing frameworks to add a new framework. Find GameKit and click on "Add"


30.4 Changes to your Exported AppDelegate Files


All of the source code files are provided at the end of this tutorial. You need to update AppDelegate.h as follows:

//
// AppDelegate.h
// AudioDemo
//
// Used to demonstrate the audio, game center and iAds add on libraries
//
// Created by Reefwing Software on Sunday, 14 April 2013
// Copyright (c) Reefwing Software. All rights reserved.
//

    #import <UIKit/UIKit.h>
    #import "AudioAddOn.h"
    #import "IAdsAddOn.h"
    #import "GameCenterAddOn.h"

    @class CodeaViewController;

    @interface AppDelegate : UIResponder <UIApplicationDelegate>

    @property (strong, nonatomic) IAdsAddOn *iAdsAddOn;
    @property (strong, nonatomic) AudioAddOn *audioAddOn;
    @property (strong, nonatomic) GameCenterAddOn *gameCenterAddOn;
    @property (strong, nonatomic) UIWindow *window;
    @property (strong, nonatomic) CodeaViewController *viewController;

@end


And AppDelegate.mm, should now look like:

//
// AppDelegate.mm
// AudioDemo
//
// Created by Reefwing Software on Sunday, 14 April 2013
// Copyright (c) Reefwing Software. All rights reserved.
//

#import "AppDelegate.h"
#import "CodeaViewController.h"

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{

    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    self.viewController = [[CodeaViewController alloc] init];

    // Create and add our AudioAddOn to Codea

    self.audioAddOn = [[AudioAddOn alloc] init];
    [self.viewController registerAddon: self.audioAddOn];

    // Create and add our iAdsAddOn to Codea

    self.iAdsAddOn = [[IAdsAddOn alloc] init];
    [self.viewController registerAddon: self.iAdsAddOn];

    // Create and add our GameCenterAddOn to Codea

    self.gameCenterAddOn = [[GameCenterAddOn alloc] init];
    [self.viewController registerAddon: self.gameCenterAddOn];

    NSString* projectPath = [[[NSBundle mainBundle] bundlePath]    stringByAppendingPathComponent:@"AudioDemo.codea"];

    [self.viewController loadProjectAtPath:projectPath];

    self.window.rootViewController = self.viewController;
    [self.window makeKeyAndVisible];

    return YES;

}

- (void)applicationWillResignActive:(UIApplication *)application
{

}

- (void)applicationDidEnterBackground:(UIApplication *)application
{

}

- (void)applicationWillEnterForeground:(UIApplication *)application
{

}

- (void)applicationDidBecomeActive:(UIApplication *)application

}

- (void)applicationWillTerminate:(UIApplication *)application
{

}

@end


Obviously there is no need to register the iAds and Audio add ons if you are just using Game Center. We have just left these in to demonstrate how you can stack add ons.



Figure 9. If you try to show Achievements or Leaderboards with none set up in iTunes Connect, you will see this screen.

30.5 The Game Center Add On


You need to add the GameCenterAddOn.h and GameCenterAddOn.m files to your project (plus the other iAds and Audio add on classes if you are using them). These are available below.

To do this, right click on the Addons folder in the project navigator and select Add files to "YourProjectName"... Navigate to where ever you saved these files and select them.

This Game Center Add On will make four new functions available in your Lua code:

  • gameCenterStart(); 
  • showLeaderBoardWithIdentifier(int ident); 
  • showAchievementsView(); and
  • playerIsAuthenticated;

You need to call gameCenterStart() first as your game must authenticate a local player before you can use any Game Center classes. 


Figure 10. You may need to sign in the first time you try to authenticate a player.

If you want to add more Game Center functionality the just follow the pattern in the add on. For example, if you want to add a save score function then, in GameCenterAddOn.h add:

static int saveScore(struct lua_State *state);

Then in GameCenterAddOn.m add/modify:

// Add to method

- (void)codea:(CodeaViewController*)controller didCreateLuaState:(struct lua_State*)L
{
    ...
    lua_register(L, "saveScore", saveScore);
    ...
}

// New Objective C method

- (void) saveNewScore: (int) score
{
    // INSERT YOUR LEADERBOARD IDENTIFIER IN THE LINE BELOW
    // Replace "Easy Difficulty" with your identifier from iTunes Connect

    GKScore *scoreReporter = [[GKScore alloc] initWithCategory: @"Easy Difficulty"];

    if (scoreReporter)
    {
        scoreReporter.value = score;

        [scoreReporter reportScoreWithCompletionHandler: ^(NSError *error)
        {
            if (error != nil)
            {
                // handle the reporting error

                NSLog(@"Game Center: Error Saving Score - %@", [error localizedDescription]);
            }
        }];   
    }
}

// New C function

static int saveScore(struct lua_State *state)
{
    [gameCenterAddOnInstance saveNewScore: lua_tonumber(state, 1)];

    return 0;
}

Then in your Lua code you can use saveScore(yourNewScore).

Figure 11. Player Authenticated.


30.6 Download the Code


//
// AppDelegate.h
// AudioDemo
//
// Used to demonstrate the audio, game center and iAds add on libraries
//
// Created by Reefwing Software on Sunday, 14 April 2013
// Copyright (c) Reefwing Software. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "AudioAddOn.h"
#import "IAdsAddOn.h"
#import "GameCenterAddOn.h"
@class CodeaViewController;
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) IAdsAddOn *iAdsAddOn;
@property (strong, nonatomic) AudioAddOn *audioAddOn;
@property (strong, nonatomic) GameCenterAddOn *gameCenterAddOn;
@property (strong, nonatomic) UIWindow *window;
@property (strong, nonatomic) CodeaViewController *viewController;
@end
view raw AppDelegate.h hosted with ❤ by GitHub
//
// AppDelegate.mm
// AudioDemo
//
// Created by Reefwing Software on Sunday, 14 April 2013
// Copyright (c) Reefwing Software. All rights reserved.
//
#import "AppDelegate.h"
#import "CodeaViewController.h"
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.viewController = [[CodeaViewController alloc] init];
// Create and add our AudioAddOn to Codea
self.audioAddOn = [[AudioAddOn alloc] init];
[self.viewController registerAddon: self.audioAddOn];
// Create and add our iAdsAddOn to Codea
self.iAdsAddOn = [[IAdsAddOn alloc] init];
[self.viewController registerAddon: self.iAdsAddOn];
// Create and add our GameCenterAddOn to Codea
self.gameCenterAddOn = [[GameCenterAddOn alloc] init];
[self.viewController registerAddon: self.gameCenterAddOn];
NSString* projectPath = [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:@"AudioDemo.codea"];
[self.viewController loadProjectAtPath:projectPath];
self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];
return YES;
}
- (void)applicationWillResignActive:(UIApplication *)application
{
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
}
- (void)applicationWillTerminate:(UIApplication *)application
{
}
@end
view raw AppDelegate.mm hosted with ❤ by GitHub
//
// AudioAddOn.h
// AudioDemo
//
// Created by David Such on 13/04/13.
// Copyright (c) 2013 Reefwing Software. All rights reserved.
//
// Version: 1.0 - Original (13/04/13)
// 1.1 - Volume control & monitoring added, metering enabled (14/04/13)
// 1.2 - Minor refactoring (19/04/13)
#import <Foundation/Foundation.h>
#import <AVFoundation/AVFoundation.h>
#import "CodeaAddon.h"
id audioAddOnInstance;
// This class conforms to the CodeaAddon & AVAudioPlayerDelegate Protocols
@interface AudioAddOn : NSObject<CodeaAddon, AVAudioPlayerDelegate>
@property (strong, nonatomic) AVAudioPlayer *player;
// Forward declare our Lua Audio functions. These are static to confine their scope
// to this file. By default c functions are global.
static int playMusic(struct lua_State *state);
static int stopMusic(struct lua_State *state);
static int getVolume(struct lua_State *state);
static int setVolume(struct lua_State *state);
static int peakPowerForChannel(struct lua_State *state);
static int averagePowerForChannel(struct lua_State *state);
@end
view raw AudioAddOn.h hosted with ❤ by GitHub
//
// AudioAddOn.m
// AudioDemo
//
// Created by David Such on 13/04/13.
// Copyright (c) 2013 Reefwing Software. All rights reserved.
//
// Version: 1.0 - Original (13/04/13)
// 1.1 - Volume control & monitoring added, metering enabled (14/04/13)
// 1.2 - Minor refactoring (19/04/13)
#import "AudioAddOn.h"
#import "lua.h"
@implementation AudioAddOn
#pragma mark - Initialisation
- (id)init
{
self = [super init];
if (self)
{
// Initialise the Audio Player with your mp3 file.
NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle]
pathForResource:@"LunarLander"
ofType:@"mp3"]];
NSError *error;
_player = [[AVAudioPlayer alloc] initWithContentsOfURL: url error: &error];
if (error)
NSLog(@"Error initialiasing Audio Add On: %@", [error localizedDescription]);
else
{
// Player initialised, assign delegate to this class
[_player setDelegate: self];
// Calling prepareToPlay preloads buffers and acquires the audio hardware needed for playback,
// which minimizes the lag between calling the play method and the start of sound output.
[_player prepareToPlay];
// A value of 0, which is the default, means to play the sound once. Set a positive integer
// value to specify the number of times to return to the start and play again. For example,
// specifying a value of 1 results in a total of two plays of the sound. Set any negative
// integer value to loop the sound indefinitely until you call the stop method.
[_player setNumberOfLoops: -1];
// The default value for the meteringEnabled property is off (Boolean NO). Before using metering
// for an audio player, you need to enable it by setting this property to YES.
[_player setMeteringEnabled: YES];
// audioAddOnInstance allows us to access self from within the c functions.
audioAddOnInstance = self;
}
}
return self;
}
#pragma mark - CodeaAddon Delegate
// Classes which comply with the <CodeaAddon> Protocol must implement this method
- (void) codea:(CodeaViewController*)controller didCreateLuaState:(struct lua_State*)L
{
NSLog(@"AudioAddon Registering Functions");
// Register the Audio functions, defined below
lua_register(L, "playMusic", playMusic);
lua_register(L, "stopMusic", stopMusic);
lua_register(L, "setVolume", setVolume);
lua_register(L, "getVolume", getVolume);
lua_register(L, "peakPowerForChannel", peakPowerForChannel);
lua_register(L, "averagePowerForChannel", averagePowerForChannel);
}
#pragma mark - Audio Add On Functions and associated Methods
// Objective C Methods
- (void)startPlayer
{
[self.player play];
}
- (void)stopPlayer
{
if ([self.player isPlaying])
[self.player stop];
}
- (void)setPlayerVolume: (int)setting
{
// The volume property is the playback gain for the AV audio player object,
// it expects a float ranging from 0.0 through 1.0.
//
// Our Codea slider control returns an integer from 0 to 100 so we need to
// convert this to a float in the appropriate range before applying it to
// our player. As a defensive measure we will clamp the result between 0.0 and 1.0.
float floatSetting = MAX(0.0f, MIN((float)setting / 100.0f, 1.0f));
[self.player setVolume: floatSetting];
}
- (int)getPlayerVolume
{
return (self.player.volume * 100.0f);
}
- (int)getPeakPower: (NSUInteger)channel
{
if ([self.player isPlaying])
{
// Refresh the average and peak power values for all channels of our audio player.
[self.player updateMeters];
// Peak power is a floating-point representation, in decibels, of a given audio channel’s current peak power.
// A return value of 0 dB indicates full scale, or maximum power while a return value of -160 dB indicates
// minimum power (that is, near silence).
//
// If the signal provided to the audio player exceeds ±full scale, then the return value may exceed 0
// (that is, it may enter the positive range).
//
// Channel numbers are zero-indexed. A monaural signal, or the left channel of a stereo signal, has channel number 0.
float power = -160.0f; // Initialise to silence
if (channel <= [self.player numberOfChannels])
power = [self.player peakPowerForChannel: channel];
// Our dial is expecting a value between 0 and 100.
if (power >= 0)
return 100;
else
{
power += 160.0f; // power is now a +ve float between 0 and 160
power = (power / 160.0f) * 100.0f; // change to a percentage
return (int)power;
}
}
else
return 0;
}
- (int)getAveragePower: (NSUInteger)channel
{
if ([self.player isPlaying])
{
// Refresh the average and peak power values for all channels of our audio player.
[self.player updateMeters];
// A floating-point representation, in decibels, of a given audio channel’s current average power.
// A return value of 0 dB indicates full scale, or maximum power; a return value of -160 dB indicates
// minimum power (that is, near silence).
//
// If the signal provided to the audio player exceeds ±full scale, then the return value may exceed 0
// (that is, it may enter the positive range).
//
// Channel numbers are zero-indexed. A monaural signal, or the left channel of a stereo signal, has channel number 0.
float power = -160.0f; // Initialise to silence
if (channel <= [self.player numberOfChannels])
power = [self.player averagePowerForChannel: channel];
// Our dial is expecting a value between 0 and 100.
if (power >= 0)
return 100;
else
{
power += 160.0f; // power is now a +ve float between 0 and 160
power = (power / 160.0f) * 100.0f; // change to a percentage
return (int)power;
}
}
else
return 0;
}
// C Functions
//
// Note that the returned value from all exported Lua functions is how many values that function should return in Lua.
// For example, if you return 0 from that function, you are telling Lua that peakPowerForPlayer (for example) returns 0 values.
// If you return 2, you are telling Lua to expect 2 values on the stack when the function returns.
//
// To actually return values, you need to push them onto the Lua stack and then return the number of values you pushed on.
static int playMusic(struct lua_State *state)
{
[audioAddOnInstance startPlayer];
return 0;
}
static int stopMusic(struct lua_State *state)
{
[audioAddOnInstance stopPlayer];
return 0;
}
static int getVolume(struct lua_State *state)
{
// Push the integer volume onto the Lua stack
lua_pushinteger(state, [audioAddOnInstance getPlayerVolume]);
// Our function returns 1 value = volume
return 1;
}
static int setVolume(struct lua_State *state)
{
[audioAddOnInstance setPlayerVolume: lua_tonumber(state, 1)];
return 0;
}
static int peakPowerForChannel(struct lua_State *state)
{
// Channel numbers are zero-indexed. A monaural signal,
// or the left channel of a stereo signal, has channel number 0.
NSUInteger channel = lua_tonumber(state, 1);
// Push the integer power onto the Lua stack
lua_pushinteger(state, [audioAddOnInstance getPeakPower: channel]);
// Our function returns 1 value = peak power
return 1;
}
static int averagePowerForChannel(struct lua_State *state)
{
// Channel numbers are zero-indexed. A monaural signal,
// or the left channel of a stereo signal, has channel number 0.
NSUInteger channel = lua_tonumber(state, 1);
// Push the integer power onto the Lua stack
lua_pushinteger(state, [audioAddOnInstance getAveragePower: channel]);
// Our function returns 1 value = peak power
return 1;
}
#pragma mark - AVAudioPlayer Delegate
// These Audio Player call back methods are not used in this tutorial but provided here
// for information. There are a number of other delegate methods available. Check the
// documentation.
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
{
}
- (void)audioPlayerDecodeErrorDidOccur:(AVAudioPlayer *)player error:(NSError *)error
{
NSLog(@"Error decoding audio file: %@", [error localizedDescription]);
}
-(void)audioPlayerBeginInterruption:(AVAudioPlayer *)player
{
}
-(void)audioPlayerEndInterruption:(AVAudioPlayer *)player
{
}
@end
view raw AudioAddOn.m hosted with ❤ by GitHub
//
// GameCenterAddOn.h
// AudioDemo
//
// Created by David Such on 25/04/13.
// Copyright (c) 2013 Reefwing Software. All rights reserved.
//
// Version: 1.0 - Original (25/04/13)
#import "CodeaAddon.h"
#import <Foundation/Foundation.h>
#import <GameKit/GameKit.h>
// Create a variable which points to the instance of this class so that we can access self
// from within the Lua c functions.
id gameCenterAddOnInstance;
// This class conforms to the CodeaAddon, GKLeaderboardViewControllerDelegate, and GKAchievementViewControllerDelegate Protocols
// We don't use GKGameCenterViewController since this is only available for iOS 6, and we want to provide this functionality for
// 1st generation iPads which run iOS 5.
@interface GameCenterAddOn : NSObject<CodeaAddon, GKLeaderboardViewControllerDelegate, GKAchievementViewControllerDelegate>
{
bool hasGameCenter;
}
@property (weak, nonatomic) CodeaViewController *codeaViewController;
// Forward declare our Lua Audio functions. These are static to confine their scope
// to this file. By default c functions are global.
static int gameCenterStart(struct lua_State *state);
static int showLeaderBoardWithIdentifier(struct lua_State *state);
static int showAchievementsView(struct lua_State *state);
static int playerIsAuthenticated(struct lua_State *state);
@end
//
// GameCenterAddOn.m
// AudioDemo
//
// Created by David Such on 25/04/13.
// Copyright (c) 2013 MyCompany. All rights reserved.
//
#import "lua.h"
#import "GameCenterAddOn.h"
@implementation GameCenterAddOn
#pragma mark - Initialisation
- (id)init
{
self = [super init];
if (self)
{
// gameCenterAddOnInstance allows us to access self from within the c functions.
gameCenterAddOnInstance = self;
// Initialise our Instance Variables
hasGameCenter = false;
}
return self;
}
#pragma mark - CodeaAddon Delegate
// Classes which comply with the <CodeaAddon> Protocol must implement this method
- (void)codea:(CodeaViewController*)controller didCreateLuaState:(struct lua_State*)L
{
NSLog(@"gameCenterAddOn Registering Functions");
// Register the iAd functions, defined below
lua_register(L, "gameCenterStart", gameCenterStart);
lua_register(L, "showLeaderBoardWithIdentifier", showLeaderBoardWithIdentifier);
lua_register(L, "showAchievementsView", showAchievementsView);
lua_register(L, "playerIsAuthenticated", playerIsAuthenticated);
// Hook up with the CodeaViewController - don't try to add subviews in this method.
self.codeaViewController = controller;
}
#pragma mark - Game Center Add On Instance Methods
- (BOOL)isGameCenterAvailable
{
// Check for presence of GKLocalPlayer API.
Class gcClass = (NSClassFromString(@"GKLocalPlayer"));
// The device must be running running iOS 4.1 or later.
NSString *reqSysVer = @"4.1";
NSString *currSysVer = [[UIDevice currentDevice] systemVersion];
BOOL osVersionSupported = ([currSysVer compare:reqSysVer options:NSNumericSearch] != NSOrderedAscending);
if (gcClass && osVersionSupported)
NSLog(@"Game Center is Available");
else
NSLog(@"Game Center is not Available");
return (gcClass && osVersionSupported);
}
- (void)registerForAuthenticationNotification
{
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc addObserver: self selector: @selector(authenticationChanged) name: GKPlayerAuthenticationDidChangeNotificationName object: nil];
}
// Your game must authenticate a local player before you can use any Game Center classes.
// If there is no authenticated player, your game receives a GKErrorNotAuthenticated error.
- (bool)isPlayerAuthenticated
{
return hasGameCenter;
}
- (void)authenticateLocalPlayer
{
if(![self isGameCenterAvailable])
{
hasGameCenter = false;
return;
}
[[GKLocalPlayer localPlayer] authenticateWithCompletionHandler:^(NSError *error)
{
if (error == nil)
{
[self registerForAuthenticationNotification];
hasGameCenter = true;
NSLog(@"Game Center - local player authenticated.");
}
else
{
hasGameCenter = false;
NSLog(@"Game Center - failed to authenticate, error: %@", [error localizedDescription]);
}
}];
}
- (void)authenticationChanged
{
if([self isGameCenterAvailable])
{
return;
}
if ([GKLocalPlayer localPlayer].isAuthenticated)
{
hasGameCenter = true;
}
else
{
hasGameCenter = false;
}
}
#pragma mark - Game Center C Functions
// C Functions
//
// Note that the returned value from all exported Lua functions is how many values that function should return in Lua.
// For example, if you return 0 from that function, you are telling Lua that function returns 0 values.
// If you return 2, you are telling Lua to expect 2 values on the stack when the function returns.
//
// To actually return values, you need to push them onto the Lua stack and then return the number of values you pushed on.
static int gameCenterStart(struct lua_State *state)
{
[gameCenterAddOnInstance authenticateLocalPlayer];
return 0;
}
static int showLeaderBoardWithIdentifier(struct lua_State *state)
{
[gameCenterAddOnInstance showLeaderboard: lua_tonumber(state, 1)];
return 0;
}
static int showAchievementsView(struct lua_State *state)
{
[gameCenterAddOnInstance showAchievements];
return 0;
}
static int playerIsAuthenticated(struct lua_State *state)
{
lua_pushboolean(state, [gameCenterAddOnInstance isPlayerAuthenticated]);
return 1;
}
#pragma mark - GKLeaderboardViewControllerDelegate Methods
- (void)showLeaderboard: (int)ident
{
NSString *identifier;
// INSERT YOUR ACHIEVEMENT IDENTIFIERS BELOW - These are set up in iTunes Connect
switch (ident)
{
case 1:
identifier = @"Easy Difficulty";
break;
case 2:
identifier = @"Medium Difficulty";
break;
case 3:
identifier = @"Hard Difficulty";
break;
default:
NSLog(@"Warning Unknown Leader Board");
break;
}
GKLeaderboardViewController *leaderBoardCont = [[GKLeaderboardViewController alloc] init];
if (leaderBoardCont)
{
leaderBoardCont.category=identifier;
leaderBoardCont.timeScope=GKLeaderboardTimeScopeToday;
leaderBoardCont.leaderboardDelegate=self;
self.codeaViewController.paused = YES;
[self.codeaViewController presentModalViewController: leaderBoardCont animated: YES];
}
}
- (void)leaderboardViewControllerDidFinish:(GKLeaderboardViewController *)viewController
{
// Dismiss the Game Center Leader Board view
[self.codeaViewController dismissViewControllerAnimated: YES completion:^
{
self.codeaViewController.paused = NO; // Unpause Codea
}];
}
#pragma mark - GKAchievementViewControllerDelegate Methods
- (void)showAchievements
{
GKAchievementViewController *achievements = [[GKAchievementViewController alloc] init];
if (achievements != nil)
{
achievements.achievementDelegate = self;
self.codeaViewController.paused = YES;
[self.codeaViewController presentViewController: achievements animated: YES completion: nil];
}
}
- (void)achievementViewControllerDidFinish:(GKAchievementViewController *)viewController
{
// Dismiss the Game Center Achievement view
[self.codeaViewController dismissViewControllerAnimated: YES completion:^
{
self.codeaViewController.paused = NO; // Unpause Codea
}];
}
@end
//
// IAdsAddOn.m
// AudioDemo
//
// Created by David Such on 19/04/13.
// Copyright (c) 2013 Reefwing Software. All rights reserved.
//
// Version: 1.0 - Original (19/04/13)
// 1.1 - Minor refactoring (25/4/13)
#import "lua.h"
#import "IAdsAddOn.h"
@implementation IAdsAddOn
#pragma mark - Initialisation
- (id)init
{
self = [super init];
if (self)
{
// audioAddOnInstance allows us to access self from within the c functions.
iAdsAddOnInstance = self;
// Initialise our Instance Variables
_isBannerVisible = NO;
_showBannerFromTop = YES;
// Initialise our iAd Banner View
CGRect frame = CGRectZero;
frame.size = [ADBannerView sizeFromBannerContentSizeIdentifier: ADBannerContentSizeIdentifierPortrait];
_bannerView = [[ADBannerView alloc] initWithFrame: frame];
_bannerView.requiredContentSizeIdentifiers = [NSSet setWithObject: ADBannerContentSizeIdentifierPortrait];
_bannerView.delegate = self;
}
return self;
}
#pragma mark - CodeaAddon Delegate
// Classes which comply with the <CodeaAddon> Protocol must implement this method
- (void) codea:(CodeaViewController*)controller didCreateLuaState:(struct lua_State*)L
{
NSLog(@"iAdAddOn Registering Functions");
// Register the iAd functions, defined below
lua_register(L, "showAdFromTop", showAdFromTop);
lua_register(L, "showAdFromBottom", showAdFromBottom);
lua_register(L, "hideAd", hideAd);
// Hook up with the CodeaViewController - don't try to add subviews in this method.
self.codeaViewController = controller;
}
#pragma mark - iAds Add On Functions and associated Methods
// Objective C Methods
- (void)showBannerViewAnimated:(BOOL)animated
{
if ([self.bannerView isBannerLoaded])
{
// We only display the banner View if it has ads loaded and isn't already visible.
// Set the banner view starting position as off screen.
CGRect frame = _bannerView.frame;
if (_showBannerFromTop)
frame.origin.y = 0.0f - _bannerView.frame.size.height;
else
frame.origin.y = CGRectGetMaxY(self.codeaViewController.view.bounds);
_bannerView.frame = frame;
// Set banner View final position to animate to.
if (_showBannerFromTop)
frame.origin.y = 0;
else
frame.origin.y -= frame.size.height;
if (animated)
[UIView animateWithDuration: 0.5 animations: ^{self.bannerView.frame = frame;}];
else
self.bannerView.frame = frame;
_isBannerVisible = YES;
}
else
NSLog(@"showBannerViewAnimated: Unable to display banner, no Ads loaded.");
}
- (void)hideBannerViewAnimated:(BOOL)animated
{
if (_isBannerVisible)
{
CGRect frame = self.bannerView.frame;
if (_showBannerFromTop)
frame.origin.y -= frame.size.height;
else
frame.origin.y = CGRectGetMaxY(self.codeaViewController.view.bounds);
if (animated)
[UIView animateWithDuration: 0.5 animations: ^{self.bannerView.frame = frame;}];
else
self.bannerView.frame = frame;
_isBannerVisible = NO;
}
}
// C Functions
//
// Note that the returned value from all exported Lua functions is how many values that function should return in Lua.
// For example, if you return 0 from that function, you are telling Lua that function returns 0 values.
// If you return 2, you are telling Lua to expect 2 values on the stack when the function returns.
//
// To actually return values, you need to push them onto the Lua stack and then return the number of values you pushed on.
static int showAdFromTop(struct lua_State *state)
{
[iAdsAddOnInstance setShowBannerFromTop: YES];
[iAdsAddOnInstance showBannerViewAnimated: YES];
return 0;
}
static int showAdFromBottom(struct lua_State *state)
{
[iAdsAddOnInstance setShowBannerFromTop: NO];
[iAdsAddOnInstance showBannerViewAnimated: YES];
return 0;
}
static int hideAd(struct lua_State *state)
{
[iAdsAddOnInstance hideBannerViewAnimated: YES];
return 0;
}
#pragma mark - iAd Banner View Delegate
// Your application implements this method to be notified when a new advertisement is ready for display.
- (void)bannerViewDidLoadAd:(ADBannerView *)banner
{
NSLog(@"Banner View loaded Ads for display.");
NSLog(@"Active View Controller: %@", self.codeaViewController.class);
// Add our banner view to the CodeaViewController view, if we haven't already.
if (![self.codeaViewController.view.subviews containsObject: _bannerView])
[self.codeaViewController.view addSubview: _bannerView];
[self showBannerViewAnimated: YES];
}
// This method is triggered when an advertisement could not be loaded from the iAds system
// (perhaps due to a network connectivity issue).
- (void)bannerView:(ADBannerView *)banner didFailToReceiveAdWithError:(NSError *)error
{
NSLog(@"bannerview failed to receive iAd error: %@", [error localizedDescription]);
[self hideBannerViewAnimated: YES];
}
// This method is triggered when the banner confirms that an advertisement is available but before the ad is
// downloaded to the device and is ready for presentation to the user.
- (void)bannerViewWillLoadAd:(ADBannerView *)banner
{
}
// This method is triggered when the user touches the iAds banner in your application. If the willLeave argument
// passed through to the method is YES then your application will be placed into the background while the user is
// taken elsewhere to interact with or view the ad. If the argument is NO then the ad will be superimposed over your
// running application.
- (BOOL)bannerViewActionShouldBegin:(ADBannerView *)banner willLeaveApplication:(BOOL)willLeave
{
self.codeaViewController.paused = YES;
NSLog(@"Ad being displayed - Codea paused.");
return YES;
}
// This method is called when the ad view removes the ad content currently obscuring the application interface.
// If the application was paused during the ad view session this method can be used to resume activity.
- (void)bannerViewActionDidFinish:(ADBannerView *)banner
{
self.codeaViewController.paused = NO;
NSLog(@"Ad dismissed - Codea running.");
}
@end
view raw iAddsAddOn.m hosted with ❤ by GitHub
//
// IAdsAddOn.h
// AudioDemo
//
// Created by David Such on 19/04/13.
// Copyright (c) 2013 Reefwing Software. All rights reserved.
//
// Version: 1.0 - Original (19/04/13)
// 1.1 - Minor refactoring (25/4/13)
#import "CodeaAddon.h"
#import <iAd/iAd.h>
#import <Foundation/Foundation.h>
// Device Detection Macros - can be used to position the banner advertisement based on device.
// Not used in this tutorial.
//#define IS_IPAD (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
//#define IS_IPHONE (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
//#define IS_IPHONE_5 (IS_IPHONE && [[UIScreen mainScreen] bounds].size.height == 568.0f)
//#define IS_IPHONE_4 (IS_IPHONE && [[UIScreen mainScreen] bounds].size.height == 480.0f)
// Create a variable which points to the instance of this class so that we can access self
// from within the Lua c functions.
id iAdsAddOnInstance;
// This class conforms to the CodeaAddon & ADBannerViewDelegate Protocols
@interface IAdsAddOn : NSObject<CodeaAddon, ADBannerViewDelegate>
@property BOOL isBannerVisible;
@property BOOL showBannerFromTop;
@property (strong, nonatomic) ADBannerView *bannerView;
@property (weak, nonatomic) CodeaViewController *codeaViewController;
// Forward declare our Lua iAd functions. These are static to confine their scope
// to this file. By default c functions are global.
static int showAdFromTop(struct lua_State *state);
static int showAdFromBottom(struct lua_State *state);
static int hideAd(struct lua_State *state);
@end
view raw iAdsAddOn.h hosted with ❤ by GitHub
-- AudioDemo
-- Main.lua
-- Uses Cider Controls v1.6 from @Mark and @aciolino
-- make sure you add this as a Dependency
function setup()
displayMode(FULLSCREEN)
setInstructionLimit(0)
ctlFrame = Control("Audio Player", 20, HEIGHT - 600, 450, HEIGHT - 20)
ctlFrame.background = color(181, 141, 203, 255)
ctlFrame.textAlign = CENTER
ctlFrame.fontSize = 24
-- Initialise the Cider Controls
playBtn = TextButton("Play", 70, 480, 225, 520)
stopBtn = TextButton("Stop", 245, 480, 400, 520)
sldVolume = Slider("Volume Control", 70, HEIGHT - 450, 400, HEIGHT - 420, 0, 100, 50)
-- Initialise the Cider Volume Indicators
dial = Dial("Left", 70, 780, 220, 930, 0, 100, 0)
doughnut = Doughnut("Right", 250, 780, 400, 930, 0, 100, sldVolume.val)
doughnut.intervals = 25
doughnut.warm = 9
doughnut.hot = 13
-- Show Banner Ad using iAdsAddOn
showAdFromTop()
-- Start Game Center using gameCenterAddOn
gameCenterStart()
end
function draw()
-- This sets a dark background color
background(178, 173, 173, 255)
-- Draw the Cider Controls
ctlFrame:draw()
playBtn:draw()
stopBtn:draw()
sldVolume:draw()
dial:draw()
doughnut:draw()
-- Update the dB Meter Dials
--
-- The iPad should have 2 channels, left = 0 and right = 1
dial.val = averagePowerForChannel(0) or 0
doughnut.val = averagePowerForChannel(1) or 0
end
function touched(touch)
if sldVolume: touched(touch) then
-- call AudioAddOn function
setVolume(sldVolume.val)
end
if playBtn:touched(touch) then
-- call AudioAddOn and iAdsAddOn function
playMusic()
hideAd()
if playerIsAuthenticated() then
showAchievementsView()
else
print("Player not authenticated")
end
end
if stopBtn:touched(touch) then
-- call AudioAddOn and iAdsAddOn function
stopMusic()
showAdFromBottom()
if playerIsAuthenticated() then
showLeaderBoardWithIdentifier(1)
else
print("Player not authenticated")
end
end
end
view raw Main.lua hosted with ❤ by GitHub

Saturday, April 20, 2013

Tutorial 29 - Codea v1.5.2: Objective C Add On, iAds


Figure 0. Codea implementing iAds - showAdFromTop()

29.1 Overview


CodeaAddon is an experimental protocol for adding native extensions to exported Codea projects. You must deal with Lua directly to register your functions and globals. This protocol should be considered alpha and is subject to change in future Codea releases.

With that disclaimer out of the way, let's have a look at what we can do with this new functionality. We have previously done tutorials to add Game Center and iAd functionality using the old runtime but with the great new export to Xcode functionality, this is no longer the best way to skin that particular cat.

You may want to re-read our previous tutorial on implementing iAds in Codea as we won't cover the same background in this tutorial.

We will build on the previous tutorial which uses the audio add on. This has the added benefit of showing how you can stack a number of add ons to the same project.

Note that before you start adding advertising support to your applications available on iTunes, you must first agree to the iAd Network agreement. Further, you must explicitly enable iAd for each application in iTunes Connect. As a part of the iAd Network, you control the kinds of ads that are delivered to your application. We don't need to do this to test it in the simulator though.


Figure 1. Add the iAd Framework to your App



29.2 Add the iAd Framework to your App


Fire up Xcode and load the exported version of your Codea application (See Tutorial 27). Click on the imported project file at the top of the project navigator then in the Build Phases tab, scroll down to the link binary with libraries area and select the drop down arrow. Click on the "+" button below your existing frameworks to add a new framework. Find iAds and click on "Add" (Figure 1).


29.3 Update the AppDelegate Files


In the AppDelegate we need to register our new add on classes. The new AppDelegate.h file now looks like:


//
//  AppDelegate.h
//  AudioDemo
//
//  Used to demonstrate the audio add on and iAds add on libraries
//
//  Created by Reefwing Software on Sunday, 14 April 2013
//  Copyright (c) Reefwing Software. All rights reserved.
//

#import <UIKit/UIKit.h>
#import "AudioAddOn.h"
#import "IAdsAddOn.h"

@class CodeaViewController;

@interface AppDelegate : UIResponder <UIApplicationDelegate>

@property (strong, nonatomic) IAdsAddOn *iAdsAddOn;
@property (strong, nonatomic) AudioAddOn *audioAddOn;
@property (strong, nonatomic) UIWindow *window;
@property (strong, nonatomic) CodeaViewController *viewController;

@end


And similarly to what we had in the previous tutorial AppDelegate.mm now looks like:


//
//  AppDelegate.mm
//  AudioDemo
//
//  Created by Reefwing Software on Sunday, 14 April 2013
//  Copyright (c) Reefwing Software. All rights reserved.
//

#import "AppDelegate.h"
#import "CodeaViewController.h"

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    self.viewController = [[CodeaViewController alloc] init];
    
    //  Create and add our AudioAddOn to Codea
    
    self.audioAddOn = [[AudioAddOn alloc] init];
    [self.viewController registerAddon: self.audioAddOn];
    
    //  Create and add our iAdsAddOn to Codea
    
    self.iAdsAddOn = [[IAdsAddOn alloc] init];
    [self.viewController registerAddon: self.iAdsAddOn];
    
    NSString* projectPath = [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:@"AudioDemo.codea"];
    
    [self.viewController loadProjectAtPath:projectPath];
    
    self.window.rootViewController = self.viewController;
    [self.window makeKeyAndVisible];
    
    return YES;
}

- (void)applicationWillResignActive:(UIApplication *)application
{
}

- (void)applicationDidEnterBackground:(UIApplication *)application
{
}

- (void)applicationWillEnterForeground:(UIApplication *)application
{
}

- (void)applicationDidBecomeActive:(UIApplication *)application
{
}

- (void)applicationWillTerminate:(UIApplication *)application
{
}

@end



29.4 The iAds Add On


Our iAds add on implements two internal booleans, isBannerVisible and showBannerFromTop. Hopefully the use of these should be fairly obvious. We also register three new functions for use within Codea:


  1. showAdFromTop();
  2. showAdFromBottom(); and
  3. hideAd()
Our example project demonstrates the use of all 3 of these functions. We start by displaying Ads from the top of the page (once the ad has been loaded from the ad server). We hide ads when the play button is tapped and then show them from the bottom when the stop button is tapped.

In the simulator apple will send you ad errors regularly (e.g. Ad inventory unavailable) to make sure that your code can handle this. Your app will get rejected if it displays an empty ad banner so the add on takes care of this situation. When displaying the ad banner, it will slide down from the top or up from the bottom depending on which function that you have used. Similarly hideAd() will animate the ad sliding above or below the screen depending on the state of the boolean showBannerFromTop.

Figure 2. Codea implementing iAds - showAdFromBottom()


This example add on only handles ads for an iPad in portrait orientation. We have included device detection macros in IAdsAddOn.h, just remove the comment slashes if you want to use these to position your Ads. To handle different orientations and devices update the banner view frame variables in the two methods:
  1. - (void)showBannerViewAnimated:(BOOL)animated; and
  2. - (void)hideBannerViewAnimated:(BOOL)animated.

If you don't want the banner views to slide to the new position then set animated = NO for the above two methods.

29.5 Download the Code


The complete code listing is available below.

//
// AppDelegate.h
// AudioDemo
//
// Used to demonstrate the audio add on and iAds add on libraries
//
// Created by Reefwing Software on Sunday, 14 April 2013
// Copyright (c) Reefwing Software. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "AudioAddOn.h"
#import "IAdsAddOn.h"
@class CodeaViewController;
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) IAdsAddOn *iAdsAddOn;
@property (strong, nonatomic) AudioAddOn *audioAddOn;
@property (strong, nonatomic) UIWindow *window;
@property (strong, nonatomic) CodeaViewController *viewController;
@end
view raw AppDelegate.h hosted with ❤ by GitHub
//
// AppDelegate.mm
// AudioDemo
//
// Created by Reefwing Software on Sunday, 14 April 2013
// Copyright (c) Reefwing Software. All rights reserved.
//
#import "AppDelegate.h"
#import "CodeaViewController.h"
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.viewController = [[CodeaViewController alloc] init];
// Create and add our AudioAddOn to Codea
self.audioAddOn = [[AudioAddOn alloc] init];
[self.viewController registerAddon: self.audioAddOn];
// Create and add our iAdsAddOn to Codea
self.iAdsAddOn = [[IAdsAddOn alloc] init];
[self.viewController registerAddon: self.iAdsAddOn];
NSString* projectPath = [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:@"AudioDemo.codea"];
[self.viewController loadProjectAtPath:projectPath];
self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];
return YES;
}
- (void)applicationWillResignActive:(UIApplication *)application
{
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
}
- (void)applicationWillTerminate:(UIApplication *)application
{
}
@end
view raw AppDelegate.mm hosted with ❤ by GitHub
//
// AudioAddOn.h
// AudioDemo
//
// Created by David Such on 13/04/13.
// Copyright (c) 2013 Reefwing Software. All rights reserved.
//
// Version: 1.0 - Original (13/04/13)
// 1.1 - Volume control & monitoring added, metering enabled (14/04/13)
// 1.2 - Minor refactoring (19/04/13)
#import <Foundation/Foundation.h>
#import <AVFoundation/AVFoundation.h>
#import "CodeaAddon.h"
id audioAddOnInstance;
// This class conforms to the CodeaAddon & AVAudioPlayerDelegate Protocols
@interface AudioAddOn : NSObject<CodeaAddon, AVAudioPlayerDelegate>
@property (strong, nonatomic) AVAudioPlayer *player;
// Forward declare our Lua Audio functions. These are static to confine their scope
// to this file. By default c functions are global.
static int playMusic(struct lua_State *state);
static int stopMusic(struct lua_State *state);
static int getVolume(struct lua_State *state);
static int setVolume(struct lua_State *state);
static int peakPowerForChannel(struct lua_State *state);
static int averagePowerForChannel(struct lua_State *state);
@end
view raw AudioAddOn.h hosted with ❤ by GitHub
//
// AudioAddOn.m
// AudioDemo
//
// Created by David Such on 13/04/13.
// Copyright (c) 2013 Reefwing Software. All rights reserved.
//
// Version: 1.0 - Original (13/04/13)
// 1.1 - Volume control & monitoring added, metering enabled (14/04/13)
// 1.2 - Minor refactoring (19/04/13)
#import "AudioAddOn.h"
#import "lua.h"
@implementation AudioAddOn
#pragma mark - Initialisation
- (id)init
{
self = [super init];
if (self)
{
// Initialise the Audio Player with your mp3 file.
NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle]
pathForResource:@"LunarLander"
ofType:@"mp3"]];
NSError *error;
_player = [[AVAudioPlayer alloc] initWithContentsOfURL: url error: &error];
if (error)
NSLog(@"Error initialiasing Audio Add On: %@", [error localizedDescription]);
else
{
// Player initialised, assign delegate to this class
[_player setDelegate: self];
// Calling prepareToPlay preloads buffers and acquires the audio hardware needed for playback,
// which minimizes the lag between calling the play method and the start of sound output.
[_player prepareToPlay];
// A value of 0, which is the default, means to play the sound once. Set a positive integer
// value to specify the number of times to return to the start and play again. For example,
// specifying a value of 1 results in a total of two plays of the sound. Set any negative
// integer value to loop the sound indefinitely until you call the stop method.
[_player setNumberOfLoops: -1];
// The default value for the meteringEnabled property is off (Boolean NO). Before using metering
// for an audio player, you need to enable it by setting this property to YES.
[_player setMeteringEnabled: YES];
// audioAddOnInstance allows us to access self from within the c functions.
audioAddOnInstance = self;
}
}
return self;
}
#pragma mark - CodeaAddon Delegate
// Classes which comply with the <CodeaAddon> Protocol must implement this method
- (void) codea:(CodeaViewController*)controller didCreateLuaState:(struct lua_State*)L
{
NSLog(@"AudioAddon Registering Functions");
// Register the Audio functions, defined below
lua_register(L, "playMusic", playMusic);
lua_register(L, "stopMusic", stopMusic);
lua_register(L, "setVolume", setVolume);
lua_register(L, "getVolume", getVolume);
lua_register(L, "peakPowerForChannel", peakPowerForChannel);
lua_register(L, "averagePowerForChannel", averagePowerForChannel);
}
#pragma mark - Audio Add On Functions and associated Methods
// Objective C Methods
- (void)startPlayer
{
[self.player play];
}
- (void)stopPlayer
{
if ([self.player isPlaying])
[self.player stop];
}
- (void)setPlayerVolume: (int)setting
{
// The volume property is the playback gain for the AV audio player object,
// it expects a float ranging from 0.0 through 1.0.
//
// Our Codea slider control returns an integer from 0 to 100 so we need to
// convert this to a float in the appropriate range before applying it to
// our player. As a defensive measure we will clamp the result between 0.0 and 1.0.
float floatSetting = MAX(0.0f, MIN((float)setting / 100.0f, 1.0f));
[self.player setVolume: floatSetting];
}
- (int)getPlayerVolume
{
return (self.player.volume * 100.0f);
}
- (int)getPeakPower: (NSUInteger)channel
{
if ([self.player isPlaying])
{
// Refresh the average and peak power values for all channels of our audio player.
[self.player updateMeters];
// Peak power is a floating-point representation, in decibels, of a given audio channel’s current peak power.
// A return value of 0 dB indicates full scale, or maximum power while a return value of -160 dB indicates
// minimum power (that is, near silence).
//
// If the signal provided to the audio player exceeds ±full scale, then the return value may exceed 0
// (that is, it may enter the positive range).
//
// Channel numbers are zero-indexed. A monaural signal, or the left channel of a stereo signal, has channel number 0.
float power = -160.0f; // Initialise to silence
if (channel <= [self.player numberOfChannels])
power = [self.player peakPowerForChannel: channel];
// Our dial is expecting a value between 0 and 100.
if (power >= 0)
return 100;
else
{
power += 160.0f; // power is now a +ve float between 0 and 160
power = (power / 160.0f) * 100.0f; // change to a percentage
return (int)power;
}
}
else
return 0;
}
- (int)getAveragePower: (NSUInteger)channel
{
if ([self.player isPlaying])
{
// Refresh the average and peak power values for all channels of our audio player.
[self.player updateMeters];
// A floating-point representation, in decibels, of a given audio channel’s current average power.
// A return value of 0 dB indicates full scale, or maximum power; a return value of -160 dB indicates
// minimum power (that is, near silence).
//
// If the signal provided to the audio player exceeds ±full scale, then the return value may exceed 0
// (that is, it may enter the positive range).
//
// Channel numbers are zero-indexed. A monaural signal, or the left channel of a stereo signal, has channel number 0.
float power = -160.0f; // Initialise to silence
if (channel <= [self.player numberOfChannels])
power = [self.player averagePowerForChannel: channel];
// Our dial is expecting a value between 0 and 100.
if (power >= 0)
return 100;
else
{
power += 160.0f; // power is now a +ve float between 0 and 160
power = (power / 160.0f) * 100.0f; // change to a percentage
return (int)power;
}
}
else
return 0;
}
// C Functions
//
// Note that the returned value from all exported Lua functions is how many values that function should return in Lua.
// For example, if you return 0 from that function, you are telling Lua that peakPowerForPlayer (for example) returns 0 values.
// If you return 2, you are telling Lua to expect 2 values on the stack when the function returns.
//
// To actually return values, you need to push them onto the Lua stack and then return the number of values you pushed on.
static int playMusic(struct lua_State *state)
{
[audioAddOnInstance startPlayer];
return 0;
}
static int stopMusic(struct lua_State *state)
{
[audioAddOnInstance stopPlayer];
return 0;
}
static int getVolume(struct lua_State *state)
{
// Push the integer volume onto the Lua stack
lua_pushinteger(state, [audioAddOnInstance getPlayerVolume]);
// Our function returns 1 value = volume
return 1;
}
static int setVolume(struct lua_State *state)
{
[audioAddOnInstance setPlayerVolume: lua_tonumber(state, 1)];
return 0;
}
static int peakPowerForChannel(struct lua_State *state)
{
// Channel numbers are zero-indexed. A monaural signal,
// or the left channel of a stereo signal, has channel number 0.
NSUInteger channel = lua_tonumber(state, 1);
// Push the integer power onto the Lua stack
lua_pushinteger(state, [audioAddOnInstance getPeakPower: channel]);
// Our function returns 1 value = peak power
return 1;
}
static int averagePowerForChannel(struct lua_State *state)
{
// Channel numbers are zero-indexed. A monaural signal,
// or the left channel of a stereo signal, has channel number 0.
NSUInteger channel = lua_tonumber(state, 1);
// Push the integer power onto the Lua stack
lua_pushinteger(state, [audioAddOnInstance getAveragePower: channel]);
// Our function returns 1 value = peak power
return 1;
}
#pragma mark - AVAudioPlayer Delegate
// These Audio Player call back methods are not used in this tutorial but provided here
// for information. There are a number of other delegate methods available. Check the
// documentation.
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
{
}
- (void)audioPlayerDecodeErrorDidOccur:(AVAudioPlayer *)player error:(NSError *)error
{
NSLog(@"Error decoding audio file: %@", [error localizedDescription]);
}
-(void)audioPlayerBeginInterruption:(AVAudioPlayer *)player
{
}
-(void)audioPlayerEndInterruption:(AVAudioPlayer *)player
{
}
@end
view raw AudioAddOn.m hosted with ❤ by GitHub
//
// IAdsAddOn.h
// AudioDemo
//
// Created by David Such on 19/04/13.
// Copyright (c) 2013 Reefwing Software. All rights reserved.
//
// Version: 1.0 - Original (19/04/13)
#import "CodeaAddon.h"
#import <iAd/iAd.h>
#import <Foundation/Foundation.h>
// Device Detection Macros - can be used to position the banner advertisement based on device.
// Not used in this tutorial.
//#define IS_IPAD (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
//#define IS_IPHONE (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
//#define IS_IPHONE_5 (IS_IPHONE && [[UIScreen mainScreen] bounds].size.height == 568.0f)
//#define IS_IPHONE_4 (IS_IPHONE && [[UIScreen mainScreen] bounds].size.height == 480.0f)
// Create a variable which points to the instance of this class so that we can access self
// from within the Lua c functions.
id iAdsAddOnInstance;
// This class conforms to the CodeaAddon & ADBannerViewDelegate Protocols
@interface IAdsAddOn : NSObject<CodeaAddon, ADBannerViewDelegate>
@property BOOL isBannerVisible;
@property BOOL showBannerFromTop;
@property (strong, nonatomic) ADBannerView *bannerView;
@property (weak, nonatomic) CodeaViewController *currentController;
// Forward declare our Lua iAd functions. These are static to confine their scope
// to this file. By default c functions are global.
static int showAdFromTop(struct lua_State *state);
static int showAdFromBottom(struct lua_State *state);
static int hideAd(struct lua_State *state);
@end
view raw IAdsAddOn.h hosted with ❤ by GitHub
//
// IAdsAddOn.m
// AudioDemo
//
// Created by David Such on 19/04/13.
// Copyright (c) 2013 Reefwing Software. All rights reserved.
//
// Version: 1.0 - Original (19/04/13)
#import "lua.h"
#import "IAdsAddOn.h"
@implementation IAdsAddOn
#pragma mark - Initialisation
- (id)init
{
self = [super init];
if (self)
{
// audioAddOnInstance allows us to access self from within the c functions.
iAdsAddOnInstance = self;
// Initialise our Instance Variables
_isBannerVisible = NO;
_showBannerFromTop = YES;
// Initialise our iAd Banner View
CGRect frame = CGRectZero;
frame.size = [ADBannerView sizeFromBannerContentSizeIdentifier: ADBannerContentSizeIdentifierPortrait];
_bannerView = [[ADBannerView alloc] initWithFrame: frame];
_bannerView.requiredContentSizeIdentifiers = [NSSet setWithObject: ADBannerContentSizeIdentifierPortrait];
_bannerView.delegate = self;
}
return self;
}
#pragma mark - CodeaAddon Delegate
// Classes which comply with the <CodeaAddon> Protocol must implement this method
- (void) codea:(CodeaViewController*)controller didCreateLuaState:(struct lua_State*)L
{
NSLog(@"iAdAddOn Registering Functions");
// Register the iAd functions, defined below
lua_register(L, "showAdFromTop", showAdFromTop);
lua_register(L, "showAdFromBottom", showAdFromBottom);
lua_register(L, "hideAd", hideAd);
// Hook up with the CodeaViewController - don't try to add subviews in this method.
self.currentController = controller;
}
#pragma mark - iAds Add On Functions and associated Methods
// Objective C Methods
- (void)showBannerViewAnimated:(BOOL)animated
{
if ([self.bannerView isBannerLoaded])
{
// We only display the banner View if it has ads loaded and isn't already visible.
// Set the banner view starting position as off screen.
CGRect frame = _bannerView.frame;
if (_showBannerFromTop)
frame.origin.y = 0.0f - _bannerView.frame.size.height;
else
frame.origin.y = CGRectGetMaxY(self.currentController.view.bounds);
_bannerView.frame = frame;
// Set banner View final position to animate to.
if (_showBannerFromTop)
frame.origin.y = 0;
else
frame.origin.y -= frame.size.height;
if (animated)
[UIView animateWithDuration: 0.5 animations: ^{self.bannerView.frame = frame;}];
else
self.bannerView.frame = frame;
_isBannerVisible = YES;
}
else
NSLog(@"showBannerViewAnimated: Unable to display banner, no Ads loaded.");
}
- (void)hideBannerViewAnimated:(BOOL)animated
{
if (_isBannerVisible)
{
CGRect frame = self.bannerView.frame;
if (_showBannerFromTop)
frame.origin.y -= frame.size.height;
else
frame.origin.y = CGRectGetMaxY(self.currentController.view.bounds);
if (animated)
[UIView animateWithDuration: 0.5 animations: ^{self.bannerView.frame = frame;}];
else
self.bannerView.frame = frame;
_isBannerVisible = NO;
}
}
// C Functions
//
// Note that the returned value from all exported Lua functions is how many values that function should return in Lua.
// For example, if you return 0 from that function, you are telling Lua that function returns 0 values.
// If you return 2, you are telling Lua to expect 2 values on the stack when the function returns.
//
// To actually return values, you need to push them onto the Lua stack and then return the number of values you pushed on.
static int showAdFromTop(struct lua_State *state)
{
[iAdsAddOnInstance setShowBannerFromTop: YES];
[iAdsAddOnInstance showBannerViewAnimated: YES];
return 0;
}
static int showAdFromBottom(struct lua_State *state)
{
[iAdsAddOnInstance setShowBannerFromTop: NO];
[iAdsAddOnInstance showBannerViewAnimated: YES];
return 0;
}
static int hideAd(struct lua_State *state)
{
[iAdsAddOnInstance hideBannerViewAnimated: YES];
return 0;
}
#pragma mark - iAd Banner View Delegate
// Your application implements this method to be notified when a new advertisement is ready for display.
- (void)bannerViewDidLoadAd:(ADBannerView *)banner
{
NSLog(@"Banner View loaded Ads for display.");
NSLog(@"Active View Controller: %@", self.currentController.class);
// Add our banner view to the CodeaViewController view, if we haven't already.
if (![self.currentController.view.subviews containsObject: _bannerView])
[self.currentController.view addSubview: _bannerView];
[self showBannerViewAnimated: YES];
}
// This method is triggered when an advertisement could not be loaded from the iAds system
// (perhaps due to a network connectivity issue).
- (void)bannerView:(ADBannerView *)banner didFailToReceiveAdWithError:(NSError *)error
{
NSLog(@"bannerview failed to receive iAd error: %@", [error localizedDescription]);
[self hideBannerViewAnimated: YES];
}
// This method is triggered when the banner confirms that an advertisement is available but before the ad is
// downloaded to the device and is ready for presentation to the user.
- (void)bannerViewWillLoadAd:(ADBannerView *)banner
{
}
// This method is triggered when the user touches the iAds banner in your application. If the willLeave argument
// passed through to the method is YES then your application will be placed into the background while the user is
// taken elsewhere to interact with or view the ad. If the argument is NO then the ad will be superimposed over your
// running application.
- (BOOL)bannerViewActionShouldBegin:(ADBannerView *)banner willLeaveApplication:(BOOL)willLeave
{
self.currentController.paused = YES;
NSLog(@"Ad being displayed - Codea paused.");
return YES;
}
// This method is called when the ad view removes the ad content currently obscuring the application interface.
// If the application was paused during the ad view session this method can be used to resume activity.
- (void)bannerViewActionDidFinish:(ADBannerView *)banner
{
self.currentController.paused = NO;
NSLog(@"Ad dismissed - Codea running.");
}
@end
view raw IAdsAddOn.m hosted with ❤ by GitHub
-- AudioDemo
-- Main.lua
-- Uses Cider Controls v1.6 from @Mark and @aciolino
-- make sure you add this as a Dependency
function setup()
displayMode(FULLSCREEN)
setInstructionLimit(0)
ctlFrame = Control("Audio Player", 20, HEIGHT - 600, 450, HEIGHT - 20)
ctlFrame.background = color(181, 141, 203, 255)
ctlFrame.textAlign = CENTER
ctlFrame.fontSize = 24
-- Initialise the Cider Controls
playBtn = TextButton("Play", 70, 480, 225, 520)
stopBtn = TextButton("Stop", 245, 480, 400, 520)
sldVolume = Slider("Volume Control", 70, HEIGHT - 450, 400, HEIGHT - 420, 0, 100, 50)
-- Initialise the Cider Volume Indicators
dial = Dial("Left", 70, 780, 220, 930, 0, 100, 0)
doughnut = Doughnut("Right", 250, 780, 400, 930, 0, 100, sldVolume.val)
doughnut.intervals = 25
doughnut.warm = 9
doughnut.hot = 13
-- Show Banner Ad using iAdsAddOn
showAdFromTop()
end
function draw()
-- This sets a dark background color
background(178, 173, 173, 255)
-- Draw the Cider Controls
ctlFrame:draw()
playBtn:draw()
stopBtn:draw()
sldVolume:draw()
dial:draw()
doughnut:draw()
-- Update the dB Meter Dials
--
-- The iPad should have 2 channels, left = 0 and right = 1
dial.val = averagePowerForChannel(0) or 0
doughnut.val = averagePowerForChannel(1) or 0
end
function touched(touch)
if sldVolume: touched(touch) then
-- call AudioAddOn function
setVolume(sldVolume.val)
end
if playBtn:touched(touch) then
-- call AudioAddOn and iAdsAddOn function
playMusic()
hideAd()
end
if stopBtn:touched(touch) then
-- call AudioAddOn and iAdsAddOn function
stopMusic()
showAdFromBottom()
end
end
view raw Main.lua hosted with ❤ by GitHub