読者です 読者をやめる 読者になる 読者になる

夏までにiPhone アプリつくってみっか!

趣味でiPhone/Androidアプリを開発し、日々勉強した事を書いています。オープンワールド系レースゲームをUnityで開発中です。

【cocos2d】あえて同時発声を制限してキレのいいSEを鳴らす

cocos2d スペースハリアー TravelShooting JP

昔のゲーム機やパソコンのハードウェアのスペック表には同時発声数: FM音源8音、PSG3音などと必ず記載されていました。
いつしかPCM音源が標準になり、ソフト的に何音でも重ねられようになり、好きなだけジャンジャン音を鳴らせるようになりました。

しかし、鳴らせるだけ鳴らせばいいという単純なものではなく、場合によってはあえて制限する事でよりキレの良いSEを鳴らすことができます。
少ない発声数でヤリクリしていた昔の人には申し訳ないですが、偽スペハリにそういう機能をつけてみました。
本物のスペースハリアーの基板の音源も同時発声数に制限のある時代の物なので、そうした方がより本物らしくなります。

比較動画を作りましたので見てください。

前半は普通にcocos denshionのplayEffectで鳴らしたもので、複数のSEが重なって鳴っています。そのため、風呂場のエコーのように聞こえたりしています。
後半はSEの同時発声数に制限を掛けて同時に1音しか鳴らないようにしたものです。プリプリプリとキレのある発射音でドラゴンが炎を吐いているのがわかると思います。

単純にSEを出す前にプレイ中のSEを止めているだけなのですが、各ノードがバラバラに制御していてはあまり効果がないので、シングルトンのクラスを作って集中管理するようにしています。
また、重なってはいけない音と重なってもいい音を任意に制御できるようにしました。例えば"ゲットレディ"という声と発射音は同時に発声できます。

シングルトンインスタンスをcocos2dのノードツリーに入れる必要はないのでNSObjectを継承しています。

実装はこうなっています。

@implementation SEManager {
    NSMutableDictionary* _IDTable;
}

+ (SEManager *)sharedManager
{
    static SEManager* sharedInstance = nil;
    if (!sharedInstance) {
        sharedInstance = [[self alloc] init];
    }
    return sharedInstance;
}

- (id)init
{
    if ((self = [super init])) {
        _IDTable = [NSMutableDictionary dictionary];
    }
    return self;
}

- (void)playSE:(NSString *)filename keyword:(NSString *)keyword
{
    id IDObject = [_IDTable objectForKey:keyword];
    if (IDObject) {
        [[SimpleAudioEngine sharedEngine] stopEffect:[IDObject integerValue]];
    }
    ALuint EffectID = [[SimpleAudioEngine sharedEngine] playEffect:filename];
    [_IDTable setObject:[NSNumber numberWithInt:EffectID] forKey:keyword];
}

- (void)playSE:(NSString *)filename keyword:(NSString *)keyword pitch:(Float32)pitch pan:(Float32)pan gain:(Float32)gain
{
    id IDObject = [_IDTable objectForKey:keyword];
    if (IDObject) {
        [[SimpleAudioEngine sharedEngine] stopEffect:[IDObject integerValue]];
    }
    ALuint EffectID = [[SimpleAudioEngine sharedEngine] playEffect:filename pitch:pitch pan:pan gain:gain];
    [_IDTable setObject:[NSNumber numberWithInt:EffectID] forKey:keyword];
}

@end

これを使う側ではキーワードを適当に設定します。この場合、"EnemyShot"という同じキーワードで発声している音がある場合はそれを停止してから新しい音を鳴らすので音が重なりません。
例えば"PlayerShot"という別のキーワードで発声している音がある場合は重なって発声されます。

        [[SEManager sharedManager] playSE:@"fire.aiff" keyword:@"EnemyShot"];

これを使う事でキレが良く、かつ必要な音は消さないように自在にSE発声をコントロールできると思います。

追記:全てのエフェクトをピタッと止める機能を追加しました。最新のソースはこちらの記事をご参照ください。

【cocos2d】全てのSEをピタッと止める - 夏までにiPhone アプリつくってみっか!