I've come across an issue with my CCScene. I'm trying to set the position of my scene on the player block. Here is my init for my scene:
- (id)init
{
// Apple recommend assigning self with supers return value
self = [super init];
if (!self) return(nil);
self.userInteractionEnabled = YES;
// Create a colored background (Dark Grey)
CCNodeColor *background = [CCNodeColor nodeWithColor:[CCColor colorWithRed:0 green:0 blue:0 alpha:1.0f]];
[self addChild:background];
// Add a sprite
_player = [[PlayerBlock alloc]initWithX:[bp getPlayerX] withY:[bp getPlayerY] maxX:1000 maxY:1000 levelBlocks:blocks endBlock:fb];
[self addChild:_player];
self.positionType=CCPositionTypePoints;
self.position=_player.position;
// done
return self;
}
Here's my init for player block:
-(id)initWithX:(double)x withY:(double)y maxX:(double)maxX maxY:(double)maxY levelBlocks:(NSMutableArray*)sprites endBlock:(FinishBlock*)finishBlock{
self=[self initWithImageNamed:#"player.png"];
self.position = ccp(x,y);
_finish=finishBlock;
_sprites=sprites;
_startX=x;
_startY=y;
_maxX=maxX;
_moving=0;
_maxY=maxY;
self.width=25;
self.height=25;
self.positionType=CCPositionTypePoints;
return self;
}
What currently happens is that it doesn't focus on the player block.
Can anyone clear this up for me?
Needed to add this line to my scene:
self.contentSize=CGSizeMake(1000, 1000);
As my sprite lay outside of the scene bounds.
Related
I'm trying to draw a green circle using drawRect but I'm getting a black background in the rectangle area around the green circle.
I need bounding rectangle's fill to be transparent.
Here my DrawCircle class:
- (void)drawRect:(CGRect)rect {
UIBezierPath *ballBezierPath = [UIBezierPath rect];
[[UIColor greenColor] setFill];
[ballBezierPath stroke];
self.opaque = NO;
self.backgroundColor = [UIColor clearColor];
}
My research indicated that adding the last two lines for setting opaque and backgroundColor would fix the issue, but it still doesn't work.
Here's where I'm adding the sub view in my ViewController's viewDidLoad method:
CGRect positionFrame = CGRectMake(160,160,200,200);
DrawCircle *drawBallView = [[DrawCircle alloc] initWithFrame:positionFrame];
[view1 addSubview:drawBallView];
Have I missed something or is there a different approach I can take?
There are a few problems with your current approach:
the properties are set too late, after you are actually drawing
you are setting those properties on each draw now, they only need to be set once
changing properties could trigger a redraw, and you'll end up redrawing over and over
you are overwriting drawRect: without a call to super. To make the backgroundColor work to either fill the rectangle yourself with that color, or have the superclass do it.
Move your view configuration to the initializers, drawRect is not a good to do any of that. Implement initWithFrame and initWithCoder (to support Interface Builder.
- (id) initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self setupView];
}
return self;
}
- (id) initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
[self setupView];
}
return self;
}
- (void) setupView {
self.opaque = NO;
self.backgroundColor = [UIColor clearColor];
}
I am very fresher to game development and I need to develop a game like NinjaJump.
I have created a CCParallaxNode to setup scrolling background and added CCPhysicsNode to setup Physics world. I have created player object as shown below.
// Add a sprite
_sprite = [CCSprite spriteWithImageNamed:#"Icon.png"];
_sprite.position = ccp(self.contentSize.width/2,100);
_sprite.physicsBody = [CCPhysicsBody bodyWithRect:(CGRect){CGPointZero, _sprite.contentSize} cornerRadius:0.0];
_sprite.physicsBody.friction = 0.0f;
_sprite.physicsBody.collisionGroup = #"player";
_sprite.physicsBody.collisionType = #"Player";
//_sprite.physicsBody.collisionMask = 0;
//[self addChild:_sprite];
[foreground addChild:_sprite];
foreground is just a node added into CCScene to easily manage player in-focus.
// code for physics world
_physicsWorld = [CCPhysicsNode node];
_physicsWorld.gravity = ccp(0,-100);
_physicsWorld.debugDraw = YES;
//_physicsWorld.collisionDelegate = self;
[self addChild:_physicsWorld];
_foreground = [CCNode node];
//[self addChild: _foreground];
[_physicsWorld addChild: _foreground];
To make player always visible we have implemented update method as
- (void) update:(CFTimeInterval)currentTime {
// Calculate player y offset
if (_player.position.y > 200.0f) {
//_midgroundNode.position = CGPointMake(0.0f, -((_player.position.y - 200.0f)/4));
_foreground.position = CGPointMake(0.0f, -(_player.position.y - 200.0f));
}
}
I can't understand but the player scrolls off screen anyhow. The code is written in Cocos2d v3.
I have also setup a demo project to show what I implemented: https://www.dropbox.com/s/5s55d00kk80wun4/HumptyJump-Example.zip?dl=0
Any kind of help is appreciated. Thanks in advance.
I could not run your sample code but one thing, I can tell you for sure that there is no need of Physics Engine here.
You just keep your player at a particular height and just move your object right to left.
Apply moving parallax background image and objects to give a feel that your character is moving upwards or downwards.
For reference you can see the games like Swing Drop, made on the same approach as above.
I have implemented 2 fixed views changing their positions while physics body goes down ;).
See the same in action
#interface TestScene () {
CCNode *_background;
CCNode *_foreground;
NSArray *grounds; // Keeps track of each of the 2 views
CCPhysicsNode *_physicsWorld;
}
#implementation TestScene
- (id)init {
self = [super init];
if (!self) return(nil);
// Enable touch handling on scene node
self.userInteractionEnabled = YES;
// Physics world setup
_physicsWorld = [CCPhysicsNode node];
_physicsWorld.gravity = ccp(0,-100);
_physicsWorld.debugDraw = YES;
_physicsWorld.collisionDelegate = self;
[self addChild:_physicsWorld];
// Foreground node in which platforms are there and moves downwards as player goes up
_foreground = [CCNode node];
// Adding background images
CCSprite *default1 = [CCSprite spriteWithImageNamed: #"Default.png"];
[default1 setAnchorPoint: ccp(0, 0)];
CCSprite *default2 = [CCSprite spriteWithImageNamed: #"Default.png"];
[default2 setAnchorPoint: ccp(0, 0)];
[default2 setPosition: ccp(0, default1.contentSize.height)];
[_foreground addChild: default1];
[_foreground addChild: default2];
// Adding into array
grounds = #[default1, default2];
// Adding into physics world
[_physicsWorld addChild: _foreground];
// creating player
_player = [CCSprite spriteWithImageNamed: #"Assets.atlas/Player.png"];
[_player setPosition: ccp(160.0f, 160.0f)];
_player.physicsBody = [CCPhysicsBody bodyWithRect:(CGRect){CGPointZero, _player.contentSize} cornerRadius:0]; // 1
_player.physicsBody.collisionGroup = #"playerGroup"; // 2
_player.physicsBody.collisionType = #"player";
//[_physicsWorld addChild:_player];
[_foreground addChild: _player];
// Multiple platforms can be added into body, they are static entities only
PlatformNode *platform = (PlatformNode *)[PlatformNode node];
[platform createPlatformAtPosition:CGPointMake(110, 50) ofType: PLATFORM_NORMAL];
[_foreground addChild: platform];
return self;
}
- (void) update:(CFTimeInterval)currentTime {
// Take background and physics world down, 163 was Y position of the player
_physicsWorld.position = ccp(_physicsWorld.position.x, 163 - _player.position.y);
// loop the ground
for (CCNode *ground in grounds) {
// Get the world position
CGPoint groundWorldPosition = [_physicsWorld convertToWorldSpace: ground.position];
// Get screen position
CGPoint groundScreenPosition = [self convertToNodeSpace: groundWorldPosition];
NSLog(#"Positioning ground ---> world: (%f, %f) & screen: (%f, %f)", groundWorldPosition.x, groundWorldPosition.y, groundScreenPosition.x, groundScreenPosition.y, nil);
if (groundScreenPosition.y <= -ground.contentSize.height) {
ground.position = ccp(ground.position.x, ground.position.y + 2 * ground.contentSize.height);
break;
}
}
}
-(void) touchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
// Take the player upside
[_player.physicsBody applyImpulse: ccp(0, 215)];
}
#end
This is how I coded the background :).
I am calling a web service that returns an array of annotations in a delegate method that I am adding to my map with the addAnnotations method for a MKMapView. Everything goes swimmingly until the delegate method send two arrays in quick succession (usually about 150ms - 500ms) and then I get a EXC_BAD_ACCESS (code=1 address 0x20) on this line [kMap addAnnotations:tileArray]; - This appears to be a memory issue but i am not really sure what to do how it or how to change my code to address it.
Here is the delegate method
-(void)rebelBaseManager:(RebelBaseManager *)manager didUpdateTileHour:(NSArray *)tileArray boundaryBreak:(NSString *)breakType atTileLevel:(int)callTileLevel {
if (timerStarted == NO) {
[self startTimer];
}
//Check for tileLevel in case multiple calls were made at different tile levels
if (tileLevel == callTileLevel) {
[kMap addAnnotations:tileArray];
[HourInMap addObjectsFromArray:tileArray];
}
}
I also added a method to allow me to animate the removal of annotations which is below in case it makes a difference:
- (void)removeAnnotationsWithFade:(NSArray *)annotations animated:(BOOL)shouldAnimate {
if (!shouldAnimate)
[self removeAnnotations:annotations];
else {
for (HourAnnotation *annotation in annotations) {
MKAnnotationView *annotationView = [self viewForAnnotation:annotation];
[UIView animateWithDuration:2
animations:^{
annotationView.alpha =0;
}completion:^(BOOL finished) {
[self removeAnnotation:annotation];
}];
}
}
--------- ADDITION ---------
Adding in my code from a custom annotation in response to #3 in Rob's answer below.
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
- (id)initWithHourAnnotation:(HourAnnotation *)hourAnnotation reuseIdentifier:(NSString *)reuseIdentifier {
CGRect myFrame = self.frame;
myFrame.size.width = hourAnnotation.frameSize;
myFrame.size.height = hourAnnotation.frameSize;
self = [super initWithFrame:myFrame];
//When I use this here I seem to get the frame and color of the old annotation displayed
//self = [super initWithAnnotation:velocityAnnotation reuseIdentifier:reuseIdentifier];
if (self)
{
self.layer.cornerRadius = self.frame.size.width / 2;
self.clipsToBounds = YES;
[self setBackgroundColor:[UIColor clearColor]];
NSArray *alphaValue = [[NSArray alloc]initWithArray:[self alphaForTileLevel]];
self.fillColor = [UIColor colorWithHue:hourAnnotation.color saturation:.06 brightness:.23 alpha:[[alphaValue objectAtIndex:hourAnnotation.tileLevel-1]doubleValue]];
self.strokeColor = [UIColor colorWithHue:hourAnnotation.color saturation:.06 brightness:.23 alpha:.35];
self.enabled = NO;
self.canShowCallout = NO;
self.userInteractionEnabled = NO;
}
return self;
}
- (void)drawRect:(CGRect)rect
{
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:rect];
[self.fillColor set];
[path fill];
[self.strokeColor set];
[path setLineWidth:2.0f];
[path stroke];
}
A couple of thoughts.
You're not showing where you are calling this removeAnnotationsWithFade. We have to assume that you're removing the appropriate model objects. use instruments to confirm that you're not leaking anywhere.
If the app is crashing when removing with fade, but not crashing without fade, then you might consider refactoring this code. Specifically, your animation code isn't actually removing the old animations until the completion block (i.e. two seconds later). Thus you are holding on to old annotations longer than needed. And if the annotations are coming in fast and furious, you could run into memory problems.
If you, for example, used transitionWithView:mapView instead, just removing the annotations directly, you might be freeing memory associated with the old annotations more quickly. It's a less elegant animation, but might be "good enough" and minimize your peak memory usage:
// this will immediately remove the annotations, but animate the fading of the transition
[UIView transitionWithView:self.mapView duration:2.0 options:UIViewAnimationOptionTransitionCrossDissolve animations:^{
[self.mapView removeAnnotations:annotations];
} completion:nil];
// presumably remove the annotations from your array, too
Is your viewForAnnotation dequeuing old annotation views and only instantiating new ones if it couldn't dequeue an old one, or is it always instantiating new ones? Stuff like that might help control peak memory usage, too.
I am working on a game where a bunch of enemies spawn outside the screen and move towards the center (where there's a space station). However, I need the spaceships to face in the direction of the center. Currently, I'm using this code:
- (void)rotateNode:(SKNode *)nodeA toFaceNode:(SKNode *)nodeB {
double angle = atan2(nodeB.position.y - nodeA.position.y, nodeB.position.x - nodeA.position.x);
if (nodeA.zRotation < 0) {
nodeA.zRotation = nodeA.zRotation + M_PI * 2;
}
[nodeA runAction:[SKAction rotateToAngle:angle duration:0]];
}
but it only gives this result:
How should I get the sprites to rotate correctly?
All suggestions are appreciated!
EDIT:
I am calling the code above in the init method for the Enemy sprite
-(id)initWithLevel:(int)level andSize:(CGSize)size{
if(self = [super initWithImageNamed:#"alien01"]){
//position and init stuff
[self setPosition:self withSeed:seed andSize:size];
SKNode *center = [[SKNode alloc] init];
center.position = CGPointMake(self.size.width/2, self.size.height/2);
[self rotateNode:self toFaceNode:center];
}
return self;
}
Your problem lies here:
SKNode *center = [[SKNode alloc] init];
center.position = CGPointMake(self.size.width/2, self.size.height/2);
[self rotateNode:self toFaceNode:center];
The center node's position is based on the size of the enemy sprite.
You need to move this part to the scene class. Call the method at the point you initialise the enemy node in the scene. I am assuming the name of the enemy class as EnemySprite and that of the space station node as spaceStation.
EnemySprite *enemy = [[EnemySprite alloc] initWithLevel:1 andSize:CGSizeMake(100, 100)]; //Assuming values as well
[self addChild:enemy];
[self rotateNode:enemy toFaceNode:spaceStation];
This should make it work.
I am just beginning programming games in Xcode 5 using cocos2D and found this pretty strange. I'm starting out fresh on a menu scene and was importing a background and a button. The following code positions my background just fine sometimes, but then other times it's adjusted upwards about 50 pixels (my simulator is on it's side, or it's length is lying horizontal, so technically it's shifting about -50 pixels in the "width" direction, although to the simulator it shifts upwards).
Note I found that every time I run my program, it alternates between being properly aligned and shifted. Why would this be happening ugh! Below is the code I'm using.
Note 2 I'm using Kobold2D and the framework I'm using has a config.lua that's a little beyond my scope for me to understand everything. The config.lua code is located here http://snipt.org/BEt6
-(id) init
{
if ((self = [super init]))
{
CCSprite *sprite = [CCSprite spriteWithFile:#"background.png"];
sprite.anchorPoint = CGPointZero;
[self addChild:sprite z:-1];
sprite = [CCSprite spriteWithFile:#"button.png"];
sprite.anchorPoint = CGPointZero;
sprite.position = CGPointMake(200,200);
[self addChild:sprite z:0];
}
return self;
}
The only problem with your code is that you are initializing sprite twice, which is NOT very good.
I would think it is causing your problem.
Try this code:
-(id) init
{
if ((self = [super init]))
{
CCSprite *sprite = [CCSprite spriteWithFile:#"background.png"];
sprite.anchorPoint = CGPointZero;
[self addChild:sprite z:-1];
CCSprite *sprite2 = [CCSprite spriteWithFile:#"button.png"];
sprite2.anchorPoint = CGPointZero;
sprite2.position = CGPointMake(200,200);
[self addChild:sprite2 z:0];
}
return self;
}