獣は月夜に何を見る...

Udemyのセールで買っちゃおう、おすすめコース!

f:id:tukumosanzou:20180824123402j:plain



休みの日の計画は何かありますか?。


休みのは、家から出たくないときもありますよね?。


そんな時は、普段できていなかった学習をしてみてはいかかでしょうか?。


「で、何するの?」ってなりますよね。


ご心配なく!、これがあるんですよ!


Udemyがセール中!

f:id:tukumosanzou:20181124013127p:plain


世界最大級のオンライン学習プラットフォーム Udemy

この夏最大のキャンペーン、夏の学び直しキャンペーンを開催してます(8月31日まで)。

本国アメリカのブラックフライデーに向けてのキャンペーン 「ブラックフライデーセール」中です。

ブラックフライデーセール」続き、サイバーマンデーセール」が、28日16:59まで開催中です。

参考:ブラックフライデーフリークレジットプロモーション


しかも、今コースを購入すると対象コースが1本分無料でもらえる「ブラックフライデーフリークレジットプロモーション」が同時開催中です。



        





Udemy(ユーデミー)については以前、こちらにも書かせてもらいました。

tukumosanzou.hatenablog.com



有料の講座が、1200円からとお安くなっております。


今回は、サイト・アプリどちらから購入しても1200円からとなってます!。


f:id:tukumosanzou:20180824130306j:plain


この価格が、最安値なので普段から学習したいと思っていたものや、Udemy が気になっていた方、ほしいと思ってい講座があった場合は絶好のチャンスだと思いますよ。



金保証もあるから安心!

もし、購入した講座が思っていたのと少しちがうかもと思ったりしたら、30日以内だったら返金してくれる制度があるので安心です。


わたしも、返金してもらったこともあります。



対応も早く、親切ですので大丈夫ですよ。
※ただし、iPhone/iPadアプリからの購入は返金対象外ですので、そこはお忘れなく。



おすすめ講座

web開発


未経験からプロのWebデザイナーになる! 400レッスン以上の完全マスターコース

世界で30万人が受講】フルスタック・Webエンジニア講座(2017最新版)

[HTML/CSS/JavaScript] フロントエンドエンジニアになりたい人の Webプログラミング入門



モバイルアプリ


React Native で iOS / Android アプリ開発をゼロから始めよう!

【iOS12対応】未経験者が有名アプリ開発者になるiOS 12の全て 20個以上アプリをつくりプロになる

Java知識ゼロOK!プロのAndroid開発者になるためのマスターコース


写真


業界最先端の動画制作テクニックを制覇!Adobe Premiere Pro 完全版

【2日で撮れる】超簡単・図解でスッキリ!!一眼レフカメラ入門


言語


高卒ニートの僕が6ヶ月でTOEIC960点取った具体的な勉強法【実践編】

「話すために聞く」英会話コース 【フリートークマスター】


デザイン


【超初心者向け】Blenderでキャラクターをモデリングしてアンリアルエンジンで動かす講座【ハンズオン】

After Effects Class 初めてでも安心!現役クリエイターが教える動画コンテンツ制作術


まとめ




ご紹介したものは、ごく一部です。


まだまだ、たくさんの講座があります。


あなたの好みの、さがしていた講座が見つかるかもしれません。


ぜひ、いちど世界最大級のオンライン学習サイトUdemy を利用してみてください。


        


【iPhone】Swiftでアプリ開発 | SimpleButtonApp

f:id:tukumosanzou:20180815202335j:plain

開発環境
iOS 12
Xcode 10
Swift 4.2



f:id:tukumosanzou:20180819003000g:plain


動画で確認できます





プロジェクトをつくる

XcodeでSingle View Appを選び、新しいプロジェクトをつくります。


SimpleButtonAppとでもしておいてください。


iPhoneアプリはUIKitを使用する場合は、Single View Appを使用することが多いです。



UIKitとはボタンやテキストや画像などを画面上に作成・配置できるものと思ってください。




f:id:tukumosanzou:20180815203245p:plain
f:id:tukumosanzou:20180815203322p:plain

Product Name プロジェクト名になります。
Team Apple IDを入力します、これがないとエラーがでます。
Organization Name 任意の名前でよいです。
Organization Identifier アプリの公開に必要な組織の識別名ですが公開するわけでなければ適当で良いです、com. 〜とするのが流行り?のようです。
Bundle Identifier Organization IdentifierとProduct Nameで自動で作成されます。
Language Swiftを選びます、Objective-Cも選べます。
Use Core Data アプリ内のデータベースを扱うためのフレームワークです、データベースを使わなければ必要ありません。
include Unit Tests 機能テスト用のファイルが作成されますが、初心者のうちは必要ありませんのでオフにしましょう。
include UI Tests UIテスト用のファイルが作成されますが、初心者のうちは必要ありませんのでオフにしましょう。




下の図のView as:の部分をクリックして、デバイスiPhone Xに変更します。


f:id:tukumosanzou:20180815223600j:plain


画像をAssetsフォルダにコピーする

Kenney.nlから使用する画像をダウンロードします。


ダウンロードしたファイルのPNGフォルダにpngファイルがありますので、今回はそこから使用します。


dog.pngとduck.pngを下の図の枠内にドラッグしてコピーします。


f:id:tukumosanzou:20180817131958j:plain


ボタンとラベルを追加する

Buttonを追加します。

f:id:tukumosanzou:20180816011227p:plain

①右上のアイコンをクリックして、ライブラリーパネルを表示します。
Xcode 10ではXcode9までとは表示の仕方が変更されてます。

②buttonで検索して、Buttonを View Controller上にドラッグして追加します。


Buttonを追加すると、黄色い三角マークでエラーが出た場合、これはButtonが画面のレイアウトで位置が指定されていないので、デバイスの種類が変わると位置が移動して、他のオブジェクト(表示するであろうテキストやボタンなど)と重なってしまいますよと警告が出ているわけです。


これを回避するには、Buttonの位置を指定する必要があります。


以下の図のように、Buttonを選択して4つのアイコンの左から2番目をクリックすると、ポップアップでAdd New Alignment Constraintsと出ますので、下の2つの項目をチェックしてください。


f:id:tukumosanzou:20180817135151j:plain



Buttonが画面の中央の正確な位置に表示されます、そうすれば警告マークが消えると思います。


ラベル(Label)を追加します。

ライブラリーパネルでLabelを検索し、View Controller上に追加します。

右のサイドバーのattributes inspectorで、Textを”START VIEW”にFontをBoldに、文字サイズを38に、Alignmentをセンタリングにします。

Labelを追加すると黄色い三角の警告がでます。

これもボタンと同じく、View ontroller上の位置が設定されていないためです。

Add New Alignment Constraintsを表示して、今回はHorizontally is Containerを選択します、そうするとセンタリングされます。

次にSTART VIEWのテキストをクリックして、CTRキーを押しながら上方向にドラッグして離すとプロパティーがでますので、Top Space to Safe Areaと選択します。

これで、画面上部からの位置が決まりました。

警告が消えると思います。

ボタンに画像を配置する

Buttonに現時点でテキストが表示されていますが、これを画像に変更します。


追加したButtonを選択して、右側のattributes inspectorでButtonのテキストを削除します。


