Design for Card Game - class-design
I just had a small question related to design patterns.
Consider Player objects that can hold Card objects.
Player player1;
Player player2;
Player dealer;
Players can give cards to each other. Is there a OO way to design a method that handles this?
player1.giveCard(Card, player2);
It doesn't seem right that player1 could utilize another player's methods. Any thoughts? For instance, should all players have a method called getCard?
Player::giveCard(Card card, Player player) {
player.getCard(card)
}
The beautiful thing about Object Oriented approaches is that there are infinite ways to abstract and design the same concept. The question is which you would like to choose.
It seems that in this particular instance, your concern with the abstraction that you have described is that player1 needs to use the methods of player2 to accomplish the goal. However, I'd like to change the way you think of methods.
Methods, in general, should be thought of, first, as our public interfaces with the world. Properties, on the other hand, are those private things that we keep secret and locked up inside our objects/heads/bodies/etc.
It is true, that many programming languages have private and protected methods that are intended for internal use only, but that is really just to clean up the code so our public methods are not hundreds of lines long.
For your card game, a Player can be given a card by any other Object, so I would define the public method Player.giveCard( Card ). This is the simple part, which has already been touched on by other answers.
But the question becomes, what happens inside this method?
Also, how does the Card get removed from the original hand?
We can do several things here, and this list is by no means complete:
The player can interact just with the other player.
In this situation, player1 chooses to give card_589 to player2. So, player1 calls the method player2.giveCard( card_589 ). In the real world, this would be demonstrated by player1 physically holding the card out for player2 to take. If player2 accepts the card, player1 no longer has it and must remove it from his hand. If player2 does not accept it, then player1 does not loose the card, and puts it back in his hand.
To model this, we would make one simple rule: the giveCard method returns a boolean result to indicate whether player2 takes the card.... After player1 calls player2.giveCard(), he has no say in whether player2 takes the card, because that is up to player2, in this scenario.
Our code might look like this somewhere inside player1's functions:
//begin pseudocode to give card to player identified by player2
//let self refer to player1's own methods
Player{
public giveCardToPlayer( player2, card_id ){
card = self.removeCardFromHand( card_id );
cardTaken = player2.giveCard( card );
if( cardTaken === false ){
self.addCardToHand( card );
}
}
//in the game management system, or elsewhere in player1's code, you then write
player1.giveCardToPlayer( player2, card_587 );
//or, if in another method "player1.play()", for example:
//self.giveCardToPlayer( player2, card_587 )
//
//end pseudocode
This is the simplest solution. Here, player1 does not see anything within player2's decision making as to whether card1 gets taken. Player1 chooses to remove the card from his own hand before he hands it over so the card is not in two places at once. If Player2 does not take the card, player1 puts it back in his deck, but otherwise does nothing, because the card is now with player2.
Personally, this is the simplest way to abstract the model, and my favorite.
The player can interact through some intermediary
This is my favorite scenario when we are modeling a game that has some sort of delay such as in a computer network simulation, or a chess by mail simulation. Player1 mails the card to player2, but player2 may or may not receive the card. In this game, lets assume you have a table, like a poker table, and any player can put a card down between himself and another player so that only that other player can reach the card.
For this scenario, we would create a new object called Table, and while there are many ways that we can choose to abstract the placing of the card on the table, I will choose this method as the publicly available interface for the action:
Table.placeCardForUser( card, userId, myId, securityToken ) : bool
Table.countCardsOnTableToUserFromUser( userId, myId, securityToken ) : int
Table.pickUpCardToUser( userId, myId, securityToken ) : Card[0..*]
Table.pickUpCardsToMe( myId, securityToken ) : Card[0..*]
This introduces security issues, because I am telling the Table that only userId can pick up the card, and only myId can verify and retrieve the card, so the Table object needs some way to verify that I ("the current object") have the right to access the spot on the table identified by "userId" and "myId", but there are lots of solutions to this as well.
//begin psuedocode for current example
//we are in player1's function body
card = self.removeCardFromHand( card_587 );
player2_id = self.identifyPlayerToReceive( card );
table.placeCardForUser( card, player2_id, myId, securityToken );
//end current action
//at next opportunity to act, check to see
//if card was taken
cardCount = table.countCardsOnTableToUserFromUser( userId, myId, securityToken );
if( cardCount > 1 ){
//player2 has not taken card or other cards that have accumulated
pickUpCards = self.decideToPickUpCardsToPlayer( player2_id );
if( pickUpCards === true ){
cards = table.pickUpCardToUser( player2_id, myId, securityToken );
foreach( cards as card ){
self.addToHand( card );
}
}
}
//now check to see if anyone has given me cards between last round and this round
cards = table.pickUpCardsToMe( myId, securityToken );
foreach( cards as card ){
//let us assume that player1 takes all cards given to him
self.addToHand( card );
}
Variations of this can be made. You can imagine a tunnel between player1 and player2. Player1 establishes the tunnel by recognizing that he does not currently have a way to give cards to player2 and so he creates a tunnel. He gives a copy of the tunnel to player2, holding the "other end", and player2 then keeps a copy of the tunnel as well. Like the table situation, this tunnel now is a place that can keep items that are being passed back and forth to player2, but because only player1 and player2 have links, or pointers, to the tunnel, only these two players can put items in the tunnel or take them out, so therefore, we have an intermediary that does not require as much security. We can create tunnels to link all players with all other players, and this is still a variation of the intermediary design.
The self aware card
Sometimes, we want designs that are easier to code and less like reality. What happens if the code for the Player object forgets to remove the card object from his hand? Now, because objects are generally passed by reference, player2 and player1 each have a reference to the card, and the game simulation thinks there are two copies of the same card!
In this situation, we can design the card to be self-aware, and give the card access to a player's hand.
For this abstraction, I will model the card as such:
//begin pseudocode
Card{
private owner;
//this is a private link to the object in which the card lies
//we will allow any object to be the owner of the card, as long
//as the object implements the "CardOwner" interface.
public putInto( newOwner ){
//whoever takes the card must specify a newOwner, which will
//implement the "CardHolder" interface.
success = newOwner.addCard( self );
if( success ){
self.owner.removeCard( self );
self.owner = newOwner;
}
}
}
We then can define the interface as follows:
//begin psuedocode
iCardHolder{
public removeCard( card ) : bool
public addCard( card ) : bool
}
In this scenario, we have divorced ourselves from "reality" by giving the card itself the ability to perform actions. But where this is useful is in large projects where you cannot trust the other programmers to remember the details about how the Card is properly handled.
By giving the card control over who has pointers to it, we can ensure that only one copy of the card exists at any time, no matter who is using it.
Now, player1's code might look like this:
//give card to player2
card = self.chooseCard();
player2.giveCard( card );
//put card on the floor
card = self.chooseCard();
floor.giveCard( card );
//put card on the table
card = self.chooseCard();
table.giveCard( card );
And in each of these objects, we have the freedom to change the way we receive the card and where we keep it.
//player2 - is a simple CardHolder
public function giveCard( card ){
myHand = self;
card.putInto( myHand );
}
//the dealer is a cheat and does not implement CardHolder,
//but he has three locations that can act as CardHoldes
//they are:
// dealer.sleave, dealer.hand, and dealer.pocket
public function giveCard( card ){
location = self.chooseCardOwner( [ sleeve, hand, pocket ] );
card.putInto( location );
}
//the floor has random piles that are accumulating
public function giveCard( card ){
pile = self.chooseRandomPile();
card.putInto( pile );
}
This option is bizarre, but it gives us a lot of flexibility. In the above example, the Dealer and the Floor are not even implementers of the iCardHolder interface, but they hold references to objects to that do implement that interface, so they can still take the card.
In each of these implementations using iCardHolder, which is completely different from the others, the code is incredibly simple, because we have offloaded the manipulation of the cards location and put that responsibility on the card itself, and all the card cares about is that the objects that interact with it, agree to a contract of sorts and promise to implement one removeCard method, and one addCard method. As security, the card keeps a copy of the current owner in its own memory, so that if there is a mistake by one of the CardHolders, the Card itself holds the answer to its current owner.
Long Story Short
There is no one right way to model your game. It really is all about personal preference and how you want the system to behave. That's the beautiful thing about being a programmer. As the person who is making the code-design, you get to set the rules for how the program will operate, and what is good object-interaction, and what is bad object-interaction.
Do you need to know from which Player the card came from?
Also, I would assume an object called CardGame would be the holder of all of the Player objects and would be making all of the calls to the players such that one Player object does not alter the state of another Player object, but that the CardGame object does all of that.
So, within the CardGame object you would make calls like:
player1.giveCard( Card );
player2.receiveCard( Card );
I would say that a Player has a Hand and that Hand.addCard( card ) and Hand.removeCard( card ) would be the methods to implement here.
The game would handle removing a card from one player and giving it to the next (via those Hand calls) because the game knows about all the player objects but the player objects shouldn't necessarily need to know about each other.
Related
How to make a scene remeber an int value Unity
I have a simple project. A button adds points one at a time to a counter. I then change the scene via another button and return to the original scene. The point counter has gone back to zero. How do i prevent this?
Many ways to achieve this. But for starters you can save the number in Player Prefs. "PlayerPrefs` is a class that stores Player preferences between game sessions. It can store string, float and integer values into the user’s platform registry" So forexample this is your button callback function: public void ButtonCallback() { number++; PlayerPrefs.SetInt("MyNumber", number); } To get the number next time you are in the scene, you can get the number using: int number = PlayerPrefs.GetInt("MyNumber"); You will probably need to check if number exists in the first place when you launch the scene first time using: PlayerPrefs.HasKey("MyNumber"); Read up more on this on: https://docs.unity3d.com/ScriptReference/PlayerPrefs.html
Updating the Computer Player action turn by turn in Unity
Currently i am developing the board game. There are four players, one is the actual human player and other remaining three is computer player or simply a bot. When it is the turn of human player to throw the dice. He can throw and update the score and other thing. But when it is the computer player turn i want everything to be updated such as throwing the dice updating score slowly so that player can see it. What i am doing now is normal and after a human player turn, the computer player turn is updated in millisecond. How can i do it so that it will be updated slowly and player can see it.
This is a very general question and without some code it's difficult to answer but there are two main options you can look into. 1) Convert your AI method into a coroutine and then add some "WaitForSeconds" in between. IEnumerator YourMethod(){ // Do stuff yield return new WaitForSeconds(time); // Do stuff yield return new WaitForSeconds(time); // Do stuff yield return new WaitForSeconds(time); 2) Break your Method into several methods and chain calls to one another at the end with Invoke void YourMethod(){ // Do stufff Invoke("YourMethod2", time); void YourMethod2(){ // Do stuff Invoke("YourMethod3", time);
Game Center Quickmatch: randomly matching opponents (GKTurnBasedMatch)
I want there to be a "Quickmatch" mode in my turn-based game, where the player gets automatically matched with the first player to become available. I'm using my own custom UI. My code so far looks like this: - (void)quickMatch { GKMatchRequest *request = [[GKMatchRequest alloc] init]; request.minPlayers = 2; request.maxPlayers = 2; request.playersToInvite = nil; [GKTurnBasedMatch findMatchForRequest:request withCompletionHandler:^(GKTurnBasedMatch *match, NSError *error) { NSLog(#"MATCH: %# %# %# %d",error,match,match.matchID,(int)match.status); }]; This successfully creates a match, but the 2nd participant in the match has a null ID (playerID:(null) status:Matching). I thought that if I ran this same code on another instance, using a different Game Center ID, then the two users would be matched against each other... but that does not appear to be correct. Whenever I call the GKTurnBasedMatch loadMatchesWithCompletionHandler function, I continue to retrieve the same matches, each with only 1 valid participant (the local player). This question appears to be similar to iOS Development: How do I auto match players in Game Center? which does indicate that setting request.playersToInvite = nil; should accomplish auto-matching, yet this doesn't appear to be working for me. How can I cause Game Center to automatically match these players against each other?
Let me address the issues you are seeing here. First off, it is not necessary to set the playersToInvite property to nil, as that is the default state unless players are assigned to it, however that is not causing your "issue". I put that in quotes because you actually did the code correctly, and only perceive a problem that isn't there. Let me walk you through what happens when findMatchForeRequest is completed. When the completion block is called, Game Center has created a new GKTurnBasedMatch object with two participants, the first is the local player (you), and the second is actually an empty participant with a nil playerID. The reason for this is that Game Center does not assign all participants when a match is created with random (unassigned) opponents. A random participant is assigned when the turn is sent to them. In your game, that match will not show up in the cloud for others to play in until you take your first turn. Now, calling loadMatchesWithCompletionHandler on your other device/Game Center ID will not automatically display the match UNLESS you specifically invited that player with playersToInvite (and have already taken your turn as specified above). Think about it like this: if it worked that way, every player in the world would see every auto-match in existence when they called loadMatchesWithCompletionHandler. The other Game Center ID must actually call findMatchForRequest with no playersToInvite property set in order to be matched into the empty seat available in the game your other ID created. This way the paradigm of "it's always your turn" when creating a match is preserved, but that player is now in the second slot, not the first. Simply create a game on the second ID in the exact same way you did on the first, and your game will be created with two participants, the first being from the ID that originally created it, and the second being the ID that joined the match by calling findMatchForRequest. The key here is findMatchForRequest doesn't ALWAYS create a new match if playersToInvite is nil. If there is an existing match with an open seat, it will simply match the local player into that. Happy Coding! Corbin
Adding videos to Unity3d
We are developing a game about driving awareness. The problem is we need to show videos to the user if he makes any mistakes after completing driving. For example, if he makes two mistakes we need to show two videos at the end of the game. Can you help with this. I don't have any idea.
#solus already gave you an answer, regarding "how to play a (pre-registered) video from your application". However, from what I've understood, you are asking about saving (and visualize) a kind of replay for the "wrong" actions, performed by the player. This is not an easy task, and I don't think that you can receive an exaustive answer, but only some advices. I will try to give you my own ones. First of all, you should "capture" the position of the player's car, in various time periods. As an example, you could read player's car position every 0.2 seconds, and save it into a structure (example: a List). Then, you would implement some logic to detect the "wrong" actions (crashes, speeding...They obviously depend on your game) and save a reference to the pair ["mistake", "relevant portion of the list containg car's positions for that event"]. Now, you have all what you need to recreate a replay of the action: that is, making the car "driving alone", by reading the previously saved positions (that will act as waypoints for generating the route). Obviously, you also have to deal with the camera's position and rotation: just leave it attached to the car (as the normal "in-game" action), or modify it during time to catch the more interesting angulations, as the AAA racing games do (this will make the overall task more difficult, of course).
Unity will import a video as a MovieTexture. It will be converted to the native Theora/Vorbis (Ogg) format. (Use ffmpeg2theora if import fails.) Simply apply it as you would any texture. You could use a plane or a flat cube. You should adjust its localScale to the aspect ratio of your video (movie.width/(float)movie.height). Put the attached audioclip in an AudioSource. Then call movie.Play() and audio.Play(). You could also load the video from a local file path or the web (in the correct format). var movie = new WWW(#"file://C:\videos\myvideo.ogv").movie; ... if(movie.isReadyToPlay) { renderer.material.mainTexture = movie; audio.clip = movie.audioClip; movie.Play(); audio.clip.Play(); }
Use MovieTexture, but do not forget to install QuickTime, you need it to import movie clip (.mov file for example).
Gamecenter Matchmaking - How do I distinguish between player 1 and 2?
I am beginning to write a game using gamecenter matchmaking. Right now, two players can connect to each other, but I am having trouble figuring out how to distinguish between player 1 and 2. For example, I want player 1 to be able to move player 1 and player 2 to be able to move player 2.
The implementation would be specific to your app so it would be hard to answer more specifically, but basically each person's instance of the app could have an object that is "local player" and an object that is "opponent"... Both of them get to move the "local player" in their local instance, and changes to their "player" are obviously mapped to the "opponent" at the other end.
Just do a random number exchance to decide who will be player1 and make the first move. Generate a random number and store it in a variable. Send that generated random number to opponent. Just like this, your opponent sends his own random number to you. When you receive it, compare with yours. If yours is greater than his, then you are player1 and he is player2. So you will make the first move. If yours is lesser than his, then you are player2 and he is player1. So he will make the first move. If yours and his number are equal (a very rare case) repeat the procedure. As a reminder, you need to distinguish message types. So use different message type headers. For example: 'R' for random, 'M' for moves.