How to make enemy have independent lives? - ios

PLEASE HELP!!! I have been trying to figure this out for along time. I have searched the internet and i cannot find anything that will help me.
I am currently making a game in which you are a space ship in the middle and enemy ships are moving towards you and you have to shoot them. some enemies have different lives. for example: a red ship takes one shot to explode, the blue ship takes 3, etc. I have everything to work only the lives. for example: whenever a blue ship is called on to the screen i shoot it once so its life goes down to 2. but whenever a another blue ship is called the first blue ship has its life reset back to 3 again. Is there anyway I can make it so that whenever a ship looses lives it remains that way even if other ships are called ?
this is my ship function that gets called and adds enemy space ships onto the screen:
func VillainRight(){
let TooMuch = self.size.width
let point = UInt32(TooMuch)
life = 3
let VillainR = SKSpriteNode(imageNamed: "BlueVillain")
VillainR.zPosition = 2
VillainR.position = CGPoint(x: self.frame.minX,y: CGFloat(arc4random_uniform(point)))
//This code makes the villain's Zposition point towards the SpaceShip
let angle = atan2(SpaceShip.position.y - VillainR.position.y, SpaceShip.position.x - VillainR.position.x)
VillainR.zRotation = angle - CGFloat(M_PI_2)
let MoveToCenter = SKAction.move(to: CGPoint(x: self.frame.midX, y: self.frame.midY), duration: 15)
//Physics World
VillainR.physicsBody = SKPhysicsBody(rectangleOf: VillainR.size)
VillainR.physicsBody?.categoryBitMask = NumberingPhysics.RightV
VillainR.physicsBody?.contactTestBitMask = NumberingPhysics.Laser | NumberingPhysics.SpaceShip
VillainR.physicsBody?.affectedByGravity = false
VillainR.physicsBody?.isDynamic = true
VillainR.run(MoveToCenter)
addChild(VillainR)
}
This is the code that calls this function:
_ = Timer.scheduledTimer(timeInterval: 5.0, target: self, selector: #selector(Level1.VillainRight), userInfo: nil, repeats: true)
I am using the spritekit in Swift.
Thank You Very Much in advance!

That is happening because life variable is declared as a property of a scene and it is not local to a specific node (enemy ship). You can solve this in a few ways... First way would be using node's userData property:
import SpriteKit
let kEnergyKey = "kEnergyKey"
class GameScene: SKScene, SKPhysicsContactDelegate {
override func didMove(to view: SKView) {
let blueShip = getShip(energy: 3)
let greenShip = getShip(energy: 2)
let redShip = getShip(energy: 1)
if let blueShipEnergy = blueShip.userData?.value(forKey: kEnergyKey) as? Int {
print("Blue ship has \(blueShipEnergy) lives left")
//hit the ship
blueShip.userData?.setValue(blueShipEnergy-1, forKey: kEnergyKey)
if let energyAfterBeingHit = blueShip.userData?.value(forKey: kEnergyKey) as? Int {
print("Blue ship has \(energyAfterBeingHit) lives left")
}
}
}
func getShip(energy:Int)->SKSpriteNode{
//determine which texture to load here based on energy value
let ship = SKSpriteNode(color: .purple, size: CGSize(width: 50, height: 50))
ship.userData = [kEnergyKey:energy]
return ship
}
}
This is what the docs say about userData property:
You use this property to store your own data in a node. For example,
you might store game-specific data about each node to use inside your
game logic. This can be a useful alternative to creating your own node
subclasses to hold game data.
As you can see, an alternative to this is subclassing of a node (SKSpriteNode):
class Enemy:SKSpriteNode {
private var energy:Int
//do initialization here
}

Related

iOS ARKit: Large size object always appears to move with the change in the position of the device camera

I am creating an iOS ARKit app where I wanted to place a large object in Augmented Reality.
When I am trying to place the object at a particular position it always appears to be moving with the change in camera position and I am not able to view the object from all angles by changing the camera position.
But if I reduce it's scale value to 0.001 (Reducing the size of the object), I am able to view the object from all angles and the position of the placed object also does not change to that extent.
Bounding Box of the Object:-
Width = 3.66
Height = 1.83
Depth = 2.438
Model/Object Url:-
https://drive.google.com/open?id=1uDDlrTIu5iSRJ0cgp70WFo7Dz0hCUz9D
Source Code:-
import UIKit
import ARKit
import SceneKit
class ViewController: UIViewController {
#IBOutlet weak var sceneView: ARSCNView!
private let configuration = ARWorldTrackingConfiguration()
private var node: SCNNode!
//MARK: - Life cycle
override func viewDidLoad() {
super.viewDidLoad()
self.sceneView.showsStatistics = false
self.sceneView.debugOptions = [ARSCNDebugOptions.showFeaturePoints]
self.sceneView.automaticallyUpdatesLighting = false
self.sceneView.delegate = self
self.addTapGesture()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
configuration.planeDetection = .horizontal
self.sceneView.session.run(configuration)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
self.sceneView.session.pause()
}
//MARK: - Methods
func addObject(hitTestResult: ARHitTestResult) {
let scene = SCNScene(named: "art.scnassets/Cube.obj")!
let modelNode = scene.rootNode.childNodes.first
modelNode?.position = SCNVector3(hitTestResult.worldTransform.columns.3.x,
hitTestResult.worldTransform.columns.3.y,
hitTestResult.worldTransform.columns.3.z)
let scale = 1
modelNode?.scale = SCNVector3(scale, scale, scale)
self.node = modelNode
self.sceneView.scene.rootNode.addChildNode(modelNode!)
let lightNode = SCNNode()
lightNode.light = SCNLight()
lightNode.light?.type = .omni
lightNode.position = SCNVector3(x: 0, y: 10, z: 20)
self.sceneView.scene.rootNode.addChildNode(lightNode)
let ambientLightNode = SCNNode()
ambientLightNode.light = SCNLight()
ambientLightNode.light?.type = .ambient
ambientLightNode.light?.color = UIColor.darkGray
self.sceneView.scene.rootNode.addChildNode(ambientLightNode)
}
private func addTapGesture() {
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(didTap(_:)))
self.sceneView.addGestureRecognizer(tapGesture)
}
#objc func didTap(_ gesture: UIPanGestureRecognizer) {
let tapLocation = gesture.location(in: self.sceneView)
let results = self.sceneView.hitTest(tapLocation, types: .featurePoint)
guard let result = results.first else {
return
}
let translation = result.worldTransform.translation
guard let node = self.node else {
self.addObject(hitTestResult: result)
return
}
node.position = SCNVector3Make(translation.x, translation.y, translation.z)
self.sceneView.scene.rootNode.addChildNode(self.node)
}
}
extension float4x4 {
var translation: SIMD3<Float> {
let translation = self.columns.3
return SIMD3<Float>(translation.x, translation.y, translation.z)
}
}
GIF of the Problem:-
Video URL of the Problem:-
https://drive.google.com/open?id=1E4euZ0ArEtj2Ffto1pAOfVZocV08EYKN
Approaches Tried:-
Tried to place the object at the origin
modelNode?.position = SCNVector3(0, 0, 0)
Tried to place the object at some distance away from the device camera
modelNode?.position = SCNVector3(0, 0, -800)
Tried with the different combinations of worldTransform/localTransform columns
modelNode?.position = SCNVector3(hitTestResult.worldTransform.columns.3.x, hitTestResult.worldTransform.columns.3.y, hitTestResult.worldTransform.columns.3.z)
modelNode?.position = SCNVector3(hitTestResult.worldTransform.columns.2.x, hitTestResult.worldTransform.columns.2.y, hitTestResult.worldTransform.columns.2.z)
modelNode?.position = SCNVector3(hitTestResult.worldTransform.columns.1.x, hitTestResult.worldTransform.columns.1.y, hitTestResult.worldTransform.columns.1.z)
modelNode?.position = SCNVector3(hitTestResult.worldTransform.columns.1.x, hitTestResult.worldTransform.columns.2.y, hitTestResult.worldTransform.columns.3.z)
modelNode?.position = SCNVector3(hitTestResult.localTransform.columns.3.x, hitTestResult.localTransform.columns.3.y, hitTestResult.localTransform.columns.3.z)
But still of no luck. It still appears to be moving with the device camera and not stuck to a position where it has been placed.
Expected Result:-
Object should be of actual size (Scale should be of 1.0). Their should be no reduction in the scale value.
Once placed at a particular position it should not move with the movement of the device camera.
Object can be seen from all angles with the movement of the device camera without any change in object position.
Unlike stated in the accepted answer, the issue is probably not about the tracking quality or a bug in the model. It looks like the model is not correctly placed on top of the ground, probably due to a mispositioned pivot point, and some part of the model stays under ground. So when you move the camera, since the part under ground is not occluded by the floor, it looks like it is shifting. Have a look at this picture:
The pivot points of the models provided by Apple are positioned correctly so that when it is placed on top of a plane on the ground, its parts stay above ground.
If you correctly position the pivot point of the model, it should work correctly, independent of the model type.
I found out the root cause of the issue. The issue was related to the Model which I was using for AR. When, I replaced the model with the one provided in this link:- https://developer.apple.com/augmented-reality/quick-look/. I was not facing any issues. So, if anyone face such type of issues in future I would recommend to use any of the model provided by Apple to check if the issue persists with it or not.
I experienced the same issue.
Whenever I try to change anything in our Model of .usdz type (which is actually an Encrypted and compressed type) we cannot edit or change anything in it. If I edit or change a little position then it behaves same way as highlighted in the question.
To handle this issue, I just moved the old model .usdz to trash and and copied the original file (again) to XCode and then it worked.

