くまちゃんのiOS/Androidゲームプログラミング

マイペースでゲームつくってます。

「はしごゲーム」にジャンプ追加 【Xcode5, iOS7】

前回のサンプルに手を加え、ジャンプできるようにしました。
キャラの動きが「ロー○ランナー」から「ドラ○ンバスター」に変わったと言えばわかるでしょうか。「ロー○ランナー」でジャンプできたら、と思う場面がよくありませんでしたか?古いですか、すいません。ちなみにジャンプは「ドラ○ンバスター」と同じ「上入力」ですので。

例によって思ったより修正に手間取りました。前日の夜中3時までうんうんうなりました。思った移動を実現するためには結構、繊細な処理の順番変更が必要でした。詳細を説明する気力は残っていませんので、前回エントリとソースを見比べて「へー」と思ってください。
アルゴリズムの大きな違いは当たり判定の矩形を設定し、壁との衝突判定をそれで行ったことです。
(前回のサンプルでは生じる”めり込み”は無くなりました)
ただ一点、このソースだとひっつき虫のように壁にくっつく場合があります。直す気力は無いしクリティカルな支障もないのでそのままほっといてます;

LadderKumakoView.h

//
//  LadderKumakoView.h
//  LadderKumako
//
//  Created by 中道 忠和 on 2014/01/11.
//  Copyright (c) 2014年 中道 忠和. All rights reserved.
//

#import <UIKit/UIKit.h>

@interface LadderKumakoView : UIView {
    int WIDTH;
    int HEIGHT;
    CGContextRef context;
    NSMutableArray* draw;
    
    CGPoint lastPoint;
    
    float px; // prayer x position
    float py; // prayer y position
    float vx;
    float vy;
    float ay;
    int   ps; // prayer status
    int   jc; // jump counter
    
    struct pc { // prayer collision
        float left;
        float right;
        float top;
        float bottom;
    };
    
    struct pc pc;
    
    int MAP[15][10];
    float mx1,mx2,mx3,mx4,mx5,mx6,mx7,mx8;  // MAP x
    float my1,my2,my3,my4,my5,my6,my7,my8;  // MAP y
    
    struct friends {
        int pngid;
        float x;
        float y;
        float vx;
        float vy;
        int dir;
    };
    
    struct friends fre;
    
    int touch_direction; // 0:stop 1:up 2:right 3:down 4:left
}

@end

LadderKumakoView.m

//
//  LadderKumakoView.m
//  LadderKumako
//
//  Created by 中道 忠和 on 2014/01/11.
//  Copyright (c) 2014年 中道 忠和. All rights reserved.
//

#import "LadderKumakoView.h"

@implementation LadderKumakoView

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code
    }
    return self;
}

-(id)initWithCoder:(NSCoder*)coder {
    self=[super initWithCoder:coder];
    if (self) {
        CGRect bounds=[[UIScreen mainScreen] bounds];
        WIDTH = bounds.size.width;
        HEIGHT= bounds.size.height;
        // 画面の準備
        context=NULL;
        
        // キャラクターの絵を準備
        draw=[NSMutableArray array];                      //配列を設定
        
        [ draw addObject:[UIImage imageNamed:@"browntile.png"]];//0
        [ draw addObject:[UIImage imageNamed:@"ladder.png"]];   //1
        [ draw addObject:[UIImage imageNamed:@"kumako01.png"]]; //2
        
        // タイマーの準備
        [NSTimer scheduledTimerWithTimeInterval:0.01f target:self selector:@selector(onTick:) userInfo:nil repeats:YES];
        
        // くまちゃんの準備
        px=0.0f;   //最初のX座標
        py=0.0f; //最初のY座標
        vx=0.0f;   //最初のX座標の移動量
        vy=1.0f;  //最初のY座標の移動量
        ay=0.1f;
        // くまちゃんの当たり判定の準備
        pc.right=px+8.0f;
        pc.top=py;
        pc.left=px+24.0f;;
        pc.bottom=py+31.0f;
        
        // ステージ
        int map[15][10]={
            0,0,0,0,0,0,0,0,0,0,
            0,2,1,1,1,1,1,1,1,2,
            0,2,0,0,0,0,0,0,0,2,
            0,2,0,0,0,0,0,0,0,2,
            0,1,1,1,1,0,0,0,0,2,
            0,0,0,0,0,0,0,0,0,2,
            1,0,0,0,0,2,1,1,1,1,
            1,1,0,0,0,2,0,0,0,0,
            1,1,1,2,0,2,0,0,0,0,
            0,0,0,2,0,1,1,1,2,1,
            0,0,0,2,0,0,0,0,2,0,
            0,2,1,1,1,0,0,0,2,0,
            0,2,0,0,0,0,2,1,1,1,
            0,2,0,0,0,0,2,0,0,0,
            1,1,1,1,1,1,1,1,1,1
        };
        
        // グローバル変数に読み込ませる
        for (int j=0;j<15;j++) {
            for (int i=0;i<10;i++) {
                MAP[j][i]=map[j][i];
            }
        }
        
        // その他変数 初期設定
        touch_direction=3;
        ps=0; // 0:normal 1:jumping
        jc=0; // 0:normal 1:jumping
        
    }
    return self;
}

