Can't get while loop to draw - c#

I'm keeping the drawing of each object in a seperate class and calling on it then in the main Draw Class. Anyway, I need to get this loop right because I'll be modelling a lot off it.
It will draw the first time for me but after that the xCoord doesn't seem to be moving for me or else there's something else wrong with the loop. There's no syntax errors or the like, the program runs, just not how I want it to!
Any help would be much appreciated...
/// <summary>
/// Draws the bottom platform (ground)
/// </summary>
public void DrawBottomPlatform()
{
int xCoord = 0;
int yCoord = (screenHeight / 10) * 9;
int width = screenWidth / 20;
int height = screenHeight / 20;
Rectangle bottomRectangle = new Rectangle(xCoord, yCoord, width, height);
int i = 0;
while (i <= 5)
{
spriteBatch.Draw(grassTexture, bottomRectangle, Color.White);
xCoord += bottomRectangle.Width;
i += 1;
}
}

You never use your updated xCoord, the value is simply ignored once the loops starts. Instead of updating xCoord, move the rectangle at each iteration:
int i = 0;
while (i <= 5)
{
spriteBatch.Draw(grassTexture, bottomRectangle, Color.White);
bottomRectangle.X += bottomRectangle.Width;
i += 1;
}

Related

Bitmap not acting properly within a loop

