How to set ball's random position in Spritekit? - ios

I am making a game where there are two balls on the scene, but the user is going to tap on the correct ball. However, when user taps on the correct ball, I want to set a random position for my second ball that when the user taps on the correct ball, I want to also want the second ball to move randomly.
Here is my code for setting up the random positions of the ball:
let currentBall = SKShapeNode(circleOfRadius: CGFloat(size))
let shape = SKShapeNode()
currentBall.fillColor = pickColor()
//Rectangle
shape.path = UIBezierPath(roundedRect: CGRect(x: 64, y: 64, width: 160,height: 160), cornerRadius: 50).cgPath
shape.fillColor = pickColor()
self.addChild(shape)
func randomBallPosition() -> CGPoint {
let xPosition = view!.scene!.frame.midX - viewMidX + CGFloat(arc4random_uniform(UInt32(viewMidX*2)))
let yPosition = view!.scene!.frame.midY - viewMidY + CGFloat(arc4random_uniform(UInt32(viewMidY*2)))
return CGPoint(x: xPosition, y: yPosition)
}
func handleTap() {
currentBall.position = randomBallPosition()
shape.position = randomBallPosition()
}

The issue is that you're setting the exact same position, which you have used for the first ball. As far as I understand both ball should appear at random independent positions. You should create a function to return a new position and call it for each ball:
class MyScene: SKScene {
var currentBall: SKShapeNode!
var shape: SKShapeNode!
override func didMove(to view: SKView) {
super.didMove(to: view)
let currentBall = SKShapeNode(circleOfRadius: CGFloat(size))
currentBall.fillColor = pickColor()
let shape = SKShapeNode()
shape.path = UIBezierPath(roundedRect: CGRect(x: 64, y: 64, width: 160,height: 160), cornerRadius: 50).cgPath
shape.fillColor = pickColor()
self.addChild(shape)
}
func handleTap() {
//your code here, except that you don't create new nodes, just modify existing ones
//at some point you will change balls' positions like this:
currentBall.position = randomBallPosition()
secondBall.position = randomBallPosition()
}
func randomBallPosition() -> CGPoint {
let xPosition = CGFloat(arc4random_uniform(UInt32((view?.bounds.maxX)! + 1)))
let yPosition = CGFloat(arc4random_uniform(UInt32((view?.bounds.maxY)! + 1)))
return CGPoint(x: xPosition, y: yPosition)
}
}
Hope this helps

Related

How to get accurate CGpoint for drawing UIBezierpath on sceneview from touchesMoved method?

UIBezierpath displays somewhere else on real world
make collection of CGPoint from below code
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let location = touches.first?.location(in: self.sceneView) else {
return
}
arrayCGPoints.append(location)
}
& display Bazierpath using below code
func updateBazierPath(){
if arrayCGPoints.count > 0 {
let bezierPath = UIBezierPath()
var i : Int = 0
for varPoint in arrayCGPoints{
if i == 0 {
bezierPath.move(to: varPoint)
}
else{
bezierPath.addLine(to: varPoint)
}
i += 1
}
bezierPath.close()
}
// Add shape
let shape = SCNShape(path: bezierPath, extrusionDepth: 0.2)
let shapeNode = SCNNode(geometry: shape)
self.sceneView.scene.rootNode.addChildNode(shapeNode)
shapeNode.geometry?.firstMaterial?.isDoubleSided = true
shapeNode.geometry!.firstMaterial?.diffuse.contents = UIColor.blue
shapeNode.position.z = -0.2
shapeNode.eulerAngles.x = -.pi / 2
}
also tried convert CGPoints into meters
arrayCGPoints.append(CGPoint(x: location.x/100, y: location.y/100))
still not working
This will draw a node # 0,0,0 with an outline of what the person dragged from, translating screen coords to world. I created a separate array of SCNVector3's that are the coords in 3d space (you could drop some nodes on the screen at these points to prove the positions). Also note the append is x,z,z (not scenepoint.y). My camera is looking at 0,0,0 from 0,15,0.1. This will at least give you a visual on what was happening to z and hopefully does answer the question you asked.
If you want the resulting shape to stay lined up with exactly where the user touched, that's not solved here.
var shapeNode = SCNNode()
func updateBazierPath()
{
for vPoints in arrayCGPoints
{
let projectedPoint = gameScene.projectPoint(SCNVector3(0, 0, 0))
let scenePoint = gameScene.unprojectPoint(SCNVector3(vPoints.x, vPoints.y, CGFloat(projectedPoint.z)))
scenePoints.append(SCNVector3(scenePoint.x, scenePoint.z, scenePoint.z))
}
let bezierPath = UIBezierPath()
bezierPath.move(to: CGPoint(x: CGFloat(scenePoints[0].x), y: CGFloat(scenePoints[0].y)))
for vCount in 1...scenePoints.count - 1
{
bezierPath.addLine(to: CGPoint(x: CGFloat(scenePoints[vCount].x), y: CGFloat(scenePoints[vCount].y)))
}
bezierPath.close()
shapeNode.removeFromParentNode()
let shape = SCNShape(path: bezierPath, extrusionDepth: 0.5)
shapeNode = SCNNode(geometry: shape)
shapeNode.geometry?.firstMaterial?.isDoubleSided = true
shapeNode.geometry!.firstMaterial?.diffuse.contents = UIColor.yellow
//let action = SCNAction.repeatForever(SCNAction.rotate(by: .pi, around: SCNVector3(1, 0, 0), duration: 5))
//shapeNode.runAction(action)
shapeNode.position = SCNVector3(0, 0, 0)
shapeNode.eulerAngles.x = -.pi / 2
gNodes.gameNodes.addChildNode(shapeNode)
}