- (void)drawRect:(CGRect)rect
{
    // Drawing code
    // 画面の準備(まあ、呪文のようなものです。アブダカダブラ。)
    if (context!=NULL) {
        CGContextRelease(context);
        context=NULL;
    }
    context=UIGraphicsGetCurrentContext();
    CGContextRetain(context);
    
    // 画面を真っ黒に塗って、全部消す。(それからあとで背景とキャラクターを高速で描くから、アニメーションに見える)
    CGContextSetRGBFillColor(context,0,0,0,1);
    CGContextSetRGBStrokeColor(context,0,0,0,1);
    CGContextFillRect(context,CGRectMake(0,0,WIDTH,HEIGHT));
    
    // iPhone5以降の時は、画面の下にバナーを出すようにする。(もともとiphone4Sの画面サイズでつくってたから、すきまができてしまう)
    if ([[UIScreen mainScreen] bounds].size.height == 568){
        //[[p_bmp objectAtIndex:5] drawAtPoint:(CGPointMake(0,455))];    //バナーを描画
    }
    
    // ステージの描画
    for (int j=0;j<15; j++) {
        for (int i=0; i<10; i++) {
            int mapid=MAP[j][i];
            if (mapid==1) [[draw objectAtIndex:0] drawAtPoint:(CGPointMake(i*32.0f,j*32.0f))];
            if (mapid==2) [[draw objectAtIndex:1] drawAtPoint:(CGPointMake(i*32.0f,j*32.0f))];
        }
    }
    
    // TEXT
    NSString* s1=[NSString stringWithFormat:@"px=%f, py=%f", px,py];
    NSString* s2=[NSString stringWithFormat:@"left =%f, top   =%f", pc.left ,pc.top];
    NSString* s3=[NSString stringWithFormat:@"right=%f, bottom=%f", pc.right,pc.bottom];
    NSString* s4=[NSString stringWithFormat:@"MAP[top   ][left ]=%d", MAP[(int)my1][(int)mx1]];
    NSString* s5=[NSString stringWithFormat:@"MAP[bottom][right]=%d", MAP[(int)my2][(int)mx2]];
    [[UIColor whiteColor] set];
    UIFont* font = [UIFont boldSystemFontOfSize:10];
    [s1 drawAtPoint:CGPointMake(0,480) withFont:font];
    [s2 drawAtPoint:CGPointMake(0,490) withFont:font];
    [s3 drawAtPoint:CGPointMake(0,500) withFont:font];
    [s4 drawAtPoint:CGPointMake(180,490) withFont:font];
    [s5 drawAtPoint:CGPointMake(180,500) withFont:font];
    
    // くまちゃんの描画
    [[draw objectAtIndex:2] drawAtPoint:(CGPointMake(px,py))];
    
    // くまちゃん移動処理
    if (touch_direction==0) {vx= 0.0f; vy=0.0f;}
    
    //まずそもそも足下に地面はあるんか
    mx7 =(px+8.0f)/32.0f;  //左下x座標+8pixel:くまちゃんのグラフィックにあわせて少し幅を狭める
    my7 =(py+32.0f)/32.0f; //左下y座標+1:足下をみるために+1pixel
    mx8 =(px+24.0f)/32.0f; //右下x座標+24pixel
    my8 =(py+32.0f)/32.0f; //右下y座標+1
    //左下+1と右下+1ともに空間か判定
    if (MAP[(int)my7][(int)mx7]==0 && MAP[(int)my8][(int)mx8]==0) {//足下が空間だから左右移動させず、落下
        vy=vy+ay;
        if (vy>3.0f) vy=3.0f; //落下速度の最高速度
    } else if (touch_direction==1) { //上
        //はしごあるのか
        mx5=(px+8.0f)/32.0f;
        my5=(py+31.0f)/32.0f;
        mx6=(px+24.0f)/32.0f;
        my6=(py+31.0f)/32.0f;
        if (MAP[(int)my5][(int)mx5]==2 || MAP[(int)my6][(int)mx6]==2) {
            vy=-1.0f; vx=0.0f;
        } else { //はしごがないからジャンプする
            vy=-4.0f;
        }
    } else if (touch_direction==3) { //下
        //はしごあるのか
        mx5=(px+8.0f)/32.0f;
        my5=(py+32.0f)/32.0f;
        mx6=(px+24.0f)/32.0f;
        my6=(py+32.0f)/32.0f;
        if (MAP[(int)my5][(int)mx5]==2 || MAP[(int)my6][(int)mx6]==2) {
            vy=1.0f; vx=0.0f;
        } else {
            vy=0.0f; vx=0.0f;
        }
    }
    //右
    if (touch_direction==2) vx= 1.0f;
    //左
    if (touch_direction==4) vx=-1.0f;

    // まず当たり判定を移動させる
    pc.left=px+8.0f+vx;
    pc.top=py+vy;
    pc.right=px+24.0f+vx;
    pc.bottom=py+31.0f+vy;
    // 当たり判定用マップ座標を計算
    mx1=pc.left/32.0f;   if (mx1<0) mx1=0; //配列添字オーバー防止
    my1=pc.top/32.0f;    if (my1<0) my1=0;
    mx2=pc.right/32.0f;  if (mx2>9) mx2=9;
    my2=pc.bottom/32.0f; if (my2>14) my2=14;
    // カベ判定
    if (MAP[(int)my1][(int)mx1]==1 || MAP[(int)my1][(int)mx2]==1 || MAP[(int)my2][(int)mx2]==1 || MAP[(int)my2][(int)mx1]==1) { //カベがあるので当たり判定をリセット
        pc.left=px+8.0f-vx;
        pc.top=py-vy;
        pc.right=px+24.0f-vx;
        pc.bottom=py+31.0f-vy;
        vx=0.0f;
        vy=0.0f;
    }
    // 画像を移動
    px=px+vx;
    if (px>288) px=288;
    if (px<0)   px=0;
    py=py+vy;
    if (py>448) py=448;
    if (py<-32) py=-32;
}

