Привет, братья во 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-юзера. При этом Obj-C прекрасно работал без всяких стрессов и головных болей.
Так вот. Определяя переменную holdrand, скажем 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 сам подставляет в случае нужды эти знаки, и после 4 часов кодирования, Вы начнете понимать логику использования идиотских символов.
Реклама
Итак, что же необходимо для игры разработчику во вторую очередь, но не последнюю? Конечно, реклама. Именно в этот 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
}
func bringAdsToFront() {
self.window?.rootViewController!.view.bringSubviewToFront(bannerView)
}
}
В теле основной функции появился вызов startAds().
Функция bringAdsToFront() на самом деле мне не нужна, но Вам может пригодиться, если Вы используете нестандартные ViewControllers, которые будут перекрывать рекламное окно при своем появлении. В этом случае баннер надо вытаскивать поверх всех Views вызовом данной процедуры.
Переменная 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 и caf файлов, вытащенных из Google, и создаем класс SoundController в отдельном файле
// 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",
"star_third": "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 не мной не переписаны, не хватает знаний и времени. AudioQuery тормозит. В дальнейшем я брошу bridge от своей Obj-C библиотеки, поскольку несмотря на функцию preload, AVAudioPlayer лагает при старте программы.
И на сладкое, попрыгунчики!
Попрыгунчики
Все ваши кнопки надписи и картинки должны появляться на экране, подобно реальным предметам из сказочного мира. Либо всплывать, либо прилетать, выходить из тени и под-дрыгивать. Начиная с 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 секунды, второй параметр — это задержка перед началом падения (delay: 0.1) на bouncing никак не влияет.
А вот 3 и 4 параметры имеют значение от 0 до 1 и определяют качество прыжка. Если установить их в 1, то отскока не будет. Если в ноль, ящик будет скакать как вошь на ветке.
Поэкспериментруйте сами, это занятие крайне веселое.
Теперь Вы готовы, чтобы написать простенькую игрульку на Swift и заработать пару долларов на пиво.
Чао!
Комментарии (13)
stepik777
27.03.2016 22:53iPhone5 = screenHeight<heightOfScreenOfiPhone5AndMoreInLogicalPixels ? false : true
В свифте что ли нет операторов 'больше либо равно' и 'не'? Иначе зачем так странно писать код?PapaBubaDiop
27.03.2016 22:58Предложите свой вариант, с удовольствием поменяю на лучший.
hardex
27.03.2016 23:21+4Вы серьезно?
iPhone5 = screenHeight >= heightOfScreenOfiPhone5AndMoreInLogicalPixels
PapaBubaDiop
27.03.2016 23:32Поменял. Сам-то я пользую deviceType:Int, поскольку там еще значение для iPad. Типа 0-iPhone4, 1-iPhone5, 2 — iPad
rafuck
28.03.2016 02:07+1А потом уберем рекламу и, внезапно, флаг iPhone5 инициализирован неправильно.
rsi
29.03.2016 07:32А почему именно iPhone 5, еще есть же iPhone 6 и iPhone 6 + или это просто легаси переменная и вам просто необходим не iPhone 4? К чему это я? К тому, что есть более удобный способ определения модели устройства. Например я использую честно украденное где то расширение для UIDevice
PapaBubaDiop
29.03.2016 09:31+1В логических пикселах для iPhone существует лишь два разрешения (320 на 480) и (320 на 568). Для создания игр этого достаточно. Некоторые разработчики проверяют и фактические размеры, создают дополнительные наборы картинок для 4 режимов (4, 5, 6, 6+).
SergeyVoyteshonok
28.03.2016 17:55+1Ну вот уже лучше, я даже плюсанул. Но вы в любом случае молодец, что перешли на Swift, почему-то в разработке под iOS очень много староверов до сих пор программирующих на Obj-c.
hoack
29.03.2016 17:31"Сам алгоритм игры я обсуждать не буду, он слишком сложен для местных модераторов, не умеющих в уме разделить 111 на 3."
А хамить-то зачем?
Sublustris
Давно хотел спросить, но всё стеснялся. Вы какому издателю игры отдаёте? Или самиздат?
PapaBubaDiop
Никакому. В эту игру, например, будут играть человек 35-37, но лет по 5 каждый. Я сам в нее рублюсь, пока не дохожу до непроходимых уровней, после чего добавляю исключения в код и выкладываю новую версию в магазин. Увлекательный цикл, я даже опасаюсь, как бы меня не уволили.
Все, решено, больше не играю.