在循环中运行延迟

4

首先,我只是一名航空公司飞行员,在酒店房间里闲暇时编写代码,所以请原谅我的无知。 我正在使用Obj. C和Cocos2D为iOS编写这个二十一点游戏。 我的问题如下: 当我点击“停止”时,我希望我的代码为庄家抽牌,直到它达到17点。我成功地做到了这一点,但是一旦我在循环中插入延迟,它就停止工作了。 我已经尝试了许多实现延迟的方法,例如:

[self performSelector:@selector(dealDealerCard:faceUp:) withObject:self afterDelay:2.0];

我的按钮会冻结,我尝试了使用CCSequence和运行动作。我还尝试了使用scheduler和NSTimer等。

以下是我的代码:

// if the stand button was pressed

-(void)standButtonPressed:(id)sender
{
 BJDrawnCard *holeCard = [dealerHand getFlippedCard];
 [holeCard flipCard];
 while ([dealerHand getTotal]<=17){
    [self performSelector:@selector(dealDealerCard:faceUp:)   withObject:self afterDelay:2.0];

}

以下是该方法:

// Deal Dealers Card

 -(void)dealDealerCard:(id) dummy faceUp:(BOOL)isFaceUp
{
drawnCard=[havila drawFromDeck];
if (isFaceUp) {
[drawnCard setDisplayFrame:[[CCSpriteFrameCache  sharedSpriteFrameCache]spriteFrameByName:drawnCard.imageFileName]];
}else [drawnCard setDisplayFrame:[[CCSpriteFrameCache   sharedSpriteFrameCache]spriteFrameByName:@"Backside.png"]];
[dealerHand getCard:drawnCard];
[drawnCard setScale:0.5f];
[drawnCard setPosition:[self shoePosition]];
[cardsheet addChild:drawnCard];
// animate the cards
float cardVelocity = (sqrtf((size.width*size.width)+(size.height*size.height)))/0.5; //     set the base speed for the movment

// calculate the time needed to move the card
CGPoint moveDifference = ccpSub([self dealerCardPosition],
                                [self shoePosition]);
float moveDuration = ccpLength(moveDifference) /
cardVelocity;

// define the movement
CCMoveTo *move = [CCMoveTo actionWithDuration:
                  moveDuration position:[self dealerCardPosition]];
CCDelayTime *delay = [CCDelayTime
                      actionWithDuration:0.5];

//Run the action

[drawnCard runAction:[CCSequence actions:move,delay,nil]];

numDealerHits++;

}

所以总结一下:我正在尝试运行一个条件循环,每次调用该方法之间间隔2.0秒,直到总数为17。

任何帮助将不胜感激。


1
通用规则:不要在 GUI 线程中编写延迟代码。你需要将操作分割成可以在它们之间带有延迟的片段。 - Hot Licks
我是......唯一不好的问题就是未曾提问。我很酷地发现你为了爱好而练习这门行业/艺术/科学。 - YvesLeBorg
谢谢大家的反馈,我尝试了下面提供的答案,但没有成功。我认为Hot Licks可能是正确的,我需要从主代码中分离GUI,也许这样就可以了。 - Benny Abramovici
3个回答

2
-(void)standButtonPressed:(id)sender
{
   BJDrawnCard *holeCard = [dealerHand getFlippedCard];
   [holeCard flipCard];
   if ([dealerHand getTotal]<=17){
      [self performSelector:@selector(dealDealerCard:faceUp:)   withObject:self afterDelay:2.0];

  }
}

在您的选择器的最后,确定是否还有更多内容,如果有更多,请...
 if ([dealerHand getTotal]<=17) {
     id more = [CCCallBlock actionWithBlock:^{
    [self performSelector:@selector(dealDealerCard:faceUp:)   withObject:self afterDelay:2.0];
}];
    [drawnCard runAction:[CCSequence actions:move,delay,more,nil]];
} else {
    [drawnCard runAction:[CCSequence actions:move,delay,nil]];
}

他已经尝试使用performSelector了,不管怎样我不想对此发表评论。我只想知道你是如何得到CCCallBlock类的? - Praveen S
我认为它出现在Cocos2d 2.0中。这不会阻塞用户界面。 - YvesLeBorg
1
很酷,我不知道它是来自框架的。我因为这个原因给你点赞。 - Praveen S
非常方便...您可以将其用于各种事情,例如在复杂序列中播放声音效果。 - YvesLeBorg
谢谢大家,所有提供的答案在仅调用一次时都有效。但是一旦我尝试在循环内运行它们,GUI就会冻结。 - Benny Abramovici
谢谢Yves,这让我更接近解决方案。虽然我不得不编写另一个方法,它几乎与大量重复代码相似,但基本上我学到的是我不能在循环内运行延迟。可能是cocos 2D或其他什么原因。再次感谢。 - Benny Abramovici

1
使用第一种解决方案会导致UI卡顿,因为您在GUI线程上延迟执行。您可以尝试使用GCD。
double delayInSeconds = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
    <#code to be executed on the main queue after delay#>
});

它是GCD,我有权编辑您的帖子+1。 - Praveen S
抱歉,我犯了错误。谢谢。 - Fabrizio Duroni
谢谢Fabrizio,你的代码对一次执行来说很好,但是我需要将它运行在一个条件循环中,这时我的按钮就会冻结。 - Benny Abramovici
你可以尝试为每个步骤添加一些延迟。例如,你可以从一个初始化为0的int变量开始,然后在每次循环中递增该变量,并将其用作调度的延迟时间。这样,你的操作将在相距2秒的距离上执行,而GUI不会冻结。 - Fabrizio Duroni
嗯,这个在循环内部不起作用,至少不能用来更新UI元素 - 所有内容都会一次性执行。而且如果我直接调用setNeedsLayout和setNeedsDisplay也不行。 - User

0
尝试一下这个方法,它不会冻结用户界面,并且会延迟启动。
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, 2.0 * NSEC_PER_SEC);
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){

      //do work
    });

谢谢waf,你的代码在执行一次后运行良好。我的问题是它需要作为循环的一部分运行,这时我的按钮会冻结,什么也不会发生。 - Benny Abramovici
嗯,这个在循环内部不起作用,至少不能用来更新UI元素 - 所有内容都会一次性执行。而且如果我直接调用setNeedsLayout和setNeedsDisplay也不行。 - User

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接