Corner radius image Swift

I'm trying to make this corner radius image...it's not exactly the same shape of the image..any easy answer instead of trying random numbers of width and height ?
thanks alot
let rectShape = CAShapeLayer()
rectShape.bounds = self.mainImg.frame
rectShape.position = self.mainImg.center
rectShape.path = UIBezierPath(roundedRect: self.mainImg.bounds, byRoundingCorners: [.bottomLeft , .bottomRight ], cornerRadii: CGSize(width: 50, height: 4)).cgPath
You can use QuadCurve to get the design you want.
Here is a Swift #IBDesignable class that lets you specify the image and the "height" of the rounding in Storyboard / Interface Builder:
#IBDesignable
class RoundedBottomImageView: UIView {
var imageView: UIImageView!
#IBInspectable var image: UIImage? {
didSet { self.imageView.image = image }
}
#IBInspectable var roundingValue: CGFloat = 0.0 {
didSet {
self.setNeedsLayout()
}
}
override init(frame: CGRect) {
super.init(frame: frame)
doMyInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
doMyInit()
}
func doMyInit() {
imageView = UIImageView()
imageView.backgroundColor = UIColor.red
imageView.contentMode = UIViewContentMode.scaleAspectFill
addSubview(imageView)
}
override func layoutSubviews() {
super.layoutSubviews()
imageView.frame = self.bounds
let rect = self.bounds
let y:CGFloat = rect.size.height - roundingValue
let curveTo:CGFloat = rect.size.height + roundingValue
let myBezier = UIBezierPath()
myBezier.move(to: CGPoint(x: 0, y: y))
myBezier.addQuadCurve(to: CGPoint(x: rect.width, y: y), controlPoint: CGPoint(x: rect.width / 2, y: curveTo))
myBezier.addLine(to: CGPoint(x: rect.width, y: 0))
myBezier.addLine(to: CGPoint(x: 0, y: 0))
myBezier.close()
let maskForPath = CAShapeLayer()
maskForPath.path = myBezier.cgPath
layer.mask = maskForPath
}
}
Result with 300 x 200 image view, rounding set to 40:
Edit - (3.5 years later)...
To answer #MiteshDobareeya comment, we can switch the rounded edge from Bottom to Top by transforming the bezier path:
let c = CGAffineTransform(scaleX: 1, y: -1).concatenating(CGAffineTransform(translationX: 0, y: bounds.size.height))
myBezier.apply(c)
It's been quite a while since this answer was originally posted, so a few changes:
subclass UIImageView directly - no need to make it a UIView with an embedded UIImageView
add a Bool roundTop var
if set to False (the default), we round the Bottom
if set to True, we round the Top
re-order and "name" our path points for clarity
So, the basic principle:
We create a UIBezierPath and:
move to pt1
add a line to pt2
add a line to pt3
add a quad-curve to pt4 with controlPoint
close the path
use that path for a CAShapeLayer mask
the result:
If we want to round the Top, after closing the path we can apply apply a scale transform using -1 as the y value to vertically mirror it. Because that transform mirror it at "y-zero" we also apply a translate transform to move it back down into place.
That gives us:
Here's the updated class:
#IBDesignable
class RoundedTopBottomImageView: UIImageView {
#IBInspectable var roundingValue: CGFloat = 0.0 {
didSet {
self.setNeedsLayout()
}
}
#IBInspectable var roundTop: Bool = false {
didSet {
self.setNeedsLayout()
}
}
override func layoutSubviews() {
super.layoutSubviews()
let r = bounds
let myBezier = UIBezierPath()
let pt1: CGPoint = CGPoint(x: r.minX, y: r.minY)
let pt2: CGPoint = CGPoint(x: r.maxX, y: r.minY)
let pt3: CGPoint = CGPoint(x: r.maxX, y: r.maxY - roundingValue)
let pt4: CGPoint = CGPoint(x: r.minX, y: r.maxY - roundingValue)
let controlPoint: CGPoint = CGPoint(x: r.midX, y: r.maxY + roundingValue)
myBezier.move(to: pt1)
myBezier.addLine(to: pt2)
myBezier.addLine(to: pt3)
myBezier.addQuadCurve(to: pt4, controlPoint: controlPoint)
myBezier.close()
if roundTop {
// if we want to round the Top instead of the bottom,
// flip the path vertically
let c = CGAffineTransform(scaleX: 1, y: -1) //.concatenating(CGAffineTransform(translationX: 0, y: bounds.size.height))
myBezier.apply(c)
}
let maskForPath = CAShapeLayer()
maskForPath.path = myBezier.cgPath
layer.mask = maskForPath
}
}
You can try with UIView extension. as
extension UIView {
func setBottomCurve(){
let offset = CGFloat(self.frame.size.height + self.frame.size.height/1.8)
let bounds = self.bounds
let rectBounds = CGRect(x: bounds.origin.x,
y: bounds.origin.y ,
width: bounds.size.width,
height: bounds.size.height / 2)
let rectPath = UIBezierPath(rect: rectBounds)
let ovalBounds = CGRect(x: bounds.origin.x - offset / 2,
y: bounds.origin.y ,
width: bounds.size.width + offset,
height: bounds.size.height)
let ovalPath = UIBezierPath(ovalIn: ovalBounds)
rectPath.append(ovalPath)
let maskLayer = CAShapeLayer()
maskLayer.frame = bounds
maskLayer.path = rectPath.cgPath
self.layer.mask = maskLayer
}
}
& use it in viewWillAppear like methods where you can get actual frame of UIImageView.
Usage:
override func viewWillAppear(_ animated: Bool) {
//use it in viewWillAppear like methods where you can get actual frame of UIImageView
myImageView.setBottomCurve()
}