I am trying to get a bitmap to bounce off the top and bottom of a Window. Which seems to be working fine. The Draw method shown is being called within a loop, leading to the bitmap to be constantly redrawn with its new position.
public class Ball
{
double SpeedX = 4;
double SpeedY = 4;
int ballWidth = 1;
public double X {get; private set;}
public double Y {get; private set;}
public void Draw(Window screen)
{
X += SpeedX;
Y += SpeedY;
if (Y <= 0) {
SpeedY = -SpeedY;
Y = 0;
}
else if (Y >= (screen.Height - ballWidth)) {
SpeedY = -SpeedY;
Y = screen.Height - ballWidth;
}
Bitmap.Draw(X,Y);
}
}
However, I am trying to change the initial values SpeedX and Speedy when the bitmap is created. When I pass these values in, my bitmap get stuck on the Top/Bottom of the window - SpeedY appears to result in 0, whereas SpeedX continues to work (leading to the bitmap to slide across the Top/Bottom of the screen). Is there something I'm doing wrong? I don't see why passing in two values from outside would be messing up the Draw function.
public Ball RandomBall(Window game)
{
Ball newball = new Ball(_Game);
Random direction = new Random();
newball.SpeedX = 5;
newball.SpeedY = 5;
return newball;

How to Draw Lines in Xamarin Forms?

I am creating One Cross Platform Application in Xamarin Forms and try to draw lines from 10 to -10 using below code. But the problem is lines are drawn from 10 to 0 only. Why this is happening I don't have any Idea.
int margin = 20;
int steps = 20;
float start = margin;
float end = width - margin;
float dHeigth = heigth - (margin * 4);
float hStep = dHeigth / Convert.ToSingle(steps);
float textMargin = 30;
// draw the line
for (int i = 10; i >= -10; i--)
{
float xpoint = i * hStep + margin;
if (i.IsOdd())
{
canvas.DrawLine(start + textMargin, xpoint, end, xpoint, LineWhitePaint);
}
else
{
decimal dText = 0;
canvas.DrawLine(start + textMargin, xpoint, end, xpoint, LineGreyPaint);
if (i < 0)
dText = i;
else
dText = (10 - i);
string txt = dText.ToString();
canvas.DrawText(txt, start + margin, xpoint + 15, TextStyleFillPaintX);
}
}
I am attaching screen shot of that
For the positive lines, you are drawing 10 - i, which yields 0 for the first iteration, 2 for the third and so on. Regarding this, you can see, that you are beginning to draw the lines from the middle of the canvas. The tenth iteration will draw the topmost line (the one with the 10). Further lines are drawn, but not on the screen.
You can see this, too, when you are writing xPoint to the debug output. As i gets negative, xPoint will, too. To fix this, you'll have to offset xPoint to always draw on screen
float xpoint = i * hStep + margin + steps / 2 * hStep;
Alternatively, you could loop from 20 to 0 and change how the text is generated.
for (int i = 20; i >= 0; i--)
{
var xPoint = i * hStep + margin;
// ...
var displayedText = GetDisplayedText(i, steps);
// ...
}
string GetDisplayedText(int i, int steps)
{
var displayedValue = i > steps / 2
? steps - i
: -i - steps / 2; // I think this should be the formula
return displayedValue.ToString();
}
Remarks: It would even better to encapsulate the concept of the lines, to separate their calculation from draawing them. You could create a factory that generates the correct line based on the index and the number of steps and then only iterate over the Line objects, and draw them by passing the canvas. This would make your code way cleaner and neater.
UPDATE
Since we have been able to clarify the requirements, I will give another shot.
First of all, I'd define methods to transform graph coordinates to canvas coordinates
private SKPoint ToCanvasCoordinates(SKPoint graphCoordinates)
{
var x = Margin + TextMargin + (_canvas.Width - 2*Margin - TextMargin)*graphCoordinates.X;
var y = (MaxY - graphCoordinates.Y)*(_canvas.Height - 2 * Margin)/(MaxY - MinY) + Margin;
return new SKPoint(x,y);
}
private SKPoint GetLegendCoordinates(int i)
{
var x = Margin;
var y = (MaxY - graphCoordinates.Y)*(_canvas.Height - 2 * Margin)/(MaxY - MinY) + Margin + 15;
return new SKPoint(x,y);
}
_canvas is a private member field in this case, Margin, MaxY and MinY are properties. I've assumed the min of x being 0 and the max bein 1.
Now you can draw your lines like
for(int i = -1; i <= 10; i++)
{
var lineStart = ToCanvasCoordinates(new SKPoint(0, i));
var lineEnd = ToCanvasCoordinates(new SKPoint(1, i));
canvas.DrawLine(lineStart, lineEnd, LineGreyPaint);
var textPosition = GetLegendCoordinates(i);
canvas.DrawText(i.ToString(), textPosition, TextStyleFillPaintX);
}
Furthermore, if you'd like to draw a line between two of the grid lines, you can use the following methods
private void DrawDataLine(SKPoint start, SKPoint end, SKPaint paint)
{
var startTransformed = ToCanvasCoordinates(start);
var endTransformed = ToCanvasCoordinates(end);
_canvas.DrawLine(startTransformed, endTransformed, paint);
}
private void DrawData(SKPaint paint)
{
for(int i=1; i<_data.Length; i++)
{
DrawDataLine(new SKPoint(data[i-1].X, data[i-1].Y), new SKPoint(data[i].X, data[i].Y)); // given that the objects in _data have the properties X and Y
}
}

Implementing snap to canvas gridlines

I'm a bit mind boggled at this, but i have the problem of trying to get my head around mouse snapping to a grid. Currently I'm drawing a grid by overriding OnRender like so;
protected override void OnRender(DrawingContext drawingContext)
{
int numberOfVerticalLines = 8;
int numberOfHorizontalLines = 8;
CellHeight = this.ActualHeight / numberOfVerticalLines;
CellWidth = this.ActualWidth / numberOfHorizontalLines;
double verticalOffset = 0;
double horizontalOffset = 0;
Pen pen = new Pen(Stroke, StrokeThickness);
pen.DashStyle = DashStyle;
for (int i = 0; i <= numberOfHorizontalLines; i++)
{
for (int j = 0; j <= numberOfVerticalLines; j++)
{
drawingContext.DrawLine(pen, new Point(horizontalOffset, verticalOffset), new Point(horizontalOffset, CellHeight + verticalOffset));
verticalOffset += CellHeight;
}
horizontalOffset += CellWidth;
verticalOffset = 0;
}
horizontalOffset = 0;
verticalOffset = 0;
for (int i = 0; i <= numberOfVerticalLines; i++)
{
for (int j = 0; j <= numberOfHorizontalLines; j++)
{
drawingContext.DrawLine(pen, new Point(horizontalOffset, verticalOffset), new Point(CellWidth + horizontalOffset, verticalOffset));
horizontalOffset += CellWidth;
}
verticalOffset += CellHeight;
horizontalOffset = 0;
}
}
And that gives the following result;
However i'm a little stuck with thinking about a route to take to snap the mouse to the nearest grid intersection (where a horizontal line meets a vertical line). Obviously as i'm using the drawingcontext to draw the lines i have no reference to these lines after they've been drawn.
So i guess essentially my question is, how can i go about implementing mouse snap to grid? Is this more of a maths question than an object orientated control question? I've read through almost every stack overflow question i found relevant but havn't been able to come to any realistic ideas yet.
Note: Whilst at the minute i've hard coded an 8x8 grid, this will be user defined eventually.
A basic approach is to compare the (x,y) of your mouse to the crosses by:
1. calculating the start- and endpoint of the width and height of the cell in which the mouse is located; and
2. comparing those two intervals (width and height) to the actual mouse (x,y) to find the nearest cell point.
Here is some quickly thrown together example code to demonstrate the snapping:
/// <summary>
/// When left shift key is pressed we snap the mouse to the closest
/// intersection
/// </summary>
void MainWindow_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.LeftShift)
{
var p = GetSnappingPoint(Mouse.GetPosition(this), new Size(200, 200));
SetCursorPos((int)p.X, (int)p.Y+20);
}
}
[DllImport("User32.dll")]
private static extern bool SetCursorPos(int x, int y);
/// <summary>
/// Get snapping point by
/// </summary>
Point GetSnappingPoint(Point mouse,Size cellSize)
{
//Get x interval based on cell width
var xInterval = GetInterval(mouse.X, cellSize.Width);
//Get y interal based in cell height
var yInterval = GetInterval(mouse.Y, cellSize.Height);
// return the point on cell grid closest to the mouseposition
return new Point()
{
X = Math.Abs(xInterval.Lower - mouse.X) > Math.Abs(xInterval.Upper - mouse.X) ? xInterval.Upper : xInterval.Lower,
Y = Math.Abs(yInterval.Lower - mouse.Y) > Math.Abs(yInterval.Upper - mouse.Y) ? yInterval.Upper : yInterval.Lower,
};
}
/// <summary>
/// Find an interval of the celsize based on mouse position and size
/// </summary>
Interval GetInterval(double mousePos,double size)
{
return new Interval()
{
Lower = ((int)(mousePos / size)) * size,
Upper = ((int)(mousePos / size)) * size + size
};
}
/// <summary>
/// Basic interval class
/// </summary>
class Interval
{
public double Lower { get; set; }
public double Upper { get; set; }
}
A rough start to an answer:
int nearGridX = CellWidth * Math.Round( mouseX / CellWidth);
int nearGridY = CellHeight * Math.Round( mouseY / CellHeight);
xPos= mouseX - (mouseX % gridWidth);
yPos= mouseY - (mouseY % gridHeight);
This would give you a quick and dirty snap to the upper-left corner of the current grid. This doesn't take into account for if you're closer to the bottom or right of the current grid. It only looks at what grid you're in and plops it there.

