Does anyone know swift?

Thread Starter

arduinolego611

Joined Jan 23, 2022
75
Hi, im wondering if anyone out there can correct my swift code. I am making a bouncing ball simulation in Xcode with Swift UI. It should have a grey background and 3 black balls that bounce off the floor, the right and left walls, and each other. Im realizing that my for loops for each of 3 circles/balls are totally out of whack with proper swift syntax. I am wondering if anyone can correct those parts of my code (while I try to learn swift) just as a fun way to learn the language

a bunch of lines have been commented out just because I don't know how to implement those concepts yet, as well as code involving color being commented out throughout the code. also, the "ball" vs "balls" in the second half is wrong, I haven't gotten there because I don't know how to do those things yet

any part of the program you can help me with is appreciated

Code:
import SwiftUI

class Ball: ObservableObject, Identifiable
{
    //var color: String
    var xPosition: Int
    var yPosition: Int
    var xVelocity: Int
    var yVelocity: Int
    var radius: Int
    var gravity: CGFloat
    var restitution: Int
  
    init (xPosition: Int, yPosition: Int, xVelocity: Int, yVelocity: Int, radius: Int, gravity: CGFloat, restitution: CGFloat)
    // COLOR
    //gravity and restitution values initialized???
    {
        //self.color = color
        self.xPosition = xPosition
        self.yPosition = yPosition
        self.xVelocity = xVelocity
        self.yVelocity = xVelocity
        self.radius = radius
        self.gravity = gravity
        self.restitution = restitution
    }
}
    //COLOR
    var ball1 = Ball(xPosition: 100, yPosition: 100, xVelocity: 3, yVelocity: 0, radius: 3, gravity: 0.3, restitution: 1)
    var ball2 = Ball(xPosition: 200, yPosition: 50, xVelocity: -2, yVelocity: 2, radius: 3, gravity: 0.3, restitution: 1)
    var ball3 = Ball(xPosition: 300, yPosition: 150, xVelocity: 4, yVelocity: -3, radius: 3, gravity: 0.3, restitution: 1)

//struct ContentView: View ???
//{ ???

    var timer = Timer.publish(every: 0.01, on: .main, in: .common).autoconnect()

//   var body: some View
//    {
//     VStack
          {
              //Background color
              Color.gray.edgesIgnoringSafeArea(.all)
            
              for ball in balls
              {
                  Circle()
                      .fill(Color.black)
                      .frame(width: 50, height: 50)
                      .position(balls[].xPosition, balls[].yPosition)
              }
            
          }
              

            .onReceive(timer)
            {
              //  _ in
                for ball in balls
                {
                    ball.yVelocity += gravity
                    ball.xPosition = CGPoint(ball.xPosition + ball.xVelocity)
                    ball.yPosition = CGPoint (ball.yPosition + ball.yVelocity)
                  
                    if ball.yPosition >= 500 - 25
                    {
                        ball.yPosition = 500 - 25
                        ball.yVelocity = -ball.yVelocity * restitution
                    }
                  
                    if ball.xPosition <= 25
                    {
                        ball.xPosition = 25
                        ball.xVelocity = -ball.xVelocity
                    }
                  
                    if ball.xPosition >= 375
                    {
                        ball.xPosition = 375
                        ball.xVelocity = -ball.velocityX
                    }
                  
                    //Bounce off other balls
                    for balls in balls
                    {
                        let dx: int = balls[i].x - x
                        let dy: int = balls[i].y - y
                        let distance: int = sqrt (dx * dx + dy * dy)
                        if distance < self.radius + balls[i].radius
                        {
                            self.vx = -self.vx * restitution
                            self.vy = -self.vy * restitution
                            balls[i].vx = -balls[i].vx * restitution
                            balls[i].vy = -balls[i].vy * restitution
                        }
                    }
                }
              
            }
 

Art Vandelay

Joined Nov 1, 2024
140
Hi, I suggest writing a text based output before trying to animate. Once you confirm that the objects behave as intended (the algorithm is sound), then it'll be easier to write the animation to go along with it. Otherwise, you are putting the cart before the horse in my ways.

You remember the pool table example I gave you last topic? I used that example because the pool table provides a 2D grid and the balls must occupy some position within that grid. This simple framework is enough for you to code for the many possible ball positions and collisions, either with other balls, or the edge of the table.

If you look at your code above, you have an if statement that fires when ball.yPosition >= 500 - 25 but wouldn't it be better say when ball.yPosition >= northWall? The point is one you define everything, you can invoke the position changes then print the text output to console. Then you can verify the output and be certain that things behave as intended.

On the other hand, since you are trying to animate at the same time, you have to find a way to confirm the physics as well as the visual analogy applied to the physics (the animation technique).

