macOSでOpenGLプログラミング（1-4. ゲーム終了時の処理を行う）

[macOSでOpenGLプログラミングの目次に戻る](http://qiita.com/sazameki/items/d060f20f621c658046d9)

# はじめに

[前回](http://qiita.com/sazameki/items/ff6e2c588a5ac785ad83)は、ディスプレイ・リンクを使ってゲーム更新用のループを作成しました。しかしまだ明示的にディスプレイ・リンクを停止する処理を書いていなかったので、今回はゲーム終了時の処理について解説し、ディスプレイ・リンクのクリーンアップ処理も追加しましょう。


# 1. AppDelegate.mを編集する

アプリケーション終了時には、AppDelegateクラスの`applicationShouldTerminate:`メソッドがコールバックされて、本当にアプリケーションを終了して良いかどうかを尋ねてきます。今回のように、ディスプレイ・リンクの処理を停止させたり、ゲーム実行中に作成したテクスチャなどのメモリを解放したりといった処理を行いたい場合には、このメソッドをオーバーライドして、必要なクリーンアップの処理を行ったあと、`NSTerminateNow`をリターンして「今すぐ終了していいですよ」と返事を返します。

ここでは、MyGLViewクラスのオブジェクトにアクセスするための`sharedInstance`メソッドを使って、ディスプレイ・リンクを停止させる`stopDisplayLink`メソッドを呼び出しています。これらのMyGLViewクラスのメソッドについては、後述しています。

なお、MyGLViewクラスにアクセスするために、先頭にMyGLView.hファイルのimport文を追加していますので、ここも忘れないようにしてください。また、最初からAppDelegateクラスに用意されているメソッドは、今後のゲーム開発ではあまり使わなさそうなので、削除しています。

```objc:AppDelegate.m
#import "AppDelegate.h"
#import "MyGLView.h"

@interface AppDelegate ()

@end

@implementation AppDelegate

- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication*)sender
{
    [[MyGLView sharedInstance] stopDisplayLink];
    return NSTerminateNow;
}

@end
```

# 2. WindowController.mを編集する

テンプレートからアプリケーションを作成した時点でのデフォルトの動作としては、ウィンドウが閉じられても、アプリケーション自体は実行が続いている状態になっています。そこでウィンドウのデリゲートであるWindowControllerクラスに処理を追加して、ウィンドウが閉じられたらアプリケーションも終了するようにコードを書きます。

次のような`windowWillClose:`メソッドを、WindowController.mファイルの中に追加します。「NSApp」というのは、アプリケーション全体の操作を行うためのNSApplicationクラスのインスタンスにアクセスするために用意されているグローバル変数です。この変数に対して`terminate:`メソッドを呼び出すことで、アプリケーションの終了がリクエストされ、結果として、1の手順で追加した`applicationShouldTerminate:`メソッドが呼ばれます。

```objc:WindowController.m（一部）
@implementation WindowController

...

- (void)windowWillClose:(NSNotification *)notification
{
    [NSApp terminate:self];
}

...

@end
```


# 3. MyGLViewクラスにメソッドを追加する

まずMyGLView.hを編集して、次のように2つのメソッドを宣言します。MyGLViewクラスのインスタンスにアクセスするための`sharedInstance`メソッド(static)と、ディスプレイ・リンクの停止処理を行うための`stopDisplayLink`メソッドです。

```objc:MyGLView.h
#import <Cocoa/Cocoa.h>

@interface MyGLView : NSOpenGLView

+ (MyGLView *)sharedInstance;
- (void)stopDisplayLink;

@end
```

次にMyGLView.mを編集します。staticな`instance`変数を追加して、MyGLViewクラスのインスタンスを保持して、必要に応じて`sharedInstance`メソッドでリターンできるようにします。`instance`変数は、`initWithFrame:`メソッドで処理化が終わった時点で `instance = self;` というコードで初期化しています。

そして`stopDisplayLink`メソッドでは、ディスプレイ・リンクの実行をストップさせる`CVDisplayLinkStop()`関数を呼び出してから、ディスプレイ・リンクを解放する`CVDisplayRelease()`関数を呼び出します。Core Videoフレームワークのディスプレイ・リンクは、ARCでは管理されませんので、`CVDisplayLinkRelease()`関数を使って明示的に解放してやる必要があります。

```objc:MyGLView.m
#import "MyGLView.h"

#import <OpenGL/OpenGL.h>
#import <OpenGL/gl3.h>

static MyGLView *instance = nil;

@implementation MyGLView {
    NSOpenGLContext     *glContext;
    CVDisplayLinkRef    displayLink;
    float               value;
    bool                hasDisplayLinkStopped;
}

+ (MyGLView *)sharedInstance
{
    return instance;
}

...

- (instancetype)initWithFrame:(NSRect)frame
{
    ...

    self = [super initWithFrame:frame pixelFormat:pixelFormat];
    if (self) {
        instance = self;
        [self setWantsBestResolutionOpenGLSurface:YES];
    }
    return self;
}

...

- (void)stopDisplayLink
{
    CVDisplayLinkStop(displayLink);
    CVDisplayLinkRelease(displayLink);
}

@end
```

# 5. おわりに

今回は、ウィンドウが閉じられたタイミングでの処理も含めて、アプリケーションの終了時の処理を追加しました。これでディスプレイ・リンクを停止させて解放することができましたので、安心してゲームの開発に専念できます。

次からはいよいよOpenGLでの描画処理の実装に入っていきましょう。

ここまでのプロジェクト：[MyGLGame_step1-4.zip](http://sazameki.jp/temp/MyGLGame_step1-4.zip)

---
次の記事：[macOSでOpenGLプログラミング（1-5. ゲーム処理を書くためのC++クラスを作る）](http://qiita.com/sazameki/items/3d39211cc102a0c6b815)