Naked Networking with SwiftUI gamification
Hopefully you got here through my other articles, this one the fourth in the same series. They all build on each other, so you need to step thru them to get here. Here is list of links.
Naked Networking with SwiftUI
More Naked networking, more SwiftUI
Naked Networking, SwiftUI and the game plan
Where is here. I started on creating a UDP listening server under iOS. I went on to build a transmitting client, and in the last article tied it all together into a app that lets you play ping/pong with each other. The app needs to run on two devices on the same network.
But it isn’t finished. We need to introduce do some gamification. To build in an element of challenge, an element that will give two players a means of competing with each other.
Lets bring in a timing element. Lets give each player less and less time to return the ping until one fails to do so. We’ll need to keep track of who is winning too.
And what about a few more buttons and get the receiver to pick one at random when it gets the ping. It then becomes a game in which you need to press one of X buttons to return the ball. .
Finally lets add some spice to it by animating our buttons. Ultimately we might also like to think about how the game would/could work with more than two players. After all our UDP broadcast is catchable by up to 255 players.
Ok, as you see there is a lot to do here and indeed think about. Lets get started.
Network
Lets start by changing our our pingPublisher in Connect.swift so that it sends data along with its notification.
let pingPublisher = PassthroughSubject<String, Never>()
We want to send the time it took to return the the ping. So we need to record when we send one, so that we can then work out the difference when we get it back. Let me put it down in code.
private var startingTime: Date?
private var endingTime: Date?...
We need two variables, a startingTime and an endingTime. When we return a ping we save the startingTime. The code in bold italic is the new code you need to add.
func sendUDP(_ content: String) {
startingTime = Date()
...
Now when we get a reply we need to calculate how long it took; this is the time between us sending and the other player replying.
func receive(on connection: NWConnection) {
endingTime = Date()
var string2Send: String = "8"
if startingTime != nil {
let answer = DateInterval(start: startingTime!, end: endingTime!)
string2Send = String(answer.duration)
}...DispatchQueue.main.async {
globalVariable.score = backToString
pingPublisher.send(string2Send)
}
What this effectively means is that the time you have to reply will gradually get shorter and shorter.
SwiftUI
Of course you now need to make a few changes to the UI as well. The new Code is in bold italic.
@State var refresh = false
@State var youLose: Timer?....onReceive(pingPublisher) { ( data ) in
print("data ",data)
self.disable = false
var countDown = Float(data)
self.youLose = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true, block: { timer in
countDown = countDown! - 0.1
if countDown! < 0.0 {
self.disable = true
self.youLose?.invalidate()
}
})
}
Now there are two things going on here. We needed to make a nano change to the .onRecieve since we now care about the data that comes back. We than set a countDown timer using said data to record how long we got before we need to disable the whack back button.
Once the timer is less than zero we need to of course stop it.
Button(action: {
if self.model.state {
makeConnect(port: "1984", message: "ping")
self.disable = true
self.refresh = !self.refresh
self.youLose?.invalidate()
} else {
makeConnect(port: "4891", message: "pong")
self.disable = true
self.refresh = !self.refresh
self.youLose?.invalidate()
}
}) {
Text("whack")
.disabled(disable)
}
You most certainly test now. Initially you have an indefinate amount of time to and then depending on the speed of the response of the other player, it will start to decrease. Eventually you’ll both be locked out. Game Over.
But wait, notice now you’re both disabled and we are stuck and it isn’t obvious as what happened either. Lets add a new label showing the time you got left to reply.
@State var timeToDie = ""...self.youLose = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true, block: { timer in
countDown = countDown! - 0.1
self.timeToDie = String(countDown!)
if countDown! < 0.0 {
self.disable = true
self.youLose?.invalidate()
}
})
}) {
Text("whack")
.disabled(disable)
}
Text(timeToDie)
That works better, but you can only play a single game and there is no indication beyond the fact that you run out of time who won and who lost. You need to read on to see how I fix that…