How to convert a value to CGPoint? - swift

I have the problem that I want to use a variable in an action, which has not the matching type. I therefore get the following error: Cannot convert value of type 'SKSpriteNode' to expected argument type 'CGPoint'.
func enemyProofNextFields() {
for i in 0...41 {
if getDistance(knightRed.position, grassTileArray[i].position) > 120 && getDistance(knightRed.position, grassTileArray[i].position) < 121 {
possibleEnemeyFields.append(grassTileArray[i])
}
}
var lenght = possibleEnemeyFields.count
let randomNumber = Int.random(in: 0 ... lenght)
print("Random Enemy Number: \(randomNumber)")
var position = possibleEnemeyFields[randomNumber]
let move = SKAction.move(to: position, duration: 0.3) //here is the error
knightRed.run(SKAction.sequence([move]))
}
For any answer I would be very grateful.

The error is pretty clear: possibleEnemeyFields contains obviously SKSpriteNode instances, but the first parameter of move expects a CGPoint, assumedly the position
You probably mean
let field = possibleEnemeyFields[randomNumber]
let move = SKAction.move(to: field.position, duration: 0.3)

Related

iOS RealityKit. Changing Entity's translation causes unexpected behaviour

I am trying to create some AR experience.
I load the Model with animations as an Entity. Lets call it a Toy.
I create an AnchorEntity.
I attach the Toy to the AnchorEntity. Up to this point everything works great.
I want the Toy to walk in random directions. And it does for the first time. Then it gets interesting, allow me to share my code:
First method uses a newly created Transform for the Toy with the modified translation x, y, to make the Toy move and that is it.
func walk(completion: #escaping () -> Void) {
guard let robot = robot else {
return
}
let currentTransform = robot.transform
guard let path = randomPath(from: currentTransform) else {
return
}
let (newTranslation , travelTime) = path
let newTransform = Transform(scale: currentTransform.scale,
rotation: currentTransform.rotation,
translation: newTranslation)
robot.move(to: newTransform, relativeTo: nil, duration: travelTime)
DispatchQueue.main.asyncAfter(deadline: .now() + travelTime + 1) {
completion()
}
}
We get that new Transform from the method below.
func randomPath(from currentTransform: Transform) -> (SIMD3<Float>, TimeInterval)? {
// Get the robot's current transform and translation
let robotTranslation = currentTransform.translation
// Generate random distances for a model to cross, relative to origin
let randomXTranslation = Float.random(in: 0.1...0.4) * [-1.0,1.0].randomElement()!
let randomZTranslation = Float.random(in: 0.1...0.4) * [-1.0,1.0].randomElement()!
// Create a translation relative to the current transform
let relativeXTranslation = robotTranslation.x + randomXTranslation
let relativeZTranslation = robotTranslation.z + randomZTranslation
// Find a path
var path = (randomXTranslation * randomXTranslation + randomZTranslation * randomZTranslation).squareRoot()
// Path only positive
if path < 0 { path = -path }
// Calculate the time of walking based on the distance and default speed
let timeOfWalking: Float = path / settings.robotSpeed
// Based on old trasnlation calculate the new one
let newTranslation: SIMD3<Float> = [relativeXTranslation,
Float(0),
relativeZTranslation]
return (newTranslation, TimeInterval(timeOfWalking))
}
The problem is that the value of Entity.transform.translation.y grows from 0 to some random value < 1. Always after the second time the walk() method is being called.
As you can see, every time the method is called, newTranslation sets the Y value to be 0. And yet the Toy's translation:
I am out of ideas any help is appreciated. I can share the whole code if needed.
I have managed to fix the issue by specifying parameter relativeTo as Toy's AnchorEntity:
toy.move(to: newTransform, relativeTo: anchorEntity, duration: travelTime)

Need help to handle this error: Variable 'x' used before being initialized

