使用long hold& amp的问题在游戏中单击一下

时间:2017-07-17 13:31:33

标签: objective-c sprite-kit touch game-physics

我用两种移动方法制作游戏。当点击屏幕时,玩家向上移动。游戏世界也朝向玩家看起来像鸟儿飞向前方。当用户按住屏幕上的时候,玩家将开始向前飞行。当玩家根本没有触摸屏幕时,玩家会摔倒(几乎像飞鸟一样)。

游戏工作得非常顺利,直到我添加了长按方法。因为一秒太短,它会被检测为水龙头。因此,当我快速点击屏幕2次时,长按方法也会被调用。所以我认为必须有另一种方式,而不是使用长按手势?

有没有办法以编程方式检测内部触摸并触摸并保持spritekit?我知道你可以在按钮上的UIKit单视图应用程序中执行此操作。

但我需要让它在屏幕上的任何地方都能正常工作,而不仅仅是点击按钮。

我使用Spritekit作为游戏技术,Xcode作为平台。

有什么想法吗?

2 个答案:

答案 0 :(得分:2)

这是一个完整的解决方案。点击跳转块,按住屏幕向前移动。当你触摸墙壁时,你将重置位置。

Swift3:

class Bird: SKSpriteNode {

  private let flyUpImpulse = CGVector(dx: 0, dy: 20)
  private let flyForwardForce = CGVector(dx: 100, dy: 0)

  // var pb: SKPhysicsBody { return self.physicsBody! }

  func initialize() {
    color = .blue
    size = CGSize(width: 50, height: 50)
    physicsBody = SKPhysicsBody(rectangleOf: size)
    physicsBody!.categoryBitMask = UInt32(1)
  }

  func flyUp() {
    physicsBody!.applyImpulse(flyUpImpulse)
  }

  func flyForward() {
    physicsBody!.applyForce(flyForwardForce)
  }

  func resetPosition() {
    guard let scene = self.scene else {
      print("reset position failed: bird has not been added to scene yet!")
      return
    }

    run(.move(to: CGPoint(x: scene.frame.minX + 50, y: scene.frame.midY), duration: 0))
    physicsBody!.velocity = CGVector.zero
  }
}

class GameScene: SKScene, SKPhysicsContactDelegate {

  enum ControlToDo { case tap, longPress, none }

  var controlToDo = ControlToDo.none

  // How many frames we have to release screen in order to recognize a "tap".
  // Less frames will give a faster response time, but may also give incorrect input:
  let tapThreshold = 10

  var isTouchingScreen = false

  var frameCountSinceFirstTouch = 0

  let bird = Bird()

  // Scene setup:
  override func didMove(to view: SKView) {
    removeAllChildren() // Remove this from your actual project.

    anchorPoint = CGPoint(x: 0.5, y: 0.5)
    physicsBody = SKPhysicsBody(edgeLoopFrom: frame)
    physicsBody!.categoryBitMask = UInt32(2)
    physicsBody!.contactTestBitMask = UInt32(1)
    physicsWorld.gravity = CGVector(dx: 0, dy: -2)
    physicsWorld.contactDelegate = self

    bird.initialize()
    addChild(bird)
  }

  // Touch handling stuff:
  func tap() {
    bird.flyUp()
    controlToDo = .none
  }

  func longPress() {
    bird.flyForward()
  }

  override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    isTouchingScreen = true
  }

  override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
    isTouchingScreen = false

    if frameCountSinceFirstTouch > tapThreshold {
      controlToDo = .none
      frameCountSinceFirstTouch = 0
    }
    else if frameCountSinceFirstTouch < tapThreshold {
      controlToDo = .tap
      frameCountSinceFirstTouch = 0
    }

  }

  override func update(_ currentTime: TimeInterval) {

    // Increase counter if touching the screen:
    if isTouchingScreen {
      frameCountSinceFirstTouch += 1
    }

    // If we have held the screen for long enough, do a longPress:
    if frameCountSinceFirstTouch > tapThreshold {
      controlToDo = .longPress
    }

    switch controlToDo {
    case .tap:       tap()
    case .longPress: longPress()
    case .none:      break
    }
  }

  // Reset positon on touch of wall:
  func didBegin(_ contact: SKPhysicsContact) {
    bird.resetPosition()
  }
}

目标C:

// MAKE SURE YOU HAVE <SKPhysicsContactDelegate> in your .h file!
#import "GameScene.h"

