[SWIFT] [Einführung] iOS-Anwendungsentwicklung Nr. 11 [Anordnung der Spiele (Betrieb mit Beschleunigungssensor usw.)]

Einführung

Arrangiert für Pacman, erstellt basierend auf den veröffentlichten Spezifikationen Ich will hinzufügen. Lassen Sie uns Pacman nicht nur durch Wischen, sondern auch durch Berührungs- und Beschleunigungssensor steuern und ein originelles Labyrinth hinzufügen. Der Quellcode ist auf GitHub verfügbar. Bitte beziehen Sie sich darauf.

Image2.png

Konfigurationsmenü

Fügen Sie ein Konfigurationsmenü hinzu, um zwischen Operationen und Labyrinthen zu wechseln. Für das Hinzufügen des Labyrinths habe ich mich auf die folgenden Spezifikationen in der Entwicklung bezogen.

Image3.png

Quellcode

Fügen Sie CgSceneCreditMode ein Menü hinzu. Nach einer Weile nach dem Einlegen des Guthabens wird in Fall 1 der Eingang des Menüs angezeigt. Wenn Sie es berühren, können Sie das Menü aufrufen.

Informationen zur Struktur der CgSceneFrame-Klasse finden Sie in der vorherigen [Einführung] 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
    }

}

Wenn ein Touch-Ereignis von der handelEvent-Methode empfangen wird, wird der von der Suchfunktion gedrückte Bereich überprüft und gegebenenfalls der Fall dieser Sequenz ausgeführt.

Der Einstellungswert wird in der CgContext-Klasse definiert.

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

    //Unten weggelassen

Bedienung durch Beschleunigungssensor

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)])
        }
    }

Generieren Sie CMMotionManager in der GameScene-Klasse und legen Sie den Zyklus fest, um den Wert im AccelerometerUpdateInterval-Member abzurufen. Setzen Sie sendAccelEvent in der Rückrufmethode.

Die sendAccelEvent-Methode wird in einem Zyklus von 0,05 Sekunden aufgerufen, in dem die Richtung aus der Neigung des Beschleunigungssensors berechnet wird und das Ereignis mit gameMain.sendEvent auf dieselbe Weise wie die Wischoperation an das Objekt gesendet wird.

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
        }
    }

Wenn die im Konfigurationsmenü festgelegte Operation in handleEvent der CgPlayer-Klasse (deligateActor.isOperationMode (Modus: CgContext.EnOperationMode.Accel)) gültig ist, wird das Ereignis des Beschleunigungssensors akzeptiert.

Informationen hierzu finden Sie in der vorherigen [Einführung] iOS App Development # 6 [Character Operation].

Hinzufügung des ursprünglichen Labyrinths

Lassen Sie die getMazeSource-Methode der CgSceneMaze-Klasse die Labyrinthdaten um den Wert des Menüs ändern. Das Erstellen dieser Labyrinthdaten ist klassisch, aber überraschend einfach.

    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
    }

Zusammenfassung

Das Folgende wurde dieses Mal als Arrangement des Pacman-Spiels hinzugefügt. --Konfigurationsmenü

Schließlich

Ich habe mit Swift iOS-Programmierung basierend auf Pacmans Spielen studiert. Der Quellcode bestand aus etwa 5000 Zeilen einschließlich Kommentaren, und es war leicht möglich, die Qualität und Anordnung eines Arcade-Spiels zu realisieren.

Es begann mit einem Sommerurlaub, in dem ich in Corona nirgendwo hingehen konnte, aber es war eine weitere gute Gelegenheit, das Programmieren zu genießen.

Dies ist das Ende aller 11 Einführungen in die Entwicklung von iOS-Anwendungen.


Danke fürs Lesen ~

Recommended Posts

[Einführung] iOS-Anwendungsentwicklung Nr. 11 [Anordnung der Spiele (Betrieb mit Beschleunigungssensor usw.)]
[Einführung] iOS-Anwendungsentwicklung # 10 [Verschiedene Spieleinstellungen (Schwierigkeitsgrad und Geschwindigkeitsstufe)]
Einführung in die Android App-Entwicklung
Roadmap für die Entwicklung von iOS-Apps (Einführung)
Spieleentwicklung mit zwei Personen mit Java 2
Spieleentwicklung mit zwei Personen mit Java 1
Spieleentwicklung mit zwei Personen mit Java 3