[SWIFT] [Introduction] Développement d'applications iOS # 10 [Divers paramètres de jeu (niveau de difficulté et de vitesse)]

introduction

Cette fois, nous avons créé des détails sur le niveau de difficulté et le niveau de vitesse qui changent en fonction du tour, et avons combiné chaque mode d'écran créé dans # 5 [Conception de séquence]. Ensuite, amenez-le à l'état presque terminé (ce qui suit est un film d'image). Le code source est disponible sur GitHub, veuillez donc vous y référer.

spécification

Les spécifications du niveau de difficulté et du niveau de vitesse qui changent en fonction du tour sont les suivantes.

Spec1.png

Spec2.png

Image3.png

Cette fois, la version étrangère de la table de difficulté et le paramètre Spurt ② ne seront pas créés.

Mise en place de table de difficulté et table de vitesse

Implémentez une table de difficulté et une table de vitesse comme un tableau de taples. Ci-dessous, tous sont inclus dans la classe CgContext sauf pour la combinaison de chaque mode d'écran.

    enum EnLevel: Int {
        case Level_A = 0, Level_B, Level_C, Level_D
    }

    let table_difficultySettings: [(round: Int, levelOfSpeed: EnLevel, timeWithPower: Int, numberOfFeedsRemaingToSpurt: Int, levelOfAppearance: EnLevel, kindOfSpecialTarget: CgSpecialTarget.EnSpecialTarget, timeNotToEat: Int, intermission: Int)] = [
            //round, speedLevel, PowerTime[ms], Spurtfeeds, GhostAppear, SpecialTarget, NoEatTime[ms], Intermission
            (     1,   .Level_A,          6000,         20,    .Level_A,      .Cherry,           4000,            0 ),
            (     2,   .Level_B,          5000,         30,    .Level_B,  .Strawberry,           4000,            1 ),
            (     3,   .Level_B,          4000,         40,    .Level_C,      .Orange,           3000,            0 ),
            (     4,   .Level_B,          3000,         40,    .Level_C,      .Orange,           3000,            0 ),
            (     5,   .Level_C,          2000,         40,    .Level_C,       .Apple,           3000,            2 ),
            (     6,   .Level_C,          5000,         50,    .Level_C,       .Apple,           3000,            0 ),
            (     7,   .Level_C,          2000,         50,    .Level_C,       .Melon,           3000,            0 ),
            (     8,   .Level_C,          2000,         50,    .Level_C,       .Melon,           3000,            0 ),
            (     9,   .Level_C,          1000,         60,    .Level_C,    .Galaxian,           3000,            3 ),
            (    10,   .Level_C,          5000,         60,    .Level_C,    .Galaxian,           3000,            0 ),
            (    11,   .Level_C,          2000,         60,    .Level_C,        .Bell,           3000,            0 ),
            (    12,   .Level_C,          1000,         80,    .Level_C,        .Bell,           3000,            0 ),
            (    13,   .Level_C,          1000,         80,    .Level_C,         .Key,           3000,            3 ),
            (    14,   .Level_C,          3000,         80,    .Level_C,         .Key,           3000,            0 ),
            (    15,   .Level_C,          1000,        100,    .Level_C,         .Key,           3000,            0 ),
            (    16,   .Level_C,          1000,        100,    .Level_C,         .Key,           3000,            0 ),
            (    17,   .Level_C,             0,        100,    .Level_C,         .Key,           3000,            3 ),
            (    18,   .Level_C,          1000,        100,    .Level_C,         .Key,           3000,            0 ),
            (    19,   .Level_C,             0,        100,    .Level_C,         .Key,           3000,            0 ),
            (    20,   .Level_C,             0,        100,    .Level_C,         .Key,           3000,            0 ),
            (    21,   .Level_C,             0,        100,    .Level_C,         .Key,           3000,            0 ),
            (    22,   .Level_D,             0,        100,    .Level_C,         .Key,           3000,            0 )
        ]

    let table_speedSettings: [ (eatNone: Int, eatFeed: Int, eatPow: Int, eatNoneInPow: Int, eatFeedInPow: Int, eatPowInPow: Int,
         ghost: Int, ghostInSpurt: Int, ghostInPow: Int, ghostInWarp: Int) ]
        = [
            // Level A
            ( eatNone: 16, eatFeed: 15, eatPow: 13, eatNoneInPow: 18, eatFeedInPow: 17, eatPowInPow: 15,
              ghost: 15, ghostInSpurt: 16, ghostInPow: 10, ghostInWarp: 8 ),
            // Level B
            ( eatNone: 18, eatFeed: 17, eatPow: 15, eatNoneInPow: 19, eatFeedInPow: 18, eatPowInPow: 16,
              ghost: 17, ghostInSpurt: 18, ghostInPow: 11, ghostInWarp: 9 ),
            // Level C
            ( eatNone: 20, eatFeed: 19, eatPow: 17, eatNoneInPow: 20, eatFeedInPow: 19, eatPowInPow: 17,
              ghost: 19, ghostInSpurt: 20, ghostInPow: 12, ghostInWarp: 10 ),
            // Level D
            ( eatNone: 18, eatFeed: 17, eatPow: 15, eatNoneInPow: 18, eatFeedInPow: 17, eatPowInPow: 15,
              ghost: 19, ghostInSpurt: 20, ghostInPow: 10, ghostInWarp: 9 )
        ]

Extrayez les données selon le tour de ce tableau de taples et définissez-les respectivement dans les membres de la classe CgContext.


    /// Set difficulty of the round
    func setDifficulty() {
        let index = demo ? 0 : round-1
        let count = table_difficultySettings.count
        let table = (index < count) ? table_difficultySettings[index] : table_difficultySettings[count-1]
        
        levelOfSpeed = table.levelOfSpeed
        timeWithPower = table.timeWithPower
        numberOfFeedsRemaingToSpurt = table.numberOfFeedsRemaingToSpurt
        levelOfAppearance = table.levelOfAppearance
        kindOfSpecialTarget = table.kindOfSpecialTarget
        timeNotToEat = table.timeNotToEat
        intermission = table.intermission
    }

La méthode pour obtenir la vitesse du joueur (Pacman) est la suivante. Modifiez la valeur à acquérir lorsque vous mangez de la nourriture puissante et inversez-la et quand elle ne l'est pas.

    func getPlayerSpeed(action: CgPlayer.EnPlayerAction, with power: Bool ) -> Int {
        let index = levelOfSpeed.rawValue
        let count = table_speedSettings.count
        let table = (index < count) ? table_speedSettings[index] : table_speedSettings[count-1]

        switch action {
            case .Walking where !power : return table.eatNone
            case .Walking where  power : return table.eatNoneInPow
            case .EatingFeed where !power : return table.eatFeed
            case .EatingFeed where  power : return table.eatFeedInPow
            case .EatingPower where !power : return table.eatPow
            case .EatingPower where  power : return table.eatPowInPow
            case .EatingFruit where !power : return table.eatNone
            case .EatingFruit where  power : return table.eatNoneInPow
            default: return 16
        }
    }

Mise en œuvre du timing d'apparition des monstres (fantômes)

Le nombre de fantômes que Pacman a mangés depuis le début du jeu renvoie le nombre de fantômes qui apparaissent pour chaque niveau par numberOfFeedsEated.

    func getNumberOfGhostsForAppearace() -> Int {
        let numberOfGhosts: Int
        // Miss Bypass Sequence
        if playerMiss {
            if numberOfFeedsEatedByMiss < 7 {
                numberOfGhosts = 1
            } else if numberOfFeedsEatedByMiss < 17 {
                numberOfGhosts = 2
            } else if numberOfFeedsEatedByMiss < 32 {
                numberOfGhosts = 3
            } else {
                playerMiss = false
                numberOfGhosts = getNumberOfGhostsForAppearace()
            }
        } else {
            switch levelOfAppearance {
                case .Level_A:
                    if numberOfFeedsEated < 30 {
                        numberOfGhosts = 2
                    } else if numberOfFeedsEated < 90 {
                        numberOfGhosts = 3
                    } else {
                        numberOfGhosts = 4
                    }
                case .Level_B:
                    if numberOfFeedsEated < 50 {
                        numberOfGhosts = 3
                    } else {
                        numberOfGhosts = 4
                    }
                case .Level_C: fallthrough
                default:
                    numberOfGhosts = 4
            }
        }
        return numberOfGhosts
    }

Mise en œuvre de l'attaque ondulée

Basculez entre ChaseMode et ScatterMode en fonction du temps compté depuis le début. Déterminez la durée du ChaseMode pour chaque niveau.

    func judgeGhostsWavyChase(time: Int) -> Bool {
        var chaseMode: Bool = false
        switch levelOfSpeed {
            case .Level_A:
                chaseMode = (time >= 7000 && time < 27000) || (time >= 34000 && time < 54000)
                         || (time >= 59000 && time < 79000) || (time >= 84000)
            case .Level_B:
                chaseMode = (time >= 7000 && time < 27000) || (time >= 34000 && time < 54000)
                         || (time >= 59000)
            case .Level_C: fallthrough
            case .Level_D:
                chaseMode = (time >= 5000 && time < 25000) || (time >= 30000 && time < 50000)
                         || (time >= 55000)
        }
        return chaseMode
    }

Combinaison de chaque mode d'écran

Enfin, combinez chaque mode créé jusqu'à présent dans la classe CgGameMain.


class CgGameMain : CgSceneFrame {

    enum EnMainMode: Int {
        case AttractMode = 0, CreditMode, WaitForStartButton, StartMode, PlayMode
    }

    enum EnSubMode: Int {
        case Character = 0, StartDemo, PlayDemo
    }

    private var scene_attractMode: CgSceneAttractMode!
    private var scene_creditMode: CgSceneCreditMode!
    private var scene_maze: CgSceneMaze!
    private var subMode: EnSubMode = .Character

    init(skscene: SKScene) {
        super.init()

        // Create SpriteKit managers.
        self.sprite = CgSpriteManager(view: skscene, imageNamed: "pacman16_16.png ", width: 16, height: 16, maxNumber: 64)
        self.background = CgCustomBackgroundManager(view: skscene, imageNamed: "pacman8_8.png ", width: 8, height: 8, maxNumber: 2)
        self.sound = CgSoundManager(binding: self, view: skscene)
        self.context = CgContext()

        scene_attractMode = CgSceneAttractMode(object: self)
        scene_creditMode = CgSceneCreditMode(object: self)
        scene_maze = CgSceneMaze(object: self)
    }

    /// 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 {
            if let mode: EnMainMode = EnMainMode(rawValue: getSequence()) {
                if mode == .AttractMode || mode == .WaitForStartButton {
                    goToNextSequence()
                }
            }
        }
    }
    
    /// 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 {
        guard let mode: EnMainMode = EnMainMode(rawValue: sequence) else { return false }

        switch mode {
            case .AttractMode: attarctMode()
            case .CreditMode: creditMode()
            case .WaitForStartButton: break // Forever loop
            case .StartMode: startMode()
            case .PlayMode: playMode()
        }
        
        // Continue running sequence.
        return true
    }

    // ============================================================
    //  Execute each mode.
    // ============================================================

    func attarctMode() {
        switch subMode {
            case .Character:
                scene_attractMode.resetSequence()
                scene_attractMode.startSequence()
                subMode = .StartDemo

            case .StartDemo:
                if !scene_attractMode.enabled {
                    context.demo = true
                    sound.enableOutput(false)
                    scene_maze.resetSequence()
                    scene_maze.startSequence()
                    subMode = .PlayDemo
                }

            case .PlayDemo:
                if !scene_maze.enabled {
                    subMode = .Character
                }
        }
    }
    
    func creditMode() {
        context.demo = false
        if scene_attractMode.enabled {
            scene_attractMode.stopSequence()
            scene_attractMode.clear()
        }
        if scene_maze.enabled {
            scene_maze.stopSequence()
            scene_maze.clear()
        }

        context.credit += 1
        scene_creditMode.resetSequence()
        scene_creditMode.startSequence()
        sound.enableOutput(true)
        sound.playSE(.Credit)
        goToNextSequence()
    }
    
    func startMode() {
        context.credit -= 1
        scene_creditMode.stopSequence()
        scene_maze.resetSequence()
        scene_maze.startSequence()
        goToNextSequence()
    }

    func playMode() {
        if !scene_maze.enabled {
            subMode = .Character
            goToNextSequence(EnMainMode.AttractMode.rawValue)
        }
    }

}

