When you are a programmer, you get used to searching for new tools, fighting with poorly documented frameworks, and searching for solutions of problems that come with new staff. You also end up learning when to discard a tool. Tools may be bad, may not adjust to your problem, may give you more problems than solutions... or may be exactly what you are looking for... But what makes a tool great is the people behind it, a technical support that turns problems into solutions. And that's exactly how I would describe Kamcord.
The moment I saw this tool I knew it could be perfect for the new game we are designing, so I decided to make a project to test it. I had two problems: one with the rotation and the other was causing a crash with certain iOS version. Thus, I wrote to the supporting team. The answer was really fast and solved the orientation issue. They needed more information about my devices and project in order to solve the crashing problem so we exchanged several mails, projects, framework's updates... In just 5 days I had the whole project perfectly working. I didn't have to insist, but they were working on my problem till they got to the solution. And every mail was really polite. I'm very happy with the support I've received :)
The moment I saw this tool I knew it could be perfect for the new game we are designing, so I decided to make a project to test it. I had two problems: one with the rotation and the other was causing a crash with certain iOS version. Thus, I wrote to the supporting team. The answer was really fast and solved the orientation issue. They needed more information about my devices and project in order to solve the crashing problem so we exchanged several mails, projects, framework's updates... In just 5 days I had the whole project perfectly working. I didn't have to insist, but they were working on my problem till they got to the solution. And every mail was really polite. I'm very happy with the support I've received :)
Kamcord, this amazing tool, is a framework that allows your app's users to record the flow of the app and share it with friends. Easy and well documented.
Available for Cocos2d and Unity. Download from the web and follow the tutorial, it's well explained.
- Copy the Kamcord folder into your project.
- Add the framework and other supporting frameworks (twitter, mediaplayer, ... ).
- Adjust the settings for iOS < 4 (turn to optional some of the just added frameworks).
- Add to 'other linker flags': -ObjC -all_load -lxml2
At this point, it should compile, if not, check all the steps. At my first attempt it failed but I realized I had skipped the third step.
On the other hand, Kamcord uses their own glview, therefore you have to do some changes in your AppDelegate.
On the other hand, Kamcord uses their own glview, therefore you have to do some changes in your AppDelegate.
- Make the root view controller extend from KCViewController
- Instantiate the glview as a KCGLView instead of an eaglview
- Set the root view controller to the window
- Configure Kamcord and set your developer key, secret and app name.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import <Kamcord/Kamcord.h> | |
@implementation AppDelegate | |
@synthesize window; | |
//** Other functions **// | |
- (void) applicationDidFinishLaunching:(UIApplication*)application { | |
// Init the window | |
window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; | |
// Try to use CADisplayLink director | |
// if it fails (SDK < 3.1) use the default director | |
if( ! [CCDirector setDirectorType:kCCDirectorTypeDisplayLink] ) | |
[CCDirector setDirectorType:kCCDirectorTypeDefault]; | |
CCDirector *director = [CCDirector sharedDirector]; | |
//** NEW **// | |
// Init the View Controller | |
// viewController = [[RootViewController alloc] initWithNibName:nil bundle:nil]; | |
// viewController.wantsFullScreenLayout = YES; | |
// Create the EAGLView manually | |
// 1. Create a RGB565 format. Alternative: RGBA8 | |
// 2. depth format of 0 bit. Use 16 or 24 bit for 3d effects, like CCPageTurnTransition | |
// | |
// | |
// EAGLView *glView = [EAGLView viewWithFrame:[window bounds] pixelFormat:kEAGLColorFormatRGB565 // kEAGLColorFormatRGBA8 | |
// depthFormat:0]; // GL_DEPTH_COMPONENT16_OES | |
// attach the openglView to the director | |
// [director setOpenGLView:glView]; | |
// Enables High Res mode (Retina Display) on iPhone 4 and maintains low res on all other devices | |
// if( ! [director enableRetinaDisplay:YES] ) | |
// CCLOG(@"Retina Display Not supported"); | |
// VERY IMPORTANT: | |
// If the rotation is going to be controlled by a UIViewController | |
// then the device orientation should be "Portrait". | |
// IMPORTANT: | |
// By default, this template only supports Landscape orientations. | |
// Edit the RootViewController.m file to edit the supported orientations. | |
//#if GAME_AUTOROTATION == kGameAutorotationUIViewController | |
// [director setDeviceOrientation:kCCDeviceOrientationPortrait]; | |
//#else | |
// [director setDeviceOrientation:kCCDeviceOrientationLandscapeLeft]; | |
//#endif | |
[director setAnimationInterval:1.0/60]; | |
[director setDisplayFPS:YES]; | |
// make the OpenGLView a child of the view controller | |
// [viewController setView:glView]; | |
//** Kamcord **// | |
//** Instantiate a KCGLView, which is a subclass of EAGLView with special recording functionality. | |
KCGLView * glView = [KCGLView viewWithFrame:[window bounds] | |
pixelFormat:kEAGLColorFormatRGB565 | |
depthFormat:0]; | |
//** Kamcord uses UIKit for autorotation, which requires special logic to handle rotations. | |
window.rootViewController = [ [KCViewController alloc] initWithNibName:nil bundle:nil]; | |
window.rootViewController.view = glView; | |
//** Tell Kamcord about the root view controller and the KCGLView | |
[Kamcord setParentViewController:window.rootViewController]; | |
[Kamcord setOpenGLView:glView]; | |
//** Set the device orientation. Must use Kamcord, not CCDirector! | |
[Kamcord setDeviceOrientation:CCDeviceOrientationLandscapeLeft]; | |
[Kamcord setDeveloperKey:@"--your key--" | |
developerSecret:@"--your secret--" | |
appName:@"testingKamcord"]; | |
//** End of Kamcord code **// | |
// make the View Controller a child of the main window | |
[window addSubview: viewController.view]; | |
[window makeKeyAndVisible]; | |
// Default texture format for PNG/BMP/TIFF/JPEG/GIF images | |
// It can be RGBA8888, RGBA4444, RGB5_A1, RGB565 | |
// You can change anytime. | |
[CCTexture2D setDefaultAlphaPixelFormat:kCCTexture2DPixelFormat_RGBA8888]; | |
// Removes the startup flicker | |
[self removeStartupFlicker]; | |
// Run the intro Scene | |
[[CCDirector sharedDirector] runWithScene: [HelloWorldLayer scene]]; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import <Kamcord/Kamcord.h> | |
//** RootViewController now extends from this new view controller | |
@interface RootViewController : KCViewController { | |
} |
Notation for the comments:
Indented // comments: cocos2d autogenerated;
Non-indented // comments: code not anymore needed;
Indented //* comments: Kamcord explanations;
Now, adding the functionality is really easy:
- Import the Kamcord framework on each scene you want.
- Call startRecording when you want to start the video
- Call stopRecording when you want to stop the recording
- Call showView (normally after you have record) to show the replay and share options of Kamcord
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// HelloWorldLayer.m | |
// testingkamcord | |
// | |
// Created by Elena Vielva on 27/03/13. | |
// Copyright __MyCompanyName__ 2013. All rights reserved. | |
// | |
// Import the interfaces | |
#import "HelloWorldLayer.h" | |
#import <Kamcord/Kamcord.h> | |
#import "AppDelegate.h" | |
// HelloWorldLayer implementation | |
@implementation HelloWorldLayer | |
+(CCScene *) scene { | |
// 'scene' is an autorelease object. | |
CCScene *scene = [CCScene node]; | |
// 'layer' is an autorelease object. | |
HelloWorldLayer *layer = [HelloWorldLayer node]; | |
// add layer as a child to scene | |
[scene addChild: layer]; | |
// return the scene | |
return scene; | |
} | |
// on "init" you need to initialize your instance | |
-(id) init { | |
if( (self=[super init])) { | |
// create and initialize a Label | |
label = [CCLabelTTF labelWithString:@"¡¡Hola mundo!!" fontName:@"Marker Felt" fontSize:64]; | |
// ask director the the window size | |
CGSize size = [[CCDirector sharedDirector] winSize]; | |
// position the label on the center of the screen | |
label.position = ccp( size.width /2 , size.height/2 ); | |
// add the label as a child to this Layer | |
[self addChild: label z:10]; | |
// Button | |
button = [CCSprite spriteWithFile:@"PanicButton.png"]; | |
button.position = ccp(size.width*0.9, size.height*0.1); | |
[self addChild:button z:5]; | |
// Grabar con Kamcord | |
startRec = [CCLabelTTF labelWithString:@"Start recording" fontName:@"Marker Felt" fontSize:56]; | |
startRec.position = ccp(size.width/2, size.height*0.3); | |
startRec.color = ccGREEN; | |
startRec.opacity=0; | |
[self addChild:startRec z:5]; | |
stopRec = [CCLabelTTF labelWithString:@"Stop recording" fontName:@"Marker Felt" fontSize:56]; | |
stopRec.position = ccp(size.width/2, size.height*0.3); | |
stopRec.color = ccRED; | |
stopRec.opacity=0; | |
[self addChild:stopRec z:5]; | |
share = [CCLabelTTF labelWithString:@"Share the video" fontName:@"Marker Felt" fontSize:26]; | |
share.position = ccp(size.width*0.85, size.height*0.2); | |
share.color = ccORANGE; | |
share.opacity=0; | |
[self addChild:share z:5]; | |
[Kamcord setDefaultEmailBody:@"¡Ey! Mira el baile que he hecho con la etiqueta de texto...\r\n"]; | |
[Kamcord setDefaultTitle:@"Baile"]; | |
touched = NO; | |
self.isTouchEnabled = YES; | |
} | |
return self; | |
} | |
- (void) ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { | |
UITouch *touch = [touches anyObject]; | |
CGPoint location = [touch locationInView:touch.view]; | |
location = [[CCDirector sharedDirector] convertToGL:location]; | |
if (CGRectContainsPoint(label.boundingBox, location)) { | |
label.position = location; | |
touched = YES; | |
return; | |
} | |
if (CGRectContainsPoint(button.boundingBox, location)) { | |
if (recording) { | |
recording = NO; | |
[Kamcord stopRecording]; | |
[share runAction:[CCFadeIn actionWithDuration:0.2]]; | |
[stopRec runAction:[CCSequence actions:[CCFadeIn actionWithDuration:0.1],[CCDelayTime actionWithDuration:0.2],[CCFadeOut actionWithDuration:0.3], nil]]; | |
}else { | |
recording = YES; | |
share.opacity = 0; | |
[Kamcord startRecording]; | |
[startRec runAction:[CCSequence actions:[CCFadeIn actionWithDuration:0.1],[CCDelayTime actionWithDuration:0.2],[CCFadeOut actionWithDuration:0.3], nil]]; | |
} | |
return; | |
} | |
if (share.opacity>0&&CGRectContainsPoint(share.boundingBox, location)) { | |
[Kamcord showView]; | |
return; | |
} | |
} | |
- (void) ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { | |
UITouch *touch = [touches anyObject]; | |
CGPoint location = [touch locationInView:touch.view]; | |
if (touched) { | |
location = [[CCDirector sharedDirector] convertToGL:location]; | |
label.position = location; | |
} | |
} | |
- (void) ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { | |
CGSize size = [[CCDirector sharedDirector] winSize]; | |
if (touched) { | |
label.position = ccp(size.width/2, size.height/2); | |
touched = NO; | |
} | |
} | |
// on "dealloc" you need to release all your retained objects | |
- (void) dealloc { | |
// in case you have something to dealloc, do it in this method | |
// in this particular example nothing needs to be released. | |
// cocos2d will automatically release all the children (Label) | |
// don't forget to call "super dealloc" | |
[super dealloc]; | |
} | |
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// HelloWorldLayer.h | |
// testingkamcord | |
// | |
// Created by Elena Vielva on 27/03/13. | |
// Copyright __MyCompanyName__ 2013. All rights reserved. | |
// | |
// When you import this file, you import all the cocos2d classes | |
#import "cocos2d.h" | |
#import <AVFoundation/AVFoundation.h> | |
// HelloWorldLayer | |
@interface HelloWorldLayer : CCLayer <UIImagePickerControllerDelegate, UINavigationControllerDelegate> { | |
CCLabelTTF *label; | |
BOOL touched; | |
BOOL recording; | |
CCSprite *button; | |
CCLabelTTF *startRec; | |
CCLabelTTF *stopRec; | |
CCLabelTTF *share; | |
UIWindow *window; | |
UIImage *newImage; | |
} | |
// returns a CCScene that contains the HelloWorldLayer as the only child | |
+(CCScene *) scene; | |
@end |
It will look like this