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

なかじまの開発ブログ

アプリ開発における備忘録などなど。

MENU

スプライトの点滅永久アニメーション【cocos2d-x】

『cpp』

// 点滅させるスプライトの用意
auto sprite = Sprite::create("ファイル名");
sprite->setPosition(Vec2(100, 100));
this->addChild(sprite);

// フェードイン/フェードアウトのアニメーションのSequenceを作成
// アニメーションの長さを変えたい場合は()の数字を変えてください。
auto act_sprite = Sequence::create(FadeOut::create(1), FadeIn::create(1), NULL);
// 永久アニメーションを実行
sprite->runAction(RepeatForever::create(act_sprite));

こちらもどうぞ

nsdevelop.hatenablog.com

UITableViewのスクロールをボタンで制御する【Objective-C】

f:id:s_nakajima:20160530122722p:plain

Objective-Cが終わりつつあると感じているなかじまです(´・ω・`)

今回はUITableViewのスクロールをUIButtonで制御しつつ、セルの表示に合わせてボタンの表示非表示も一緒にメモします!

storyboardのUIViewにUITableViewとUIButtonを2つ設置します。

f:id:s_nakajima:20160530122155p:plain

設置したUIをOutlet接続し、ボタンはAction接続もします。

『.h』

@interface ViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>

@property (weak, nonatomic) IBOutlet UITableVIew *_tableView;
@property (weak, nonatomic) IBOutlet UIButton *upBtn;
@property (weak, nonatomic) IBOutlet UIButton *downBtn;

- (IBAction)upBtnTap:(id)sender;
- (IBAction)downBtnTap:(id)sender;

セルの表示処理部分は省きます。

『.m』

@synthesize _tableView;
@synthesize upBtn;
@synthesize downBtn;

- (void)viewDidLoad {
    [super viewDidLoad];

    _tableView.delegate = self;
    _tableView.dataSource = self;

    // tableViewのスクロール検知を実行しておく
  [self scrollViewDidScroll:_tableView];
}

/* スクロール終了検知 */
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    // セルが表示範囲を超えてない場合
    if (_tableView.contentOffset.y < _tableView.frame.size.height) {
        // 上ボタンを非表示
        upBtn.hidden = YES;
        // 下ボタンを非表示
        downBtn.hidden = YES;
    }
    // 一番下までスクロールしている場合
    else if (_tableView.contentOffset.y >= _tableView.contentSize.height - _tableView.frame.size.height) {
        // 上ボタンを表示
        upBtn.hidden = NO;
        // 下ボタンを非表示
        downBtn.hidden = YES;
    }
    // 一番上までスクロールしている場合
    else if (_tableView.contentOffset.y == 0) {
        // 上ボタンを非表示
        upBtn.hidden = YES;
        // 下ボタンを表示
        downBtn.hidden = NO;
    }
    // それ以外
    else {
        // 上ボタンを表示
        upBtn.hidden = NO;
        // 下ボタンを表示
        downBtn.hidden = NO;
    }
}


/* 上ボタン押下処理 */
- (IBAction)upBtnTap:(id)sender {
    NSIndexPath *indexPath = [_tableView indexPathForRowAtPoint:CGPointMake(0, _tableView.contentOffset.y + 1)];
    if (indexPath && indexPath.row > 0) {
        indexPath = [NSIndexPath indexPathForRow:indexPath.row - 1 inSection:0];
        [_tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animation:YES];
    }
}

/* 下ボタン押下処理 */
- (IBAction)downBtnTap:(id)sender {
    NSIndexPath *indexPath = [_tableView indexPathForRowAtPoint:CGPointMake(0, _tableView.contentOffset.y + 1)];
    if (indexPath) {
        indexPath = [NSIndexPath indexPathForRow:indexPath.row + 1 inSection:0];
        [_tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animation:YES];
    }
}

画面起動時にTextFieldを選択された状態にする【Objective-C】

Outlet接続などでUITextFieldを用意。

『.h』

@property (weak, nonatomic) IBOutlet UITextField *textField;


起動時に実行されるメソッド内に下記を入力

// textFieldを選択状態にする
[textField becomeFirstResponder];

『.m』

@synthesize textField;

// インスタンス化された直後に実行(初回に一度だけ)する場合
- (void)viewDidLoad {
    [super viewDidLoad];

    // textFieldを選択状態にする
    [textField becomeFirstResponder];
}
@synthesize textField;

// 画面が表示される直前に実行する場合
- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];

    // textFieldを選択状態にする
    [textField becomeFirstResponder];
}
@synthesize textField;

// 画面が表示された直後に実行する場合
- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];

    // textFieldを選択状態にする
    [textField becomeFirstResponder];
}


ちなみに選択を解除したい場合は

[self.view endEditing:YES];

指定した秒数後にメソッドを実行する【Objective-C】

3秒遅らせてメソッドを実行させたい場合、次のようにします。

- (void)ahan {
  NSLog("あ");
}

- (void)viewDidLoad {
  [super viewDidLoad];

  // 3秒後にahanメソッドを実行
  [self performSelector:@selector(ahan) withObject:nil afterDelay:3.0];
}


これだけです!

これで3秒後ログに「あ」と表示されます。

for文で複数のrunActionを実行しつつ最後のアクション終了を取得する方法【cocos2d-x】

タイトルって難しいですね。

何が言いたいかっていいますと、

複数のスプライトのアニメーションの移動距離が違うと、アニメーションの終了する順番がカウンタに沿ってではなく、バラバラになります。

つまり、for文のカウンタ最大値(?)で最後の処理を実行しようとしても、ズレのせいでうまく実行できない。。。

f:id:s_nakajima:20160421102753p:plain

for (int i = 0; i != SpriteVector.size(); i++) {
  // 移動
  SpriteVector.at(i)->runAction(Sequence::create(MoveTo::create(/*省略*/), CallFunc::create([=](){
    log("%d移動完了", i);
    if (i == SpriteVector.size()-1) log("最後のスプライト移動完了");
  }), NULL));
}

上の図と処理の場合、結果として

2移動完了
最後のスプライト移動完了
1移動完了
0移動完了


てなかんじになるわけですよ!!!(*´Д`)もー。


