I know how to display those info in the screen, but I would like to log them in a file/console for offline investigation. How could I do that?
You can measure it yourself using scene - (void)update method. This method calls each frame need to calculate.
- (void)update
{
self.frameCount++; // increase frame count value
uint64_t currentTime = mach_absolute_time(); // get current time
// according to this two values and last update method call time you're already can calculate the fps
self.lastUpdateTick = currentTime; // remember for the future method call
}
To get the nodes count value you should just to count children of all the scene children. It may be some kind of recursive algorithm (not tested).
- (NSUInteger)childrenOf:(SKNode *)node
{
NSUInteger count = 0;
for (SKNode *child in node.children)
count += [self childrenOf:child] + 1;
return count;
}
- (void)calculateSceneChildrenCount
{
NSUInteger count = [self childrenOf:self];
NSLog(#"count is %lu",count);
}
Related
I'm trying to make a couple hundred levels for a game I'm developing, so obviously I want to use a for loop and not create each button individually.
I do all of this fine, but the code I'm currently using makes it so that the call block for each button is the same. Obviously if I'm going to have the different level buttons go to different levels, the call block must be different for each one.
In order to differentiate which level I'm on, I call [[LevelManager sharedInstance] nextLevel] the number of times corresponding to the level number. I attempted to change this number by getting the touch location of the user touching the button, getting a row/column number, then running the above code a certain number of times. However, it was not updating the touch position before running the whole call block after touching the button, which obviously makes my code not work.
Is there a way to update the touch position manually, before the call block finishes? Is there a way to somehow store every button in an array and establish a different call block for each one? I'm not sure what the best approach is to fixing my problem. Thanks and my code is below.
Called upon initialization
for (int l = 0; l < NUMBER_OF_WORLDS; l++) {
for (int j = 0; j < 4; j++) {
for (int k = 0; k < 5; k++) {
NSString *tempTitle = [NSString stringWithFormat:#"%i",(k+1)+(l*5)];
CCButton *tempButton;
tempButton = [CCButton buttonWithTitle:tempTitle]
tempButton.block = ^(id sender) {
int row = floorf(touchLocation.y/(screenBounds.size.height/6)) + 1;
int column = floorf(touchLocation.x/(screenBounds.size.width/4)) + 1;
int buttonIndex = row*column;
for (int m = 1; m < buttonIndex; m++) {
[[LevelManager sharedInstance] nextLevel];
}
CCScene *gameplayScene = [CCBReader loadAsScene:#"LevelScene"];
[[CCDirector sharedDirector] replaceScene:gameplayScene];
levelNumber = i;
};
tempButton.position = ccp((screenBounds.size.width/4)*j + levelImagePlaceholder.contentSize.width*tempButton.scale, screenBounds.size.height/6*k + 50 + l*screenBounds.size.height);
[self addChild:tempButton];
i++;
}
}
}
Method for getting touch position
-(void) touchBegan:(UITouch *)touch withEvent:(UIEvent *)event
{
CCLOG(#"touch ran");
touchLocation = [touch locationInNode:self];
}
You already have the row/column available. They are j and k. Those variables can be referenced in your button's "on pressed" block.
I am new to the community, so let me know if my question is unclear. I am trying to make a choice reaction exercise on the iPAD. There are two images that should appear in random sequence on the left and right of the screen, and the user will respond by tapping a button that corresponds to the position of the appeared image. Here's the problem, I tried to get the two images to appear at random order using the following way:
- (void) viewDidAppear:(BOOL)animated
{
for(int n = 1; n <= 20; n = n + 1)
{
int r = arc4random() % 2;
NSLog(#"%i", r);
if(r==1)
{
[self greenCircleAppear:nil finished:nil context: nil];
}
else
{
[self redCircleAppear:nil finished:nil context: nil];
}
}
}
However, 20 random numbers get generated while only 1 set of animation is run. Is there a way to let the animation finish running in each loop before the next loop begins? Any help is appreciated, thanks in advance!
When you say "only one set of animation is run" I'm assuming that means greenCircleAppear and redCircleAppear begin the sequence of images appearing and the user pressing a button. If that's the case, I'd recommend not using a for loop in viewDidAppear but instead have viewDidAppear initialize the current state and call a method that presents the next animation. When the animation is finished, have it call the method that presents the next animation. Something along these lines:
Add this to the interface:
#interface ViewController ()
#property NSInteger currentIteration;
#end
This is in the implementation:
- (void)viewDidAppear:(BOOL)animated {
self.currentIteration = 0;
[self showNextAnimation];
}
- (void)greenCircleAppear:(id)arg1 finished:(id)arg2 context:(id)arg3 {
//perform animation
NSLog(#"green");
[self showNextAnimation];
}
- (void)redCircleAppear:(id)arg1 finished:(id)arg2 context:(id)arg3 {
//perform animation
NSLog(#"red");
[self showNextAnimation];
}
- (void)showNextAnimation {
self.currentIteration = self.currentIteration + 1;
if (self.currentIteration <= 20) { //you should replace '20' with a constant
int r = arc4random() % 2;
NSLog(#"%i", r);
if(r==1)
{
[self greenCircleAppear:nil finished:nil context: nil];
}
else
{
[self redCircleAppear:nil finished:nil context: nil];
}
}
else {
//do what needs to be done after the last animation
}
}
Right now I have a NSArray with x amount of coordinates inside; I have a sprite that needs to go to each of the points to go along the selected path. I tried using a for loop, but that does it in such quick succession that it appears to just teleport to the final destination. I've tried a with selectors but I can't get those to work either. Anyone know how to do this?
you have to use an NSTimer
#implementation whatever
{
int count;
NSTimer *myTimer;
CCSprite *mySprite;
NSArray *locationArray;
}
and then start the timer fromsomewhere...
count=0
//1.0 is one second, so change it to however long you want to wait between position changes
myTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(movealong) userInfo:nil repeats:YES];
And then it calls this until it goes through all the object
-(void)movealong{
//assuming array is full of CGPoints...
//mySprite.position = [locationArray objectAtIndex:count];
//try this then
CGPoint newPoint = [locationArray objectAtIndex:count];
mySprite.position = ccp(newPoint.x,newPoint.y);
//I think maybe THIS is what you need to do to make it work...
//if you want it to not jump directly there
//you can also use CCMoveTo
/*// under actionWithDuration: put the same amount of time or less of what you had
// under scheduledTimerWithTimeInterval in your NSTimer
CCFiniteTimeAction *newPos = [CCMoveTo actionWithDuration:1.0 position:[locationArray objectAtIndex:count]];
[mySprite runAction:newPos];
*/
count++;
if(count >= locationArray.count){
[myTimer invalidate];
myTimer = nil;
}
}
Create a method that will place your sprite to position that is at some index (for example, _i) in the positions array. And at the end of this method call it again with delay, using CCDelayTime and CCCallFunc actions in sequence. And do not forget to increment index. Smth like
// somewhere in code to start move
_i = 0;
[self setNewPosition];
// method that will set the next position
- (void) setNewPosition
{
sprite.position = // get position at index _i from your array here
_i++;
BOOL needSetNextPosition = // check if _i is inside bounds of your positions array
if( needSetNextPosition )
{
id delay = [CCDelayTime actionWithDuration: delayBetweenUpdate];
id callback = [CCCallFunc actionWithTarget: self selector: #selector(setNewPosition)];
id sequence = [CCSequence actionOne: delay two: callback];
[self runAction = sequence];
}
}
This is just example, but I hope, you can adapt it for your needs.
I'm working on my first big application and have inadvertently driven myself into a panic from a small flaw in design. I've made a timer that counts at the touch of a button, and upon a second touch, transitions into a secondary timer with a 60 second countdown.
The problem lies in the fact that once I repeat this process, the (kRest-Pause) call is remembered. I'm looking to create a default countdown of 60 without the continuation of the time. Should I kill it in memory and create a new instance for each subsequent button press? Or is there a logic game that looks at the aggregate time and corrects with each new occurance?
I don't know how to approach this, I'm done attempting -if statements with returns as I have learned that's not how that works. Any help would be appreciated
EDIT: Sorry for the lack of clarity. I'm pretty green on programming of any caliber. So far, the main timer counts down from 10-0 then up from 0-120. In that 2 minute period, if the button is pressed again, the 0-120 count is paused for 60 seconds or until the button is pressed for the third time. If this 60-0 countdown reaches 0 or is interrupted, the initial 0-120 countup resumes its count. My issue is that if I push the button a fourth time, the 60-0 countdown is resumed from the moment of interruption without retaining a default of 60. This is why I named the post "creating defaults in Objective C". It's the wrong use of the word and way to broad, but it's what I could come up with.
kRest=60
-(void)increase{
if (mode==1){
count++;
int d = 10-count;
if (d==0){ timeLabel.text = #"Begin";
[self startPlaybackForPlayer: self.startTimerSound];}
else {timeLabel.text = [NSString stringWithFormat:#"%d", abs(d)];}
if(d<0){
//workign out
active = TRUE;
if (d <= -20) {
[self stopTimer];
}
}
else{
//no user interface
active = FALSE;
}
}
else{
pause++;
countdownLabel.text = [NSString stringWithFormat:#"%d!", (kRest-pause)];
NSLog(#"Paused at time %d", pause);
UIColor *textColor = nil;
if (pause % 2==0){
textColor = [UIColor yellowColor];
}
else{
textColor = [UIColor redColor];
}
timeLabel.textColor = textColor;
if ((kRest-pause)==0){
countdownLabel.text = [NSString stringWithFormat:#"%d!",pause];
mode=1;
pause=0;
[button setTitle:#"Stop" forState:UIControlStateNormal];
repCount++;
myRepCount.text = [NSString stringWithFormat:#"Rep Count: %d", repCount];
countdownLabel.text = #"";
}
}
}
if you are using a timer to access this counter then you should be able to update the counter that the timer uses. Just make sure you synchronize the object so you dont edit while you are reading.
here is an example of what I mean.
int counter = 0;
int limit = 60;
- (BOOL) incrementUntilReached{
#synchronized(self){
if (counter == limit) return YES;
counter++;
return NO;
}
}
- (void) resetTimer{
#synchronized(self){
counter = 0;
}
}
- (int) countsLeft {
#synchronized(self){
return limit - counter;
}
}
I'm trying to build an iOS app that displays the total distance travelled when running or walking. I've read and re-read all the documentation I can find, but I'm having trouble coming up with something that gives me an accurate total distance.
When compared with Nike+ GPS or RunKeeper, my app consistently reports a shorter distance. They'll report the same at first, but as I keep moving, the values of my app vs other running apps gradually drift.
For example, if I walk .3 kilometers (verified by my car's odometer), Nike+ GPS and RunKeeper both report ~.3 kilometers every time, but my app will report ~.13 kilometers. newLocation.horizontalAccuracy is consistently 5.0 or 10.0.
Here's the code I'm using. Am I missing something obvious? Any thoughts on how I could improve this to get a more accurate reading?
#define kDistanceCalculationInterval 10 // the interval (seconds) at which we calculate the user's distance
#define kNumLocationHistoriesToKeep 5 // the number of locations to store in history so that we can look back at them and determine which is most accurate
#define kValidLocationHistoryDeltaInterval 3 // the maximum valid age in seconds of a location stored in the location history
#define kMinLocationsNeededToUpdateDistance 3 // the number of locations needed in history before we will even update the current distance
#define kRequiredHorizontalAccuracy 40.0f // the required accuracy in meters for a location. anything above this number will be discarded
- (id)init {
if ((self = [super init])) {
if ([CLLocationManager locationServicesEnabled]) {
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
self.locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
self.locationManager.distanceFilter = 5; // specified in meters
}
self.locationHistory = [NSMutableArray arrayWithCapacity:kNumLocationHistoriesToKeep];
}
return self;
}
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
// since the oldLocation might be from some previous use of core location, we need to make sure we're getting data from this run
if (oldLocation == nil) return;
BOOL isStaleLocation = [oldLocation.timestamp compare:self.startTimestamp] == NSOrderedAscending;
[self.delegate locationManagerDebugText:[NSString stringWithFormat:#"accuracy: %.2f", newLocation.horizontalAccuracy]];
if (!isStaleLocation && newLocation.horizontalAccuracy >= 0.0f && newLocation.horizontalAccuracy < kRequiredHorizontalAccuracy) {
[self.locationHistory addObject:newLocation];
if ([self.locationHistory count] > kNumLocationHistoriesToKeep) {
[self.locationHistory removeObjectAtIndex:0];
}
BOOL canUpdateDistance = NO;
if ([self.locationHistory count] >= kMinLocationsNeededToUpdateDistance) {
canUpdateDistance = YES;
}
if ([NSDate timeIntervalSinceReferenceDate] - self.lastDistanceCalculation > kDistanceCalculationInterval) {
self.lastDistanceCalculation = [NSDate timeIntervalSinceReferenceDate];
CLLocation *lastLocation = (self.lastRecordedLocation != nil) ? self.lastRecordedLocation : oldLocation;
CLLocation *bestLocation = nil;
CGFloat bestAccuracy = kRequiredHorizontalAccuracy;
for (CLLocation *location in self.locationHistory) {
if ([NSDate timeIntervalSinceReferenceDate] - [location.timestamp timeIntervalSinceReferenceDate] <= kValidLocationHistoryDeltaInterval) {
if (location.horizontalAccuracy < bestAccuracy && location != lastLocation) {
bestAccuracy = location.horizontalAccuracy;
bestLocation = location;
}
}
}
if (bestLocation == nil) bestLocation = newLocation;
CLLocationDistance distance = [bestLocation distanceFromLocation:lastLocation];
if (canUpdateDistance) self.totalDistance += distance;
self.lastRecordedLocation = bestLocation;
}
}
}
As it turns out, the code I posted above works great. The problem happened to be in a different part of my app. I was accidentally converting the distance from meters to miles, instead of from meters to kilometers. Oops!
Anyway, hopefully my post will still have some merit, since I feel it's a pretty solid example of how to track a user's distance with Core Location.
You probably have set kRequiredHorizontalAccuracy too low. If there is no location in the history that has accuracy < kRequiredHorizontalAccuracy, then you ignore all those points and add 0 to the distance.