I'm new to unity and component-based programming. I'm not doing anything visual yet (animation, graphics, etc.) just the logic.
I'm creating a unique card game as a hobby and need some help with lists. I created a PlayerDeck class for the PlayerDeck Game Object in the Hierarchy that loads, shuffles, and deals cards from a database to four players. Here's the code:
public class PlayerDeck : MonoBehaviour
{
public int d;
public int w;
public int x;
public int y;
public int z;
public List<Card> deck;
public List<Card> container;
public List<Card> player1hand;
public List<Card> player2hand;
public List<Card> player3hand;
public List<Card> player4hand;
public List<Card> distributed;
public List<Card> discarded;
public int decksize;
void Start()
{
LoadVariables();
LoadDeck();
ShuffleDeck();
DealCards();
}
public void LoadVariables()
{
d = 0;
w = 0;
x = 0;
y = 0;
z = 0;
decksize = 64;
}
public void LoadDeck()
{
for (int i = 0; i < 64; i++)
{
d = i;
deck[d] = CardDatabase.cardList;
}
}
public void ShuffleDeck()
{
for (int i=0; i<decksize; i++)
{
container[0] = deck;
int rnd = Random.Range(i, decksize);
deck = deck[rnd];
deck[rnd] = container[0];
}
}
public void DealCards()
{
// deal to player one
for (int i = 0; i < 7; i++)
{
w = i;
player1hand[w] = deck;
distributed = player1hand[w];
deck.Remove(deck);
}
//deal to player two
for (int i=7; i < 14; i++)
{
player2hand[x] = deck;
distributed = player2hand[x];
deck.Remove(deck);
x++;
}
//deal to player three
for (int i = 14; i < 21; i++)
{
player3hand[y] = deck;
distributed = player3hand[y];
deck.Remove(deck);
y++;
}
//deal to player four
for (int i = 21; i < 28; i++)
{
player4hand[z] = deck;
distributed = player4hand[z];
deck.Remove(deck);
z++;
}
}
}
So far the logic seems legit. I debugged it and I can see each player getting the right cards from the deck.
Now, I created a Game Object called Player1 and wanted to create a class where Player1 has access to the player1hand list in the Player Deck. Basically, how can I transfer this info to Player1 in code? Here's what I did but I get blanks for Player 1:
[RequireComponent(typeof (PlayerDeck))]
public class PlayerHand : MonoBehaviour
{
PlayerDeck playerhand;
public List<Card> dealthand;
public int a;
private void Start()
{
playerhand = GetComponent<PlayerDeck>();
for (int i = 0; i < 7; i++)
{
a = 0;
dealthand[a] = playerhand.player1hand;
a++;
}
}
Any ideas? Any help will be appreciated.
Your design doesn't make much sense. Why does the deck own the cards in the hands of the players? Let the hands manage their own cards, and the deck should just tell the hands to take the cards and do whatever they do with them.
I would add a method to PlayerHand called Add which takes a Card and adds it to their hand.
Each PlayerHand should be on a different gameobject. Also, your example code makes it seem like every player draws from the same deck and discards to the same pile, so there is no need for requiring a PlayerDeck on the same component as PlayerHand
In case the hand needs a reference to the deck at some other point, also add a method called RegisterDeck to give the deck reference to the PlayerHand.
public class PlayerHand : MonoBehaviour
{
[SerializeField] List<Card> dealtHand;
[SerializeField] PlayerDeck myDeck;
private void Awake()
{
dealtHand = new List<Card>();
}
public void Add(Card addedCard)
{
dealtHand.Add(addedCard);
}
public void RegisterDeck(PlayerDeck myDeck)
{
this.myDeck = myDeck;
}
}
Then in PlayerDeck, replace your playerXhand fields with a single List<PlayerHand> playerHands field, and then deal to those players using AddToHand. I'm not sure what you were trying to do by assigning a List as an item in a List like player1hand[w] = deck; but just remove cards from the top of the list.:
public class PlayerDeck : MonoBehaviour
{
// ...
[SerializeField] List<PlayerHand> playerHands;
// ...
public void DealCards()
{
// deal 7 to each player
foreach (PlayerHand hand in playerHands)
{
hand.RegisterDeck(this);
for (int i = 0; i < 7; i++)
{
Debug.Assert(deck.Count > 0, "Ran out of cards while dealing");
Card drawnCard = deck[0];
deck.RemoveAt(0);
hand.Add(drawnCard);
}
}
}
}
Then, in the editor, I would specify the length of Player Hands to be 4, and drag the gameObjects that each PlayerHand is attached to into that list so that PlayerDeck can get to them. Each PlayerHand should definitely be in a different gameObject, by the way.
Related
I am learning C# with Unity and I am trying to create terrain generator. I made a class called Terrain which contains an array of class called Chunk. Chunk should be an array of square GameObjects.
The code looks like this:
public class Terrain : MonoBehaviour {
public Chunk[] terrain;
// Use this for initialization
void Start () {
terrain[0] = new Chunk(0, 0);
}
}
and class Chunk looks like this:
public class Chunk : MonoBehaviour {
public int size;
public GameObject tile;
private GameObject[] chunk;
private int xCoord, yCoord;
public void Create(int chunkX, int chunkY){
for(int y = 0; y < size; y++) {
for(int x = 0; x < size; x++) {
int xCoord = x + chunkX*size;
int yCoord = y + chunkY*size;
chunk[x + y*size] = GameObject.Instantiate(tile, new Vector3(xCoord, yCoord), Quaternion.identity);
x = chunkX;
y = chunkY;
}
}
}
//Constructor
public Chunk(int chunkX, int chunkY) {
xCoord = chunkX;
yCoord = chunkY;
}
}
I get 1 error and 1 warning:
You are trying to create a MonoBehaviour using the 'new' keyword. This is not allowed. MonoBehaviours can only be added using AddComponent().
IndexOutOfRangeException: Array index is out of range.
How can I fix this and can you explain in newbie's terms why I can't use new for creating a new chunk. Also, why is array index out of range? Last question, is this structuring any good and how would you improve it or implement it differently?
Simply remove : MonoBehaviour from your Chunk class as you do not any behaviour for that. This is just a holder class for your data, and it does not follow (does not need to either) start-update routine that MonoBehaviour extending classes do.
I am working on a card game made of 32 cards. The cards values are 10(highest),9,8,7,king,queen, jack(lowest). Its a card game of four players. there must be five classes including players and tournament. The first class I made was card class:
public class Cards
{
private string face;
private string suit;
public Cards(string cardFace, string cardSuit)
{
face = cardFace;
suit = cardSuit;
}
public override string ToString()
{
return face + " van " + suit;
}
}
Then I have defined those cards inside another class which I called it Deck, but what so ever there are no values attached to the cards, inside the deck class there is also the shuffling activity, which should happen after every round
public class Deck
{
private Cards[] deck;
private int currentCard;
private const int NUMBER_OF_CARDS = 32;
private Random ranNum;
public Deck()
{
string[] faces = { "Jack", "Queen", "King", "Ace", "seven", "eight", "Nine", "Ten" };
string[] suits = { "Diamonds", "Hearts", "Spades", "Clubs" };
deck = new Cards[NUMBER_OF_CARDS];
currentCard = 0;
ranNum = new Random();
for (int count = 0; count < deck.Length; count++)
deck[count] = new Cards(faces[count % 8], suits[count / 8]);
}
public void shuffel()
{
int currentCard = 0;
for (int first = 0; first < deck.Length; first++)
{
int second = ranNum.Next(NUMBER_OF_CARDS);
Cards temp = deck[first];
deck[first] = deck[second];
deck[second] = temp;
}
}
public Cards DealCards()
{
if (currentCard < deck.Length)
return deck[currentCard++];
else
return null;
}
}
and here is the program class:
public class Program
{
public static void Main(string[] args)
{
Deck deck1 = new Deck();
deck1.shuffel();
for (int i = 0; i < 16; i++)
{
Console.WriteLine(deck1.DealCards());
if ((i + 1) % 4 == 0)
Console.WriteLine();
}
Console.ReadLine();
}
}
I am having troubles how to make it as user input the players name which are 4, the dealt cards should go as each player will have four cards , I could make with this code four hands of cards but not assigned to a player's name, as well there should be a user input for which card the user wants to play. Any valuable help would highly be appreciated.
What I have are 4 platforms that I instantiate at 4 locations. What I want is for the platforms to be shuffled every time. My code so far:
using UnityEngine;
public class PlatformCreator : MonoBehaviour {
public GameObject[] platforms;
public Transform[] points;
private void Start()
{
for (int i = 0; i < points.Length; i++)
{
Instantiate(platforms[i], points[i].position, Quaternion.identity);
}
}
}
For example the platforms now always spawn in the same order - pink, yellow, blue, purple
I want them to be spawned in different order every time, for example - yellow, blue, purple, pink. I've tried creating an int index with random.range, but I am messing something up
You could add the points to a List instead of an array, which will help you to "shuffle" the values. Taking the shuffle function from This SO post, you could do something like this:
public class PlatformCreator : MonoBehaviour {
public GameObject[] platforms;
public List<Transform> points;
private Random rng;
public void Shuffle<T>(this IList<T> list)
{
int n = list.Count;
while (n > 1) {
n--;
int k = rng.Next(n + 1);
T value = list[k];
list[k] = list[n];
list[n] = value;
}
}
private void Start()
{
rng = new Random();
points.Shuffle();
for (int i = 0; i < points.Count; i++)
{
Instantiate(platforms[i], points[i].position, Quaternion.identity);
}
}
}
I'm trying to make a game in which I use a statemachine.
The "GameState" creates the world (on entering) by calling getInstance() from the "World" class which is a singleton.
The World singleton has a 2D List with "Tile" objects (the world is made out of tiles). In the constructor of the World class the list is filled by a nested for loop which uses the SimpleTileFactory class to create tiles and puts them in the list.
Problem is that I get a StackOverflow. I debugged the code and found that the constructor of the World singleton is called more than once, which is probably causing the stack overflow. I can't find out why it's called more than once though and have the feeling I'm overlooking something simple. Is my singleton correct?
The Code is below.
Thanks in advance.
GameState Class
public partial class GameState : UserControl, IState<MainView>
{
private bool ready = false;
public GameState()
{
InitializeComponent();
}
public void enter(MainView owner)
{
this.Width = owner.Width;
this.Height = owner.Height;
this.Location = new Point(0, 0);
owner.Controls.Add(this);
World.getInstance();
this.ready = true;
}
public void update(MainView owner)
{
this.Refresh();
}
public void exit(MainView owner)
{
owner.Controls.Remove(this);
}
public bool isReady()
{
return this.ready;
}
private void GameState_Paint(object sender, PaintEventArgs e)
{
//for (int i = 0; i < World.getInstance().tiles.Count; i++)
//{
// for (int j = 0; j < World.getInstance().tiles[0].Count; j++)
// {
// //e.Graphics.DrawImage(new Image(), new Rectangle());
// }
//}
}
}
World Class
class World
{
private static World instance;
public GameResources gameResources;
public SimpleTileFactory tileFactory;
public List<List<Tile>> tiles = new List<List<Tile>>();
public Size worldSize = new Size(100, 100);
private World()
{
this.gameResources = new GameResources();
this.tileFactory = new SimpleTileFactory();
for (int i = 0; i < worldSize.Height; i++)
{
List<Tile> row = new List<Tile>();
for (int j = 0; j < worldSize.Width; j++)
{
row.Add(tileFactory.createTile(new Point(j, i)));
}
}
}
public static World getInstance()
{
if (instance == null)
{
instance = new World();
}
return instance;
}
}
SimpleTileFactory Class
class SimpleTileFactory
{
public SimpleResourceFactory resourceFactory = new SimpleResourceFactory();
private Random random = new Random();
private int maxNumOfResourcesPerTile = 5;
public SimpleTileFactory()
{
}
public Tile createTile(Point location)
{
//create tile
Tile tile = new Tile();
tile.location = location;
//give tile terrain type
Terrain terrain = new Terrain();
terrain.type = random.Next(0, World.getInstance().gameResources.terrainNames.Count);
terrain.name = World.getInstance().gameResources.terrainNames[terrain.type];
terrain.accessability = 1000;
//add resources to terrain
int numberOfResources = random.Next(0, maxNumOfResourcesPerTile + 1);
for (int i = 0; i < numberOfResources; i++)
{
terrain.resources.Add(resourceFactory.createTerrainResource(terrain.type));
}
tile.terrain = terrain;
return tile;
}
}
SimpleResourceFactory Class
class SimpleResourceFactory
{
public List<string> resourceNames = new List<string>();
public List<Image> resourceImages = new List<Image>();
private Random random = new Random();
public SimpleResourceFactory()
{
resourceNames.Add("asdfasfd");
resourceNames.Add("asdfasfd");
resourceNames.Add("asdfasfd");
}
public TerrainResource createTerrainResource(int terrainType)
{
TerrainResource resource = new TerrainResource();
resource.type = random.Next(0, resourceNames.Count);
resource.name = this.resourceNames[resource.type];
resource.amount = 100;
return resource;
}
}
Tile Class
class Tile
{
public Point location;
public Terrain terrain;
public Tile()
{
}
}
Terrain Class
class Terrain
{
public int type;
public string name;
public int accessability;
public List<TerrainResource> resources = new List<TerrainResource>();
public Terrain()
{
}
}
TerrainResource Class
class TerrainResource
{
public int type;
public string name;
public int amount;
public TerrainResource()
{
}
}
createTile is calling getInstance, and causing the overflow:
WpfApplication2.exe!WpfApplication2.SimpleResourceFactory.SimpleResourceFactory() Line 116 C#
WpfApplication2.exe!WpfApplication2.SimpleTileFactory.SimpleTileFactory() Line 76 C#
WpfApplication2.exe!WpfApplication2.World.World() Line 49 C#
WpfApplication2.exe!WpfApplication2.World.getInstance() Line 66 C#
WpfApplication2.exe!WpfApplication2.SimpleTileFactory.createTile(System.Windows.Point location) Line 95 C#
WpfApplication2.exe!WpfApplication2.World.World() Line 56 C#
WpfApplication2.exe!WpfApplication2.World.getInstance() Line 66 C#
WpfApplication2.exe!WpfApplication2.SimpleTileFactory.createTile(System.Windows.Point location) Line 95 C#
WpfApplication2.exe!WpfApplication2.World.World() Line 56 C#
WpfApplication2.exe!WpfApplication2.World.getInstance() Line 66 C#
WpfApplication2.exe!WpfApplication2.SimpleTileFactory.createTile(System.Windows.Point location) Line 95 C#
The problem was that createTile() was called within the World constructor. This way the World object isn't fully created yet when createTile() is executed which will then call getInstance() which in it's turn creates a new World (because it wasn't fully created yet) which calls createTile() again. It's an infinite loop.
I did a quick fix to check it by changing the World constructor and createTile() code as can be seen below. This is not the most beautiful way to solve it, so I'll probably just seperate GameResources from World.
private World()
{
this.gameResources = new GameResources();
this.tileFactory = new SimpleTileFactory();
for (int i = 0; i < this.worldSize.Height; i++)
{
List<Tile> row = new List<Tile>();
for (int j = 0; j < this.worldSize.Width; j++)
{
row.Add(tileFactory.createTile(new Point(j, i), this));
}
this.tiles.Add(row);
}
}
And the createTile method:
public Tile createTile(Point location, World world)
{
//create tile
Tile tile = new Tile();
tile.location = location;
//give tile terrain type
Terrain terrain = new Terrain();
terrain.type = random.Next(0, world.gameResources.terrainNames.Count);
terrain.name = world.gameResources.terrainNames[terrain.type];
terrain.accessability = 1000;
//add resources to terrain
int numberOfResources = random.Next(0, maxNumOfResourcesPerTile + 1);
for (int i = 0; i < numberOfResources; i++)
{
terrain.resources.Add(resourceFactory.createTerrainResource(terrain.type));
}
tile.terrain = terrain;
return tile;
}
I'm a bit of a programming newbie and I'm making a Black Jack game, but I'm really struggling to add a card from my shuffled deck, into the player's hand, when I called the Hit function I get this message:
Object reference not set to an instance of an object.
I know the solution is probably really obvious but could you please help?
The problem is right at the bottom of the Deck Class, in the Hit() Function
/* Main Class */
using System;
using System.Collections.Generic;
using System.Text;
namespace BlackJackGameX
{
public class MainClass
{
public static void Main (string[] args)
{
Deck cards = new Deck();
Hand playerHand = new Hand(cards);
Console.WriteLine("Welcome to Black Jack\n\nPress Enter To Start");
Console.ReadLine ();
cards.Hit();
playerHand.PrintHand();
}
}
}
/* Card Class */
using System;
using System.Collections.Generic;
using System.Text;
namespace BlackJackGameX
{
public enum Suit {Spades, Hearts, Clubs, Diamonds}
public enum FaceValue {Ace, Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten, Jack, Queen, King}
public class Card
{
public Suit CardSuit;
public FaceValue CardValue;
public int iValue;
public Card (Suit cardSuit, FaceValue cardValue, int ivalue)
{
CardSuit = cardSuit;
CardValue = cardValue;
iValue = ivalue;
}
public Card ()
{
}
}
}
/* Hand Class */
using System.Collections.Generic;
using System.Text;
namespace BlackJackGameX
{
public class Hand
{
public List<Card> PlayerHand;
private Deck cardDeck = new Deck();
public Hand (Deck Cards)
{
cardDeck = Cards;
}
public void PrintHand()
{
for (int i = 0; i < PlayerHand.Count; ++i)
{
Console.WriteLine("You have a " + PlayerHand[i].CardValue + " of " + PlayerHand[i].CardSuit);
if (i < PlayerHand.Count)
{
Console.WriteLine ("&");
}
}
Console.ReadLine();
}
}
}
/* Deck Class */
using System;
using System.Collections.Generic;
using System.Text;
namespace BlackJackGameX
{
public class Deck
{
Hand playerHand;
Random rNumber = new Random();
List<Card> Cards;
public int iValue1 = 11;
public int iValue2 = 2;
public int iValue3 = 3;
public int iValue4 = 4;
public int iValue5 = 5;
public int iValue6 = 6;
public int iValue7 = 7;
public int iValue8 = 8;
public int iValue9 = 9;
public int iValue10 = 10;
public int iValue11 = 10;
public int iValue12 = 10;
public int iValue13 = 10;
public Deck()
{
Cards = NewDeck();
}
private List<Card> NewDeck()
{
var AllSuits = new Suit[]
{
Suit.Spades,
Suit.Hearts,
Suit.Clubs,
Suit.Diamonds
};
var AllFaces = new FaceValue[]
{
FaceValue.Ace,
FaceValue.Two,
FaceValue.Three,
FaceValue.Four,
FaceValue.Five,
FaceValue.Six,
FaceValue.Seven,
FaceValue.Eight,
FaceValue.Nine,
FaceValue.Ten,
FaceValue.Jack,
FaceValue.Queen,
FaceValue.King
};
var AllValues = new int[]
{
iValue1,
iValue2,
iValue3,
iValue4,
iValue5,
iValue6,
iValue7,
iValue8,
iValue9,
iValue10,
iValue11,
iValue12,
iValue13
};
List<Card> myDeck = new List<Card>();
for (int i = 0; i <= 3; i++)
{
for (int j = 0; j <= 12; j++)
{
Card newCard = new Card(AllSuits[i], AllFaces[j], AllValues[j]);
myDeck.Add(newCard);
}
}
List<Card> shuffledDeck = new List<Card> ();
while (myDeck.Count > 0)
{
int c = myDeck.Count;
int n = rNumber.Next (0, c);
// var will translate into an int which will be used to pick the card needed from the list
var value = myDeck[n];
shuffledDeck.Add(value);
myDeck.Remove(value);
}
return shuffledDeck;
}
public void Print()
{
for (int i = 0; i <= 51; i++)
{
Console.WriteLine("You drew a " + (Cards [i].CardValue) + " of " + (Cards [i].CardSuit));
Console.ReadLine ();
}
}
public void Hit()
{
playerHand.PlayerHand.Add(Cards[0]);
Cards.Remove(Cards[0]);
/*var tmp = Cards[0];
Cards.Remove(tmp);
return tmp;*/
}
}
}
public List<Card> PlayerHand; in your Hand class is never assigned to and is always null.
In Hit, you call playerHand.PlayerHand.Add - playerHand.PlayerHand is null, so calling Add results in a NullReferenceException.
Debugging through the function where the exception is thrown should show you such issues clearly - by watching the variables used you can see which ones have the expected values and which ones do not.
It looks like Deck.playerHand is never initialized. Same for Hand.PlayerHand.
You're never setting the playerHand inside Deck, so when you call hit, playerHand is null.