I want to do a little game for IOS-devices with Cocos2d v2.x. One of it's features is circular map scrolling - it's like when you scroll the screen to the most right edge of map, it jumps back to the left-most side. Like you rotate the globe.
My problem: in some point of time i has to render map (all the game graphics) twice - when you move screen to the most right edge and even bit further one half of screen should display a piece of map left-end, and half should display right-end of map. How to achieve it with less pain?
Map is sub-class of CCNode with lots of childs and some custom draw methods.
I see two ways:
1. Just make a copy of map-CCNode and render it near first one:
[---map---][---map-copy---]
|-/screen/-|
Make CCRenderTexture, render map to it and display part of map + part of RenderTexture.
[----map----][RenderTex]
|-/ screen /-]
Both of my ideas has disadvantages:
CCRenderTexture horribly slows performance, even on iPad-4 full-screen-size renderTexture works at about 20fps only;
second copy of map root-CCNode will eat much of memory, especially if it has lots of children;
Maybe is there a way of rendering root-map CCNode second time with applied offset? Like:
[self offsetMap:deltaX];
[self.map vizit];
?
Thank for ideas!
quite an easy )
class worldMap : CCNode;
#interface mainScene : CCNode
{
-(id)init;
}
#property worldMap theMap;
#end
#implementation
-(id)init
{
... usual blah-blah here
[self addChild:theMap];
}
-(void)visit
{
[super visit];
CGPoint oldMapPos = self.theMap.position;
CGPoint newMapPos = ccp(oldMapPos.x + self.theMap.width, oldMapPos.y);
self.theMap.position = newMapPos;
[self.theMap visit];
self.theMap.position = oldMapPos;
}
#end
Related
I'm fairly new to Cocos2d and I'm "really" struggling with sizing a CCTableView. What I'm referring to is how can its height be controlled so that I have space at the top and the bottom.
I have my table view functioning and I'm trying to visually wrap the top and bottom with a sprite to provide the look and feel of a frame. But when I touch the table view to scroll
It up or down, it occupies most of the screen vertically. I started messing with the contentSize, thinking this would alter where each cell appears and disappears from the top and bottom. But this had no effect.
Having had s Google about, I'm reading that I have to use a Clipping Node with a Stencil? Coming from UIkit, this seems like a bit of a faff. Would anyone like to hazard at a tutorial, bit of code with a simple CCTableView representation, sat in the middle of the scene where each cell appears and dissapears under a content frame?
You should take a look at CCCropNode in the cocos2d extensions package:
https://github.com/cocos2d/cocos2d-swift-ext/tree/master/Extensions/CCCropNode
the CCCropNode uses a glScissor to clip the drawing to a specified area: Much simpler than the CCClippingNode (which uses the stencil buffer).
Also this forum post might help:
http://forum.cocos2d-swift.org/t/ccclippingnode-woes/14752
You'll still have issues with touches outside of the target region hitting into your scroll / table area (and I've found that the CCCropNode touch clipping doesn't work - need to investigate that as it is causing me problems).
I faced the same issue and I managed to solve the touches outside the table region by adding code to CCScrollView.m as following:-
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(CCTouch *)touch
{
// Added by Season
//*************************************************************
CGPoint lcgpTouch = [touch locationInWorld];
if (!(lcgpTouch.x >= [self position].x && lcgpTouch.x <= [self position].x + [self contentSize].width &&
lcgpTouch.y >= [self position].y && lcgpTouch.y <= [self position].y + [self contentSize].height)) {
return NO;
}
//*************************************************************
if (!_contentNode) return NO;
if (!self.visible) return NO;
if (!self.userInteractionEnabled) return NO;
And you are required to setContentSize to CCTableView in order to have this piece of code get the size correctly.
Additional info:
I have some CCSprite on top of the outside target region that suppose to response to touchBegan failed after the above coding, then I changed all CCSprite to CCButton and it works like a charm. You do not need to do what I did here, you can modify coding to allow CCSprite to response to touchBegan easily.
Hope this coding above help you all.
Say I have two UIViews UIViewWithGravity and UIViewWithoutGravity. UIViewWithGravity is added to the gravity behavior:
[self.gravity addItem:UIViewWithGravity];
UIViewWithoutGravity is not. At this point, UIViewWithGravity falls off the screen, UIViewWithoutGravity stays put. So far, so good.
Now, I tie the two together using a UIAttachmentBehavior. Something like:
- (void)attachViews {
self.attachment = [[UIAttachmentBehavior alloc] initWithItem:[self.UIViewWithGravity viewWithTag:GRAVITY_TAG]
attachedToItem:[self.UIViewWithoutGravity viewWithTag:NO_GRAVITY_TAG]];
[self.animator addBehavior:self.attachViews];
}
UIViewWithGravity now drags UIViewWithoutGravity off the screen.
How can I attach these views together such that UIViewWithGravity swings freely from UIViewWithoutGravity, but does not cause UIViewWithoutGravity to be pulled down?
You need to attach the view without gravity to something that doesn't move (a fixed anchor).
I have a SKSpriteNode called "SpikyRedBall" which is a red ball. I wanted to add spikes to it so I used the following code. I can see the Spike attached to the ball but when the ball collides with another ball it does not take the fixed joints into consideration and moves them separately. I am using the following implementation:
#implementation SpikyRedBall
-(instancetype) init
{
self = [super init];
[self attachSpikes];
return self;
}
-(void) attachSpikes
{
Spike *spike = [[Spike alloc] init];
spike.position = CGPointMake(0, 0);
// attach the joint
SKPhysicsJointFixed *ballAndSpikeJointFixed = [SKPhysicsJointFixed jointWithBodyA:self.physicsBody bodyB:spike.physicsBody anchor:CGPointZero];
[self.scene.physicsWorld addJoint:ballAndSpikeJointFixed];
[self addChild:spike];
}
#end
It sounds like you don't have collision or contact categories setup for the spikes themselves. I would try setting all physicsBody properties on the spikes to be identical to those of the balls, but obviously ensuring that they don't have collision or contact categories setup in a way that they would collide with their own parent ball.
If you can require iOS 7.1, you could use +bodyWithBodies: instead of attaching any joints.
Why don't you just add the spikes to the sprite image? If they need to disappear or fall off you can just create multiple versions of the image without spikes.
I need to implement a slider control like the below image
Slider will have uneven length steps.
Whenever user slides the slider slider will only stop at one of the step which is near to the current range.
But the real challenge is UISlider with dots at custom points (range) which will be provided initially.
Can anyone help me in achieving this functionality.
I think you are best to step away from trying to mangle UISlider into this configuration
If this was my problem I would build a custom view structured like this
DKCustomSliderView
-> UIImageView (subview for slider head)
which implements an interface like this.
#interface DKCustomSliderView : UIView
-(void)setStopPoints:(NSArray *)stopPoints; //array of NSNumbers between 0-1 which define indents
-(void)setSliderHeadImage:(UIImage *)sliderHeadImage; //image to use as slider head
-(void)setIndentImage:(UIImage *)indentImage; //image to use as indent marker
-(void)setTrackImage:(UIImage *)trackImage; //stretchy image to use on track
#end
I would track a pan gesture on the slider image view.
-(void)handlePanGesture:(UIPanGestureRecogniser *)pgr {
if(pgr.state == UIGestureRecogniserStateChanged) {
//am i near an indent? , if so lock me here and be slightly sticky.
}
}
and the draw rect for DKCustomSliderView would look a little like this.
-(void)drawRect:(CGRect)dirtyRect {
CGRect bounds = self.bounds;
// 1. draw track across width
for(NSNumber *marker in self.stopPoints) {
//draw a instance of self.indentImage at ([marker floatValue]/bounds.size.width)
}
}
I am putting together a simple board game in cocos2d to get my feet wet.
To track user clicks I intend to listen for click on game squares, not the game pieces to simplify tracking game pieces. It would be 8x8 board.
Is it more efficient to:
A. Make an array of CGRects to test against and need to put the struct in an NSObject before adding to array. Seams simple but looks like a lot of work going into access the CGRects every time they are needed.
or
B. Make actual CCSprites and test against their bounding rectangle. Simple to code, but that there are an extra 64 unneeded visual objects on the screen, bloating memory use.
or even
C. Some other method, and I am fundamentally misunderstanding this tool.
I agree it seems unnecessary to create a sprite for each game square on your board if your board is totally static.
However CGRect is not an object type so it can not be added to an NSMutableArray, and I will also assume that at some point you will want to do other things with your game square, such as highlighting them and other stuff. What I suggest you do is that you create a class called GameSquare that inherits from CCNode and put them into an array:
// GameSquare.h
#interface GameSquare : CCNode {
//Add nice stuff here about gamesquares and implement in GameSquare.m
}
After that, you can create gamesquares as nodes:
// SomeLayer.h
#interface SomeLayer : CCLayer {
NSMutableArray *myGameSquares;
GameSquare *magicGameSquare;
}
#property (nonatomic, strong) GameSquare *magicGameSquare;
// SomeLayer.m
/* ... (somewhere in the layer setup after init of myGameSquares) ... */
GameSquare *g = [[GameSquare alloc] init];
g.position = CGPointMake(x,y); //replace x,y with your coordinates
g.size = CGSizeMake(w,h); //replace w,h with your sizes
[myGameSquares addObject:g];
self.magicGameSquare = [[GameSquare alloc] init];
magicGameSquare.position = CGPointMake(mX,mY); //replace with your coordinates
magicGameSquare.size = CGSizeMake(mW,mH); //replace with your sizes
After that, you can do hit test against the gamesquares like this (in your CCLayer subclass):
// SomeLayer.m
- (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
CGPoint location = [self convertTouchToNodeSpace: touch];
// Example of other objects that user might have pressed
if (CGRectContainsPoint([magicSquare getBounds], location)) {
// User pressed magic square!
[magicSquare doHitStuff];
} else {
for (int i=0; i<myGameSquares.count; i++) {
GameSquare *square = [myGameSquares objectAtIndex:i];
if (CGRectContainsPoint(square.boundingBox, location)) {
// This is the square user has pressed!
[square doHitStuff];
break;
}
}
}
return YES;
}
Yes, you will have to look through the list, but unless the player can press many squares in one touch, you can stop the search as soon as the right one is found, as demonstrated in the example.
(Assumed use of ARC)
PS. If you at some point need to add a any sprite for the GameSquare, simply add a CCSprite member in your GameSquare class and refer to that.