返回

iOS SpriteKit打造简单手游虚拟摇杆(通用组件)

IOS

写在前面

2016年,MOBA手游市场可谓是火爆异常(此处不为任何游戏站台),笔者也是在同年开始接触SpriteKit,即苹果专为2D游戏开发打造的框架。

对于MOBA手游的爱好者而言,游戏中的角色移动方式一定再熟悉不过了。而对于未曾接触过MOBA手游的读者,咱们也不绕圈子,直接上图。

(图片展示:MOBA手游中的虚拟摇杆)

怎么样,现在想起来了嘛?因为……

触控虚拟摇杆的实现

1. 创建组件

首先,我们创建一个自定义组件,命名为VirtualJoystick,代码如下:

import SpriteKit

class VirtualJoystick: SKSpriteNode {
    // 摇杆的背景图片
    private let backgroundImage: SKSpriteNode
    // 摇杆的控制按钮
    private let controlImage: SKSpriteNode
    
    // 摇杆的最大移动范围
    private let maxRadius: CGFloat
    // 摇杆的当前位置
    private var currentPosition: CGPoint
    
    // 摇杆事件委托
    weak var delegate: VirtualJoystickDelegate?
    
    init(backgroundImageName: String, controlImageName: String, maxRadius: CGFloat) {
        self.backgroundImage = SKSpriteNode(imageNamed: backgroundImageName)
        self.controlImage = SKSpriteNode(imageNamed: controlImageName)
        self.maxRadius = maxRadius
        self.currentPosition = .zero
        
        super.init(texture: nil, color: .clear, size: backgroundImage.size)
        
        addChild(backgroundImage)
        addChild(controlImage)
        
        controlImage.position = .zero
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    // MARK: - 触控处理
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        guard let touch = touches.first else { return }
        let location = touch.location(in: self)
        
        if controlImage.contains(location) {
            currentPosition = location
            delegate?.virtualJoystickDidBeginDragging(self)
        }
    }
    
    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        guard let touch = touches.first else { return }
        let location = touch.location(in: self)
        
        // 计算偏移量,限制摇杆移动范围
        let offset = location - currentPosition
        let length = offset.length()
        
        if length > maxRadius {
            let normalizedOffset = offset.normalized() * maxRadius
            currentPosition = currentPosition + normalizedOffset
            controlImage.position = normalizedOffset
        } else {
            currentPosition = location
            controlImage.position = offset
        }
        
        delegate?.virtualJoystickDidMove(self, offset: offset)
    }
    
    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        // 手指离开屏幕时,摇杆复位
        controlImage.position = .zero
        currentPosition = .zero
        
        delegate?.virtualJoystickDidEndDragging(self)
    }
    
    override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
        // 手指离开屏幕时,摇杆复位
        controlImage.position = .zero
        currentPosition = .zero
        
        delegate?.virtualJoystickDidEndDragging(self)
    }
}

// MARK: - 摇杆事件代理
protocol VirtualJoystickDelegate: AnyObject {
    // 摇杆开始拖动
    func virtualJoystickDidBeginDragging(_ virtualJoystick: VirtualJoystick)
    // 摇杆拖动中
    func virtualJoystickDidMove(_ virtualJoystick: VirtualJoystick, offset: CGPoint)
    // 摇杆停止拖动
    func virtualJoystickDidEndDragging(_ virtualJoystick: VirtualJoystick)
}

2. 使用组件

在游戏场景中,添加VirtualJoystick组件并设置其代理:

// 创建虚拟摇杆
let joystick = VirtualJoystick(backgroundImageName: "joystick_background", controlImageName: "joystick_control", maxRadius: 100)
joystick.position = CGPoint(x: 100, y: 100)
joystick.delegate = self
addChild(joystick)

3. 实现代理方法

在游戏场景中,实现VirtualJoystickDelegate代理方法,处理摇杆事件:

extension GameScene: VirtualJoystickDelegate {
    func virtualJoystickDidBeginDragging(_ virtualJoystick: VirtualJoystick) {
        // 摇杆开始拖动时的处理
    }
    
    func virtualJoystickDidMove(_ virtualJoystick: VirtualJoystick, offset: CGPoint) {
        // 摇杆拖动中的处理
    }
    
    func virtualJoystickDidEndDragging(_ virtualJoystick: VirtualJoystick) {
        // 摇杆停止拖动时的处理
    }
}

结语

通过上述步骤,我们就可以在SpriteKit中实现一个简单的虚拟摇杆。这个组件支持拖动控制,并且可以设置摇杆的移动范围和外观。

希望本教程对您有所帮助。如果您有任何问题或建议,欢迎在评论区留言。