C# Singleton stackoverflow - c#

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;
}

Related

How to transfer information between objects in Unity C#?

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.

How to rotate a game object through an array of Quaternions?

Im trying to rotate a gameobject through an array of Quaternions that I've read in via a CSV file. Its currently not rotating the objectas i believe i'm not updating the transform.rotation correctly. Any help resolving this would be greatly appreciated, let me know if you need anymore information.
RotationAnimator.cs The Rotator Script:
public class RotationAnimator : MonoBehaviour
{
public Transform playbackTarget;
[HideInInspector]
public bool playingBack = false;
public CSVReader csvReader;
public void PlayRecording()
{
// -1 as it reads the last blank line of the CSV for reasons
for (int row = 0; row < csvReader.quaternionRecords.Length-1; row++)
{
playbackTarget.rotation = new Quaternion(csvReader.quaternionRecords[row].x, csvReader.quaternionRecords[row].y, csvReader.quaternionRecords[row].z, csvReader.quaternionRecords[row].w);
}
}
}
CSVReader.cs The script reading the csv file
public class CSVReader : MonoBehaviour
{
public QuaternionRecord[] quaternionRecords;
public List<List<String>> fileData;
ILogging logger;
string path = "Assets\\Logs\\RotationList.csv";
private void OnEnable()
{
logger = Logging.GetCurrentClassLogger();
}
public void ReadFile()
{
var r = File.OpenText(path);
fileData = r.ReadToEnd().Split('\n').Select(s => s.Split(',').ToList()).ToList();
quaternionRecords = new QuaternionRecord[fileData.Count];
// skip last empty line at "file data.count -1"
for(int row = 0; row < fileData.Count-1; row++)
{
try
{
quaternionRecords[row] = new QuaternionRecord();
quaternionRecords[row].time = float.Parse(fileData[row][0]);
quaternionRecords[row].x = float.Parse(fileData[row][1]);
quaternionRecords[row].y = float.Parse(fileData[row][2]);
quaternionRecords[row].z = float.Parse(fileData[row][3]);
quaternionRecords[row].w = float.Parse(fileData[row][4]);
}
catch(Exception e)
{
Debug.Log("Ouch!");
}
}
r.Close();
}
public struct QuaternionRecord
{
public float time;
public float x;
public float y;
public float z;
public float w;
}
}
Where PlayRecording is called:
public void Playback()
{
Debug.Log("Beginning Playback...");
csvReader.ReadFile();
rotationAnimator.PlayRecording();
}
Here is a way to itterate the quaternionRecords using Coroutines by modifying your existing code.
//Change void to IEnumerator
public IEnumerator PlayRecording()
{
for (int row = 0; row < csvReader.quaternionRecords.Length; row++)
{
playbackTarget.rotation = new Quaternion(csvReader.quaternionRecords[row].x, csvReader.quaternionRecords[row].y, csvReader.quaternionRecords[row].z, csvReader.quaternionRecords[row].w);
//Add this line to wait 1 second until next itteration
yield return new WaitForSeconds(1f);
}
}
public void Playback()
{
Debug.Log("Beginning Playback...");
csvReader.ReadFile();
//Change to StartCourotine
StartCoroutine(rotationAnimator.PlayRecording());
}
I haven't tested the code so it might not work and it might not be the best solution but it is a a way to do it. Other ways is using Update with Time.deltaTime

How can I access member of main form

