I am programming my game at the moment in sprite kit, I have 8 differnt methods and i have it set up to call a 1 method every 5 seconds, but instead of just being able to call 1 method i want it to randomly select 1 of the 8 methods and call that. Here is my current code:
- (void)updateWithTimeSinceLastUpdate:(CFTimeInterval)timeSinceLast {
self.lastSpawnTimeInterval += timeSinceLast;
if (self.lastSpawnTimeInterval > 5) {
self.lastSpawnTimeInterval = 0;
[self shootPizza];
}
}
- (void)update:(NSTimeInterval)currentTime {
// Handle time delta.
// If we drop below 60fps, we still want everything to move the same distance.
CFTimeInterval timeSinceLast = currentTime - self.lastUpdateTimeInterval;
self.lastUpdateTimeInterval = currentTime;
if (timeSinceLast > 1) { // more than a second since last update
timeSinceLast = 1.0 / 60.0;
self.lastUpdateTimeInterval = currentTime;
}
[self updateWithTimeSinceLastUpdate:timeSinceLast];
}
You could use selectors to achieve your goal.
Eg.
- (IBAction)performRandomMethod:(id)sender {
// put the method names as NSStrings into an array
// selectors are not objects, thus we convert to NSValue to allow storage in NSArray
NSArray *applicableMethods = #[[NSValue valueWithPointer:#selector(doA)],
[NSValue valueWithPointer:#selector(doB)],
[NSValue valueWithPointer:#selector(doC)]];
// randomly pick one of the objects from the array and convert back to a selector
NSUInteger randomIndex = arc4random_uniform(applicableMethods.count);
SEL randomMethodSelector = [[applicableMethods objectAtIndex:randomIndex] pointerValue];
// perform the selector
// ARC may complain regarding a selector leak - we can suppress with the following pragma marks
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[self performSelector:randomMethodSelector withObject:nil];
#pragma clang diagnostic pop
}
- (void)doA {
NSLog(#"doA");
}
- (void)doB {
NSLog(#"doB");
}
- (void)doC {
NSLog(#"doC");
}
For more information on the code to suppress the selector leak warning, you should refer to the following question: performSelector may cause a leak because its selector is unknown
An intro to selectors can be found in Cocoa Core Competencies: Selector (Apple Docs)
This generates a random number between 0 and 7 inclusive.
#include <stdlib.h>
...
...
int method = arc4random() % 8;
You can then use integer stored in method to select between your various methods.
Related
I am currently making a game in sprite kit and i have 8 methods, I have written all the timing code E.T.C so it calls a method every 1 second, but i want it to call a random one of the eight methods, I have been trying to get this working for weeks, any help would be muchly appreciated, Here is my timing code -
- (void)updateWithTimeSinceLastUpdate:(CFTimeInterval)timeSinceLast {
self.lastSpawnTimeInterval += timeSinceLast;
if (self.lastSpawnTimeInterval > 5) {
self.lastSpawnTimeInterval = 0;
[self shoot1];
}
}
- (void)update:(NSTimeInterval)currentTime {
// Handle time delta.
// If we drop below 60fps, we still want everything to move the same distance.
CFTimeInterval timeSinceLast = currentTime - self.lastUpdateTimeInterval;
self.lastUpdateTimeInterval = currentTime;
if (timeSinceLast > 1) { // more than a second since last update
timeSinceLast = 1.0 / 60.0;
self.lastUpdateTimeInterval = currentTime;
}
[self updateWithTimeSinceLastUpdate:timeSinceLast];
}
As you can see instead of [self shoot1]i want it to randomly call one of the eight methods, Also all the methods are named Shoot1, Shoot2, all the way to Shoot8. Thankyou
I can think of two options...
Option 1
Just pick a random number between 1 and 8, and use a switch statement:
- (void)updateWithTimeSinceLastUpdate:(CFTimeInterval)timeSinceLast {
self.lastSpawnTimeInterval += timeSinceLast;
if (self.lastSpawnTimeInterval > 5) {
self.lastSpawnTimeInterval = 0;
int randomNumber = arc4random_uniform(8);
switch(randomNumber) {
case 0:
[self shoot1];
break;
case 1:
[self shoot2];
break;
// ... cases 2-6
case 7:
[self shoot8];
break;
}
}
}
Option 2
Rewrite your shootN methods so that you only have one method that takes an integer as a parameter:
- (void)shoot:(int)index;
Then, you can just do the following:
[self shoot:arc4random_uniform(8)];
You could also live dangerously...
int random = arc4random() % 8;
NSString *selectorName = [NSString stringWithFormat:#"shoot%i", random];
SEL selector = NSSelectorFromString(selectorName);
if ([self respondsToSelector:selector]) {
[self performSelector:selector];
}
This will, by the way, generate a warning when using ARC.
I have a for....loop and the UI then is blocked when in the loop.
Is the accelerometer also blocked?
I have following code in viewDidLoad
- (void)viewDidLoad{
[super viewDidLoad];
UIAccelerometer *accel = [UIAccelerometer sharedAccelerometer];
accel.delegate = self;
accel.updateInterval = 1.0f/60.0f;
}
and further down
- (void)accelerometer:(UIAccelerometer *)acel didAccelerate:(UIAcceleration *)acceleration {
acc=acceleration.x;
}
then an IBAction connected to a button which starts a loop
- (IBAction)doSomething {
for (int n = 1; n<=100;n++) {
// do someting in the loop
NSLog(#"x: %g", acc);
}
}
when logging the accelerometer x value it shows only the first value when the button is pressed and does not update when the loop is running. The same first value is repeating continuously.
Is there a way to log the acceleration when in the loop?
It would seem that UIAccelerometer needs the main thread so is getting blocked by the for...loop. Core Motion also has an accelerometer property in the CMMotionManager object that can report acceleration, and does better backgrounding since it's what Nike+ uses. There are two ways to access it - a pull method where you get the data directly, or a call-back. To be honest, I just tried it and couldn't get the call-back to work (mostly because it requires NSOperationQueue and I don't have much experience with that so I'm probably putting it on the wrong queue), but if you're ok with pulling data, which your approach seems to need, then this works:
CMMotionManager *motionManager = [[CMMotionManager alloc] init];
[motionManager startAccelerometerUpdates];
for (int i = 0; i < 10000; i++)
{
NSLog(#"Acceleration: %f", motionManager.accelerometerData.acceleration.x);
}
I just tried it on a device and the values reported in the for...loop update with the current acceleration.
Source: UIBackgroundModes and UIAccelerometer
I have a flood fill function:
-(void) fillArea :(int) fillNum x:(int) xSpot y:(int) ySpot
{
int gridValue = 1;
int gridCount = [theGrid count];
[[theGrid objectAtIndex:(xSpot+ySpot*120)] getValue:&gridValue];
if (gridValue != 0) {
return;
}
[theGrid replaceObjectAtIndex:(xSpot + ySpot*120) withObject:[NSNumber numberWithInt:fillNum]];
[self fillArea:fillNum x:(xSpot+1) y:(ySpot)];
[self fillArea:fillNum x:(xSpot+1) y:(ySpot-1)];
[self fillArea:fillNum x:(xSpot) y:(ySpot-1)];
[self fillArea:fillNum x:(xSpot-1) y:(ySpot-1)];
[self fillArea:fillNum x:(xSpot-1) y:(ySpot)];
[self fillArea:fillNum x:(xSpot-1) y:(ySpot+1)];
[self fillArea:fillNum x:(xSpot) y:(ySpot+1)];
[self fillArea:fillNum x:(xSpot+1) y:(ySpot+1)];
return;
}
theGrid is an NSMutableArray of ints (either a 0 or a 1). It is just a 1D array that simulates a 2D array by multiplying the ySpot by 120 (the width of my grid). I checked the gridCount and it is equal to 9600.
However, I get an exc_bad_access at [[theGrid objectAtIndex:(xSpot+ySpot*120)] getValue:&gridValue]. I check my xSpot and ySpot when this happens and I know that (xSpot+ySpot*120) < 9600 every time. So I know it's not that I'm trying to access an object who's index is outside my array.
Futhermore, in my tick function I ran the code:
int gVal = 1;
int gIndex = 0;
while (gIndex < [theGrid count]) {
[[theGrid objectAtIndex:gIndex] getValue:&gVal];
gIndex += 1;
}
I did not get an exc_bad_access error. Please help me figure out why I'm getting an exc_bad_access error.
EDIT:
I split [[theGrid objectAtIndex:(xSpot+ySpot*120)] getValue:&gridValue]; into:
id object = [theGrid objectAtIndex:(xSpot+ySpot*widthInGridSize)];
gridValue = [object intValue];
I still get exc_bad_access and it says it is on the line:
gridValue = [object intValue];
So I assume this means object has already been released? I don't understand how that's possible. I thought ints didn't need to be retained in any way since they're just ints. Also I thought adding an object to an array automatically retained it so why would my int get released.
In the debug section the value of object is said equal: (_NSCFNumber *) 0x005aec80 (int) 0
As per comments - this isn't actually an out of bounds issue, but rather the object you're pulling out of 'theGrid' is bogus (already released). Break that into multiple lines to confirm; and turn on "zombies" in your debug settings. Cheers!
fresh to objC and cocos2d :)
i'm following "learn cocos2d game development with iOS5", in chapter4, there is a "DoodleDrop" game.
define some variable in GameScene.h like this
#interface GameScene : CCLayer
{
CCSprite *player;
CGPoint playerVelocity;
CCArray *spiders;
CGSize screenSize;
int dropedSpidersCount;
float duration;
}
+ (CCScene *)scene;
#end
in GameScene.m the init method looks like this
- (id)init
{
if (self = [super init]) {
duration = 4.0;
[self createPlayer];
[self createSpiders]; // spiders were inited here.
[self resetSpiders];
[self schedule:#selector(chooseSpider:) interval:0.7];
}
return self;
}
while in chooseSpider, i cannot access spiders, xcode broke
in other methods, spiders or duration just behave normally, why does this happens?
gist code added
https://gist.github.com/2940466
After inspecting your code, I suggest you to try this fix:
- (void)createSpiders
{
CCSprite *tempSpider = [CCSprite spriteWithFile:#"spider.png"];
CGSize spiderSize = [tempSpider texture].contentSize;
int spiderCount = screenSize.width / spiderSize.width;
spiders = [[CCArray arrayWithCapacity:spiderCount] retain];
for (int i = 0; i < spiderCount; i++) {
CCSprite *spider = [CCSprite spriteWithFile:#"spider.png"];
[self addChild:spider];
[spiders addObject:spider];
}
}
where the only difference is in the line:
spiders = [[CCArray arrayWithCapacity:spiderCount] retain];
Indeed, if you do not retain you spiders object, it will be autoreleased at the next run loop iteration.
OLD ANSWER:
Without seeing more code it is not possible to say exactly what is happening, but it seems that in the interval between creating the spiders and the actual execution of chooseSpiders, your spiders array gets deallocated.
As a quick try, I would suggest adding:
[spiders retain];
before calling
[self schedule:#selector(chooseSpider:) interval:0.7];
and see wether the crash keeps happening.
if you provide more code, it could be possible to help you further.
What i want to do is read the acceleration.y and do something like:
if (acceleration.y > 0.8) {
// Do something
}
As didAccelerate is deprecated I wonder how to get the y-value:
motionManager = [[[CMMotionManager alloc] init] autorelease];
motionManager.accelerometerUpdateInterval = kUpdateInterval;
if (motionManager.accelerometerAvailable) {
[motionManager startAccelerometerUpdates];
}
else {
//this device doesn't have accelerometer notice somewhere??
}
- (void)startAccelerometerUpdates {
// READ Y-VALUE?????
}
I want to use raw accelerometer data so the app also works on 3GS. Is it possible to read the Y-value?
EDIT: the answer below is deprecated, check these posts for the right way.
Old answer:
Use a UIAccelerometer singleton instance for this, for example in your AppDelegate
//in your launching method
UIAccelerometer * accel = [UIAccelerometer sharedAccelerometer];
accel.delegate = self;
//delegate method:
-(void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration{
// use the y-property of the acceleration
}
Look at the CMMotionManager class reference and search section named "Handing Motion Updates at Specified Intervals". In the "Accelerometer" bullet, it says
Set the accelerometerUpdateInterval property to specify an update interval. Call the startAccelerometerUpdatesToQueue:withHandler: method, passing in a block of type CMAccelerometerHandler. Accelerometer data is passed into the block as CMAccelerometerData objects.
CMAccelerometerData has a reference to CMAcceleration which is a struct that holds per axis acceleration.