Casual-заготовки под Swift

в 17:33, , рубрики: admob, audio player, bouncing, game development, swift, разработка мобильных приложений, разработка под iOS

image

Привет, братья во swift-e. Пришлось и мне освоить сей птичий язык, и нет лучшего способа для обучения, чем сделать законченный продукт под истрепанный iPhone. Меня давно кусала изнутри сложная математическая головоломка, а тут раз! И вдруг на свифте. Код местами выглядит смешно, зато понятно. Сам алгоритм игры я обсуждать не буду, он слишком сложен для местных модераторов, не умеющей в уме разделить 111 на 3. А вот примеры полезных функций для разработчиков casual game — пожалуйста.

Случайность

Прежде всего в любой игре нужен собственный генератор случайных последовательностей целых чисел. Для генерации повторяющихся раскладов и для много еще чего.
Если код функции на Джава, JS и С выглядит одинаково (ведь Ваша игра на всех языках будет написана?), то на Swift методом тыка пришлось сильно переделать код. Вместо 2 строк он стал занимать 4 строки.

    var holdrand  = 0
    
    func microsoft_rnd()->Int {
        var k = Int.multiplyWithOverflow(holdrand, 214013)
        k = Int.addWithOverflow(k.0, 2531011)
        holdrand =  k.0
        return Int((holdrand >> 16)  & 0x7FFF)
    }
    
    func microsoft_rand(number:Int)->Int {
        return microsoft_rnd() % number
    }

А вот как выглядел С-ый вариант миксофотовского датчика, выдранного мной из MSVC 15 лет назад.

- (int)microsoft_rnd {
	holdrand = holdrand * 214013  + 2531011;
	return ((holdrand >> 16)  & 0x7FFF);
}

- (int) microsoft_rand:(int) number{
	return [self microsoft_rnd] % number;
}

Как видим Swift очень строг к переполнениям и добавляет головные боли разработчику, но, видимо, снимает стресс с runtime-юзера.

Так вот. Определяя переменную holdrand скажем в 25, мы заведомо повторим расклад для условного уровня 25 во всех вариантах головоломки — будь она написана на PHP для игры на сервере, либо на Swift для игры на iPhone. Это дает ощущение братства для играющих — они как бы все оффлайн, но в то же время заочно соревнуются на тех же раскладах. Похоже на турнир по игре в Бридж. Давно я не играл в бридж, кстати.

Перейдем к практике. Давайте создадим простейший проект на Swift используя Xcode. Нажимаем кнопки File->New->Project...->Single View Application и получаем готовый проект-заготовку, главный класс которого AppDelegate.swift выглядит пугающе просто


import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?
    
    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
        // Override point for customization after application launch.
    }
}  

Что изменилось со времен Obj-C? Во-первых, вместо двух файлов, стал один. Это я одобряю.
Кроме того, в тексте появились странные символы? и!.. Не пугайтесь, это признаки типа переменной, если переменная x:Float! — то точно Float, если x:Float? — то автор не уверен, что данная переменная всегда будет Float.

Честно говоря, Xcode сам подставляет в случае нужды эти знаки, и после дня кодирования, Вы начнете понимать логику использования идиотских символов.

Реклама

Итак, что же необходимо для игры разработчику во вторую очередь, но не последнюю? Конечно, реклама. Именно в этот class AppDelegate ее надо добавлять. Скачиваем последнюю версию GoogleMobileAds со Свифтом, добавляем в проект и пишем пару функций

import UIKit
import GoogleMobileAds

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?
    var bannerView: GADBannerView!
    var iPhone5 = true
        
    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
        startAds()
        return true
    }
    
    func startAds() {
        bannerView = GADBannerView(adSize: kGADAdSizeBanner)
        print("Google Mobile Ads SDK version: " + GADRequest.sdkVersion())
        bannerView.adUnitID = yourAdMobID
        bannerView.rootViewController = self.window?.rootViewController
        let adViewHeight = bannerView.frame.size.height
        print("adViewHeight: (adViewHeight)")
        let screenSize: CGRect = UIScreen.mainScreen().bounds
        let screenHeight = screenSize.height
        bannerView.frame.origin = CGPointMake(0, screenHeight -  adViewHeight)
        let request = GADRequest()
        request.testDevices = [kGADSimulatorID ]
        self.window?.rootViewController!.view.addSubview(bannerView)
        bannerView.loadRequest(request)
 // Заодно и флаг установим, чтоб 2 раза не ходить
       iPhone5 = screenHeight<heightOfScreenOfiPhone5AndMoreInLogicalPixels ? false : true
    }
    
    func bringAdsToFront() {
        self.window?.rootViewController!.view.bringSubviewToFront(bannerView)
    }
}  