It makes sense to confirm the physics with a visual animation but our visual systems are not very good at pinpointing where an object is at a given time, especially in relation to other objects. Just imagine if I asked you to list the coordinates traversed by a puck as it is bouncing around a hockey arena.

You'll only be able to give an approximation which is where the text based output comes in. If I attach a sensor to the puck that logs when it hits a wall (or a person) to text output, we can be sure it actually happened at that location.

I know this didn't really answer your question but it's something to think about.
 

wayneh

Joined Sep 9, 2010
18,087
Your final loop operates on each ball in the array of balls. That syntax keeps track of the count and the indexing for you. You don't need the index of balls and in fact "i" is not defined.

I haven't looked at your code in detail to see if the parentheses match up and so forth. I like to add a comment to each closing paren to indicate which opening paren it is paired with. I also add print statements frequently to indicate where code execution is and what the variable values are at that point. For instance in the loop above you might try:

Code:
                    for ball in balls
                    {
                       print("Beginning loop for ball at" \(ball.x) "and " \(ball.y)  )
                        let dx: int = ball.x - x
                        let dy: int = ball.y - y
                        let distance: int = sqrt (dx * dx + dy * dy)
                       print("Comparing the distance" \(distance))
                       if distance < self.radius + ball.radius
                        {
                          print("Adjusting restitution by" \(restitution))
                          self.vx = -self.vx * restitution
                            self.vy = -self.vy * restitution
                            ball.vx = -ball.vx * restitution
                            ball.vy = -ball.vy * restitution
                        }  // Close IF
                    }   // Close For Ball loop
Once the code is up and working, comment out the print statements.
 

Thread Starter

arduinolego611

Joined Jan 23, 2022
75
Your final loop operates on each ball in the array of balls. That syntax keeps track of the count and the indexing for you. You don't need the index of balls and in fact "i" is not defined.

I haven't looked at your code in detail to see if the parentheses match up and so forth. I like to add a comment to each closing paren to indicate which opening paren it is paired with. I also add print statements frequently to indicate where code execution is and what the variable values are at that point. For instance in the loop above you might try:

Code:
                    for ball in balls
                    {
                       print("Beginning loop for ball at" \(ball.x) "and " \(ball.y)  )
                        let dx: int = ball.x - x
                        let dy: int = ball.y - y
                        let distance: int = sqrt (dx * dx + dy * dy)
                       print("Comparing the distance" \(distance))
                       if distance < self.radius + ball.radius
                        {
                          print("Adjusting restitution by" \(restitution))
                          self.vx = -self.vx * restitution
                            self.vy = -self.vy * restitution
                            ball.vx = -ball.vx * restitution
                            ball.vy = -ball.vy * restitution
                        }  // Close IF
                    }   // Close For Ball loop
Once the code is up and working, comment out the print statements.
"for ball in balls" gets me error message: " Closure containing control flow statement cannot be used with result builder 'ViewBuilder' "
 
Last edited:

wayneh

Joined Sep 9, 2010
18,087
I have no experience with ViewBuilder.

Swift is a rich language and there are so many ways to do things. My approach (as a more or less novice) is to stand on the shoulders of others. By that I mean dig around in Stackoverflow and other sites to find strategies and code that are“close” to what you need. Tutorials are often free and fully working demos off GitHub are a godsend at times.

This shit is hard! Once you learn everything you need to learn, you’ll realize how easy it could have been.

Life is best understood in reverse but we are only able to live it forward.
 

wayneh

Joined Sep 9, 2010
18,087
A few more comments on the code.

Code:
import SwiftUI

class Ball: ObservableObject, Identifiable
{
    //var color: String
    var xPosition: Int
    var yPosition: Int
    var xVelocity: Int
    var yVelocity: Int
    var radius: Int
    var gravity: CGFloat
    var restitution: Int
 
    init (xPosition: Int, yPosition: Int, xVelocity: Int, yVelocity: Int, radius: Int, gravity: CGFloat, restitution: CGFloat)
    // COLOR
    //gravity and restitution values initialized???
}
There's no need to declare the variables twice.
So your init statement could be simplified:
Code:
import SwiftUI
class Ball: ObservableObject, Identifiable
{
    //var color: String
    var xPosition: Int
    var yPosition: Int
    var xVelocity: Int
    var yVelocity: Int
    var radius: Int
    var gravity: CGFloat
    var restitution: Int
 
    init (xPosition, yPosition, xVelocity, yVelocity, radius, gravity, restitution)
    // COLOR
    //gravity and restitution values initialized???
}
Note that you declared restitution as an integer but changed it to CGFLoat. Since you haven't assigned values to any of the variables, I'm not sure the INIT accomplishes anything.
 
Top