I have a problem in Swift whit optional function param. I want to set an value for an SKSpriteNote within a function, but this value dos not exist before the function is called, so i obviously get an error ;/
Can anyone please tell me what to do, to solve this?
THIS IS THE ERROR I GET:
Variable 'bought_ingredient_market_graphic' used before being initialized
THIS IS MY FUNCTION (see
func show_market_graphic(x: CGFloat, y: CGFloat, level: String, ingredient: String?){
var market_graphic: SKSpriteNode
var bought_ingredient_market_graphic: SKSpriteNode
if(level == "locked"){
market_graphic = SKSpriteNode(imageNamed: "locked_ingredient")
} else {
market_graphic = SKSpriteNode(imageNamed: "bought_ingredient")
bought_ingredient_market_graphic = SKSpriteNode(imageNamed: ingredient!)
}
market_graphic.zPosition = 10
market_graphic.position = CGPoint(x: x, y: y)
addChild(market_graphic)
if(level != "locked"){
// * ERROR ON LINE BELOW
bought_ingredient_market_graphic.zPosition = 20
// * ERROR ON LINE BELOW
bought_ingredient_market_graphic.position = market_graphic.position
// * ERROR ON LINE BELOW
addChild(bought_ingredient_market_graphic)
}
}
First off, I would consider using an enum when passing in the parameter "level" into the function. This will help fix possible logic issues within your function.
Where your problem probably lies: If you do not pass in a non-nil ingredient String, you will encounter an error when accessing or using bought_ingredient_market_graphic. You could have a typo in your string that you use to initialize your image. Make sure your string matches your image name. This is likely what is happening.
Also why not try initializing your sprite in the else logic?
func show_market_graphic(x: CGFloat, y: CGFloat, level: String, ingredient: String?) {
var market_graphic: SKSpriteNode
var bought_ingredient_market_graphic: SKSpriteNode
if level == "locked" {
market_graphic = SKSpriteNode(imageNamed: "locked_ingredient")
} else {
market_graphic = SKSpriteNode(imageNamed: "bought_ingredient")
bought_ingredient_market_graphic = SKSpriteNode(imageNamed: ingredient!)
bought_ingredient_market_graphic.zPosition = 20
bought_ingredient_market_graphic.position = market_graphic.position
addChild(bought_ingredient_market_graphic)
}
market_graphic.zPosition = 10
market_graphic.position = CGPoint(x: x, y: y)
addChild(market_graphic)
}
Your code should work in principle, the compiler fails to detect the corelation between level == "locked" and level != "locked".
I assume that the compiler sees a risk that the value of level could change betwwen the statelents. So the best way would be to initialize bought_ingredient_market_graphic in the first if-clause.

RGB Values doing strange things when colorizing? - Swift

func colorBall() {
let colorize1 = SKAction.colorizeWithColor(UIColor.redColor(), colorBlendFactor: 1.0, duration: 0.1)
let colorize2 = SKAction.colorizeWithColor(UIColor.greenColor(), colorBlendFactor: 1.0, duration: 0.1)
let colorize3 = SKAction.colorizeWithColor(UIColor.blueColor(), colorBlendFactor: 1.0, duration: 0.1)
let actions = [colorize1, colorize2, colorize3]
let randomIndex = Int(arc4random_uniform(3))
self.Ball.runAction(actions[randomIndex])
}
var colorBucket = [UIColor]()
func randomColor() -> UIColor {
if colorBucket.isEmpty {
fillBucket()
}
let randomIndex = Int(arc4random_uniform(UInt32(colorBucket.count)))
let randomColor = colorBucket[randomIndex]
colorBucket.removeAtIndex(randomIndex)
return randomColor
}
func fillBucket() {
colorBucket = [UIColor.redColor(), UIColor.greenColor(), UIColor.blueColor()]
}
When I run this code in my game, and print out the color value of my ball, it sometimes prints out numbers like this:
UIDeviceRGBColorSpace 1 2.98023e-08 2.98023e-08 1
Why does it do this? I just want it to say: UIDeviceRGBColorSpace 0 0 1 1 if it's blue, IDeviceRGBColorSpace 1 0 0 1 if it's red, etc.
How can I keep those numbers from going higher than one, or much lower than one? What makes them do that in my code?
Based partially on zneak's answer I've made this (no thrills or frills) extension to UIColor which could come in handy:
extension UIColor {
func isVisuallyEqual(color: UIColor) -> Bool {
let compareValues = CGColorGetComponents(color.CGColor)
let values = CGColorGetComponents(self.CGColor)
let count = CGColorGetNumberOfComponents(self.CGColor)
if count != CGColorGetNumberOfComponents(color.CGColor) {
debugPrint("color-parameter has mismatching colorSpace")
return false
}
for index in 0..<count {
if !fuzzyFloatCompares(values[index], float2: compareValues[index]) {
return false
}
}
return true
}
private func fuzzyFloatCompares(float1: CGFloat, float2: CGFloat) -> Bool {
let difference = float1 - float2
return difference >= -1/256 && difference <= 1/256
}
}
2.98023e-08 is 0.0000000298023. If you look up the value 2.98023e-08 on Google or another search engine, you can find several examples of people getting that value because of rounding errors. Rounding errors occur because of how computers treat floating-point numbers.
It's probably a rounding error from the interpolation code that colorizeWithColor uses, and you get it instead of zero. For practical purposes, when talking about color components about to be displayed to an end user, I'd say that anything smaller than 1/256 can be considered to be zero.
You can test if two floating point numbers are "about equal" like this (typed on my phone, not really guaranteed to work):
func areAboutTheSame(a: Double, b: Double) -> Bool {
let difference = a-b
return difference < 1/256 && difference > -1/256
}

why i got this error while moving object in spritekit

I am trying make a game something like Tower defense. I am adding units to my map and I want to try movepoint to another point.
My code is:
func setTheNode(position:[Int],speed:[Int]){
let moveDown1 = SKAction.moveToY(300, duration:4)
let moveRight1 = SKAction.moveToX(300, duration: 3)
let sequence = SKAction.sequence([moveDown1, moveRight1]);
self.runAction(sequence)
}
This works, however I want to read these position and durations from parameters.
When I try this way:
func setTheNode(position:[Int],speed:[Int]){
let moveDown1 = SKAction.moveToY(position[0], duration:speed[0])
let moveRight1 = SKAction.moveToY(position[1], duration:speed[1])
let sequence = SKAction.sequence([moveDown1, moveRight1]);
self.runAction(sequence)
}
I ger the error:
Cannot invoke 'moveToY' with an argument list of type '(Int, duration:
Int)'
I also tried
let moveDown1 = SKAction.moveToY(position[0] as? Int, duration:speed[0] as? Int)
And got the same error again. Could you help me on this?
Thank you.
SKAction.moveToY accept a CGFloat for Y and an NSTimeInterval for duration. Therefore you need to change position's type to [CGFloat] and speed's type to [NSTimeInterval].
There are a few other things I notice with your code:
1. I think it's a typo - you've written let moveRight1 = SKAction.moveToY in your second function. Should that be SKAction.moveToX?
2. You're using speed to represent time. That's going to give you some strange results because speed and time are inversely proportional. Therefore, with your implementation, if you increase speed it's going to take longer for your node to move...
I would recommend writing your function like so:
func setTheNode(deltaPosition dp: CGVector, times: (x: NSTimeInterval, y: NSTimeInterval)){
let moveDown1 = SKAction.moveToY(dp.dx, duration: times.x)
let moveRight1 = SKAction.moveToX(dp.dy, duration: times.y)
let sequence = SKAction.sequence([moveDown1, moveRight1]);
self.runAction(sequence)
}

