Apply a constant force to a SCNnode using sceneKit - swift

using ARKit and SceneKit i'm try to make flying a Drone model.
for now successfully I can place the drone over a surface, simulate collision end detect contact between the base and the drone.
Now I would like to try to fly this drone, apply some constant force!
(I don't want to simply move the Y axis to move up the drone) but I want to simulate a force pulling it up like it happen in the real life.
I started to write some code, and call this method once I press the up button..
// hit test and find object with specific name
func applyForceToDrone() {
print("tapped apply force drone at position ")
let nodo = arrayDrone[0] // return the scnNode of the Drone in the scene
let force = SCNVector3(0, 15, 0)
print("appply force")
nodo.physicsBody?.applyForce(force, asImpulse: false)
}
resulting of this code is the drone jumping up and down every time I press the up button.
but this force need to be apply constant in the time ...
How can I constant apply a force to keep the drone up in the air? any tips how I can approach this problem ...
I can't find any example where a constant force is apply to an object.
thanks for the help.

You have to apply force inside one of loop functions. To do so, define a variable var applyForce=false. Then on for example buttonUp touch begin, set applyForce=true, on touch end set applyForce=false. Then inside loop function : if applyForce then do apply force. You have to tweak amount of force. Also you can add more parameters.

Related

Unity AR Rotate Scene to match reference point

How to Match a reference point in 2 different AR scenes by position and rotation?
Here are some details about my project:
I have 2 scenes: "new scan" and "load scan". In the "new scan" scene I instantiate a 3d cube and make all the other points relative to it. This is my reference point. Then I instantiate some more points and finally save all the data to the device (my phone).
Next, in "load scan" I load the scene again and instantiate the cube in the exact same world position. For now, I managed to set the right position for each point but the axis is rotated because I start the scene from a different real-world location and different phone rotation.
Based on the cubes which are instantiated in the same place, I need to match the rotation and the position of the scene so the points will appear in the same relative position as the first cube.
Note: one can assume that the cube will instantiate with the user standing in the same direction as the desired position. But Do NOT assume that the user starts the "load scan" scene in the same direction as the "new scan" scene (which effect the whole scene rotation).
Here is a visualization of the problem:
Image of New Scan:
Image of Load Scan:
Thanks
If you want to make sure that the cube will appear in the same position/rotation in every AR session you have several options:
Use an Image marker
Use ARWorldMap (iOS exclusive)
Use a Cloud Tracking solution (google cloud anchors / Azure spatial anchors)
Of course you can also try to make the user place the cube correctly themselves, or redesign your app to work without these restrictions.
So I've found a solution but this is not the ultimate solution:
First, make a class with public static parameter so we can pass it through other scripts and scenes. Something like that:
public static class SceneStage
{
public static int ResetScene = 0;
}
Now, every time the camera turns on, check the SceneStage.ResetScene state. If value == 0 don't do anything, otherwise ask the user to stand facing the desire direction and then press a button, which call the function ResetScene:
private void ResetScene(int _scene)
{
var xrManagerSettings = UnityEngine.XR.Management.XRGeneralSettings.Instance.Manager;
xrManagerSettings.DeinitializeLoader();
SceneManager.LoadScene(_scene); // reload current scene
xrManagerSettings.InitializeLoaderSync();
}
Here I send the scene build index to the function with:
ResetScene(SceneManager.GetActiveScene().buildIndex);
So basically, the flow is like that: for the first time we open the scene (when SceneStage.ResetScene = 1) -> change the value to 0, and reset the scene. The second time don't do anything, but when we leave the scene set the value back to 1 so the next scene will reset too (because the ARPose driver still tracking the environment).

How to add a view to SceneKit?

I am very new to Swift and currently coding a minigolf game.
However, the player can hit the ball n times.
How do I make a simple view which should show how many hits the player has left?
I think that you want something like this:
First of all, i recommend you to create a function to call when the game is restarted, there you can reset the scene, reset the player moves, etc...
You may do this like:
func restart(){
//Everything you need to restart the game
}
To call it later you just have to:
restart()
Ok, let's go to what you asked now:
declare a Int variable that contains the number of moves that the player has:
var playerMoves = 10 //Whatever number you want
Ok, you have a variable that contains the number of moves, now you have to show this on your scene, to do this, create an SKLabelNode:
let moveLabel = SKLabelNode()
Now, set the label text to be equal your playerMoves, set the position, the zPosition, the font, whatever you want:
moveLabel.text = "\(playerMoves)" //add this line to restart function that i mentioned and also set the variable playerMoves to "n" again.
moveLabel.position = ....
moveLabel.fontSize = ....
moveLabel.zPosition = ... //A number higher than any other zPosition
addChild(moveLabel) //Don't forget to add the label to scene!
Thats it! And now you have to update the playerMoves every time the player moves, to do this i'm assuming you are controlling somehow when the player moves (using update function, touches began, created a function, etc....)
so...when the player take one move:
playerMoves -= 1
moveLabel.text = "\(playerMoves)"
THATS IT!!! :) HOPE IT HELPS!
there are several ways to go about it. the most common way would be to overlay a spritekit scene over the scenekit scene. in the spritekit scene you can have a label element that you update
the code might look something like
let overlay = SKScene(fileNamed: "yourspritekitscene.sks")
yourScnView.overlaySKScene = menuOverlay
you can also assign a spritekit scene as a texture on a 3D object in your scene. or you can can make the numbers a texture you swap as you count down.