続いて、imageの項目でタブをクリックしてDogを選択すると、ButtonにDog.pngが追加されます。


f:id:tukumosanzou:20180817132302p:plain


2つ目の画面を作成する

Dogの画像をクリックしたら、画面が切り替わるようにします。


新しい画面用のView Controllerを追加します。


下の図のように、libraryパネルからView Controllerをストーリーボード上にドラッグして追加します。


f:id:tukumosanzou:20180817140508p:plain


このように、なにもまだ追加されていないView Controllerが追加されます。

f:id:tukumosanzou:20180817140906j:plain


ここでも、ボタンとラベルを追加します。


方法は、最初のView Controllerと同じです、今度は画像はDuckです。


それでもまだ、黄色い三角マークの警告がでると思います。


今回はなぜかというと、新たにView Controllerを追加したのですが、要するに最初の画面と次の画面との接続ができていないと警告が出ているわけです。


これを回避するには、下の図のようにDocの画像をクリックしてCTRキーを押しながら次の画面にドラッグします。



f:id:tukumosanzou:20180817142032j:plain


プロパティーが出ますので、Action SegueのPresent As Popoverを選択します。


これは、画面が切り替わる時のアクション(アニメーション)を設定してます。


今回は、下から次の画面が飛び出すアクションです。


2つ目のView Controllerの処理を書くファイルをつくる

ScondViewController.swiftを作ったらView Cotrollerと紐つけます。

storyboard上の2つ目のView Controllerをクリックして、Identity inspectorのCustom ClassをSecondViewControllerにします。

@IBOutletと@IBActionを定義する

@IBOutletを設定します。
@IBOutlet --- View ControllerとSwiftコードのプロパティ(変数)を接続するもの。


ラベルをクリックして、CTRキーを押しながらドラッグします。

f:id:tukumosanzou:20180819142648j:plain




プロパティがでますので設定します。


f:id:tukumosanzou:20180819143319j:plain

Connection --- 変数との接続ですから、Outletのままです。
Object --- この変数がある場所(View Controller)がどこなのか。
Name --- この部分に変数の名前を入力します。
Type --- この変数のタイプ
Storage --- 今回はStrongでよいです。


StrageがStrongかWeakなのかについては、この辺りが参考になるかもしれません。

IBOutletにおける weak vs strong

Swiftの循環参照問題におけるunownedとweakの使い分けについて




@IBActionもやり方は同じです。
@IBAction --- View ControllerとSwiftコードのアクション(関数)を接続するもの。


Duckの画像をクリックしてドラッグするとプロパティが出ます。

f:id:tukumosanzou:20180820222054j:plain

Nameの部分にアクション(変数)名を入れます。

他の部分は、そのままで大丈夫です。

Swiftコードを書く。

varは変数を宣言するのに使います。


var changeLabel: UILabel!のchangeLabelの部分が変数名になります。


UILabel!は、その変数のなかに入れるもののタイプ(種類)が何であるかを表します、変数名と「:」で区切ります、後についている「!」は必ずそのタイプである事を指定しているような意味です。


Swiftでは、この部分がすこし理解がムズカシイ部分ですので、最初のうちはそのようなものとおもっててください。


この辺りが参考になるかもしれません。
どこよりもわかりやすいSwiftの”!”と”?” - Qiita




SecondViewController.swift

import UIKit

class SecondViewController: UIViewController {
    
    //START VIEWと紐ついた変数
    @IBOutlet var changeLabel: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()

        
    }
    
    //Buttonと紐ついた関数。
    @IBAction func clickDuck(_ sender: Any) {
        
        //テキストを入れ替える
        changeLabel.text = "Duck! Duck!"
        
    }
    
}





UIKit | Apple Developer Documentation

UIButton - UIKit | Apple Developer Documentation





いかがだったでしょうか?。

ストーリボードとSwiftコードを紐つけてできる、かんたんなアプリケーションのつくりかたでした。



【iPhone】Swiftでアプリ開発 | UITabBarApp

f:id:tukumosanzou:20180811115351j:plain



今回は、TabBarControllerという下側にナビゲーションバーがでてくるやつです。



ナビゲーションのアイコンをクリックする事で画面が切り替わるようになります。



iOSアプリケーションの開発では、ストーリーボードでかんたんにつくれます。

開発環境
iOS 12
Xcode 10
Swift 4.2


f:id:tukumosanzou:20180822091017g:plain


動画で確認できます








ストーリーボードでかんたんにつくれます

Xcodeでsingle viewの新しいプロジェクトをつくります。


プロジェクト名は、UITabBarAppとでもしてしださい。


Main.storyboardを開いて、既にあるView controllerを図のように選択して削除してください。


f:id:tukumosanzou:20180811085832p:plain

画面の部分をクリックで選択して、deleteキーで削除する。


グレーになってView Controllerと文字がでたら、もういちどdeleteキーを押すと削除できます。


f:id:tukumosanzou:20180811090523p:plain

すると、Main.storyboardが真っ白になります。


f:id:tukumosanzou:20180811090657p:plain


右下のObject LibraryからTab Bar Controllerを選択して、Main.storyboard上にドラッグします。


f:id:tukumosanzou:20180811091713p:plain


追加できれば、下のような図が表示されます。


f:id:tukumosanzou:20180811092408p:plain


Tab Bar Controllerには、初期設定として2つのBar Itemで構成されています。


右側に矢印で別れて2つのView Controllerが表示されていますが、これが各Bar ItemごとのView Controllerになります。


ここで、いちどプロジェクトをビルドしてみてください(今回はiPhone xで行います)。


Failed to instantiate the default view controller for UIMainStoryboardFile Main-perhaps the designnated entry point is not set?


要するに、初期のView Controller(最初に表示する画面のこと)が設定されていないためです。


きちんと設定されて入れば下のような矢印が表示されていないといけません。


f:id:tukumosanzou:20180811093845p:plain


今の段階では、表示されていないのでこれを設定したいと思います。


Tab Bar Controllerをクリックして、ユーティリティパネルの属性オプション(attributes inspector)に移動します。


f:id:tukumosanzou:20180811094811p:plain


initial View Controller(最初に表示される画面のこと)の部分がチェックされていないので、図のようにチェックしてください。


f:id:tukumosanzou:20180811094854p:plain


プロジェクトをビルドすると、今度は正常に動作すると思います。


※実は、この→(矢印)はドラッグして移動することができます。

Main.storyboardを見ると、Tab Bar controllerには、各タブにひとつずつ、View Controllerがあらかじめ追加されていますが、Tab Bar ControllerからタブごとのView Controllerが矢印でつながっていますが、このつながりを「Segues(セグ)」といいます。


ここでは「Segues(セグ)」について詳細は省きますがそういうものだと覚えておいてください。


つぎに、2つの新しいView ControllerをTab Bar Controllerに追加したいと思います。


2つあるView Controllerの下側の分を図のように移動して、さらに2つのView ControllerをMain.storyboardに追加します。


f:id:tukumosanzou:20180811101559j:plain


下の図のようになればOKです。


f:id:tukumosanzou:20180811101646p:plain