Draw sin(x)/x to PictureBox in animation mode by Timer component

I need to draw sin(x)/x graphic into PictureBox in animation mode by timer component. I have axes already on my picBox and graphic draws from 0;0. Also I have some code from this forum, but there my graphic draws FROM RIGHT TO THE LEFT, and I need to draw it FROM LEFT TO THE RIGHT. And I need to draw it in animation mode by timer. Could anybody help me?
Here's my drawing function:
private void drawStream()
{
const int scaleX = 35;
const int scaleY = 35;
Point picBoxTopLeft = new Point(0, 0);
Point picBoxTopLeftm1 = new Point(-1, 0);
int halfX = picBox.Width / 2;
int halfY = picBox.Height / 2;
Size size = new Size(halfX + 20, picBox.Height);
Graphics gr = picBox.CreateGraphics();
gr.TranslateTransform(halfX, halfY);
gr.ScaleTransform(scaleX, scaleY);
gr.ResetClip();
float lastY = (float)Math.Sin(0);
float y = lastY;
Pen p = new Pen(Color.Red, 0.015F);
float stepX = 1F / scaleX;
for (float x = 0; x < 15; x += stepX)
{
gr.CopyFromScreen(picBox.PointToScreen(picBoxTopLeft), picBoxTopLeftm1, size, CopyPixelOperation.SourceCopy);
y = (float)Math.Sin(x);
gr.DrawLine(p, -stepX, lastY, 0, y);
lastY = y;
}
}
Thanx a lot.
P.S. Sorry for my English, I'm Ukrainian.
It looks like the line:
gr.DrawLine(p, -stepX, lastY, 0, y);
Will always draw a line from (-stepX, lastY) to (0, y). Only the Y coordinates of these points change during your loop which doesn't look like what you want.
Moreover, you're stepping in the X direction by stepX which is defined as being 1 / 35.0f. This means you're stepping 35 times per whole pixel; a bit excessive. Get rid of your ScaleTransform and instead scale your independent variable (x) to get a more sensible frequency. You should probably also increase your amplitude to get a good looking curve as well.
I think your drawing loop should look more like:
for (int x = 1; x < halfX; x += 1)
{
y = (float) amplitude * Math.Sin(x * stepX);
gr.DrawLine(p, x - 1, lastY, x, y);
lastY = y;
}
This will draw from the origin (0,0) to the right side of the picture box at once. To animate this you're going to need to take this code and instead of looping all the way to halfX you want to loop only part of the way and keep track of where you the next time your Timer fires it's event.
edit:
Every time you create a Pen object you take a handle from Windows through GDI. These handles are only returned to be reused when you Dispose() the Pen object. If you create a new Pen every time you draw and don't dispose them you'll run out of handles eventually!
To be safe when using these types of objects (Pen, Brush, Font, and more need to be disposed) wrap them in a using statement:
using (Pen pen = new Pen(Color.Red, 0.015f)) {
// ... use the pen here
}
// After here it is Disposed and cannot be accessed
Ok, spent 20 minutes to try it myself.
public Form1()
{
InitializeComponent();
_bitmap = new Bitmap(pictureBox1.Width, pictureBox1.Height);
pictureBox1.Image = _bitmap;
Timer timer = new Timer();
timer.Tick += new EventHandler(timer_Tick);
timer.Interval = 10;
timer.Start();
}
private int x = 0;
void timer_Tick(object sender, EventArgs e)
{
if(++x < _bitmap.Width)
{
int y = _bitmap.Height/2
- ((int)(Math.Sin(x * (Math.PI * 2) / _bitmap.Width) * (_bitmap.Height / 2)));
//Small rounding error
if (y > _bitmap.Height - 1)
y = _bitmap.Height - 1;
_bitmap.SetPixel(x, y, Color.Black);
}
pictureBox1.Image = _bitmap;
}
Tested and working.
You should really add Points to a GraphicsPath and just paint that.
Also: You should not create a Graphics object like that. Get one from the Bitmap or use the one supplied by the Paint event.
1) Right to left. Just change your for loop.
for (float x = 0; x < 15; x += stepX)
becomes
for (float x = size.Width; x >= 0; x
-= stepX)
2) A timer.
Simple solution is to just add a sleep.
for (float x = 0; x < 15; x += stepX)
Thread.Sleep(500); //draw...

