Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Card Game - Newbie needs some help

Discussion in 'Editor & General Support' started by ultimate2106, Nov 20, 2017.

  1. ultimate2106

    ultimate2106

    Joined:
    Nov 18, 2017
    Posts:
    26
    Hey guys!
    So I just downloaded Unity a few days ago to make a table card game I like in unity.
    But after "creating" the playmat (looks aweful haha) I encountered a few problems as a newbie in Unity.
    Well, first things first let's have a look on the playmat:

    The red square is the pile that contains ALL cards and you draw cards from it to fill empty spaces in the middle panel so that there are always 5 cards in that panel.
    The black pile is the players deck which contains 7x Card X and 3x Card Y. Like you can guess it will be shuffled and you draw 5 cards each turn (with some cards you might draw more or less).
    Now my problems are:
    1. How do I get my cards into my game/Unity? I have about 50 different cards in the red pile with different amount of copies of each like 5x Card 1, 8x Card 2, 3x Card 3 and so on. Programatically I'd make a card map with Cardobjects of each kind of card mapped to their name. Webbased I could have databases with a table that contains all kinds of cards. But how to get them into Unity?
    2. After getting my cards into Unity.. How to make a pile out of them? My first thought would be putting them on each other like you would in RL.
    3. How do I shuffle my piles if needed?
    4. How to draw a card from a pile like my deck? First thought would be using a plugin for path animations (saw something for that).
    5. How to put them "ordered" in hand when drawn like in Hearthstone so the cards are kind of centered (saw a Tutorial for Drag and Drop where he showed something to order cards when dragging, maybe I could use that).
    Sorry for so many questions but thank you for your help in advance! :)
     
  2. FMark92

    FMark92

    Joined:
    May 18, 2017
    Posts:
    1,243
    Many possible ways. I would make a card prefab with a script that changes the texture of the card depending on suit and number variables.

    I don't think the "like in real life" approach is best here. Just make a pile-looking thing and pretend the cards are in there. Attach a script that holds an array shuffled values and a public link to the previously mentioned card prefab to it or something.

    As I said above, you could make it as easy as shuffling an array.

    If you use the system I (vaguely) described above, you just remove the card value from array and instantiate a prefab with that same value. And yes as far as animation goes, you can use anything you find.

    Something like a horizontal layout?
     
    Ryiah likes this.
  3. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194
    Have you written the game logic for the cards or looked at any tutorials on C# card games?

    As once you have your card game running logically e.g. so it can shuffle and draw cards then print out the hands in the console, it's just a matter of drawing and animating the cards doing it on the display.

    Unity has it's own animation system and there are Tweening libraries API calls that can help a lot with moving things like cards from one place to another smoothly.

    You do need to realise that the cards are just sprites and it is working out the UI/UX and turns or phases of the game that the user interacts with that will be needed. What the user can do each turn. Then you have your card game broken down into stages or steps that need to be implemented.
     
    Ryiah likes this.
  4. ultimate2106

    ultimate2106

    Joined:
    Nov 18, 2017
    Posts:
    26
    It's not enough to just change the texture. How do I get the cardeffect and values like costs to buy the card into it? Thought about a gigantic if for all cardnumbers and then do their stuff but that can't be the way to go :D Or do you mean I make a prefab for each card and change the texture from back to front or the other way?

    Yeah something like you see here:

    Thanks for pointing that out. You're right and I should do that. Bringing me to another question: Is it possible to code in C++ instead of C#?
     
  5. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,614
    Sort of. You can write C++ libraries and use them in Unity, but you need to write scripts in Unity to call into that code.

    I wouldn't recommend it anyway, as this use case wouldn't take advantage of any of the benefits C++ has over C#.

    There are literally too many ways to approach this to discuss. If you know C++, I'd say to think about how you'd go about adding information and functionality to cards outside of Unity, then apply the same approach here. Or do a bit of research into component oriented design and get some inspiration there.

    Edit: I hope that didn't sound dismissive or unhelpful. What I'm trying to get at is to not let the fact that you're using Unity and it has its own Editor and so on stop you from thinking about stuff like a programmer (I assume you know a bit of that from your asking about using C++ instead). You've got a full .NET (Mono) environment here, you've just also got a bunch of extra stuff on top of it.
     
    Last edited: Nov 22, 2017
  6. FMark92

    FMark92

    Joined:
    May 18, 2017
    Posts:
    1,243
    I already told you to attach a script to a card prefab.

    No, I said make A card perfab (1), that can change it's texture according to script values like suit and number.
     
  7. ultimate2106

    ultimate2106

    Joined:
    Nov 18, 2017
    Posts:
    26
    Ahh so you mean that I create a card prefab that's used for every card as template and create a script for each card available and add them when instantiating a card, right?

    Ahh I think you think my card texture contains editable objects like a circle where I write the cost in, etc. but my cards are images that already contains everything so I just need to change the sprite according to cardname or something I guess.
     
  8. FMark92

    FMark92

    Joined:
    May 18, 2017
    Posts:
    1,243
    No, create 1 script for all cards. We call variables variables for a reason. :)

    No.

    Yes, I'm telling you you can change sprites with script.
     
  9. ultimate2106

    ultimate2106

    Joined:
    Nov 18, 2017
    Posts:
    26
    Sry for being dull haha :D But how do I reuse a script for effects when the effects variate very hard like card A let you draw a card and card B is an attack. Shall I make a gigantic if statement to ask which card is used? Sry I am kinda confused :(

    But what do you mean then with changing by suit and values? Do you have an example? :)
     
  10. pk_Holzbaum

    pk_Holzbaum

    Joined:
    Jul 26, 2012
    Posts:
    95
    There are many ways to do this. You could create an abstract card class which contains all logic that can be applied to every card and then make derived classes for every card type (eg. Attack Card, Draw Card, Special Effect Card)
     
  11. FMark92

    FMark92

    Joined:
    May 18, 2017
    Posts:
    1,243
    Make an IEffectScrip tinterface that will be a wrapper class for all events. Then the card can have a list of all IEffectScript references and you can choose then from the list depending on card name identifier. Continuation below.

    I said "suit and value" because I assumed you're talking about normal 52 card deck. You can have it much simpler.
    Let's say you use interger as identifier for your cards.
    Then you can have a class in the card prefab script that will hold sprite names and use integers as keys.

    Code (CSharp):
    1. [System.Serializable]
    2. public class Card
    3. {
    4.     public int identifier;
    5.     public IEffectScript effect;
    6.     public Sprite sprite;
    7. }
    This way you can just fill up the list of Card-s in editor.

    And then your Reveal method would look something line this:
    Code (CSharp):
    1. public void Reveal(int identifier) {
    2.     GetComponent<SpriteRenderer>().sprite = cardList[identifier].sprite;
    3.     _effect = cardList[identifier].effect
    4. }
     
  12. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194
    OK It's hard to judge what level people are at but why not start off with a new scene and...
    • Get one card to turn over when clicked.
    • Get a deck of cards to deal out n when clicked.
    Then build up from there, card movement, player actions ect?
     
  13. ultimate2106

    ultimate2106

    Joined:
    Nov 18, 2017
    Posts:
    26
    @FMark92
    At first I wanna say: I really thank you :) You are helping a lot and I understand more and more about your idea of a concept! :D

    So I googled a bit about wrapper classes (never heard of that) but I am still not sure how you want to use that concept here. Let's say we have 10 different cards with each having an other effect. I understand the Card Class and I think you let the card call a method like effect.useEffect(identifier) which then calls the effect from the effectlist, located in the IEffectScriptInterface (correct me if I am wrong), by the identifier. Or do you have a method for each effect in the IEffectScriptInterface and a List of References to the methods (if that is even possible) and then effect.useEffect(identifier) just calls the method behind the identifier in the list?

    @Arowx
    That rly sounds like a good idea and I will try that as soon as I understood FMark92's approach of implementing the cards and their effects :)
     
  14. FMark92

    FMark92

    Joined:
    May 18, 2017
    Posts:
    1,243
    It's already monday... Sorry for the late reply.

    I was rushing when I mentioned wrapper class, sorry. IEffectScrip is an interface which CardEffect scripts then implement.
    https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/interfaces/
    Interfaces are useful in your case as you will be able to put all effect scripts into one Array of IEffectScript type.

    That said, if you want CardEffect to have any of the MonoBehaviour functions (Awake, Start, Update...), interfaces will not work. This is because MonoBehaviour is already an interface. In such cases you can use abstract classes.
    https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/abstract.


    No, IEffectScrip (AND abstract EffectScript) is just a list of communication rules (and default method implementations if you are using abstacts)

    Small example of abstracts:

    Code (CSharp):
    1. public abstract class CardEffect [B]: MonoBehaviour[/B]
    2. {
    3.     public int effectId;
    4.     public abstract void useEffect();
    5.     public abstract void Update();
    6. }
    Code (CSharp):
    1. public class CardEffectDrawTwo [B]: CardEffect[/B] {
    2.  
    3.     public override void useEffect()
    4.     {
    5.         // transfer two cards from the deck to your hand hand
    6.     }
    7.  
    8.     public override void Update()
    9.     {
    10.        Debug.Log(""+effectId);
    11.        // Just here to demonstrate you can use MonoBehaviour methods in abstract implmentations.
    12.     }
    13. }
    14.  
    And you will need to have CardEffect instances somewhere in the scene becasue unInstantiated effects can't be referenced.

    upload_2017-11-27_8-0-13.png

    I appreciate that you have placed the understanding of the concepts I have presented as a priority but don't ever stop trying new or different things. @Arowx 's idea is sound. Make a minimum viable product first and then build on it. Debugging will also be a lot easier when you have a solid ground to stand on.
     
  15. ultimate2106

    ultimate2106

    Joined:
    Nov 18, 2017
    Posts:
    26
    It's okay :D

    So I create a Script for each effect abd those implement the IEffectScript. Then I have an array that contains all this scripts and I'll call them with the card identifier. But where do I save the array of the effects then or where do I create it to fill it with the effects?

    So I just create a empty GameObject and instanciate the Effects there then I guess.

    Yeah you're right :)
    And yes I'll do what @Arowx said.

    Thanks you two!
     
  16. FMark92

    FMark92

    Joined:
    May 18, 2017
    Posts:
    1,243
    Yes. Though, since your effects may or may not contain animations (I don't know that) you might want to use abstract classes instead (automated Update() and stuff).

    Into the Card script, which is attached to a prefab - that way array contents will be saved and every card instance will contain the full list of effect object locations.

    You attach a new script and implement the IEffectScript in it, yes.

    Good luck!
     
  17. ultimate2106

    ultimate2106

    Joined:
    Nov 18, 2017
    Posts:
    26
    I think I finally got it now, thank you!

    Shall I initiate the id, effect and sprite for the card in the Start() method or better use a constructor or even a own written init method? The problem I have is that I can't/shouldn't pass values to Start() :(
    I think a good way is to have an instance of the CardPile it came from in the Card, like the players deck, so it can interact with it like using an effect on it (draw Cards, search for a Card, destroy a Card, etc.). I'd have a ICardPileInterface then that want standard methods like drawCard implemented and the card will have something like this
    Code (CSharp):
    1. public ICardPileInterface cardPile;
    I'd init this then in a constructor or init method.
     
    Last edited: Nov 27, 2017
  18. FMark92

    FMark92

    Joined:
    May 18, 2017
    Posts:
    1,243
    Init method or constructor. Definitely.
    Having a card pile abstract or interface seems like a good idea, yes.
    Just one thing about the naming convention:
    The "I" in the beginning of the class name already marks it as an interface. No need to write it twice. ;)
     
  19. ultimate2106

    ultimate2106

    Joined:
    Nov 18, 2017
    Posts:
    26
    After googling a bit I read that you shouldn't use a constructor because it would be called multiple times when used in MonoBehaviour.
    Read here: https://answers.unity.com/questions/862032/c-constructor-in-monobehaviour.html
    So I'll use init Methods :)
    I now made a SceneHandler Script that I attach to the background which will handle most actions like interacting with cardpiles or playing cards. Like this I read it should be easier for AIs to do player actions because they can just use the functions of differents scripts instead of simulate user actions if that would be even possible. The script also calls the init methods in Start(). What you think about this idea?
     
  20. FMark92

    FMark92

    Joined:
    May 18, 2017
    Posts:
    1,243
    Constructors would work if you didn't write effects as monobehaviour implementations. And with SceneHandler that's still a possibility.

    Not sure. As I imagined it, init only gets called when you have instantiated the card and know what card it is. If you have all cards instantiated and initalized at the beginning, having a cardpile is obsolete.
    But do it anyway. The only way you'll see if it works is by trying.:)
     
  21. ultimate2106

    ultimate2106

    Joined:
    Nov 18, 2017
    Posts:
    26
    I think I should read something about MonoBehaviour because I don't know anything about them..^^' That way I know if it is needed to implement it or not.

    I think you got me wrong here. I want to init the cardpiles like filling the lists of the piles with the card IDs and shuffle them :) And maybe the hand too by telling the game to draw 5 cards at the beginning after init the piles.
     
  22. FMark92

    FMark92

    Joined:
    May 18, 2017
    Posts:
    1,243
    Long story short: MonoBehaviour is an interface. You need it only if you will attach a script to a GameObject. IIRC every script can only implement one interface.

    Sounds good. Do it. :)
     
    Last edited: Nov 29, 2017
  23. ultimate2106

    ultimate2106

    Joined:
    Nov 18, 2017
    Posts:
    26
    So I can have Scripts and use them without attaching them to a gameobject, like the Effects? How to reference them then?

    Btw do I think about how to handle the cards in hand. My 1st thought was to have a vector for each hand (player and enemy/s) in the SceneHandler and my 2nd thought was to create a GameObject for each hand that contains a vector with the cards. Actually I can't find any advantage for one over the other except of seperate data and behaviour a bit.
     
  24. FMark92

    FMark92

    Joined:
    May 18, 2017
    Posts:
    1,243
    Yes. You would have to instantiate and reference them inside a monobehaviour. It becomes simple OOP at that point.

    No point in thinking about it too early. First a working prototype.
     
  25. ultimate2106

    ultimate2106

    Joined:
    Nov 18, 2017
    Posts:
    26
    I asked because you said that I have to have instances of my effects somewhere because I can't reference them uninstantiated.


    Well I wanted to work on the scripts first so I can draw a card to my hand. But okay I am now working on drawing a single card by clicking on the deck and place it on the table middle.

    Btw I saw the dude in this video using more than one interface. So it seems like a script can implement more than one interface :)
     
    FMark92 likes this.
  26. FMark92

    FMark92

    Joined:
    May 18, 2017
    Posts:
    1,243
    Yeah I was mistaken. you can only INHERIT one ABSTRACT class but IMPLEMENT multiple INTERFACES.
    I need sleep.
     
  27. ultimate2106

    ultimate2106

    Joined:
    Nov 18, 2017
    Posts:
    26
    Ahhh yeah that makes sense :)
     
  28. ultimate2106

    ultimate2106

    Joined:
    Nov 18, 2017
    Posts:
    26
    @FMark92
    So I can draw a card and instantiate it to have a blank/white Card GameObject. Now I want to change the sprite source of the image but I can't manage to load the image to use as Sprite.. it remains white :(
    This is my hierachy:


    This is the function to draw a card:
    Code (CSharp):
    1. public void DrawCard(GameObject pile){
    2. CardPileHandler CPH = pile.GetComponent<CardPileHandler> ();
    3. string cardname = CPH.GetCardByIndex (0);
    4. GameObject card = Instantiate (cardPrefab);
    5. CardScript CS = card.GetComponent<CardScript>();
    6. CS.InitCard (cardname);
    7. if (pile.name.Equals ("PlayerDeck")) {
    8. card.transform.SetParent (GameObject.Find("PlayerCardsPlayed").transform);
    9. }
    10. }
    And the code to Init the card:
    Code (CSharp):
    1. public void InitCard(string identifier){
    2.         cardname = identifier;
    3.         sprite = Resources.Load<Sprite>("/Sprites/"+cardname);
    4.         this.GetComponent<UnityEngine.UI.Image>().sprite = sprite;
    5.     }
     
  29. FMark92

    FMark92

    Joined:
    May 18, 2017
    Posts:
    1,243
    Hey. I was away for the weekend again, sorry.
    I did a quick examination of your code and I noticed that you load a sprite from
    which I wouldn't recommend as it can cause some slowdown and it runs every time, no matter if duplicate card was already instantiated.
    .

    It may have something to do with sprite metadata
    https://gamedev.stackexchange.com/q...the-source-image-for-a-ui-image-how-to-fix-it.
     
    Last edited: Dec 4, 2017
  30. ultimate2106

    ultimate2106

    Joined:
    Nov 18, 2017
    Posts:
    26
    @FMark92 Hey! Now I had a breaktime because of study^^'
    So what would you do instead of using resources?
    And my images are 2D Sprites so that's not the problem :/
     
  31. FMark92

    FMark92

    Joined:
    May 18, 2017
    Posts:
    1,243
    Oh boy I'm back to this thread :)

    You can still use resources, but I would recommend pooling, so that you only reference resources at the beginning of runtime. Anything that won't require additional calls to ccold storage is a good practice.

    But I may just be prematurely optimizing.
     
  32. ultimate2106

    ultimate2106

    Joined:
    Nov 18, 2017
    Posts:
    26
    Wb @FMark92 :)
    Well.. I still have the problem of not being able to load the images properly.. It's always white when creating the card :(
     
  33. FMark92

    FMark92

    Joined:
    May 18, 2017
    Posts:
    1,243
    Show me the code you use to load images and assign them to the cards.
     
  34. ultimate2106

    ultimate2106

    Joined:
    Nov 18, 2017
    Posts:
    26
    @FMark92
    I already did here: Post #28 :)

    Maybe I did something wrong with the prefab?

    If I do this
    Code (CSharp):
    1. Debug.Log (sprite.name);
    after Resource.Load(); I get the following Error:
     
    Last edited: Dec 16, 2017
  35. FMark92

    FMark92

    Joined:
    May 18, 2017
    Posts:
    1,243
    Check if file exists and modify path accordingly.
    Code (CSharp):
    1. if (File.Exists("Assets/Resources/" + identifier + ".extension"))
     
  36. ultimate2106

    ultimate2106

    Joined:
    Nov 18, 2017
    Posts:
    26
    If I check for existence with this code
    Code (CSharp):
    1. public void InitCard(string identifier){
    2.     cardname = identifier;
    3.     sprite = Resources.Load<Sprite>("/Sprites/"+cardname+".jpg");
    4.     if (System.IO.File.Exists ("Assets/Resources/Sprites/" + cardname + ".jpg")) {
    5.         Debug.Log ("Hello");
    6.     }
    7.     this.GetComponent<UnityEngine.UI.Image>().sprite = sprite;
    8. }
    Then the "Hello" is printed so it exists. It seems that I assign the new sprite the wrong way..
     
  37. FMark92

    FMark92

    Joined:
    May 18, 2017
    Posts:
    1,243
    Can you print the sprite before you assing it to UI Image?
    Code (CSharp):
    1.     public void InitCard(string identifier){
    2.         cardname = identifier;
    3.         sprite = Resources.Load<Sprite>("/Sprites/"+cardname+".jpg");
    4.         if (System.IO.File.Exists ("Assets/Resources/Sprites/" + cardname + ".jpg")) {
    5.             Debug.Log ("Hello");
    6.         }
    7.         Debug.Log (sprite);
    8.         this.GetComponent<UnityEngine.UI.Image>().sprite = sprite;
    9.     }
    10.  
     
  38. ultimate2106

    ultimate2106

    Joined:
    Nov 18, 2017
    Posts:
    26
    I can but it's Null :/ Do I load it the wrong way? Idk..
     
  39. FMark92

    FMark92

    Joined:
    May 18, 2017
    Posts:
    1,243
    If it's NULL then it's not loading from resources correctly, yes.
    Please tell me why you need Resources folder again?
     
  40. ultimate2106

    ultimate2106

    Joined:
    Nov 18, 2017
    Posts:
    26
    I want to load a new Spriteimage to set the image of my card.

    Found the mistake. I wrote
    Code (CSharp):
    1. sprite = Resources.Load<Sprite>("/Sprites/"+cardname);
    2.  
    But I shouldn't put a \ before Sprites here :)

    You have a better idea of init the sprite/image for the card? It's an image GameObject.
     
    Last edited: Dec 22, 2017
  41. FMark92

    FMark92

    Joined:
    May 18, 2017
    Posts:
    1,243
    Yes, but why resources?
    Why not normal publically refrenced assets?


    If your entire game takes place on the canvas, then no, It's fine like this.
     
  42. ultimate2106

    ultimate2106

    Joined:
    Nov 18, 2017
    Posts:
    26
    Now the next thing is that my Image gets scaled when created.. I don't want that :(
    I have a Layout in my Panel where I put the cards in as Childs. Maybe that's why? Can I tell the Card not to scale?

    Edit: Well I tested and it's not the layout. If I tell the Card to ignore Layouts it's still scaled down from 1 to about 0.56
     
    Last edited: Dec 22, 2017
  43. FMark92

    FMark92

    Joined:
    May 18, 2017
    Posts:
    1,243
    Just scale it after you create it, then.
    Code (CSharp):
    1. RectTransform rct = GetComponent<RectTransform>();
    2. rct.localScale = new Vector3(
    3.     rct.localScale.x / 0.56f,
    4.     rct.localScale.y / 0.56f,
    5.     rct.localScale.z / 0.56f
    6. );
     
  44. ultimate2106

    ultimate2106

    Joined:
    Nov 18, 2017
    Posts:
    26
    Thanks! That did it! :)
     
  45. ultimate2106

    ultimate2106

    Joined:
    Nov 18, 2017
    Posts:
    26
    Hey @FMark92 :)
    I am sitting here thinking about how to save the cardinformation like cardname, cardtext, cardtype, etc. and got 2 solutions:
    1. Save cardname in the CardScript and use a Dictionary for each information to map the cardname with its values. I'd have those Dicts in the SceneHandler and the card can get the needed information from it like this:
    Code (CSharp):
    1. public void ShowCardtext(){
    2.     show(SceneHandler.cardtexts[cardname]);
    3. }
    2. Creating a small Cardinformation Script to save the information about a card and have a Dictionary to map it to a cardname. The way to get the information would be the same like above. The upside would be that I'd have only one Dict for the information about the cards instead of one for each info. The downside would be the new script that I would have to fill then but in the same time it would have the positive effect capsulate the info into objects. Also the new Script would contain the Sprite which would do what you said: loading all Sprites at beginning only once.

    I also thought about saving the information in the card too when I instanciate it like
    Code (CSharp):
    1. public void InitCard(Cardinformation info){
    2.     cardinfo = info;
    3. }
    but that way I'd have information duplicates :/ Or can I have references like in C++ with &var?

    If you have another idea, a tip or anything, I'd really appreciate it :)
     
    Last edited: Dec 28, 2017