Home > 3D World & Physics
3D World & Physics
SpriteKit, SceneKit, CAEmitterLayer์ ํ๋์ ๋ชจ๋์ ๋ชจ์ 2D/3D ์ฅ๋ฉด๊ณผ ํํฐํด ์์คํ ์ ๋น๊ตํ๋ฉฐ ๋ฐฐ์๋๋ค.
์น์ ๊ตฌ์ฑ
์ธ ๊ฐ์ง ํ๋ ์์ํฌ๋ฅผ ๊ฐ๊ฐ์ ์น์ ์ผ๋ก ๊ตฌ๋ถํด ๊ฐ์ ๋ชจ๋ ์์์ ๋น๊ตํ ์ ์๋๋ก ๊ตฌ์ฑํ์ต๋๋ค.
SpriteKit Physics โ Gravity Balls
ํญ์ผ๋ก ๊ณต์ ๋จ์ด๋จ๋ฆฌ๊ณ , ๋๋ฐ์ด์ค ๊ธฐ์ธ๊ธฐ๋ก ์ค๋ ฅ ๋ฐฉํฅ์ ์ ์ดํฉ๋๋ค. SpriteKit์ ๋ฌผ๋ฆฌ์์ง + CoreMotion ํตํฉ์ ๋ณด์ฌ์ค๋๋ค.
3D World โ Solar System
ํ์๊ณผ 5๊ฐ ํ์ฑ์ ๊ณต์ , ๋ฌ์ ์ข ์ ๊ถค๋, ์๋ ์ฌ๋ผ์ด๋๋ฅผ ํตํ ์๋ฎฌ๋ ์ด์ . SceneKit์ `SCNAction.rotateBy`์ ์์ ๋ ธ๋ ๊ณ์ธต์ ํ์ฉํฉ๋๋ค.
Particle Effects โ Weather
๋/๋น/๋ฒ๊ฝ ํํฐํด์ segmented picker๋ก ์ ํ. `CAEmitterLayer`์ `birthRate`, `velocity`, `spin` ์์ฑ ์กฐ์ ์์์ ๋๋ค.
Particle Effects โ Confetti
๋ฒํผ ํธ๋ฆฌ๊ฑฐ ์ถํ ์ดํํธ, continuous ๋ชจ๋ ํ ๊ธ. 3D ํ ๋ธ๋ง ํ์ ์ ์ํ `spin` + `spinRange` ํ์ฉ.
์ ์ธ ํ๋ ์์ํฌ๋ฅผ ๋ฌถ์๋๊ฐ
๊ณตํต์ : ์ธ ํ๋ ์์ํฌ ๋ชจ๋ ๋ ๋๋ง ๋ฃจํ๋ฅผ ๊ฐ์ง ์์ ๋ทฐ๋ฅผ SwiftUI์ ๋ด์ฅํ๋ ํจํด์ ๋๋ค.
| Framework | SwiftUI ํตํฉ | ๋ ๋ ๋์ |
|---|---|---|
| SpriteKit | SpriteView(scene:) |
2D ๋ฌผ๋ฆฌ ๋ ธ๋ |
| SceneKit | SceneView(scene:) |
3D ๊ธฐํ์ฒด |
| CAEmitterLayer | UIViewRepresentable |
2D ํํฐํด |
์ฐจ์ด์ : ๊ฐ์ ๋ค๋ฅธ ๊ณ์ธต์ ์ถ์ํ๋ฅผ ์ ๊ณตํฉ๋๋ค โ ๋ฌผ๋ฆฌ ์๋ฎฌ๋ ์ด์ (SpriteKit), 3D ์ฌ ๊ทธ๋ํ(SceneKit), ๋จ์ ํํฐํด ๋ ๋(CAEmitterLayer).
SpriteKit ํต์ฌ API
import SpriteKit
class GravityScene: SKScene {
override func didMove(to view: SKView) {
physicsBody = SKPhysicsBody(edgeLoopFrom: frame)
physicsWorld.gravity = CGVector(dx: 0, dy: -9.8)
}
override func touchesBegan(_ touches: Set<UITouch>, with: UIEvent?) {
guard let t = touches.first else { return }
let ball = SKShapeNode(circleOfRadius: 20)
ball.position = t.location(in: self)
ball.physicsBody = SKPhysicsBody(circleOfRadius: 20)
ball.physicsBody?.restitution = 0.6 // ํ์ฑ
addChild(ball)
}
}
์ฃผ์ ๊ฐ๋
: SKPhysicsBody (์ถฉ๋/์ง๋), physicsWorld.gravity (์ค๋ ฅ ๋ฒกํฐ), SKAction (์ ์ธ์ ์ ๋๋ฉ์ด์
์ํ์ค).
SceneKit ํต์ฌ API
import SceneKit
let scene = SCNScene()
let sphere = SCNSphere(radius: 1.0)
sphere.firstMaterial?.diffuse.contents = UIColor.orange
let node = SCNNode(geometry: sphere)
node.position = SCNVector3(0, 0, -3)
scene.rootNode.addChildNode(node)
// Continuous rotation
let spin = SCNAction.rotateBy(x: 0, y: .pi * 2, z: 0, duration: 4)
node.runAction(SCNAction.repeatForever(spin))
์ฃผ์ ๊ฐ๋
: SCNScene (์ฌ ๊ทธ๋ํ ๋ฃจํธ), SCNNode (๊ณ์ธต์ ํธ๋์คํผ), SCNMaterial (์
ฐ์ด๋ฉ ์์ฑ), SCNAction (์ ๋๋ฉ์ด์
).
CAEmitterLayer ํต์ฌ API
let emitter = CAEmitterLayer()
emitter.emitterShape = .line
emitter.emitterPosition = CGPoint(x: view.bounds.midX, y: 0)
emitter.emitterSize = CGSize(width: view.bounds.width, height: 1)
let cell = CAEmitterCell()
cell.contents = UIImage(systemName: "snowflake")?.cgImage
cell.birthRate = 10 // per second
cell.lifetime = 6
cell.velocity = 80
cell.velocityRange = 40
cell.yAcceleration = 20
cell.spinRange = .pi
emitter.emitterCells = [cell]
view.layer.addSublayer(emitter)
์ฃผ์ ์์ฑ:
birthRate ร lifetime= ํ๋ฉด์ ์กด์ฌํ๋ ํ๊ท ํํฐํด ์velocity+velocityRange= ์๋ ๋ถํฌemitterShape= ์์ฑ ํํ (.point,.line,.rectangle,.circle)
ํ์ ํ
- SwiftUI
@Statevs Scene ๋ด๋ถ ์ํ: SpriteKit/SceneKit ์ฌ์ SwiftUI์ relay๊ฐ ์๋๋๋ค. ์ฌ์@Stateํ๋กํผํฐ๋ก ํ ๋ฒ๋ง ์์ฑํด ์ฌ์ฌ์ฉํ์ธ์. - ์ฑ๋ฅ:
CAEmitterLayer๋ GPU ๊ฐ์์ผ๋ก ์์ฒ ๊ฐ ํํฐํด๋ ๋ฌด๋ํ์ง๋ง, SpriteKit์์ ์๋ฐฑ ๊ฐ ์ด์์ ๋ฌผ๋ฆฌ body๋ CPU ๋ณ๋ชฉ์ด ๋ ์ ์์ต๋๋ค. - ์๋ฎฌ๋ ์ดํฐ ํ๊ณ: SceneKit์ Metal ์ ฐ์ด๋ ๋ชจ๋ํ์ด์ด๋ ์๋ฎฌ๋ ์ดํฐ์์ ์ ๋๋ก ๋์ํ์ง ์์ ์ ์์ต๋๋ค. ์ค๊ธฐ๊ธฐ ํ ์คํธ๊ฐ ํ์ํฉ๋๋ค.
- ์ขํ๊ณ: SpriteKit์ ์ขํ๋จ ์์ (UIKit๊ณผ ๋ฐ๋), SceneKit์ ์ฐ์ ์ขํ๊ณ (Y ์, Z ํ๋ฉด ์), CAEmitterLayer๋ UIKit๊ณผ ๋์ผํ ์ข์๋จ ์์ ์ ๋๋ค.