Skip to content

iOS Everyplay Tutorial_ko

FGAGM edited this page Feb 6, 2014 · 2 revisions

이 자습서에서는 EveryplayTutorial을 통해서 everyplay-ios-examples 리포지토리에 수록된 예제 앱을 살펴볼 수 있습니다.

이 문서에서는 다음 내용을 다룹니다.

  • 기본적인 Everyplay 클라이언트 ID/시크릿 초기화
  • Everyplay 델리게이트 처리
  • 사용자 정의 EAGLView 클래스에 Everyplay 관련 후크 추가
  • 녹화한 비디오에 메타데이터를 추가하여 사용자 경험과 정보 탐색 향상
  • Everyplay 뷰 시작, 녹화 시작/중단 및 마지막으로 녹화한 비디오 재생을 위한 간단한 UI 단추 추가

사용 가능한 메서드에 대한 더 자세한 내용은 iOS 클래스 참조 문서를 참조하십시오.

이 문서에 담긴 내용을 시작하기 전에 다음 필수 문서를 모두 읽고 이해하십시오.

준비하기

Github에서 Everyplay SDK 및 예제 다운로드

% git clone https://github.com/Everyplay/everyplay-ios-sdk.git
% git clone https://github.com/Everyplay/everyplay-ios-examples.git
% cd everyplay-ios-examples
% open -a Xcode EveryplayTutorial.xcodeproj

코드

  • Everyplay.framework와 Everyplay.bundle을 EveryplayTutorial 프로젝트로 끌어다 놓습니다.

  • EveryplayTutorial 타깃을 선택한 다음 Summary(요약) 탭에서 Linked Frameworks and Libraries(연결된 프레임워크 및 라이브러리)를 선택하여 최소한 다음 프레임워크에 대해서 연결하십시오.
    • AdSupport(iOS 6 이상. iOS 6 이전 버전 호환성을 위해 Optional(선택적) 연결로 설정)
    • AssetsLibrary
    • AudioToolbox(사전에 EveryplayTutorial에 추가됨)
    • AVFoundation
    • CoreGraphics(사전에 EveryplayTutorial에 추가됨)
    • CoreImage(iOS 5 이상. iOS 5 이전 버전 호환성을 위해 Optional 연결로 설정)
    • CoreMedia
    • CoreVideo
    • Foundation(사전에 EveryplayTutorial에 추가됨)
    • MessageUI
    • MobileCoreServices
    • OpenGLES(사전에 EveryplayTutorial에 추가됨)
    • QuartzCore(사전에 EveryplayTutorial에 추가됨)
    • Security
    • Social(iOS 6 이상. iOS 6 이전 버전 호환성을 위해 Optional 연결로 설정)
    • StoreKit
    • SystemConfiguration
    • Twitter(iOS 5 이상. iOS 5 이전 버전 호환성을 위해 Optional 연결로 설정)
    • UIKit(사전에 EveryplayTutorial에 추가됨)

  • 뷰 컨트롤러 헤더 파일 ERViewController.h에 Everyplay 헤더 엄브렐러를 가져오고 Everyplay의 델리게이트를 추가합니다.
  */

 #import <UIKit/UIKit.h>
 #import <QuartzCore/QuartzCore.h>
 #import "EAGLView.h"
 #import "Config.h"