Check if disabled navmesh agent (player) is on navmesh

I am using the navmesh/agent on the player as an assistance autopathing function where the agent is disabled at all times unless the user clicks a point on the floor to walk towards. Then the agent will be disabled again once the destination is reached.
I need a way to check if the player is currently on the navmesh, within a tolerable threshold without the navmeshagent being enabled. Or, if there is a way to remove the player-binding 'effect' of the navmeshagent without disabling it, as I could use that to solve my problem too.
I guess in pseudocode, this is what i'm trying to accomplish with navmeshagent disabled:
if (!agent.isOnNavMesh){ DisableClickSelection();}
I was thinking of the possibility of comparing the Y transform of the player and the navmesh to get a height difference and using that to determine if the player is on the navmesh but i don't know how to go about getting the Y transform of the navmesh at a specific X and Z point. Maybe i can use a raycast? I'm not sure the best way. Like i said, if there is a way to remove the player-binding 'effect' of the agent on the player but keep the agent enabled I would be able to work with that too.
You should be able to do this through use of NavMesh.SamplePosition(). This method basically searches in a spherical radius around a given position for the nearest point on the navmesh. All you need to do is verify that the returned position is vertically in line with the player position, and is on/above it.
Here's an idea on how you might apply this in code:
// Don't set this too high, or NavMesh.SamplePosition() may slow down
float onMeshThreshold = 3;
public bool IsAgentOnNavMesh(GameObject agentObject)
{
Vector3 agentPosition = agentObject.transform.position;
NavMeshHit hit;
// Check for nearest point on navmesh to agent, within onMeshThreshold
if (NavMesh.SamplePosition(agentPosition, out hit, onMeshThreshold, NavMesh.AllAreas))
{
// Check if the positions are vertically aligned
if (Mathf.Approximately(agentPosition.x, hit.position.x)
&& Mathf.Approximately(agentPosition.z, hit.position.z))
{
// Lastly, check if object is below navmesh
return agentPosition.y >= hit.position.y;
}
}
return false;
}
So for using it with your example, you'd write:
if (!IsAgentOnNavMesh(agent.gameObject))
{
DisableClickSelection();
}
Hope this helps! Let me know if you have any questions.

Swift SpriteKit: How to make a sprite gain slight velocity when it collides with an object?

Im creating a game that where you tap to start the player to make him jump, and then as he goes up, more and more objects generate for you to collect to gain points (think of doodle jump). I have it set up so this will happen, but instead of being given a boost by the object, he just floats up out of the scene. Id also like to know how to remove the object from the scene when the player touches it. Thanks in advance(:
Here is my code for making him gain velocity when he touches the object:
func bounceOff() {
player.physicsBody?.affectedByGravity = false
player.physicsBody?.applyImpulse(CGVectorMake(0, 10))
let advance = SKAction.moveByX(0, y: 10, duration: 5)
runAction(advance)
}
Your object goes off the screen because you cancel gravity. The impulse you apply pushes up, but there is no force pulling down. As to removing node from the scene. You can use node's removeFromParent method.

How to make an object on the screen rotates , in swift

I am creating an Iphone game in Xcode using swift , and there is a ball i want it to keep constantly rotating , i think i need to do it with the update method , but how could i do it , and if there another way with out using the update method please let me know
You could use an SKAction (take a look at the documentation; you'll see SKActions have a variety of uses). For a rotation you could do:
// The angle is measured in radians and the duration is measured in seconds.
let rotateAction = SKAction.rotateByAngle(1, duration: 1)
To make the action run forever you can use another SKAction:
let repeatAction = SKAction.repeatActionForever(rotateAction)
Finally, you need to make your node (ball) run the action:
ball.runAction(repeatAction)
Take a look at the SKNode documentation for other methods you can use to run an SKAction.
Also, I'm assuming you're new to Sprite Kit, so I'd recommend you take a look at The Sprite Kit Programming Guide.