Character is not moving in desired scene in scenekit - ios

i want to move my character in scene kit .i have a map as at github Fpscontroller and i want my character move in this map but when ever i touch to move character it jumps and don,t know where is goes here is my code.`
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches {
if CGRectContainsPoint(gameView.virtualDPadBounds(), touch.locationInView(gameView)) {
if padTouch == nil {
padTouch = touch
controllerStoredDirection = float2(0.0)
}
} else if panningTouch == nil {
panningTouch = touches.first
}
if padTouch != nil && panningTouch != nil {
break
}
}
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
if let touch = panningTouch {
let displacement = (float2(touch.locationInView(view)) - float2(touch.previousLocationInView(view)))
panCamera(displacement)
}
if let touch = padTouch {
let displacement = (float2(touch.locationInView(view)) - float2(touch.previousLocationInView(view)))
controllerStoredDirection = clamp(mix(controllerStoredDirection, displacement, t: ViewController.controllerAcceleration), min: -ViewController.controllerDirectionLimit, max: ViewController.controllerDirectionLimit)
}
}
func commonTouchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
if let touch = panningTouch {
if touches.contains(touch) {
panningTouch = nil
}
}
if let touch = padTouch {
if touches.contains(touch) || event?.touchesForView(view)?.contains(touch) == false {
padTouch = nil
controllerStoredDirection = float2(0.0)
}
}
}
override func touchesCancelled(touches: Set<UITouch>?, withEvent event: UIEvent?) {
commonTouchesEnded(touches!, withEvent: event)
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
commonTouchesEnded(touches, withEvent: event)
}
i followed apple fox example for moving with touch and in my viewcontroller
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
func walkGestureRecognized(gesture: UIPanGestureRecognizer) {
if gesture.state == UIGestureRecognizerState.Ended || gesture.state == UIGestureRecognizerState.Cancelled {
gesture.setTranslation(CGPointZero, inView: self.view)
}
}
func renderer(aRenderer: SCNSceneRenderer, updateAtTime time: NSTimeInterval) {
//get walk gesture translation
let translation = walkGesture.translationInView(self.view)
//create impulse vector for hero
let angle = heroNode.presentationNode.rotation.w * heroNode.presentationNode.rotation.y
var impulse = SCNVector3(x: max(-1, min(1, Float(translation.x) / 50)), y: 0, z: max(-1, min(1, Float(-translation.y) / 50)))
impulse = SCNVector3(
x: impulse.x * cos(angle) - impulse.z * sin(angle),
y: 0,
z: impulse.x * -sin(angle) - impulse.z * cos(angle)
)
heroNode.physicsBody?.applyForce(impulse, impulse: true)
let scene = gameView.scene!
let direction = characterDirection()
let groundNode = character.walkInDirection(direction, time: time, scene: scene, groundTypeFromMaterial:groundTypeFromMaterial)
setting character position and direction....
private func characterDirection() -> float3 {
let controllerDirection = self.controllerDirection()
var direction = float3(controllerDirection.x, 0.0, controllerDirection.y)
if let pov = gameView.pointOfView {
let p1 = pov.presentationNode.convertPosition(SCNVector3(direction), toNode: nil)
let p0 = pov.presentationNode.convertPosition(SCNVector3Zero, toNode: nil)
direction = float3(Float(p1.x - p0.x), 0.0, Float(p1.z - p0.z))
if direction.x != 0.0 || direction.z != 0.0 {
direction = normalize(direction)
}
}
return direction
}
there are actually two scenes called in view controller class one is empty scene called for mapNode as hereoNode and other is character sceneNode
let scene = SCNScene()
scene.rootNode.addChildNode(heroNode)
let personScene = SCNScene(named: "game.scnassets/person.scn")!
personNode = personScene.rootNode.childNodeWithName("person", recursively: true)
personNode?.position = SCNVector3(x:4.5,y:0,z:25)
node.addChildNode(personNode)
scene.rootNode.addChildNode(character.node)
i am updating my question with some more details as you can see in this screen shot image i m attatching here enter image description here i want to move this fox in map with touch but it is not working it just jump and go out from map when i touch .....plz someone tell me where is my fault in code

Related

make SKPhysicsBody unidirectional

I have a SKSpriteNode as a ball, it's been given all the SKPhysicsBody properties move around in all direction. What I want now is to make it unidirectional (only move in that direction it hasn't move to before and not go back in to a path it had move upon). Currently I have following thoughts on this the problem,
make a fieldBitMask, to the path that is iterated by it and repel
the ball to not go back
apply some kind of force/ impulses on the ball from touchesBegan/ touchesMoved method to keep it from going back
something that can be handled in update method
a lifesaver from stackflowoverflow, who is coding even on the weekend :)
Supporting Code snippets for better understanding,
//getting current touch position by using UIEvent methods
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else {return}
let location = touch.location(in: self)
lastTouchPoint = location
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else {return}
let location = touch.location(in: self)
lastTouchPoint = location
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
lastTouchPoint = nil
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
lastTouchPoint = nil
}
//ball created
func createPlayer(){
player = SKSpriteNode(imageNamed: "player")
player.position = CGPoint(x: 220, y: 420)
player.zPosition = 1
//physics for ball
player.physicsBody = SKPhysicsBody(circleOfRadius: player.size.width / 2)
player.physicsBody?.allowsRotation = false
player.physicsBody?.linearDamping = 0.5
player.physicsBody?.categoryBitMask = collisionTypes.player.rawValue
player.physicsBody?.contactTestBitMask = collisionTypes.finish.rawValue
player.physicsBody?.collisionBitMask = collisionTypes.wall.rawValue
addChild(player)
}
//unwarp the optional property, calclulate the postion between player touch and current ball position
override func update(_ currentTime: TimeInterval) {
guard isGameOver == false else { return }
if let lastTouchPosition = lastTouchPoint {
//this usually gives a large value (related to screen size of the device) so /100 to normalize it
let diff = CGPoint(x: lastTouchPosition.x - player.position.x, y: lastTouchPosition.y - player.position.y)
physicsWorld.gravity = CGVector(dx: diff.x/100, dy: diff.y/100)
}
}
Well it was a combination little hacks in touchesBegan/ touchesMoved and update func,
First you need to catch on which touch occurred, get it's name (in my
case I made nodes which had alpha of 0, but become visible upon
moving over them i.e alpha 1). In touchesBegan, touchesMoved as follow
guard let touch = touches.first else {return}
let location = touch.location(in: self)
lastTouchPoint = location
let positionInScene = touch.location(in: self)
let touchedNode = self.atPoint(positionInScene)
if let name = touchedNode.name
{
if name == "vortex"
{
touching = false
self.view!.isUserInteractionEnabled = false
print("Touched on the interacted node")
}else{
self.view!.isUserInteractionEnabled = true
touching = true
}
}
}
Second use a BOOL touching to track user interactions, on the screen by using getting a tap recogniser setup, as follow
func setupTapDetection() {
let t = UITapGestureRecognizer(target: self, action: #selector(tapped(_:)))
view?.addGestureRecognizer(t)
}
#objc func tapped(_ tap: UITapGestureRecognizer) {
touching = true
}
Finally in update put checks as follow,
guard isGameOver == false else { return }
self.view!.isUserInteractionEnabled = true
if(touching ?? true){
if let lastTouchPosition = lastTouchPoint {
//this usually gives a large value (related to screen size of the device) so /100 to normalize it
let diff = CGPoint(x: lastTouchPosition.x - player.position.x, y: lastTouchPosition.y - player.position.y)
physicsWorld.gravity = CGVector(dx: diff.x/100, dy: diff.y/100)
}
}
}

Stretch SKSpriteNode between two points/touches

I have an SKSpriteNode in which I've set the centerRect property so that the node can be stretched to appear like a styled line. My intention is for the user to touch the screen, and draw/drag a straight line with the node. The line would pivot around an anchor point to remain straight.
In touchesBegan:, the node is added:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else { return }
let positionInScene = touch.location(in: self)
if let _ = fgNode.childNode(withName: "laser") {
print("already there")
} else {
laser.centerRect = CGRect(x: 0.42857143, y: 0.57142857, width: 0.14285714, height: 0.14285714)
laser.anchorPoint = CGPoint(x: 0, y: 0.5)
laser.position = positionInScene
fgNode.addChild(laser)
}
}
And adjusted in touchesMoved::
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else { return }
let positionInScene = touch.location(in: self)
stretchLaserTo(positionInScene)
}
The node is stretched and rotated with two functions:
func stretchLaserTo(_ point: CGPoint) {
let offset = point - laser.anchorPoint
let length = offset.length()
let direction = offset / CGFloat(length)
laser.xScale = length
rotate(sprite: laser, direction: direction)
}
func rotate(sprite: SKSpriteNode, direction: CGPoint) {
sprite.zRotation = atan2(direction.y, direction.x)
}
I think I'm somewhat on the right track. The line rotates with my touch and expands, however, it's extremely sensitive and doesn't stay with my touch. Maybe I'm going about it wrong. Is there a standard technique for doing something like this?
An example of this working can be seen here: https://imgur.com/A83L45i
I suggest you set anchor point of the sprite to (0, 0), set the sprite's scale to the distance between the sprite's position and the current touch location, and then rotate the sprite.
First, create a sprite and set its anchor point.
let laser = SKSpriteNode(color: .white, size: CGSize(width: 1, height: 1))
override func didMove(to view: SKView) {
laser.anchorPoint = CGPoint(x: 0, y: 0)
addChild(laser)
}
In touchesBegan, set the position of the sprite to the location of the touch. In this case, it's also the start of the line.
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else { return }
let positionInScene = touch.location(in: self)
laser.position = positionInScene
laser.setScale(1)
}
Update the sprite so that it forms a line that starts at the position of the sprite and ends at the current touch location.
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else { return }
let positionInScene = touch.location(in: self)
stretchLaserTo(positionInScene)
}
Stretch the sprite by setting its xScale to the distance from the start of the line to the location of the current touch and then rotate the sprite.
func stretchLaserTo(_ point: CGPoint) {
let dx = point.x - laser.position.x
let dy = point.y - laser.position.y
let length = sqrt(dx*dx + dy*dy)
let angle = atan2(dy, dx)
laser.xScale = length
laser.zRotation = angle
}

How to move single SKspritenode from array

I have an array of sprites and I move it when click on it, the problem is that it is moving all the sprites and I want it to move a single element, how can I do this? thank you in advance
This is my code:
var containerPieces: [ContainerPieces] = []
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location = touch.location(in: self)
let rsCurrent = self.atPoint(location)
for nodo in containerPieces {
let rsBody = nodo.block.physicsBody
if rsBody == rsCurrent.physicsBody {
if nodo.block.frame.contains(location) {
nodo.block.position = location
nodo.block.run(blinkAnimation(), withKey:"wiggle")
isFingerOnPaddle = true
}
}
}
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
if isFingerOnPaddle {
let touch = touches.first
let touchLocation = touch!.location(in: self)
let previousLocation = touch!.previousLocation(in: self)
for nodo in containerPieces{
var paddleX = nodo.block.position.x + (touchLocation.x - previousLocation.x)
var paddleY = nodo.block.position.y + (touchLocation.y - previousLocation.y)
paddleX = max(paddleX, nodo.block.size.width/2)
paddleX = min(paddleX, size.width - nodo.block.size.width/2)
paddleY = max(paddleY, nodo.block.size.width/2)
paddleY = min(paddleY, size.width - nodo.block.size.width/2)
nodo.block.position = CGPoint(x: paddleX, y: paddleY)
}
}
}

UIButton Frame changed After Move from one Position To Another

I have 1 UIButton in StoryBoard like below screen and I move UIButton from one position to another position by following this Answer.
Edit
Another thing that I want to rotate UIButton and for that I have tried below code and it works fine but after rotating UIButton when I try to move UIButton position from 1 place to another then UIButton frame is changed.
Entire UIButton code.
class DraggableButton: UIButton {
var localTouchPosition : CGPoint?
var lastRotation: CGFloat = 0
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
// ROTATION CODE
let rotate = UIRotationGestureRecognizer(target: self, action:#selector(rotatedView(_:)))
self.addGestureRecognizer(rotate)
}
// SCROLLING CONTENT FROM ONE POSITION TO ANOTHER
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
let touch = touches.first
self.localTouchPosition = touch?.preciseLocation(in: self)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesMoved(touches, with: event)
let touch = touches.first
guard let location = touch?.location(in: self.superview), let localTouchPosition = self.localTouchPosition else{
return
}
self.frame.origin = CGPoint(x: location.x - localTouchPosition.x, y: location.y - localTouchPosition.y)
print(self.frame)
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesEnded(touches, with: event)
self.localTouchPosition = nil
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesCancelled(touches, with: event)
self.localTouchPosition = nil
}
// ROTATION CODE
#objc func rotatedView(_ sender: UIRotationGestureRecognizer) {
var originalRotation = CGFloat()
if sender.state == .began {
sender.rotation = lastRotation
originalRotation = sender.rotation
} else if sender.state == .changed {
let newRotation = sender.rotation + originalRotation
sender.view?.transform = CGAffineTransform(rotationAngle: newRotation)
} else if sender.state == .ended {
lastRotation = sender.rotation
}
}
}
Edit 1
I uploaded issue video.
Edit 2
If I used UIButton Rotation and Movement Code separately then it works but when I write both code it generate this issue.
I believe the issue is that the rotation is about the center of the view, so when you apply the rotation the frame size increases which throws off the distance relative to the origin of the frame.
I was able to fix this by moving the view relative to its center:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
let touch = touches.first
guard let location = touch?.location(in: self.superview) else { return }
// Store localTouchPosition relative to center
self.localTouchPosition = CGPoint(x: location.x - self.center.x, y: location.y - self.center.y)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesMoved(touches, with: event)
let touch = touches.first
guard let location = touch?.location(in: self.superview), let localTouchPosition = self.localTouchPosition else{
return
}
self.center = CGPoint(x: location.x - localTouchPosition.x, y: location.y - localTouchPosition.y)
print(self.frame)
}

Detecting when a user taps a SKSpriteNode

I'm new to swift programming and I decided I would make a simple game to start with SpriteKit. I have a SpriteNode that is supposed to pick 1 of 6 locations and move there when it is tapped, however from the methods I've seen I can't figure out how to implement it (again I'm new at this) Here is my code from the GameScene.swift file:
import SpriteKit
class GameScene: SKScene {
override func didMoveToView(view: SKView) {
let screenSize: CGRect = UIScreen.mainScreen().bounds
let greenTileWidth = screenSize.width * 0.5
let greenTileHeight = screenSize.height * 0.33
let greenTilePositionY = [greenTileHeight / 2, greenTileHeight / 2 + greenTileHeight, greenTileHeight / 2 + greenTileHeight * 2 ]
let greenTilePositionX = [greenTileWidth / 2, greenTileWidth / 2 + greenTileWidth]
let backgroundTile = SKSpriteNode(imageNamed: "whiteTile")
backgroundTile.size.width = screenSize.width * 100
backgroundTile.size.height = screenSize.height * 100
addChild(backgroundTile)
let greenTile = SKSpriteNode(imageNamed: "greenTile")
greenTile.size.width = greenTileWidth
greenTile.size.height = greenTileHeight
greenTile.position.y = greenTilePositionY[0]
greenTile.position.x = greenTilePositionX[0]
greenTile.userInteractionEnabled = true
addChild(greenTile)
var randomX:Int = 0
var randomY:Int = 0
func getRandomY() -> Int{
randomY = Int(arc4random_uniform(26))%3
return randomY
}
func getRandomX() -> Int{
randomX = Int(arc4random_uniform(26))%2
return randomX
}
func moveGreenTile(){
greenTile.position.x = greenTilePositionX[randomX]
greenTile.position.y = greenTilePositionY[randomY]
}
getRandomX()
getRandomY()
moveGreenTile()
}
when the SpriteNode greenTile is tapped, getRandomY() getRandomX() and moveGreenTile() should be called.
First you have to set the name attribute of your SKSpriteNodes:
greenTile.name = "greenTile"
First I see some errors in your code. The return values of getRandomX and getRandomY never get really used. Because you set the randomX and randomY variables without actually calling getRandom. So you should update it to:
func moveGreenTile(){
greenTile.position.x = greenTilePositionX[getRandomX()]
greenTile.position.y = greenTilePositionY[getRandomY()]
}
That way you only have to call moveGreenTile and it will call the getRandom methods by itself.
Then you have to use the touchesBegan method to check if the user touches the screen. So with the name you can check if the user touched the greenTile by checking the name you've set earlier:
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
for touch in touches{
let location = touch.locationInNode(self)
let node:SKNode = self.nodeAtPoint(location)
if(node.name == "greenTile"){
moveGreenTile()
}
}
}
This code detects tap events, not only touches, on a SKSpriteNode.
You can change how sensitive the tap gesture is by modifying TapMaxDelta.
class TapNode : SKSpriteNode {
// Tap Vars
var firstPoint : CGPoint?
var TapMaxDelta = CGFloat(10)
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
init() {
let texture = SKTexture(imageNamed: "Test.png")
super.init(texture: texture, color: UIColor.clear, size: texture.size())
isUserInteractionEnabled = true
}
// ================================================================================================
// Touch Functions
// ================================================================================================
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let firstTouch = touches.first {
firstPoint = firstTouch.location(in: self)
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if let firstTouch = touches.first, let firstPoint = firstPoint {
let curPoint = firstTouch.location(in: self)
if abs(curPoint.x - firstPoint.x) <= TapMaxDelta && abs(curPoint.y - firstPoint.y) <= TapMaxDelta {
print("tap yo")
}
}
}
}

Resources