'Floor affects ball force direction

I'm creating a pool game. The ball has a trajectory direction and I move the ball using AddForce. My problem right now is if the ball is touching the floor it's direction will go a little offcourse while if it doesn't touch the floor the direction is pretty accurate. What can be the cause of this? Btw, I have set the angular drag and drag to 0 same as friction



Solution 1:[1]

Make sure the floor is using a box collider and not a mesh collider, in case maybe the mesh has some deformities. Check the same for the billiard balls that they are using sphere colliders. It also sounds like maybe the floor could be at a slight angle, so be sure to check the rotation of its GameObject as well as all its parents in the hierarchy.

Solution 2:[2]

It would help if you provided a more concrete description of what you want to do with your "offscreen view."

However, here's an example of getting a UIImage from a UIView that has never been added to the view hierarchy (so it is "offscreen").

First, the simple view subclass:

class MySimpleView: UIView {

    let label: UILabel = {
        let v = UILabel()
        v.textAlignment = .center
        v.numberOfLines = 0
        v.backgroundColor = .yellow
        v.text = "Multiline Label"
        return v
    }()
    let containerView: UIView = {
        let v = UIView()
        v.backgroundColor = .systemBlue
        return v
    }()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    func commonInit() {
        [label, containerView].forEach { v in
            v.translatesAutoresizingMaskIntoConstraints = false
        }
        containerView.addSubview(label)
        self.addSubview(containerView)
        NSLayoutConstraint.activate([
            
            containerView.topAnchor.constraint(equalTo: self.topAnchor, constant: 12.0),
            containerView.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 12.0),
            containerView.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -12.0),
            containerView.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -12.0),
            
            label.topAnchor.constraint(equalTo: containerView.topAnchor, constant: 20.0),
            label.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 20.0),
            label.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: -20.0),
            label.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: -20.0),

            // label max width: 240
            label.widthAnchor.constraint(lessThanOrEqualToConstant: 240.0),
            
        ])
        
        self.backgroundColor = .systemRed
    }
    
}

It has a label - .numberOfLines = 0 and max-width of 240 - as a subview of a "container" view, which is a subview of itself. The label is constrained inside the "container" view, with 20-pts "padding" on all 4 sides. The "container" view is constrained with 12-pts "padding" on all 4 sides.

It looks like this to start:

enter image description here

Changing the label text to "This string will likely need to wrap onto two lines." and it looks like this:

enter image description here

So far, pretty basic.

To get a UIImage of it, we can add this property:

var image: UIImage {
    get {
        self.setNeedsLayout()
        self.layoutIfNeeded()
        let renderer = UIGraphicsImageRenderer(size: self.bounds.size)
        return renderer.image { _ in
            self.drawHierarchy(in: self.bounds, afterScreenUpdates: true)
        }
    }
    set {}
}

By including:

self.setNeedsLayout()
self.layoutIfNeeded()

we can get the view to update itself, even if it's not in the view hierarchy.

Here's the completed class, along with an example controller:

class MySimpleView: UIView {

    var image: UIImage {
        get {
            self.setNeedsLayout()
            self.layoutIfNeeded()
            let renderer = UIGraphicsImageRenderer(size: self.bounds.size)
            return renderer.image { _ in
                self.drawHierarchy(in: self.bounds, afterScreenUpdates: true)
            }
        }
        set {}
    }

    let label: UILabel = {
        let v = UILabel()
        v.textAlignment = .center
        v.numberOfLines = 0
        v.backgroundColor = .yellow
        v.text = "Multiline Label"
        return v
    }()
    let containerView: UIView = {
        let v = UIView()
        v.backgroundColor = .systemBlue
        return v
    }()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    func commonInit() {
        [label, containerView].forEach { v in
            v.translatesAutoresizingMaskIntoConstraints = false
        }
        containerView.addSubview(label)
        self.addSubview(containerView)
        NSLayoutConstraint.activate([
            
            containerView.topAnchor.constraint(equalTo: self.topAnchor, constant: 12.0),
            containerView.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 12.0),
            containerView.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -12.0),
            containerView.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -12.0),
            
            label.topAnchor.constraint(equalTo: containerView.topAnchor, constant: 20.0),
            label.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 20.0),
            label.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: -20.0),
            label.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: -20.0),

            // label max width: 240
            label.widthAnchor.constraint(lessThanOrEqualToConstant: 240.0),
            
        ])
        
        self.backgroundColor = .systemRed
    }
    
}