'(Int, Int)' is not identical to 'CGPoint'

I got error: '(Int, Int)' is not identical to 'CGPoint'
How to convert a (Int, Int) to CGPoint
let zigzag = [(100,100),
(100,150),(150,150),
(150,200)]
override func drawRect(rect: CGRect)
{
// Get the drawing context.
let context = UIGraphicsGetCurrentContext()
// Create the shape (a vertical line) in the context.
CGContextBeginPath(context)
//Error is here
CGContextAddLines(context, zigzag, zigzag.count)
// Configure the drawing environment.
CGContextSetStrokeColorWithColor(context,UIColor.redColor().CGColor)
// Request the system to draw.
CGContextStrokePath(context)
}
CGContextAddLines() expects an array of CGPoint. If you already have an
array of (Int, Int) tuples then you can convert it with
let points = zigzag.map { CGPoint(x: $0.0, y: $0.1) }
An alternate way to avoid the boilerplate code required to create instances of the same type is to make CGPoint implement the ArrayLiteralConvertible, making it initializable by assigning an array of CGFloat:
extension CGPoint : ArrayLiteralConvertible {
public init(arrayLiteral elements: CGFloat...) {
self.x = elements.count > 0 ? elements[0] : 0.0
self.y = elements.count > 1 ? elements[1] : 0.0
}
}
and then use it as follows:
let zigzag:[CGPoint] = [
[100,100],
[100,150],
[150,150],
[150,200]
]
A few notes:
stylistically, it doesn't look good - it would be good if literals could be used for tuples, but I am not aware of any way to do that
if an empty array is used, the CGPoint is initialized with x = 0 and y = 0
if an array with one element is used, it is initialized with y = 0
if more than 2 values are used, all the ones after the 2nd are ignored
If it tells you to use CGPoint, use it! Just (number,number) is a pair of ints.
let zigzag = [CGPointMake(100,100),
CGPointMake(100,150),CGPointMake(150,150),
CGPointMake(150,200)]
Yet another:
func CGPoints(points:(x:CGFloat, y:CGFloat)...) -> [CGPoint] {
return map(points) { CGPoint($0) }
}
let zigzag = CGPoints(
(100,100),(100,150),(150,150),(150,200)
)