iOS Gradient not perpendicular to gradient vector

I have added a gradient on the component, it is at an angle. This gradient is not perpendicular to the gradient vector. Device iPhone 6s, for convenience, have set the size constant. What could be the problem?
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let topPoint = CGPoint(x: 33, y: -55)
let botPoint = CGPoint(x: 217, y: 469)
let iPhoneSize = CGSize(width: 375, height: 667)
let additionalLayer = CAGradientLayer()
additionalLayer.frame = view.bounds
additionalLayer.startPoint = CGPoint(x: topPoint.x / iPhoneSize.width, y: topPoint.y / iPhoneSize.height)
additionalLayer.endPoint = CGPoint(x: botPoint.x / iPhoneSize.width, y: botPoint.y / iPhoneSize.height)
additionalLayer.colors = [UIColor.white.cgColor, UIColor.darkGray.cgColor, UIColor.black.cgColor]
additionalLayer.locations = [0.0, 0.468, 1.0]
drawLine(onLayer: additionalLayer, fromPoint: topPoint, toPoint: botPoint)
view.layer.addSublayer(additionalLayer)
}
func drawLine(onLayer layer: CALayer, fromPoint start: CGPoint, toPoint end: CGPoint) {
let line = CAShapeLayer()
let linePath = UIBezierPath()
linePath.move(to: start)
linePath.addLine(to: end)
line.path = linePath.cgPath
line.fillColor = nil
line.opacity = 1.0
line.strokeColor = UIColor.red.cgColor
layer.addSublayer(line)
}
}
P.S. I've tried add this code to viewDidLayoutSubviews()
P.P.S. Also have added screenshot.

