How to increase interval over time? - iphone

I was wondering on how do I increase the interval over time so I can add target. I am still new to cocos2d.
[self schedule:#selector(gameLogic:) interval:0.7];
-(void)gameLogic:(ccTime)dt {
[self addTarget];
}

Why not declare a simple property (int, float, etc.) to hold the number of times your method has been called, and increment it when you call the method itself? That way, it's just a multiplication problem:
//.h
...
#property (nonatomic, assign) int iterations;
//.m
#synthesize iterations = iterations_;
[self schedule:#selector(gameLogic:) interval:0.7*iterations_];
-(void)gameLogic:(ccTime)dt {
[self addTarget];
iterations_++;
}

float interval = .7;
-(id)init{
...
[self scheduleOnce:#selector(gameLogic:) delay:interval]; //Check the name of the method, I'm not 100% sure about it
...
}
-(void)gameLogic:(ccTime)dt {
[self addTarget];
interval += dt; //Or whatever you want to increase it by
[self scheduleOnce:#selector(gameLogic:) delay:interval]; //Check the name of the method, I'm not 100% sure about it
}

Related

Efficient way to count view visits in iOS app

I have an iOS app with about 50 views. I want to perform some operation after every 5th screen that the user visits. I know I can create a sort of global counter variable and update that on viewDidLoad of each view, and if count is 5, then perform that operation, and reset that counter variable. Is there a better, more efficient way of doing this? Also looking ahead, if I require to alter something, I would rather do it in a single file than all of my views. Would really appreciate some inputs on this.
I would create a singleton class to keep track of your counter logic, create a base class for all of your view controllers and then make your call to the counter singleton in the viewDidLoad of your base class.
I think something like this would work for you:
#interface ViewCountManager()
#property(nonatomic) NSInteger viewCount;
#end
#implementation ViewCountManager
#define kOperateOnCount 5
+(ViewCountManager *)viewCountManager
{
static ViewCountManager *viewCountManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
viewCountManager = [[self alloc] init];
});
return viewCountManager;
}
-(BOOL)shouldPerformOperation
{
BOOL retValue = NO;
if( self.viewCount == kOperateOnCount - 1 )
{
retValue = YES;
self.viewCount = 0;
}
else
{
self.viewCount++;
}
return retValue;
}
#end
#implementation CountedViewController
-(void)viewDidLoad:(BOOL)animated
{
[super viewDidLoad:animated];
BOOL shouldPerform = [[ViewCountManager viewCountManager] shouldPerformOperation];
[self performOperation];
}
#end

Cocos2D Character animation

I started playing around with Cocos2D and I figured out to do a sprite animation with sprite sheets.
Now I have a little robot walking around on my screen. But I am wondering how I can put one animation into another.
For example, my robot has a walking animation and I want to move his arms separately. But his shoulders will rise and fall during walking and the arms should move relative to his shoulder position.
My walk animation has five sprites and I have nine different arm positions. Now, I could add the arms to every single walking sprite for all nine arm positions. but then I would have 45 Images instead of 14.
That's a good question (finally!).
What I would do is separate your character into different sprites (you already did this) that are animatable on their own.
Then, whenever a frame of that animation is presented, I would have a block of code executed that modified the position of the arms animation, so it matches the shoulders.
To execute that block of code, you'd need Cocos 1.1 or better, since they added the CCAnimationFrame there, however, those frames can only execute code via an NSNotification, so I made an improvement so we can set a block on a frame, and that block would be executed whenever that frame is displayed.
Just find CCAnimation.h and modify the CCAnimationFrame interface to look like this:
typedef void(^FrameBlock)(CCSprite *sprite);
/** CCAnimationFrame
A frame of the animation. It contains information like:
- sprite frame name
- # of delay units.
- offset
#since v1.1
*/
#interface CCAnimationFrame : NSObject <NSCopying>
{
CCSpriteFrame* spriteFrame_;
float delayUnits_;
NSDictionary *userInfo_;
FrameBlock frameBlock;
}
/** CCSpriteFrameName to be used */
#property (nonatomic, readwrite, retain) CCSpriteFrame* spriteFrame;
/** how many units of time the frame takes */
#property (nonatomic, readwrite) float delayUnits;
/** A CCAnimationFrameDisplayedNotification notification will be broadcasted when the frame is displayed with this dictionary as UserInfo. If UserInfo is nil, then no notification will be broadcasted. */
#property (nonatomic, readwrite, retain) NSDictionary *userInfo;
/** If the block is not NULL, it will be executed when the frame becomes visible **/
#property (nonatomic, readwrite, copy) FrameBlock frameBlock;
/** initializes the animation frame with a spriteframe, number of delay units and a notification user info */
-(id) initWithSpriteFrame:(CCSpriteFrame*)spriteFrame delayUnits:(float)delayUnits userInfo:(NSDictionary*)userInfo;
-(id) initWithSpriteFrame:(CCSpriteFrame*)spriteFrame delayUnits:(float)delayUnits block:(FrameBlock)block;
#end
And then open CCAnimation.m and make sure the CCAnimationFrame implementation looks like this:
#implementation CCAnimationFrame
#synthesize spriteFrame = spriteFrame_, delayUnits = delayUnits_, userInfo=userInfo_;
#synthesize frameBlock;
-(id) initWithSpriteFrame:(CCSpriteFrame *)spriteFrame delayUnits:(float)delayUnits userInfo:(NSDictionary*)userInfo
{
if( (self=[super init]) ) {
self.spriteFrame = spriteFrame;
self.delayUnits = delayUnits;
self.userInfo = userInfo;
}
return self;
}
-(id) initWithSpriteFrame:(CCSpriteFrame*)spriteFrame delayUnits:(float)delayUnits block:(FrameBlock)block{
self = [self initWithSpriteFrame:spriteFrame delayUnits:delayUnits userInfo:nil];
if(self){
[self setFrameBlock:block];
}
return self;
}
-(void) dealloc
{
CCLOGINFO( #"cocos2d: deallocing %#", self);
[spriteFrame_ release];
[userInfo_ release];
[super dealloc];
}
-(id) copyWithZone: (NSZone*) zone
{
CCAnimationFrame *copy = [[[self class] allocWithZone: zone] initWithSpriteFrame:[[spriteFrame_ copy] autorelease] delayUnits:delayUnits_ userInfo:[[userInfo_ copy] autorelease] ];
return copy;
}
-(NSString*) description
{
return [NSString stringWithFormat:#"<%# = %08X | SpriteFrame = %08X, delayUnits = %0.2f >", [self class], self, spriteFrame_, delayUnits_ ];
}
#end
Then, when creating the animation frames, add a block to them in which you set the position of the arms to match the shoulders.
I hope it helps.

Connecting extremely basic objective-c program to iPhone application

I was wondering if it is feasible for me to connect this simple console program I have in objective-c into a very simple iPhone application.
Since I have no experience using Interface Builder, I'm not sure how long it would take for me to learn it.
Also, I believe my code would have to be adjusted to some iPhone APIs, rather than using the console for input and output.
Here is the program's main.m file, where all of its code is store:
//Simple program to convert Fahrenheit to Celsius and Celsius to Fahrenheit
#import <stdio.h>
#interface Converter: NSObject
{
//Instance variable which stores converting formula for objects
double formula;
}
//Declare instance methods for setting and getting instance variables
-(void) convert: (double) expression;
-(double) formula;
#end
#implementation Converter;
//Define instance method to set the argument (expression) equal to the instance variable
-(void) convert: (double) expression
{
formula = expression;
}
//Define instance method for returning instance variable, formula
-(double) formula
{
return formula;
}
#end
int main (int argc, char *argv[])
{
//Point two new objects, one for the Fahrenheit conversions and the other for the Celsius conversions, to the Converter class
Converter *fahrenheitConversion = [[Converter alloc] init];
Converter *celsiusConversion = [[Converter alloc] init];
//Declare two double variables holding the user-inputted data, and one integer variable for the if else statement
double fahrenheit, celsius;
int prompt;
NSLog(#"Please press 0 to convert Celsius to Fahrenheit, or 1 to convert Fahrenheit to Celsius\n ");
scanf("%d", &prompt);
if(prompt == 0) {
NSLog(#"Please enter a temperature in Celsius to be converted into Fahrenheit!:\n");
scanf("%lf", &celsius);
if(celsius < -273.15) {
NSLog(#"It is impossible to convert temperatures less than −273.15 degrees Celsius, because this is absolute zero, the coldest possible temperature.");
}
else {
[fahrenheitConversion convert: (((((celsius)*9)/5)+32))];
NSLog(#"%lf degrees Celsius is %lf Fahrenheit", celsius, [fahrenheitConversion formula]);
}
}
else {
NSLog(#"Please enter a temperature in Fahrenheit to be converted into Celsius!:\n");
scanf("%lf", &fahrenheit);
if(fahrenheit < -459.67) {
NSLog(#"It is impossible to convert temperatures less than −459.67 degrees Fahrenheit, because this is absolute zero, the coldest possible temperature.");
}
else {
[celsiusConversion convert: ((((fahrenheit - 32)/9)*5))];
NSLog(#"%lf degrees Fahrenheit is %lf Celsius", fahrenheit, [celsiusConversion formula]);
}
}
return 0;
}
How about this?
I tried not to change your code too much. It uses your original formulas and converter class, even though I was tempted to change it.
Screenshot:
alt text http://brockwoolf.com/shares/stackoverflow/3443063/tempcalc-iphone.png
Download Xcode project zipped (29kb)
Source Code:
ViewController interface:
#import <UIKit/UIKit.h>
#interface TempCalc_iPhoneViewController : UIViewController {
UITextField *celciusTextField;
UITextField *fahrenheitTextField;
UILabel *statusLabel;
double minFahrenheit;
double minCelcius;
NSString *normalStatus;
NSString *belowFahrenheitMessage;
NSString *belowCelciusMessage;
}
#property (nonatomic,retain) IBOutlet UITextField *celciusTextField;
#property (nonatomic,retain) IBOutlet UITextField *fahrenheitTextField;
#property (nonatomic,retain) IBOutlet UILabel *statusLabel;
- (IBAction) convertCelciusToFahrenheit:(UITextField*)sender;
- (IBAction) convertFahrenheitToCelcius:(UITextField*)sender;
#end
ViewController implementation
#import "TempCalc_iPhoneViewController.h"
#import "Converter.h"
#implementation TempCalc_iPhoneViewController
#synthesize celciusTextField, fahrenheitTextField, statusLabel;
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
minCelcius = -273.15;
minFahrenheit = -459.67;
normalStatus = #"Please type to convert temperature";
belowFahrenheitMessage = #"impossible to convert temperatures less than −459.67 degrees Fahrenheit";
belowCelciusMessage = #"impossible to convert temperatures less than −273.15 degrees Celsius";
}
// Delegate method
- (BOOL)textFieldShouldReturn:(UITextField *)theTextField
{
NSLog(#"User pressed the DONE button on the keyboard (any keyboard!)");
[theTextField resignFirstResponder];
return YES;
}
- (IBAction) convertCelciusToFahrenheit:(UITextField*)sender
{
Converter *celciusConversion = [[Converter alloc] init];
double celcius = [sender.text doubleValue];
if(celcius < minCelcius) {
NSLog(#"It is impossible to convert temperatures less than −273.15 degrees Celsius, because this is absolute zero, the coldest possible temperature.");
self.statusLabel.text = belowCelciusMessage;
}
else
{
[celciusConversion convert: ((((celcius)*9)/5)+32)];
NSString *fahrenheitValue = [NSString stringWithFormat:#"%lf", [celciusConversion formula]];
NSLog(#"%lf degrees Celsius is %# Fahrenheit", celcius, fahrenheitValue);
self.fahrenheitTextField.text = fahrenheitValue;
self.statusLabel.text = normalStatus;
}
[celciusConversion release];
}
- (IBAction) convertFahrenheitToCelcius:(UITextField*)sender
{
Converter *fahrenheitConversion = [[Converter alloc] init];
double fahrenheit = [sender.text doubleValue];
if(fahrenheit < minFahrenheit) {
NSLog(#"It is impossible to convert temperatures less than −459.67 degrees Fahrenheit, because this is absolute zero, the coldest possible temperature.");
self.statusLabel.text = belowFahrenheitMessage;
}
else {
[fahrenheitConversion convert: (((fahrenheit - 32)/9)*5)];
NSString *celciusValue = [NSString stringWithFormat:#"%lf", [fahrenheitConversion formula]];
NSLog(#"%lf degrees Fahrenheit is %# Celsius", fahrenheit, celciusValue);
self.celciusTextField.text = celciusValue;
self.statusLabel.text = normalStatus; }
[fahrenheitConversion release];
}
Porting it should be really simple. Apple provides some good sample apps. your fastest road to success is probably to download a simple textfield + button sample and add your calculations. Excluding the time it takes to download and install you'll have it ready in 1-2 hours.
Good luck!

How to pass arguments when calling function with timer in objective c

-(void)setX:(int)x andY:(int)y andObject:(Sprite*)obj
{
[obj setPosition:CGPointMake(x,y)];
}
Now, I want to call above method, using following timer.
[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector() userInfo:nil repeats:NO];
What to set Here?
How to Pass arguments? (as per my knowledge - selector specifies only the method to invoke)
You'll need to used +[NSTimer scheduledTimerWithTimeInterval:invocation:repeats:] instead. By default, the selector used to fire a timer takes one parameter. If you need something other than that, you have to create an NSInvocation object, which the timer will use instead.
If you have a fairly complex set of arguments that you want to use to invoke the method, I would recommend capturing the arguments into something that holds a configuration and can do whatever it is that needs doing based on that configuration...
Something with an interface like this:
PositionSetter.h:
#interface PositionSetter : NSObject
{
NSInteger x;
NSInteger y;
Sprite *target;
}
+ positionSetterWithX: (NSInteger) xPos y: (NSInteger) yPos sprite: (Sprite *) aSprite;
- (void) applyPosition;
#end
PositionSetter.m:
#interface PositionSetter()
#property(readwrite, nonatomic) NSInteger x;
#property(readwrite, nonatomic) NSInteger y;
#property(readwrite, nonatomic, retain) Sprite *target;
#end
#implementation PositionSetter
#synthesize x, y, target;
+ positionSetterWithX: (NSInteger) xPos y: (NSInteger) yPos sprite: (Sprite *) aSprite;
{
PositionSetter *positionSetter = [PositionSetter new];
positionSetter.x = xPos;
positionSetter.y = yPos;
positionSetter.target = aSprite;
return [positionSetter autorelease];
}
- (void) applyPosition;
{
[self.target setPosition:CGPointMake(self.x,self.y)];
}
#end
Usage is quite straightforward:
positionSetter = [PositionSetter positionSetterWithX: 42 y: 21 sprite: mySprite];
[positionSetter performSelector: #selector(applyPosition) withObject: nil afterDelay: 1.0];
While a tad more code, the resulting implementation will be fast enough -- probably faster than NSInvocation, but fast enough to be irrelevant given that this is gonna cause drawing -- and a heck of a lot more flexible. I could easily see refactoring the above into driving, say, CoreAnimation.
Copied from an answer by Matt Ball:
- (void)startMyTimer {
/* ... Some stuff ... */
NSDictionary *userDict;
userDict = [NSDictionary dictionaryWithObjectsAndKeys:someValue,
#"value1",
someOtherValue,
#"value2", nil];
[NSTimer scheduledTimerWithTimeInterval:0.1
target:self
selector:#selector(callMyMethod:)
userInfo:userDict
repeats:YES];
}
- (void)callMyMethod:(NSTimer *)theTimer {
NSString *value1 = [[theTimer userInfo] objectForKey:#"value1"];
NSString *value2 = [[theTimer userInfo] objectForKey:#"value2"];
[self myMethod:value1 setValue2:value2];
}
If you use a target-action timer, you can't have the timer directly call an arbitrary method. A timer's action must have a very specific signature. You can pass additional data in the userinfo dictionary and have the timer's action call the method you ultimately want, or you can use the invocation form as Dave said. Personally, I usually do the former, because I find NSInvocations to be annoying and setting one up can actually take more code than just writing an intermediary method.
You can pass an NSDictionary*, or some other object, as the userInfo and put the arguments in that.
As an alternative to NSTimer, on iOS 4.0+ and 10.6+, you could use Grand Central Dispatch and dispatch sources to do this using a block. Apple has the following code for this in their Concurrency Programming Guide:
dispatch_source_t CreateDispatchTimer(uint64_t interval, uint64_t leeway, dispatch_queue_t queue, dispatch_block_t block)
{
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
if (timer)
{
dispatch_source_set_timer(timer, dispatch_walltime(NULL, 0), interval, leeway);
dispatch_source_set_event_handler(timer, block);
dispatch_resume(timer);
}
return timer;
}
You could then set up a one-second timer event using code like the following:
dispatch_source_t newTimer = CreateDispatchTimer(1ull * NSEC_PER_SEC, (1ull * NSEC_PER_SEC) / 10, dispatch_get_main_queue(), ^{
[self setX:someValue andY:otherValue andObject:obj];
});
as long as you store and release your timer when done. This can even let you trigger a timer to execute items on a background thread by using a concurrent queue instead of the main queue used above.
This can avoid the need for boxing and unboxing arguments.
Create dictionary with those arguments and pass that dictionary with timer userinfo. That will solve your problem

Can the cocos2d Director return time?

I am currently using the cocos2d Director for controlling my animation with the pause, resume, and stopAnimation methods. Is it also possible to use the Director to return the time that the animation has played?
I am currently using this method:
-(void)stopAnimation:(id)sender {
//Timer initialized elsewhere: startTimer = [NSDate timeIntervalSinceReferenceDate];
//Do other method stuff here
[[Director sharedDirector] stopAnimation];
stopTimer = [NSDate timeIntervalSinceReferenceDate];
elapsedTime = (stopTimer - startTimer);
NSLog(#"elapsedTime = %f", elapsedTime);
}
I looked through the Director source and didn't see anything that would help you. I did notice that your code, as written, doesn't take into account the time your animation was paused or when other scenes were playing.
If that is a concern you can keep track of the elapsed time in a tick method that you schedule in your Scene or Layer.
MyLayer.h
#interface MyLayer : Layer {
ccTime totalTime;
}
#property (nonatomic, assign) ccTime totalTime;
MyLayer.m
-(id)init
{
if( (self = [super init]) )
{
[self schedule:#selector(update:)];
}
return self;
}
// deltaTime is the amount of running time that has passed
// since the last time update was called
// Will only be called when the director is not paused
// and when it is part of the active scene
-(void)update:(ccTime)deltaTime
{
totalTime += deltaTime;
}