29 利用 viewDidLoad 客製 薩爾達傳說主題畫面

非常喜歡薩爾達系列遊戲,還記得看劇情看到哭呢!光是曠野之息我就玩了1年(200多小時),今年五月出了王國之淚。來做一個遊戲介紹吧~

使用功能

1. 利用 AVPlayerLooper 重覆播放影片

2. 利用 CABasicAnimation & CAShapeLayer 繪製線條動畫

3. 利用 UIImageView 實現多張圖片連續播放的動畫(gif)

4. 使用 CAEmitterLayer 製作下雪動畫

5. 使用 CATransition 實現文字跑馬燈動畫

6. 利用 mask 設計特別形狀的影片

完成畫面

操作說明:

一、建立背景照片

override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.

//設定背景圖片
let backgroundView = UIImageView(image:UIImage(named: "theme"))
//設定背景圖片大小等於整個螢幕畫面
backgroundView.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
//顯示背景圖片
view.addSubview(backgroundView)
}

二、加入gif檔案

下載gif檔案後進入這個網站轉成多張圖片。

//設定gif畫面大小
let korokView = UIImageView(frame: CGRect(x: 105, y: 650, width: 200, height:200))
//顯示gif檔案
view.addSubview(korokView)
let animatedImage = UIImage.animatedImageNamed("korok1-", duration: 1)
korokView.image = animatedImage

三、加入螢火蟲特效

建立製造粒子效果的 SpriteKit Particle File

(一)File > New > File。

(二)選擇 SpriteKit Particle File、命名檔案名稱

(三)加入 SpriteKit framework

我們必須利用 SpriteKit 的 API 加入 SpriteKit Particle File 產生的粒子效果 ,因此在 controller 的 swift 檔裡 import SpriteKit。

這邊發生了一個狀況,就是我預覽後發現沒有粒子效果,特別請教彼得大大才知道要把view.insertSubview(skView, at: 0)改成…at:1這樣才不會被其他圖層擋住。

import SpriteKit
...
let skView = SKView(frame: view.frame)
view.insertSubview(skView, at: 1)
skView.backgroundColor = .clear
let scene = SKScene(size: skView.frame.size)
scene.backgroundColor = UIColor.clear
//縮小螢火蟲效果的範圍
scene.anchorPoint = CGPoint(x: 0.5, y: 0.1)
let emitterNode = SKEmitterNode(fileNamed: "MySnowParticle")
scene.addChild(emitterNode!)

四、特殊形狀影片及輪播影片

(一)在 Assets.xcassets 加入特殊形狀的圖片(這邊我選擇水墨風格圓形圖片)。

(二)插入程式碼

        var videoLooper: AVPlayerLooper?

在viewDidLoad內
//影片重複播放
let url = Bundle.main.url(forResource: "movie", withExtension: "mov")
let videoItem = AVPlayerItem(url: url!)
let videoPlayer = AVQueuePlayer()
videoLooper = AVPlayerLooper(player: videoPlayer, templateItem: videoItem)
let videoPlayerViewController = AVPlayerViewController()
videoPlayerViewController.player = videoPlayer
present(videoPlayerViewController, animated: true, completion: {
videoPlayerViewController.player?.play()})

//設定遮罩形狀
let circleImage = UIImage(named: "circle")
let circleImageView = UIImageView(image: circleImage)
let videoView = UIView(frame: CGRect(origin: CGPoint(x: 70, y: 480), size: circleImageView.frame.size))
view.addSubview(videoView)
//型別 AVPlayer 的 player 負責播放影片,但顯示到畫面上則需要型別 AVPlayerLayer 的 playerLayer。
let videoPlayerLayer = AVPlayerLayer(player: videoPlayer)
videoPlayerLayer.frame = CGRect(origin: .zero, size: circleImageView.frame.size)
//.resizeAspectFill 將讓影片佔滿 playerLayer 的長方形框框。
videoPlayerLayer.videoGravity = .resizeAspectFill
videoView.layer.addSublayer(videoPlayerLayer)
videoView.mask = circleImageView
videoPlayer.play()

五、文字跑馬燈

這邊參考彼得大大的文章,非常感謝在這麼晚的時間彼得大還是秒回我問題(半夜12點),他真的是非常好的好人。

我卡到的問題是,我文字內容寫在viewDidLoad裡面,但跑馬燈的funtion是另外寫,導致function讀不到label,這邊要用到的是:

將宣告成 controller 的 property。

class ViewController: UIViewController {
var videoLooper: AVPlayerLooper?
var index = 0
var timer: Timer?

//寫成 class 的 property,之後的function 才能讀到
var label = UILabel(frame: CGRect(x: 0, y: 100, width: 400, height:200))

第二段viewDidLoad裡面:

//字體路徑
let fontUrl = Bundle.main.url(forResource: "ChenYuluoyan-Thin", withExtension: "ttf")! as CFURL
CTFontManagerRegisterFontsForURL(fontUrl, .process, nil)
label.numberOfLines = 0
label.textColor = .white
label.font = UIFont(name: "ChenYuluoyan-Thin", size: 38)
//設定timer
timer = Timer.scheduledTimer(withTimeInterval: 3.5, repeats: true) { _ in self.nextLabel()
}
view.addSubview(label)

第三段function內容:

func nextLabel(){
let Content = [
"薩爾達傳說是日本任天堂",
"自1986年起推出的遊戲。",
"遊戲以虛構的奇幻世界為背景。",
"描述著林克的冒險經歷,",
"為任天堂的招牌作品之一,",
"長期受到歐美玩家的喜愛。"
]
index = (index + 1) % Content.count
let transition = CATransition()
transition.duration = 2
transition.type = .push
transition.subtype = .fromTop
label.text = Content[index]
label.layer.add(transition, forKey: "nextLabel")
}

六、利用 CABasicAnimation & CAShapeLayer 繪製線條動畫

這邊在驅魔之劍上面加上薩爾達傳說的三角之力(邊寫邊覺得我很廚XDD)

以下是程式說明:

//製作線條動畫
let path = UIBezierPath()
var point = CGPoint(x:200, y: 300)
path.move(to: point)
point = CGPoint(x: 90, y: 450)
path.addLine(to: point)
point = CGPoint(x: 300, y: 450)
path.addLine(to: point)
point = CGPoint(x: 200, y: 300)
path.close()
var point2 = CGPoint(x:140, y: 380)
path.move(to:point2)
point2 = CGPoint(x: 250, y: 380)
path.addLine(to: point2)
point2 = CGPoint(x: 195, y: 450)
path.addLine(to: point2)
path.close()


let checkmarkLayer = CAShapeLayer()
checkmarkLayer.path = path.cgPath
checkmarkLayer.lineWidth = 5
checkmarkLayer.strokeColor = UIColor.white.cgColor
checkmarkLayer.fillColor = UIColor.clear.cgColor
view.layer.addSublayer(checkmarkLayer)

CATransaction.begin()
CATransaction.setCompletionBlock {
checkmarkLayer.fillColor = UIColor.clear.cgColor
}
let animation = CABasicAnimation(keyPath: "strokeEnd")
animation.fromValue = 0
animation.toValue = 1
animation.duration = 10
checkmarkLayer.add(animation, forKey: nil)
CATransaction.commit()

github連結:

--

--

HydeeChen
彼得潘的 Swift iOS / Flutter App 開發教室

純種文組生轉職程式異世界 持續學習swift, Objective C, flutter…