Tab Bar Contrpllerをクリックしてcotrolキーをおしながら追加したView Controllerまでドラッグすると、下の図のようなオプションが出ますのでRelationship SegueのView Controllerを選択します。


f:id:tukumosanzou:20180811103003p:plain


これを、2つのView Controller毎に行うと、図のようになります。


「Segues(セグ)」が正しく設定できれば、Tab Bar Controllerと2つのView Controllerの間に矢印ができていると思います。


f:id:tukumosanzou:20180811103213p:plain


Main.storyboardの表示がiPhone xの外観になっていないときはView as:の部分をクリックするとデバイスのアイコンが出るのでiPhon xを選択すると切り替わります。


うまくいかないときは、別のデバイスを選択してからiPhone xを選択するとよいです。


f:id:tukumosanzou:20180811104321p:plain


追加した2つのView Controllerは属性インスペクタ(attributes inspector)からテキストとアイコンをカスタマイズできます。


Object libraryからTab Bar ItemをView Controllerのタブのアイコンまでドラッグします、緑色のプラスマークが表示されたらOKです。


f:id:tukumosanzou:20180811105514p:plain


View ControllerのTab Barを選択して、属性インスペクタ(attributes inspector)からSystem Itemをクリックするとアイコンのタイプが選べるのでDownloadsとします。


f:id:tukumosanzou:20180811112303p:plain


View ControllerのViewの部分をクリックして属性インスペクタ(attributes inspector)のBackgroundで背景色が変更できます。


f:id:tukumosanzou:20180811112547p:plain


テキストも変更できます。


下の図のように左端のアイコンをクリックして属性インスペクタ(attributes inspector)のTitleでView Controllerのタイトルが変更できます。


f:id:tukumosanzou:20180811112651p:plain


アイコンのカスタマイズとテキストと背景色の変更を残りのView Controllerにも行ってください。


残りは「Contacts」、「 Favorites」、「 Search」です、背景色はなんでもよいです。


完成すると、図のようになると思います。


f:id:tukumosanzou:20180811113635p:plain


プロジェクトをビルドすると図のようになります。


f:id:tukumosanzou:20180808120727j:plain


ストーリーボードではコードを書くこともなく、ここまでできてしまいます、慣れるまでXcodeはムズカシイ感じがしますが、いちど慣れてしまうとかなり楽できますね。


ストーリーボードを使った、4つのタブを持つかんたんなアプリケーションをつくりました、いかがだったでしょうか?。


今回は、これにて終了です。





【iPhone】Swiftでアプリ開発 | SKShapeNodeAnimation

f:id:tukumosanzou:20180826223315j:plain




今回は、SkShapeNodeをつかった、かんたん泡泡なアニメーションの作り方です。


開発環境
iOS12
Swift 4,2
Xcode 10 Beta


f:id:tukumosanzou:20180826223441g:plain


プロジェクトをつくります

新しいプロジェクトをつくります。


プロジェクト名はSKShapeNodeAnimationとでもしましょう。


GameScene.swiftを開いて、余分なコードを削除して以下のようにします。


import SpriteKit

class GameScene: SKScene {
    
    override func didMove(to view: SKView) {
        
    }
    
    
    override func update(_ currentTime: TimeInterval) {
       
    }
}




プロジェクト作成時にintegrate GamePlayKitのチェックを入れているとimport GamePlayKitの一文があると思いますが、今回は使わないので削除して良いです。



では、先ず背景をつくるcreateBackground()を作成します。

class GameScene: SKScene {
    
    //背景用の変数を作成。
    var background: SKShapeNode!
    
    override func didMove(to view: SKView) {
        
   //関数の呼び出し
        createBackground()

    }
    
    
    func createBackground() {

        //画面と同じ大きさの四角形を作成する。
        let rect = CGRect(x: frame.minX - 1, y: frame.minY - 1, width: frame.width, height: frame.height + 2)

        //四角形をSKShapeNodeとして登録する。
        background = SKShapeNode(rect: rect, cornerRadius: 0.01)

        //四角形の面の部分の色を決める。
        background.fillColor = SKColor.darkGray

        //z軸方向の重なり順を決める。
        background.zPosition = 2

        //GameSceneに追加する。
        self.addChild(background)
    }
}




特に変わったことはしていません、今回は画像を使わずに背景となる四角形を作成するようにしました。



frame.midXは画面サイズのx軸のちょうど真ん中、frame.heightは画面サイズの高さの値をそれぞれ表してます。



frame.minX - 1、frame.minY - 1としているのは、そのままだと上下に少し隙間ができるため1px分下げて下側の隙間を埋めて、その分frame.height + 2で上の隙間を埋めてます。



ほかにも、midY minX minY maxX maxY width などがあります。



SKShapeNode - SpriteKit | Apple Developer Documentation
CGRect - Core Graphics | Apple Developer Documentation



シミュレーターで見てみると以下のようになってれば大丈夫です。
f:id:tukumosanzou:20180806072742j:plain

泡をつくる、createBubble()を作成します。



createBackground()の直前に追加します。

class GameScene: SKScene {
    
    var background: SKShapeNode!
    
    override func didMove(to view: SKView) {
        
        createBackground()
    }
    
    
    override func update(_ currentTime: TimeInterval) {

        //関数の呼び出し。
        createBubble()
    }
    
    
   func createBubble() {
        let bubble = SKSpriteNode(color: SKColor.white, size: CGSize(width: 10, height: 10))
        let startPosition = CGPoint(x: frame.width / 2, y: frame.minY - bubble.size.width)
        bubble.position = startPosition
        bubble.zPosition = 3
        background.addChild(bubble)
    }
    
    
    func createBackground() {
    }
}



関数の呼び出しはoverride update()の中で行います。


これでシミュレーターで見てみると特に変わっていないのですが、それは泡の発生する位置が画面の外になっているからです。

stratPosition- bubble.size.widthで泡の大きさ分画面の外のなっているので試しにその部分を消してみると画面の一番下に出てくると思います。



floatBubbles()を作ります。

いまのままだと、同じ位置にしか泡ができないので徐々に浮上するようにしたいと思います。

呼び出しを、createBubble()の直後に追加します。

floatBubbles()createBubble()の直前に追加します。

class GameScene: SKScene {
    
    var background: SKShapeNode!
    
    var activeBubbles: [SKSpriteNode] = []
    
    override func didMove(to view: SKView) {
        
        createBackground()
    }
    
    
    override func update(_ currentTime: TimeInterval) {
        createBubble()

   //関数の呼び出し。
        floatBubbles()
    }
    
    
    func floatBubbles() {

        //for inでループさせる。
        for i in background.children {

            //泡をx軸方向に少しだけ動かすための値。
            let xOffset = CGFloat(arc4random_uniform(20)) - 10.0

    //泡をy軸方向に動かすための値。
            let yOffset = CGFloat(20.0)

            //移動先の値を決める
            let newLocation = CGPoint(x: i.position.x + xOffset, y: i.position.y + yOffset)

            //移動先へ0.2秒間隔で動かす。
            let newAction = SKAction.move(to: newLocation, duration: 0.2)

            //アクションを実行する。
            i.run(newAction)
         }
    }

