I'm developing an application that simulates traffic patterns and represents each vehicle as a colored rectangle.
As the program is running, each Vehicle's position is incremented based on speed until it gets too close to the vehicle ahead of it.
private void SlowDown(int carBehind, int carAhead, List<Vehicle> vehicles)
{
vehicles[carBehind].SetActualVelocity(vehicles[carAhead].GetVelocity());
}
Then I increment each Vehicle's position:
private void AdvancePosition(List<Vehicle> vehicles)
{
Vehicle tempVehicle;
double velocity = 0;
int position = 0;
int newPosition = 0;
for(int i = vehicles.Count - 1; i >= 0; i--) //starting with forward vehicles first
{
tempVehicle = vehicles[i];
velocity = tempVehicle.GetVelocity() * 1.466667 / 33; //convert mph to fps then allow for 30 frames per second
position = tempVehicle.GetPosition();
newPosition = (int)(velocity + position);
tempVehicle.SetPosition(newPosition);
}
}
This works great until Vehicles go from off-screen to on-screen (or position negative to position positive). They begin to creep up on each other and eventually overlap even though I set their speeds to be equal.
Here is my code for painting the graphics
private void paintTraffic(Road road, List<Vehicle> vehicles)
{
using (Graphics g = CreateGraphics())
{
Vehicle tempVehicle;
int x = 0;
int y = 0;
int index = 0;
int vehicleSize = 0;
Refresh();
for (int i = 0; i < vehicles.Count; i++)
{
/*
* I tried using this condition but I'm still seeing the problem
*if(x < vehicleSize)
*{
* vehicleSize = x;
*}
*/
tempVehicle = vehicles[i];
x = tempVehicle.GetPosition();
y = tempVehicle.GetLane() * 100;
vehicleSize = tempVehicle.GetSize();
g.FillRectangle(new SolidBrush(color), x, y, vehicleSize, 20);
}
}
}
Each Vehicle is painted as a small rectangle where the Vehicle's position is the front of the Vehicle.
My question is: This doesn't seem like a calculation error by the program because the Vehicle's speed is set to that of the Vehicle in front of it.
Does this sound like a Graphics bug or a strange calculation error?
Related
i have somewhat implemented marching cubes in unity/c# (you dont need to know unity to help me though) and i cant stop feeling like i have made a big mistake in my code because it is so slow. i am already running it on a separate thread but it just takes ages to complete. please help me optimize my code.
private void _UpdateChunk()
{
lock (this)
{
// clear the tri, vert and uv lists
ClearMeshData();
// Loop through each "cube" in the terrain.
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
for (int z = 0; z < width; z++)
{
// Create an array of floats representing each corner of a cube and get the value from our terrainMap.
float[] cube = new float[8];
float[] strengths = new float[8];
for (int i = 0; i < 8; i++)
{
Vector3Int corner = new Vector3Int(x, y, z) + gamedata.CornerTable[i];
cube[i] = terrainMap[corner.x, corner.y, corner.z].BlockType;
strengths[i] = terrainMap[corner.x, corner.y, corner.z].Strength;
}
// Pass the value into the MarchCube function.
MarchCube(new Vector3(x, y, z), cube, strengths);
}
}
}
}
}
void MarchCube(Vector3 position, float[] cube, float[] strengths)
{
// Get the configuration index of this cube.
int configIndex = GetCubeConfiguration(cube);
// If the configuration of this cube is 0 or 255 (completely inside the terrain or completely outside of it) we don't need to do anything.
if (configIndex == 0 || configIndex == 255)
return;
// Loop through the triangles. There are never more than 5 triangles to a cube and only three vertices to a triangle.
int edgeIndex = 0;
Vector3 vert1 = new Vector3();
Vector3 vert2 = new Vector3();
float vert1sample = 0;
float vert2sample = 0;
float lerp = 0;
int indice = 0;
for (int i = 0; i < 5; i++)
{
for (int p = 0; p < 3; p++)
{
// Get the current indice. We increment triangleIndex through each loop.
indice = gamedata.TriangleTable[configIndex, edgeIndex];
// If the current edgeIndex is -1, there are no more indices and we can exit the function.
if (indice == -1)
return;
// Get the vertices for the start and end of this edge.
vert1 = position + gamedata.EdgeTable[indice, 0];
vert2 = position + gamedata.EdgeTable[indice, 1];
vert1sample = strengths[gamedata.EdgeIndexTable[indice, 0]];
vert2sample = strengths[gamedata.EdgeIndexTable[indice, 1]];
// Get the midpoint of this edge.
lerp = Mathf.Abs(vert1sample) / (Mathf.Abs(vert2sample) + Mathf.Abs(vert1sample));
Vector3 vertPosition = Vector3.Lerp(vert1, vert2, lerp);
// Add to our vertices and triangles list and incremement the edgeIndex.
vertices.Add(vertPosition);
triangles.Add(vertices.Count - 1);
if (getChunkVoxel(vert1 + chunkPosition) != 0)
{
uvs.Add(new Vector2(getChunkVoxel(vert1 + chunkPosition) - 1, 0));
}
else
{
uvs.Add(new Vector2(getChunkVoxel(vert2 + chunkPosition) - 1, getChunkVoxel(vert2 + chunkPosition) - 1));
}
edgeIndex++;
}
}
}
int GetCubeConfiguration(float[] cube)
{
// Starting with a configuration of zero, loop through each point in the cube and check if it is below the terrain surface.
int configurationIndex = 0;
for (int i = 0; i < 8; i++)
{
// If it is, use bit-magic to the set the corresponding bit to 1. So if only the 3rd point in the cube was below
// the surface, the bit would look like 00100000, which represents the integer value 32.
if (cube[i] < terrainSurface)
configurationIndex |= 1 << i;
}
return configurationIndex;
}
it appears that this is the part that slows my game down, help would be appreciated
i already made it faster by changing terrainpoint from a class to a struct but it is still very slow.
One main reason it is slow is that there is a lot of allocations in the loop putting a lot of pressure on the garbadge collector. There is currently 11 allocation per "cube" in the terrain in _UpdateChunk and up to 17 in MarchCube (possibly even more if the expressions like position + gamedata.EdgeTable[indice, 0] allocates a new vector). This is not reasonable. Many allocation are not needed. For example cube and strengths can be preallocated once for all the cubes in the beginning of _UpdateChunk. You do not need to allocate the vector in the expression to compute corner: you can just compute the components separately manually (or you can possibly preallocate the vector and reset its component when needed). The same thing applies for the new Vector3(x, y, z) can can be preallocated and set in the loop. Such an algorithm is computationally intensive so you should get away any overhead like virtual method calls and allocations/GC -- only low-level arrays accesses and mathematical operations should remains.
Note that some computations can be optimized. For example GetCubeConfiguration can be modified so to be branchless. Mathf.Abs(vert1sample) can be precomputed so not to compute it twice (though the compiler may already do that). I am also wondering if the expression like vertices.Add are efficient but this is dependent of the type of container which is not provided here.
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;
I am developing a C# win form GUI for controlling a two-motor XY stage. I have drawn a 100 x 100 square grid pattern on a picturebox in which each square, when clicked, represents a coordinate that the two motors must move to. I have studied this link
PictureBox Grid and selecting individual cells when clicked on and this PictureBox- Grids and Filling in squares (Game of Life) for drawing a grid and marking the clicked positions.
Now I have to transform the series of randomly clicked points to actual movement of the two motors.
How shall I translate the click coordinates programmatically to give commands to control the motors?
I know how to move and control the motors without referring to the screen coordinates, i.e. by using eyes.
Thank you very much for your kind help.
Update1:
Hello... I think I am thinking too much in a confusing way to move the motors from one point to another despite Sebastien's great help. I wanted to try some logic below but I appreciate if somebody can enlighten me how best to implement this.
private void pictureBoxGrid_MouseClick(object sender, MouseEventArgs e)
{
//int x = e.X;
int x = cellSize * (e.X / cellSize);
int y = cellSize * (e.Y / cellSize);
int i = x / 8; // To limit the value to below 100
int j = y / 8;
// Reverse the value of fill_in[i, j] - if it was false, change to true,
// and if true change to false
fill_in[i, j] = !fill_in[i, j];
if (fill_in[i, j])
{
//Save the coordinate in a list
filledSq.Add(new Point(i, j));
using (Graphics g = Graphics.FromImage(buffer))
{
g.FillRectangle(Brushes.Black, x + 1, y + 1, 7, 7);
}
}
else
{
//Delete the coordinate in a list
filledSq.Remove(new Point(i, j));
Color customColor = SystemColors.ControlLightLight;
using (Graphics g = Graphics.FromImage(buffer))
using (SolidBrush shadowBrush = new SolidBrush(customColor))
{
g.FillRectangle(shadowBrush, x + 1, y + 1, 7, 7);
}
}
//pictureBoxGrid.BackgroundImage = buffer;
pictureBoxGrid.Invalidate();
}
private void buttonSavePoints_Click(object sender, EventArgs e)
{
// to be implemented...
}
private void buttonRun_Click(object sender, EventArgs e)
{
var noOfDots = filledSq.Count;
filledSq = filledSq.OrderBy(p => p.X).ThenBy(p => p.Y).ToList();
var motor = new Motor();
for (var i = 0; i < noOfDots; i++)
{
motor.Move(filledSq[i].X, filledSq[i].Y); //call the motor to move to X,Y here?
//do sth at each position
}
}
Since you wrote, that you know how to move the motors programmatically this answer will be more theoretical:
Each steppermotor has a predefined anglewidth per step (e.g. 1.8°).
And if you know where your Motors are (for example at a predefined starting point with limitswitches (0|0)) you can calculate where they need to be.
For the precision there are multiple factors like if you are using Belts or threaded rods.
An examplemethod could look like this:
private static float stepwidth = 1.8;
private static beltConverionPerDegree = 0.2; // Or rod
private float currentPositionX = 0;
private float currentPositionY = 0;
public Tuple<int, int> GetSteps(float x, float y) {
// calculate the position relative to the actual position (Vector between two points)
float relativeX = x - currentPositionX;
float relativeY = y - currentPositionY;
return new Tuple<int, int> (relativeX / (stepwidth * beltConverionPerDegree), relativeY / (stepwidth * beltConverionPerDegree));
}
beltConverionPerDegree means how much distance your motor moves the belt for each degree.
I'm struggling to set the values of an element at a specific index within a list to a new value.
The list is of type object Rectangle and i get the following error when I try change any of the values of the rectangle in the list e.g.
Property or indexer 'System.Drawing.Rectangle.Bottom' cannot be
assigned to -- it is read only
I've tried converting the list to an array but i still run into the same issue of the values being read-only.
Basically the app takes in a user defined number of rectangles and draws the rectangles with varying widths and heights but along the same baseline. The code im trying to implement needs to take those rectangles and redraw them vertically from the base upwards, while keeping the same number of rectangles and keeping the same outer shape as the previous rectangles created.
Code:
public void RotateRectangles(List<Rectangle> Rectangles, int startPositionX, int userInput, Graphics DrawingSurface)
{
Graphics RectangleGraphics = DrawingSurface;
try
{
// loop in reverse to compare one rectangle to all the other rectangles in the vector
for (int i = Rectangles.Count - 1; i > -1; --i)
{
bool mustChange = true;
for (int t = Rectangles.Count - 1; t > -1; --t)
{
// only compare if the current position in the vector A if different to the position in vector B.
if (i > t)
{
if (mustChange == true)
{
// If the top Y coordinate of RECT at Position i in vector A is bigger than Y coordinate
// at Position t in vector B
if (Rectangles[i].Top >= Rectangles[t].Top)
{
//adjusting points accordingly
Rectangles[i].Left = (Rectangles[t].Left);
Rectangles[t].Bottom = (Rectangles[i].Top);
}
else
{
// If the Y coordinate is not bigger, then we need to stop checking
mustChange = false;
}
}
}
}
}
// loop forward to compare one rectangle to all the other rectangles in the vector
for (int i = 0; i < Rectangles.Count; ++i)
{
bool forwardChange = true;
for (int t = 0; t < Rectangles.Count; ++t)
{
// If the top Y coordinate of RECT at Position i in vector A is bigger than Y coordinate at Position t
// in vector B AND the two rectangales touch
if (i < t && Rectangles[i].Top <= Rectangles[t].Bottom)
{
if (forwardChange == true)
{
// If the top Y coordinate of RECT at Position i in vector A is bigger than Y coordinate at Position t
// in vector B
if (Rectangles[i].Top > Rectangles[t].Top)
{
//adjusting points accordingly
Rectangles[i].Right = (Rectangles[t].Right);
Rectangles[t].Bottom = (Rectangles[i].Top);
}
else
{
// If the Y coordinate is not bigger, then we need to stop checking
forwardChange = false;
// Addjust the Y position of each rectangle so it does not overlap with the first drawing
for (int z = 0; z < Rectangles.Count; ++z)
{
Rectangles[z].Top = (250 - Rectangles[z].Top);
Rectangles[z].Bottom = (250 - Rectangles[z].Bottom);
}
}
}
}
}
}
for (int z = 0; z < Rectangles.Count; ++z)
{
Rectangle DrawRec = myRectangleClass.MyRectangle(Rectangles[z].Left, Rectangles[z].Top, Rectangles[z].Right, Rectangles[z].Bottom);
RectangleGraphics.DrawRectangle(Pen, DrawRec);
ReadWrite.writeOutput(Rectangles[z].Left, Rectangles[z].Top, Rectangles[z].Right, Rectangles[z].Bottom);
}
}
catch (Exception e)
{
}
}
The parts that are giving the errors are:
Rectangles[i].Left = (Rectangles[t].Left);
Rectangles[t].Bottom = (Rectangles[i].Top);
Rectangles[i].Right = (Rectangles[t].Right);
Rectangles[t].Bottom = (Rectangles[i].Top);
Rectangles[z].Top = (250 - Rectangles[z].Top);
Rectangles[z].Bottom = (250 - Rectangles[z].Bottom);
Please can someone help me out or at least direct me in the right direction
The properties Right, Left, Top, Bottom are read-only. You can use the methods Offset and Inflate, and the properties Location, Size, Width and Height, to adjust the position and size of the rectangle, or you can replace an existing rectangle with a newly created one.
e.g.
Rectangle ri = Rectangles[i];
Rectangle rt = Rectangles[t];
Rectangle[i] = new Rectangle( rt.Left, ri.Bottom, rt.Height, rt.Width );
the problem is less the value type of the rectangle, more that the List[] operator returns a new value object.
List<Rectangle> a;
a[4].X += 4; // does the same like the following code:
var r = a[4]
r.X += 4; // will change r, but not a[4]
so you need to store back the rectangle value in the list
Rectangles[i] = new Rectangle(Rectangles[t].Left, Rectangles[i].Top, Rectangles[i].Width, Rectangles[i].Height);
Assign a new rectangle to the wanted index.
Okay, I have a 2d Tile Map editor I'm working on in xna c#.
In the Draw method I loop through (with a 'for' loop) my 2 dimensional array of tiles so that
my map updates and draws all the tiles every frame.
My question is, how do you draw only the tiles that are seen on screen.
Also is there a better way to draw the tile map (Rather than updating every frame).
In the platformer demo I played around with the visible tiles were calculated and then only those tiles were drawn. I believe you will have to include them in the draw method to be drawn in each time.
Here is a snippet (this only had left to right scrolling so no vertical range was calculated). This kept track of the camera position to calculate it.
Edit:: Added the second method shows how it updated camera position based on the player position stored in a player object.
private void DrawTiles(SpriteBatch spriteBatch)
{
// Calculate the visible range of tiles.
int left = (int)Math.Floor(cameraPosition / Tile.Width);
int right = left + spriteBatch.GraphicsDevice.Viewport.Width / Tile.Width;
right = Math.Min(right, Width - 1);
// For each tile position
for (int y = 0; y < Height; ++y)
{
for (int x = left; x <= right; ++x)
{
// If there is a visible tile in that position
Texture2D texture = tiles[x, y].Texture;
if (texture != null)
{
// Draw it in screen space.
Vector2 position = new Vector2(x, y) * Tile.Size;
spriteBatch.Draw(texture, position, Color.White);
}
}
}
}`
private void ScrollCamera(Viewport viewport)
{
const float ViewMargin = 0.35f;
// Calculate the edges of the screen.
float marginWidth = viewport.Width * ViewMargin;
float marginLeft = cameraPosition + marginWidth;
float marginRight = cameraPosition + viewport.Width - marginWidth;
// Calculate how far to scroll when the player is near the edges of the screen.
float cameraMovement = 0.0f;
if (Player.Position.X < marginLeft)
cameraMovement = Player.Position.X - marginLeft;
else if (Player.Position.X > marginRight)
cameraMovement = Player.Position.X - marginRight;
// Update the camera position, but prevent scrolling off the ends of the level.
float maxCameraPosition = Tile.Width * Width - viewport.Width;
cameraPosition = MathHelper.Clamp(cameraPosition + cameraMovement, 0.0f, maxCameraPosition);
}
int MapSizeX = 20;
int MapSizeY = 20;
int LeftCornerX = 0; //the position of the Tile in the 2Darray that is going
int LeftCornerY = 0; //to be drawn in the left corner of the screen.
int ScreenSizeX = 10;
int ScreenSizeY = 10;
public Tiles[,] tiles = new Tile[MapSizeX, MapSizeY]; //list of all Tiles
//then you can draw it like this....
int counterX = 0; //represents the position on screen
int counterY = 0;
// y and x inside the for loops represents the position in tiles
for(int y = LeftCornerY; y < MapSizeY < y++)
{
for(int x = LeftCornerX; y < MapSizeX < x++)
{
if(counterX < ScreenSizeX && counterY < ScreenSizeY)
{
tiles[x, y].draw(tiles[counterX , counterY]);
}
counterX ++;
//when you do like this you draw the tiles you want
//at the position you want. In the draw method you just
// drawn the tile you want at the position of the tile you
// send as in parameter to the draw method.
}
counterY++;
counterX = 0;
}
then you just have to increase the LeftCorner variables to draw another part of the map