SpriteKit Game Losing FPS after main scene restart

I'm creating a game using Sprite Kit, and I have an issue where the game slowly loses fps with every reiteration of the my main scene.
I've tried to use Xcode's Instruments, but have gotten no closer to the root of my issue.
This is my function to end the game and present the Game Over Scene:
func endGame(){
let endGameScene = SKScene(fileNamed: "EndGameScene" ) as! EndGameScene
endGameScene.score = score
//let transition = SKTransition.push(with: SKTransitionDirection.right, duration: 0.5)
self.view?.presentScene(endGameScene)
transitionNode = SKSpriteNode(color: UIColor.white, size: self.frame.size)
transitionNode.zPosition = 5
self.addChild(transitionNode)
transitionNode.run(SKAction.fadeIn(withDuration: 0.3))
gameOver = true
}
This is my function to present the Game Scene as a result of the
restart button being tapped:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first
if let location = touch?.location(in: self) {
let nodeArray = self.nodes(at: location)
if(nodeArray.first?.name == "replayButton"){
self.view?.presentScene(gameScene)
transitionNode = SKSpriteNode(color: UIColor.white, size: self.frame.size)
transitionNode.zPosition = 5
self.addChild(transitionNode)
transitionNode.run(SKAction.fadeIn(withDuration: 0.3))
I also checked for leaks using instruments, but none come up. Similarly, the heaviest stack trace log shows no areas in my class file that took the most time, as shown here: (wasn't able to upload an image for some reason, so here's the link)
https://puu.sh/wtrMZ/1bb8c6542a.png
The game starts at a solid 60 fps, and then drops to 58, then to 57, then to 55, then to 54, and so on. I tried the deinit {} test, and the scene deallocates fine. I used the debugger to check my memory usage, and while it does go up ever so slightly, it barely breaks 50 mb in a single sitting. I also don't think it's a problem of removing unused nodes, because I make sure to remove them in every instance, but I could be wrong:
deinit
deinit {
self.removeAllChildren()
}
I am genuinely stumped, as I don't even know how to begin to fix this. Any help or constructive criticism is very much appreciated, thanks.
EDIT:
I never invalidated the two main timers in my Game Scene class. I simply invalidated them in my endGame function:
`func endGame(){
let endGameScene = SKScene(fileNamed: "EndGameScene" ) as! EndGameScene
endGameScene.score = score
//let transition = SKTransition.push(with: SKTransitionDirection.right, duration: 0.5)
self.view?.presentScene(endGameScene)
transitionNode = SKSpriteNode(color: UIColor.white, size: self.frame.size)
transitionNode.zPosition = 5
self.addChild(transitionNode)
transitionNode.run(SKAction.fadeIn(withDuration: 0.3))
pillarTimer.invalidate()
durationTimer.invalidate()
gameOver = true
}`
Fixed my issue perfectly.

Sprite-Kit: Staying Inside Borders Created with Tile Editor

I'm testing the possibility of the tile editor that comes with Xcode 8 (8.2.2). And I've created a PacMan-like map as shown above. There's a game character at the top-left corner in a rectangle. I wonder if there's an easy way of making the game character staying inside the blue borders? So far, I've set a (red) wall to the left like the following through the scene editor. And I have the following lines of code.
struct PhysicsCategory {
static let None: UInt32 = 0
static let Player: UInt32 = 0b1 // 1
static let Edge: UInt32 = 0b10 // 2
static let Wall: UInt32 = 0b100 // 4
}
class GameScene: SKScene {
// MARK: - Variables
var background: SKTileMapNode! // background
var player: SKNode! // player
// MARK: - DidMove
override func didMove(to view: SKView) {
setupNodes()
}
func setupNodes() {
background = self.childNode(withName: "World") as! SKTileMapNode
background.physicsBody = SKPhysicsBody(edgeLoopFrom: background.frame)
background.physicsBody?.categoryBitMask = PhysicsCategory.Edge
let wall = self.childNode(withName: "Wall")
wall?.physicsBody = SKPhysicsBody(rectangleOf: (wall?.frame.size)!)
wall?.physicsBody?.isDynamic = false
wall?.physicsBody?.categoryBitMask = PhysicsCategory.Wall
player = self.childNode(withName: "Player")
player.physicsBody = SKPhysicsBody(circleOfRadius: 32)
player.physicsBody?.categoryBitMask = PhysicsCategory.Player
player.physicsBody?.collisionBitMask = 4
player.physicsBody?.allowsRotation = false
}
}
The user will get to control the player position with CoreMotion. For now, the game character does respect the left edge. But if the map gets complicated, I could end up placing a lot of walls here and there. And that kind of kills the fun as it could be time-consuming. So, again, is there a simplier way of making the game character collide the map borders?
Take a look at GameplayKit's pathfinding classes, specifically GKGridGraph and GKGridGraphNode which allow you to specify a graph that only has these kind of rectilinear paths.
There is a good tutorial from Apple here: https://developer.apple.com/library/content/documentation/General/Conceptual/GameplayKit_Guide/Pathfinding.html
And a demo app here: https://developer.apple.com/library/content/samplecode/Pathfinder_GameplayKit/Introduction/Intro.html#//apple_ref/doc/uid/TP40016461

How to consolidate all characters under one main player to perform same action in SpriteKit

I am building a game in SpriteKit. The game will include 5 players and instead of coding SKActions five times for every player, I want to consolidate all characters into one and code one time for each SKAction, but the code below doesn't seem to work. Am I missing something?
import SpriteKit
var player1 = SKSpriteNode()
var player2 = SKSpriteNode()
var player3 = SKSpriteNode()
var player4 = SKSpriteNode()
var player5 = SKSpriteNode()
var mainPlayer = SKSpriteNode()
// in view did load
player1 = mainPlayer
player2 = mainPlayer
player3 = mainPlayer
player4 = mainPlayer
player5 = mainPlayer
//in touches began
let rightMoveAction = SKAction.moveByX(50, y: 0, duration: 0.1)
mainPlayer.runAction(SKAction.repeatActionForever(rightMoveAction))
I'm assuming your motivation is to make the code shorter and because mainPlayer is probably a complex node that you don't want to replicate the code 5 times. You don't show where you add the nodes to the scene, but the way you currently have it will crash when you try to add player2 because it is already added. It looks like you are trying to simply make 5 of the same character that can perform the same action.
Here is a general strategy I would use to try to accomplish what you're after. Since your mainPlayer node is probably some complex character that you will be reusing (possibly even in other scenes), then you might consider making it into its own class (and put the code in a separate .swift file). You can then add the functions it will use to move and perform other SKActions
For example:
import SpriteKit
class Character : SKSpriteNode {
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override init(texture: SKTexture?, color: UIColor, size: CGSize) {
super.init(texture: texture, color: color, size: size)
//setup your node here
self.userInteractionEnabled = true//you need this to detect touches
//log so you know it was created - take this out at some point
print("character created")
}
//create an example action that will move the node to the right by 50
func moveRight() {
print("moving right")
self.runAction(SKAction.moveByX(50, y: 0, duration: 0.1))
}
//you can also override the touch functions so you'll know when the node was touched
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
print("charater touched")
}
}
Then in your scene, when you want to create and use a new character, you can do this:
//create the character
let newPlayer = Character.init(texture: nil, color: UIColor.blueColor(), size: CGSizeMake(50, 50))
//position the character where you want
newPlayer.position = CGPointMake(self.frame.size.width / 2, self.frame.size.height / 2)
//add the character to the scene
self.addChild(newPlayer)
/move the character to the right
newPlayer.moveRight()
Making things more "modular" like this is great for re-usability of code. Now you can reuse the Character class whenever and wherever you need. Even in an entirely different game.
In terms of making all characters run the same action with one function call, I don't know of anything that would allow this. Although you could write your own function for it by perhaps placing all of the character nodes into an array and passing the array as an argument to a custom function, but personally I would just call the moveRight function on each of the nodes after you instantiated them.

How can i have multiple select options for a single image?

I am using SceneKit to import a 3d image model of a human body. When i select a particular location point in the image, i want the app to recognize the body part and perform a different function for each part. How do i go about implementing this? What is the best way to do this?
P.s. when the image is rotated it shows a different view. I need the app to be able to recognize the body part even when it is rotated by the user. Any guidance as to how to proceed would be appreciated..
Here's a simple SceneKit picking example.
The scene is setup in the viewDidLoad, for your use case I'd expect a scene would be loaded from file (best done in another method). This file will hopefully have the different components you wish to pick as separate components in a tree-like hierarchy. The author of this 3D body model will have hopefully labelled these components appropriately so that your code can identify what to do when your left-femur is selected (and not comp2345).
For a complex model expect several 'hits' for any xy coordinate as you will be returned all nodes intersected by the hit ray. You may wish to only use the first hit.
import UIKit
import SceneKit
class ViewController: UIViewController {
#IBOutlet var scenekitView: SCNView!
override func viewDidLoad() {
super.viewDidLoad()
let scene = SCNScene()
let boxNode = SCNNode(geometry: SCNBox(width: 1, height: 1, length: 1, chamferRadius: 0))
boxNode.name = "box"
scene.rootNode.addChildNode(boxNode)
let sphereNode = SCNNode(geometry: SCNSphere(radius: 1))
sphereNode.name = "sphere"
sphereNode.position = SCNVector3Make(2, 0, 0)
boxNode.addChildNode(sphereNode)
let torusNode = SCNNode(geometry: SCNTorus(ringRadius: 1, pipeRadius: 0.3))
torusNode.name = "torus"
torusNode.position = SCNVector3Make(2, 0, 0)
sphereNode.addChildNode(torusNode)
scenekitView.scene = scene
scenekitView.autoenablesDefaultLighting = true
scenekitView.allowsCameraControl = true
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
//get the first touch location in screen coordinates
guard let touch = touches.first else {
return
}
//convert the screen coordinates to view coordinates as the SCNView make not take
//up the entire screen.
let pt = touch.locationInView(self.scenekitView)
//pass a ray from the points 2d coordinates into the scene, returning a list
//of objects it hits
let hits = self.scenekitView.hitTest(pt, options: nil)
for hit in hits {
//do something with each hit
print("touched ", hit.node.name!)
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}

Resources