Whenever i try to draw a SKSpriteNode, it would be drawn lower than it should be.
But it seems that other SKSpriteNode works fine with no problems.
This is my current code:
func initMainGround() {
let gSize = CGSizeMake(self.size.width/4*3, 120);
ground = SKSpriteNode(color: SKColor.brownColor(), size: gSize);
ground.name = gName;
ground.position = CGPointMake(0, 0);
ground.physicsBody = SKPhysicsBody(rectangleOfSize: ground.size);
ground.physicsBody.restitution = 0.0;
ground.physicsBody.friction = 0.0;
ground.physicsBody.angularDamping = 0.0;
ground.physicsBody.linearDamping = 0.0;
ground.physicsBody.allowsRotation = false;
ground.physicsBody.usesPreciseCollisionDetection = true; //accurate collision
ground.physicsBody.affectedByGravity = false;
ground.physicsBody.dynamic = false;
ground.physicsBody.categoryBitMask = gBitmask;
ground.physicsBody.collisionBitMask = pBitmask;
self.addChild(ground);
}
func addBomb() {
let bomb = SKSpriteNode(imageNamed: "trap");
bomb.size = CGSizeMake(30, 30);
bomb.position = CGPointMake(ground.position.x, actualY+10);
bomb.physicsBody = SKPhysicsBody(circleOfRadius: bomb.size.width/2);
bomb.physicsBody.restitution = 0.0;
bomb.physicsBody.allowsRotation = false;
bomb.physicsBody.usesPreciseCollisionDetection = true;
bomb.physicsBody.affectedByGravity = false;
bomb.physicsBody.dynamic = false;
bomb.physicsBody.categoryBitMask = bBitmask;
bomb.physicsBody.collisionBitMask = pBitmask;
bomb.physicsBody.contactTestBitMask = pBitmask;
self.addChild(bomb);
}
Although the bomb is suppose to be almost directly above the ground, but it seems that the bomb is almost 100+ above the ground instead.
The ground is suppose to fill up almost one third of the screen height since the game is in landscape, but it is way lower than normal.
Why is it that the ground is drawn at the wrong position, but the bomb is drawn at the correct position?
This sounds like the same problem I had when starting to work with SpriteKit. If your game uses an .sks file to present it's main scene (as it does by default), this scene uses arbitrary dimension values defined in the .sks file.
Try setting the dimensions of your scene dynamically to see if this is the case.
In your didMoveToView function, add something like this at the top of the function:
self.size = view.bounds.size
This way the dimension values from the .sks file will be overridden with your actual screen dimensions.
Hope this helps!
Set the anchorPoint on your ground image.
ground.anchorPoint = CGPointZero // same as CGPointMake(0, 0)
ground.position = CGPointZero
The default anchorPoint is (0.5, 0.5), the center of the image. So without it set, the center of the ground image is drawn in the lower left corner of the screen (0, 0).
Your bomb draws where you expect it for the same reason, the center is placed at the position you specified.
Related
Good day, I am trying to use a SKEmitterNode in swift, but I can't seem to be able to change its' width, so the particles only cover half of the screen.
My code:
if let particles = SKEmitterNode(fileNamed: "Snow.sks") {
particles.position = CGPointMake(frame.size.width/2, frame.size.height)
particles.targetNode = self.scene
particles.zPosition = 999
addChild(particles)
}
How can I make the particles to cover the whole screen width?
After looking at the so called "emitter editor", as suggested by #Knight0fDragon, I was able to find the right parameter - particlePositionRange
if let particles = SKEmitterNode(fileNamed: "Snow.sks") {
particles.position = CGPointMake(frame.size.width/2, frame.size.height)
particles.targetNode = self.scene
// frame.size.width to cover the length of the screen.
particles.particlePositionRange = CGVector(dx: frame.size.width, dy: frame.size.height)
particles.zPosition = 999
addChild(particles)
}
Through the position and particlePositionRange you can get your goal
By the documentation:
particlePositionRange
Declaration
var particlePositionRange: CGVector { get set }
Discussion
The default value is (0.0,0.0). If a component is non-zero, the same component of a particle’s position is randomly determined and may vary by plus or minus half of the range value
I'm working on a sidescroller with Spritekit and Swift. I don't understand how to define a playable area bigger than the screen and center the camera on the player. How can this be done?
This is my current code, I tried to create a "world node" which I could move around to simulate the camera, however it's somehow disassociated from it's shape and I haven't been able to get the player inside it.
override func didMoveToView(view: SKView) {
self.anchorPoint = CGPointMake(0.5, 0.5)
self.physicsWorld.contactDelegate = self
self.size = CGSizeMake(view.bounds.size.width, view.bounds.size.height)
// Add world
let r : CGRect = CGRectMake(0, 0, 500, 500)
world = SKShapeNode(rectOfSize: r.size)
world.physicsBody = SKPhysicsBody(edgeLoopFromRect: r)
world.strokeColor = SKColor.blackColor()
world.position = CGPointMake(0, 0)
self.addChild(world)
// Add player
player = SKShapeNode(rectOfSize: CGSize(width: 50, height: 50))
player.physicsBody = SKPhysicsBody(rectangleOfSize: CGSize(width: 50, height: 50))
player.fillColor = SKColor.blackColor()
player.position = CGPointMake(0, 0)
world.addChild(player)
// Accelerometer updates
motionManager.startAccelerometerUpdates()
}
The example in Apple's documentation is in the Advanced Scene Processing section. Apple suggests making a "World" SKNode as a child of the Scene, and a "Camera" SKNode as a child of the world.
They suggest constantly moving the world so that it centers on the Camera during the didSimulatePhysics step. This method allows you to perform actions or simulate physics on the Camera itself, if you so choose. If you center the camera prior to this step, you won't be able to use physics to affect the Camera Node.
If you specifically only want left and right scrolling, simply restrict the movement to the X-axis.
Edit:
The current problem is because of your creation of the world and the physicsBody from a Rect that has its position predetermined. This is causing trouble with your anchorPoint setting (the world & physicsBody are being created with their lower left corners starting at the Scene's anchor point).
This can be fixed by creating the World and Player without using a Rect with position set. SKShapeNode's shapeNodeWithRectOfSize works, as does SKSpriteNode's spriteNodeWithColor:size: PhysicsBody is a bit trickier, and should likely use bodyWithEdgeLoopFromPath: world.path
EDIT: For future persons interested in creating a side-scroller with a camera always focused on the player, this is probably the simplest way to get one to work:
var player = SKShapeNode()
var world = SKShapeNode()
override func didMoveToView(view: SKView) {
self.anchorPoint = CGPointMake(0.5, 0.5)
self.size = CGSizeMake(view.bounds.size.width, view.bounds.size.height)
// Add world
world = SKShapeNode(rectOfSize: CGSizeMake(300, 300))
world.physicsBody = SKPhysicsBody(edgeLoopFromPath: world.path)
world.fillColor = SKColor.blackColor()
self.addChild(world)
// Add player
player = SKShapeNode(rectOfSize: CGSize(width: 50, height: 50))
player.physicsBody = SKPhysicsBody(rectangleOfSize: CGSize(width: 50, height: 50))
player.fillColor = SKColor.blackColor()
world.addChild(player)
}
override func update(currentTime: CFTimeInterval) {
world.position.x = -player.position.x
world.position.y = -player.position.y
}
I followed Ray Wenderlich's tutorial on a side scrolling game Super Koalio (or something like that). That game is a side scroller where the game map is larger sideways than the screen. The following code is for my game which is a vertical scrolling game.
In the update method I call a method called setViewPointCenter.
-(void)update:(CFTimeInterval)currentTime
{
// Other things are called too
[self setViewPointCenter:self.player.position];
}
Then in that method you just update the view
- (void)setViewPointCenter:(CGPoint)position
{
NSInteger x = MAX(position.x, self.size.width / 2);
NSInteger y = MAX(position.y, self.size.height /2);
x = MIN(x, (self.map.mapSize.width * self.map.tileSize.width) - self.size.width / 2);
y = MIN(y, (self.map.mapSize.height * self.map.tileSize.height) - self.size.height / 2);
CGPoint actualPostion = CGPointMake(x, y);
CGPoint centerOfView = CGPointMake(self.size.width/2, self.size.height/2);
CGPoint viewPoint = CGPointSubtract(centerOfView, actualPostion);
self.map.position = viewPoint;
}
Now my character is always in the center of the screen. I did change some items from horizontal to vertical, but at least you get an idea. Also, I used a tile map that is why you see mapSize and tileSize. You might want to take a look at Ray's tutorial. And of course you will need to convert into the methods into Swift!!
I changed the anchor point of my SKSpriteNode, but it seems that the SKPhysicalBody is not correct.
Without setting the anchor point of my sprite, it would appear out of the screen by a little, using anchor point fixed this problem.
But after setting the anchor point, the physical body is still at the previous location (before changing the anchor point)
func addGround() {
let gSize = CGSizeMake(self.size.width/4*3, 120);
let ground = SKSpriteNode(color: SKColor.brownColor(), size: gSize);
ground.name = gName;
ground.anchorPoint = CGPointZero
ground.position = CGPointMake(0, 0);
ground.physicsBody = SKPhysicsBody(rectangleOfSize: ground.size);
ground.physicsBody.restitution = 0.0;
ground.physicsBody.friction = 0.0;
ground.physicsBody.angularDamping = 0.0;
ground.physicsBody.linearDamping = 0.0;
ground.physicsBody.allowsRotation = false;
ground.physicsBody.usesPreciseCollisionDetection = true; //accurate collision
ground.physicsBody.affectedByGravity = false;
ground.physicsBody.dynamic = false;
ground.physicsBody.categoryBitMask = gBitmask;
ground.physicsBody.collisionBitMask = pBitmask;
}
The box is drawn correctly, but enabling skView.showPhysics showed me that the physical body is still not correct.
The left side is the physics body for the box seen in the middle of the screen....
The physics body is diagonally down towards the left side.
The anchorPoint is the position of the texture relative to the node's position. In other words, changing the anchorPoint changes where the texture is drawn relative to the sprite's position.
As such, the physics body shape is unaffected by changes to the anchorPoint since the node's position remains unaffected.
Note that on 4" iPhones your ground body's size would be 426x120 extending from the bottom left corner of the screen (assuming the scene's position is 0,0).
Also note that there is a small threshold around collision shapes which prevent them from lining up exactly, there may be a small gap if you use the exact same size as the images. Therefore you may need to make the shapes a little bit smaller.
I am trying to draw a basic ground to the game for my sprite to run on.
But it seems that the ground is too short although it is suppose to take up 1/3 of the height of the screen.
My GameScene.sks is already changed to 568x320 (landscape, iPhone 5/5S)
this is my current code
func initMainGround() {
let gSize = CGSizeMake(self.size.width/4*3*2, 120);
let ground = SKSpriteNode(color: SKColor.brownColor(), size: gSize);
ground.name = gName; //Ground
ground.position = CGPointMake(0, 0);
ground.physicsBody = SKPhysicsBody(rectangleOfSize: gSize);
ground.physicsBody.restitution = 0.0;
ground.physicsBody.friction = 0.0;
ground.physicsBody.angularDamping = 0.0;
ground.physicsBody.linearDamping = 0.0;
ground.physicsBody.allowsRotation = false;
ground.physicsBody.usesPreciseCollisionDetection = true; //accurate collision
ground.physicsBody.affectedByGravity = false;
ground.physicsBody.dynamic = false;
ground.physicsBody.categoryBitMask = gBitmask; // 0x1 << 0
ground.physicsBody.collisionBitMask = pBitmask; //0x1 << 1 playerCategoryBitmask
self.addChild(ground);
}
NSLog(String(self.size.height)) return 320.0 which is perfectly fine.
But why is it that the SKSpriteNode is draw wrongly?
Setting the height of the ground to 320 only fills up half of the screen although the height of the screen in landscape is 320.
Like Jon said, this is a placement issue not a size issue. The default anchor point of any given node is in its center, so you have two options here:
1) set ground.position to CGPointMake(CGRectGetMidX(self.frame),CGRectGetMidY(self.frame))
(or even better yet, capture that as an ivar, because you'll be referring to it a whole lot when adding things to your screen, and there's no real reason to do the calculations dozens of times)
2) change the anchor point of the ground node. This is done as a CGPoint, but is interpreted as a percentage of the size of the node in question, with the default (center) being (0.5, 0.5). ground.anchorPoint = CGPointZero (which is just a shortcut for CGPointMake(0, 0)) will set the node's anchor point to its lower-left corner, at which point setting its position to (0,0) will correctly place it starting at the lower-left corner of your scene (or its parent node, in any event).
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.