多线程在塔防游戏中寻路

时间:2010-10-26 14:14:27

标签: iphone objective-c multithreading cocos2d-iphone

我正在进行塔防游戏,而且我坚持寻路的多线程。如果我不进行NSInvocationOperation的寻路操作,那么在渲染中会有越来越多的停顿,我有更多的恶魔和塔。

但是我无法解决“锁定”问题。关于枚举我的恶魔NSMutableArray,我会经常发生崩溃。如果我在完成寻路时尝试锁定主线程中的枚举,游戏仍会锁定,我已经失去了多线程的全部内容..

我如何解决这类问题?

4 个答案:

答案 0 :(得分:3)

想想放置另一座塔时会发生什么。敌人要么在他们的路径决定中要么考虑新塔,要么不考虑。确定这些新路径需要时间,无论您选择何种多线程解决方案,都会导致延迟。问题最终在于你的寻路算法。如果计算时间太长,那么你会看到延迟。

看看你可以避免找到这些路径的方法。在位置(x,y)下降塔会影响所有敌人吗?你能算出一些路径,敌人会选择跟随哪一条路径吗?您是否可以缓存路径并重复使用已经计算过的部分(例如河流如何通过分支支流流动)?

我认为有很多方法可以减少问题中路径查找的影响。

答案 1 :(得分:1)

我同意Tim Rupe的观点,即优化寻路可能会有所帮助。

另一种(附加)方法是将“当前”路径数组与“下一个”路径数组分开。因此,游戏继续使用当前的路径数组,而下一个数组则在另一个线程中计算。这可以避免死锁,崩溃等等。

然后,在某个明确定义的点上,您将'next'替换为'current'(由互斥锁或其他任何东西保护)。基本上,您总是处理快照 - 但您可以控制快照交换的速率(前提是您的寻路算法不会太慢)。

答案 2 :(得分:1)

线程肯定有助于延迟策略,例如在路径查找完成之前开始向总体方向移动。

编辑:我错过了你说你的恶魔阵列造成了问题。我认为你最好的选择是松耦合。尽量不要让路径查找器依赖于你的恶魔阵列。只发送一个恶魔所需数据的副本(起始位置,目标位置等)并获得一系列积分。不要让它访问整个数组,甚至不能引用任何恶魔对象。

答案 3 :(得分:1)

跨线程边界发送可变数据是解决问题的好方法。在将对象发送到另一个线程时,应始终使用NSArrayNSStringNSSSet等代替其可变子类。这个简单的规则使多线程变得更加愉快。

值得庆幸的是,所有集合类都实现了NSCopying协议,因此有一个漂亮的-[copy]方法返回s本身的不可变副本。

接下来你需要决定的是如果改变原始数组该怎么做。你能不能:

  1. 只是将新的探路者操作排队?
  2. 放弃当前的路径查找器操作,然后启动新的?
  3. 选项1.只添加一个新操作是最简单的,但如果您的原始数组经常变异,可能会浪费时间。 选项2.需要您做更多的工作。更具体地说:

    1. 您必须保留最后一个操作,以便向其发送-[NSOperation cancel]消息。或者,如果您有一个仅限于路径查找的队列,则可以使用[NSOperationQueue cancelAllOperations]取消对其进行的所有操作。
    2. 如果被取消,你应该尽早从你的行动中拯救。这需要您继承NSOperation,您不能再按原样使用NSInvocationOperation
    3. 您的NSOperation子类实现应该如下所示:

      @implementation CWPathfinderOperation
      
      -(id)initWithFiends:(NSArray*)fiends delegate:(id<CWPathfinderOperation>)delegate {
          self = [super init];
          if (self) {
              self.fiends = fiends;
              self.delegate = delegate;
          }
          return self;
      }
      
      -(void)main {
          NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
          while (notDone) {
              if ([self isCancelled]) goto bailOut;
              // Do smallpart of work
          }
          [self.delegate performSelectorOnMainThread:@selector(pathfinderOperatioDidFindPaths:)
                                          withObject:result
                                       waitUntilDone:NO];
      bailOut:
          [pool release];
      }
      @end