在Sprite纹理中检测触摸而不是整个框架iOS Swift SpriteKit?

Posted

技术标签:

【中文标题】在Sprite纹理中检测触摸而不是整个框架iOS Swift SpriteKit?【英文标题】:Detect touch within Sprite texture and not the entire frame iOS Swift SpriteKit? 【发布时间】:2015-10-08 05:02:18 【问题描述】:

我现在正在使用 SpriteKit,我遇到了一个看似简单的问题,但我在互联网上找不到任何东西。我有三个形状像平行四边形堆叠在一起的按钮,它看起来像这样: Screenshot of buttons

let button = SKSpriteNode(imageNamed: "playbutton")
let leaderButton = SKSpriteNode(imageNamed: "leaderbutton")
let homeButton = SKSpriteNode(imageNamed: "homebutton")

    button.position = CGPoint(x: size.width/2, y: size.height/2)
    addChild(button)

    leaderButton.position = CGPoint(x: size.width/2, y: size.height/2 - button.size.height/2)
    addChild(leaderButton)

    homeButton.position = CGPoint(x: size.width/2, y: size.height/2 - button.size.height)
    addChild(homeButton)

override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) 
    for touch: AnyObject in touches 

        let location = touch.locationInNode(self)

        if button.containsPoint(location) 
            button.runAction(SKAction.scaleTo(0.8, duration: 0.1))
        
        else if leaderButton.containsPoint(location) 
            leaderButton.runAction(SKAction.scaleTo(0.8, duration: 0.1))
        
        else if homeButton.containsPoint(location) 
            homeButton.runAction(SKAction.scaleTo(0.8, duration: 0.1))
        
    

这就是我检测触摸的方式。问题是它们重叠,因为精灵实际上是一个矩形,所以当我尝试点击第二个按钮的左上角时,顶部按钮会检测到它。我想知道有一种方法可以仅在纹理中检测触摸,例如如何将物理主体设置为纹理。 感谢您能给我的任何帮助!

链接现在有效。

所以我尝试了这个:

    button.position = CGPoint(x: size.width/2, y: size.height/2)
    button.physicsBody = SKPhysicsBody(texture: SKTexture(imageNamed: "playbutton"), size: button.size)
    button.physicsBody?.dynamic = false
    addChild(button)

    leaderButton.position = CGPoint(x: size.width/2, y: size.height/2 - button.size.height/2)
    leaderButton.physicsBody = SKPhysicsBody(texture: SKTexture(imageNamed: "leaderbutton"), size: leaderButton.size)
    leaderButton.physicsBody?.dynamic = false
    addChild(leaderButton)

    homeButton.position = CGPoint(x: size.width/2, y: size.height/2 - button.size.height)
    homeButton.physicsBody = SKPhysicsBody(texture: SKTexture(imageNamed: "homebutton"), size: homeButton.size)
    homeButton.physicsBody?.dynamic = false
    addChild(homeButton)

override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) 
    for touch: AnyObject in touches 

        let location = touch.locationInNode(self)

        if physicsWorld.bodyAtPoint(location)?.node == button 
            button.runAction(SKAction.scaleTo(0.8, duration: 0.1))
            print("play")
        
        if physicsWorld.bodyAtPoint(location)?.node == leaderButton 
            leaderButton.runAction(SKAction.scaleTo(0.8, duration: 0.1))
            print("leader")
        
        if physicsWorld.bodyAtPoint(location)?.node == homeButton 
            homeButton.runAction(SKAction.scaleTo(0.8, duration: 0.1))
        
    

它仍然注册整个帧,而不仅仅是物理体。请参阅链接以查看按钮以及它们的坐标如何相交。

【问题讨论】:

