Developer Tools 入门 8m
天鹅任务第三章:非凡卷轴
Swan's Quest, Chapter 3: The notable scroll
2020年6月24日
一句话判断
Swan’s Quest 第三章转向了视觉领域——你需要用 Core Graphics 在屏幕上绘制特定的图形和符号来解谜,从音频编程跳到了 2D 绘图,考验的是你对 CGPath、CGContext 和贝塞尔曲线的掌握。
这场 Session 讲了什么
第三章从音频挑战转向了图形绘制挑战。玩家需要用 Core Graphics 绘制特定的几何图形(线条、曲线、形状组合),这些图形作为「卷轴」上的密码来解锁下一关。
核心编程任务是:理解 CGContext 的绘制状态机(save/restore)、用 CGPath 或 UIBezierPath 构建复杂路径、控制线条样式(颜色、宽度、虚线模式)、以及将绘制结果渲染到屏幕上。
这一章的设计意图是让开发者在游戏过程中自然地学习 Core Graphics 的基础 API,而不是啃枯燥的文档。
值得深挖的点
贝塞尔曲线的绘制技巧
解谜过程中需要绘制精确的曲线路径。UIBezierPath 的 addCurve(to:controlPoint1:controlPoint2:) 使用三次贝塞尔曲线,两个控制点决定曲线的弯曲方向和程度。理解控制点的几何含义是解谜的关键——控制点就像磁铁,把曲线拉向自己的方向。
代码片段
import UIKit
// 绘制解谜图形
class ScrollView: UIView {
override func draw(_ rect: CGRect) {
guard let context = UIGraphicsGetCurrentContext() else { return }
// 设置绘制属性
context.setStrokeColor(UIColor.systemBlue.cgColor)
context.setLineWidth(3.0)
context.setLineCap(.round)
context.setLineJoin(.round)
// 绘制一个五角星路径
let center = CGPoint(x: rect.midX, y: rect.midY)
let radius: CGFloat = 80
let path = UIBezierPath()
for i in 0..<5 {
let angle = CGFloat.pi / 2 + CGFloat(i) * 2 * CGFloat.pi / 5
let outerPoint = CGPoint(
x: center.x + radius * cos(angle),
y: center.y - radius * sin(angle)
)
if i == 0 {
path.move(to: outerPoint)
} else {
path.addLine(to: outerPoint)
}
// 内角点
let innerAngle = angle + CGFloat.pi / 5
let innerRadius = radius * 0.4
let innerPoint = CGPoint(
x: center.x + innerRadius * cos(innerAngle),
y: center.y - innerRadius * sin(innerAngle)
)
path.addLine(to: innerPoint)
}
path.close()
context.addPath(path.cgPath)
context.strokePath()
}
}
最佳实践
- 用
saveGState/restoreGState管理绘制状态。每次变换颜色、线宽或做仿射变换前先 save,画完后 restore,避免状态泄漏影响后续绘制。 - 在 Playground 中实时预览。Swift Playgrounds 的 Live View 可以即时显示 Core Graphics 绘制结果,改一行代码就能看到效果,比在 Xcode 中编译运行快得多。
还有什么值得关注
- 第四章是最终章,会综合前三章学到的知识。
- Core Graphics 的坐标系原点在左下角,而 UIKit 的原点在左上角,绘制时注意坐标系的差异。
- 如果你用 SwiftUI 做 iOS 14+ 的开发,大部分场景可以用
Path和Shape替代 Core Graphics,语法更简洁。
WWDC 2020