class OffScreenTestViewController: UIViewController {

    let onScreentestStrings: [String] = [
        "Short String",
        "A bit longer String",
        "This string will likely need to wrap onto two lines.",
        "This string is going to be really, really long, and will almost certainly need to wrap onto more than two lines.",
    ]
    
    let offScreentestStrings: [String] = [
        "Off-screen String",
        "A bit longer Off-screen String",
        "Off-screen string will likely need to wrap onto two lines.",
        "This Off-screen string is going to be really, really long, and will almost certainly need to wrap onto more than two lines.",
    ]
    
    var onScreenIDX: Int = 0
    var offScreenIDX: Int = 0

    let onScreenTestView = MySimpleView()
    let offScreenTestView = MySimpleView()

    let resultsImageView: UIImageView = {
        let v = UIImageView()
        v.contentMode = .scaleAspectFit
        v.backgroundColor = .systemGreen
        return v
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()

        onScreenTestView.translatesAutoresizingMaskIntoConstraints = false
        resultsImageView.translatesAutoresizingMaskIntoConstraints = false
        
        view.addSubview(onScreenTestView)
        view.addSubview(resultsImageView)
        
        let stack: UIStackView = {
            let v = UIStackView()
            v.spacing = 20
            v.distribution = .fillEqually
            v.translatesAutoresizingMaskIntoConstraints = false
            return v
        }()
        ["On Screen", "Off Screen"].forEach { title in
            let b = UIButton()
            b.backgroundColor = .systemBlue
            b.setTitleColor(.white, for: .normal)
            b.setTitleColor(.lightGray, for: .highlighted)
            b.setTitle(title, for: [])
            b.addTarget(self, action: #selector(btnTapped(_:)), for: .touchUpInside)
            stack.addArrangedSubview(b)
        }
        view.addSubview(stack)
        let g = view.safeAreaLayoutGuide
        NSLayoutConstraint.activate([
            stack.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
            stack.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
            stack.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),

            onScreenTestView.topAnchor.constraint(equalTo: stack.bottomAnchor, constant: 20.0),
            onScreenTestView.centerXAnchor.constraint(equalTo: g.centerXAnchor),
            
            resultsImageView.topAnchor.constraint(equalTo: onScreenTestView.bottomAnchor, constant: 20.0),
            resultsImageView.widthAnchor.constraint(equalToConstant: 240.0),
            resultsImageView.heightAnchor.constraint(equalTo: resultsImageView.widthAnchor),
            resultsImageView.centerXAnchor.constraint(equalTo: g.centerXAnchor),
            
        ])
        
        // needs this, even though we're not adding it to the view hierarchy
        offScreenTestView.translatesAutoresizingMaskIntoConstraints = false
        
        // just to make it really, really clear that the
        //  off-screen view is being used to generate the image
        offScreenTestView.label.backgroundColor = .blue
        offScreenTestView.label.textColor = .yellow
        if let font = UIFont(name: "SnellRoundhand-Black", size: 22.0) {
            offScreenTestView.label.font = font
        }
        offScreenTestView.containerView.backgroundColor = .systemYellow
        offScreenTestView.backgroundColor = .systemOrange
        
    }

    @objc func btnTapped(_ btn: UIButton) {
        
        guard let t = btn.currentTitle else { return }
        
        if t == "On Screen" {
            onScreenTestView.label.text = onScreentestStrings[onScreenIDX % onScreentestStrings.count]
            onScreenIDX += 1
        } else {
            offScreenTestView.label.text = offScreentestStrings[offScreenIDX % offScreentestStrings.count]
            let img = offScreenTestView.image
            resultsImageView.image = img
            offScreenIDX += 1
        }
        
    }
    
}

When you run this, it will start out looking like this:

enter image description here

The green square is a UIImageView set to .scaleAspectFit with no image to begin with.

Each time we tap the "On Screen" button, the text in the custom view's label will cycle through 4 sample strings:

enter image description here enter image description here

enter image description here enter image description here

We've also created an instance of MySimpleView called offScreenTestView and changed some of its properties... label font and subview colors, just to make it abundantly clear it's not the same instance.

Each tap on the "Off Screen" button will cycle through a similar set of strings for the label and set the green image view's .image to offScreenTestView.image:

enter image description here enter image description here

enter image description here enter image description here

All of the green image view updates are happening while offScreenTestView - which is using constraints for its own sizing - has never been added to the view hierarchy.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 obieFM
Solution 2 DonMag