-@interface ERViewController : UIViewController {
+#import <Everyplay/Everyplay.h>
+
+@interface ERViewController : UIViewController <EveryplayDelegate> {
     EAGLContext *context;
     GLuint program;

     BOOL animating;
     NSInteger animationFrameInterval;
     CADisplayLink *__unsafe_unretained displayLink;
  • application:didFinishLaunchingWithOptions: 메서드가 호출될 때 클라이언트 ID, 시크릿, 리다이렉트 URI로 Everyplay를 초기화하는 코드를 애플리케이션 델리게이트 구현 파일 ERAppDelegate.m에 추가합니다.

    Everyplay 델리게이트 및 뷰 처리를 위해 델리게이트 처리 및 뷰 컨트롤러를 위해 사용되는 인스턴스를 전달해야 합니다. 이 예제에서는 ERViewController 핸들을 모두 사용합니다.

         self.viewController = [[ERViewController alloc] initWithNibName:@"ERViewController_iPhone" bundle:nil];
     } else {
         self.viewController = [[ERViewController alloc] initWithNibName:@"ERViewController_iPad" bundle:nil];
     }
     self.window.rootViewController = self.viewController;
     [self.window makeKeyAndVisible];

+    // Everyplay SDK를 클라이언트 ID와 시크릿으로 초기화
+    // 이러한 정보는 https://developers.everyplay.com에서 생성
+    [Everyplay setClientId:@"b459897317dc88c80b4515e380e1378022f874d2" clientSecret:@"f1a162969efb1c27aac6977f35b34127e68ee163" redirectURI:@"https://m.everyplay.com/auth"];
+
+    // Everyplay에 뷰 표시와 델리게이트 호출을 위해 rootViewController를 사용하도록 전달합니다.
+    [Everyplay initWithDelegate:self.viewController andParentViewController:self.window.rootViewController];
+
     return YES;
 }

 - (void)applicationWillResignActive:(UIApplication *)application
 {
     // 응용 프로그램이 활성 상태에서 비활성 상태로 움직이기 직전에 보냅니다. 이것은 특정한 유형의 일시적인 중단(전화 또는 SMS 메시지 수신)이 발생하거나 사용자가 응용 프로그램을 종료하여 백그라운드 상태로의 전환이 시작될 때 일어날 수 있습니다.
     // 이 메서드를 사용하여 진행 중인 작업을 일시 중지하고 타이머를 비활성화하며 OpenGL ES 프레임 속도를 낮출 수 있습니다. 또, 이 메서드를 사용해서 게임을 일시 중지해야 합니다.
  • 뷰 헤더 파일 EAGLView.h에 Everyplay 헤더 엄브렐러를 가져오고 EAGLView 인스턴스 변수를 위해 everyplayCapture를 추가합니다.
 #import <OpenGLES/ES1/gl.h>
 #import <OpenGLES/ES1/glext.h>
 #import <OpenGLES/ES2/gl.h>
 #import <OpenGLES/ES2/glext.h>
 #import "Config.h"

+#import <Everyplay/Everyplay.h>
+
 // 이 클래스는 CoreAnimation의 CAEAGLLayer를 편리한 UIView 서브 클래스로 래핑합니다.
 // 뷰 콘텐츠는 기본적으로 EAGL 표면이며, 이곳으로 OpenGL 장면을 렌더링합니다.
 // EAGL 표면에 알파 채널이 있을 때만 뷰를 불투명 아님으로 설정할 수 있음을 알아 두십시오.
 @interface EAGLView : UIView {
 @private
     EAGLContext *context;

...

     // 프레임 버퍼와 렌더 버퍼를 위한 OpenGL ES 이름은 이 뷰를 렌더링하는 데 사용됩니다.
     GLuint defaultFramebuffer, colorRenderbuffer, depthRenderbuffer;

     // MSAA에서 사용됩니다.
     GLuint msaaFramebuffer, msaaColorRenderbuffer, msaaDepthRenderbuffer;
     BOOL useMSAA;
+
+    EveryplayCapture *everyplayCapture;
 }

 @property (nonatomic, strong) EAGLContext *context;
 @property (nonatomic, assign, readonly) GLfloat aspect;

 - (void)setFramebuffer;
 - (BOOL)presentFramebuffer;
  • EAGLView 구현 파일 EAGLView.m에 약간의 수정을 가하여 OpenGL 초기화 및 프레임 버퍼/뷰 처리를 완전히 통합해야 합니다.

    프레임 버퍼(또는 MSAA를 사용할 때는 복수의 프레임 버퍼) 생성이 완료되면 생성한 프레임 버퍼를 EveryplayCapture의 createFramebuffer:withMSAA: 메서드로 전달해야 합니다. MSAA를 전혀 사용하지 않는다면 대신 createFramebuffer: 메서드를 호출해도 됩니다.

    Everyplay는 전달한 프레임 버퍼 핸들에서 필요한 필수 정보(프레임 버퍼 넓이, 높이, 안티앨리어싱 상태 등)를 쿼리합니다.

    프레임 버퍼는 뷰 처리에 따라서 런타임 중에 임의의 지점에서 다시 생성될 수 있습니다. 따라서, 프레임 버퍼를 삭제할 때는 언제든지 EveryplayCapture의 deleteFramebuffer 메서드를 호출하십시오.

    if (!framebufferHeight) return 0.0;

    return ((GLfloat) framebufferWidth) / ((GLfloat) framebufferHeight);
 }

 - (void)createFramebuffer
 {
+    if (everyplayCapture == nil) {
+        everyplayCapture = [[EveryplayCapture alloc] initWithView:self eaglContext:context layer:(CAEAGLLayer *)self.layer];
+    }
+
     if (context && !defaultFramebuffer) {
         // 기본 프레임 버퍼 객체를 생성합니다.
         glGenFramebuffers(1, &defaultFramebuffer);
         glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebuffer);

         // 색상 렌더 버퍼를 생성하고 백업 저장소를 할당합니다.
         glGenRenderbuffers(1, &colorRenderbuffer);
...

             glBindRenderbuffer(GL_RENDERBUFFER, msaaColorRenderbuffer);
         }

         if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
             NSLog(@"Failed to make complete framebuffer object %x", glCheckFramebufferStatus(GL_FRAMEBUFFER));
         glViewport(0, 0, framebufferWidth, framebufferHeight);