    func createBubble() {




arc4random_uniform()は乱数を生成します。

CGFloat(arc4random_uniform(20))では、現在表示されている泡を上に浮かべるために、フレームごとにxの値に0〜19の幅で乱数をつくります。


CGFloat(arc4random_uniform(20)) - 10.0は、結局のところ-10~9の間で乱数をつくり、それを泡のx軸方向の値に足すことでx軸方向の位置を変化させるという事になります。

つまり、泡が左右に動くことになります。

yの位置に関しては20を追加する事で、一定間隔で上に浮かぶようにします。

これで、ゆらゆらとしながら泡が浮かんでいくように見えます。




removeExcessBubbles()を作ります。

最後に、画面の上部に到達した後に泡を取り除かなければ、泡がいつまでも増え続けるのは、好ましくないので、画面外に出たら削除するようにしたいと思います。

以下のようにします。

呼び出しをupdate()内のfloatBunnles()の直後に、関数removeExcessBubbles()func floatBubbles()の直前に追加します。




最後に、全体のコードで表示します。

import SpriteKit

class GameScene: SKScene {
    
    var background: SKShapeNode!
    
    override func didMove(to view: SKView) {
        
        createBackground()
    }
    
    
    override func update(_ currentTime: TimeInterval) {
        createBubble()
        floatBubbles()

        //関数呼び出し。
        removeExcessBubbles()
    }
    
    
    func removeExcessBubbles() {

        //for inでループさせる。
        for i in background.children {

            //画面の最上部より上になったら
            if i.position.y > frame.maxY {

                //泡を削除する。
                i.removeFromParent()
            }
        }
    }


    func floatBubbles() {
        for i in background.children {
            let xOffset = CGFloat(arc4random_uniform(20)) - 10.0
            let yOffset = CGFloat(20.0)
            let newLocation = CGPoint(x: i.position.x + xOffset, y: i.position.y + yOffset)
            let newAction = SKAction.move(to: newLocation, duration: 0.2)
            i.run(newAction)
         }
    }

    
    func createBubble() {
        let bubble = SKSpriteNode(color: SKColor.white, size: CGSize(width: 10, height: 10))
        let startPosition = CGPoint(x: frame.width / 2, y: frame.minY - bubble.size.width)
        bubble.position = startPosition
        bubble.zPosition = 3
        background.addChild(bubble)
    }
    
    
    func createBackground() {
        let rect = CGRect(x: frame.minX - 1, y: frame.minY - 1, width: frame.width, height: frame.height + 2)
        background = SKShapeNode(rect: rect, cornerRadius: 0.01)
        background.fillColor = SKColor.darkGray
        background.zPosition = 2
        self.addChild(background)
    }
}







今回は、基本的なSpriteKitでのアニメーションの紹介でした。 では、また次回。





今のスマホ時代、ブルーライト対策は必要!

わたしも、「pcめがね」を使ってます。 使わないと、違いはわからない!。 「J!NS SCREEN」、デザインがおしゃれでわたしは好きです。


JINS PC」は、疲れと戦うメガネ「JINS SCREEN」に生まれ変わりました! 眠りが浅い、寝つきが悪い、その原因はブルーライトかもしれません。ブルーライトを防いで、快適な眠りを体験してみませんか?





【 iPhone 】Swiftでアプリ開発 | UserNotifications

ローカル通知の覚書

iOSにおいて、アプリが起動していなくても、スケジュール等を通知してくれるアレ。

Main.storyboardにNavigation Controllerを追加。


f:id:tukumosanzou:20180803014128j:plain

ViewController.swift

import UIKit
import UserNotifications

//受信通知と通知関連の操作を処理するためのインタフェース。UNUserNotificationCenterのdelegateプロパティを使うのに必要。
class ViewController: UIViewController, UNUserNotificationCenterDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()

        //ナビゲーションバーの左ボタンを追加する。
        navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Register", style: .plain, target: self, action: #selector(registerLocal))

        //ナビゲーションバーの右ボタンを追加する。
        navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Schedule", style: .plain, target: self, action: #selector(scheduleLocal))
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    
    @objc func registerLocal() {

        //通知センターの取得、アプリまたはアプリ拡張の共有ユーザー通知センターオブジェクトを返します。
        let center = UNUserNotificationCenter.current()

        //ユーザーのデバイスにローカルおよびリモートの通知が配信されたときに、ユーザーと対話するための認可を要求します。
        //options: UNAuthorizationOptions = []
        center.requestAuthorization(options: [.alert, .badge, .sound]) { (granted, error) in
            if granted {
                print("Yay!")
            } else {
                print("D'oh")
            }
            
        }
    }
    
    @objc func scheduleLocal() {
        registerCategories()
        
        let center = UNUserNotificationCenter.current()

        //すべての保留中の通知要求のスケジュールを解除します。
        center.removeAllPendingNotificationRequests()

        //通知の編集可能なコンテンツ。
        let content = UNMutableNotificationContent()

        //通知アラートに表示するためのローカライズされた文字列を返します。
        content.title = NSString.localizedUserNotificationString(forKey: "Late wake up call", arguments: nil)
        content.body = NSString.localizedUserNotificationString(forKey: "The early bird catches the worn, but the second mouse gets chees.", arguments: nil)

        //通知のタイプを表すカテゴリオブジェクトの識別子。
        content.categoryIdentifier = "alarm"

        //通知に関連付けられたカスタム情報の辞書。
        content.userInfo = ["customData": "fizzbuzz"]

        //通知が配信されたときに再生されるサウンド。
        content.sound = UNNotificationSound.default()

        //このオブジェクトの作成に使用された日付コンポーネント。
        var dateComponents = DateComponents()
        dateComponents.hour = 10
        dateComponents.minute = 30

        //指定された時間が経過した後に通知を配信するトリガー条件。
        let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 5, repeats: false)

        //特定の日時に通知を配信するトリガー条件。
//        let trigger = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: true)

        //ローカル通知をスケジュールする要求。通知の内容と配信のトリガー条件が含まれます。
        let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: trigger)
        center.add(request)
    }
    
    //func scheduleLocal()内部で最初に呼び込む。
    func registerCategories() {
        let center = UNUserNotificationCenter.current()

        //受信通知と通知関連の処理を処理するオブジェクト、UNUserNotificationCenterDelegateを継承する必要がある。
        center.delegate = self

        //配信された通知に応答して実行するタスク。
        let show = UNNotificationAction(identifier: "show", title: "Tell me more ", options: .foreground)

        //あなたのアプリがサポートしている通知のタイプとそれを使って表示するカスタムアクション。
        let category = UNNotificationCategory(identifier: "alarm", actions: [show], intentIdentifiers: [])

        //アプリケーションの通知タイプと、それがサポートするカスタムアクションを登録します。
        center.setNotificationCategories([category])
    }
    //デリゲートに、配信された通知に対するユーザーの応答を処理するように要求します。
    func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
        let userInfo = response.notification.request.content.userInfo
        
        if let customData = userInfo["customData"] as? String {
            print("Custom data received: \(customData)")

            //ユーザーが選択したアクションの識別子文字列。
            switch response.actionIdentifier {

            //ユーザーが通知インターフェイスを右にスライドさせてからアプリを開いたことを示すアクション
            case UNNotificationDefaultActionIdentifier:
                
                print("Default identifier")
              
            //ユーザーが通知インターフェイスを左にスライドさせてからアプリを開いたことを示すアクション
            //UNUserNotificationActionのidentifierプロパティの値
            case "show":
                
                print("Show more information...")
                
            default:
                break
            }
        }

        //終了時には、必ず補完ハンドラを呼び出します。
        //ユーザーの応答を処理した後、このブロックを実行して、完了したことをシステムに知らせる必要があります。
        completionHandler()
    }
}