En plus de l'introduction du personnage, il y a une démo de jeu en mode attractif. Il s'agissait d'un indicateur de démonstration qui pouvait être facilement implémenté en basculant entre une opération de balayage et une table d'opération préparée.

La table d'opération et la méthode d'acquisition sont implémentées dans la classe CgContext. La direction est retirée en fonction du nombre d'images depuis le début.

    let table_operationInDemo: [ (frameCount: Int, direction: EnDirection) ] = [
        (9, .Left), (36, .Down), (61, .Right), (82, .Down), (109, .Right), (133, .Up), (162, .Right),
        (189, .Up), (215, .Right), (238, .Down), (261, .Right), (308, .Down), (335, .Left), (523, .Up),
        (555, .Right), (569, .Up), (609, .Left), (632, .Up), (648, .Right), (684, .Up), (732, .Left),
        (831, .Down), (864, .Left), (931, .Up), (948, .Left), (970, .Up), (1063, .Right), (1113, .Down),
        (1157, .Right), (1218, .Down)
    ]

    func getOperationForDemo() -> EnDirection {
        guard(demoSequence < table_operationInDemo.count) else { return .None }
        let table = table_operationInDemo[demoSequence]
        var direction: EnDirection = .None
        if counterByFrame >= table.frameCount {
            direction = table.direction
            demoSequence += 1
        }
        return direction
    }

Obtenez la direction de l'opération avec la méthode getOperationForDemo et définissez-la dans le lecteur. Ajouté à la séquence sequenceUpdating de la classe CgSceneMaze.

    func sequenceUpdating() {
        // Operate player in demonstration automatically.
        if context.demo {
            let direction = context.getOperationForDemo()
            if direction != .None {
                player.targetDirecition = direction
            }
        }

        //Omis ci-dessous

Sommaire

Enfin, il était presque terminé.

Il n'y a aucun problème avec la vitesse de fonctionnement du jeu. Le code source est toujours autour de 5000 lignes, ce qui est assez facile à faire.

La prochaine fois, je fais mon propre travail, alors j'aimerais prendre diverses dispositions pour le terminer.

Article suivant

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

Recommended Posts

[Introduction] Développement d'applications iOS # 10 [Divers paramètres de jeu (niveau de difficulté et de vitesse)]
[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
Introduction au développement d'applications Android