【SwiftUI】Image를 포물선으로 던진다

13579 단어 iOSSwiftSwiftUI

소개



SwiftUI를 사용한 게임을 만들고 있고, (ImageView)를 던지는 동작을 만들고 싶습니다.

포물선으로 Image를 이동시키는 방법이 조사해도 나오지 않기 때문에 구현을 생각해 만들어 보았습니다.

덧붙여 포물선이라고 합니다만 사법 투사 등의 엄밀한 것은 아니고(수학 서투르므로 모른다)
왠지 부드럽게 이동한다고 파악해 주시면 좋겠습니다.

1. 만든 것



이쪽이 됩니다.
부드럽게 움직이고 있습니다.






2. 우선은 직선으로 움직여 본다.



우선 가로 직선으로 움직이는 애니메이션을 만들어 보겠습니다.

동작으로서는 지정한 값(width)의 분Image를 옆으로 이동시킵니다.



구현에 대해서는, 화상대로 이동량 width를 10분할해, 1회의 이동량(dx)으로 합니다.
그리고 타이머를 사용해 일정의 초수마다 x좌표를 dx분 이동하도록 합니다.

코드는 여기입니다.

ContentView.swift
import SwiftUI

struct ContentView: View {
    // Viewの現在の座標
    @State private var x : CGFloat = 30
    @State private var y : CGFloat = UIScreen.main.bounds.size.height * 0.8

    func move(width: CGFloat) {
        // 1回の移動量
        let dx = width / 10

        // ImageViewの初期座標
        let ofX : CGFloat = 0
        let ofY : CGFloat = UIScreen.main.bounds.size.height * 0.8

        // 初期位置ofXからdxずつ10回移動する。
        var i = 1
        Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true){ timer in        
            self.x = ofX + CGFloat(i) * dx
            i += 1
            if i > 10 { timer.invalidate()}
        }
    }

    var body: some View {
        VStack {
            Image("en").resizable()
                .frame(width: 50.0, height: 50.0, alignment: .leading)
                .position(x: self.x, y: self.y)
                .animation(.easeOut)

            Button(action: {
                // ボタンを押すとwidthに指定した分横に移動させる。
                self.move(width: 300)
            }
            , label: {
                Text("Button")
            })
        }
    }

}


이동하는 동작은 move 함수를 만들고 이동량을 인수로 전달합니다.
withTimeInterval 의 값을 변경하여 이동하는 속도를 조정할 수 있습니다.
여기도 인수라도 좋을지도 모릅니다.

거동 확인








3. 원을 따라 이동



계속해서 드디어 세로로 살짝 이동시켜 보겠습니다.
여기에서는 원을 생각하고 그것을 따라 이동하는 구현으로하고 싶습니다.

반경 r = width/2의 원을 고려





이미지는 여기입니다.
width를 지름으로 하는 원을 생각해 그 원을 따라 y좌표를 갱신합니다.

수식



위 그림은 원점을 r만 어긋나므로 원의 식은
 (x - r)^2 + y^2 = r^2

 
이것을 y에 대해 풀면
  y = \pm\sqrt{r^2 - (x - r)^2}

좌표로서 원하는 것은 이것의 플러스 측이므로 절대치를 취하면 좋을 것 같습니다.

구현



move() 함수를 다음과 같이 수정합니다.
func move(width: CGFloat, height: CGFloat) {
        // 一回の移動量
        let dx = width / 10

        // Viewの初期座標
        let ofX : CGFloat = 0
        let ofY : CGFloat = UIScreen.main.bounds.size.height * 0.8

        // 直径widthの半径r
        let r = width / 2

        var i = 1
        Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true){ timer in

            self.x = ofX + CGFloat(i) * dx
            self.y = ofY - abs(sqrt(pow(r,2) - pow(CGFloat(i)*dx - r,2)))
            i += 1
            if i > 10 { timer.invalidate()}
        }
    }

self.y에 방금 요구한 식을 대입하고 있습니다.
주의점으로서는 iPhone의 화면은 좌상이 원점이 되기 때문에 y축을 위로 이동시키고 싶을 때는 y좌표를 마이너스 해 갑니다.

그 때문에 y의 초기 좌표 ofY로부터 원의 식으로 구한 수치를 뺀다.
self.y = ofY - abs(sqrt(pow(r,2) - pow(CGFloat(i)*dx - r,2)))

거동 확인








부드럽게 이동할 수있었습니다! !

단지 완전한 원이라고 던지는 동작으로서는 부자연스러운 느낌이 있네요.

높이를 미세 조정



대책으로서는 y좌표에 적당한 수를 걸어 스케일 하는 것으로 그것처럼 보이고 있습니다.
self.y = ofY - abs(sqrt(pow(r,2) - pow(CGFloat(i)*dx - r,2))) * 0.6






이상입니다!

조금 무리 화려한 방법이지만 좋은 방법이 있으면 댓글 주시면 기쁩니다!

좋은 웹페이지 즐겨찾기