How to make a switch from circle to square shapes? -SpriteKit

I am creating a game, where the player is tapping for circles, and I want the player to tap on the square instead of circles.
Here is my code for only tapping circles
override func addBall(_ size: Int) {
let currentBall = SKShapeNode(circleOfRadius: CGFloat(size))
let shape = SKShapeNode()
let viewMidX = view!.bounds.midX
let viewMidY = view!.bounds.midY
currentBall.fillColor = pickColor()
//Rectangle
shape.path = UIBezierPath(roundedRect: CGRect(x: 64, y: 64, width: 160, height: 160), cornerRadius: 50).cgPath
shape.fillColor = pickColor()
func randomBallPosition() -> CGPoint {
let xPosition = CGFloat(arc4random_uniform(UInt32((view?.bounds.maxX)! + 1)))
let yPosition = CGFloat(arc4random_uniform(UInt32((view?.bounds.maxY)! + 1)))
return CGPoint(x: xPosition, y: yPosition)
}
currentBall.position = randomBallPosition()
shape.position = randomBallPosition()
self.addChild(shape)
//Remove other balls and add the new one
if scoreCount != 0{
if scoreCount == 1{
self.addChild(score)
self.addChild(timeLeftLabel)
self.childNode(withName: "welcome")?.removeFromParent()
}
self.childNode(withName: "ball")?.run(getSmaller)
self.childNode(withName: "ball")?.removeFromParent()
}
currentBall.name = "ball"
self.addChild(currentBall)
}
Use SKShapeNode(rectOfSize: )
instead of SKShapeNode(circleOfRadius:)
rest of your code will work fine.
EDIT:
You also wanna use UIBezierPath(rect: CGRect) instead of init(roundedRect: CGRect, cornerRadius: CGFloat)

How to create a node in another node

I have a circle, an SKShapeNode, and I want to create a child node inside it. How I can do this?
func AddCircle() {
Circle = SKShapeNode(circleOfRadius: circleRadius)
Circle.position = CGPoint(x: self.size.width/2, y: self.size.height/2)
Circle.strokeColor = UIColor.whiteColor()
self.addChild(Circle)
Circle.addChild(BooCharacter)
}
Main Character:
func AddCharacter() {
BooCharacter.size = CGSize(width: 30, height: 30)
BooCharacter.anchorPoint.y = 0
BooCharacter.zRotation = CGFloat(-M_PI_2)
BooCharacter.position.y += circleRadius
}
I want to create an object in my circle but I don't know how you write it.
For example, I've tried to add a rect inside of my circle:
func AddRect() {
Rect = SKShapeNode()
Rect.path = UIBezierPath(roundedRect: CGRect(x:0, y: 250, width: 256, height: 256), cornerRadius: 64).CGPath
Rect.fillColor = UIColor.whiteColor()
}
func AddCircle() {
Circle = SKShapeNode(circleOfRadius: circleRadius)
Circle.position = CGPoint(x: self.size.width/2, y: self.size.height/2)
Circle.strokeColor = UIColor.whiteColor()
self.addChild(Circle)
Circle.addChild(BooCharacter)
Circle.addChild(Rect)
but it's still not working.
I want to create and add triangles inside of my circle like this
You're doing exactly the right thing, although I don't see where BooCharacter is being created – are you definitely creating that somewhere? Using addChild() to add a node to your scene, or to add one node to another node, is correct, and should work for all types of SKNode.

Resources