[SWIFT] [Introduction] Développement d'applications iOS # 11 [Disposition des jeux (fonctionnement à l'aide du capteur d'accélération, etc.)]

introduction

Organisé pour Pacman créé sur la base des [Spécifications] publiées (https://jsai.ixsq.nii.ac.jp/ej/?action=pages_view_main&active_action=repository_view_main_item_detail&item_id=9670&item_no=1&page_id=13&block_id=23) Je veux ajouter. Faisons en sorte que Pacman contrôle non seulement en faisant glisser mais aussi par le toucher et le capteur d'accélération, et ajoutons un labyrinthe original. Le code source est disponible sur GitHub, veuillez donc vous y référer.

Image2.png

Menu de configuration

Ajoutez un menu de configuration pour basculer entre les opérations et les labyrinthes. Pour le labyrinthe à ajouter, je me suis référé aux spécifications suivantes en cours de développement.

Image3.png

Code source

Ajoutez un menu à CgSceneCreditMode. Quelques instants après l'insertion du crédit, l'entrée du menu s'affiche dans le cas 1. Si vous la touchez, vous pouvez accéder au menu.

Pour la structure de la classe CgSceneFrame, reportez-vous à la précédente [Introduction] iOS App Development # 5 [Sequence Design].

/// Credit Mode
class CgSceneCreditMode : CgSceneFrame {

    enum EnEvent: Int {
        case EnterConfig = 3
        case Operation = 5
        case ExtraMode = 6
        case DebugMode = 7
        case Language = 8
        case ResetSetting = 9
        case ExitConfig = 10
        case None
    }

    private let table_enterConfiguration: [(Int,Int,Int,Int,EnEvent)] = [
        (  26, 34, 28, 36, .EnterConfig)
    ]

    private let table_setConfiguration: [(Int,Int,Int,Int,EnEvent)] = [
        (  26, 34, 28, 36, .ExitConfig),
        (   4, 25, 28, 27, .Operation),
        (   4, 20, 28, 22, .ExtraMode),
        (   4, 15, 28, 17, .DebugMode),
        (   4, 10, 28, 12, .Language),
        (   4,  5, 28,  7, .ResetSetting)
    ]

    private var table_search: [(column0:Int,row0:Int,column1:Int,row1:Int,event:EnEvent)] = []
    private var configMode: Bool = false

    /// Event handler
    /// - Parameters:
    ///   - sender: Message sender
    ///   - id: Message ID
    ///   - values: Parameters of message
    override func handleEvent(sender: CbObject, message: EnMessage, parameter values: [Int]) {
        if message == .Touch {
            let position = CgPosition.init(x: CGFloat(values[0]), y: CGFloat(values[1]))
            let event = search(column: position.column, row: position.row)
            if event == .None {
                if !configMode {
                    stopSequence()
                }
            } else {
                goToNextSequence(event.rawValue)
            }
        }
    }

    /// Handle sequence
    /// To override in a derived class.
    /// - Parameter sequence: Sequence number
    /// - Returns: If true, continue the sequence, if not, end the sequence.
    override func handleSequence(sequence: Int) -> Bool {
        switch sequence {
            case  0:
                configMode = false
                table_search = []
                clear()
                printFrame()
                printPlayerScore()
                printHighScore()
                printCredit()
                printRounds()
                background.print(0, color: .Orange, column: 6, row: 19, string: "PUSH START BUTTON")
                background.print(0, color: .Cyan, column: 8, row: 15, string: "1 PLAYER ONLY")

                if context.language == .English {
                    background.print(0, color: .Pink, column: 1, row: 11, string: "BONUS PAC-MAN FOR 20000 ]^_")
                    background.print(0, color: .Purple, column:4, row: 4, string: "@ 2020 HIWAY.KIKUTADA")
                } else {
                    background.print(0, color: .Pink, column: 1, row: 11, string: "BONUS PAC-MAN FOR 10000 ]^_")
                    background.print(0, color: .Purple, column: 7, row: 7, string: "@ #$%&'()*  2020")   // NAMACO
                }
                goToNextSequence(after: 60*16*2)
            
            case 1:
                table_search = table_enterConfiguration
                background.print(0, color: .White, column:  27, row: 35, string: "$")
                goToNextSequence()

            case 2:
                // wait for event
                break;

            case 3:
                configMode = true
                table_search = table_setConfiguration
                background.fill(0, texture: 0)
                printFrame()
                printPlayerScore()
                printHighScore()
                printCredit()
                background.print(0, color: .White, column: 27, row: 35, string: "#")
                background.print(0, color: .Red,   column:  8, row: 31, string: "CONFIGURATION")
                background.print(0, color: .White, column:  4, row: 26, string: "OPERATION")
                background.print(0, color: .White, column:  4, row: 21, string: "EXTRA MODE")
                background.print(0, color: .White, column:  4, row: 16, string: "DEBUG MODE")
                background.print(0, color: .White, column:  4, row: 11, string: "LANGUAGE")
                background.print(0, color: .White, column:  4, row:  6, string: "SETTING")
                print_operation(color: .Pink)
                print_extraMode(color: .Pink)
                print_debugMode(color: .Pink)
                print_language(color: .Pink)
                print_resetSetting(color: .Pink)
                goToNextSequence()

            case 4:
                // wait for event
                break;

            case 5: // operation
                context.operationMode = context.operationMode.getNext()
                print_operation(color: .Yellow)
                goToNextSequence(4)
                
            case 6: // ExtraMode/
                context.extraMode = context.extraMode.getNext()
                print_extraMode(color: .Yellow)
                goToNextSequence(4)

            case 7: // DebugMode
                context.debugMode = context.debugMode.getNext()
                print_debugMode(color: .Yellow)
                goToNextSequence(4)

            case 8: // Language
                context.language = context.language.getNext()
                print_language(color: .Yellow)
                goToNextSequence(4)

            case 9: // ResetSetting
                context.resetSetting = context.resetSetting.getNext()
                print_resetSetting(color: .Yellow)
                goToNextSequence(4)

            case 10:
                context.saveConfiguration()
                goToNextSequence(0)

            default:
                clear()
                // Stop and exit running sequence.
                return false
        }
        
        return true
    }
    
    func clear() {
        background.fill(0, texture: 0)
    }

    func print_operation(color: CgCustomBackgroundManager.EnBgColor) {
        let str = context.operationMode.getString()
        background.print(0, color: color, column: 16, row: 26, string: str)
    }

    func print_extraMode(color: CgCustomBackgroundManager.EnBgColor) {
        let str = context.extraMode.getString()
        background.print(0, color: color, column: 16, row: 21, string: str)
    }

    func print_debugMode(color: CgCustomBackgroundManager.EnBgColor) {
        let str = context.debugMode.getString()
        background.print(0, color: color, column: 16, row: 16, string: str)
    }

    func print_language(color: CgCustomBackgroundManager.EnBgColor) {
        let str = context.language.getString()
        background.print(0, color: color, column: 16, row: 11, string: str)
    }

    func print_resetSetting(color: CgCustomBackgroundManager.EnBgColor) {
        let str = context.resetSetting.getString()
        background.print(0, color: color, column: 16, row: 6, string: str)
    }

    func search(column: Int, row: Int) -> EnEvent {
        var event: EnEvent = .None
        for i in 0 ..< table_search.count {
            let t = table_search[i]
            if (t.column0 <= column && t.row0 <= row) && (t.column1 >= column && t.row1 >= row) {
                event = t.event
                break
            }
        }
        return event
    }

}

Lorsqu'un événement Touch est reçu par la méthode handelEvent, la plage pressée par la fonction de recherche est vérifiée et, le cas échéant, le cas de cette séquence est exécuté.

La valeur du paramètre est définie dans la classe CgContext.

class CgContext {

    enum EnOperationMode: Int {
        case Swipe = 0, Touch, Accel
        
        func getString() -> String {
            switch self {
                case .Swipe: return "(SWIPE)"
                case .Touch: return "(TOUCH)"
                case .Accel: return "(ACCEL)"
            }
        }
        
        func getNext() -> EnOperationMode {
            switch self {
                case .Swipe: return .Touch
                case .Touch: return .Accel
                case .Accel: return .Swipe
            }
        }
    }

    enum EnLanguage: Int {
        case English = 0, Japanese
        
        func getString() -> String {
            switch self {
                case .English:  return "(ENGLISH) "
                case .Japanese: return "(JAPANESE)"
            }
        }
        
        func getNext() -> EnLanguage {
            switch self {
                case .English: return .Japanese
                case .Japanese: return .English
            }
        }
    }

    enum EnOnOff: Int {
        case Off = 0, On
        
        func getString() -> String {
            switch self {
                case .On:  return "(ON) "
                case .Off: return "(OFF)"
            }
        }
        
        func getNext() -> EnOnOff {
            switch self {
                case .On: return .Off
                case .Off: return .On
            }
        }
    }

    enum EnSetting: Int {
        case Clear = 0, Keep
        
        func getString() -> String {
            switch self {
                case .Clear: return "(CLEAR)"
                case .Keep:  return "(KEEP) "
            }
        }
        
        func getNext() -> EnSetting {
            switch self {
                case .Clear: return .Keep
                case .Keep: return .Clear
            }
        }
    }

    var operationMode: EnOperationMode = .Swipe
    var extraMode: EnOnOff = .Off
    var debugMode: EnOnOff = .Off
    var resetSetting: EnSetting = .Clear
    var language: EnLanguage = .Japanese

    //Omis ci-dessous

Fonctionnement par capteur d'accélération

class GameScene: SKScene {
    
    /// Main object with main game sequence
    private var gameMain: CgGameMain!
    
    /// Points for Swipe operation
    private var startPoint: CGPoint = CGPoint.init()
    private var endPoint: CGPoint = CGPoint.init()

    // MotionManager for accel
    private var motionManager: CMMotionManager!

    override func didMove(to view: SKView) {

        //  Create and start game sequence.
        gameMain  = CgGameMain(skscene: self)
        gameMain.startSequence()

        // Create motion manager
        motionManager = CMMotionManager()
        motionManager.accelerometerUpdateInterval = 0.05

        motionManager.startAccelerometerUpdates(
            to: OperationQueue.current!, withHandler: {
                (accelData: CMAccelerometerData?, errorOC: Error?) in self.sendAccelEvent(acceleration: accelData!.acceleration)
            }
        )

    }

    func sendAccelEvent(acceleration: CMAcceleration){
        let x_diff: Int = Int(acceleration.x * 100)
        let y_diff: Int = Int(acceleration.y * 100)

        if abs(x_diff) > abs(y_diff) {
            gameMain.sendEvent(message: .Accel, parameter: [Int(x_diff > 0 ? EnDirection.Right.rawValue : EnDirection.Left.rawValue)])
        } else {
            gameMain.sendEvent(message: .Accel, parameter: [Int(y_diff > 0 ? EnDirection.Up.rawValue : EnDirection.Down.rawValue)])
        }
    }

Générez CMMotionManager dans la classe GameScene et définissez le cycle pour obtenir la valeur dans le membre accelerometerUpdateInterval, Définissez sendAccelEvent dans la méthode de rappel.

La méthode sendAccelEvent est appelée dans un cycle de 0,05 s, dans lequel la direction est calculée à partir de l'inclinaison du capteur d'accélération, et l'événement est envoyé à l'objet avec gameMain.sendEvent de la même manière que l'opération de balayage.

class CgPlayer : CgActor {

    /// Event handler
    /// - Parameters:
    ///   - sender: Message sender
    ///   - id: Message ID
    ///   - values: Parameters of message
    override func handleEvent(sender: CbObject, message: EnMessage, parameter values: [Int]) {
        guard !deligateActor.isDemoMode() else { return }
        switch message {
            case .Accel where deligateActor.isOperationMode(mode: CgContext.EnOperationMode.Accel): fallthrough
            case .Swipe where deligateActor.isOperationMode(mode: CgContext.EnOperationMode.Swipe):
                if let direction = EnDirection(rawValue: values[0]) {
                    targetDirecition = direction
                }

            case .Touch where deligateActor.isOperationMode(mode: CgContext.EnOperationMode.Touch):
                setTargetPosition(x: values[0], y: values[1])
                targetDirecition = decideDirectionByTarget(forcedDirectionChange: true)
                position.amountMoved = 0
                
            default:
                break
        }
    }

Si l'opération définie dans le menu de configuration est valide dans handleEvent de la classe CgPlayer (deligateActor.isOperationMode (mode: CgContext.EnOperationMode.Accel)), l'événement du capteur d'accélération est accepté.

Pour cela, veuillez vous référer à la précédente [Introduction] Développement d'applications iOS # 6 [Fonctionnement des caractères].

Ajout du labyrinthe original

Faites en sorte que la méthode getMazeSource de la classe CgSceneMaze permute les données du labyrinthe par la valeur du menu. La création de ces données de labyrinthe est classique mais étonnamment facile.

    func getMazeSource() -> [String] {
        
        let mazeSource: [String] = [
            "aggggggggggggjiggggggggggggb",
            "e111111111111EF111111111111f",
            "e1AGGB1AGGGB1EF1AGGGB1AGGB1f",
            "e3E  F1E   F1EF1E   F1E  F3f",
            "e1CHHD1CHHHD1CD1CHHHD1CHHD1f",
            "e11111111111111111111111111f",
            "e1AGGB1AB1AGGGGGGB1AB1AGGB1f",
            "e1CHHD1EF1CHHJIHHD1EF1CHHD1f",
            "e111111EF1111EF1111EF111111f",
            "chhhhB1EKGGB1EF1AGGLF1Ahhhhd",
            "     e1EIHHD2CD2CHHJF1f     ",
            "     e1EF          EF1f     ",
            "     e1EF QhUWWVhR EF1f     ",
            "gggggD1CD f      e CD1Cggggg",
            "____  1   f      e   1  ____" ,
            "hhhhhB1AB f      e AB1Ahhhhh",
            "     e1EF SggggggT EF1f     ",
            "     e1EF          EF1f     ",
            "     e1EF AGGGGGGB EF1f     ",
            "aggggD1CD1CHHJIHHD1CD1Cggggb",
            "e111111111111EF111111111111f",
            "e1AGGB1AGGGB1EF1AGGGB1AGGB1f",
            "e1CHJF1CHHHD2CD2CHHHD1EIHD1f",
            "e311EF1111111  1111111EF113f",
            "kGB1EF1AB1AGGGGGGB1AB1EF1AGl",
            "YHD1CD1EF1CHHJIHHD1EF1CD1CHZ",
            "e111111EF1111EF1111EF111111f",
            "e1AGGGGLKGGB1EF1AGGLKGGGGB1f",
            "e1CHHHHHHHHD1CD1CHHHHHHHHD1f",
            "e11111111111111111111111111f",
            "chhhhhhhhhhhhhhhhhhhhhhhhhhd"
        ]

        let mazeSourceExtra1: [String] = [
                "aggggjiggggggjiggggggjiggggb",
                "e1111EF111111EF111111EF1111f",
                "e1AB1EF1AGGB1CD1AGGB1EF1AB1f",
                "e3EF1EF1E  F1111E  F1EF1EF3f",
                "e1CD1CD1CHHD1AB1CHHD1CD1CD1f",
                "e111111111111EF111111111111f",
                "kGGB1AGGB1AGGLKGGB1AGGB1AGGl",
                "YHJF1EIHD1CHHJIHHD1CHJF1EIHZ",
                "e1EF1EF111111EF111111EF1EF1f",
                "e1EF1EKGGGGB1EF1AGGGGLF1EF1f",
                "e1CD1CHHHHHD2CD2CHHHHHD1CD1f",
                "e11111111          11111111f",
                "e1AB1AGGB QhUWWVhR AGGB1AB1f",
                "e1EF1CHHD f      e CHHD1EF1f",
                "e1EF11111 f      e 11111EF1f",
                "kGLF1AGGB f      e AGGB1EKGl",
                "YHHD1EIHD SggggggT CHJF1CHHZ",
                "e1111EF11          11EF1111f",
                "e1AGGLF1AGGGGGGGGGGB1EKGGB1f",
                "e1CHHJF1CHHHHJIHHHHD1EIHHD1f",
                "e1111EF111111EF111111EF1111f",
                "kGGB1EKGGGGB1EF1AGGGGLF1AGGl",
                "YHHD1CHHHHHD2CD2CHHHHHD1CHHZ",
                "e111111111111  111111111111f",
                "e1AGGGB1AGGGGGGGGGGB1AGGGB1f",
                "e1CHHJF1CHHHHJIHHHHD1EIHHD1f",
                "e3111EF111111EF111111EF1113f",
                "kGGB1EF1AB1AGLKGB1AB1EF1AGGl",
                "YHHD1CD1EF1CHHHHD1EF1CD1CHHZ",
                "e1111111EF11111111EF1111111f",
                "chhhhhhhnmhhhhhhhhnmhhhhhhhd"
        ]

        return context.extraMode == CgContext.EnOnOff.Off ? mazeSource : mazeSourceExtra1
    }

Sommaire

Ce qui suit a été ajouté en tant qu'arrangement du jeu Pacman cette fois. --Menu de configuration --Capteur d'accélération, opération tactile

finalement

J'ai étudié la programmation iOS avec Swift basée sur les jeux de Pacman. Le code source était d'environ 5000 lignes, commentaires compris, et il était possible de réaliser facilement la qualité et l'arrangement équivalents à un jeu d'arcade.

Cela a commencé par des vacances d'été où je ne pouvais aller nulle part à Corona, mais c'était une autre bonne occasion de profiter de la programmation.

C'est la fin des 11 introductions au développement d'applications iOS.


Merci d'avoir lu ~

Recommended Posts

[Introduction] Développement d'applications iOS # 11 [Disposition des jeux (fonctionnement à l'aide du capteur d'accélération, etc.)]
[Introduction] Développement d'applications iOS # 10 [Divers paramètres de jeu (niveau de difficulté et de vitesse)]
Introduction au développement d'applications Android
Feuille de route des compétences de développement d'applications iOS (introduction)
Développement de jeux avec deux personnes utilisant java 2
Développement de jeux avec deux personnes utilisant java 1
Développement de jeux avec deux personnes utilisant java 3