I am only learning C# and I am trying to make a 2D game. I am at the stage where I have my Form1 set up with a 'PictureBox' for the player and the start of a player class:
class Player
{
private string _name;
private int _health;
internal Player(string name, int health = 100)
{
_name = name;
_health = health;
}
int X = 0;
internal void Draw()
{
updateInput();
Draw();
}
internal void updateInput()
{
if(Keyboard.IsKeyDown(Key.Right))
X = 1;
else if (Keyboard.IsKeyDown(Key.Left))
X = -1;
else
X = 0;
}
}
There is a PictureBox "pb_play" which contains the character's sprite on the main form. I tried setting its access modifier to public but that did not help. I want to change the position of the character by whatever the X value becomes. So I was trying to essentially access that member of the form the class.
I was attempting to do this inside the draw method, so it would update the input, then after that it would set the position, and then repeat the Draw method, looping constantly.
If there is a better way though, feel free to educate me. How can I fix this?
EDIT: Okay, I moved the methods into the UI, as mentioned by a comment. Here is what I have, but the sprite refuses to move:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
Draw();
}
int X = 0;
internal void Draw()
{
updateInput();
pb_play.Location = new Point((pb_play.Location.X + X), 0);
Draw();
}
internal void updateInput()
{
if (Keyboard.IsKeyDown(Key.Right))
X = 5;
else if (Keyboard.IsKeyDown(Key.Left))
X = -5;
else
X = 0;
}
}
I used a timer to fix the issue:
internal void Draw()
{
pb_play.Location = new Point((pb_play.Location.X + X), (pb_play.Location.Y + Y));
}
internal void updateInput()
{
if (Keyboard.IsKeyDown(Key.Right))
X = 1;
else if (Keyboard.IsKeyDown(Key.Left))
X = -1;
else
X = 0;
if (Keyboard.IsKeyDown(Key.Up))
Y = -1;
else if (Keyboard.IsKeyDown(Key.Down))
Y = 1;
else
Y = 0;
}
private void timer1_Tick(object sender, EventArgs e)
{
updateInput();
Draw();
}

XNA - adding items to a list and draw them

So I'm making a Space Invaders clone in XNA. I created the invaders array and added their movement logic. I want to make them shoot bullets. So I've been following a tutorial about it and used only the code I need. But still no bullets are drawn on the screen. Here's my invader class, I removed what is not related to the question from it:
class botInvaders
{
public botInvaders(Texture2D newBulletTex)
{
bulletsList = new List<blasterLasers>();
bulletTex = newBulletTex;
botInvadersHealth = 5;
currentDificultyLevel = 1;
bulletDelay = 40;
isVisible = true;
}
public static Texture2D botInvaderTex, bulletTex;
public static Rectangle botInvaderHitBox;
public static Vector2 botInvaderOrigin;
public int botInvaderCurrentFrame = 1, botInvaderFrameWidth = 52, botInvaderFrameHeight = 90, bulletDelay, botInvadersHealth, currentDificultyLevel, invaderRows = 3, invaderCollumns = 10; // invaderRows = 5 // For 50 invaders
public static Rectangle[,] botInvadersRect;
public bool isVisible;
public List<blasterLasers> bulletsList;
public void LoadContent(ContentManager Content)
{
botInvaderTex = Content.Load<Texture2D>(".\\gameGraphics\\gameSprites\\botInvaders\\normalInvaders\\invaderShip1");
bulletTex = Content.Load<Texture2D>(".\\gameGraphics\\gameSprites\\botInvaders\\normalInvaders\\botInvaderLaser");
botInvadersRect = new Rectangle[invaderRows, invaderCollumns];
}
public void Update(GameTime gameTime)
{
for (int r = 0; r < invaderRows; r++)
{
for (int c = 0; c < invaderCollumns; c++)
{
EnemyShoot();
UpdateBullets();
}
}
}
public void Draw(Texture2D invadersTex, Rectangle[,] invadersDestinationRect, Nullable<Rectangle> invadersSourceRect, Color invadersColor, float invadersRotation, Vector2 invadersOrigin, SpriteEffects invadersEffects, float invadersScale, SpriteBatch spriteBatch)
{
for (int r = 0; r < invaderRows; r++)
{
for (int c = 0; c < invaderCollumns; c++)
{
spriteBatch.Draw(botInvaderTex, botInvadersRect[r, c], botInvaderHitBox, Color.White);
foreach (blasterLasers bulletSpawn in bulletsList)
{
bulletSpawn.Draw(spriteBatch);
}
}
}
}
public void UpdateBullets()
{
foreach (blasterLasers bulletsSpawn in bulletsList)
{
bulletsSpawn.bulletPos.Y = bulletsSpawn.bulletPos.Y + bulletsSpawn.bulletSpeed;
if (bulletsSpawn.bulletPos.Y >= -632)
{
bulletsSpawn.isVisible = false;
}
}
for (int i = 0; i < bulletsList.Count(); i++)
{
if (!bulletsList[i].isVisible)
{
bulletsList.RemoveAt(i);
i--;
}
}
}
public void EnemyShoot()
{
if (bulletDelay >= 0)
{
bulletDelay--;
}
if (bulletDelay <= 0)
{
blasterLasers newBullet = new blasterLasers(bulletTex);
newBullet.bulletPos = new Vector2(botInvaderHitBox.X + botInvaderFrameWidth / 2 - newBullet.bulletTex.Width / 2, botInvaderHitBox.Y + 90);
newBullet.isVisible = true;
if (bulletsList.Count() < 20)
{
bulletsList.Add(newBullet);
}
}
if (bulletDelay == 0)
{
bulletDelay = 40;
}
}
}
I initialize the class in Game1:
// Create a var
botInvaders botInvader;
// Init it
botInvader = new botInvaders(botInvaders.bulletTex);
// Load Content
botInvader.LoadContent(Content);
// Update
botInvader.Update(gameTime);
// Draw Invaders
botInvader.Draw(botInvaders.botInvaderTex, botInvaders.botInvadersRect, botInvaders.botInvaderHitBox, Color.White, 0f, botInvaders.botInvaderOrigin, SpriteEffects.None, 1.0f, spriteBatch);
Could the problem be that I'm not actually drawing the bullets?
Or I'm not adding any bullets to the list?
If I debug I can see:
bulletsList Count = 0
_size 0
_items [0] null [1] null [2] null [3] null
EDIT:
blasterLasers class:
public class blasterLasers
{
public Texture2D bulletTex;
public Vector2 bulletOrigin, bulletPos;
public bool isVisible;
public float bulletSpeed;
public Rectangle boundingBox;
public blasterLasers(Texture2D newBulletTex)
{
bulletSpeed = 10f;
bulletTex = newBulletTex;
isVisible = false;
}
public void Draw(SpriteBatch spriteBatch)
{
spriteBatch.Draw(bulletTex, bulletPos, Color.White);
}
}
It looks like you are removing the bullets from bulletslist when you call UpdateBullets, so when you go call Draw, it loops through bulletList, which is empty, hence nothing is drawn.

