I have a C# project for school. I am unable to move my character in this ASCII console game. It has to be OO. After I run the code I get this:
After I press left or right I get
Can someone help me on this? Any help is appreciated.
program.cs:
class Program
{
static void Main(string[] args)
{
SuperConsole.Init(50, 44);
// create the game
Game game = new Game(30, 30);
//create objects
Entity spaceship = new Entity(1, 15, '#', SuperConsoleColor.Cyan);
// start the game loop
RunGameLoop(game);
}
protected static void RunGameLoop(Game game)
{
SuperConsole.BackgroundColor = SuperConsoleColor.DarkBlue;
SuperConsole.ForegroundColor = SuperConsoleColor.Gray;
int refreshRate = 20;
SuperConsole.CursorVisible = false;
SuperConsole.BackgroundColor = SuperConsoleColor.DarkBlue;
SuperConsole.ForegroundColor = SuperConsoleColor.Gray;
SuperConsole.Clear();
Reset(game);
game.Draw(0.0f);
SuperConsole.Flush();
/*
SuperConsole.SetCursorPosition(0, 0);*/
while (true)
{
while (SuperConsole.KeyAvailable)
{
ConsoleKeyInfo key = SuperConsole.ReadKey(true);
game.OnInput(key.Key);
}
System.Threading.Thread.Sleep(1000 / refreshRate);
Reset(game);
game.Draw(1.0f / (float)refreshRate);
SuperConsole.Flush();
}
}
protected static void Reset(Game game)
{
SuperConsole.BackgroundColor = SuperConsoleColor.Black;
SuperConsole.ForegroundColor = SuperConsoleColor.Black;
SuperConsole.SetCursorPosition(0, 0);
for (int y = 0; y < game.GetHeight(); ++y)
{
for (int x = 0; x < game.GetWidth(); ++x)
{
SuperConsole.Write(" ");
}
SuperConsole.WriteLine();
}
SuperConsole.SetCursorPosition(0, 0);
}
}
}
game.cs:
class Game : Entity
{
protected int width, height;
Entity spaceship = new Entity(1, 15, '#', SuperConsoleColor.Cyan);
public Game(int newWidth, int newHeight)
{
// set the size
width = newWidth;
height = newHeight;
// set the window
Console.WindowWidth = width+1;
Console.WindowHeight = height+1;
}
public int GetWidth()
{
return width;
}
public int GetHeight()
{
return height;
}
public void Draw(float dt)
{
SuperConsole.SetCursorPosition(spaceship.GetX(), spaceship.GetY());
SuperConsole.ForegroundColor = spaceship.GetColour();
SuperConsole.Write(spaceship.GetChar());
}
public void OnInput(ConsoleKey key)
{
int redo = 0;
ConsoleKey pressedKey = key;
do
{
Console.Clear();
switch (pressedKey)
{
case ConsoleKey.LeftArrow:
SuperConsole.SetCursorPosition(spaceship.GetX() - 1, spaceship.GetY());
break;
case ConsoleKey.UpArrow:
break;
case ConsoleKey.RightArrow:
SuperConsole.SetCursorPosition(spaceship.GetX()+1, spaceship.GetY());
break;
case ConsoleKey.DownArrow:
break;
}
} while (redo == 0);
}
}
}
entity.cs:
class Entity
{
protected int xPos;
protected int yPos;
protected char character;
protected SuperConsoleColor colour;
public Entity()
{
}
public Entity(int xPosNew, int yPosNew, char newCharacter, SuperConsoleColor newColour)
{
//define position
xPos = xPosNew;
yPos = yPosNew;
//define character
character = newCharacter;
//define colour
colour = newColour;
}
public char GetChar()
{
return character;
}
public int GetX()
{
return xPos;
}
public int GetY()
{
return yPos;
}
public SuperConsoleColor GetColour()
{
return colour;
}
}
}
I see two issues:
Within RunGameLoop(Game game) you should replace while (SuperConsole.KeyAvailable) with if (SuperConsole.KeyAvailable).
Within Game.OnInput(ConsoleKey) you change the cursor position instead of the spaceship position
Also try using breakpoints to Check if the code reaches Game.Draw() and check if the spaceship position and the cursor position are correct.
Also you should get a bit more into C#.
Instead of
public char GetChar()
{
return character;
}
private character;
.Net allows you to use Properties:
public char Character
{
get; private set;
}
or
public char Character
{
get { return character; }
}
private character = '#';
Hope this helps.
Apart from that: No offense, but this question isn't really what Stackoverflow is for. In future, please be more patient and try to googling debugging tips instead of letting Stackoverflow do the 'dirty work' for you.
Related
I am currently programming a game in which an infinite procedural city is generated. so far everything works but because it leads to laggs if there are too many objects in the scene I wanted to make a script in which objects only appear near the player. I watched this video for help:https://www.youtube.com/watch?v=xlSkYjiE-Ck. When I tried to link this to my script (GenerateBuilding script) this error came:ArgumentException:
An item with the same key has already been added. Key: (0.0, 1.0)
System.Collections.Generic.Dictionary...
I need help to make the script work in which the houses are generated as well as the planes do, they should only be showed when the player is nearby
---Relevant Lines---
(Endless City)
calling updateChunk function in update()(updateChunk/building function is in GenerateBuilding script)
public void UpdateBuildings()
{
for (int i = 0; i < buildingObjects.Count; i++)
{
buildingObjects[i].SetVisible(false);
}
buildingObjects.Clear();
}
adding to dictionary line 68-80(UpdateVisibleChunks function)
if (building.cityChunkDictionary.ContainsKey(viewedChunkCoord))
{
building.cityChunkDictionary[viewedChunkCoord].UpdateCityChunk(viewerPosition, viewedChunkCoord, chunkSize, maxViewDst);
if (building.cityChunkDictionary[viewedChunkCoord].IsVisible())
{
building.buildingObjects.Add(building.cityChunkDictionary[viewedChunkCoord]);
}
}
else
{
building.AddTest(viewedChunkCoord, chunkSize);
}
EndlessCity, CityChunk class
CityChunk function, sending position to GenerateBuilding script to instantiate buildings in right position.
building.requestBuildingSquad(positionV3);
GenerateBuilding relevant lines
builderH function, instantiates the buildings
public float builderH(GameObject[] obj, float Height, Vector3 position)
{
Transform objTrans = obj[Random.Range(0, obj.Length)].transform;
//Instantiate house Object
GameObject objekt = Instantiate(objTrans.gameObject, position + new Vector3(xOfsset * spaceBetween, Height, zOfsset * spaceBetween), transform.rotation);
float height = Test.transform.localScale.y;
objectsss.Add(objekt);
return height;
}
AddTest function, adds instantiates objects from builderH to a dictionary
public void AddTest(Vector2 viewedChunkCoord, float chunkSize)
{
for (int i = 0; i < objectsss.Count; i++)
{
cityChunkDictionary.Add(viewedChunkCoord, new Testing(objectsss[i]));
}
}
Testing class, testing function, adds objects to class
public Testing(GameObject obj)
{
MeshObject = obj;
}
that should be all relevant lines
full scripts(really similar)
EndlessCity Script(this scripts generates the planes and gives position for GenerateBuilding script)
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System;
public class EndlessCity : MonoBehaviour
{
public const float maxViewDst = 10;
public Transform viewer;
private GenerateBuilding building;
public static Vector2 viewerPosition;
int chunkSize;
int chunksVisibleInViewDst;
Dictionary<Vector2, CityChunk> terrainChunkDictionary = new Dictionary<Vector2, CityChunk>();
List<CityChunk> terrainChunksVisibleLastUpdate = new List<CityChunk>();
void Start()
{
chunkSize = 8 - 1;
chunksVisibleInViewDst = Mathf.RoundToInt(maxViewDst / chunkSize);
}
void Update()
{
viewerPosition = new Vector2(viewer.position.x, viewer.position.z);
UpdateVisibleChunks();
}
void UpdateVisibleChunks()
{
building = FindObjectOfType<GenerateBuilding>();
building.UpdateBuildings();
for (int i = 0; i < terrainChunksVisibleLastUpdate.Count; i++)
{
terrainChunksVisibleLastUpdate[i].SetVisible(false);
}
terrainChunksVisibleLastUpdate.Clear();
int currentChunkCoordX = Mathf.RoundToInt(viewerPosition.x / chunkSize);
int currentChunkCoordY = Mathf.RoundToInt(viewerPosition.y / chunkSize);
for (int yOffset = -chunksVisibleInViewDst; yOffset <= chunksVisibleInViewDst; yOffset++)
{
for (int xOffset = -chunksVisibleInViewDst; xOffset <= chunksVisibleInViewDst; xOffset++)
{
Vector2 viewedChunkCoord = new Vector2(currentChunkCoordX + xOffset, currentChunkCoordY + yOffset);
if (terrainChunkDictionary.ContainsKey(viewedChunkCoord))
{
terrainChunkDictionary[viewedChunkCoord].UpdateTerrainChunk();
if (terrainChunkDictionary[viewedChunkCoord].IsVisible())
{
terrainChunksVisibleLastUpdate.Add(terrainChunkDictionary[viewedChunkCoord]);
}
}
else
{
terrainChunkDictionary.Add(viewedChunkCoord, new CityChunk(viewedChunkCoord, chunkSize, transform));
}
if (building.cityChunkDictionary.ContainsKey(viewedChunkCoord))
{
building.cityChunkDictionary[viewedChunkCoord].UpdateCityChunk(viewerPosition, viewedChunkCoord, chunkSize, maxViewDst);
if (building.cityChunkDictionary[viewedChunkCoord].IsVisible())
{
building.buildingObjects.Add(building.cityChunkDictionary[viewedChunkCoord]);
}
}
else
{
building.AddTest(viewedChunkCoord, chunkSize);
}
}
}
}
public class CityChunk
{
private GenerateBuilding building;
public GameObject meshObject;
public Vector3 positionV3;
Vector2 position;
Bounds bounds;
public CityChunk(Vector2 coord, int size, Transform parent)
{
building = FindObjectOfType<GenerateBuilding>();
position = coord * size;
bounds = new Bounds(position, Vector2.one * size);
positionV3 = new Vector3(position.x, 0, position.y);
int xPosition = building.xLength / 2;
int zPosition = building.zLength / 2;
float xOfsset = building.xOfsset;
float zOfsset = building.zOfsset;
float spaceBetween = building.spaceBetween;
//Instantiate plane
meshObject = Instantiate(building.groundObject, positionV3 + new Vector3((xPosition + xOfsset) * spaceBetween, -.5f, (xPosition + 1 + zOfsset) * spaceBetween), Quaternion.identity);
SetVisible(false);
building.requestBuildingSquad(positionV3);
}
public void UpdateTerrainChunk()
{
float viewerDstFromNearestEdge = Mathf.Sqrt(bounds.SqrDistance(viewerPosition));
bool visible = viewerDstFromNearestEdge <= maxViewDst;
SetVisible(visible);
}
public void SetVisible(bool visible)
{
meshObject.SetActive(visible);
}
public bool IsVisible()
{
return meshObject.activeSelf;
}
}
}
GenerateBuilding(this script generates Buildings on the planes)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GenerateBuilding : MonoBehaviour
{
public int minHeight = 2;
public int maxHeight = 8;
public int cubeTileX;
public int cubeTileZ;
public int xLength;
public int zLength;
public float spaceBetween;
public float xOfsset;
public float zOfsset;
public GameObject TesObject;
public GameObject[] Base;
public GameObject[] secondB;
public GameObject[] roof;
public GameObject groundObject;
public List<GameObject> objectsss;
public Dictionary<Vector2, Testing> cityChunkDictionary = new Dictionary<Vector2, Testing>();
public List<Testing> buildingObjects = new List<Testing>();
public GameObject Test;
void Start()
{
//requestBuildingSquad(this.transform.position);
}
void Update()
{
if (Input.GetKeyDown(KeyCode.I))
{
//
}
}
public void requestBuildingSquad(Vector3 position)
{
//*getting the middle of the city squad
int xPosition = xLength / 2;
int zPosition = zLength / 2;
//*
for (int z = 0; z < zLength; z++)
{
zOfsset++;
for (int x = 0; x < xLength; x++)
{
GenerateBuildings(position);
}
xOfsset = 0;
}
zOfsset = 0;
}
public void GenerateBuildings(Vector3 position)
{
int bHeight = Random.Range(minHeight, maxHeight);
float bOfsset = 0;
bOfsset += builderH(Base, bOfsset, position);
for (int i = 0; i < bHeight; i++)
{
bOfsset += builderH(secondB, bOfsset, position);
}
bOfsset += builderH(roof, bOfsset, position);
xOfsset++;
}
public float builderH(GameObject[] obj, float Height, Vector3 position)
{
Transform objTrans = obj[Random.Range(0, obj.Length)].transform;
//Instantiate house Object
GameObject objekt = Instantiate(objTrans.gameObject, position + new Vector3(xOfsset * spaceBetween, Height, zOfsset * spaceBetween), transform.rotation);
float height = Test.transform.localScale.y;
objectsss.Add(objekt);
return height;
}
public void AddTest(Vector2 viewedChunkCoord, float chunkSize)
{
for (int i = 0; i < objectsss.Count; i++)
{
cityChunkDictionary.Add(viewedChunkCoord, new Testing(objectsss[i]));
}
}
public void UpdateBuildings()
{
for (int i = 0; i < buildingObjects.Count; i++)
{
buildingObjects[i].SetVisible(false);
}
buildingObjects.Clear();
}
public class Testing
{
public GameObject MeshObject;
Vector2 position;
Bounds bounds;
public Testing(GameObject obj)
{
MeshObject = obj;
}
public void SetVisible(bool visiblee)
{
MeshObject.SetActive(visiblee);
}
public bool IsVisible()
{
return MeshObject.activeSelf;
}
public void UpdateCityChunk(Vector3 viewerPosition, Vector2 coord, int size, float maxViewDst)
{
position = coord * size;
bounds = new Bounds(position, Vector2.one * size);
float viewerDstFromNearestEdge = Mathf.Sqrt(bounds.SqrDistance(viewerPosition));
bool visible = viewerDstFromNearestEdge <= maxViewDst;
SetVisible(visible);
}
}
}
The problem is that you are trying to add twice elements with the same key.
here is the documentation of the Add method for dictionaries, and as it states, trying to add an existing key throws an error.
You can either use the TryAdd method, which adds an item only if the key doesn't exist already in the dictionary, or update the value with the existing key as you can see here.
so for my first project with MonoGame I decided to make a tetris clone but I have an issue which I don't know how to solve.
Currently my code is generating a block and moves it downwards until it reach a specific y position. The block need to stay at this position and a new block spawns. I'm doing this with a List which contains object of the block class and then just draw all the blocks in this list.
I took out parts which I believe are not involved in the problem:
public class PlayField : DrawableGameComponent
{
private Game1 gameRef;
private Texture2D fieldTexture;
private BlockGenerator blockGenerator;
private Texture2D[] allBlocks;
private Block currentBlock;
public bool[,] fieldFilled;
private int down_Blocks = 22;
private int side_Blocks = 10;
public List<Block> placedBlocks;
public PlayField(Game game) : base(game)
{
placedBlocks = new List<Block>();
allBlocks = new Texture2D[4];
blockGenerator = new BlockGenerator(allBlocks,gameRef);
}
public override void Update(GameTime gameTime)
{
base.Update(gameTime);
try
{
if (currentBlock.isMoving == false)
{
placedBlocks.Add(currentBlock);
currentBlock = null;
currentBlock = blockGenerator.GenerateBlock();
}
else
{
currentBlock.UpdatePosition(gameTime);
if (InputManager.CheckForKeyBoardRelease(Keys.A))
{
currentBlock.MoveLeft();
}
if (InputManager.CheckForKeyBoardRelease(Keys.D))
{
currentBlock.MoveRight();
}
}
}
catch(NullReferenceException e)
{
currentBlock = blockGenerator.GenerateBlock();
}
}
public override void Draw(GameTime gameTime)
{
gameRef.SpriteBatch.Begin();
if(currentBlock != null)
{
currentBlock.DrawBlocks();
}
foreach(Block b in placedBlocks)
{
b.DrawBlocks();
}
gameRef.SpriteBatch.End();
base.Draw(gameTime);
}
The method "GenerateBlock" returns a object of type "Block"
public class Block : DrawableGameComponent
{
Game1 gameRef;
public Texture2D blockTexture;
public Vector2[] blockPositions;
TimeSpan lastMove;
TimeSpan blockMove = TimeSpan.FromMilliseconds(500);
public bool isMoving;
public Block(Game game, Texture2D _blockTexture, Vector2[] _blockPositions) : base(game)
{
gameRef = (Game1)game;
blockTexture = _blockTexture;
blockPositions = _blockPositions;
isMoving = true;
}
public void UpdatePosition(GameTime gameTime)
{
Vector2 bottomBlockPositon = FindBottomBlock();
if(bottomBlockPositon.Y < 550)
{
if (WaitTillMove(gameTime))
{
for (int i = 0; i < blockPositions.Length; i++)
{
blockPositions[i] = new Vector2(blockPositions[i].X, blockPositions[i].Y + 25);
}
}
}
else
{
isMoving = false;
Console.WriteLine("X: " +blockPositions[0].X + " Y:" + blockPositions[0].Y);
}
}
public Vector2 FindBottomBlock()
{
Vector2 result = new Vector2(0, 0);
for(int i = 0; i < blockPositions.Length; i++)
{
if(blockPositions[i].Y > result.Y)
{
result = blockPositions[i];
}
}
return result;
}
public bool WaitTillMove(GameTime gameTime)
{
if (lastMove + blockMove < gameTime.TotalGameTime)
{
lastMove = gameTime.TotalGameTime;
return true;
}
return false;
}
public void DrawBlocks()
{
gameRef.SpriteBatch.Draw(blockTexture, blockPositions[0], Color.White);
gameRef.SpriteBatch.Draw(blockTexture, blockPositions[1], Color.White);
gameRef.SpriteBatch.Draw(blockTexture, blockPositions[2], Color.White);
gameRef.SpriteBatch.Draw(blockTexture, blockPositions[3], Color.White);
}
}
Debugging says that my List contains an Element even though it has the wrong positions. But this shouldn't matter because I still only "see" one Block at the same time.
Hopefully someone can toss me into the right direction.
Edit:
public class BlockGenerator
{
Random random;
Texture2D[] allBlocks;
Vector2[] blockPositions;
Texture2D currentBlock;
BlockEnums currentBlockEnum;
Game1 gameRef;
public BlockGenerator(Texture2D[] blocks, Game1 game)
{
gameRef = (Game1)game;
allBlocks = blocks;
currentBlock = allBlocks[1];
blockPositions = new Vector2[4];
random = new Random();
}
public Block GenerateBlock()
{
int colorValue = random.Next(0, 4); //0 = blue, 1 = green, 2 = red, 3 = yellow
currentBlock = allBlocks[colorValue];
currentBlockEnum = BlockEnums.Line;
blockPositions[0] = new Vector2(100, 0);
blockPositions[1] = new Vector2(125, 0);
blockPositions[2] = new Vector2(150, 0);
blockPositions[3] = new Vector2(175, 0);
Block generatedBlock = new Block(gameRef,currentBlock, blockPositions);
return generatedBlock;
}
public class BlockGenerator
{
Random random;
Texture2D[] allBlocks;
Vector2[] blockPositions; // delete this
Then move the initialization code from the constructor into GenerateBlock (add var).
var blockPositions = new Vector2[4];
Then it should work. You were creating new vectors each time you created a block but re-using the same instance of blockPositions each time, so both blocks do exist but will always have the exact same positions.
I haven't had time to test but I think it's right...let me know :)
edit: I'd also suggest moving blockPositions entirely into the Block class. I see no value (in the code you have posted, maybe you have plans for later) in having that logic outside of the class that really owns it...and if it was contained within the class to begin with you would have avoided this problem. Just a suggestion :)
I wrote this little Unity3D class to fade an array of UI.Image in or out by reducing or increasing the alpha value of their color property.
Then I wanted to use it for an array of materials on meshrenderers but realized that materials and UI.Image, though they both have a color property which can be changed, are totally different classes and you access their color properties differently so I cannot use this class for them both.
I tried making a list of just the colors to be changed, but in unity a Color is a struct and cannot be passed by reference.
Do you have any idea how to make this class generic so it can change the color property on several different classes? I sort of wrote myself into a corner here, I'd rather not just copy the class and change it for meshrenderer.material, that would be ugly
public class FadeThingInOut : MonoBehaviour
{
//public Setting fields
public Image[] ThingsToFade;
public bool Visible = true;
public float FadeTime = 1f;
//private fields
Color[] InColour;
Color[] OutColor;
void Start()
{
if (Visible)
{
SetColours(ref InColour, ref OutColor,0f);
}
else
{
SetColours(ref OutColor,ref InColour,1f);
}
}
void SetColours(ref Color[] one,ref Color[] two,float other)
{
one = new Color[ThingsToFade.Length];
two = new Color[ThingsToFade.Length];
for (int i = 0; i < ThingsToFade.Length; i++)
{
one[i] = ThingsToFade[i].color;
two[i] = Tools.colorfromalpha(one[i], other);
}
}
public void Fade(bool inout)
{
if (inout && !Visible)
{
StartCoroutine(FadeLerp(OutColor,InColour,inout));
}
else if (!inout && Visible)
{
StartCoroutine(FadeLerp(InColour, OutColor, inout));
}
}
IEnumerator FadeLerp(Color[] from, Color[] to,bool endstate)
{
float nowtime = 0f;
while (nowtime < FadeTime)
{
nowtime += Time.deltaTime;
float ratio = nowtime / FadeTime;
for (int i = 0; i < ThingsToFade.Length; i++)
{
ThingsToFade[i].color = Color.Lerp(from[i], to[i], ratio);
yield return null;
}
}
for (int i = 0; i < ThingsToFade.Length; i++)
{
ThingsToFade[i].color = to[i];
}
Visible = endstate;
}
}
UPDATE
Heres what I ended up doing.
I keep an array of the components and then cast them to one of three types that I am dealing with. There is a series of if statements to deal with the different classes differntly.
I'm still interested in a more elegant solution that may use some aspects of .net or C# that I'm not familiar with though
public class FadeThingInOut : MonoBehaviour
{
//public Setting fields
public Component[] ThingsToFade;
public bool Visible = true;
public float FadeTime = 1f;
public float Max = 1f;
//private fields
Color[] InColour;
Color[] OutColor;
void Start()
{
if (Visible)
{
SetColours(ref InColour, ref OutColor,0f);
}
else
{
SetColours(ref OutColor, ref InColour, Max);
}
}
void SetColours(ref Color[] one,ref Color[] two,float other)
{
one = new Color[ThingsToFade.Length];
two = new Color[ThingsToFade.Length];
for (int i = 0; i < ThingsToFade.Length; i++)
{
one[i] = GetColour(i);
two[i] = Tools.colorfromalpha(one[i], other);
}
}
public void Fade(bool inout)
{
if (inout && !Visible)
{
StartCoroutine(FadeLerp(OutColor,InColour,inout));
}
else if (!inout && Visible)
{
StartCoroutine(FadeLerp(InColour, OutColor, inout));
}
}
Color GetColour(int index)
{
if(ThingsToFade[index].GetType() == typeof(MeshRenderer))
{
return (ThingsToFade[index] as MeshRenderer ).material.color;
}
else if (ThingsToFade[index].GetType() == typeof(Image))
{
return (ThingsToFade[index] as Image).color;
}
else if (ThingsToFade[index].GetType() == typeof(RawImage))
{
return (ThingsToFade[index] as RawImage).material.color;
}
return Color.black;
}
void SetColour(int index,Color col)
{
if (ThingsToFade[index].GetType() == typeof(MeshRenderer))
{
(ThingsToFade[index] as MeshRenderer).material.color = col;
return;
}
else if (ThingsToFade[index].GetType() == typeof(Image))
{
(ThingsToFade[index] as Image).color = col;
return;
}
else if (ThingsToFade[index].GetType() == typeof(RawImage))
{
(ThingsToFade[index] as RawImage).material.color = col;
return;
}
}
IEnumerator FadeLerp(Color[] from, Color[] to,bool endstate)
{
float nowtime = 0f;
while (nowtime < FadeTime)
{
nowtime += Time.deltaTime;
float ratio = nowtime / FadeTime;
for (int i = 0; i < ThingsToFade.Length; i++)
{
SetColour(i, Color.Lerp(from[i], to[i], ratio));
yield return null;
}
}
for (int i = 0; i < ThingsToFade.Length; i++)
{
SetColour(i, to[i]);
}
Visible = endstate;
}
}
This isn't super ideal but you can do it this way.
public interface ISetColor
{
public Color color { get; set; }
}
public class ImageSetColor : MonoBehaviour, ISetColor
{
public Image m_Image
public Color color { get {return m_Image.color;} set { m_Image.color = value}
}
public class MaterialSetColor : MonoBehaviour, ISetColor
{
public Material m_Material
public Color color { get {return m_Material.color;} set { m_Material.color = value}
}
Then instead of an Image array you can do an array of public MonoBehaviour[] ThingsToFade then anytime you use ThingsToFade you need to cast it to an ISetColor.
Then any component you want to be able to set colors with will need to create a class that inherits from MonoBehaviour and implements the ISetColor interface
This isn't the easiest solution to use in Unity but it should work
You should not write a class. You should write an extension method. Because structs are immutable though, you cannot change their value, so you should write a static function that has as an argument a reference to color, by using the "ref" keyword. Using structs as mutable objects is not recommended most of the time, but it's the best option here.
I wrote this little Unity3D class to fade an array of UI.Image in or
out by reducing or increasing the alpha value of their color property.
I dont see why to overcomplicate everything.
Edit: This will fade by adjusting the alpha directly, using floats instead of color swapping. Faster, cheaper, shorter.
IEnumerator FadeLerp(float _from, float _to, bool endstate)
{
float nowtime = 0f;
while (nowtime < FadeTime)
{
nowtime += Time.deltaTime;
float ratio = nowtime / FadeTime;
for (int i = 0; i < ThingsToFade.Length; i++)
{
Color _color = ThingsToFade[i].color;
_color.a = Mathf.Lerp(_from, _to, ratio);
ThingsToFade[i].color = _color;
yield return null;
}
}
for (int i = 0; i < ThingsToFade.Length; i++)
{
ThingsToFade[i].renderer.enabled = endstate;
}
}
use color.lerp function.
it will fade the color nicely, and if you know how to use the lerp, with a counter and with an array, there should not be a problem :)
if tomorrow you are still stucked i'll write the lerping function for you, but keep trying :)
It will be a sweet experience if you do it on your own :D
I am learning to make a small variant of chess game using windows forms C#, the game includes only the pawns of both sides, i have drew the board and organized the pieces on there places, but i honestly do not know how to start implementing the moves by clicking the mouse on the piece and then the location where i want to move it.
as references the black pawn is named piece, and white pawn is named pieceW
here is my code for the board
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace AIchess
{
public partial class Form1 : Form
{
static System.Drawing.Bitmap piece = AIchess.Properties.Resources.piece;
ChessPiece Piece = new ChessPiece(piece, ChessColor.Black);
static System.Drawing.Bitmap pieceW = AIchess.Properties.Resources.pieceW;
ChessPiece PieceW = new ChessPiece(pieceW, ChessColor.White);
Square[,] square = new Square[8, 8];
public Form1()
{
InitializeComponent();
int i, j;
for (i = 0; i < 8; i++)
{
for (j = 0; j < 8; j++)
{
this.square[i, j] = new Square();
this.square[i, j].BackColor = System.Drawing.SystemColors.ActiveCaption;
this.square[i, j].BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.square[i, j].Location = new System.Drawing.Point(57 + i * 60, 109 + j * 60);
this.square[i, j].Name = i.ToString()+j.ToString();
this.square[i, j].Size = new System.Drawing.Size(60, 60);
this.square[i, j].TabIndex = 2;
this.square[i, j].TabStop = false;
this.Controls.Add(this.square[i, j]);
if (j == 1)
{
this.square[i, j].Image = piece;
this.square[i, j].AllocatedBy = "black";
}
if (j == 6)
{
this.square[i, j].Image = pieceW;
this.square[i, j].AllocatedBy = "white";
}
if (((i+j) % 2) ==0)
this.square[i, j].BackColor = Color.RoyalBlue;
else
this.square[i, j].BackColor = Color.LightBlue;
}
}
}
}
public enum ChessColor
{
White,
Black,
};
class ChessPiece
{
private Image DisplayedImage;
private ChessColor DisplayedColor;
private Point CurrentSquare;
public ChessPiece(Image image, ChessColor color)
{
DisplayedImage = image;
DisplayedColor = color;
}
}
class Square:PictureBox
{
private bool color;
public string AllocatedBy;
}
}
Here's a really simple implementation, I hope you won't mind that I did it from scratch.
Obviously it's very simple, there's no drag and drop and no animation but it fulfills your requirement.
I'll go through each part and explain them
InitializeGame
There you do set your images dimensions (they should be identical obviously)
You add in the dictionary the relationship between piece type/color and your bitmap
Note : the grid will be scaled so you can throw any size of bitmap you like
CreateBoard, DrawGame, DrawPieces
Nothing exceptional in there, note that for keeping things simple I do that every time a user clicks but it shouldn't be much of an issue, it's not Crysis after all :D
PickOrDropPiece
This is the logic where picking/dropping happens, it's really trivial and I'll let you take a look by yourself.
Differences between your code
I've created a Board type which holds the pieces and that you can easily update.
Note : do not remove the equality members in Piece they are here to help the dictionary.
Make sure to use 32-bit bitmaps with transparent borders
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
pictureBox1.MouseDown += pictureBox1_MouseDown;
}
#region Properties
private Board Board { get; set; }
private Piece CurrentPiece { get; set; }
private Dictionary<Piece, Bitmap> PieceBitmaps { get; set; }
private int TileWidth { get; set; }
private int TileHeight { get; set; }
#endregion
#region Events
private void Form1_Load(object sender, EventArgs e)
{
InitializeGame();
DrawGame();
}
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
PickOrDropPiece(e);
DrawGame();
}
#endregion
#region Methods
private void InitializeGame()
{
TileWidth = 64;
TileHeight = 64;
Board = new Board();
PieceBitmaps = new Dictionary<Piece, Bitmap>();
PieceBitmaps.Add(new Piece(PieceType.Pawn, PieceColor.Black), new Bitmap("pawnblack.png"));
PieceBitmaps.Add(new Piece(PieceType.Pawn, PieceColor.White), new Bitmap("pawnwhite.png"));
}
private void DrawGame()
{
var tileSize = new Size(TileWidth, TileHeight);
Bitmap bitmap = CreateBoard(tileSize);
DrawPieces(bitmap);
pictureBox1.Image = bitmap;
}
private Bitmap CreateBoard(Size tileSize)
{
int tileWidth = tileSize.Width;
int tileHeight = tileSize.Height;
var bitmap = new Bitmap(tileWidth*8, tileHeight*8);
using (Graphics graphics = Graphics.FromImage(bitmap))
{
for (int x = 0; x < 8; x++)
{
for (int y = 0; y < 8; y++)
{
Brush brush = (x%2 == 0 && y%2 == 0) || (x%2 != 0 && y%2 != 0) ? Brushes.Black : Brushes.White;
graphics.FillRectangle(brush, new Rectangle(x*tileWidth, y*tileHeight, tileWidth, tileHeight));
}
}
}
return bitmap;
}
private void DrawPieces(Bitmap bitmap)
{
using (Graphics graphics = Graphics.FromImage(bitmap))
{
Board board = Board;
for (int x = 0; x < 8; x++)
{
for (int y = 0; y < 8; y++)
{
Piece piece = board.GetPiece(x, y);
if (piece != null)
{
Bitmap bitmap1 = PieceBitmaps[piece];
graphics.DrawImageUnscaled(bitmap1, new Point(x*TileWidth, y*TileHeight));
}
}
}
}
}
private void PickOrDropPiece(MouseEventArgs e)
{
Point location = e.Location;
int x = location.X/TileWidth;
int y = location.Y/TileHeight;
bool pickOrDrop = CurrentPiece == null;
if (pickOrDrop)
{
// Pick a piece
Piece piece = Board.GetPiece(x, y);
Board.SetPiece(x, y, null);
if (piece != null)
{
label1.Text = string.Format("You picked a {0} {1} at location {2},{3}", piece.Color, piece.Type, x,
y);
}
else
{
label1.Text = "Nothing there !";
}
CurrentPiece = piece;
}
else
{
// Drop picked piece
Board.SetPiece(x, y, CurrentPiece);
label1.Text = string.Format("You dropped a {0} {1} at location {2},{3}", CurrentPiece.Color,
CurrentPiece.Type, x,
y);
CurrentPiece = null;
}
}
#endregion
}
public class Board
{
private readonly Piece[] _pieces;
public Board()
{
_pieces = new Piece[8*8];
PopulatePieces();
}
public Piece GetPiece(int x, int y)
{
int i = y*8 + x;
return _pieces[i];
}
public void SetPiece(int x, int y, Piece piece)
{
int i = y*8 + x;
_pieces[i] = piece;
}
private void PopulatePieces()
{
for (int i = 0; i < 8; i++)
{
SetPiece(i, 1, new Piece(PieceType.Pawn, PieceColor.Black));
SetPiece(i, 7, new Piece(PieceType.Pawn, PieceColor.White));
}
}
}
public class Piece
{
private readonly PieceColor _color;
private readonly PieceType _type;
public Piece(PieceType type, PieceColor color)
{
_type = type;
_color = color;
}
public PieceType Type
{
get { return _type; }
}
public PieceColor Color
{
get { return _color; }
}
protected bool Equals(Piece other)
{
return _color == other._color && _type == other._type;
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != GetType()) return false;
return Equals((Piece) obj);
}
public override int GetHashCode()
{
unchecked
{
return ((int) _color*397) ^ (int) _type;
}
}
public static bool operator ==(Piece left, Piece right)
{
return Equals(left, right);
}
public static bool operator !=(Piece left, Piece right)
{
return !Equals(left, right);
}
}
public enum PieceType
{
Pawn
}
public enum PieceColor
{
Black,
White
}
}
I'm learning to use the as operator, and my goal was to create an option window (non windows form) that can:
Have options added to it (for flexibility, in case I want to use if statements to add menu items)
Be able to display text, textures, or a class (using the classes draw function)
Be controlled through the host GameState
I still haven't added the options for indicating an item is selected, my apologies for not posting a complete work. I also have not sorted the code into regions yet. Sorry again!
Is my code (particularly the draw function) properly using the is and as operators properly, from a performance and readability (non spaghetti code) standpoint?
public class OptionWindow : DrawableGameComponent
{
public Dictionary<int, Option> options;
int selectedOption;
bool windowLoops;
Rectangle drawRectangle;
int spacer;
int totalItemHeight;
SpriteFont sf;
SpriteBatch sb;
public Rectangle DrawRectangle
{
get { return drawRectangle; }
set { drawRectangle = value; }
}
public int SelectedOption
{
get { return selectedOption; }
set
{
if (windowLoops)
{
if (selectedOption >= options.Count())
selectedOption = 0;
if (selectedOption < 0)
selectedOption = options.Count() - 1;
}
else
{
if (selectedOption >= options.Count())
selectedOption = options.Count() - 1;
if (selectedOption < 0)
selectedOption = 0;
}
}
}
public OptionWindow(Game game, bool windowLoops, SpriteFont sf, Rectangle drawRectangle)
: base(game)
{
options = new Dictionary<int, Option>();
this.windowLoops = windowLoops;
this.sf = sf;
DrawRectangle = new Rectangle(drawRectangle.X, drawRectangle.Y, drawRectangle.Width, drawRectangle.Height);
}
public void Add(object option, bool selectable, bool defaultSelection, int height)
{
options.Add(options.Count(), new Option(selectable, option, height));
if (defaultSelection)
SelectedOption = options.Count() - 1;
UpdatePositions();
}
public void UpdatePositions()
{
UpdateTotalItemHeight();
if (options.Count() - 1 != 0)
spacer = (drawRectangle.Height - totalItemHeight) / (options.Count() - 1);
for (int i = 0; i < options.Count(); i++)
{
if (i == 0)
options[i].Position = new Vector2(drawRectangle.X, drawRectangle.Y);
else
{
options[i].Position = new Vector2(
drawRectangle.X,
options[i - 1].Position.Y + options[i - 1].Height + spacer);
}
}
}
public void UpdateTotalItemHeight()
{
totalItemHeight = 0;
for (int i = 0; i < options.Count(); i++)
{
totalItemHeight += options[i].Height;
}
}
protected override void LoadContent()
{
sb = new SpriteBatch(GraphicsDevice);
base.LoadContent();
}
public override void Draw(GameTime gameTime)
{
for (int i = 0; i < options.Count(); i++)
{
if (options[i].OptionObject is string)
sb.DrawString(sf, options[i].OptionObject as string, options[i].Position, Color.White);
if (options[i].OptionObject is Texture2D)
sb.Draw(options[i].OptionObject as Texture2D,
new Rectangle(
(int)options[i].Position.X,
(int)options[i].Position.Y,
options[i].Height,
(options[i].Height / (options[i].OptionObject as Texture2D).Height) * (options[i].OptionObject as Texture2D).Width),
Color.White);
if (options[i].OptionObject is DisplayObject)
(options[i].OptionObject as DisplayObject).Draw(gameTime);
}
base.Draw(gameTime);
}
}
public class Option
{
bool selectable;
object optionObject;
int height;
Vector2 position;
public bool Selectable
{
get { return selectable; }
set { selectable = value; }
}
public object OptionObject
{
get { return optionObject; }
set { optionObject = value; }
}
public int Height
{
get { return height; }
set { height = value; }
}
public Vector2 Position
{
get { return position; }
set { position = value; }
}
public Option(bool selectable, object option, int height)
{
Selectable = selectable;
OptionObject = option;
Height = height;
}
}
It is never adviseable to use is and then as. The usual way to go would be to either of the following:
just use is (if you just want to know the type without subsequent casting)
assign the result of as to a variable and check whether that variable is (not) null
The code analysis tool FxCop helps you find any spots in your code that use is and then as and warns you because of performance concerns.
Note however that a better approach altogether might be to declare your OptionObject property as some abstract class with a Draw method. You could then derive a subclass for strings, one for Texture2D instances and another one for DisplayObject instances and just call Draw in your OptionWindow.Draw method. This would leave the decision which actual drawing operations to execute up to built-in polymorphism features of the framework.