// タイマー
-(void)onTick:(NSTimer*)timer {
    [self setNeedsDisplay];
}

// タッチパネル操作
-(void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event {
    lastPoint = [[touches anyObject] locationInView:self.superview];
}

-(void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event {
    // タッチした座標を取得
    CGPoint p=[[touches anyObject] locationInView:self.superview];
    
    // タッチ操作量はX方向とY方向のどちらが多いか絶対値で比較する(lastPointとpの座標を取得する構造がゆえに)
    float temp_vx, temp_vy;
    temp_vx=abs(lastPoint.x-p.x);
    temp_vy=abs(lastPoint.y-p.y);
    
    if (temp_vx > temp_vy) {
        if (lastPoint.x-p.x<0) {
            touch_direction=2;
        } else {
            touch_direction=4;
        }
    } else {
        if (lastPoint.y-p.y<0) {
            touch_direction=3;
        } else {
            touch_direction=1;
        }
    }
    
    // さっき取得したタッチの座標をラストポイントに書き換えておく。
    lastPoint = p;
}

-(void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event {
    //touch_direction=0;
}

-(void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event {
    [self touchesEnded:touches withEvent:event];
}

@end

https://itunes.apple.com/jp/app/kumachanjanpu2/id789138227?mt=8&uo=4&at=10l8JW&ct=hatenablog