UNUserNotificationCenter | Apple Developer Documentation

 func registerCategories() {
        let center = UNUserNotificationCenter.current()

        //受信通知と通知関連の処理を処理するオブジェクト、UNUserNotificationCenterDelegateを継承する必要がある。
        center.delegate = self

delegate = self とするためにはUNUserNtificationCenterDelegateをclassに追加する必要がある。

import UIKit
import UserNotifications

//受信通知と通知関連の操作を処理するためのインタフェース。UNUserNotificationCenterのdelegateプロパティを使うのに必要。
class ViewController: UIViewController, UNUserNotificationCenterDelegate {
 //ユーザーが通知インターフェイスを右にスライドさせてからアプリを開いたことを示すアクション
            case UNNotificationDefaultActionIdentifier:
                
                print("Default identifier")
              
            //ユーザーが通知インターフェイスを左にスライドさせてからアプリを開いたことを示すアクション
            //UNUserNotificationActionのidentifierプロパティの値
            case "show":
                
                print("Show more information...")



右スライドでOpenをクリック。 f:id:tukumosanzou:20180803021934j:plain コマンドパレットには以下のように表示される。 f:id:tukumosanzou:20180803024252j:plain



左スライドでViewをクリック。 f:id:tukumosanzou:20180803022005j:plain 表示がこうなるので、「Tell me more」をクリック。 f:id:tukumosanzou:20180803025056j:plain コマンドパレットには以下のように表示される。 f:id:tukumosanzou:20180803025125j:plain




@objc func registerLocal() {

        //通知センターの取得、アプリまたはアプリ拡張の共有ユーザー通知センターオブジェクトを返します。
        let center = UNUserNotificationCenter.current()

        //ユーザーのデバイスにローカルおよびリモートの通知が配信されたときに、ユーザーと対話するための認可を要求します。
        //options: UNAuthorizationOptions = []
        center.requestAuthorization(options: [.alert, .badge, .sound]) { (granted, error) in
            if granted {
                print("Yay!")
            } else {
                print("D'oh")
            }
            
        }
    }

UNUserNotificationCenter | Apple Developer Documentation

あなたのアプリやアプリの拡張子のすべての通知関連行動を管理するために共有UNUserNotificationCenterオブジェクトを使用します。

requestAuthorization(options:completionHandler:) | Apple Developer Documentation

Parameters options --- UNAuthorizationOptionsを継承。 completionHandler granted error ローカルおよびリモートの通知がユーザーのデバイスに配信されたときに、ユーザーと対話するための承認を要求します。




//func scheduleLocal()内部で最初に呼び込む。
    func registerCategories() {
        let center = UNUserNotificationCenter.current()

        //受信通知と通知関連の処理を処理するオブジェクト、UNUserNotificationCenterDelegateを継承する必要がある。
        center.delegate = self

        //配信された通知に応答して実行するタスク。
        let show = UNNotificationAction(identifier: "show", title: "Tell me more ", options: .foreground)

        //あなたのアプリがサポートしている通知のタイプとそれを使って表示するカスタムアクション。
        let category = UNNotificationCategory(identifier: "alarm", actions: [show], intentIdentifiers: [])

        //アプリケーションの通知タイプと、それがサポートするカスタムアクションを登録します。
        center.setNotificationCategories([category])
    }

UNNotificationAction | Apple Develper Documentation

UNNotificationActionオブジェクトを使用して、配信された通知に応じてアプリケーションが実行できるアクションを定義します。

あなたのアプリがサポートするアクションを定義します。

たとえば、ミーティングのアプリケーションは、ミーティングの招待を受け入れるか拒否するかのアクションを定義します。

アクションオブジェクト自体には、アクションボタンとボタンの外観に表示するタイトルが含まれています。

アクションオブジェクトを作成したら、それらをUNNotificationCategoryオブジェクトに追加し、カテゴリーをシステムに登録します。

UNNotificationActionOptions | Apple Developer Documentation

.foreground --- このアクションにより、アプリはフォアグラウンドで起動します。

ユーザーがこのオプションを含むアクションを選択すると、システムはアプリケーションをフォアグラウンドに持ち込み、ユーザーに必要に応じてデバイスのロックを解除するように要求します。

このオプションは、ユーザーがあなたのアプリをさらに操作する必要がある操作に使用します。

単にこのオプションを使用してアプリケーションをフォアグラウンドに持ってこないでください。

Udemy(ユーデミー)とは?評価と評判 | お得な使い方まとめ!

f:id:tukumosanzou:20180801152711j:plain
更新日:2021-5-15

機械学習」を学ぶのにお世話になった、動画学習サービスのUdemy (ユーデミー)についてわかりやすく紹介させていただきます。

けっ..けっ..けっ..、決して流行りにのったわけではないですよ。


OpenCV」っていう、画像認識のライブラリが使いたかったんです。


ま、それはさておき

何か技能を習得したいと思ったとき、手軽に始められたら良いのになと思いますよね?。

そんな望みをかなえられるのが、Udemy(ユーデミー)なのです!。


Udemy (ユーデミー)をうまく使えば、プロ並みの技術を習得することもできるんです。


実際、私もWebデザイン、Webアプリケーション、データサイエンスを学ぶことができました、もちろん格安で!


そこでこの記事は、Udemy (ユーデミー)とはどういったサービスなのか・どういった使い方がいいのか等をまとめてみたいと思います。


           


この記事の目次

Udemy(ユーデミー)はどんなサービス?|料金は?

世界最大級のオンライン学習プラットフォームである「Udemy(ユーデミー)」です。 米国シリコンバレー発祥のUdemyは、学びたい人と教えたい人をつなぐCtoCの学習マーケットプレイスです。

なんだか、イマイチわからない人もいるかとおもいます。

「オンライン学習プラットフォーム...、CtoCの学習マーケットプレイス...」

?... 結局、なんのことかな?

大ざっぱに言うと、インターネットを使って動画でいろんな教材を学習しよう、ということです。

少し前までは、本や学習教室でしか学べなかった様々なものが、場所に関係なく学べるということですね。

Udemy(ユーデミー)で何が学べるのか



開発
ビジネススキル
ITとソフトウェア
仕事の生産性
パーソナルライフ&ファミリー
デザイン
マーケティング
趣味・実用・ホビー
写真
ヘルス&フィットネス
講師向けトレーニン
音楽
学問・教養
言語
入試・資格

現時点で、カテゴリーとしては上記のようになってます、各カテゴリーごとにはサブカテゴリーが存在します。

例えば、「開発」のカテゴリーではさらに

全ての開発
ウェブ開発
モバイルアプリ
プログラミング言語
ゲーム開発
データベース
ソフトウェアテスト
ソフトウェアエンジニアリング
開発ツール
Eコマース

となってまして、その中の「ウェブ開発」だとさらに

全てのウェブ開発
JavaScript
Angular
React
Node.Js
CSS
PHP
HTML
Reduxフレームワーク

と言語ごとにカテゴリーが存在します。

ですが、検索機能がありますのでキーワードで検索することもできるようになってます。


Udemy(ユーデミー)の料金プラン

気になる価格についてですがUdemy(ユーでミー)では講座ごとに価格が違います。

無料のものから2〜3万円ぐらいまで、いろいろです。

ですが、Udemy (ユーデミー)で講座の動画を購入すると料金がかかるのは最初だけで、あとは購入した動画に関しては無制限に視聴することができるので月払いで利用するわけではないので、お得だと思います。


Udemy(ユーデミー)のおすすめ動画

最近流行りの「データサイエンス(機械学習)」関連もありますね。

「データサイエンス」の基礎から学べるコースです。 とにかく長いですので、少しづつやるのがコツです。
世界で34万人が受講】データサイエンティストを目指すあなたへ〜データサイエンス25時間ブートキャンプ〜

機械学習ライブラリの「scikit-learn」を使って学習しますが、できれば書籍等で基礎学習してからの方が理解しやすいと思います。
Pythonで機械学習:scikit-learnで学ぶ識別入門

こちらも、初学者に人気のコースです。
機械学習」の初歩を数学を交えて教えてくれます。
スタートダッシュにはもってこいかもです。
【キカガク流】人工知能・機械学習 脱ブラックボックス講座 - 初級編 -

こちらは、上の続きの中級編です。 「初級編」は単純な単回帰でしたが、こちらは変数が複数ある重回帰という、ちょっとレベルが上の分析方法を学べます。
【キカガク流】人工知能・機械学習 脱ブラックボックス講座 - 中級編 -

Pythonやるなら、酒井さんというシリコンバレーで現役のエンジニアのコースが人気です。
基礎からわかりやすく説明してくれるので、最近話題のコースです。
現役シリコンバレーエンジニアが教えるPython 3 入門 + 応用 +アメリカのシリコンバレー流コードスタイル

最近話題の「データサイエンス(機械学習)」関連が日本語で学べる、質の高い動画をいくつか紹介しましたが、これ以外にもたくさんあるのでチェックしてみてください。

人気のあるものには「ベストセラー」のタグがついています、一つの目安にはなると思います。



Udemy(ユーデミー)のメリット|評判と評価は?

f:id:tukumosanzou:20180801193815j:plain

・内容が思ったより、しっかりしている
・価格が安い
・いろんなジャンルがある
・本を買うより安く、それ以上の内容のものが得られる
・動画なので覚えやすい

比較的、良い印象を受けました。

Udemy(ユーデミー)は動画の内容のクオリティが高いです。

動画には購入者がレビューやコメントができるシステムで評価する制度がキチンとあり、講師の方も売り上げを上げるためにはいい加減なものをだせないようになってます。

こうしたレビューやコメントはだれでも見れますので、購入するときの目安にもなり安心です。

スマートフォンタブレット用のアプリがある。

Udemy (ユーデミー)ではスマートフォンタブレット用にアプリケーションが準備されていますiPhoneandroid共にありますのでご安心を!

このアプリを使うと、購入した動画をダウンロードしてインターネット環境の無い場所でも視聴できます。

休憩の時間や、ちょっとした移動時間に学習が可能です。




Udemy(ユーデミー)のデメリット&注意点

f:id:tukumosanzou:20180801194224j:plain

動画には翻訳機能がない?

Udemy (ユーデミー)は米国発のサービスなので、まだまだ日本語での動画が少ないです。


だからと言って、質が悪いのではなく日本の場合は逆に専門家の方がほとんどですので、クオリティは高いものが多いです。


動画には字幕機能はあるのですが、翻訳はできないので英語の場合はそのまま英語でしか利用できません。


※現時点では、自動翻訳による自動生成字幕で、英語音声の動画に関しては日本語が表示できるようです。


参考: 自動生成字幕に関するよくある質問


でも動画とは、見て学習するわけですから大抵は大丈夫だと思います。


実際、私も困ることはほとんどありませんでした、いざとなれば字幕をgoogle翻訳でっていうのも、今はできますからね。





Udemy(ユーデミー)を格安で使う方法!

クーポンを利用するとお得に購入できる!



Udemy (ユーデミー)で動画を購入すると、講師から不定期にクーポンコードがメールで送られてきます、もちろんその講師が作成した動画にしか使えませんがかなり安く買えたりするので欲しい動画のときは利用すると良いですね。


または、Udemy(ユーでミー)のことを取り扱ったりしたWebやテック系のサイトなどでクーポンコードを発行してくれてたりすることもあります。


情報は常にチェックしておくと、ラッキーなことがあるかもしれませんよ。




キャンペーン・セールを利用するとお得に購入できる!



Udemy (ユーデミー)では、かなり頻繁にキャンペーン・セールが行われます。

Webサイト版・アプリ版のどちらとも同じセールがあることもあれば、Webサイト版のみだったりアプリ版のみだったりするので、そこは注意してください。

購入さえすれば、Webサイトでもアプリでも同じ動画が使用できるようになりますので、ご心配なく。


新学期就職時期夏休み年末ブラックフライデー等にはかなりお得になることがあります。


それ以外でも、キャンペーン・セールは行われるのでしっかりチェックしてお得に購入しましょう。




キャンペーン・セール情報


2021 5/18から、今年最大のセールがあるようです
価格は1270円〜です
ほしいコースは、普段からチェックしておいてセールの時に買うのがベストです。


私もよくチェックしていますよ。



今回の年末・年始はステイホームの方が多いのでは無いでしょうか?、こんな時には学習してみるのも良いかもですよ。

まとめ

アプリを使って、好きな時間・場所で快適に学習しましょう!。


クーポンコードで、気になった動画をお得に購入しましょう。


セールを見極めて、お得に購入しましょう。


世界最大級のオンライン学習サイトUdemy とはどういったサービスなのかをザクっとまとめさせていただきました!、いががでしたでしょうか?


「Udemy(ユーデミー)ってなんだろう?」と思っていた人もいるかと思います。 実際、私も利用するまではそうでした。


でも、利用してみると知りたかった技術や知識を得ることができました。


思っているだけで、行動しないのはもったいないです。


今は、知りたいことが手軽に知ることができる時代になりました、ぜひこの機会に世界最大級のオンライン学習サイトUdemy を利用してみてくださいませ!。




          

SpriteKitでゲーム その1- SPACE SHOOTER⑨

f:id:tukumosanzou:20180702205113p:plain

前回までで、完成してますが今回はおまけを、すこし追加したいと思います。

せっかくですから、LaunchScreen.storyboard を調整してゲームのオープニング画面を作ります。


LaunchScreen.storyboard は既にあるので開いてください。

以下のような、感じに変更していきます。
f:id:tukumosanzou:20180728001821p:plain


右下の Object Library から Labelview に追加して以下の図の様に変更します。
f:id:tukumosanzou:20180728002028p:plain


"SPACE" "SHOOTER" の各テキストをクリックして図の部分の白い四角形内の赤い線が太くなっていたら線自体をクリックして図の状態にして Autoresizing の設定をオフにします。

各テキストごとに行ってください。
f:id:tukumosanzou:20180728002333p:plain


ちょっとわかりづらいので、拡大しますね。
f:id:tukumosanzou:20180728002507p:plain

赤い太線をクリックすると、以下のようになります。 そうすると、右側の赤い四角が中央に来ると思います。
f:id:tukumosanzou:20180728002522p:plain
これで、テキストがデバイスのサイズに合わせて自動で中央に表示できるようになります。



以上で終了です、全てはうまく説明仕切れてないかもですがなんとなく感じが掴めてもらえれば幸いです。

ありがとうございました。

完成した各ファイルの全コードを載せておきます。

GameScene.swift

import SpriteKit
import GameplayKit

var gameScore = 0

class GameScene: SKScene, SKPhysicsContactDelegate {
    
    var logoLabel: SKLabelNode!
    var gameState = GameState.startGame
    
    var livesImages = [SKSpriteNode]()
    var lives = 3
    
    var player: SKSpriteNode!
    
    var enemy: SKSpriteNode!
    var enemyArray = ["ufoBlue", "ufoRed", "ufoGreen", "ufoYellow"]
    
    var getTimer: Timer!
    
    var scoreLabel: SKLabelNode!
    var score = gameScore {
        didSet {
            scoreLabel.text = "SCORE: \(score)"
        }
    }
    
    var backgroundMusic = SKAudioNode()
    let musicURL = Bundle.main.url(forResource: "music", withExtension: "m4a")
    let laserSound = SKAction.playSoundFileNamed("LaserSoundEffect.mp3", waitForCompletion: false)
    let explosionSound = SKAction.playSoundFileNamed("explosion.wav", waitForCompletion: false)
    
    
    struct PhysicsCategories {
        static let none: UInt32 = 0
        static let player: UInt32 = 0x1 << 1 //1
        static let laser: UInt32 = 0x1 << 2 //2
        static let enemy: UInt32 = 0x1 << 3 //3
    }
    
    enum GameState {
        case startGame
        case endGame
    }
    
    
    override func didMove(to view: SKView) {
        
        physicsWorld.gravity = CGVector(dx: 0, dy: 0)
        physicsWorld.contactDelegate = self
        
        createBackground()
        createPlayer()
        createScore()
        createLives()
        
        if getTimer == nil {
            getTimer = Timer.scheduledTimer(timeInterval: 0.5, target: self, selector: #selector(createEnemy), userInfo: nil, repeats: true)
        }

        
        backgroundMusic = SKAudioNode(url: musicURL!)
        addChild(backgroundMusic)
    }
    
    
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        
        guard let touch = touches.first else { return }
        
        switch gameState {
        case .startGame:
            let location = touch.location(in: self)
            
            
            if location.x < player.position.x {
                player.position.x -= 50
            } else if location.x > player.position.x {
                player.position.x += 50
            }
            
            if player.position.x > frame.maxX - player.size.width {
                player.position.x = frame.maxX - player.size.width / 2
                
            }
            
            if player.position.x < frame.minX + player.size.width {
                player.position.x = frame.minX + player.size.width / 2
            }
            
            createLaser()
            
        case .endGame:
            break
        }
    }
    
    
    override func update(_ currentTime: TimeInterval) {
       
    }
    
    
    func didBegin(_ contact: SKPhysicsContact) {
        
        if contact.bodyA.node?.name == "laser" || contact.bodyB.node?.name == "laser" {
            if contact.bodyA.node?.name == "enemy" {
                contact.bodyA.node?.removeFromParent()
                contact.bodyB.node?.removeFromParent()
                
                if let explosion = SKEmitterNode(fileNamed: "ExplosionEffect") {
                    explosion.position = (contact.bodyA.node?.position)!
                    addChild(explosion)
                }
                
            } else {
                contact.bodyA.node?.removeFromParent()
                contact.bodyB.node?.removeFromParent()
                
                if let explosion = SKEmitterNode(fileNamed: "ExplosionEffect") {
                    explosion.position = (contact.bodyB.node?.position)!
                    addChild(explosion)
                }
            }
            
            run(explosionSound)
            
            gameScore += 1
            score = gameScore
            
            return
        }
        
        guard contact.bodyA.node != nil && contact.bodyB.node != nil else { return }
        
        if contact.bodyA.node?.name == "player" || contact.bodyB.node?.name == "player" {
            if let explosion = SKEmitterNode(fileNamed: "ExplosionEffect") {
                explosion.position = player.position
                addChild(explosion)
            }
            
            run(explosionSound)
            
            contact.bodyA.node?.removeFromParent()
            contact.bodyB.node?.removeFromParent()
            
            subtractLife()
            createPlayer()
            
        }
    }
    
    
    func changeScene() {
        
        let scene = GameOverScene(size: frame.size)
        scene.scaleMode = self.scaleMode
        let transition = SKTransition.crossFade(withDuration: 1.5)
        view?.presentScene(scene, transition: transition)
    }
    
    
    func runGameOver() {
        if getTimer != nil {
            getTimer.invalidate()
            getTimer = nil
            
        }
        
        let changeSceneAction = SKAction.run(changeScene)
        let waitToChangeScene = SKAction.wait(forDuration: 2.5)
        let changeSceneSequence = SKAction.sequence([waitToChangeScene, changeSceneAction])
        run(changeSceneSequence)
    }
    
    
    func subtractLife() {
        lives -= 1
        
        var life: SKSpriteNode
        
        if lives == 2 {
            life = livesImages[0]
        } else if lives == 1 {
            life = livesImages[1]
        } else {
            life = livesImages[2]
        }
        
        life.removeFromParent()
        
        if lives == 0 {
            gameState = .endGame
            runGameOver()
        }
    }
    
    
    func createLives() {
        for i in 0 ..< lives {
            let spriteNode = SKSpriteNode(imageNamed: "player")
            spriteNode.position = CGPoint(x: frame.minX + 40 + CGFloat(i * 99) * 0.25, y: UIScreen.main.bounds.height * 0.90)
            spriteNode.setScale(0.25)
            addChild(spriteNode)
            
            livesImages.append(spriteNode)
        }
    }
    
    
    func createScore() {
        scoreLabel = SKLabelNode(fontNamed: "Optima-ExtraBlack")
        scoreLabel.fontSize = 24

        scoreLabel.position = CGPoint(x: frame.minX + 80, y: UIScreen.main.bounds.height * 0.92)
        scoreLabel.text = "SCORE:0"
        scoreLabel.fontColor = UIColor.white
        
        addChild(scoreLabel)
    }
    
    
    @objc func createEnemy() {
        
        enemyArray = GKRandomSource.sharedRandom().arrayByShufflingObjects(in: enemyArray) as! [String]
        print(enemyArray)
        let enemyTexture = SKTexture(imageNamed: enemyArray[0])
        enemy = SKSpriteNode(texture: enemyTexture)
        
        enemy.physicsBody = SKPhysicsBody(circleOfRadius: max(enemyTexture.size().width / 2, enemyTexture.size().height / 2))
        enemy.physicsBody!.categoryBitMask = PhysicsCategories.enemy
        enemy.physicsBody!.collisionBitMask = PhysicsCategories.none
        enemy.physicsBody!.contactTestBitMask = PhysicsCategories.player | PhysicsCategories.laser
        
        enemy.zPosition = -10
        enemy.name = "enemy"
        enemy.setScale(0.5)
        
        let randomDistribute = GKRandomDistribution(lowestValue: Int(enemy.size.width), highestValue: Int((frame.size.width) - enemy.size.width))
        enemy.position = CGPoint(x: CGFloat(randomDistribute.nextInt()), y: frame.size.height)
        addChild(enemy)
        
        let moveEnemy = SKAction.moveTo(y: -self.size.height + enemy.size.height, duration: 5)
        let moveReset = SKAction.removeFromParent()
        let moveSequence = SKAction.sequence([moveEnemy, moveReset])
        let moveForever = SKAction.repeatForever(moveSequence)
        
        enemy.run(moveForever)
    }
    

    func createLaser() {
        let laser = SKSpriteNode(imageNamed: "laser")
        
        laser.physicsBody = SKPhysicsBody(circleOfRadius: max(laser.size.width / 2, laser.size.height / 2))
        laser.physicsBody!.categoryBitMask = PhysicsCategories.laser
        laser.physicsBody!.collisionBitMask = PhysicsCategories.none
        laser.physicsBody!.contactTestBitMask = PhysicsCategories.enemy
        
        laser.zPosition = 5
        laser.name = "laser"
        laser.position = CGPoint(x: player.position.x, y: player.position.y + player.size.height * 0.8)
        addChild(laser)
        
        let moveLaser = SKAction.moveTo(y: self.size.height + laser.size.height, duration: 1)
        let moveReset = SKAction.removeFromParent()
        let moveSequence = SKAction.sequence([laserSound, moveLaser, moveReset])
        
        laser.run(moveSequence)
    }
    
    
    func createPlayer() {
        let playerTexture = SKTexture(imageNamed: "player")
        player = SKSpriteNode(texture: playerTexture)
        
        player.physicsBody = SKPhysicsBody(circleOfRadius: max(player.size.width / 2, player.size.height / 2))
        player.physicsBody!.categoryBitMask = PhysicsCategories.player
        player.physicsBody!.collisionBitMask = PhysicsCategories.none
        player.physicsBody!.contactTestBitMask = PhysicsCategories.enemy
        
        player.setScale(0.5)
        player.zPosition = 10
        player.name = "player"
        player.position = CGPoint(x: frame.midX, y: UIScreen.main.bounds.height * 0.05)
        addChild(player)
    }

    
    func createBackground() {
        let backgroundTexture = SKTexture(imageNamed: "background")
        
        for i in 0 ... 1 {
            let background = SKSpriteNode(texture: backgroundTexture)
            background.zPosition = -30
            background.anchorPoint = CGPoint.zero
            background.position = CGPoint(x: 0, y: (backgroundTexture.size().height * CGFloat(i)) - CGFloat(1 * i))
            addChild(background)
            
            let moveDown = SKAction.moveBy(x: 0, y: -backgroundTexture.size().height, duration: 20)
            let moveReset = SKAction.moveBy(x: 0, y: backgroundTexture.size().height, duration: 0)
            let moveLoop = SKAction.sequence([moveDown, moveReset])
            let moveForever = SKAction.repeatForever(moveLoop)
            
            background.run(moveForever)
        }
    }
}



GameViewController.swift

import UIKit
import SpriteKit

class GameViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        if let view = self.view as! SKView? {
            if let scene = SKScene(fileNamed: "GameScene") {
                if (UIDevice.current.model.range(of: "iPad") != nil) {
                    scene.scaleMode = .resizeFill
                } else if UIScreen.main.nativeBounds.height == 2436.0 {
                    scene.scaleMode = .resizeFill
                } else {
                    scene.scaleMode = .aspectFill
                }
                view.presentScene(scene)
            }
            
            
            view.ignoresSiblingOrder = true
            
            view.showsFPS = true
            view.showsNodeCount = true
            view.showsPhysics = true
        }
    }

    override var shouldAutorotate: Bool {
        return true
    }

    override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
        if UIDevice.current.userInterfaceIdiom == .phone {
            return .allButUpsideDown
        } else {
            return .all
        }
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Release any cached data, images, etc that aren't in use.
    }

    override var prefersStatusBarHidden: Bool {
        return true
    }
}



