I want to write a simple game in SwiftUI where the user can drag a circle around to dodge trees and it should draw a track of where it was however the track is really weird and rectangular instead of a smooth curve. Can anyone help me?
Here’s my code:
import SwiftUI
struct ContentView: View {
@State private var track = [CGFloat]()
@State private var offset: CGFloat = 0
let timer = Timer.publish(every: 0.001, on: .current, in: .common).autoconnect()
var body: some View {
GeometryReader { geo in
ZStack {
Color.black
Path { path in
let height = (geo.size.height / 2) / CGFloat(track.count)
for i in 0..<track.count {
path.addLine(to: CGPoint(x: geo.size.width * track[i], y: height * CGFloat(i)))
path.move(to: CGPoint(x: geo.size.width * track[i], y: height * CGFloat(i)))
}
} .stroke(lineWidth: 1)
.foregroundStyle(.yellow)
Circle()
.frame(width: 25)
.foregroundStyle(.red)
.position(x: offset, y: geo.size.height / 2)
.gesture (
DragGesture()
.onChanged { value in
offset = min(geo.size.width - 15, max(15 ,value.location.x))
}
)
.onAppear {
offset = geo.size.width / 2
}
.onReceive(timer, perform: { _ in
track.append((offset / geo.size.width))
if track.count >= 100 {
track.removeFirst()
}
})
}
}
}
}
The timer publishes too fast, faster than how fast the
Gestureupdates its value.Every 0.001 seconds, you take a sample of the circle's offset. Many of these samples would likely be the same value, which makes the track look "rectangular".
Note that the rate of the timer also represents the speed of the circle, you only display 100 samples of them on the top half of the screen. This means every 0.1 seconds, the circle would "move vertically" by
geo.size.height / 2. I'm not sure if you want a circle that fast.I would recommend a timer interval around the rate at which the gesture updates. A value like
1 / 30works fine in my experiments.Also, your drawing code is a bit incorrect. I would calculate all the points first, and use
addLines. You should drawtracksin the reverse order, and from bottom to top.With your current control, you cannot freely control the vertical speed of the circle.
A more flexible approach would be to record the time as well as the offset in the gesture's
onChanged. Instead of storing a constant 100 of these, store everything with a time within the past X seconds.When drawing the line, you would use the difference between timestamps to figure out how much the circle would have moved vertically in that period (according to a speed that you choose). This gives you the difference in y coordinates between the points.
Here is a rough sketch: