実録! 2年半ぶりのSwiftアプリバージョンアップ 2日目 その1

おはようございます。続きです。

【エラーメッセージ】
C-style for statement has been removed in Swift 3
【修正前】
for var y = newYStart; y <= newYEnd; y += 1 {
【修正後】
for y in newYStart … newYEnd { // 3.1
【解説】
Swift 3.0で for ループの構文が変わりました。影響はとても大きいですが、記述が簡潔になりました。英断でしたね。個人的にはそのままにして欲しかった・・・

【エラーメッセージ】
C-style for statement has been removed in Swift 3
【修正前】
for var xi = 1; xi <= xNum; xi += 1 {
【修正後】
for xi in 1 … xNum { // 3.1
【解説】
同じ

【エラーメッセージ】
C-style for statement has been removed in Swift 3
【修正前】
for var yi = 1; yi <= yNum; yi += 1 {
【修正後】
for yi in 1 … yNum { // 3.1
【解説】
同じ

【エラーメッセージ】
C-style for statement has been removed in Swift 3
【修正前】
for var i = 0; i < gs.m_touches.count; i++ {
【修正後】
for i in 0 ..< gs.m_touches.count {
【解説】
“以下”の場合は … で、”未満”の場合は ..< です。 【エラーメッセージ】
C-style for statement has been removed in Swift 3
【修正前】
for var xi = gs.m_glass.enable() ? 8 – gs.m_glass.xV % 8 : 0; xi <= xNum; xi += 8 {
【修正後】
for xi in stride(from: (gs.m_glass.enable() ? 8 – gs.m_glass.xV % 8 : 0), through: xNum, by: 8) { // 3.1
【解説】
飛び石パターンです。throughが”以下”、toが”未満”です。このパターンはもう1箇所ありました。

【エラーメッセージ】
C-style for statement has been removed in Swift 3
【修正前】
for var x:CGFloat = (bPlusX ? start.x + unitX : start.x – unitX);
(bPlusX ? x < end.x : end.x < x);
x += (bPlusX ? unitX : -unitX) {
// 処理
}
【修正後】
if (bPlusX) {
for x : CGFloat in stride(from: start.x + unitX, to: end.x, by: unitX) {
// 処理
}
} else {
for x : CGFloat in stride(from: end.x, to: start.x – unitX, by: unitX).reversed() {
// 処理
}
}
【解説】
時間かかりました・・・
事前に条件分岐して2つのforループに分けるしかない・・・よね?全体として記述が長くなってしまったパターンです。複雑なforループは書くべきじゃないということか・・・このパターンはもう1箇所ありました。

【エラーメッセージ】
Use of unresolved identifier ‘countElements’
【修正前】
countElements(target)
【修正後】
target.characters.count
【解説】
変数targetはString型です。文字の長さ取得だけで、結構手間ががかります。charactersは、見た目の文字数を取得します。

【エラーメッセージ】
‘Default’ has been renamed to ‘default’
【修正前】
UIAlertActionStyle.Default
【修正後】
UIAlertActionStyle.default
【解説】
Defaultの先頭大文字が小文字に変わっただけ。合計3箇所ありました。

【エラーメッセージ】
Cannot convert value of type ‘String’ to expected argument type ‘UnsafeMutableRawPointer’
【修正前】
if let path = Bundle.main.path(forResource: file as String, ofType: “sks”) {
var sceneData = Data(bytesNoCopy: path, count: .DataReadingMappedIfSafe, deallocator: nil)!
【修正後】
if let path = Bundle.main.path(forResource: file as String, ofType: “sks”) {
let pathURL : URL = URL(fileURLWithPath: path)
var sceneData : Data
do {
sceneData = try Data(contentsOf: pathURL, options: .mappedIfSafe)
} catch {
print(“unarchiveFromFile error.”)
}
【解説】
結構変わるなあ・・・

【エラーメッセージ】
‘init(forReadingWithData:)’ has been renamed to ‘init(forReadingWith:)’
【修正前】
var archiver = NSKeyedUnarchiver(forReadingWithData: sceneData)
【修正後】
var archiver = NSKeyedUnarchiver(forReadingWith: sceneData)
【解説】
ラベル名が変わっただけ。

【エラーメッセージ】
‘Any?’ is not convertible to ‘GameScene’; did you mean to use ‘as!’ to force downcast?
【修正前】
let scene = archiver.decodeObjectForKey(NSKeyedArchiveRootObjectKey) as GameScene
【修正後】
let scene = archiver.decodeObject(forKey: NSKeyedArchiveRootObjectKey) as! GameScene
【解説】
ダウンキャストは必ず成功する想定なので、as! で強制的にキャストして対処します、が、いつからこのエラー出るようになったんですかね?

【エラーメッセージ】
Argument labels ‘(data:)’ do not match any available overloads
【修正前】
var data: Data = Data(data:UIImagePNGRepresentation(image))
【修正後】
var data: Data = UIImagePNGRepresentation(image)!
【解説】
UIImagePNGRepresentationから直接Dataを取得可能になったようです。いつ頃変わったんだろう・・・

・・・続く

実録! 2年半ぶりのSwiftアプリバージョンアップ 1日目 その4

続きです。

【エラーメッセージ】
Extra argument ‘error’ in call
【修正前】
fileManager.removeItemAtPath(pngPath, error: nil)
【修正後】
do { // 3.1
try fileManager.removeItem(atPath: pngPath) // 3.1
} catch { // 3.1
print(“removeItem error”) // 3.1
} // 3.1
【解説】
エラー処理が変わりました。今後はtry catchで統一するんですね。また、removeItemAtPathはremoveItemに名称変更となり、第一引数のラベル名atPathが必須になりました。

【エラーメッセージ】
‘++’ is unavailable: it has been removed in Swift 3
【修正前】
if MAX_HISTORY <= ++gs.m_hisIdx {
【修正後】
gs.m_hisIdx += 1 // 3.1
if MAX_HISTORY <= gs.m_hisIdx { // 3.1
【解説】
++や–の演算子は廃止だそうです。わかりにくくなるからかしら・・・
変数の前の++は該当行より前で、変数の後の++は該当行より後でインクリメントします。デクリメントも同様です。

【エラーメッセージ】
‘bytes’ is unavailable: use withUnsafeBytes instead
【修正前】
var buffer : UnsafePointer = CFDataGetBytePtr(dataRef)
memcpy(&gs.m_buffer, Data(bytes: UnsafePointer(buffer), count : newX * newY * 4).bytes, newX * newY * 4)
【修正後】
var buffer : UnsafePointer = CFDataGetBytePtr(dataRef)
memcpy(&gs.m_buffer, buffer, newX * newY * 4)
【解説】
エラーの直接の解説ではないです。withUnsafeBytesを使う以前に、なんで一旦Data型に変換してたんだろう・・・
直接 UnsafePointer をmemcpyの引数に指定することでエラーを回避。動くかなぁ・・・

【エラーメッセージ】
NSString’ is not implicitly convertible to ‘String’; did you mean to use ‘as’ to explicitly convert?
【修正前】
let path = paths1[0].stringByAppendingPathComponent(NSString(format:”slot%02d.png”,index))
【修正後】
let path = paths1[0].stringByAppendingPathComponent(String(format:”slot%02d.png”,index)) // 3.1
【解説】
エラーの意味は「NSStringは暗黙的にStringへ変換できないよ?’as’使って明示的に変換したら?」です。stringByAppendingPathComponentの引数はStringですが、そこにNSStringを指定したのでこのエラーが発生しています。Stringにもformatがあるのでそれを使うようにし、NSStringを使わないように対処しました。

【エラーメッセージ】
GameViewController.swift:109:19: Method does not override any method from its superclass
【修正前】
override func supportedInterfaceOrientations() -> Int {
if UIDevice.current.userInterfaceIdiom == .phone {
return Int(UIInterfaceOrientationMask.allButUpsideDown.rawValue)
} else {
return Int(UIInterfaceOrientationMask.all.rawValue)
}
}
【修正後】
override var supportedInterfaceOrientations : UIInterfaceOrientationMask {
if UIDevice.current.userInterfaceIdiom == .phone {
return UIInterfaceOrientationMask.allButUpsideDown
} else {
return UIInterfaceOrientationMask.all
}
}
【解説】
サポートしている画面の向きを返すメソッド supportedInterfaceOrientations の定義が変わったようです。戻り値も変わってるので、それに合わせてreturnの内容も調整しました。

【エラーメッセージ】
‘CGContextSetLineDash’ is unavailable: Use setLineDash(self:phase:lengths:)
【修正前】
CGContextSetLineDash(UIGraphicsGetCurrentContext(), 0, len, len.count)
【修正後】
UIGraphicsGetCurrentContext()?.setLineDash(phase: 0.0, lengths: len) // 3.1
【解説】
そろそろ疲れてきました ←解説になってない
2年半ぶりのSwiftなので、オプショナル型とかアンラップとかの知識を付け直しました。

今日はもう寝ます。さようなら2017年7月15日。

・・・続く

実録! 2年半ぶりのSwiftアプリバージョンアップ 1日目 その3

コンバートしたらエラーが増えた!とか言っててもエラーが無くなる訳ではないので、諦めてエラー箇所を直していきます。

以下、修正したエラーです。

【エラーメッセージ】
GameScene.swift:218:19: Method does not override any method from its superclass
【修正前】
override func touchesBegan(_ touches: NSSet, with event: UIEvent) {
【修正後】
override func touchesBegan(_ touches: Set, with event: UIEvent?) { // 3.1
【解説】
メソッドの定義が変わったようです。overrideを指示しているにもかかわらず、該当のメソッドがスーパークラスに見つからないので怒られています。最新のメソッド定義に直しました。NSSetがSetになりました。Swift1.2でコレクションクラスSetが追加されたため、Objective-CからあったNSSetを置き換えたのでしょうね。UIEvent?は、オプショナル型というやつですね。nilを許容するようになりましたが、なぜ許容するようにしたんですかね?

【エラーメッセージ】
GameScene.swift:221:19: Method does not override any method from its superclass
【修正前】
override func touchesMoved(_ touches: NSSet, with event: UIEvent) {
【修正後】
override func touchesMoved(_ touches: Set, with event: UIEvent?) { // 3.1
【解説】
同じ

【エラーメッセージ】
GameScene.swift:224:19: Method does not override any method from its superclass
【修正前】
override func touchesEnded(_ touches: NSSet, with event: UIEvent) {
【修正後】
override func touchesEnded(_ touches: Set, with event: UIEvent?) { // 3.1
【解説】
同じ

【エラーメッセージ】
GameScene.swift:227:19: Method does not override any method from its superclass
【修正前】
override func touchesCancelled(_ touches: NSSet!, with event: UIEvent!) {
【修正後】
override func touchesCancelled(_ touches: Set, with event: UIEvent?) { // 3.1
【解説】
同じ

【エラーメッセージ】
Missing argument label ‘rawValue:’ in call
【修正前】
fileprivate let m_bitmapInfo : CGBitmapInfo = CGBitmapInfo(CGImageAlphaInfo.PremultipliedLast.rawValue)
【修正後】
fileprivate let m_bitmapInfo : CGBitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.PremultipliedLast.rawValue) // 3.1
【解説】
Swift3.0から、1つ目の引数にもラベル名が必要になったみたいです。今までは2つめ以降のみラベル名が必要だったので、統一感が出て良いのではないでしょうか。

【エラーメッセージ】
‘PremultipliedLast’ has been renamed to ‘premultipliedLast’
【修正前】
fileprivate let m_bitmapInfo : CGBitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.PremultipliedLast.rawValue)
【修正後】
fileprivate let m_bitmapInfo : CGBitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue) // 3.1
【解説】
‘PremultipliedLast’の先頭が小文字に変更になったとのことです。

【エラーメッセージ】
Value of type ‘String’ has no member ‘stringByAppendingPathComponent’
【エラー箇所】
let paths1 = NSSearchPathForDirectoriesInDomains(
FileManager.SearchPathDirectory.documentDirectory,
FileManager.SearchPathDomainMask.userDomainMask, true)
let path = paths1[0].stringByAppendingPathComponent(“settings.dat”)
【対策】
extension String {
func stringByAppendingPathComponent(_ path: String) -> String {
let nsSt = self as NSString
return nsSt.appendingPathComponent(path)
}
}
【解説】
NSSearchPathForDirectoriesInDomains の戻り値が、NSArray* から [String]に変わってしまったようです。で、stringByAppendingPathComponentはNSStringのメンバなので、呼べないと怒られています。対策として、エクステンションにより既存のStringクラスにstringByAppendingPathComponentを追加し、内部的にNSStringの該当メソッドを呼ぶようにしました。エクステンション素敵。なお、NSStringのメソッドstringByAppendingPathComponentは名前がappendingPathComponentに変更となったようです。

・・・続く

実録! 2年半ぶりのSwiftアプリバージョンアップ 1日目 その2

二度寝から目覚めたら、コンバートが終わっていました!
よかった!

Apple「変換しといたったで。ちゃんとどこ変わったか確認してな」

Apple「ちゃんと見てな。何かトラブっても知らんで」

いや変更箇所多すぎます。いちいち全部見てられないです・・・
「Save」ボタンを押下。

ワーニング2個、エラー51個

エラー増えました。

・・・続く

実録! 2年半ぶりのSwiftアプリバージョンアップ 1日目

2年半前にSwiftで作ったアプリのXcodeプロジェクトを開きます。

上記画像の通り、最終日付は2015年2月18日となっています。
果たして無事に開けるのでしょうか?

気合を入れてもうまく開ける訳ではありません。
無の心境で「xcodeproj」ファイルをダブルクリックします。

出ました!開いた途端にワーニング115個とエラー30個ですっ!
しかしそこはApple様、自動でコンバートしてくれるようです。
コンバートダイアログが出ましたっ!

「Swiftの構文を変換しますか?
ターゲット”Dotter”と”DotterTests”は以前のバージョンのSwiftコードを含みます。
これらのターゲットをSwift3に変換するには”Convert”を選択してください。
この操作は、後でエディットメニューの”Convert to Current Swift Syntax”
から実行できます。」

超気合を入れて”Convert”ボタンを押します!

・・・

・・

8時7分:”Convert”ボタンを押下
8時8分:あれ?これ動いてるのかな?不安になる
8時12分:眠くなってきた
8時19分:二度寝に突入

・・・続く

アプリ「Dotter」の改良

新作アプリ「My Tiny Planet」の制作にあたって、テクスチャ作りに自作アプリ「Dotter」を使っています。

「Dotter」はPNG専用ドット絵エディタです。

久しぶりに使ったら、当時気付かなかった使いにくい点がいくつかあったので改良しようと思っています。

・ペンや消しゴムの”幅”を 1 もしくは 3 もしくは 5 ドットから選べますが、変更するのにいちいち別画面を開かなくてはならないため、ドット絵を描くのと同じ画面で変更出来るようにします。

・グリッド線の表示/非表記も別画面を開かなくてはならないため、これも同じ画面で切り替えられるようにします。

・思った位置にドットが打てない場合があるので、右手で”カーソル”を動かし、左手(二点以上画面をタップ)でそのカーソル位置にドットを打つような、思った位置に超打ち易い描画モードを追加します。

この内容で近々バージョンアップするかも知れません。

パクリアプリ開発日記 118日目 続ゲームのアイデア出し

引き続きゲームのアイデア出しです。

・”生命の種”は、研究所で生産出来る。研究所はクラクラと同じような仕組みで建設やレベルアップが出来る。

・研究所などの建物は”ルピー”で建設出来る。ルピーは採掘場で採れる。採掘場もクラクラと同じような仕組みで以下略

・生命の種やルピーは貯蔵庫で蓄えることが出来る。採掘場もクラクラと同じような仕組みで以下略

★☆★ 次回の作業 ★☆★

プロトタイプの作成

パクリアプリ開発日記 107〜117日目 ゲームのアイデア出し

ゲームの骨格部分がまだ決まっていません。
煮詰まってきたため、アイデアを箇条書きにして、出し切ったらその中から何が良いか選びたいと思います。

・シミュレーションゲームである(SimCityなどに近い)

・マイクラのようなブロックで構成された世界である。ただし、マイクラのようなただっぴろい世界ではなく、特定の大きさを持つ浮遊大陸である。

・植物、昆虫、草食動物、肉食動物などが生息する。これらを個体と呼称する。

・個体は”生命の種”で強化することができる。

・各個体はHPを持っており、植物は光合成で、昆虫や草食動物は植物を食べて、肉食動物は草食動物を食べてHPが増える。

・特定HPを超えると、複製して個体数が増える。

・各個体は、それぞれ動作ロジックを持っている。複製時、まれに一部ロジックが変化する。

・ロジックの変化により、より環境に適応するロジックならばその個体は増え、適さないロジックならばその個体は死滅する。

・自分のお気に入りの個体はストックすることができる。

・ストックした個体を、他人の浮遊大陸に放つことができる。

・他人の浮遊大陸で個体を一定数以上にすると、”生命の種”を奪える。

★☆★ 次回の作業 ★☆★

プロトタイプの作成

ねこマタ入門 第16回 「Front Wall」

アプリ「ねこマタ」は、ニャンコ型のオートマタをロジックで制御するプログラミング風のロジカルパズルゲームです。

来るべきAI時代の到来に備え、将来AIに負けて職を失わないよう、むしろAIを制御できるような立場となるための訓練として、本アプリをお勧めします。

今までの回は以下のリンクからご確認下さい。

第1回 ロジックボードの表示
第2回 ロジックボードの開始
第3回 ロジックボードの編集タイミング
第4回 ロジックボードの停止
第5回 ロジックパネルのセット
第6回 ロジックパネルの接続
第7回 「Walk」
第8回 ステージの選択順序
第9回 「Start」パネルの秘密
第10回 「Start」パネルの秘密2
第11回 休まず歩け
第12回 敵との衝突
第13回 「TurnL」
第14回 汎用性の無い思考
第15回 100倍の差

前回は、条件判断が1つあるだけで、ロジックがどれほど効率的になるかを説明しました。

今回はいよいよ、その条件判断を行う「ジャッジパネル」を使って見ましょう。

「ジャッジパネル」とは、その名の通り、判断を行うパネルです。

何を判断するのでしょうか?

それは、パネルの名前が示しています。「Front Wall」を直訳すると、”前方が壁”です。

ねこマタの向いている方向の目の前の”マス”が壁かどうかで判断します。

Yesならこの処理、Noならこの処理と、処理を分岐することが出来ます。

壁と言いましたが、厳密には壁で無くても、木でもブロックでも、”行けないマス”なら何でも対象です。マップの端でも壁と判断します。

では実際にロジックを組んで見ます。

ステージ4の初期配置を再度確認しましょう。

ねこマタの位置からゴールにたどり着くには、ずーっとまっすぐ歩いて、柵にぶつかりそうになったら左に曲がれば良さそうです。

目の前が壁かどうかで判断する「Front Wall」をベースに、ロジカルに考えれば、

・”目の前が壁”=Yes ならば左に曲がる「TurnL」
・”目の前が壁”=No ならば進む「Walk」

となります。

“目の前が壁”=No ならば進む「Walk」は、柵の直前までと、柵を目の前にして曲がった後と、両方で利用されるのがポイントです。

では「ロジックボード」に「ロジックパネル」を配置して行きましょう。

まず、「Start」をタップし、次に「Start」の右側のパネルをタップします。「Action」か「Judge」の選択が出るので、「Judge」を選びましょう!

そして、「Front Wall」を選択します。

はい、こうなったでしょうか?

次に、「Front Wall」をタップします。

周りのパネルが「YES」の文字とともに赤く光りました。「YES」は、”目の前が壁”の場合に処理を進める場所を決めます。ここでは左上(「Start」パネルからだと右上)をタップしましょう。

続けて、「NO」の文字とともに周りのパネルが青く光りだしました。”目の前が壁”では無い場合の処理を進める場所を決めます。左上はすでに「YES」の場合の処理で確定しているので、光っていません。ここでは左下(「Start」パネルからだと右下)をタップしましょう。

はい、これで分岐処理の処理先が決まりました。赤い矢印は条件に一致する場合、青い矢印は条件に一致しない場合の処理先を指し示しています。

目の前が壁なら曲がりたいので、赤い矢印の場所には「TurnL」を置きましょう。

次に、目の前が壁でなければ進みたいので、青い矢印の先には「Walk」を置きましょう。

完成しました!

では、「再生ボタン」を押して、ロジックを再生してみましょう!

  

ネズミの方が速くて、先にゴールにつきました。ただし、ねこマタの方がHPが高いためネズミを押し退けて、ゴールに着くことが出来ました。

クリアのご褒美として「TurnR」を2枚手に入れました。これで「TurnL」と合わせ、自由自在に動けます!

最後に、ネズミより素早く動けるようにしてみましょう。

現状のロジックだと、「TurnL」と「Walk」パネルから矢印が出ていなく、処理が「Start」パネルに戻ってしまいます。

「Start」パネルに戻ると、1秒余分に時間がかかってしまうんでしたね。

次のようにしてみてください。

まず「TurnL」をタップし、次に「Front Wall」をタップします。そして、「Walk」をタップし、「Front Wall」をタップします。

こうなったでしょうか?これで、処理が「Start」に戻らないため、余計な時間がかかりません。

再生ボタンを押してみましょう。

どうでしょうか?ネズミより素早く動けて、先にゴールできました。これは、ステージ6をクリアするためのテクニックの応用ですね。

さて、1024回にわたって続けてきたねこマタ入門はいかがだったでしょうか。

「ロジックボード」の基本から「アクションパネル」や「ジャッジパネル」まで、一通り学びましたね。これ以降も様々なパネルが出てきますが、恐れることは有りません。パネルの使い方は、敵のロジックを見て学べることも憶えましたね。

各種パネルの説明はこちらにも有りますので合わせてご確認下さい。

最後に、苦労して作成したロジックは、メニュー(画面左上の横棒3つのマーク)の「Logic Board」から、保存や読み込みが手軽に出来ることを憶えておいてください。

では、プログラマーが世界を支配する日を楽しみにしていますね。それではまた。

ヽ(′ー`*ヽ)

ねこマタ入門 完

大手には作れないアプリを(気持ちだけは(๑•̀ㅂ•́)و✧)