@implementation GameScene {
  SKSpriteNode *_bird;

  /// 1 is tap, 2 is longpress (i couldn't figure out how to compare strings, or do an enum)
  int _controlToDo;

  CGFloat _tapThreshold;
  CGFloat _frameCountSinceFirstTouch;
  Boolean _isTouchingScreen;
}

- (void) setupProperties {
  _controlToDo = 0;
  _tapThreshold = 10;
  _isTouchingScreen = false;
  _frameCountSinceFirstTouch = 0;
}

- (void) setupScene {
  self.anchorPoint = CGPointMake(0.5, 0.5);
  [self removeAllChildren];
  self.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:self.frame];
  self.physicsBody.categoryBitMask = 2;
  self.physicsBody.contactTestBitMask = 1;
  self.physicsWorld.gravity = CGVectorMake(0, -2);
  self.physicsWorld.contactDelegate = self;
  _bird = [SKSpriteNode spriteNodeWithColor:[UIColor blueColor] size:CGSizeMake(50, 50)];
  _bird.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:_bird.size];
  _bird.physicsBody.categoryBitMask = 1;
  [self addChild:_bird];
}

- (void) birdResetPosition {
  CGFloat min = 0 - (self.size.width / 2) + (_bird.size.width / 2) + 5;
  CGPoint center = CGPointMake(min, 0);
  SKAction *movement = [SKAction moveTo:center duration:0];
  [_bird runAction:movement];
}

- (void)didMoveToView:(SKView *)view {
  [self setupProperties];
  [self setupScene];
}


- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
  _isTouchingScreen = true;
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
  _isTouchingScreen = false;

  if (_frameCountSinceFirstTouch > _tapThreshold) {
    _controlToDo = 0;
  }
  else if (_frameCountSinceFirstTouch < _tapThreshold) {
    _controlToDo = 1;
  }

  _frameCountSinceFirstTouch = 0;
}


-(void)update:(CFTimeInterval)currentTime {

  // Increase counter if touching the screen:
  if (_isTouchingScreen == true) {
    _frameCountSinceFirstTouch += 1;
  }

  // If we have held the screen for long enough, do a longPress:
  if (_frameCountSinceFirstTouch > _tapThreshold) {
    _controlToDo = 2;
  }

  // Bird fly up:
  if (_controlToDo == 1) {
    [_bird.physicsBody applyImpulse:CGVectorMake(0, 20)];
    _controlToDo = 0;
  }

  // Bird fly forward:
  else if (_controlToDo == 2) {
    [_bird.physicsBody applyForce:CGVectorMake(100, 0)];
  }

}

- (void) didBeginContact:(SKPhysicsContact *)contact {
  [self birdResetPosition];
  _bird.physicsBody.velocity = CGVectorMake(0, 0);
}

@end

答案 1 :(得分:1)

所有SKNode都可以访问触摸事件,您只需启用userInteractionEnabled

即可

默认设置为false,但如果您使用的是SKS文件,则SKS默认设置为true

只要您没有启用任何其他节点,当您触摸场景时,它将为场景触发。

要举行暂停活动,我建议您在场景中使用SKAction

基本上,我们希望等待特定时间段,然后触发事件

如果在任何时候手指被移除,那么我们会移除动作,而不会触发你的事件。

夫特:

override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
    let wait = SKAction.wait(forDuration:1)
    let beginHoldEvent = SKAction(run:{//function to start hold event})
    run(SKAction.sequence([wait,beginHoldEvent],withKey:"holding")
}


override func touchesEnded(touches: NSSet, withEvent event: UIEvent) {
    guard let _ = action(forKey:"holding") else {return}
    removeAction(forKey:"holding")
    //do single click event
}

目标C:

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {

    void (^holdBlock)(void) = ^{
       //do holding code here
    };
    SKAction* wait = [SKAction waitForDuration:1];
    SKAction* beginHoldEvent = [SKAction runBlock:holdBlock];
    SKAction* seq = [SKAction sequence:@[wait,beghinHoldEvent]];
    [self runAction:seq withKey:@"holding"];
}


-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    if([self actionForKey:@"holding"] == nil){
        //we are on a long touch do nothing
       return;
    }

    [self removeActionForKey:@"holding")];
    //do tap event

}

现在这段代码不能处理多个触摸,你需要处理它。