So, I'm trying to make a small program that generates chunks, as in 2 Texture2Ds'. I Made another class called Terrain_Plains.
Constructor (you can guess I already made the variables):
public Terrain_Plains(Texture2D chunkSprite, int y)
{
chunks = chunkSprite;
this.y = y;
//Load drawRectangle
drawRectangle = new Rectangle(0, y, 50, 50);
}
Then I made a draw method:
public void Draw(SpriteBatch spriteBatch)
{
spriteBatch.Draw(chunks, drawRectangle, Color.White);
}
I made an array with the Terrain_Plains in the Game1 method:
Terrain_Plains[] terrain = new Terrain_Plains[2];
And then initialized it using a for loop.
Everything went as expected except one thing: It wasn't showing the 2nd Terrain_Plains.
How can I make them both side-by-side, without having to make another Rectangle? Reason I want to only use one Rectangle is because I want to add more than just 2 Terrain_Plains
EDIT FOR Preston Guillot:
for loop used to initialize array:
for (int i = 0; i < 2; i++)
{
terrain[i] = new Terrain_Plains(sprite, WINDOW_HEIGHT - 50);
}
for loop to draw the array:
for (int i = 0; i < 2; i++)
{
terrain[i].Draw(spriteBatch);
}
Related
I'm new to Monogame and C#. I'm attempting to render a test Tiled map on the screen using TiledSharp. However, when I try to draw multiple layers, they're clearly looping and drawing over one another.
Here's my draw method code:
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
// TODO: Add your drawing code here
spriteBatch.Begin(sortMode: SpriteSortMode.FrontToBack, blendState: BlendState.AlphaBlend, samplerState: SamplerState.PointClamp, null, null, null,
Matrix.CreateScale(6.0f));
for (currentTileLayer=0; currentTileLayer < easyMap.TileLayers.Count; currentTileLayer++)
{
Console.WriteLine(easyMap.TileLayers[currentTileLayer].Name.ToString());
for (var i = 0; i < easyMap.TileLayers[currentTileLayer].Tiles.Count; i++)
{
int gid = easyMap.TileLayers[currentTileLayer].Tiles[i].Gid;
// empty tile, do nothing
// gid => global id of tile
if (gid == 0)
{
}
else
{
int tileFrame = gid - 1;
int column = tileFrame % tilesetTilesWide;
int row = (int)Math.Floor((double)tileFrame / (double)tilesetTilesWide);
float x = (i % easyMap.Width) * easyMap.TileWidth;
float y = (float)Math.Floor(i / (double)easyMap.Width) * easyMap.TileHeight;
Rectangle tileSetRec = new Rectangle(tileWidth * column, tileHeight * row, tileWidth, tileHeight);
spriteBatch.Draw(easyTileset, new Rectangle((int)x, (int)y, tileWidth, tileHeight), tileSetRec, Color.White);
}
}
}
spriteBatch.End();
base.Draw(gameTime);
}
My original map looks like this:
When I run the code it looks like this:
There are three layers to the map. How can I fix my loops? Thanks!
You should use sorted layers since Tiled supports them very well. Using layers gives you much more possibilies in terms of map design. I also enables to make you character hide behind objects and so on.
In tiled, create your background layer first if needed several layers on top to avoid a wrong drawing order.
A simple solution could look like this
const int LAYER_BACKGROUNDLAYER = 0;
const int LAYER_FRONTLAYER = 1;
const int LAYER_TOPLAYER = 2;
Depending on how many layers you want to use.
Then you simply pass the layer number you want to draw to your Draw-Method. Usually from 0 to the max number.
public void DrawLayer(int layer)
{
for (var j = 0; j < curMap.Layers[layer].Tiles.Count; j++)
{
int gid = curMap.Layers[layer].Tiles[j].Gid;
if (gid == 0)
{
//empty tile
}
else
{
//draw your tile
}
}
}
In this case "curMap" is you current .tmx file loaded via curMap = new TmxMap(mapSource);
The draw code above should fit to your code since I've based it on the TiledSharp example as well ;)
I might be wrong but I think you need to sort your tiles from back to front. Whichever tiles should be on bottom should be rendered first.
in for (currentTileLayer=0; currentTileLayer < easyMap.TileLayers.Count; currentTileLayer++)
replace your TileLayers with var orderedTiles = easyMap.TileLayers.OrderBy(t => t.Tile.Gid)
I have a 2D Array of Objects that move synchronously on the screen and I need to detect when they hit the edge of the screen so that they change direction (think Space Invaders). So far, I had it working with a Rectangle pre-defined to be the size and position of the objects in the Array but the Objects can be hit with missiles and are no longer drawn so when all of them on one side are destroyed, the rectangle stays the same size and they change direction too early.
Is there a better way to do what I want to do? This is my code for the functionality at the moment:
(In LoadContent Method)
invaderRect = new Rectangle(
0, 0,
invadersWide * (invaderImage.Width + 15) - 15,
invadersHigh * (invaderImage.Height + 15) - 15);
(In Update Method)
if ((invaderRect.X + invaderRect.Width) >= screenRectangle.Width - 15)
invadersHitWall = true;
else if (invaderRect.X <= 15)
invadersHitWall = false;
if (!invadersHitWall)
invaderRect.X += 2;
else if (invadersHitWall)
invaderRect.X -= 2;
It's all a matter of organizing your code. You should use an object oriented approach. Create a class that represents your game objects. I would use an abstract base class defining the basic properties and methods. Then derive concrete game objects from this base class. The concrete game objects' constructors have the task to initialize the game objects.
public abstract class GameObject
{
protected BlockType[,] _buildingBlocks; // The 2-d array. Replace "BlockType"
// by the type you are using.
protected int _x0, _y0; // Indexes of the first non empty block.
protected int _x1, _y1; // Indexes of the last non empty block + 1.
// Pixel coordinates of upper left corner of the intact game object.
public Point Location { get; set; }
// Represents the current position and size of the possibly diminished
// game object in pixels.
public Rectangle BoundingBox
{
get {
return new Rectangle(
Location.X + BlockSize * _x0,
Location.Y + BlockSize * _y0,
BlockSize * (_x1 - _x0),
BlockSize * (_y1 - _y0)
);
}
}
public void Draw(Graphics g, int x, int x)
{
for (int i = _x0; i < _x1; i++) {
for (int j = _y0; j < _y1; j++) {
BlockType block = _buildingBlocks[i, j];
if (block != null) {
// Replace by the appropriate drawing methods for XNA.
g.FillRectangle(block.Brush,
x + BlockSize * i, y + BlockSize * j,
BlockSize, BlockSize);
}
}
}
}
// Call this after changes have been made to the arrray which may affect the
// apparent size of the game object, e.g. after the object was hit by a bomb.
protected void CalculateBounds()
{
_x0 = _buildingBlocks.GetLength(0);
_y0 = _buildingBlocks.GetLength(1);
_x1 = 0;
_y1 = 0;
for (int i = 0; i < _buildingBlocks.GetLength(0); i++) {
for (int j = 0; j < _buildingBlocks.GetLength(1); j++) {
if (buildingBlocks[i, j] != null) {
_x0 = Math.Min(_x0, i);
_y0 = Math.Min(_y0, j);
_x1 = Math.Max(_x1, i + 1);
_y1 = Math.Max(_y1, j + 1);
}
}
}
}
public void DestroyBlocksAt(IEnumerable<Point> points)
{
//TODO: destroy hit blocks.
CalculateBounds();
}
}
After an object has been hit by a bomb, call CalculateBounds(); in order to recalculate the real size of the object. The idea is that instead of using invaderRect you would be using the BoundingBox property that reflects the real extents of the gaming object. BoundingBox takes into account the position of the game object on the screen (Location) and the position within the array (_x0, _x1, _y0, _y1).
This is a raw sketch. You may have to refine it and adapt it to your current logic. Don't use magical numbers like 15. Define constants like public const int BlockSize = 15;. This makes it easier to change the numbers later and also to understand the code.
Here is an example of an invader class
public class Invader : GameObject
{
private const int WIDTH = 10, HEIGHT = 7; // Width and height of invader in blocks.
public Invader()
{
_buildingBlocks = new BlockType[WIDTH, HEIGHT];
_x1 = WIDTH;
_y1 = HEIGHT;
_buildingBlocks[0, 0] = ...
...
}
}
UPDATE
As I understood your post, every movable “thing” is stored in its own 2D array storing the 15 x 15 pixel blocks it is made of. GameObject is the base class of all the visible movable things, like invaders, space ships, bombs and so on and is a wrapper around the 2D arrays (named _buildingBlocks in my code examples). It adds the logic needed to determine the real bounds of objects after they have been hit by bombs. CalculateBounds recalculates the position and size of the remaining object after a bomb hit within the 2D array (of course you must call it every time the shape of the objects changes). BoundingBox moves these internal 2D array bounds (stored in _x0, _x1, _y0 and _y1) to real screen positions (stored in the Location property) by multiplying with the block size (the 15 pixels) and adding the screen location.
For every game object type (i.e. movable shape type) you have to derive a class (e.g. a class Invader for invaders). Then create an invader object with Invader invader = new Invader(); for every single invader. GameObject is not the main class. The main class includes the game loop and game logic. It creates game objects and calculates their new positions (stored in the Location property) as they move around. Instead of working with invaderRect the wall-hitting logic would now work with BoundingBox which returns the real size and positions of the objects.
Rectangle invaderBounds = invader.BoundingBox;
bool isLeftWallHit = invaderBounds.Left <= 0;
bool isRightWallHit = invaderBounds.Right >= screenRectangle.Width;
bool isUpperWallHit = invaderBounds.Top <= 0;
bool isLowerWallHit = invaderBounds.Bottom >= screenRectangle.Height;
Right now, I'm working on a little Jump'n'Run game in XNA. I decided to load textures from a tileset,
and designed a method for getting the source rectangles of a bigger rectangle. Within the method, the calculated rectangles are being added to a list, and the list is returned at the end of the method. However, the method doesn't seem to work properly. When trying to get a rectangle from a given index out of the list, I'm getting a index out of range exception. I tried this with several numbers, it still wont work. here is my method for getting the rectangles:
public static List<Rectangle> getSourceRectangles(Rectangle original, int partWidth, int partHeight)
{
List<Rectangle> rlist = new List<Rectangle>();
int ix, iy;
ix = original.Width / partWidth;
iy = original.Height / partHeight;
for (int x = 0; x < ix; x++)
{
for (int y = 0; y < iy; y++)
{
Rectangle r = new Rectangle(x * partWidth, y * partHeight, partWidth, partHeight);
rlist.Add(r);
}
}
return rlist;
}
I save the return value in a global Container class.
this is were I get the error: (ist just the constructor of the NormalTile class!)
public NormalTile(int _TextureIndex,Rectangle _rect)
{
this.rect = _rect;
this.TileTextureIndex = _TextureIndex - 1;
this.tex_rect = Global.TileTextures[TileTextureIndex];//HERE
}
Why is this even Happening?
I am making a simple process which will create a colour changing spiral so I can test a range of variables and learn some C#. However, though I can not find a problem in the code, when I debug it, I am returned with a blank blue screen. Can any one find the problem. Here is all the code containing the variables needed for the ball point:
Vector2 Tripos;
List<Color> Tricol;
List<Vector2> datatripos;
List<int> count;
Color currentcol;
float Tri_angle;
float Triscale;
int Tri_speed;
int screenwidth;
int screenheight;
int addtocount;
Texture2D Ball;
int colourchangespeed;
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
Tripos = new Vector2(screenwidth / 2, screenheight / 2);
Triscale = 1;
Tri_angle = 1;
Tri_speed = 1;
colourchangespeed = 1;
Ball = Content.Load<Texture2D>("ball");
currentcol = new Color(0, 0, 0);
addtocount = 0;
datatripos = new List<Vector2>();
Tricol = new List<Color>();
}
This is called by the update method:
private void Posgen()
{
Tripos.X += (float)Math.Sin(MathHelper.ToRadians(Tri_angle))*Tri_speed;
Tripos.Y += (float)Math.Cos(MathHelper.ToRadians(Tri_angle))*Tri_speed;
Tri_angle++;
}
private void colchanger()
{
currentcol.R += (byte)colourchangespeed;
if (currentcol.R == 255)
{
currentcol.R = 0;
currentcol.G += (byte)colourchangespeed;
}
if (currentcol.G == 255)
{
currentcol.G = 0;
currentcol.B += (byte)colourchangespeed;
}
if (currentcol.B == 255)
{
currentcol.B = 0;
}
}
private void dataadd()
{
addtocount++;
Tricol.Add(currentcol);
datatripos.Add(Tripos);
count.Add(addtocount);
}
Called by the draw method:
private void drawtri()
{
foreach (Vector2 data in datatripos)
{
spriteBatch.Draw(Ball, data, null, currentcol, 0, new Vector2(5, 5), Triscale, SpriteEffects.None, 0);
}
}
If you want the full code ask in advance. There are some variables I don't use but I intend to use for later so ignore these.
Thanks in advance.
Yours Mona.
Check that you are calling dataadd().
As a precaution, colourchangespeed should be declared as a byte rather than as an int because you're only casting to a byte anyway. When checking you colour values, the current value should be compared as >= 255 in the case that colourchangespeed is not 1.
A tip to writing good questions: Remove everything that's unrelated. Everything.
So what I'm trying to do is create like a random image from panels of different colors. The user can choose how many panels (i.e. pixels) he wants to have and the number of different colors and then the program automatically generates that image. I'd really like to use panels for this because I will need this picture later on and need to modify every single pixel. As I'm comfortable with panels, I'd like to keep them and not use anything else.
So here's the code I'm using to create this panels:
//Creates two lists of panels
//Add items to list so that these places in the list can be used later.
//nudSizeX.Value is the user-chosen number of panels in x-direction
for (int a = 0; a < nudSizeX.Value; a++)
{
horizontalRows.Add(null);
}
//nudSizeY.Value is the user-chosen number of panels in y-direction
for (int b = 0; b < nudSizeY.Value; b++)
{
allRows.Add(null);
}
for (int i = 0; i < nudSizeY.Value; i++)
{
for (int j = 0; j < nudSizeX.Value; j++)
{
// new panel is created, random values for background color are assigned, position and size is calculated
//pnlBack is a panel used as a canvas on whoch the other panels are shown
Panel pnl = new Panel();
pnl.Size = new System.Drawing.Size((Convert.ToInt32(pnlBack.Size.Width)) / Convert.ToInt32(nudSizeX.Value), (Convert.ToInt32(pnlBack.Size.Height) / Convert.ToInt32(nudSizeY.Value)));
pnl.Location = new Point(Convert.ToInt32((j * pnl.Size.Width)), (Convert.ToInt32((i * pnl.Size.Height))));
//There are different types of panels that vary in color. nudTypesNumber iis the user-chosen value for howmany types there should be.
int z = r.Next(0, Convert.ToInt32(nudTypesNumber.Value));
//A user given percentage of the panels shall be free, i.e. white.
int w = r.Next(0, 100);
if (w < nudPercentFree.Value)
{
pnl.BackColor = Color.White;
}
//If a panel is not free/white, another rendom color is assigned to it. The random number determinig the Color is storede in int z.
else
{
switch (z)
{
case 0:
pnl.BackColor = Color.Red;
break;
case 1:
pnl.BackColor = Color.Blue;
break;
case 2:
pnl.BackColor = Color.Lime;
break;
case 3:
pnl.BackColor = Color.Yellow;
break;
}
}
//Every panel has to be added to a list called horizontal rows. This list is later added to a List<List<Panel>> calles allRows.
horizontalRows[j] = (pnl);
//The panel has also to be added to the "canvas-panel" pnl back. The advantage of using the canvas panel is that it is easier to determine the coordinates on this panel then on the whole form.
pnlBack.Controls.Add(pnl);
}
allRows[i] = horizontalRows;
}
As you might imagine, this is very slow when creating a checkerboard of 99x99 because the program has to loop through the process nearly 10000 times.
What would you to to improve performance? I said I'd like to keep doing it with panels because I'm comfortable with them, but if using panels is even more dumb than I thought, I'm open to other options. The program gets slower and slower the more panels it has already created. I guess that's because of the adding to the list that grows larger and larger?
This is how the output looks right now:
This is what I want to do with my "picture" later: I basically want to do Schellings model. That model shows how different groups of people (i.e. different colors) segregate when they want to have a certain percentage of people around them that belong to their group. That means that later on I have to be able to check for each of the panels/pixels what the neighbours are and have to be able to be able to change color of each pixel individually.
I don't want a ready solution, I'm just hoping for tips how to improve the speed of the picture-creating process.
Thank you very much
Instead of using Panels use a matrix to store your colors and other information you need.
In OnPaint event, use this matrix to draw the rectangles using GDI+.
Here is an example on how to draw 10x10 "pixels" if you have a matrix that contains colors:
private void myPanel_Paint(object sender, PaintEventArgs e)
{
for (var y=0; y < matrix.GetUpperBound(0); y++)
for (var x=0; x < matrix.GetUpperBound(1); x++)
{
var Brush = new SolidBrush(matrix[y,x]);
e.Graphics.FillRectangle(Brush, new Rectangle(x*10, y*10, 10, 10));
}
}
Use a picturebox to do your drawing. You've already got the code to see where each panel should be, just change it to draw a rectangle at each position. This way, you'll just be drawing a few rectangles on a board instead of working with 10.000 GUI objects.
Oh, keep your model/logic and view separated. Keep one matrix with all your information and just use a "Paint method" to draw it.
Your model could look something like this:
MyPanel[,] panels;
class MyPanel
{
Color color;
}
This way it's easy to check all neighbours of a panel, just check in the panels matrix.
And your view should just do something like this:
class View
{
Paint(MyPanel[,] panels)
{
//Draw
}
}
I think your best approach here is to write a custom Control class to draw the squares, and a custom collection class to hold the squares.
Your square collection class could look like this:
public sealed class ColouredSquareCollection
{
readonly int _width;
readonly int _height;
readonly Color[,] _colours;
public ColouredSquareCollection(int width, int height)
{
_width = width;
_height = height;
_colours = new Color[_width, _height];
intialiseColours();
}
public Color this[int x, int y]
{
get { return _colours[x, y]; }
set { _colours[x, y] = value; }
}
public int Width
{
get { return _width; }
}
public int Height
{
get { return _height; }
}
void intialiseColours()
{
for (int y = 0; y < _height; ++y)
for (int x = 0; x < _width; ++x)
_colours[x, y] = Color.White;
}
}
Then you write a custom control. To do so, add a new Custom control via Add new item -> Windows Forms -> Custom Control, and call it ColouredSquareHolder.
Then change the code to look like this. Notice how it is responsible for drawing all the squares:
public sealed partial class ColouredSquareHolder: Control
{
ColouredSquareCollection _squares;
public ColouredSquareHolder()
{
ResizeRedraw = true;
DoubleBuffered = true;
InitializeComponent();
}
public ColouredSquareCollection Squares
{
get
{
return _squares;
}
set
{
_squares = value;
Invalidate(); // Redraw after squares change.
}
}
protected override void OnPaint(PaintEventArgs pe)
{
base.OnPaint(pe);
if (_squares == null)
return;
int w = Width;
int h = Height;
int nx = _squares.Width;
int ny = _squares.Height;
var canvas = pe.Graphics;
for (int yi = 0; yi < ny; ++yi)
{
for (int xi = 0; xi < nx; ++xi)
{
int x1 = (xi*w)/nx;
int dx = ((xi + 1)*w)/nx - x1;
int y1 = (yi*h)/ny;
int dy = ((yi+1)*h)/ny - y1;
using (var brush = new SolidBrush(_squares[xi, yi]))
canvas.FillRectangle(brush, x1, y1, dx, dy);
}
}
}
}
Now you'll need to set up the square collection, add it to a ColouredSquareHolder and then add that to a form.
Firstly, add the ColouredSquareHolder to your test program and compile it so that it will show up in the Toolbox for the Windows Forms Editor.
Then create a new default Form called Form1, and from the Toolbox add a ColouredSquareHolder to it, and set the ColouredSquareHolder to Dock->Fill. Leave it called the default colouredSquareHolder1 for this demonstration.
Then change your Form1 class to look like this:
public partial class Form1: Form
{
readonly ColouredSquareCollection _squares;
readonly Random _rng = new Random();
public Form1()
{
InitializeComponent();
_squares = new ColouredSquareCollection(100, 100);
for (int x = 0; x < _squares.Width; ++x)
for (int y = 0; y < _squares.Height; ++y)
_squares[x, y] = randomColour();
colouredSquareHolder1.Squares = _squares;
}
Color randomColour()
{
return Color.FromArgb(_rng.Next(256), _rng.Next(256), _rng.Next(256));
}
}
Run your program and see how much faster it is at drawing the squares.
Hopefully this will give you the basis for something that you can build on.
Note: If you change the colours in the square collection, you will need to call .Invalidate() on the control in the form to make it redraw with the new colours.
well I suggest you using GDI+ instead , you can store your colors in a 2 dimensional array so you can draw the desired image based on that and also you can loop through them for further process , take a look at this code and also the demo project :
as you mentioned that you're not familiar with gdi+ , there is a demo project included so you can check it yourself and see how It's done in gdi+ :
demo project : ColorsTableDemoProject
Color[,] colorsTable;
Bitmap b;
Graphics g;
int size = 80; // size of table
int pixelWidth = 5; // size of each pixel
Random r = new Random();
int rand;
// CMDDraw is my Form button which draws the image
private void CMDDraw_Click(object sender, EventArgs e)
{
colorsTable = new Color[size, size];
pictureBox1.Size = new Size(size * pixelWidth, size * pixelWidth);
b = new Bitmap(size * pixelWidth, size * pixelWidth);
g = Graphics.FromImage(b);
for (int y = 0; y < size; y++)
{
for (int x = 0; x < size; x++)
{
rand = r.Next(0, 4);
switch (rand)
{
case 0: colorsTable[x, y] = Color.White; break;
case 1: colorsTable[x, y] = Color.Red; break;
case 2: colorsTable[x, y] = Color.Blue; break;
case 3: colorsTable[x, y] = Color.Lime; break;
default: break;
}
g.FillRectangle(new SolidBrush(colorsTable[x, y]), x * pixelWidth, y * pixelWidth, pixelWidth, pixelWidth);
}
}
pictureBox1.Image = b;
}