Im creating an spark affect for a bomb that rotates and scrolls across the screen using SKEmitterNode. I've come to a sticking point as i can't correctly position the spark effect. I can get the SKEmitterNode and spriteNode to move across the screen together with the spark affect positioned in the centre of the bomb while the bomb rotates. The effect i wish to achieve is the spark positioned correctly at the end of the wick on the bomb while the bomb rotates.
The first piece of code is from myScene:
MCBomb *_multiUp = [MCBomb spriteNodeWithImageNamed:#"MultiShotPowerUp"];
[_mainLayer addChild:_multiUp];
// Create spark.
NSString *bombSparkPath = [[NSBundle mainBundle] pathForResource:#"BombSpark" ofType:#"sks"];
SKEmitterNode *_bombSpark = [NSKeyedUnarchiver unarchiveObjectWithFile:bombSparkPath];
_bombSpark.targetNode = _mainLayer;
[_mainLayer addChild:_bombSpark];
_multiUp.spark = _bombSpark;
The next two pieces of code are from the class .m
-(void)updateSpark {
if (self.spark) {
self.spark.position = self.position;
}
and .h
#interface MCBomb : SKSpriteNode
#property (nonatomic) SKEmitterNode *spark;
-(void)updateSpark;
As soon as i add CGPointMake into the mix e.g.
self.spark.position = CGPointMake(0.9, 0.9);
when called the spriteNode floats across the screen and the SKEmitterNode appear bottom left corner static.
Or if i do something like:
self.spark.position = CGPointMake (self.size.width +50 , self.size.height + 400);
The SKEmitterNode is static in its new position and the spriteNode floats across the screen.
Or if i do this:
self.spark.position = CGPointMake (self.size.width +50 , self.size.height + 400);
self.position = self.spark.position;
I get both spriteNode and EmitterNode static in there new position.
As you can see, In my frustration i'm clutching at straws! The positioning is extreme just for affect, I'm still learning and could do with some advice on how to get the correct affect.
Below is the complete class, please see new comment:
-(void)spawnMultiShotPowerUp {
// Setting up Texture Atlas.
SKTextureAtlas *atlas = [SKTextureAtlas atlasNamed:#"Parts"];
MCBomb *_multiUp = [MCBomb spriteNodeWithImageNamed:#"MultiShotPowerUp"];
_multiUp.name = #"multiUp";
_multiUp.position = CGPointMake(-_multiUp.size.width, randomInRange(150, self.size.height -100));
_multiUp.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:16.0];
_multiUp.physicsBody.categoryBitMask = mCMultiUpCategory;
_multiUp.physicsBody.collisionBitMask = 0;
_multiUp.physicsBody.velocity = CGVectorMake(10, randomInRange(-40, 40));
_multiUp.physicsBody.angularVelocity = M_PI;
_multiUp.physicsBody.linearDamping = 0.0;
_multiUp.physicsBody.angularDamping = 0.0;
[_mainLayer addChild:_multiUp];
// Create spark.
NSString *bombSparkPath = [[NSBundle mainBundle] pathForResource:#"BombSpark" ofType:#"sks"];
SKEmitterNode *_bombSpark = [NSKeyedUnarchiver unarchiveObjectWithFile:bombSparkPath];
_bombSpark.targetNode = _mainLayer;
_bombSpark.position = CGPointMake(20, 0);
_multiUp.spark = _bombSpark;
[_multiUp addChild:_bombSpark];
}
You have these two nodes you are creating:
MCBomb *_multiUp = [MCBomb spriteNodeWithImageNamed:#"MultiShotPowerUp"];
[_mainLayer addChild:_multiUp];
// Create spark.
NSString *bombSparkPath = [[NSBundle mainBundle] pathForResource:#"BombSpark" ofType:#"sks"];
SKEmitterNode *_bombSpark = [NSKeyedUnarchiver unarchiveObjectWithFile:bombSparkPath];
_bombSpark.targetNode = _mainLayer;
[_mainLayer addChild:_bombSpark];
but you are adding your spark to the mainLayer which is not correct for what you are trying to do. You need to add the spark as a child to your bomb instead of the mainLayer.
[_multiUp addChild:_bombSpark];
Keep in mind that the _bombSpark positioning has now changed because it is a child of _multiUp. That means _bombSpark's position 0,0 equals the center position of the _multiUp node.
Add a position to _bombSpark like this:
_bombSpark.position = CGPointMake(20,0);
That puts the spark 20 points up from the center of _multiUp. Play around with the exact x,y coordinate values until you get it to where it has to be.
Related
I have a problem whit the camera in SceneKit, by default i can move, rotate, zoom my camera whit this line :
myView.allowsCameraControl = YES;
But my camera can pass through walls and floor (which allows me to see the underside of the stage ) .
My first request: is it possible to apply constraints to the camera (position, rotation) ?
My second request: I thought making a cube encompassing stage and do my collision detection between my cube and my camera, but it does not work ...
code for viewDidLoad:
SCNView *myView = (SCNView *)self.view3D;
myView.scene = [SCNScene sceneNamed:#"art.scnassets/Pointe Marrin 3 def 3 def 2.dae"];
myView.scene.physicsWorld.contactDelegate = self;
myView.scene.physicsWorld.gravity = SCNVector3Make(0, 0, 0);
cubeLimite = [myView.scene.rootNode childNodeWithName:#"Cube" recursively:YES];
camera = [myView.scene.rootNode childNodeWithName:#"Caméra" recursively:YES];
cubeLimite.physicsBody = [SCNPhysicsBody bodyWithType:SCNPhysicsBodyTypeDynamic shape:nil];
cubeLimite.physicsBody.mass = 0;
cubeLimite.categoryBitMask = SCNPhysicsCollisionCategoryDefault;
cubeLimite.physicsBody.collisionBitMask = SCNPhysicsCollisionCategoryAll;
//test camera
CGFloat boxSide = 0.001;
SCNBox *box = [SCNBox boxWithWidth:boxSide
height:boxSide
length:boxSide
chamferRadius:0];
SCNNode *boxNode = [SCNNode nodeWithGeometry:box];
SCNPhysicsShape *shape = [SCNPhysicsShape shapeWithGeometry:boxNode.geometry options:nil];
camera.physicsBody = [SCNPhysicsBody bodyWithType:SCNPhysicsBodyTypeDynamic shape:shape];
camera.physicsBody.mass = 0;
camera.categoryBitMask = SCNPhysicsCollisionCategoryDefault;
camera.physicsBody.collisionBitMask = SCNPhysicsCollisionCategoryAll;
Code for delegate :
- (void)physicsWorld:(SCNPhysicsWorld *)world
didBeginContact:(SCNPhysicsContact *)contact{
NSLog(#"Contact debut");
}
- (void)physicsWorld:(SCNPhysicsWorld *)world
didUpdateContact:(SCNPhysicsContact *)contact{
NSLog(#"Contact milieu");
}
- (void)physicsWorld:(SCNPhysicsWorld *)world didEndContact:(SCNPhysicsContact *)contact{
NSLog(#"Contact fin");
}
The delegate :
SCNPhysicsContactDelegate
is declared in the .h file
Ideas ?
thank you beforehand
It's not possible, as far as I know, to constrain the default camera when allowsCameraControl is enabled.
The answer to Rotate SCNCamera node looking at an object around an imaginary sphere outlines an approach that might work for you, depending on what constraints you need to build.
I created an SKEmitterNoder (standard Fire.sks template) & added it to an SKSpriteNode called ball. The thing is, I followed a tutorial of iOS Games by Tutorials & the way they taught me to create Tiled maps, is essentially flip them upon init so that (0, 0) would be in bottom left corner of the screen & NOT top left. I think this is affecting my SKEmitterNode targetNode property because as the ball is moving in the scene, the fire emitter node goes in the exact opposite direction. Could someone tell me what to do to change this? I can't change the world parameters, so I need something that would alter the SKEmitterNode trajectory so that it actually follows the ball node properly. Here's my current code:
NPCBall *ball = [[NPCBall alloc] initWithBallType:BallTypeRed andName:#"Ball Red"];
ball.position = CGPointMake(x + w/2, y + h/2);
ball.zPosition = -104.0;
[_entityNode addChild:ball];
//Create a random Vector.dx & dy for the NPC. This will apply a random impulse to kick start NPC non-stop movement.
[ball.physicsBody applyImpulse:CGVectorMake([self randomVectorGeneration].dx, [self randomVectorGeneration].dy)];
//Create a particle for the ball (fire).
SKEmitterNode *emitFire = [NSKeyedUnarchiver unarchiveObjectWithFile:[[NSBundle mainBundle] pathForResource:#"Particle_Fire" ofType:#"sks"]];
emitFire.position = CGPointMake(ball.position.x, ball.position.y);
emitFire.targetNode = ball;
[_entityNode addChild:emitFire];
P.S. _entityNode, to which both the ball & SKEmitterNode fire are added, is an SKNode. Which in turn is a child of an SKNode called _worldNode. _worldNode is a child of self (which is an SKScene). This _worldNode is the one flipped to have (0, 0) coordinates begin at the bottom left.
A buddy of my'n has found a solution to this. I'm posting it here in case someone also has an inverted Tiled map where the (0, 0) coordinates are in the bottom left corner of the scene.
//Create a ball sprite.
SKSpriteNode *ball = [SKSpriteNode spriteNodeWithColor:[SKColor redColor] size:CGSizeMake(20, 20)];
ball.position = CGPointMake(_worldNode.frame.size.width/2, _worldNode.frame.size.height/2);
[_entityNode addChild:ball]; //<--_entityNode is also an SKNode. It's a child of _worldNode.
ball.zPosition = -104.0;
//Create a random Vector.dx & dy for the NPC. This will apply a random impulse to kick start NPC non-stop movement.
[ball.physicsBody applyImpulse:CGVectorMake([self randomVectorGeneration].dx, [self randomVectorGeneration].dy)];
//Create a particle effect for the ball.
SKEmitterNode *emitFire = [NSKeyedUnarchiver unarchiveObjectWithFile:[[NSBundle mainBundle] pathForResource:#"Particle_Fire" ofType:#"sks"]];
emitFire.name = #"Fire Emitter";
emitFire.targetNode = _worldNode; //<-- This is the demon. Make the emitter follow this SKNode since it moves when the ball moves. Everything in my game is a child of _worldNode.
[ball addChild:emitFire]; //Add the emitter to the ball.
Progress so far:
So what I have at the moment is this:
(the green point represents the parent "BlankNode, adding children then rotating them around that node,
Im a bit stick how to get it work properly, for some reason they dont sit next to eachother but opposite (as showen in http://i.stack.imgur.com/w7QvS.png)
inGameLevel
myArc = [[Arcs alloc]initWithArcCount:myAmmountOfSprites];
[self addChild:myArc];
My wish is for the sprite.rotation to be slightly offset from the next loaded...here they are split...
(The diagram belows showing the arc shape I would like to load the sprites in)
**With one stick loaded, maybe its easier to spot the mistake
(if I load a second sprite it loads directly opposite to the previous and not at the expected angle incremented
In this version I have just loaded the stick and blanknode, positioned it using anchor points, Im confused how the rotation works... **
SKSpriteNode *blank = [[SKSpriteNode alloc]
///like the otherone
blank.zRotation=0;
blank.anchorPoint = CGPointMake(0.5, 0.5);
[self addChild:blank];
//set to 0 value so I can see what its natural state is (it is vertical and above the parent node)
//but this value will be incremented each time a new sprite is added
int rotationAmount = 0;
Rotation = Rotation-rotationAmount; //will increment
objectPic = [SKSpriteNode spriteNode....as normal
//use blank nodes anchorpoint
objectPic.anchorPoint = blank.anchorPoint;
//Rotation
objectPic.zRotation = Rotation;
float moveUp_donut = 0.3;
//"moveUp_donut" moving this value up moves the stick up
//and outward from the center
objectPic.anchorPoint =
CGPointMake(0.0,-moveUp_donut); //(0.0,-moveOutward);
[blank addChild:objectPic];
}
}
I have made an xcode project available for anyone interested to have a look at the problem, hopefully you can explain how to get the rotation working correctly.
at the moment it is just loading one sprite, so you might need to play with the setting,
myArc = [[Arcs alloc]initWithArcCount:addLotsOfSticks];
//and play with the rotation ammount
int rotationAmount = 3;
http://www.filedropper.com/rotationtest
Solution Found! see below:
🌸
A huge thanks to WangYudong for giving such a great answer!
I made a sample project and hope it can help. The algorithm is not base on your project, so make some change to fit your need.
Firstly, add a blank node to the middle of the scene:
self.blank = [[SKSpriteNode alloc] initWithColor:[SKColor greenColor]size:CGSizeMake(20, 20)];
self.blank.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame));
[self addChild:self.blank];
Then, create the stick:
- (SKSpriteNode *)newStick
{
SKSpriteNode *stick = [[SKSpriteNode alloc] initWithColor:[SKColor redColor]size:CGSizeMake(5, 100)];
return stick;
}
And given the amount of sticks, the radius (of the inner circle), the starting radian and ending radian, add a method:
- (void)loadStickArcWithStickAmount:(NSUInteger)amount radius:(CGFloat)radius startRadians:(CGFloat)startRad endRadians:(CGFloat)endRad
{
for (NSUInteger index = 0; index < amount; index++) {
SKSpriteNode *stick = [self newStick];
CGFloat halfStickLength = stick.size.height / 2;
CGFloat rotateRad = startRad + (endRad - startRad) / (amount - 1) * index;
stick.zRotation = M_PI_2 + rotateRad;
stick.position = CGPointMake((radius + halfStickLength) * cos(rotateRad),
(radius + halfStickLength) * sin(rotateRad));
[self.blank addChild:stick];
}
}
Some hints:
rotateRad divides radians of endRad - startRad.
M_PI_2 is an offset of zRotation.
Trigonometric maths calculates the position of sticks.
Both anchor points of blank node and stick remain default (0.5, 0.5).
Use the method:
[self loadStickArcWithStickAmount:27 radius:50.0 startRadians:M_PI endRadians:2*M_PI];
to achieve the following result:
I have created a simple sks particle file in my project,I would like to know A: how can I implement this particle in my view & how can I add the appropriate parameters so that the particles travel in the direction that the device is being held in, (very similar to iOS7 Dynamic Wallpapers for example), so in my case, if I have stones falling straight down and the device is tilted to the right, the stones should start falling with a different angle. I'd really appreciate some advice.
I was actually thinking about applying this effect to my own game as well. This is a crude answer and I haven't actually tried combining these two elements, but it should definitely give you some pointers.
#property (strong,nonatomic) SKEmitterNode * starEmitter;
#property (strong,nonatomic) CMMotionManager * self.motionManager;
// Create your emitter
NSString *starPath = [[NSBundle mainBundle] pathForResource:#"startButtonDisappear" ofType:#"sks"];
self.starEmitter = [NSKeyedUnarchiver unarchiveObjectWithFile:starPath];
// Create a motion manager
self.motionManager = [CMMotionManager new];
[self.motionManager startAccelerometerUpdates];
In your update method, calculate the gravity vector and assign acceleration properties of the emitter.
- (void)update:(NSTimeInterval)currentTime {
CMAccelerometerData* data = self.motionManager.accelerometerData;
// You really don't want NSLogs in this method in a release build, by the way
NSLog(#"x [%2.1f] y [%2.1f] z [%2.1f]",data.acceleration.x,data.acceleration.y,data.acceleration.z)
CGVector gravity;
if ([[UIApplication sharedApplication] statusBarOrientation] == UIInterfaceOrientationLandscapeLeft) {
gravity = CGVectorMake(data.acceleration.x, data.acceleration.y);
} else {
gravity = CGVectorMake(-data.acceleration.x, -data.acceleration.y);
}
CGFloat tmp = gravity.dy;
gravity.dy = gravity.dx;
gravity.dx = tmp;
// self.physicsWorld.gravity = gravity;
self.starEmitter.xAcceleration = gravity.x;
self.starEmitter.yAcceleration = gravity.y;
NSLog(#"Emitter acceleration set to [%+4.2f,%+4.2f]",gravity.dx,gravity.dy);
}
I have a CClayer class and when this class inits it creates a CCSprite that should be centered, so later, when I rotate an object created with that CCLayer class, it rotates around its center. I mean, if the sprite on that class is an image 200 pixels wide and 300 pixels height, I want the CCLayer pivot to be at 100,150.
I have tried to set it at 0,0 and 0.5,0.5 without success.
As far as I understand, CCLayer has no bounding box, it is like a kind of node, right? so, I create the class like this:
-(id) initWithImage:(UIImage*)image Name:(NSString*)name
{
if( (self=[super init])) {
self.isTouchEnabled = YES;
self.mySprite =
[CCSprite spriteWithCGImage:image.CGImage key:name];
self.mySprite.position = CGPointZero;
[self addChild:self.mySprite];
self.mySprite.anchorPoint = ccp(0.0f, 0.0f);
// have tried also 0.5f, 0.5f... no success
}
return self;
}
How do I do that?
thanks
Provide a method in your CCLayer subclass to rotate the sprite:
-(void) rotateMySpriteToAngle:(float) angle
{
self.mySprite.rotation = angle;
}
The anchor point of the sprite should be (0.5, 0.5) to rotate it about its centre.
I feel you are making your program too complex though? Could you just use a sprite instead of a layer with a sprite as a child? Then you could rotate it directly.
It looks as though you want to make your sprite touchable. Consider using CCMenu and CCMenuItems if you are looking to implement buttons.
Edit
Try setting the anchor point of the layer to (0, 0) and the anchor point of the sprite to (0.5, 0.5), then set the position of the sprite to (0, 0)
This means the centre of the sprite is at (0, 0) on the layer and you then rotate the layer around it's origin.
Scene
=============================================
= =
= =
= =
= =
= | =
= | Layer (effective infinite size) =
= __|__ =
= | | | =
= | +--|-------------- =
= |_____| =
= Sprite =
=============================================
The + is the origin of the layer and the center point of the sprite
When you rotate the layer around it's origin, you are simultaneously rotating the sprite about it's centre.