Weird Behaviour when initiate a class

I'm working on a game, and I made all the building blocks. now I'm working on the game logic and rendering.
I have abstract Monster class and a class call GreenMonster that inherits from it.
Now the weird thing is, when I try to init a GreenMonster object.
when I do this:
private void initGreenMonsters()
{
for (int i = 0; i < greenMonsters.Length; i++)
{
greenMonsters[i] = new GreenMonster(new Point(0,40),new Size(40, 40));
}
}
every thing works like I planned and I can render the images on the form.
but when I try to init like that:
private void initGreenMonsters()
{
for (int i = 0; i < greenMonsters.Length; i++)
{
greenMonsters[i] = new GreenMonster();
greenMonsters[i].Position = new Point(0, 40);
greenMonsters[i].Size = new Size(40, 40);
}
}
I don't get any errors, and the app runs, but I can render the monsters.
This is the Monster class constructor and the Draw Method I use to draw a Monster:
public Monster(Point _startPosition,Size _size)
{
this.size = _size;
this.position = _startPosition;
}
public virtual void Draw(Graphics g)
{
Rectangle monsterRec = new Rectangle(position, size);
g.DrawImage(img, monsterRec);
}
and this is the GreenMonster class constructor:
public GreenMonster(Point _startPosition, Size _size)
: base(_startPosition, _size)
{
this.img = new Bitmap(SpaceInvadersGame.Properties.Resources.NormalMonster);
this.hp = 1;
this.speed = 1;
}
public GreenMonster()
{
this.img = new Bitmap(SpaceInvadersGame.Properties.Resources.NormalMonster);
this.hp = 1;
this.speed = 1;
}
the only thing that bothers me is, that when I'm looking at both ways I init the objects, it just looks the same..
I just can't find any different between in both of the ways.
someone have any idea how its different?
If you need more code so the question is more clear, I would be happy to add!
this is the Monster class and its properties
public abstract class Monster
{
protected Point position;
public Point Position { get { return position; } set { position = value; } }
protected Size size;
public Size Size { get { return size; } set { value = size; } }
public int speed;
protected Bitmap img;
protected int hp;
public int HP { get { return hp; } }
public void SetStartingPosition(int x, int y)
{
this.position = new Point(x, y);
}
public virtual void Draw(Graphics g)
{
Rectangle monsterRec = new Rectangle(position, size);
g.DrawImage(img, monsterRec);
}
}
You are setting your incoming value to the current size, rather than setting the current size to the incoming value, in the method below:
public Size Size { get { return size; } set { value = size; } }
should be
public Size Size { get { return size; } set { size = value; } }
Your code for Position looks OK though:
public Point Position { get { return position; } set { position = value; } }

Categories