GameOverScene.swift

import Foundation
import SpriteKit

class GameOverScene: SKScene {
    
    let restartLabel = SKLabelNode(fontNamed: "AppleSDGothicNeo-Bold")
    
    override func didMove(to view: SKView) {
        
        let background = SKSpriteNode(imageNamed: "background")
        background.position = CGPoint(x: frame.size.width / 2, y: frame.size.height / 2)
        background.zPosition = 0
        addChild(background)
        
        
        let gameeOverLabel = SKLabelNode(fontNamed: "Helvetica-Bold")
        gameeOverLabel.text = "GAME OVER"
        gameeOverLabel.fontSize = 60
        gameeOverLabel.fontColor = SKColor.white
        gameeOverLabel.position = CGPoint(x: frame.size.width * 0.5, y: frame.size.height * 0.7)
        gameeOverLabel.zPosition = 1
        addChild(gameeOverLabel)
        
        
        let scoreLabel = SKLabelNode(fontNamed: "AppleSDGothicNeo-Bold")
        scoreLabel.text = "SCORE: \(gameScore)"
        scoreLabel.fontSize = 30
        scoreLabel.fontColor = SKColor.white
        scoreLabel.position = CGPoint(x: frame.size.width / 2, y: frame.size.height * 0.55)
        scoreLabel.zPosition = 1
        addChild(scoreLabel)
        
        
        let defaults = UserDefaults()
        var highScoreNumber = defaults.integer(forKey: "highScoreSaved")
        
        if gameScore > highScoreNumber {
            highScoreNumber = gameScore
            defaults.set(highScoreNumber, forKey: "highScoreSaved")
        }
        
        
        let highScoreLabel = SKLabelNode(fontNamed: "AppleSDGothicNeo-Bold")
        highScoreLabel.text = "HIGH SCORE: \(highScoreNumber)"
        highScoreLabel.fontSize = 30
        highScoreLabel.fontColor = SKColor.white
        highScoreLabel.zPosition = 1
        highScoreLabel.position = CGPoint(x: frame.size.width / 2, y: frame.size.height * 0.45)
        addChild(highScoreLabel)
        
        
        restartLabel.text = "Restart"
        restartLabel.fontSize = 30
        restartLabel.fontColor = SKColor.white
        restartLabel.zPosition = 1
        restartLabel.position = CGPoint(x: frame.size.width / 2, y: frame.size.height * 0.3)
        addChild(restartLabel)

    }
    
    
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        for touch: AnyObject in touches {
            let pointOfTouch = touch.location(in :self)
            
            if restartLabel.contains(pointOfTouch) {
                let scene = GameScene(size: self.size)
                scene.scaleMode = self.scaleMode
                let transition = SKTransition.crossFade(withDuration: 0.5)
                view?.presentScene(scene, transition: transition)
            }
        }
    }
}