I'm programming a simple 2 players board game with GameKit and the GKTurnBased API, my problem is at the end of my game, when I want to detect that a player won and another lost.
First at all, the handleTurnEventForMatch:didBecomeActive: method fired, in this method, I check the other participant status:
// Test other players status
for(GKTurnBasedParticipant *participant in match.participants)
{
if([participant.playerID isEqualToString:[GKLocalPlayer localPlayer].playerID])
continue;
if(participant.matchOutcome == GKTurnBasedMatchOutcomeQuit || participant.matchOutcome == GKTurnBasedMatchOutcomeLost)
{
[self findMeInMatch:match].matchOutcome = GKTurnBasedMatchOutcomeWon;
[match endMatchInTurnWithMatchData:match.matchData
completionHandler:nil];
return;
}
else if(participant.matchOutcome == GKTurnBasedMatchOutcomeWon)
{
[self findMeInMatch:match].matchOutcome = GKTurnBasedMatchOutcomeLost;
[match endMatchInTurnWithMatchData:match.matchData
completionHandler:nil];
return;
}
else if(participant.matchOutcome == GKTurnBasedMatchOutcomeTied)
{
[self findMeInMatch:match].matchOutcome = GKTurnBasedMatchOutcomeTied;
[match endMatchInTurnWithMatchData:match.matchData
completionHandler:nil];
return;
}
else
{
// OK
}
}
[...]
My problem now is that the handleMatchEnded: method is never called... The weird thing is that this method is part of the same delegate protocol as handleTurnEventForMatch:didBecomeActive: which actually works.
Any idea is welcome.
Cheers.
UPDATE 03/17:
As my first player quitted is turn, and the second one end the match, I think the two players are not going to receive the event. This event is probably made for other players in game, or when players are not within the app when the others leave. Didn't find anything related in the documentation, but it seem's logical.
Cyril
Related
I've been building a little card game with a friend. We used ds_lists for building the hand, deck, play zone and discard, moving the card objects back and forth between them. One part we've struggled with was checking what zone a card is in. For example: You can see both your hand and the discard at all times but only cards in hand should be playable, i.e. move when dragged into play.
Since the objects don't know what list they're in, we've been manually updating a list of booleans inDeck, inHand, inPlay etc. then nesting the tap event logic in an if block.
Create Event:
var inDeck = false;
var inHand = false;
var inPlay = false;
var inDiscard = false
if (ds_list_find_index(Hand, card) >= 0)
{
inHand = true
}
else if (ds_list_find_index(Play, this_card) >= 0)
{
inPlay = true
}
Repeat for each zone
Tap event:
if (inHand)
{
playCard();
}
if (inPlay)
{
useCard();
}
Is this the best we can do or is there a different approach we could/should take?
Last year was my first successful year in teaching my students to create catch and avoid games in Flash with AS3. This year is getting better. Each year I come here for help at least once.
I'm trying to add shooting into the possibilities for their second game project. I can make the shot happen from the ship, gun, whatever, and make it move, and get rid of it when it is off screen, but have not figured out a clean way to have both the shot and the target go away (removeChild and array.splice) upon collision.
The code I have sort of works, but I keep getting the error,
TypeError: Error #1010: A term is undefined and has no properties. at
DropShootV02_fla::MainTimeline/checkShots()
.
Normally I know that this is because of a mismatch between objects and index numbers, but this is related to the call to a second array in removing boxes and bullets.
For simplicity I'll just include the shooting code. Similar organization creates and drops the boxes.
Any help is appreciated. BTW we are not using external script in an AS file.
var shotSpeed = 18;
var shots:Array = new Array();
import flash.events.MouseEvent;
import flash.events.Event;
stage.addEventListener(MouseEvent.CLICK, fireLaser);
function fireLaser(e:MouseEvent):void
{
if (gameOn==true)
{
var shot:Shot=new Shot();
addChild(shot);
shots.push(shot);
shot.gotoAndStop(1);
shot.x = user.x;
shot.y = user.y;
trace(shots.length);
}
}
addEventListener(Event.ENTER_FRAME, moveShot);
function moveShot(e:Event):void
{
for (var i:int=shots.length-1; i>=0; i--)
{
shots[i].y -= shotSpeed;
if (shots[i].y < -25)
{
removeChild(shots[i]);
shots.splice(i,1);
}
}
}
addEventListener(Event.ENTER_FRAME, checkShots);
function checkShots(e:Event):void
{
for (var i:int=shots.length-1; i>=0; i--)
{
for (var k:int=boxes.length-1; k>=0; k--)
{
if (shots[i].hitTestObject(boxes[k]))
{
if (boxes[i].type == "good")
{
score--;
scoreDisplay.text = "Score:" + score;
}
if (boxes[i].type == "bad")
{
score++;
}
removeChild(boxes[k]);
boxes.splice(k,1);
//if I leave this part out I get no errors,
//but but the bullet goes on to strike again
removeChild(shots[i]);
shots.splice(i,1);
}
}
}
}
Thanks kaarto:
I had tried that previously and kept getting the same error. I used that elsewhere in this game code. Turns out I needed to moderate how often the player was shooting. I changed from shooting with the mouse to using space instead and now the problem is gone. Break is definitely a good one here.
Merge move shot and checkShots in one ENTER_FRAME handler.
I have an array of Bullets and Enemies objects. I want the game to check each element in the arrays to see whether there was a collision between the two or not. If there was any collision, then damage that enemy and delete the bullet. Right now every time a bullet hits an enemy it decreases the health of ALL the enemies in the array, thus killing them all at once. How can I make it so that it only decreases the health of the enemy which I'm shooting at?
//This function handles our collision detection
func didBegin(_ contact: SKPhysicsContact) {
//print("COLLISIONS!")
// If contact is with another object
if contact.bodyA.categoryBitMask == pickupCategory && contact.bodyB.categoryBitMask == playerCategory
{
displayText(text: "Picked up Pistol")
player.setHasPistol(gotPistol: true)
pistol.removeFromParent()
}
//Bullet hits an object
if contact.bodyA.categoryBitMask == objectCategory && contact.bodyB.categoryBitMask == bulletCategory
{
bulletCleanup(killNow: true)
//print("BULLET HAS HIT THE PIPE!")
}
//Do collisions between bullets and enemies...
for bullet in bulletList
{
for enemy in enemyList
{
//if the enemy was hit have them take damage.
if contact.bodyA.categoryBitMask == enemyCategory && contact.bodyB.categoryBitMask == bulletCategory
{
//deletes the bullet after hitting an object
bulletCleanup(killNow: true)
//apply damage
enemy.setHitPoints(setPoints: enemy.getHitPoints() - pistol.getDamage())
// print("BULLET HAS HIT THE enemy!!!!")
}
}
}
}
didBegin(contact:) is called for a single collision between one specific object and another specific object. (actually between physics bodies...)
Your problem is occurring because you are iterating over every bullet and every enemy for a single collision and at each iteration you are testing if the collision was between a bullet and an enemy, which it was, so you are applying the damage to every enemy.
All you need to do is to extract the specific enemy from the SKPhysicsContact object and apply damage to that particular enemy.
Also, I suspect you are missing some contacts because for your contact tests, you are not checking if the bodyA and bodyB physics bodies are reversed i.e.
if contact.bodyA.categoryBitMask == pickupCategory && contact.bodyB.categoryBitMask == playerCategory
bodyA might be player and bodyB might be pickup, so you need to test for this also.
Here is an alternative didBegincontact() you might find helpful (It's Swift2, but that shouldn't be a big problem):
func didBeginContact(contact: SKPhysicsContact) {
let contactMask = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask
switch contactMask {
case pickupCategory | playerCategory
displayText(text: "Picked up Pistol")
player.setHasPistol(gotPistol: true)
let pistol = (contact.bodyA.categoryBitMask == pickupCategory) ? contact.bodyA.node! : contact.bodyB.node!
pistol.removeFromParent()
case objectCategory | bulletCategory
bulletCleanup(killNow: true)
//print("BULLET HAS HIT THE PIPE!")
case enemyCategory| bulletCategory
//deletes the bullet after hitting an object
bulletCleanup(killNow: true)
//apply damage
let enemy = (contact.bodyA.categoryBitMask == enemyCategory) ? contact.bodyA.node! : contact.bodyB.node!
enemy.setHitPoints(setPoints: enemy.getHitPoints() - pistol.getDamage())
default :
//Some other contact has occurred
print("Some other contact")
}
}
I'm not sure how your bulletCleanup() function works - you don't appear to be sending it a specific bullet object so I don't know how it knows which bullet to process. The specific bullet involved in the contact can be obtained in didBegincContact: as follows:
let bullet = (contact.bodyA.categoryBitMask == bulletCategory) ? contact.bodyA.node! : contact.bodyB.node!
which is a short form of saying "is nodeA's categoryBitMask a bulletCategory? If it is, then set bullet equal to nodeA, otherwise set bullet to nodeB.
Note that if your categories are more complicated (i.e. objects can belong to multiple categories), this won't work as the simple AND tests (enemyCategory| bulletCategory) won't match.
What I have done is made an array and after doing so I've pushed two objects(enemies) in to that array.
Now I'm checking if any of the enemies from that array touch my player.
private function collision():void
{
goblin1 = new Goblin;
goblin2 = new Goblin;
goblinArray.push(goblin1);
goblinArray.push(goblin2);
//get all enemies in to the loop
for (var i:int = 0; i < goblinArray.length; i++)
{
var goblin:Goblin = goblinArray[i]; //datatype goblin to the goblin class
if (goblin.hitTestObject(player)) //if anything from that array hits the player then do this
{
//make that individual goblin stop
}
else
{
//make the goblin and other goblins move
}
}
}
This should technically work, but I can't think of a solution, I would appreciate hints very much.
Thank you for taking your time.
Ps I've just remembered: what ever goblin the player hits, I want him to do what's in the if statement, not all the goblins, i want to target that goblin that's been hit, and hit only.
Sorry If I could not explain it at best.
Edit; I'm trying to accomplish a hit test where the hit test tests to see if the player is hitting the goblins in the array.
If it does hit that goblin, in that array, then only that goblin in the array will stop, whilst the other goblins in the array move.
what ever goblin the player hits, I want him to do what's in the if statement, not all the goblins, i want to target that goblin that's been hit, and hit only.
But your statement is correct, you will have that needed goblin...
If you want to stop your collision algorithm after first collision, you could use break or return;
if (goblin.hitTestObject(player)) //if anything from that array hits the player then do this
{
//Do whatever you need
//Decide what flag you need canMove *or* cantMove ;)
goblin.canMove = false;
break;
}
It depends what the goblin.cantMove and goblin.canMove setters do. If they're just Booleans, you will only need one - canMove can be set to true or false.
I assume you have a game loop somewhere else in your code. And when you update your goblins, if canMove is set to false they're prevented from moving.
If that's what you intend, then in your game loop code, I'd recommendand looping through your goblins and calling their update() method. Then, in your Goblin update() method check the goblin's canMove property and moving it only if canMove is true.
Thanks to the updates to GameKit API in iOS 6, I am finally able to implement my turn-based board game the way it should be, complete with turn timeouts and better programmatic creation of matches. However, I am running into an issue that I cannot seem to solve. My desire is to have Game Center running entirely invisible to the end-user, so that everything is programmatic and uses my own custom interfaces. Therefore, I use my own custom table view to display matches, not the default GKTurnBasedMatchmakerViewController. Right now, I have no problem displaying open matches using the -loadMatchesWithCompletionHandler: method. I also use a custom screen to create a match, with a direct creation for auto-match (not a problem) and a table view that loads Game Center friends of the localPlayer for invitation. Since the playersToInvite attribute can now be filled with playerID's, this is possible in iOS 6.
My main problem is handling the invitation on the recipient's side. I send the invitation with the following code
-(void)invitarAmigoMio:(NSArray*)losAmigos
{
GKMatchRequest *request = [[GKMatchRequest alloc] init];
request.minPlayers = 2;
request.maxPlayers = 2;
request.defaultNumberOfPlayers=2;
request.playersToInvite = losAmigos;
request.inviteMessage = #"Your Custom Invitation Message Here";
request.inviteeResponseHandler = ^(NSString *playerID, GKInviteeResponse
response)
{
if (response==GKInviteeResponseAccepted)
{
NSLog(#"INVITACION ACEPTADA");
}
else
{
NSLog(#"INVITACION RECHAZADA");
}
};
}
I have a delegate that handles the notifications. The following code is launch when the user is authenticated
-(void)registroNotificaciones
{
NSLog(#"registroNotificaciones");
[GKMatchmaker sharedMatchmaker].inviteHandler = ^(GKInvite* acceptedInvite, NSArray *playersToInvite)
{
if(acceptedInvite != nil)
{
// Get a match for the invite we obtained...
[[GKMatchmaker sharedMatchmaker] matchForInvite:acceptedInvite completionHandler:^(GKMatch *match, NSError *error)
{
if(match != nil)
{
NSLog(#"match != nil: ");
}
else if(error != nil)
{
NSLog(#"ERROR: From matchForInvite: %#", [error description]);
}
else
{
NSLog(#"ERROR: Unexpected return from matchForInvite...");
}
}];
}
};
NSLog(#"FIN registroNotificaciones");
}
Why notifications are not send?Or notifications are not received?Is there another way to send notification to invite to play a game?
I checked and my sandbox accounts of Game Center allow invitations, and I dont know what´s going on
You did pretty much all that is needed to create the invite; you just need to send it with this code:
[[GKMatchmaker sharedMatchmaker] findMatchForRequest:request withCompletionHandler:^(GKMatch* match, NSError *error) {
if (error)
{
//Invite has not been sent
}
else if (match != nil)
{
//whatever you want to do when the receiver accepts the invite
}
}];
I know you are looking to use custom displays but I haven't made my own view controllers for this, instead I used GKMatchmakerViewController which handles the 'send' invites for me and I think that's what's missing in your code.
Here is my code for that, maybe you can try it and check how GKMatchmakerViewController works internally (if possible):
GKMatchRequest *request = [[[GKMatchRequest alloc] init] autorelease];
request.minPlayers = minPlayers;
request.maxPlayers = maxPlayers;
request.playersToInvite = pendingPlayersToInvite;
GKMatchmakerViewController *mmvc = [[[GKMatchmakerViewController alloc] initWithMatchRequest:request] autorelease];
mmvc.matchmakerDelegate = self;
[myPresentingViewController presentModalViewController:mmvc animated:YES];
I have almost the same code you have that handles the notifications (registroNotificaciones) and it gets executed as soon as the user authenticates with GameCenter and is one of the first things the app does when launched too, from what you say, it looks like this code is working properly, just . Hope it helps!