How can I control UISlider Value Changed-events frequency? - iphone

I'm writing an iPhone app that is using two uisliders to control values that are sent using coreBluetooth. If I move the sliders quickly one value freezes at the receiver, presumably because the Value Changed events trigger so often that the write-commands stack up and eventually get thrown away. How can I make sure the events don't trigger too often?
Edit:
Here is a clarification of the problem; the bluetooth connection sends commands every 105ms. If the user generates a bunch of events during that time they seem to que up. I would like to throw away any values generated between the connection events and just send one every 105ms.
This is basically what I'm doing right now:
-(IBAction) sliderChanged:(UISlider *)sender{
static int8_t value = 0;
int8_t new_value = (int8_t)sender.value;
if ( new_value > value + threshold || new_value < value - threshold ) {
value = new_value;
[btDevice writeValue:value];
}
}
What I'm asking is how to implement something like
-(IBAction) sliderChanged:(UISlider *)sender{
static int8_t value = 0;
if (105msHasPassed) {
int8_t new_value = (int8_t)sender.value;
if ( new_value > value + threshold || new_value < value - threshold ) {
value = new_value;
[btDevice writeValue:value];
}
}
}

I guess that it does make sense to still triggered them... What I would do in your case, would be to check the delta between the current value and the previous value. For instance:
Current value -> 5.
Next value -> 6.
Delta 1
Just a bit of pseudo-code:
if(delta>5){
//do your stuff
}
I wouldn't probably do this but:
-(void)sliderValueChanged:(UISlider *)sender{
[self performSelector:#selector(removeAction:) withObject:sender afterDelay:0.3];
// Do your stuff
}
- (void)removeAction:(UISlider *)sender{
[sender removeTarget:self action:#selector(sliderValueChanged:) forControlEvents:UIControlEventValueChanged];
[self performSelector:#selector(addAction:) withObject:sender afterDelay:0.3];
}
- (void)addAction:(UISlider *)sender{
[mySlider addTarget:self action:#selector(sliderValueChanged:) forControlEvents:UIControlEventValueChanged];
}
I didn't tested this, but I think you get the idea.

You could get complicated and filter the input from the slider based on either some timestamp or whether the difference between the new value and the old value is greater than some threshold.
A simpler way would be to just use a different event: touchEnded. That way you are only sending a final value and not all the intermediate values.
May not be appropriate for your app but it is not entirely clear what you are needing to do.

Just an idea, but what you could do is not send in the value changed event. You could store the value of the last transmission in a variable, then you could have a timer running in the background that checks if the last value sent is different to the current slider value, if it is then send the new value. The timer could be set to fire every 105ms and it will only send value every 105ms if the new value is different to the last sent value.

Related

Determining the player who got connected in first place

In the game I'm working on I would like to give users the possibility to select between asynchronous and real-time turn-based matches. The one thing I'm missing from the latter is how do I know who's got the first turn. I'm trying to find a way to detect who's got connected first and set that player's turn automatically. Unfortunately, it seems that both players get connected at the same time since right after finding a match expectedPlayersCount yields 1 for both players and in the didChangeState event that same variable yields 0 for both of them too. So I have no way to tell who's got first to the match since it seems that it all happens simultaneously.
As a temporary fix, I'm just using their IDs. The one with the lowest ID is the one having the first turn. However, I'm trying to find a better way since with this approach player A will always get the first turn when playing against player B and that represents a small advantage in my game.
This method is what I call when starting a new game. I pass a realTime bool value to specify whether the game returned by the game center wrapper should be a GKMatch or GKTurnBasedMatch.
//Calculate the level group to find other players in the same group
NSInteger levelGroup = (self.player.levelValue / STLevelSkillInterval) * STLevelSkillInterval;
//Find a match for the local player
[self.gameCenter findMatchWithPlayerAttributes:levelGroup realTime:realTime group:0 onCompletion:^(GKTurnBasedMatch *turnMatch, GKMatch *realTimeMatch) {
[self handleTurnMatchFound:turnMatch realTimeMatch:realTimeMatch completionBlock:onCompletion];
}];
...
This method here is the responsible to handle game center response after finding match. The create and synchronize method stores a match instance in CoreData and also fills its data by fetching the corresponding info from Game Center and my backend. So if at the time of that's done the expected player count reached 0, I call the completion block immediately since the match can begin. Otherwise I just store the completion block so I can use it later when the other player connects to the match. The problem in that part is that it never reaches 0, not for any of the two players.
- (void)handleTurnMatchFound:(GKTurnBasedMatch *)turnMatch realTimeMatch:(GKMatch *)realTimeMatch completionBlock:(void(^)(STMatch *match, NSError *error))onCompletion
{
if (turnMatch != nil)
{
[self createAndSynchronizeLocalMatch:turnMatch onCompletion:^(STMatch *localMatch) {
onCompletion(localMatch, nil);
}];
}
else if (realTimeMatch != nil)
{
[self createAndSynchronizeRealTimeLocalMatch:realTimeMatch onCompletion:^(STMatch *localMatch) {
if (realTimeMatch.expectedPlayerCount == 0)
{
onCompletion(localMatch, nil);
}
else
{
self.findRealTimeMatchCompletionBlock = onCompletion;
}
}];
}
else
{
onCompletion(nil, nil);
}
}
...
This is the method that handles player's state changes. So basically here I just update the local instance of the real time match with values from the connected player and then I call the completion block stored earlier.
- (void)match:(GKMatch *)match player:(NSString *)playerID didChangeState:(GKPlayerConnectionState)state
{
if (state == GKPlayerStateConnected)
{
STMatchParticipant *placeholderParticipant = [self.realTimeMatch getMatchParticipantWithPlayerId:nil];
placeholderParticipant.playerId = playerID;
placeholderParticipant.statusValue = GKTurnBasedParticipantStatusActive;
//This will sync the information for the connected player only
[self syncAllPlayerInfoForRealTimeMatchOnCompletion:^(NSArray *players) {
self.findRealTimeMatchCompletionBlock(self.realTimeMatch, nil);
}];
}
else
{
//Notify the observers that the participant has disconnected from the match
STMatchParticipant *participant = [self.realTimeMatch getMatchParticipantWithPlayerId:playerID];
for (id<STTurnBasedMatchDelegate> observer in self.matchesObservers)
{
if ([observer respondsToSelector:#selector(realTimeMatch:participantDidDisconnect:)])
{
[observer realTimeMatch:self.realTimeMatch participantDidDisconnect:participant];
}
}
}
}
I would appreciate any comments.
Instead of trying to determine who got connected first, why don't you just have all the players pick a random number using arc4random ?
int randomNumber = arc4random%1000000;
The player with the biggest number can play first. You will have to make sure all the players send each other their random numbers so everyone can compare and decide who is first.
In the above example, the range would be upto 1 million, so the odds of two players picking the same random number is low.
If two players do pick the same random number, you could compute the hash of their playerId's, and have the player with the larger hash be first.
if ([GKLocalPlayer localPlayer].playerID.hash > otherPlayerId.hash )
The chances of a hash collision occurring are very low, since the playerId strings are short. You can still check for this collision and handle it appropriately (maybe by hashing again ;) ).

In Cocos2d is it possible to use multiple tags for a single object?

I want to be able to identify a group of objects for a CCSpriteBatchNode, but also identify a sub group of that group. To do something similar to this
CCArray *listOfGameObjects = [sceneSpriteBatchNode children];
for (GameObject *tempObject in listOfGameObjects) {
if ([tempObject tag] == kBottleTagValue) {
//make bottle yellow
if ([tempObject tag] == kBrokenBottleTagValue) {
//also make bottle smaller
}
}
}
In the example all bottles would be turned yellow, and if the bottle was also tagged as broken it would be made smaller. So the broken bottle would need to be tagged with kBottleTagValue and kBrokenBottleTagValue. Is there away to do this? cause when I try to just add two tags it fails.
Yes you can do that using bit masking.
For example define your tag like that:
enum
{
kBottleTagValue = 1 << 0;
kBrokenBottleTagValue = 1 << 1;
};
Then tag your sprite:
[yoursprite setTag:kBottleTagValue|kBrokenBottleTagValue];
To finish you can check if your sprite is a broken bottles by doing something like that:
CCArray *listOfGameObjects = [sceneSpriteBatchNode children];
for (GameObject *tempObject in listOfGameObjects)
{
if ([tempObject tag] & kBottleTagValue)
{
//make bottle yellow
if ([tempObject tag] & kBrokenBottleTagValue)
{
//also make bottle smaller
}
}
}
I hope it'll help you.
Using bitmasking is overkill and there's no need to abuse the tag property.
Speaking of properties: You can add a boolean property to your class and use if ([tempObject isClass:[BottleClass class]]) for the first logic gate.
I don't actually know Cocos2d but based on a quick reading, I guess you've descended GameObject, by whatever roundabout route, from CCNode? In that case the tag field is an integer. You can't store multiple values to it, but you could use it as a bit field. For example:
#define kTagValueBottle 0x0001
#define kTagValueBroken 0x0002
#define kTagValueAnotherAttribute 0x0004
#define kTagValueAThirdAttribute 0x0008
#define kTagValueAFourthAttribute 0x0010
/* etc */
You'd then assign the type as, for example:
object.tag = kTagValueBottle | kTagValueBroken;
So that computes the bitwise OR of kTagValueBottle and kTagValueBroken, and stores it as the new tag. You could also add a property at any time using a bitwise OR:
object.tag |= kTagValueBroken;
Or remove by using a bitwise AND with the inverse mask:
object.tag &= ~kTagValueBroken;
You'd replace your direct comparison tests with testing individual bits via a bitwise AND:
// if ([tempObject tag] == kBottleTagValue) // old test
if ([tempObject tag] & kBottleTagValue) // new test
This is the same sort of system that Apple use for properties like autoresizingFlags on UIView.
If you can handle reading example code in PHP rather than Objective-C, this seemed to be the most helpful article that I could find quickly through Google, though admittedly from slender pickings.

iPhone - core motion timestamp

I am using core motion on my app. I have the motionManager object assigned to a property on my main class. Something like
#property (nonatomic, retain) CMMotionManager *motionManager;
every time I will use core motion, I assign it using something like:
- (void) initializeCoreMotion {
CMMotionManager *myMotionManager = [[CMMotionManager alloc] init];
self.motionManager = myMotionManager;
[myMotionManager release];
}
Then, on the method that samples that data, I have this to read the timestamp of a sample.
CMDeviceMotion *motion = self.motionManager.deviceMotion;
timestamp = motion.timestamp;
if (firstTime) {
timestampReference = timestamp;
firstTime = NO;
} else {
timestamp = timestamp - timestampReference;
}
That is: the first time it samples, it stores the initial value. The subsequent times it samples, it will subtract the current value from the reference to know how many seconds passed.
the problem is this. Suppose I am sampling one time per second. I first sample for 10 seconds. So timestamp variable will go like 0, 1, 2, 3, 4, 5...10. Then I stop and do not sample for 20 seconds. When I start sampling again. The second time, timestamp will start at 31, 32, 33, 34...
I have checked the sampling method and firstTime is YES every time a first sample occurs...
any thoughts? How do I reset that?
thanks.
Seems to be fine the way you do it. But why do you create a new instance of CMMotionManager? I use:
if ([motionManager isDeviceMotionAvailable] && [motionManager isDeviceMotionActive]) {
[motionManager stopDeviceMotionUpdates];
}
and to continue:
[motionManager startDeviceMotionUpdatesToQueue:operationQueue withHandler:deviceMotionHandler];
I was curious to see if there is still something wrong in iOS with the timestamps and thus I just tried out your code or similar thing using a static. Everything works as expected. Maybe your timestampReference is overwritten somewhere else?

Building a simple guessing game, iPhone

I'm trying to build a simple guessing game. I'm having a weird problem:
-(int)setRandom {
randomNum=(int)arc4random() % 100;
return randomNum;
}
-(IBAction)submit
{
num=[self setRandom];
if([numberField.text intValue] > num)
randomNumLabel.text=#"Too high. Try again";
else if([numberField.text intValue] < num)
randomNumLabel.text=#"Too low. Try again";
else
randomNumLabel.text=#"You got it, congrats!";
}
The problem is that I get a new random number every time I press submit. I thought that the first method would create the random number, and it would be the same every time, but apparently not. How do I fix this?
Don't call setRandom every time in submit. arc4random() returns a new random number every time you call it.
Create a property to store the random number and set it only when you need to -- in init and when the game resets.
For this purpose you need a global variable.so first a fall declare
NSInteger num; in .h class then in viewDidLoad write like this
- (void)viewDidLoad {
num=[self setRandom];
//you stuff
}
then in submit
-(IBAction)submit
{
if([numberField.text intValue] > num)
randomNumLabel.text=#"Too high. Try again";
else if([numberField.text intValue] < num)
randomNumLabel.text=#"Too low. Try again";
else
{
randomNumLabel.text=#"You got it, congrats!";
num=[self setRandom];
}
}
-(int)setRandom {
randomNum=(int)arc4random() % 100;
return randomNum;
}
Here you get a random number at view load then you get new random number when your answer matches with the number.
So this will help you.

How to make a big switch control structure with variable check values?

For example, I have a huge switch control structure with a few hundred checks. They're an animation sequence, which is numbered from 0 to n.
Someone said I can't use variables with switch. What I need is something like:
NSInteger step = 0;
NSInteger i = 0;
switch (step) {
case i++:
// do stuff
break;
case i++:
// do stuff
break;
case i++:
// do stuff
break;
case i++:
// do stuff
break;
}
The point of this is, that the animation system calls a method with this big switch structure, giving it a step number. I want to be able to simply cut-copy-paste large blocks and put them in a different position inside the switch. for example, the first 50 blocks to the end.
I could do that easily with a huge if-else structure, but it would look ugly and something tells me switch is much faster.
How to?
By not doing it this way. A case label has to be a constant.
Here's one way to do this that might be better:
Define a selector for each thing you want to do e.g.
-(void) doOneThing;
-(void) doAnotherThing;
// etc
Put them in an array:
SEL anArray[] = { #selector(doOneThing), #selector(doAnotherThing) /* other selectors */, NULL };
And then you can just iterate through them.
SEL* selPtr = anArray;
while (selPtr != NULL)
{
[self performSelector: *selPtr];
selPtr++;
}