В теле основной функции появился вызов startAds().
Функция bringAdsToFront() на самом деле мне не нужна, но Вам может пригодиться, если Вы используете нестандартные ViewControllers, которые будут перекрывать рекламное окно. В этом случае баннер надо вытаскивать поверх всех окон вызовом данной процедуры.

Переменная iPhone5 всегда необходима, как воздух, пока живы два Aspect Ratio в iOS. Напоминаю, что 5% процентов населения все еще пользуется 4 iPhone, пропорции которого отличаются от всех современных моделей. Кроме того, если Ваше приложение iPhone only, то при запуске его на iPad Вы получаете снова старый добрый режим 320 на 480 пикселов.

Почему так размещаю баннер? Приложение у меня, чтобы играть в метро одной рукой на телефоне, поэтому всегда рабочий режим — Портрет. И рекламу 320х50 я спускаю вниз экрана, это увеличивает заработки вдвое. На момент публикации статьи я уже заработал 40 центов США.

В принципе, теперь Вы научились программировать на Swift.

Звук

Что еще необходимо в игре, даже самой примитивной? Звуки Му.

Добавляем.


import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?
    var soundLib: SoundController?
    
    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
      soundLib = SoundController.init()
   }

    func setVolume(v:Double) {
        soundLib?.setVolume(v*v)
    }
    
    func playSound(i:Int) {
        soundLib?.playSound(i)
    }
}  

Кроме того, в проект добавлем десяток mp3 файлов, вытащенных из Google, и создаем класс SoundController в отдельном файле

SoundController.swift

//  Created by Vadim Bashurov on 3/19/16.
//

import UIKit
import AVFoundation

class SoundController {
    let audioFiles: [String: String] = [
        "Ashley_glad_1": "caf",
        "star_first": "caf",
        "bird shot-a3": "caf",
        "bouncer":"caf",
        "bird shot-a3":"mp3",
        "ball_bounce":"mp3",
        "bird_a4":"mp3",
        "HittingBasketBoard":"mp3",
        "piglette_a1":"mp3",
        ]
    
    var players : [AVAudioPlayer] = []
    var volume = 0.0
    
    init () {
        for (name, ext) in audioFiles {
            let a1 = self.setupAudioPlayerWithFile(name, type:ext)
            players.append(a1!)
        }
    }
    
    func setVolume(v:Double) {
        volume = v
        for audioPlayer in players {
            audioPlayer.volume = Float(volume)
        }
    }
    
    func playSound(i:Int)
    {
        let audioPlayer = players[i]
        audioPlayer.play()
    }
    
    func setupAudioPlayerWithFile(file:NSString, type:NSString) -> AVAudioPlayer?  {
        let path = NSBundle.mainBundle().pathForResource(file as String, ofType: type as String)
        let url = NSURL.fileURLWithPath(path!)
        var audioPlayer:AVAudioPlayer?
        do {
            try audioPlayer = AVAudioPlayer(contentsOfURL: url)
            audioPlayer!.prepareToPlay()
            
        } catch {
            print("Player not available")
        }
        return audioPlayer
    }
}

Разумеется, я перебрал все 4 варианта воспроизведения звука под iOS. Системные звуки не подходят для разработки игры, поскольку программно не контролируют уровень воспроизведения. AudioService из OAL библиотеки мной на Swift не освоена, скорее всего брошу bridge и буду использовать свою Obj-C библиотеку, поскольку несмотря на preload, данный способ лагает при старте программы.

И на сладкое, попрыгунчики!

Попрыгунчики

Все ваши кнопки надписи и картинки должны появляться на экране, подобно реальным предметам из сказочного мира. Либо всплывать, либо прилетать, выходить из тени и подрыгивать. Начиная с iOS 8 Apple предложил левую функцию для bouncing анимации.

Вот пример

                    UIView.animateWithDuration(1.2, delay: 0.1, usingSpringWithDamping: 0.3, initialSpringVelocity: 0.0, options: UIViewAnimationOptions.CurveLinear, animations: {
                            
                            box1.center = CGPointMake(box2.center.x,box2.center.y)
                            
                            },
                            completion: {finished in
                                self.showFingers()
                        })
                        
                    

В результате работы работы функции ящик 1 упадет на ящик 2 (это сюжет моей головоломки) не просто тупо, а с подскоками, после которых мирно успокоится.
Физику подскока можно варьировать 3 параметрами — временем всего процесса — у меня это 1.2 секунды, второй параметр задержан перед началом падения, он на bouncing никак не влияет, а 3 и 4 параметры имеют значение от 0 до 1 и определяют качество прыжка. Если установить их в 1, то отскока не будет. Если в ноль, ящик будет скакать как вошь на ветке.

Поэкспериментруйте сами, это занятие крайне веселое.

Теперь Вы готовы, чтобы написать простенькую игрульку на Swift и заработать пару долларов на пиво. Чао!

Автор: PapaBubaDiop

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js