leaderButton 高于button?我无法想象它们为什么会重叠,因为它们应该使用您的代码彼此相邻。 在 GameViewController.swift 中添加 skView.showsPhysics = true 以检查按钮的物理主体。是平行四边形还是长方形? 您可以尝试使用SKRegion 吗?您可以构建与按钮轮廓匹配的形状的 3 个区域,并在触摸事件中检查触摸位置是否在任何区域中... 当我展示物理时,它完美地展示了围绕纹理的物理体。由于某种原因,检测触摸是否在物理体中的代码不起作用。中间按钮甚至不起作用,即使它与其他两个按钮具有相同的代码,我也无法解释 【参考方案1】:

如果您将物理体附加到每个按钮,您可以检测到您的触摸落在哪个物理体上。

您可以使用SKPhysicsBody(texture:size:)SKPhysicsBody(texture:alphaThreshold:size:) 从按钮的纹理(假设按钮为SKSpriteNode)生成物理体,或者您可以创建描述按钮形状的CGPath 并使用SKPhysicsBody(polygonFromPath:)。将主体分配给按钮的physicsBody 属性。假设您实际上并不希望物理模拟器移动您的按钮,请将每个主体的 dynamic 属性设置为 false

然后,您可以使用physicsWorld.bodyAtPoint(_:) 来获取触摸所触及的实体之一(其中physicsWorld 是您的SKScene 的属性)。使用主体的node 属性返回按钮节点。如果主体重叠,bodyAtPoint 返回任意主体。

如果您需要您触摸的所有实体,您可以使用physicsWorld.enumerateBodiesAtPoint(_:usingBlock:)

一个完全不同的方法,如果你可以创建一个CGPath描述按钮的形状,是使用SKScene.convertPointFromView(_:)然后SKNode.convertPoint(_:fromNode:_)将点转换成按钮的坐标系,然后使用CGPathContainsPoint(a全局函数)来检测该点是否在描述按钮形状的路径中。

【讨论】:

@samando 值得尝试最后一种方法:CGPathContainsPoint 方法【参考方案2】:

(我看不到您的 imgur 链接,但我认为我对您所描述的内容有一个很好的了解。)

有some discussions on how to read the alpha value of out an SKTexture,但这些需要相当多的开销来实现“我是否触摸了平行四边形”?尤其是当您为按钮添加 alpha 效果时。

您可以对位置执行任何您想要的逻辑 - 您有一个包含该位置的 CGPoint,并且您知道按钮的大小和形状。

编写一个函数来测试一个点是否在平行四边形按钮内应该非常简单。由于平行四边形基本上是在“内部”矩形的每一端都有三角形的矩形,如果你知道那个“内部”矩形的大小,你可以很容易地确定触摸是否在你想要的位置:

检查它是否在整个按钮的矩形中。如果不是,你就知道它不在平行四边形中。

检查它是否在“内部”矩形内 - 平行四边形的部分,三角形“切掉”末端。如果是这样,那么您就知道它在平行四边形中。

检查它是否在其中一个三角形内。您知道在包含三角形的矩形中触摸点向上有多远和多远,并且您知道分割该矩形以根据其宽度和高度构成三角形的线的斜率。这使得检查该点是否在三角形中变得微不足道 - 只需对一条线使用“y = mx + b”公式,其中m是斜率,b是你需要添加的任何东西才能让这条线通过通过矩形的一角(通常是您的“y 截距”)。如果y坐标小于/大于m*(x坐标)+b,就可以判断触摸是在那个三角形里面还是外面。

一旦你有了这个功能,你就可以用它作为检查每个按钮的测试。

【讨论】:

以上是关于在Sprite纹理中检测触摸而不是整个框架iOS Swift SpriteKit?的主要内容,如果未能解决你的问题,请参考以下文章

检测 Sprite Kit 中的长触摸

我们如何检测精灵的触摸?

cocos判断整个场景是不是有点击触摸事件

检测 UITableView 的超级视图中的触摸,如果反弹不是 = 否,它会反弹

检测用户是不是在 iOS 上触摸过屏幕

Sprite kit:删除特定节点而不是所有节点