For my studies, I have to create a game on iOS (like a space invaders). Actually, I'm stucked for the game area : I need that the bullet can be pulled from the maximum value of the x position no matter the form factor.
Indeed, my code works perfectly on the old form factor (as iPhone 8). However, on the new form factor (iPhone X, XS..), the planet can disappear of all the screen.
(I'm a beginner about Swift, so I know there is a lot of mistake in my code). But I think that the problem is from the fact that I divide by an int.
First picture : what I want (which works on iPhone 8)
Second one : My problem.
Thanks for you help (and sorry for my bad english, french people ahaha).
translate :
zone_de_jeu = game_area
largeur_jouable = width_playable
marge_largeur = width_margin
hauteur_jouable = height_playable
marge_hauteur = height_margin
enter code here
var zone_de_jeu: CGRect
override init(size: CGSize) {
let max_ratio_aspect: CGFloat = 16.0 / 9.0
let largeur_jouable = size.height / max_ratio_aspect
let marge_largeur = (size.width - largeur_jouable) / 2
let hauteur_jouable = size.width / max_ratio_aspect
let marge_hauteur = (size.height - hauteur_jouable) / 2
zone_de_jeu = CGRect(x: marge_largeur, y: marge_hauteur, width: largeur_jouable, height: hauteur_jouable)
super.init(size: size)
let initialisation_mouvement_planete = touch.location(in: self)
let mouvement_planete = touch.previousLocation(in: self)
let arrive_final_planete_x = initialisation_mouvement_planete.x - mouvement_planete.x
planete.position.x += arrive_final_planete_x
if planete.position.x > zone_de_jeu.maxX - planete.size.width / 6.5 {
planete.position.x = zone_de_jeu.maxX - planete.size.width / 6.5
}
if planete.position.x < zone_de_jeu.minX + planete.size.width / 6.5 {
planete.position.x = zone_de_jeu.minX + planete.size.width / 6.5
}
Should I modify, my divider by something more general ?
Related
I've run this Git. What I'm trying to do is when the node is further than the distance I set, for example 1 meter, node is removed. With in it, I've added this method (Code 1).
Code 1
func getDistanceBtw(cameraPostion: SCNVector3, nodePosition: SCNVector3) -> Float {
let powX = (cameraPostion.x - nodePosition.x) * (cameraPostion.x - nodePosition.x)
let powY = (cameraPostion.y - nodePosition.y) * (cameraPostion.y - nodePosition.y)
let powZ = (cameraPostion.z - nodePosition.z) * (cameraPostion.z - nodePosition.z)
let powXYZ = powX + powY + powZ
let distance = sqrt(powXYZ)
return distance
}
And in renderer didUpdate Node method, I added... (Code 2)
Code 2
let cameraPostion = sceneView.pointOfView?.position
let nodePosition = node.position
if getDistanceBtw(cameraPostion: cameraPostion!,
nodePosition: nodePosition) > 1 {
node.removeFromParentNode()
}
I thought this my solve what I want to achieve, it did something but not what I wanted.
For the life of me, I cannot understand why this shader animation is not running. Any help is appreciated. Please see the code below... the shader displays correctly, but does not animate. Thank you.
NOTE: Xcode 9.4.1, Swift 4.1, iOS 11.4
import SpriteKit
class GameScene: SKScene {
static let marquee: SKShader = {
let shader = SKShader(
source: "void main() {" +
"float np = mod(a_path_phase + v_path_distance / u_path_length, 1.0);" +
"vec3 hsb = vec3(np, 1.0, 1.0);" +
"vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);" +
"vec3 p = abs(fract(hsb.xxx + K.xyz) * 6.0 - K.www);" +
"vec3 rgb = hsb.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), hsb.y);" +
"vec4 df = SKDefaultShading();" +
"gl_FragColor = vec4(rgb, df.z);" +
"}"
)
shader.attributes = [SKAttribute(name: "a_path_phase", type: .float)]
return shader
} ()
private let beat: TimeInterval = 0.05
private var phase: Float = 0
override func didMove(to view: SKView) {
let node = SKShapeNode(rectOf: CGSize(width: 256, height: 256), cornerRadius: 16)
node.fillColor = .clear
node.strokeColor = .white
node.strokeShader = GameScene.marquee
node.lineWidth = 8
node.glowWidth = 4
node.run(
SKAction.repeatForever(
SKAction.sequence(
[
SKAction.wait(forDuration: beat),
SKAction.run() { [weak self] in
if let weak = self {
weak.phase = fmodf(weak.phase + Float(weak.beat), 1)
node.setValue(SKAttributeValue(float: weak.phase), forAttribute: "a_path_phase")
}
}
]
)
)
)
addChild(node)
}
}
I was also having trouble with .setValue(forAttribute:). It did not seem to work. Upon investigation, my conclusion is that...
I believe SKShapeNode.setValue(forAttribute:) is not working.
The fact that SKSpriteNode.setValue(forAttribute:) did work correctly under the same set of conditions led me to conclude that it was almost surely some bug in iOS in SKShapeNode.
The fact was that any SKAttributes I might set in
SKShapeNode.fillShader or SKShapeNode.strokeShader kept their
values set to 0.0 no matter what I did.
However, the same did not happen with SKSpriteNode.shader.
So I had to find a way to circumvent this bug.
Some sort of hack.
Fortunately, in my case, I did find a way.
And fortunately, it seems to be enough to solve your problem too.
What you can do, in this case, is to pass the value of your phase as one of the components of the strokeColor, for example, the red component.
To do this you will have to normalize the phase to values between 0.0 to 1.0.
You can pass a phase multiplier using a shader.uniform to help denormalize it.
Then read the phase value inside the shader from SKDefaultShading().x, (the red component).
Make sure to set the alpha component of the
strokeColor to 1.0, for SKDefaultShading() will have its color
component values already premultiplied by its alpha.
Now, because your strokeColor is already being used to pass the phase, you can pass the actual color of the stroked line as a vec4 shader.uniform.
In my case, I also made use of u_time to help in performing my
animation. Stuff like fract(u_time) can be very useful.
Here is how the corrected code might look like:
> NOTE: There is a much simpler solution. Look by the end of this post.
import SpriteKit
class GameScene: SKScene {
private typealias Me = GameScene
static let phaseMultiplier: Float = 1000
static let marquee: SKShader = {
let shader = SKShader(
source: "void main() {" +
"float red = SKDefaultShading().x;" +
"float phase = red * u_phase_multiplier;" +
"float np = mod(phase + v_path_distance / u_path_length, 1.0);" +
"vec3 hsb = vec3(np, 1.0, 1.0);" +
"vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);" +
"vec3 p = abs(fract(hsb.xxx + K.xyz) * 6.0 - K.www);" +
"vec3 rgb = hsb.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), hsb.y);" +
"vec4 df = u_stroke_color;" +
"gl_FragColor = vec4(rgb, df.z);" +
"}"
)
let white = vector_float4.init(1, 1, 1, 1)
shader.uniforms = [
SKUniform(name: "u_phase_multiplier", float: Me.phaseMultiplier),
SKUniform(name: "u_stroke_color", vectorFloat4: white)]
return shader
} ()
private let beat: TimeInterval = 0.05
private var phase: Float = 0
override func didMove(to view: SKView) {
let node = SKShapeNode(rectOf: CGSize(width: 256, height: 256), cornerRadius: 16)
node.fillColor = .clear
node.strokeColor = .white
node.strokeShader = Me.marquee
node.lineWidth = 8
node.glowWidth = 4
node.run(
SKAction.repeatForever(
SKAction.sequence(
[
SKAction.wait(forDuration: beat),
SKAction.run() { [weak self] in
if let weak = self {
weak.phase = fmodf(weak.phase + Float(weak.beat), 1)
let phase = weak.phase / Me.phaseMultiplier
node.strokeColor = SKColor.init(red: phase, green: 0, blue: 0, alpha: 1)
}
}
]
)
)
)
addChild(node)
}
}
NOTE: I did not compile the code above posted. I should serve merely as a sketch.
The day after this post, I found out that attribute parameters are not supposed to be used in fragment shaders, only in vertex shaders.
Take a look at StackOverflow question:
In WebGL what are the differences between an attribute, a uniform, and a varying variable?
The SDK documentation of SKAttribute and SKAttributeValue, however, seem to suggest that there may be no direct relation between "SKAttributes" and actual shader attribute parameters. They just seem to be named the same.
Simpler Solution:
In the end, however, there is never any need to make use of any hack like the one I show here.
All that needs to be done is to set the value of a uniform through SKShader.uniformNamed("u_phase")?.floatValue = phase, instead of using .setValue(forAttribute:) just as jmdecombe, correctly, later pointed out.
I will keep my answer here for reference, as it presents an unconventional solution to a problem.
I am using Google Mobile Vision to detect face landmarks and the distances between them. The weird thing about this error is that it is not consistent and does not show up all the time I run the same function.
Here is the error message...
TERMINATION
ert_Error ebs_ObjectStack::~ebs_ObjectStack():
Stack is not empty at destruction.
This can be an indiaction for a stack leak.
Please check the code where this instance was used.
libc++abi.dylib: terminating with uncaught exception of type ert_Error
(lldb)
Usually the first time I run the app in 24 hours everything goes smoothly and occasionally afterwards. For the most part though it appears. As I said earlier I am using Google Mobile Vision to detect and store certain values.
Here is the function that is causing the error...
func train2(image2: UIImage) {
print("TRAIN2 has been called")
// let options1 = [GMVDetectorFaceLandmarkType: GMVDetectorFaceLandmark.all.rawValue, GMVDetectorFaceClassificationType: GMVDetectorFaceClassification.all.rawValue, GMVDetectorFaceTrackingEnabled: true, GMVDetectorFaceMinSize: 0.09] as [String : Any]
var deviceOrientation = UIDevice.current.orientation
var devicePostion = AVCaptureDevice.Position.back //.Position(rawValue: (currentCameraPosition?.hashValue)!)
if CameraController().currentCameraPosition == .rear {
devicePostion = AVCaptureDevice.Position.back
}else if CameraController().currentCameraPosition == .front {
devicePostion = AVCaptureDevice.Position.front
}
var lastKnownDeviceOrientation = UIDeviceOrientation(rawValue: 0)
var orientation = GMVUtility.imageOrientation(from: deviceOrientation, with: devicePostion, defaultDeviceOrientation: lastKnownDeviceOrientation!)
var options3 = [GMVDetectorImageOrientation:orientation.rawValue]
// var sampbuff = cvPixelBufferRef(from: image2)
// var AImage = GMVUtility.sampleBufferTo32RGBA(sampbuff as! CMSampleBuffer)
var happyFaces = GfaceDetector.features(in: image2, options: options3) as! [GMVFaceFeature] // THE ERROR SEEMS TO ORIGINATE FROM THIS LINE
print("The Amount Of Happy Faces is: \(happyFaces.count)")
for face:GMVFaceFeature in happyFaces {
print(face.smilingProbability)
print("Go Google")
var YAngle = Float(face.headEulerAngleY)
var ZAngle = Float(face.headEulerAngleZ)
print("Y Angle \(YAngle), Z Angle \(ZAngle)")
if YAngle > -2.0 && YAngle < 4.0 && ZAngle > -2.0 && ZAngle < 2.0 {
var proportion = ((face.bounds.width * face.bounds.width) + (face.bounds.height * face.bounds.height))
var ratio = sqrtf(Float(proportion))
DBetweenEyes = (Float(distanceBetween(face.leftEyePosition, face.rightEyePosition)) * 1000) / ratio
DBetweenLEyeAndLEar = (Float(distanceBetween(face.leftEyePosition, face.leftEarPosition)) * 1000) / ratio
DBetweenREyeAndREar = (Float(distanceBetween(face.rightEyePosition, face.rightEarPosition)) * 1000) / ratio
DBetweenLEarAndREar = (Float(distanceBetween(face.leftEarPosition, face.rightEarPosition)) * 1000) / ratio
DBetweenLEyeAndNose = (Float(distanceBetween(face.leftEyePosition, face.noseBasePosition)) * 1000) / ratio
DBetweenREyeAndNose = (Float(distanceBetween(face.rightEyePosition, face.noseBasePosition)) * 1000) / ratio
DBetweenLEyeAndMouth = (Float(distanceBetween(face.leftEyePosition, face.bottomMouthPosition)) * 1000) / ratio
DBetweenREyeAndMouth = (Float(distanceBetween(face.rightEyePosition, face.bottomMouthPosition)) * 1000) / ratio
DBetweenLEarAndMouth = (Float(distanceBetween(face.leftEarPosition, face.bottomMouthPosition)) * 1000) / ratio
DBetweenREarAndMouth = (Float(distanceBetween(face.rightEarPosition, face.bottomMouthPosition)) * 1000) / ratio
print("Distances Established")
}
}
}
I have just discovered that the error is very rare when called when a button is pressed after the view loads. The error occurs far more frequently when called in the viewDidLoad method. Could this indicate some sort of pattern? How would I fix this error?
Any help or suggestions are helpful.
Thanks in advance!
I was reading a blog and there was a function like below. I want to know what could be the purpose of writing it in this way?
public func moveNinjaToBottomRight() {
ninja.center = {
let x = (frame.maxX - ninja.frame.width / 2)
let y = (frame.maxY - ninja.frame.height / 2)
return CGPoint(x: x, y: y)
}()
}
This is just a matter of style.
Writing the function this way:
public func moveNinjaToBottomRight() {
ninja.center = {
let x = (frame.maxX - ninja.frame.width / 2)
let y = (frame.maxY - ninja.frame.height / 2)
return CGPoint(x: x, y: y)
}()
}
adds clarity to the purpose of the function. At a glance you can see that it just updates ninja.center and you know that the code inside the closure is computing that new center. This would especially be true if computing the new center took many more lines.
The other way to write it would be:
public func moveNinjaToBottomRight() {
let x = (frame.maxX - ninja.frame.width / 2)
let y = (frame.maxY - ninja.frame.height / 2)
ninja.center = CGPoint(x: x, y: y)
}
In this case, you have to read through all of the lines before you get to what the function really does. Again, for such a short function it makes little difference, but it it were longer it would take the reader longer to access what was going on.
I would recommend only using this functional style if the code inside the closure has a specific purpose. In this case it is creating a CGPoint. If the code in the closure was causing side effects like playing sounds or updating the user interface, then that I believe the imperative style used by the second example makes more sense.
The parens after the braced expression are called an Immediately Invoked Closure and have the effect of executing the closure inside the braces.
The style is commonly used for initialisation where it serves a valuable purpose of allowing you to create a property with complex initialisation:
public let ninja: UIImageView = {
let image = UIImage(named: "ninja")
let view = UIImageView(image: image)
view.frame = CGRect(x: 0, y: 0, width: 45, height: 39)
return view
}()
Using it inside a function as you have shown has no technical purpose. It is likely to be less efficient unless the compiler is capable of optimising it to simpler logic.
It maintains a consistency with the way they are initialising variables elsewhere in the sample (assuming you are looking at the same code I found).
In C programming, it's common to just use a pair of braces to provide local scoping of variables. That is a robust programming practice to ensure they are not re-used further down a method.
The Swift idiom is to use do { ...} if you want to achieve such scoping.
So, to be clear, the function you are looking at could have been written plainly
public func moveNinjaToBottomRight() {
let x = (frame.maxX - ninja.frame.width / 2)
let y = (frame.maxY - ninja.frame.height / 2)
ninja.center = CGPoint(x: x, y: y)
}
Or even
public func moveNinjaToBottomRight() {
ninja.center = CGPoint(
x: (frame.maxX - ninja.frame.width / 2),
y: (frame.maxY - ninja.frame.height / 2))
}
Or, if there was more logic and they wanted local scoping.
public func moveNinjaToBottomRight() {
do {
let x = (frame.maxX - ninja.frame.width / 2)
let y = (frame.maxY - ninja.frame.height / 2)
ninja.center = CGPoint(x: x, y: y)
}
// more code here which wants to use x and y names
// but is not reusing the variables above
}
When i give two nodes in my game a physics body, they stop doing what i want them to do. I have two bars at the bottom of the scene,one starts on the left, and the other is on the right, and they continuously go to meet at the middle then move back to there starting spot, like a floor opening up. Without a physics body, they do exactly this, but when i give them a physics body,when they both meet in the middle, they stop there and don't move back out.
Here are the bars:
bar.position = CGPoint(x:-self.size.width * 0.2, y:(self.size.height * 0.03));
bar.physicsBody = SKPhysicsBody(texture: text, size: text.size())
bar.physicsBody?.categoryBitMask = PhysicsCategory.bar
bar.physicsBody?.contactTestBitMask = PhysicsCategory.ball
bar.physicsBody?.collisionBitMask = PhysicsCategory.bar
self.addChild(bar)
bar1.position = CGPoint(x:self.size.width * 1.2, y:(self.size.height * 0.03));
bar1.physicsBody = SKPhysicsBody(texture: text, size: text.size())
bar1.physicsBody?.categoryBitMask = PhysicsCategory.bar
bar1.physicsBody?.contactTestBitMask = PhysicsCategory.ball
bar1.physicsBody?.collisionBitMask = PhysicsCategory.bar
self.addChild(bar1)
And this is how they move. This is in the update method:
if (bar.position.x == -self.size.width * 0.2)
{
let move = SKAction.moveToX(self.size.width * 0.012, duration: 1);
bar.runAction(move);
} else if (bar.position.x == self.size.width * 0.012) {
let move = SKAction.moveToX(-self.size.width * 0.2, duration: 1)
bar.runAction(move)
}
if (bar1.position.x == self.size.width * 1.2)
{
let move = SKAction.moveToX(self.size.width * 0.988, duration: 1);
bar1.runAction(move);
} else if (bar1.position.x == self.size.width * 0.988) {
let move = SKAction.moveToX(self.size.width * 1.2, duration: 1)
bar1.runAction(move)
}
What could be causing them to stick together at the middle when i give them a physics body.