I write the code for scrolling image in background of game. Simply define two sprite and set their position.
CGSize screenSize=[[CCDirector sharedDirector]winSize];
sky1 = [CCSprite spriteWithFile:#"sky1.png"];
sky1.position=ccp(screenSize.width/2, screenSize.height/2);
[self addChild:sky1];
CCLOG(#"sky1 position width=%f height=%f",sky1.position.x,sky1.position.y);
sky2=[CCSprite spriteWithFile:#"sky2.png"];
sky2.position=ccp(screenSize.width/2, sky1.position.y+sky1.contentSize.height/2+sky2.contentSize.height/2);
[self addChild:sky2];
[self schedule:#selector(scroll:) interval:0.01f];
And write the code for scrolling in scroll method:
-(void)scroll:(ccTime)delta{
CGSize screen=[[CCDirector sharedDirector] winSize];
CCLOG(#"come from schedule method pos.y=%f",sky1.position.y);
sky1.position=ccp(sky1.position.x, sky1.position.y-1);
sky2.position=ccp(sky2.position.x, sky2.position.y-1);
if(sky1.position.y <= -([sky1 boundingBox].size.height));
{
sky1.position=ccp(sky1.position.x, sky2.position.y+[sky2 boundingBox].size.height);
CCLOG(#"COMING IN first ifin scroll mwthod position width=%f pos.y=%f",sky1.position.x,sky1.position.y);
}
if(sky2.position.y<= -[sky2 boundingBox].size.height);
{
sky2.position=ccp(sky2.position.x, sky1.position.y+[sky1 boundingBox].size.height);
CCLOG(#"coming in secnond if ");
}
}
When I remove the if conditions then it works properly for one time. I don't know what is wrong with my condition and what the hell is going on with my code.
Can anybody explain?
I'm not too sure why your code isn't working properly. One thought that pops in my head is your interval may be too short. In other words, 0.01f (1/100) is shorter than your game update speed which is most likely 0.016 (1/60). So first thing I would try is tweaking your interval to maybe 0.02f. You could also drop the interval argument from the schedule call so it just runs once every frame. I would also try dropping the ccTime argument since it isn't being used.
[self schedule:#selector(scroll)];
-(void)scroll {}
But besides all that, this is probably best handled using CCActions. This looks like a simple pair of CCMoveTo actions combined with a CCRepeatForever to get the effect you want. This is the way I would go.
EDIT: here is a bit more detail about using CCActions to accomplish this. There are multiple ways of doing the same thing, but here is one you could try:
float moveDuration = 3; // or however fast you want it to scroll
CGPoint initialPosition = ccp(screenSize.width/2, screenSize.height/2);
CGPoint dp = ccp(0, -1*sky1.contentSize.height); // how far you want it to scroll down
CCMoveBy *scrollSky1 = [CCMoveBy actionWithDuration:moveDuration position:dp];
CCMoveTo *resetSky1 = [CCMoveTo actionWithDuration:0 position:initialPosition];
CCSequence *sequence = [CCSequence actionOne:scrollSky1 two:resetSky1];
CCRepeatForever *repeat = [CCRepeatForever actionWithAction:sequence];
[sky1 runAction:repeat];
Related
Please bear with me because I'm very new to OOP/ObjC/Cocos2d.
I have a method that is triggered every second like so: [self schedule:#selector(eyelidsBlink:) interval:1.0];
The schedule method is this:
-(CCTimer *) schedule:(SEL)selector interval:(CCTime)interval
{
return [self schedule:selector interval:interval repeat:CCTimerRepeatForever delay:interval];
}
The method is below:
- (void)eyelidsBlink:(CCTime)dt{
CCActionRemove *actionRemoveEyelidsNormal = [CCActionRemove action];
[_whiteGuy_EyelidsNormal runAction:actionRemoveEyelidsNormal];
_whiteGuy_EyelidsBlink = [CCSprite spriteWithImageNamed:#"EyelidsBlink_iPhone4.png"];
_whiteGuy_EyelidsBlink.position = ccp(self.contentSize.width/2,self.contentSize.height/2);
[_whiteGuy_EyelidsBlink setScale:0.5];
[self addChild:_whiteGuy_EyelidsBlink];
CCActionRemove *remove_eyelidsBlink = [CCActionRemove action];
[_whiteGuy_EyelidsBlink runAction:remove_eyelidsBlink];
NSLog(#"Eyelids blinked");
_whiteGuy_EyelidsNormal = [CCSprite spriteWithImageNamed:#"EyelidsNormal_iPhone4.png"];
_whiteGuy_EyelidsNormal.position = ccp(self.contentSize.width/2,self.contentSize.height/2);
[_whiteGuy_EyelidsNormal setScale:0.5];
[self addChild:_whiteGuy_EyelidsNormal];
}
I can see the first blink, but I can't see any others after that. My NSLog is printing in the console every second, so I know the eyelidsBlink method is being called.
Can anyone help me figure out why I can't see any blinks after the first? Let me know if you need more information, or if you can suggest any tests to troubleshoot.
There's practically no time for the blink sprite to be rendered because you remove it the instant it was added. You'd have to schedule another selector once, ie eyeLidsBlinkOff that runs 0.1 seconds later and hides the blink sprite.
Note: This code is very inefficient. Creating sprites is a relatively slow operation. Instead keep both sprites as children but set one sprite's visible property to NO. While blinking simply flip each sprite's visible flag. This will make the code a lot shorter too.
I'm having a problem with side scrolling in Cocos2d. What the situation is, is that i have a sprite that contains multiple other sprites know as actions. The user can swipe back and forth horizontally to scroll through the multiple actions. Whats happening now is that it is very jerky and seems to lag and not a smooth scroll but just very choppy. Not sure what the problem is, I've tried to change the time of the animation but that doesn't seem to work.
- (void)translateInventoryForSwipe:(int)xTranslationValue {
NSArray* tempArray = [NSArray arrayWithArray:self.slotsCenterCoordinates];
[self.slotsCenterCoordinates removeAllObjects];
for (NSNumber* i in tempArray) {
NSNumber* newXCoordinate = [NSNumber numberWithInt:[i intValue] + xTranslationValue];
[self.slotsCenterCoordinates addObject:newXCoordinate];
}
[self updatePositionOfActionsInInventory];
}
this method takes in the delta x of the two touches from the parent view. (current touch minus previous touch) This sets the centre coord of all the actions in the scrolling view.
- (void)updatePositionOfActionsInInventory {
for (int inventoryCounter = 0; inventoryCounter < self.inventorySize; inventoryCounter++) {
FFAction* action = [self.actions objectAtIndex:inventoryCounter];
if (action != self.actionBeingDragged)
[self placeAction:action atIndex:inventoryCounter];
}
self.tempAction = nil;
}
- (void)placeAction:(FFAction*)action atIndex:(int)index {
const float yCenterCoordinate = self.boundingBox.size.height/2;
NSNumber* xCenterCoordinate = [self.slotsCenterCoordinates objectAtIndex:index];
CGPoint centerPointForActionAtIndex = ccp([xCenterCoordinate floatValue], yCenterCoordinate);
CCAction* updatePositionAction = [CCMoveTo actionWithDuration:0.03f position:centerPointForActionAtIndex];
if ([action.view numberOfRunningActions] == 0 || self.tempAction == action) {
[action.view runAction:updatePositionAction];
[action.view released];
}
}
this part is from the parent sprite that handles the touch:
CGPoint currentTouch = [self convertTouchToNodeSpace:touch];
CGPoint previousTouch = [touch previousLocationInView:[touch view]];
int translationPoint = currentTouch.x - previousTouch.x;
[self.inventory translateInventoryForSwipe:translationPoint withPoint:currentTouch];
this then sets the action coordinate mimicking a scrolling effect. I'm not sure where its causing the jerky motion but if anyone has any help on the situation it would be awesome!
Assuming all of the complexity in your code is not required, there are several aspects to consider here, I'll go through them one by one.
First, memory allocation is expensive and a lot of it is done in every call of translateInventoryForSwipe:. A whole new NSArray is created and the self.slotsCenterCoordinates is repopulated. Instead, you should iterate the action sprites and reposition them one by one.
This brings us to the second aspect, which is the use of CCAction to move the sprites. A new CCAction is created for every sprite, again causing delay because of the memory allocation. The CCAction is created, even if it would not be used. Also, the use of actions might be the main cause of the lag as a new action won't be accepted until the previous has finished. A better way to do this would be to directly reposition the sprites by delta instead of assigning actions for repositioning. The action is not required to get smooth movement as the frequency of calls to translateInventoryForSwipe: will be high.
You should also consider using float to send the delta value to the method instead of int. The touch coordinates are floats and especially on retina devices this matters as the distance of two pixels is 0.5f.
Based on these aspects, here is a template of what a fixed method could look like. This is not tested, so there may be errors. Also, I assumed that action.view is the actual sprite, as the actions are assigned there.
- (void)translateInventoryForSwipe:(float)xTranslationValue {
for (FFAction *action in self.actions) {
if (action == self.actionBeingDragged)
continue;
// Position the items manually
float xCoordinate = action.view.position.x + xTranslationValue;
float yCoordinate = self.boundingBox.size.height/2;
action.view.position = ccp(xCoordinate, yCoordinate);
}
}
I have CCMoveTo action on sprite, which is moving it from one point to another. When user hits a button the sprite should slow down with ease and continue moving to target location with new speed. I have no clue how to make this happen.
Update. Actually I replaced CCMoveTo with CCMoveBy but question still same.
With the current implementation of CCEaseIn/CCEaseOut actions, you can only ease the rate of actions from and to zero. This means that if you ease CCMoveBy/CCMoveTo they will ease the movement speed from/to a standstill.
However, starting from cocos2d 2.1 CCMoveBy/CCMoveTo are stackable. With this feature you can implement a workaround that results in the effect you want.
Setup and simulataneously run two CCMoveBy actions for the sprite: actionA will have the slower movement speed that you get after the button press. actionB will have the speed corresponding to the difference of the faster speed and the slower speed.
Then, when the user presses the button, you can CCEeaseOut actionB (stop CCMoveBy, and then launch it again with the desired CCEaseOut). This will look like the sprite eases from the movement speed of actionA + actionB to the speed of actionA.
Despite this explanation, if you are implementing game controls that you want to precisely fine tune, it might be a better idea to avoid CCActions and just update the sprite position frame-by-frame by implementing custom movement code.
You can use the EaseIn/Out action to get that effect :
id action = [CCMoveTo actionWithDuration:2 position:ccp(100,100)];
id ease = [CCEaseIn actionWithAction:action rate:2];
[sprite runAction: ease];
Taken from here
You can find different types of easing to suit your needs .
To dynamically change the speed of a CCMoveBy/CCMoveTo, there is a CCSpeed action you can implement.
First, when adding the CCMoveBy action, wrap it in a CCSpeed action as follows:
CCSpeed *action = [CCSpeed actionWithAction:[CCMoveBy actionWithDuration:myDuration position:ccp(xPos,yPos)] speed:1];
action.tag = 42;
[mySprite runAction:action];
Then, when you wish to modify the speed, obtain the CCSpeed action and modify it:
CCSpeed *action = (CCSpeed*)[mySprite getActionByTag:42];
[action setSpeed:6];
CGPoint startPoint = ccp(10,100);
CGPoint endPoint = ccp(300,100);
float fastTime = 5.f;
CCSprite *sp = [CCSprite spriteWithFile:#"sprite.png"];
sp.position = ccp(10,100);
[self addChild:sp];
[sp runAction:[CCMoveTo actionWithDuration:fastTime position:endPoint]];
//on hit
float slowTime = 10.f;
[sp stopAllActions];
float newSlowTime = ccpDistance(sp.position, endPoint)*slowTime/ccpDistance(startPoint, endPoint);
[sp runAction:[CCMoveTo actionWithDuration:newSlowTime position:endPoint]];
you can replace the ccmoveto action by ccactionmoveto like:
CCActionMoveTo *mAction = [CCActionMoveTo actionWithDuration:0.2f position:_selectedSpritePos];
[_selectedSprite stopAllActions];
[_selectedSprite runAction:mAction];
I am using COCOS-2D. I want to reduce the speed of rotation of sprite with object movement towards destination.
Initially the speed is faster, i want to reduce it smoothly with movement.
Please help how can I do this.
Thanks
The simplest approach is to run two actions in parallel - one for movement and another for rotation. Since you want your rotation to gradually slowdown, it could be done by applying an ease action on top of the rotation action. Something along these lines:
float animDuration = 1.5f;
float animRotateAngle = 720.f; // deg
CCActionInterval* effect = [CCSpawn actions:
[CCMoveTo actionWithDuration: animDuration position: destPoint],
[CCEaseSineOut actionWithAction: [CCRotateBy actionWithDuration: animDuration angle:animRotateAngle]],
nil];
[object runAction: effect];
Create a series of rotations, either explicitly or programmatically, increasing the duration and/or decreasing the angle with each new rotation.
- (void)update:(CCTime)delta {
// reduce _ship.rotation
[_ship.physicsBody applyTorque:(_ship.rotation - rotation_01)*800];
rotation_01 = _ship.rotation;
}
I thought doing a simple animation would be easy but is is taking hours and Im not getting even close to have the expected effect...
I need to simulate a simple Flash Motion Tween like for iphone/ipad using xcode
this is the desired effect: http://www.swfcabin.com/open/1340330187
I already tried setup a timer adding X position and it doens't get the same effect, my cooworkers suggested me cocos 2d to do this using actions and sprites, which might would be fine although I wouldn't like to third party frameworks, but if there is a way to do the same with cocos I would definitively use it.
Does anybody have any suggestions, I feel like it might be simpler than I thought
thanks all
If there is no troubles to you, that you will have to do it insinge OpenGL view, it is really very simple. To show some info, you need CCLabel class. To change it's position, you need CCMoveTo/CCMoveBy action, to change opacity, you need CCFadeTo/CCFadeIn/CCFadeOut actions, to make delay you need CCDelayTime. To make it all work together you need CCSpawn and CCSequence.
CCSpawn will run several actions at the same time(for example fade in and move from right to the center), CCSequence will run several actions one by one (sequence to fade in + move to center, delay for same time, sequence to fade out + move from center to the left). Then you should only schedule method, that will create labels and run actions on them. In code it will be something like
lets define full animation time
#define ANIMATION_TIME 4.f
schedule method in any place you want to start animation
[self schedule:#selector(runNextMessage) interval:ANIMATION_TIME];
it will call runNextMessage method every ANIMATION_TIME seconds
- (void) runNextMesage
{
NSString* message = //get next message
CCLabelTTF* label = [CCLabelTTF labelWithString:message
dimensions:desiredDimensionsOfTheLabel
alignment:UITextAlignmentLeft
lineBreakMode:UILineBreakModeWordWrap
fontName:#"Arial"
fontSize:20.f];
CGSize winSize = [[CCDirector sharedDirector] winSize];
// place the label out the right border
[label setPosition: ccp(winSize.width + label.contentSize.width, winSize.height / 2)];
// adding it to the screen
[self addChild:label];
ccTime spawnTime = ANIMATION_TIME / 3;
// create actions to run
id appearSpawn = [CCAction actionOne:[CCMoveTo actionWithDuration:spawnTime]
two:[CCFadeIn actionWithDuration:spawnTime]];
// create show action and disappear action
// create result sequence
id sequence = [CCSequence actions: appearSpawn, showAction, disappearAction, nil];
[label runAction: sequence];
}