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

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.

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 ;) ).

How can I control UISlider Value Changed-events frequency?

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.

How can/would I implement a morse code generator?

I was wondering what would be the best way to implement a morse code generator. The way it would work is the user types in a word or phrase and then that NSString would be passed into my method as an argument for processing. The way I want to process is to loop through each character in the string and then play the correct sequence of tones as I go. I've already implemented the mechanism in a separate class.
I've already tried so many different methods but they all seem to introduce some sort of problem. Things i've tried so far are: creating an NS Timer, using delaying method calls (performSelector:withObject:afterDelay).
Below is an example of the method i'm using to delay, but the problem is that if the user types in say "aaaa" only the first "a" will play because the entire loop will have been completed before the morse sequence is even done generating. It would be more ideal if the loop could wait for the switch case to finish before continuing.
Anyways this is the way that i thought it should be implemented but it doesn't seem to be working, so maybe there is a better way of implementing the morse code generator or i'm just missing a step so if anyone can please help me figure this out i'd really appreciate it.
- (NSString *)convertTextToMorse:(NSString *)phrase {
for (int i = 0; i < [phrase length]; i++) {
unichar ch;
ch = [phrase characterAtIndex:i];
NSLog(#"Processing charachter %c",ch);
//Dot = 100ms
//Dash = 300ms
//Pause = 200ms
switch (ch) {
case 'a':
//Morse "a"
[self playDotSound];
[self performSelector:#selector(playDashSound) withObject:nil afterDelay:0.2];
break;
case 'b':
//Morse "b"
break;
default:
break;
}
}
return phrase;
}
Another option might be to run the entire thing on a separate thread:
- (NSString *) convertTextToMorse:(NSString *)phrase {
[self performSelectorInBackground:#selector(morseInternal:) withObject:phrase];
return phrase;
}
- (NSString *) morseInternal:(NSString *)phrase {
NSAutoreleasePool *pool = [NSAutoreleasePool new];
for (int i = 0; i < [phrase length]; i++) {
if (stopMorse) {
break;
}
unichar ch = [phrase characterAtIndex:i];
NSLog(#"Processing charachter %c",ch);
switch (ch) {
case 'a':
[self playDotSound];
[self pause];
[self playDashSound];
break;
case 'b':
//Morse "b"
break;
default:
break;
}
}
[pool release];
}
- (void) pause {
[NSThread sleepForTimeInterval:0.2];
}
Notice I added a stopMorse boolean instance variable that you can set to true if you want to stop the morse code generation in the middle of the string.
As you figure out what sounds to play, add them to an array.
Play through the array and save your position in the array as you go along. If you get stuck parsing, pause, and continue right where you left off.
Alternatively, figure out how long each letter takes and use a variable for the playback delay.
NSTimers and run loop delays produce very poor Morse Code, and get worse at higher WPM. It's better to create a DSP audio synthesis engine good enough for millisecond precise drum loops (probably requiring use of the RemoteIO Audio Unit), and using that to mix down timed beeps instead of percussion instrument samples.

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++;
}

hot do i make a comparison of integers and values of text box in Objective C

i'm new to iphone programming and i encountered/noticed some problems while i was coding. which was after i typed a statement like
if (label.text > label2.text) {
do something...}
however, after typing my application can be compiled and run however when i try to validate it by comparing the values, my specified actions can run and i can change my image view image, however the conditions is not true but the specified actions can be run. Do enlighten me thanks! i will post my codes at the bottom, do comment if you spot any better practices? thanks once again.
Oh and how can i specify to check in my label that the default value is not "Label" or empty because i would like the values to be populated with number before commencing.
-(IBAction) beginMatch {
if (resultP1.text, resultP2.text = #"Label") {
errorMsg.text = #"Please Press Roll (:";
}
else
if (resultP1.text > resultP2.text) {
MG = [MainGameController alloc];
MG.player1 = playerName.text;
}
else {
MG.player1 = playerNameP2.text;
}
[self.view addSubview:MG.view];
}
this is one example that it does not work i have another one which is below.
-(IBAction) btn:(id) sender {
ptc = [playerTurnController alloc];
if (ptc.player1name = MGplayerName.text) {
if (lblDiceResultP1.text > lblDiceResultP2.text) {
img.image = [UIImage imageNamed:#"yellow.png"];
}
else if (ptc.player2name = MGplayerName.text) {
img.image = [UIImage imageNamed:#"Blue.png"];
}
}
}
Thank you.
Your code contains quite a few errors. You're trying to compare NSString values with ">", you're using the comma operator and = operator incorrectly, and you're allocating new objects in (what look to be) the wrong places.
You really should work your way through the introductory documentation on Apple's developer website first:
Learning Objective-C: A Primer
and
Your First iPhone Application
In here you're comparing string (alphabetically) addresses:
lblDiceResultP1.text > lblDiceResultP2.text
You probably want to extract NSNumbers of out the strings and compare the numeric values.
This here is an assignment and not a comparison:
ptc.player2name = MGplayerName.text
You probably meant to use == which is also wrong.
NSStrings are compared with the isEqualToString e.g.
NSString * s1 = #"String One";
NSString * s2 = #"String Two";
if([s1 isEqualToString:s2])
// do something when strings are equal