I'm really new to C#, I started with Javascript so it's a bit of a struggle to understand the behaviour of C#.
I am trying to create a simple object inside my for loop and then display the assigned value afterwards but there is a scope issue with this, and am unclear on how I can then access it outside of the for loop?
This is my code setup:
public class Player{
public int identity;
public int score;
public Player(int id){
identity = id;
score = 0;
}
}
for (int i = 0; i < max; i++){
Player player = new Player(i);
}
//here i want to access a player and print the information for said player
I don't know how i can access the newly created players outside of the loop, how is this done?
I can understand how this could be confusing coming from JavaScript, in JavaScript all your variables are hoisted to the function (or global) scope. That's not true in C#, in C# variables are generally scoped within the nearest set of {}s. But are available for use within child scopes.
So you could write your code like this:
List<Player> players = new List<Player>();
Player player = null;
for (int i = 0; i < max; i++){
player = new Player(i);
players.Add(player);
}
Console.WriteLine(players[x]);
In this example you would have access to each individual player outside of the foreach because it's declared at the higher scope. But this would be more appropriately written:
List<Player> players = new List<Player>();
for (int i = 0; i < max; i++){
Player player = new Player(i);
players.Add(player);
}
Console.WriteLine(players[x]);
It's generally considered good practice to declare your variables as close to their usage as you can.
A final optimisation, that you yourself suggested, would be to eliminate the player declaration completely (as it's unnecessary) and inline it. That leaves us with the final version:
List<Player> players = new List<Player>();
for (int i = 0; i < max; i++){
players.Add(new Player(i));
}
Console.WriteLine(players[x]);
you can try something like this
Player player = new Player();
List<Player> playerlist = new List<Player>();
for (int i = 0; i < max; i++){
player = new Player(i);
playerlist.Add(player);
}
Related
I am getting a performance warning from my IDE that the GetComponent() call is expensive, especially since I am calling MoveCards() in Update(). I understand why it's expensive and usually know how to fix with caching in Awake() but since it's prefixed with cards[i], I am unsure how to go about it.
private void MoveCards()
{
// This loop moves the cards.
for (var i = 0; i < cards.Length; i++)
{
cards[i].localPosition = Vector3.Lerp(cards[i].localPosition, cardPositions[i + cardArrayOffset],
Time.deltaTime * cardMoveSpeed);
if (!(Mathf.Abs(cards[i].localPosition.x - cardPositions[i + cardArrayOffset].x) < 0.01f)) continue;
cards[i].localPosition = cardPositions[i + cardArrayOffset];
// This disables interaction with cards that are not on top of the stack.
cards[i].gameObject.GetComponent<CanvasGroup>().interactable = cards[i].localPosition.x == 0;
}
}
What I would do is make the card GameObject have it’s own MonoBehaviour to store a reference to those components. Then, change the cards collection to store a reference to the card MonoBehaviour.
You would store cards[] as a CanvasGroup instead of a GameObject.
You would declare it like this in Awake():
GameObject[] cardObjects = new GameObject[10];
CanvasGroup[] cards = new CanvasGroup[cardObjects.Length];
for (int i = 0; i < cardObjects.Length; i++)
{
cards[i] = cardObjects[i].GetComponent<CanvasGroup>();
}
We loop through all of the card objects, and assign that component to a certain object in an array.
In your function you showed in the question, you would change it to this:
private void MoveCards()
{
for (int i = 0; i < cardObjects.Length; i++)
{
cardObjects[i].transform.localPosition = Vector3.Lerp(cardObjects[i].transform.localPosition, cardPositions[i + cardArrayOffset], Time.deltaTime * cardMoveSpeed);
if (!(Mathf.Abs(cardObjects[i].transform.localPosition.x - cardPositions[i + cardArrayOffset].x) < 0.01f)) continue;
cardObjects[i].localPosition = cardPositions[i + cardArrayOffset]
cards[i].interactable = cardObjects[i].transform.localPosition.x == 0;
}
}
We change all of the cards variables to cardObjects. On the GetComponent<>() line, we keep cards and remove the .gameObject.GetComponent<CanvasGroup>().
Important: You didn’t show us where some of the variables were created. Like cardPositions. If this is created using cards, make sure to use the cardObjects variable.
I am seeing some odd behavior, when adding objects to an array of GameObjects. Here is my sudo code:
public class GameController : MonoBehaviour {
public GameObject[] MyArray;
void Awake() {
Application.runInBackground = true;
MyArray= new GameObject[144];
}
//some code that calls myFunction
public IEnumerator myFunction(float time)
{
int counter = 0;
GameObject card= AnotherArrayOfGameObjects[0];
for (int j = 0; j < 144; j++)
{
card.name = "BLANK_" + (j+1);
Instantiate(card, cardVector, Quaternion.Euler(0, rotateAdujust, 180));
MyArray[counter] = card;
yield return new WaitForSeconds(time);
counter++;
}
}
}
This function kind of works ok. It creates my object and instantiates it. But, when it adds the object "card" to the array MyArray, it does not add it correctly. I.e, as this is in a loop, 0-143, I would expect the array to look like this:
MyArray[0] = "BLANK_1"
MyArray[1] = "BLANK_2"
....
MyArray[143] = "BLANK_144"
Instead, it looks like this:
MyArray[0] = "BLANK_144"
MyArray[1] = "BLANK_144"
....
MyArray[143] = "BLANK_144"
I can see all this, by the way, because MyArray is a public variable in my code, so I can see it in Unity's Inspector. If I debug, I can see each iteration overwrite the previous. So, in the first iteration of the loop, i get:
MyArray[0] = "BLANK_1"
Then in the second iteration of the loop, I get:
MyArray[0] = "BLANK_2"
MyArray[1] = "BLANK_2"
In the Third:
MyArray[0] = "BLANK_3"
MyArray[1] = "BLANK_3"
MyArray[2] = "BLANK_3"
Until it completes the full loop and all items in the array say "BLANK_144"
Can anyone explain why this is happening?
You are adding same card object to array on each iteration. And you are changing name of this object on each iteration. Thus you end up with array, all items of which are pointing to same card instance. And that card instance whill have latest name which you have assigned (on last iteration):
MyArray[counter] = card;
Remember - Instantiate method returns cloned object, but it does not change the object which you are cloning. So you instantiate new clone on each iteration, but you don't save it anywhere.
You should add to array card clones which you are instantiating:
GameObject card = AnotherArrayOfGameObjects[0];
for (int j = 0; j < 144; j++)
{
var cardClone = Instantiate(card, cardVector, Quaternion.Euler(0, rotateAdujust, 180));
cardClone.name = "BLANK_" + (j+1);
MyArray[j] = cardClone;
yield return new WaitForSeconds(time);
}
using UnityEngine;
using System.Collections;
public class GameBoardScript : MonoBehaviour {
public int m_size;
public GameObject m_PuzzlePiece;
void Start() {
GameObject temp;
for (int i = 0; i < m_size; i++) {
for (int j = 0; j<m_size; j++) {
temp = (GameObject)Instantiate(m_PuzzlePiece, new Vector2(i*400/m_size, j*400/m_size), Quaternion.identity);
}
}
}
}
The reason you get this warning is because you assign the variable. But you don't use the variable of temp. Do anything with the variable after it's deceleration and the warning will go away! For example:
for (int i = 0; i < m_size; i++)
{
for (int j = 0; j<m_size; j++)
{
temp = (GameObject)Instantiate(m_PuzzlePiece, new Vector2(i*400/m_size, j*400/m_size), Quaternion.identity);
temp.SetActive(false); // this is an arbitrary use of temp. don't actually do this
}
}
Joe Blow is right that you should just call instantiate without assigning it's return to a variable if you don't intend to do anything to the instantiated GameObject afterwards. However just declaring the variable local to the inner for loop will still not remove the warning if you still fail to use it.
Please Note The compiler doesn't work out that the m_size variable could be 0 and might not reach the inner for loop. The warning occurrs because the line
temp = (GameObject)Instantiate(m_PuzzlePiece, new Vector2(i*400/m_size, j*400/m_size), Quaternion.identity);
is an assignment not a use of the variable temp. You could assign it as many times as you want and you will still get the same warning. You need to either use it or not assign the returned GameObject from Instantiate to a variable at all.
My code works as expected except for when I add a playerHand, which is a list of cards, to a playerHands list, it changes all previous player hands in the list to the current playerHand.
My thought as to what is happening is that there is only one playerHand in memory which is constantly being updated with dealt cards. The items in playerHands list are simply pointing to that single playerHand.
How do I create multiple instances of playerHand so that each item in the playerHands list is unique?
Note: my initial reaction was to use Arrays instead of List, but as far as I can tell Arrays are old-school and deprecated in favor of List.
Solving my problem is my goal here but if there are any thoughts as to the direction I am taking with this program would be accepted.
using System;
using System.Collections.Generic;
namespace Blackjack
{
class Program
{
static void Main()
{
int numOfDecks = 2;
List<Cards> shoe = Deck.createDeck(numOfDecks);
Deck.shuffleDeck(shoe, numOfDecks);
Hand playerHand = new Hand();
Hands playerHands = new Hands();
//Test Hands
//Create ten hands of dealt cards
for (int i = 0 ; i < 10; i++)
{
playerHand.clearHand();
playerHands.addHand(playerHand);
for (int j = 0; j < 5; j++)
{
playerHand.addCard(shoe[j]);
shoe.RemoveAt(0); //delete card delt from shoe
}
}
//Display the cards in each of the hands
for (int i = 0; i < playerHands.hands.Count; i++)
{
Console.Write("Hand {0}: Cards: ", i);
for (int j = 0; j < playerHand.hand.Count; j++)
{
Console.Write("{0}{1}, ", playerHands.hands[i].hand [j].rank, playerHands.hands[i].hand[j].suit);
}
Console.WriteLine();
}
Console.Read();
}
}
}
class Hand
{
/////////////////////////////////////////////////////////////////////
//Hand should contain, at a minimum:
// A List<> of cards that holds the individual cards delt to a player
// Game Number: A game is continues until the player quits
// Hand Number: Sequential hand played during a game
//////////////////////////////////////////////////////////////////////
public List<Cards> hand;
public Hand()
{
hand = new List<Cards>();
}
public void addCard(Cards card)
{
this.hand.Add(card);
}
public void clearHand()
{
hand.Clear();
}
}
class Hands
{
public List<Hand> hands;
public Hands()
{
hands = new List<Hand>();
}
public void addHand(Hand hand)
{
hands.Add(hand);
}
}
The issue is really, really simple: you are not creating enough Hand instances.
You call Hand playerHand = new Hand(); just once.
This means you have only one Hand instance, which you are clearing and filling over and over in your first for loop.
You need to call new Hand() once for each hand instance (just editing the relevant part of your code):
Hands playerHands = new Hands();
//Test Hands
//Create ten hands of dealt cards
for (int i = 0 ; i < 10; i++)
{
Hand playerHand = new Hand();
playerHands.addHand(playerHand);
for (int j = 0; j < 5; j++)
{
playerHand.addCard(shoe[j]);
shoe.RemoveAt(0); //delete card delt from shoe
}
}
Have fun!
I am working on an assignment for school (Assignment Here).
I am currently trying to shuffle a deck of cards. I have used PlayingCards from Codeplex (sorry, could only have two links...) to create a collection of cards to create a deck. Please help, when I click the Shuffle button, nothing happens. You can see my progress here it's easier to see the entire code: Github
public void Shuffle()
{
PlayingCards.Deck theDeck = new PlayingCards.Deck();
random = new Random();
for (int i = 0; i < theDeck.Cards.Count; i++)
{
int second = random.Next(NUMBER_OF_CARDS);
PlayingCards.Card temp = theDeck.Cards[i];
theDeck.Cards[i] = theDeck.Cards[second];
theDeck.Cards[second] = temp;
}
}
You are creating a new instance of PlayingCards.Deck, shuffling it, and then throwing it away at the end of the shuffle.
PlayingCards.Deck theDeck = new PlayingCards.Deck();
You need to change the call to Shuffle to include PlayingCards.Deck as a parameter, so you should be doing this:
public void Shuffle(PlayingCards.Deck theDeck)
{
random = new Random();
for (int i = 0; i < theDeck.Cards.Count; i++)
{
int second = random.Next(NUMBER_OF_CARDS);
PlayingCards.Card
temp = theDeck.Cards[i];
theDeck.Cards[i] = theDeck.Cards[second];
theDeck.Cards[second] = temp;
}
}
You should also move the new Random() outside of this method as you may find that if you tried to create two shuffled decks immediately after one another that they'll have the same order because the seed Random uses is based on the system clock.
I had a look at the code you've got for adding the cards to the deck. You should use this code instead:
foreach (var face in Enum.GetValues(typeof(PlayingCards.CardSuits)).Cast<PlayingCards.CardSuits>())
{
foreach (var value in Enum.GetValues(typeof(PlayingCards.CardValues)).Cast<PlayingCards.CardValues>())
{
theDeck.Cards.Add(new PlayingCards.Card(face, value));
}
}