Drawing Isometric Tilemaps

I've been working on drawing an isometric map with C# / XNA Game Studio, and while I've gotten pretty far it doesn't look quite right and I was wondering if anybody can help.
Here's the code I have for drawing the map:
public void Draw(SpriteBatch theBatch, int drawX, int drawY)
{
if ((drawY % 2 == 0))
theBatch.Draw(tileTexture, new Rectangle((drawX * width), (drawY * length / 2), width, length), Color.White);
else
theBatch.Draw(tileTexture, new Rectangle(((drawX * width) + (width / 2)), (drawY * length / 2), width, length), Color.White);
}
The code within this method acts as if it were inside a nested for loop, drawing left to right, top to bottom. When the y-value is odd, the row is shifted over to fit, however it looks a bit off.
This is the produced output for an 8x5 map:
As you can see, it doesn't quite look right, and I'm not sure if its an issue with the math in my code, or if it has to do with the order everything is being drawn in. I'm very new to C# and working with sprites, so any help would be greatly appreciated.
Because it might be helpful, here is the other relevant parts of code which draw the map.
The entire Tile Class:
public class Tile
{
// Dimension variables
int height;
int width;
int length;
String type;
Texture2D tileTexture;
Vector2 coordinates;
///
/// Tile Constructor
///
public Tile(ContentManager theContent, String theType, Vector2 theCoordinates)
{
width = 68;
length = 46;
type = theType;
coordinates = theCoordinates;
// Sets the right texture to the texture ref
if (theType == "grass")
tileTexture = theContent.Load<Texture2D>(#"Tiles\iso_grass");
}
///
/// Draws the tile at the given location
///
public void Draw(SpriteBatch theBatch, int drawX, int drawY)
{
if ((drawY % 2 == 0))
theBatch.Draw(tileTexture, new Rectangle((drawX * width), (drawY * length / 2), width, length), Color.White);
else
theBatch.Draw(tileTexture, new Rectangle(((drawX * width) + (width / 2)), (drawY * length / 2), width, length), Color.White);
}
}
The TileRow class, which holds one row of tiles.
public class TileRow
{
public List<Tile> Row = new List<Tile>();
public int rowLength;
public TileRow(int theLength, int yIndex, ContentManager theContent)
{
rowLength = theLength;
Tile thisTile;
// Here the tiles are created and added to the row
for (int x = 0; x < rowLength; x++)
{
thisTile = new Tile(theContent, "grass", new Vector2(x, yIndex));
Row.Add(thisTile);
}
}
///
/// Draw -- invokes the draw method of each tile in the row
///
public void DrawRow(SpriteBatch theBatch, int currentY)
{
for (int x = 0; x < rowLength; x++)
{
Row[x].Draw(theBatch, currentY, x);
}
}
}
}
and the MapStruct class, which holds all the rows
public class MapStruct
{
public List<TileRow> allRows = new List<TileRow>();
int depth;
// Constructor
public MapStruct(ContentManager theContent, int theDepth, int rowLength)
{
depth = theDepth;
TileRow thisRow;
// Here we make a row of tiles for each level of depth
for (int y = 0; y < depth; y++)
{
thisRow = new TileRow(rowLength, depth, theContent);
allRows.Add(thisRow);
}
}
///
/// Draw - this method invokes the draw method in each tile row, which then draws each tile
///
public void DrawMap(SpriteBatch theBatch)
{
for (int y = 0; y < depth; y++)
{
allRows[y].DrawRow(theBatch, y);
}
}
}
Any help on how I could fix this issue, as well as advice on how I could improve my code would be greatly appreciated!
Looks like your loop adds a little to much to the Y on each row.
I found this variable in your Tile function.
length = 46;
I havent checked, but I believe "length" is the height of the tile? if so, try ajusting it a bit. Perhaps, you've forgotten to minus the height of the tile. So if the side of the tile is like 6 pixels, then the length for offset pr. row is only 40.
Also remember to plot from top and down, since the tiles nearest camera has to be plotted last, to make the depth illusion.
I beleive BerggreenDK is right, but your comment make it seem you misunderstood his answer. Your tiles need to be drawn at an Y offset that only includes the green surface areas "screen height". So, draw the full tile size, but offset the rows with length - 5 (Where 10 is the estimated offset and 5 is half that, as each row should be offset by only half the distance).
theBatch.Draw(tileTexture, new Rectangle((drawX * width), (drawY * (length) / 2), width, length - 5), Color.White);
...if I got the parameters right.
Also, the rows need to be drawn from back to front, but they seem to be drawn from left to right? I don't know XNA, so can't show code to fix that...when I look closely some tiles in a row "further away" is over-drawn by a tile "closer". I don't know XNA and don't get how the drawing order is determined, so can't offer a fix for that...

Categories

Resources