I am actually learning SpriteBuilder which is really a great tool for me, but I am facing a trouble concerning including a CCNode inside my scene (programmatically way).
So, I have a scene "Gameplay" where my characters are implemented from other CCB files.
Concerning the scenery, at first I put my map and some wall (for scene limit) within my Gameplay.ccb (withing a physic node).
Then, I wanted to add that scenery from external file (because I would like to be able to switch between different scenery within the same scene). So I created a CCSprite, I inserted my map inside and then my wall (this new file is map.ccbi).
When I implement the map.ccbi inside my scene, the map is displayed, but the wall seems to be away (there is no collision between the character and the wall).
The map is implemented within the physic node of my Gameplay scene.
Here's the part of the code where I am implementing the map:
- (void)didLoadFromCCB {
self.userInteractionEnabled = TRUE;
// Set the class as delegate for collision
_physicWorld.collisionDelegate = self;
_hero.physicsBody.collisionType = #"hero";
// Set up the map
CCNode *map = [CCBReader load:#"Map/TestIsland"];
// position the map
map.position = ccpAdd(_physicWorld.position, ccp(0.5, 0.5));
// add the map to the world
[_physicWorld addChild:map z:-2];
}
My map is implemented via a Class:
#implementation TestIsland
- (id)init {
self = [super init];
if (self) {
CCLOG(#"Map loaded");
}
return self;
}
#end
The keyword for this trouble should probably be sub-folder (or "tree").
Sprite builder is actually not supporting joint (sub-folder to a CCNode) while importing a node.
So one of the solution while facing such problem is to change the way you made your CCNode.
In this case, it have been solved by changing this:
(LAYER1 >>> LAYER2 >>> LAYER3 >>> ........)
CCNode >>> CCSprite + CCPhysicNode >>> CCNode(Wall) + CCNode(Obstacle) + ....
To this:
CCNode >>> CCSprite + CCNode(wall) + CCNode(Obstacle) + ....
Thanks to #Tibor for warning me that the implementation was good (it induced me that the trouble should be in the interface of SB) and for giving me the good tips for debugging my work.
Related
Near as I can tell, there isn’t a way to add physics joints in the scene editor. Is that right?
Consider a simple person object with a body, child legs and arms w/ pin joints. If I want to design this person in the scene editor and then programmatically add him to a scene, I’m not getting very far. I’m able to find the nodes in the scene, remove them from their parent and add them as a child at a new position in my scene, but I still have to specify all their joints manually.
Thoughts?
Here is my solution. I'm still hoping there is a way to create physics joints with the scene editor but I haven't found it so...
Step 1) Add all the child nodes to the scene, ensuring that objects are grouped by parent.
Step 2) Define a swift class for your complex node.
class MyNode : SKSpriteNode {
func spawn(parentNode: SKNode, position: CGPoint) {
parentNode.addChild(self) // before physics joints
let arm = self.childNode(withName:"arm")
// note if you didn't add physics bodies in scene file
// do that first
let shoulders = SKPhysicsJointPin.joint(withBodyA:self.physicsBody!, bodyB: arm.physicsBody!, anchor: CGPoint(x:position.x,y:position.y-1))
scene!.physicsWorld.add(shoulders)
// feature of pulling a child from a scene, it's always paused by default.
self.isPaused = false
}
}
Set the class for your body node in your scene.
Step 3) Transfer your node to your game scene at init time.
let tmpScene = SKScene.init(fileNamed: "MyNode.sks")
var myNode = tmpScene.childNamed(withName:"myNode") as! MyNode
myNode.removeFromParent()
myNode.spawn(world, position) // or your own parent and position as needed for your scene
I'm implementing a mass-spring system (many small physics bodies joined together with SKPhysicsJointSpring instances) with SpriteKit. Some of the particles would get snagged while traversing the center of the scene.
There seems to be a small, static body in the middle of the scene and I don't know why it's there.
Here's an easy way to see what I'm talking about:
In XCode 8, create a brand new project with the "Game" template.
In GameViewController.viewDidLoad(), add view.showsPhysics = true
If you run the project, you should see a little dot in the middle, which is the errant body:
Anyone know how to get rid of it?
Edit: I tried to manually create the scene object:
In GameViewController.viewDidLoad(), I replaced this:
// Load the SKScene from 'GameScene.sks'
if let scene = SKScene(fileNamed: "GameScene") {
view.presentScene(scene)
}
with this:
let scene = GameScene(size: view.frame.size)
scene.anchorPoint = CGPoint(x: 0.5, y: 0.5)
view.presentScene(scene)
but that didn't fix it.
Anyways, I decided to make an answer because comments are not suitable due to lot of info I want to share. Also my answer, sadly, doesn't answer the question but it gives some useful info about this unidentified, obviously capable of flying (physics body) object :)
So this is the code how to grab it (and modify it???):
//you can use self.frame here...I just copied Alessandro's code
self.physicsWorld.enumerateBodies(in:(label?.frame)!) { body, stop in
if let node = body.node {
print("Type of this node: \(type(of:node))")
print("Frame of this node: \(node.frame))")
}else{
print("This body's node property is nil")
body.affectedByGravity = true
body.isDynamic = true
body.applyImpulse(CGVector(dx: 0.003, dy: 0.0003))
}
print("Area covered by this node physicsBody: \(body.area)")
}
So if you put a break point inside of that else statement, you can scan this physics body completely and get all the info about it, like that its node property is set to nil or that its isDynamic property is set to false. But you can change that, and like in my code, set for example isDynamics to true. This makes it moveable. So if you apply some forces to it, it will move.
Still, like I said in comments, I don't have an idea why it is there and what it represents or what is its purpose.
Also, for those who are wondering how it is possible that one physics body doesn't have a node associated with it ( body.node equals nil) but is still visible on screen when showsPhysics is set to true, there is a reasonable explanation. Physics world is separated from the node tree. So we can remove a sprite from a node tree, but that doesn't mean that its physics body will be removed instantly. It may happen that physics engine haven't finished simulation... So you probably wonder, how this might happen?
Let say you have three SKSpriteNode objects intersecting at the same time (say A contacts B and A contacts C at the same time). SpriteKit can process only one contact at time. And say that you are removing A from a parent when it is contacting with B. Then, there is a contact between A and C also, so didBegin:(_ contact) will be called twice. And if you remove A from its parent in first didBegin(_ contact) call, in the next didBegin(_ contact) call, bodyA.node will be nil (bodyA is a physics body of sprite A), but its physics body will remain visible until engine finishes what needed. This is because node tree and a physics world are separated.
About the "hello world" xCode game template , it seems a little physicsBody associated to the GameScene node.
With some code I've found this:
class GameScene: SKScene {
private var label : SKLabelNode?
private var spinnyNode : SKShapeNode?
override func didMove(to view: SKView) {
...
// End part of this function:
if let b = physicsWorld.body(in: (label?.frame)!) {
if let node = b.node {
print("Type of this node: \(type(of:node))")
print("Frame of this node: \(node.frame))")
}
print("Area covered by this node physicsBody: \(b.area)")
}
}
}
With a breakpoint to the last brace, you can see two bodies (maybe an array of bodies), one of them is the physicsBody to the left debugArea (array with index 1) with the same hex address as my body b in my code : 0x79794f90, a little rectangle body with area 4.444
Printing description of ((PKPhysicsBody *)0x79794f90):
<SKPhysicsBody> type:<Rectangle> representedObject:[<SKScene> name:'(null)' frame:{{-375, -667}, {750, 1334}} anchor:{0.5, 0.5}]
(lldb)
I had a similar problem. I have a game with two sprite nodes joined together (SKPhysicsJointFixed.joint) moving around an SKEditor created scene.
As per my design, this node-pair would impact a third sprite node and be propelled smoothly away from the third sprite node, EXCEPT when the impact was in the center of the scene. For the center of the scene impact, the node-pair would compress together while be propelled away from the third sprite node, presenting a poor graphical image.
After significant time debugging my code, I found this post. Kudos for the explanations and code. I can’t answer the “why” question but for the “particles would get snagged while traversing the center of the scene” question my suggested solution is to clear the collisionBitMask instead of moving the body.
BTW categoryBitMask is 0 when loaded.
//moves the problem offscreen unless it hits another node
//body.affectedByGravity = true
//body.isDynamic = true
//body.applyImpulse(CGVector(dx: 0.003, dy: 0.0003))
//
// collisionBitMask loads with 4294967295 = default value to collide with all categories
//
body.collisionBitMask = 0
Here is my question:
How can I convert an SKSpriteNodearray into an array of GKObstacles so that an agent can appropriately avoid these obstacles by using the goalToAvoidObstacles:(nonnull NSArray<GKObstacle *> *) maxPredictionTime:(NSTimeInterval)/?
It seems that the way in which I am creating the GKObstacle array is not allowing the GKGoal to identify these as obstacles correctly, no matter what weight I give to the goal.
I have currently several SKNodes that are added to an SKScene chapterScene. Each of these nodes are added to an array that I am storing called obstaclesArray. I have set up a test in the following way that works quite well:
WORKING OBSTACLES
- (void)didMoveToView:(nonnull SKView *)view {
[super didMoveToView:view];
// Add three obstacles in a triangle formation around the center of the scene.
NSArray<GKObstacle *> *obstacles = #[
[self addObstacleAtPoint:CGPointMake(CGRectGetMidX(self.frame),
CGRectGetMidY(self.frame) + 150)],
[self addObstacleAtPoint:CGPointMake(CGRectGetMidX(self.frame) - 200,
CGRectGetMidY(self.frame) - 150)],
[self addObstacleAtPoint:CGPointMake(CGRectGetMidX(self.frame) + 200,
CGRectGetMidY(self.frame) - 150)],
];
// The player agent follows the tracking agent.
self.player = [[OPlayer alloc] initWithScene:self
radius:50
position:CGPointMake(CGRectGetMidX(self.frame),
CGRectGetMidY(self.frame))];
self.player.agent.behavior = [[GKBehavior alloc] init];
[self.agentSystem addComponent:self.player.agent];
// Create the seek goal, but add it to the behavior only in -setSeeking:.
self.seekGoal = [GKGoal goalToSeekAgent:self.character];
// Add an avoid-obstacles goal with a high weight to keep the agent from overlapping the obstacles.
[self.player.agent.behavior setWeight:100 forGoal:[GKGoal goalToAvoidObstacles:obstacles maxPredictionTime:1]];
}
- (GKObstacle *)addObstacleAtPoint:(CGPoint)point {
SKShapeNode *circleShape = [SKShapeNode shapeNodeWithCircleOfRadius:50];
circleShape.lineWidth = 2.5;
circleShape.fillColor = [SKColor grayColor];
circleShape.strokeColor = [SKColor redColor];
circleShape.zPosition = 1;
circleShape.position = point;
[self addChild:circleShape];
GKCircleObstacle *obstacle = [GKCircleObstacle obstacleWithRadius:50];
obstacle.position = (vector_float2){point.x, point.y};
return obstacle;
}
While this works just fine, the issue I am having is that I can not get the behavior to identify the SKNode bodies array that I have as obstacles. I can only get these manually created GKCircleObstacle items to register as valid obstacles that the agent avoids. The reason this is an issue is because I am relying on many obstacles that are not in fact simple circles, but polygon structures some complex, some more straightforward. Nevertheless, I was attempting the following but my agent did not seem to avoid any of the obstacles that are of SKNode this way:
NOT WORKING OBSTACLES
NSArray *obstacles = [SKNode obstaclesFromNodePhysicsBodies:obstaclesArray];
/*
The other code seen above
*/
// Add an avoid-obstacles goal with a high weight to keep the agent from overlapping the obstacles.
[self.player.agent.behavior setWeight:100 forGoal:[GKGoal goalToAvoidObstacles:obstacles maxPredictionTime:1]];
Unfortunately, this does not seem to work as no matter how high I set the weight to, the agent never responds to avoiding the obstacles. Here is a log of the array I am passing to it:
2016-07-24 22:32:24.907 <GAME>[1516:475581] obstacles: (
"<GKPolygonObstacle: 0x14d20d70>",
"<GKPolygonObstacle: 0x14d20e10>",
"<GKPolygonObstacle: 0x14d20ba0>",
"<GKPolygonObstacle: 0x14d20bb0>",
"<GKPolygonObstacle: 0x14d20bc0>",
"<GKPolygonObstacle: 0x14d20a60>",
"<GKPolygonObstacle: 0x14d208b0>",
"<GKPolygonObstacle: 0x14d207d0>",
"<GKPolygonObstacle: 0x14d20a70>"
)
Each of which have all been clearly and appropriately initialized and added to the scene. Each of these elements are in fact responsive with the SpriteKit didBeginContact:(SKPhysicsContact *)contact method, so it seems that the nodes are properly handing the boundaries that they should be having.
If someone knows what I am doing wrong or a better way to turn each of these 'obstacle nodes' into GKObstacle's I would be much appreciative. Thanks in advance!
UPDATE: Here is a node with a physics body that I am testing:
SKSpriteNode *innerMap1 = [SKSpriteNode spriteNodeWithImageNamed:#"test"];
innerMap1.physicsBody = [SKPhysicsBody bodyWithTexture:[SKTexture textureWithImageNamed:#"test"] size:CGSizeMake(innerMap1.frame.size.width*0.92, innerMap1.frame.size.height*0.92)];
innerMap1.position = CGPointMake(-220, -140);
innerMap1.physicsBody.dynamic = NO;
[chapterSKSpriteNodes addObject:innerMap1];
Here is what the body looks like with skView.showsPhysics = YES;:
So I know that the physics body is what I need it to be. When I attempt to create the GKObstacle from this body however, it does not seem to register as an obstacle when being assigned in the avoidObstacles goal.
This sounds like a bug. You should check the return array from obstaclesFromNodePhysicsBodies and make sure you are actually getting back the obstacles you expect. I suspect you are not.
You can check the relevant vertices on your obstacles as such:
https://developer.apple.com/library/ios/documentation/GameplayKit/Reference/GKPolygonObstacle_Class/index.html#//apple_ref/occ/cl/GKPolygonObstacle
Make sure they are what you expect.
I have 2 classes (that are important here), a Player class and a GameScene class. I have a method in the GameScene class (createSceneContents), that is called by didMoveToView. in the createSceneContents class I call a method from the Player class called newPlayer which returns a SKSpriteNode with a size, color, and physics body.
In the Player class there's also a method called movement which returns what the new x position of the sprite should be:
movement:
#import <CoreMotion/CoreMotion.h>
#interface Player ()
#property CMMotionManager *motionManager;
#property CMAccelerometerData *accelerometerData;
#end
-(float)movement {
self.accelerometerData = self.motionManager.accelerometerData;
float xAccelerationPosition = 0;
if ((self.accelerometerData.acceleration.x)<-0.2) {
xAccelerationPosition = -2 * self.accelerometerData.acceleration.x;
}
if ((self.accelerometerData.acceleration.x)>0.2) {
xAccelerationPosition = 2 * self.accelerometerData.acceleration.x;
}
return xAccelerationPosition;
}
To change the player's position I want to set the players position equal to the return value of the movement method, but when I try to do this I get an error. How should I do this? Should I do it this way, or is there a better way?
Yes, you can change player's position by simply set it's position property. And if the task is that simple, just keep using it.
But let's take a look if it needs to set the gravity force on player, or inertia.
In that case you need to use SpriteKit Physics Simulation. At first, all the object you need to be under the simulation must have physics bodies. It's not a big deal to add a body to sprite node
SKSpriteNode *player = [SKSpriteNode spriteNodeWithImageNamed:#"player"];
player.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(player.size.width/2, player.size.height/2)];
// your player now have a body, you can set it's density or another properties
To change the body (and the node body attached with) position you can use impulses and forces.
[player.physicsBody applyImpulse:CGVectorMake(0,1)]; //up impulse
Your movement method perfect suits to task. Just use acceleration value as that impulse.
Your movement method returns a float but the player's position is a CGPoint, you cannot sign a float to a CGPoint is what gives the error. You can set the player's position to a new CGPoint :
self.position=CGPointMake(self.position.x+[self movement],self.position.y);
What would be the best way to do this? I see the CCEaseSineInOut action but it doesn't look like that could be used to do this.
I need to move from one side of the screen to the other. The sprite should move in a sine-wave pattern across the screen.
I always like to have complete control over CCNode motion. I only use CCActions to do very basic things. While your case sounds simple enough to possibly do with CCActions, I will show you how to move a CCNode according to any function over time. You can also change scale, color, opacity, rotation, and even anchor point with the same technique.
#interface SomeLayer : CCLayer
{
CCNode *nodeToMove;
float t; // time elapsed
}
#end
#implementation SomeLayer
// Assumes nodeToMove has been created somewhere else
-(void)startAction
{
t = 0;
// updateNodeProperties: gets called at the framerate
// The exact time between calls is passed to the selector
[self schedule:#selector(updateNodeProperties:)];
}
-(void)updateNodeProperties:(ccTime)dt
{
t += dt;
// Option 1: Update properties "differentially"
CGPoint velocity = ccp( Vx(t), Vy(t) ); // You have to provide Vx(t), and Vy(t)
nodeToMove.position = ccpAdd(nodeToMove.position, ccpMult(velocity, dt));
nodeToMove.rotation = ...
nodeToMove.scale = ...
...
// Option 2: Update properties non-differentially
nodeToMove.position = ccp( x(t), y(t) ); // You have to provide x(t) and y(t)
nodeToMove.rotation = ...
nodeToMove.scale = ...
...
// In either case, you may want to stop the action if some condition is met
// i.e.)
if(nodeToMove.position.x > [[CCDirector sharedDirector] winSize].width){
[self unschedule:#selector(updateNodeProperties:)];
// Perhaps you also want to call some other method now
[self callToSomeMethod];
}
}
#end
For your specific problem, you could use Option 2 with x(t) = k * t + c, and y(t) = A * sin(w * t) + d.
Math note #1: x(t) and y(t) are called position parameterizations. Vx(t) and Vy(t) are velocity parameterizations.
Math note #2: If you have studied calculus, it will be readily apparent that Option 2 prevents accumulation of positional errors over time (especially for low framerates). When possible, use Option 2. However, it is often easier to use Option 1 when accuracy is not a concern or when user input is actively changing the parameterizations.
There are many advantages to using CCActions. They handle calling other functions at specific times for you. They are kept track of so that you can easily pause them and restart them, or count them.
But when you really need to manage nodes generally, this is the way to do it. For complex or intricate formulas for position, for example, it is much easier to change the parameterizations than to figure out how to implement that parameterization in CCActions.