そして考えた結果、
スプライトの入ったVectorの要素数を1から足した合計値と、
for文で順番に足していった値を比較して、同じになったら最後の処理になるじゃないか!!

ということで↓

『.h』

Vector<Sprite *> SpriteVector;
int vectorsize;
int cnt;

bool countNum(int num);


『cpp』

// SpriteVectorの要素数を1から足した合計値を取得
int getTotal(int num) {
  int sum = 0;
  for (int i = 1; i != num + 1; i++) {
    sum += i;
  }

  return sum;
}


// 合計値とcntが一緒かチェック
bool SampleScene::countNum(int num) {
  cnt += (num + 1);

  int (cnt == getTotal(vectorsize)) return true;

  return false;
}


使い方

for (int i = 0; i != SpriteVector.size(); i++) {
  // 移動
  SpriteVector.at(i)->runAction(Sequence::create(MoveTo::create(/*省略*/), CallFunc::create([=](){
    log("%d移動完了", i);
    if (countNum(i)) log("最後のスプライト移動完了");
  }), NULL));
}


どんなけバラバラでも、要素数を1から足した合計値は変わらないので、すべてを足し終わったときが最後の処理となるわけですね。

Spriteを格納したVectorをソートする【cocos2d-x】

y座標をもとにしてソートしてみました!

『.h』

typedef Vector<Sprite *> SpriteVector;

格納部分は省略します。

『.cpp』

// 昇順(y座標がちっちゃい順)
SpriteVector sortup(SpriteVector v) {
  for (int i = 0; i < (int)v.size()-1; i++) {
     for (int j = 0; j < (int)v.size()-1; j++) {
       if (v.at(j)->getPosition().y < v.at(j-i)->getPosition().y) {
         auto b1 = v.at(j);
         auto b2 = v.at(j-1);
         v.replace(j, b2);
         v.replace(j-1, b1);
       }
     }
  }

  return v;
}

// 降順(y座標がおっきい順)
SpriteVector sortdown(SpriteVector v) {
  for (int i = 0; i < (int)v.size()-1; i++) {
     for (int j = 0; j < (int)v.size()-1; j++) {
       if (v.at(j)->getPosition().y > v.at(j-i)->getPosition().y) {
         auto b1 = v.at(j);
         auto b2 = v.at(j-1);
         v.replace(j, b2);
         v.replace(j-1, b1);
       }
     }
  }

  return v;
}

こちらもどうぞ

nsdevelop.hatenablog.com

1つの配列のソートに合わせて、もう1つの配列の順番も変える【Objective-C】

2つの配列を用意。

NSArray *array1 = [NSArray arrayWithObjects:@"さしすせそ", @"かきくけこ", @"あいうえお", @"なにぬねの", @"たちつてと", nil];
NSArray *array2 = [NSArray arrayWithObjects:@"3行目", @"2行目", @"1行目", @"5行目", @"4行目", nil];

現在の順番

さしすせそ 3行目
かきくけこ 2行目
あいうえお 1行目
なにぬねの 5行目
たちつてと 4行目

そしてこの2つの配列を、ソートする方をキーとして『NSMutableDictionary』に格納します。
ここではarray2をソースします。

NSMutableDictionary *dic = [NSMutableDictionary dictionary];
for (int i = 0; i != array2.count; i++) {
  [dic setObject:[array1 objectAtIndex:i] forKey:[array2 objectAtIndex:i]];
}

array2を昇順にソートします。
配列の要素が文字列で、数字を数値として比較したい場合はオプションとして『NSNumericSearch』を使います。

array2 = [array2 sortedArrayUsingComparator:^NSComparisonResult(id  _Nonnull obj1, id  _Nonnull obj2) {
  NSStringCompareOptions options = (NSNumericSearch);
  return [obj1 compare:obj2 options:options]; // 降順にしたい場合はobj1とobj2を入れ替えてください。
}];

そして先ほど登録したNSMutableDictionaryをarray2で取り出し、別の配列に格納することによって順番を変えることができます。

NSMutableArray *array3 = [[NSMutableArray alloc] init];
for (int i = 0; i != array2.count; i++) {
  [array3 addObject:[dic objectForKey:[array2 objectAtIndex:i]]];
}

入れ替え後

あいうえお 1行目
かきくけこ 2行目
さしすせそ 3行目
たちつてと 4行目
なにぬねの 5行目

配列の要素が数値の場合は、

NSArray *array1 = [NSArray arrayWithObjects:@"さしすせそ", @"かきくけこ", @"あいうえお", @"なにぬねの", @"たちつてと", nil];
NSArray *array2 = [NSArray arrayWithObjects:@3, @2, @1, @5, @4, nil];

NSMutableDictionary *dic = [NSMutableDictionary dictionary];
for (int i = 0; i != array2.count; i++) {
  [dic setObject:[array1 objectAtIndex:i] forKey:[array2 objectAtIndex:i]];
}

NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:nil ascending:YES]; // 降順にしたい場合はNO
array2 = [array2 sortedArrayUsingDescriptors:@[sort]];

NSMutableArray *array3 = [[NSMutableArray alloc] init];
for (int i = 0; i != array2.count; i++) {
  [array3 addObject:[dic objectForKey:[array2 objectAtIndex:i]]];
}