+
+        [everyplayCapture createFramebuffer:defaultFramebuffer withMSAA:msaaFramebuffer];
     }
 }

 - (void)deleteFramebuffer
 {
     if (context) {
         [EAGLContext setCurrentContext:context];

+        [everyplayCapture deleteFramebuffer];
+
         if (defaultFramebuffer) {
             glDeleteFramebuffers(1, &defaultFramebuffer);
             defaultFramebuffer = 0;
         }

         if (colorRenderbuffer) {
             glDeleteRenderbuffers(1, &colorRenderbuffer);
  • presentFramebuffer 메서드는 수많은 OpenGL 애플리케이션 및 해당 애플리케이션의 렌더링 루프를 위한 핵심입니다. 이 메서드는 일반적으로 잠재적인 MSAA 프레임 버퍼 분해를 처리하고, EAGLContext의 presentRenderbuffer: 메서드를 통해 화면으로 OpenGL 렌더 버퍼를 전달하기 전에 성능 최적화를 위해 오래된 MSAA 프레임 버퍼 콘텐츠를 버리는 역할을 전담합니다.

    EveryplayCapture의 beforePresentRenderbuffer: 메서드와 afterPresentRenderbuffer: 메서드는 모두 불 값을 반환합니다. 두 메서드는 녹화 중에 YES를 반환하는데, 아래에 나타난 규칙을 사용하면 불필요한 OpenGL 명령을 추가로 최적화할 수 있습니다.

    EveryplayCapture의 afterPresentRenderbuffer: 메서드는 녹화 중에 사용자 정의 프레임 버퍼 또는 상황에 따라서 내부 MASS 프레임 버퍼나 프로그래머가 전달한 MASS 프레임 버퍼에 바인드됩니다. 기술적인 제약 때문에 내부 32bit MSAA 프레임 버퍼는 16bit MSAA 프레임 버퍼를 가질 때만 사용됩니다. 또, 내부 32bit 프레임 버퍼는 MSAA 프레임 버퍼에 대해 바인드되지 않을 때도 사용됩니다.

    EveryplayCapture의 beforePresentRenderbuffer: 메서드는 녹화 중에 MSAA 프레임 버퍼 분해를 처리하고 연산을 중단하며, 사용한 내부 녹화 버퍼를 변경합니다.

    두 메서드는 모두 가벼우며, 녹화가 진행되지 않을 때 성능 오버헤드를 일으키지 않습니다. 두 메서드를 항상 그래픽 파이프라인에 통합된 채로 두는 것이 바람직합니다.

     BOOL success = FALSE;

     if (context) {
         [EAGLContext setCurrentContext:context];

 #define ARRAY_LENGTH(X) (sizeof(X) / sizeof((X)[0]))

+        if (![everyplayCapture beforePresentRenderbuffer:defaultFramebuffer]) {
             if (useMSAA) {
                 glBindFramebuffer(GL_READ_FRAMEBUFFER_APPLE, msaaFramebuffer);
                 glBindFramebuffer(GL_DRAW_FRAMEBUFFER_APPLE, defaultFramebuffer);
                 glResolveMultisampleFramebufferAPPLE();

                 static const GLenum s_attachments[] = { GL_COLOR_ATTACHMENT0, GL_DEPTH_ATTACHMENT };
                 glDiscardFramebufferEXT(GL_READ_FRAMEBUFFER_APPLE, ARRAY_LENGTH(s_attachments), s_attachments);
             } else {
                 static const GLenum s_attachments[] = { GL_DEPTH_ATTACHMENT };
                 glDiscardFramebufferEXT(GL_READ_FRAMEBUFFER_APPLE, ARRAY_LENGTH(s_attachments), s_attachments);
             }
+        }

         glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer);
         success = [context presentRenderbuffer:GL_RENDERBUFFER];

+        if (![everyplayCapture afterPresentRenderbuffer:(useMSAA ? msaaFramebuffer : defaultFramebuffer)]) {
             if (useMSAA) {
                 glBindFramebuffer(GL_FRAMEBUFFER, msaaFramebuffer);
             }
+        }
     }

     return success;
 }

 - (void)layoutSubviews
 {
     // 다음 번 setFramebuffer 메서드 호출이 시작될 때 프레임 버퍼가 다시 생성됩니다.
  • 조금만 더 하면 테스트를 시작할 수 있습니다! ERViewController 구현 파일에 단추 몇 개와 Everyplay 델리게이트 핸들러를 추가하면 앱을 빌드하고 실행할 준비가 끝납니다.

    everyplayRecordingStopped에서, 녹화한 세션에 메타데이터를 추가합니다. 메타데이터는 게임에서 최고 점수, 플레이한 레벨 또는 사용한 등장인물 등이 될 수 있습니다.

    사용자의 경험과 탐색 및 검색의 향상을 위해 메타데이터를 추가할 것을 권장합니다. Everyplay는 이후 버전 및 업데이트에서 게임 플레이에 관한 정보를 출력할 목적으로 이러한 정보를 사용하며, 귀사에서는 서버 API를 통해서 개인적으로 이러한 정보를 사용할 수도 있습니다.

     NUM_ATTRIBUTES
 };

 @interface ERViewController ()
 @property (nonatomic, strong) EAGLContext *context;
 @property (nonatomic, unsafe_unretained) CADisplayLink *__unsafe_unretained displayLink;

+@property (nonatomic, retain) IBOutlet UIButton *everyplayButton;
+@property (nonatomic, retain) IBOutlet UIButton *recordButton;
+@property (nonatomic, retain) IBOutlet UIButton *videoButton;
+
 - (BOOL)loadShaders;
 - (BOOL)compileShader:(GLuint *)shader type:(GLenum)type file:(NSString *)file;
 - (BOOL)linkProgram:(GLuint)prog;
 - (BOOL)validateProgram:(GLuint)prog;
 @end

 @implementation ERViewController
 @synthesize animating;
 @synthesize context;
 @synthesize displayLink;

+@synthesize everyplayButton;
+@synthesize recordButton;
+@synthesize videoButton;
+
 - (void)dealloc
 {
     if (program) {
         glDeleteProgram(program);
         program = 0;
     }

...
     [[NSString stringWithFormat:@"%@/loop.wav", [[NSBundle mainBundle] resourcePath]] getCString:buffer maxLength:200 encoding:NSASCIIStringEncoding];
     result = fmod_system->createSound(buffer, FMOD_SOFTWARE | FMOD_LOOP_NORMAL, NULL, &sound1);

     result = fmod_system->playSound(FMOD_CHANNEL_FREE, sound1, false, &channel);
 #endif

     // 뷰를 불러온 다음(일반적으로 nib에서) 추가 설정을 수행합니다.
+    [self createButtons];
 }

 - (void)viewDidUnload
 {
    [super viewDidUnload];

     if (program) {
...

 - (BOOL) shouldAutorotate {
     return YES;
 }

 #pragma mark -

+- (void)createButtons {
+    int buttonX = 10;
+    int buttonY = 10;
+    int buttonWidth = 200;
+    int buttonHeight = 40;
+    int padding = 8;
+
+    everyplayButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
+    everyplayButton.frame = CGRectMake(buttonX, buttonY, buttonWidth, buttonHeight);
+    [everyplayButton setTitle: @"Everyplay" forState: UIControlStateNormal];
+    [everyplayButton setTitleColor: [UIColor blackColor] forState: UIControlStateNormal];
+    [everyplayButton setTitleColor: [UIColor yellowColor] forState: UIControlStateHighlighted];
+
+    [everyplayButton addTarget:self action:@selector(everyplayButtonPressed:)
+              forControlEvents:UIControlEventTouchUpInside];
+    [self.view addSubview: everyplayButton];
+
+    buttonY = buttonY + buttonHeight + padding;
+    recordButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
+    recordButton.frame = CGRectMake(buttonX, buttonY, buttonWidth, buttonHeight);
+    [recordButton setTitle: @"Start Recording" forState: UIControlStateNormal];
+    [recordButton setTitleColor: [UIColor blackColor] forState: UIControlStateNormal];
+    [recordButton setTitleColor: [UIColor yellowColor] forState: UIControlStateHighlighted];
+
+    [recordButton addTarget:self action:@selector(recordButtonPressed:)
+           forControlEvents:UIControlEventTouchUpInside];
+    [self.view addSubview: recordButton];
+
+    buttonY = buttonY + buttonHeight + padding;
+    videoButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
+    videoButton.frame = CGRectMake(buttonX, buttonY, buttonWidth, buttonHeight);
+    [videoButton setTitle: @"Play Last Recording" forState: UIControlStateNormal];
+    [videoButton setTitleColor: [UIColor blackColor] forState: UIControlStateNormal];
+    [videoButton setTitleColor: [UIColor yellowColor] forState: UIControlStateHighlighted];
+    videoButton.hidden = YES;
+
+    [videoButton addTarget:self action:@selector(videoButtonPressed:)
+          forControlEvents:UIControlEventTouchUpInside];
+    [self.view addSubview: videoButton];
+}
+
+- (IBAction)everyplayButtonPressed:(id)sender {
+    [[Everyplay sharedInstance] showEveryplay];
+}
+
+- (IBAction)recordButtonPressed:(id)sender {
+    if ([[[Everyplay sharedInstance] capture] isRecording]) {
+        [[[Everyplay sharedInstance] capture] stopRecording];
+    } else {
+        [[[Everyplay sharedInstance] capture] startRecording];
+    }
+}
+
+- (IBAction)videoButtonPressed:(id)sender {
+    [[Everyplay sharedInstance] playLastRecording];
+}
+
+#pragma mark - Delegate Methods
+- (void)everyplayShown {
+    NSLog(@"everyplayShown");
+
+    [self stopAnimation];
+#if USE_AUDIO
+    channel->setMute(true);
+#endif
+}
+
+- (void)everyplayHidden {
+    NSLog(@"everyplayHidden");
+
+    [self startAnimation];
+#if USE_AUDIO
+    channel->setMute(false);
+#endif
+}
+
+- (void)everyplayRecordingStarted {
+    NSLog(@"everyplayRecordingStarted");
+
+    [recordButton setTitle:@"Stop Recording" forState:UIControlStateNormal];
+}
+
+- (void)everyplayRecordingStopped {
+    NSLog(@"everyplayRecordingStopped");
+
+    [recordButton setTitle:@"Start Recording" forState:UIControlStateNormal];
+    videoButton.hidden = NO;
+
+    [[Everyplay sharedInstance] mergeSessionDeveloperData:@{@"testString" : @"hello"}];
+    [[Everyplay sharedInstance] mergeSessionDeveloperData:@{@"testInteger" : @42}];
+}
+
 @end
  • EveryplayTutorial은 몇 가지 애드온이 추가된 완성 예제(EveryplayRecord)로도 학습할 수 있으며, 이 예제는 everyplay-ios-examples에서 다운로드할 수 있습니다.

Clone this wiki locally