I'm building a tile engine for my first game ever and i've been following a guide step by step (can't link it because i'm limited to 2). I have however, made a few modifications to the tilemap so that i can continue with my project.
And while testing my program a noticed a bug that i was hoping you guys could help me resolve...
...The tile engine is drawing the wrong tiles!
This is my TileMap (with numbers assigned to each tile) and this is what my engine draws.
While it should follow the order as seen below (the code is self-explanatory)
rows[0].columns[3].tileID = 0;
rows[0].columns[4].tileID = 1;
rows[0].columns[5].tileID = 2;
rows[0].columns[6].tileID = 3;
rows[0].columns[7].tileID = 4;
rows[1].columns[3].tileID = 5;
rows[1].columns[4].tileID = 6;
rows[1].columns[5].tileID = 7;
rows[1].columns[6].tileID = 8;
rows[1].columns[7].tileID = 9;
rows[2].columns[3].tileID = 10;
rows[2].columns[4].tileID = 11;
rows[2].columns[5].tileID = 12;
rows[2].columns[6].tileID = 13;
rows[2].columns[7].tileID = 14;
rows[3].columns[3].tileID = 15;
rows[3].columns[4].tileID = 16;
rows[3].columns[5].tileID = 17;
rows[3].columns[6].tileID = 18;
rows[3].columns[7].tileID = 19;
rows[4].columns[3].tileID = 20;
rows[4].columns[4].tileID = 21;
rows[4].columns[5].tileID = 22;
rows[4].columns[6].tileID = 23;
rows[4].columns[7].tileID = 24;
rows[5].columns[3].tileID = 25;
rows[5].columns[4].tileID = 26;
rows[5].columns[5].tileID = 27;
rows[5].columns[6].tileID = 28;
rows[5].columns[7].tileID = 29;
Code Essentials
The Important parts of my code
Tile.class
Where i define the tile size and have a function which navigates the tilemap to find my deiserd tile.
namespace TileEngine
{
static class Tile
{
static public Texture2D tileSetTexture;
static public int tileWidth = 48;
static public int tileHeight = 48;
static public Rectangle getSourceRectangle(int tileIndex)
{
int tileY = tileIndex / (tileSetTexture.Width / tileWidth);
int tileX = tileIndex % (tileSetTexture.Height / tileHeight);
return new Rectangle(tileX * tileWidth, tileY * tileHeight, tileWidth, tileHeight);
}
}
}
MapCell
Where i define the tileID
class MapCell
{
public List<int> baseTiles = new List<int>();
public int tileID
{
get { return baseTiles.Count > 0 ? baseTiles[0] : 0; }
set
{
if (baseTiles.Count > 0)
baseTiles[0] = value;
else
addBaseTile(value);
}
}
public void addBaseTile(int tileID)
{
baseTiles.Add(tileID);
}
public MapCell(int tileID)
{
this.tileID = tileID;
}
}
TileMap
Where i build the map
class MapRow
{
public List<MapCell> columns = new List<MapCell>();
}
class TileMap
{
public List<MapRow> rows = new List<MapRow>();
public int mapWidth = 50;
public int mapHeight = 50;
public TileMap()
{
for (int y = 0; y < mapHeight; y++)
{
MapRow thisRow = new MapRow();
for (int x = 0; x < mapWidth; x++)
{
thisRow.columns.Add(new MapCell(0));
}
rows.Add(thisRow);
}
}
}
Draw
And lastly my draw code.
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.Black);
spriteBatch.Begin();
Vector2 firstSquare = new Vector2(Camera.location.X / Tile.tileWidth, Camera.location.Y / Tile.tileHeight);
int firstX = (int)firstSquare.X;
int firstY = (int)firstSquare.Y;
Vector2 squareOffset = new Vector2(Camera.location.X % Tile.tileWidth, Camera.location.Y % Tile.tileHeight);
int offsetX = (int)squareOffset.X;
int offsetY = (int)squareOffset.Y;
for (int y = 0; y < squaresDown; y++)
{
for (int x = 0; x < squaresAcross; x++)
{
foreach (int tileID in myMap.rows[y + firstY].columns[x + firstX].baseTiles)
{
spriteBatch.Draw(
Tile.tileSetTexture,
new Rectangle(
(x * Tile.tileWidth) - offsetX, (y * Tile.tileHeight) - offsetY,
Tile.tileWidth, Tile.tileHeight),
Tile.getSourceRectangle(tileID),
Color.White);
}
}
}
spriteBatch.End();
base.Draw(gameTime);
}
Related
I'm working on writing a Mendelbrot renderer in C# to practice multithreading, but am having an issue where my calculation code maxes out at 2 iterations. I don't really understand why since the online references I've been looking at seem to calculate it the same way as me. No matter what coordinates of pixels I provide, it always returns 2 (aside from 0,0 and 0,1).
`
using System;
using System.Numerics;
using SkiaSharp;
namespace Mandelbrot
{
internal class Program
{
const int MaxIterations = 100;
static void Main(string[] args)
{
int size = 250;
int[,] grid = Run(size, 0, 0, 1);
// Console.WriteLine("Please specify a square size for the image.");
// try
// {
// Console.Write("Length: ");
// height = Int32.Parse(Console.ReadLine());
// }
// catch (FormatException e)
// {
// Console.WriteLine(e);
// throw;
// }
using (var surface = SKSurface.Create(width: size, height: size, SKColorType.Rgba8888, SKAlphaType.Premul))
{
SKCanvas canvas = surface.Canvas;
canvas.DrawColor(SKColors.Coral);
for (int i = 0; i < grid.GetLength(0); i++)
{
for (int j = 0; j < grid.GetLength(1); j++)
{
if (grid[i, j] >= MaxIterations)
canvas.DrawPoint(new SKPoint(i, j), SKColors.Black);
}
}
canvas.DrawPoint(new SKPoint(250, 250), SKColors.Chartreuse);
OutputImage(surface);
}
Console.WriteLine("Program successfully completed.");
}
public static int[,] Run(int size, int fromX, int fromY, int h)
{
int[] oulltput = new int[size * size];
int[,] output = new int[size, size];
for (int i = 0; i < output.GetLength(0); i += 1)
{
for (int j = 0; j < output.GetLength(1); j += 1)
{
float x = fromX + i * h;
float y = fromY + j * h;
output[i, j] = IterCount(x, y, MaxIterations);
}
}
return output;
}
public static int IterCount(float constX, float constY, int maxIterations)
{
const float maxMagnitude = 2f;
const float maxMagnitudeSquared = maxMagnitude * maxMagnitude;
int i = 0;
float x = 0.0f, y = 0.0f;
float xSquared = 0.0f, ySquared = 0.0f;
while (xSquared + ySquared <= maxMagnitudeSquared && i < maxIterations)
{
xSquared = x * x;
ySquared = y * y;
float xtmp = xSquared - ySquared + constX;
y = 2.0f * x * y + constY;
x = xtmp;
i++;
}
return i;
}
private static void OutputImage(SKSurface surface)
{
Console.WriteLine("Attempting to write .png to disk...");
using (var image = surface.Snapshot())
using (var data = image.Encode(SKEncodedImageFormat.Png, 80))
using (var stream = File.OpenWrite("out.png"))
{
// save the data to a stream
data.SaveTo(stream);
Console.WriteLine("Success!");
}
}
}
}
`
I tried to use breakpoints and writeline statements to debug but I can't figure out where my math is going wrong. I keep getting 2 for my iteration count.
I'm following along a course on udemy which is for free it's a memory game (https://www.udemy.com/xamarin-native-ios-memory-game-csharp/learn/v4/overview)
Now with the randomizer method i get a new problem with the same out of range error
using System;
using System.Collections;
using System.Collections.Generic;
using UIKit;
namespace iOSMemoryGame
{
public partial class ViewController : UIViewController
{
#region vars
List<String> imgArr = new List<String>();
float gameViewWidth;
int gridSize = 6;
ArrayList tilesArr = new ArrayList();
ArrayList coordsArr = new ArrayList();
#endregion
public ViewController(IntPtr handle) : base(handle)
{
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
// Perform any additional setup after loading the view, typically from a nib.
}
public override void ViewDidAppear(bool animated)
{
base.ViewDidAppear(animated);
// make sure Game View is Laid Out
gameView.LayoutIfNeeded();
gameViewWidth = (float)gameView.Frame.Size.Width;
// let's load all of our images into an array
for (int i = 1; i <= 18; i++)
{
imgArr.Add("img_" + i.ToString() + ".png");
}
// let's make a call to tileMaker
tileMaker();
// let's call the randomizer
randomizer();
}
private void randomizer()
{
// we are gonna go through our tiles in ORDER
// and we are gonna assign a new center to them RANDOMLY
foreach (UIView any in tilesArr)
{
// UIImageView thisTile = (UIImageView)tilesArr[i];
Random myRand = new Random();
int randomIndex = myRand.Next(0, coordsArr.Count);
CoreGraphics.CGPoint newRandCenter = (CoreGraphics.CGPoint)coordsArr[randomIndex];
any.Center = newRandCenter;
coordsArr.RemoveAt(randomIndex);
}
}
private void tileMaker()
{
float tileWidth = gameViewWidth / gridSize;
float xCenter = tileWidth / 2;
float yCenter = tileWidth / 2;
int imgCounter = 0;
for (int h = 0; h < gridSize; h++)
{
for (int v = 0; v < gridSize; v++)
{
UIImageView tileImgView = new UIImageView();
CoreGraphics.CGPoint newCenter = new CoreGraphics.CGPoint(xCenter, yCenter);
tileImgView.Frame = new CoreGraphics.CGRect(0, 0, tileWidth - 5, tileWidth - 5);
String imgName = imgArr[imgCounter];
tileImgView.Image = new UIImage(imgName);
tileImgView.Center = newCenter;
// user CAN interact with this image view
tileImgView.UserInteractionEnabled = true;
// adding the new image view to the array
tilesArr.Add(tileImgView);
gameView.AddSubview(tileImgView);
xCenter = xCenter + tileWidth;
imgCounter++;
if (imgCounter == gridSize * gridSize / 2)
{
imgCounter = 0;
}
}
xCenter = tileWidth / 2;
yCenter = yCenter + tileWidth;
}
}
public override void DidReceiveMemoryWarning()
{
base.DidReceiveMemoryWarning();
// Release any cached data, images, etc that aren't in use.
}
partial void Rst4Button_TouchUpInside(UIButton sender)
{
throw new NotImplementedException();
}
partial void Rst6Button_TouchUpInside(UIButton sender)
{
throw new NotImplementedException();
}
}
}
Something is wrong with the randomizer method but i dont know what.
it gives me again a out of range error.
without the randomizer method it works fine
This line
for (int i = 1; i < 18; i++)
creates 17 images, not 18 as expected by the following loops.
// 0-5 = 6 loops
for (int h = 0; h < gridSize; h++)
{
// 0-5 = 6 loops (h*v=18)
for (int v = 0; v < gridSize; v++)
{
....
You need to write
for (int i = 0; i < 18; i++)
imgArr.Add("img_" + (i+1).ToString() + ".png");
I want to realize method "Draw" of class Polygon
I have WindForms project, form, and the pictureBox1,
I want that "Draw" drawing polygon in pictureBox1 and I have opportunity to move image
I don't konw how to realize it. Help, please.
public class Polygon
{
public Point[] vertexes { get; protected set; }
public Polygon(params int[] vertex)
{
if (vertex == null || vertex.Length <= 2)
throw new Exception("someText");
if (vertex.Length % 2 != 0)
throw new Exception("someText");
vertexes = new Point[vertex.Length / 2];
ColorContour = System.Drawing.Color.DarkRed;
Priming = false;
for (int i = 0, j = 0; i < vertexes.Length; i++, j += 2)
vertexes[i] = new Point(vertex[j], vertex[j + 1]);
vertexes = Point.Sort(vertexes);
if (vertexes == null || vertexes.Length <= 2)
throw new Exception("someText");
}
public double Perimetr
{
get
{
double res = 0;
for (int i = 1; i < vertexes.Length; i++)
res += Point.Length(vertexes[i - 1], vertexes[i]);
return res;
}
}
public override void Move(int deltax, int deltay)
{
vertexes[0].x = deltax;
vertexes[0].y = deltay;
for (int i = 1; i < vertexes.Length; i++)
{
vertexes[i].x -= deltax;
vertexes[i].y -= deltay;
}
}
public void Zoom(double size)
{
if (size == 0)
return;
Point firstP = new Point(vertexes[0].x, vertexes[0].y);
Point Center = Point.CentrMass(vertexes);
for (int i = 0; i < vertexes.Length; ++i)
{
vertexes[i].x = Convert.ToInt32(size * (vertexes[i].x - Center.x) + Center.x);
vertexes[i].y = Convert.ToInt32(size * (vertexes[i].y - Center.y) + Center.y);
}
Move(firstP.x, firstP.y);
}
public void Draw( ??)
{
**????**
}
publicabstract double Square { get; }
You need to take in a System.Drawing.Graphics as a parameter, and call Graphics.DrawPolygon() function. Then in the picturebox, override or implement the OnPaint() event, and call your draw function with the Graphics you receive as a parameter (child of the eventargs) in OnPaint().
I'm trying to make a drag and drop from an array of object. At first, they will copied in another array. But, when i try to updating the original array, the copy has updating too.
here my codes:
namespace drop_test2
{
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
InputManager input;
Texture2D texure;
DragableObject[] dragObjects, copyDragObjects;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
this.IsMouseVisible = true;
this.input = new InputManager(this);
this.dragObjects = new DragableObject[6 * 6];
}
protected override void LoadContent()
{
this.spriteBatch = new SpriteBatch(this.GraphicsDevice);
this.input.Load();
this.texure = new Texture2D(this.GraphicsDevice, 50, 50, false, SurfaceFormat.Color);
Color[] c = new Color[this.texure.Width * this.texure.Height];
for (int i = 0; i < c.Length; i++) c[i] = Color.White;
this.texure.SetData(c);
for (int x = 0; x < 6; x++)
{
for (int y = 0; y < 6; y++)
{
var obj = new DragableObject();
obj.Size = new Vector2(50);
obj.Position = new Vector2(35) + new Vector2(x, y) * obj.Size;
this.dragObjects[x + y * 6] = obj;
}
}
this.copyDragObjects = this.dragObjects;
}
protected override void Update(GameTime gameTime)
{
this.input.Update(gameTime);
for (int i = 0; i < this.dragObjects.Length; i++)
{
if (this.dragObjects[i].CheckHover(input.CurrentCursorPost))
{
this.copyDragObjects[i].Position += new Vector2(.5f, 0);
}
}
}
Color c = Color.Blue;
protected override void Draw(GameTime gameTime)
{
this.GraphicsDevice.Clear(Color.CornflowerBlue);
this.spriteBatch.Begin();
for (int x = 0; x < 6; x++)
{
for (int y = 0; y < 6; y++)
{
if ((x + y) % 2 == 0)
c = Color.DarkGreen;
else
c = Color.LightGreen;
if (this.dragObjects[x + y * 6].IsHover)
c = Color.DarkOrchid * .5f;
if (this.dragObjects[x + y * 6].IsSelected)
c = Color.DarkRed * .75f;
this.spriteBatch.Draw(this.texure, this.dragObjects[x + y * 6].Position, null, c,
0f, new Vector2(texure.Width, texure.Height) * .5f, 1f, SpriteEffects.None, 0);
}
}
this.spriteBatch.End();
}
}
}
this is the dragableobject class
namespace drop_test2
{
struct DragableObject
{
public Vector2 Position
{
get;
set;
}
public Vector2 Size
{
get;
set;
}
public bool IsSelected
{
get;
set;
}
public bool IsHover
{
get;
set;
}
public bool CheckHover(Vector2 vector2)
{
Rectangle r = new Rectangle((int)(this.Position.X - this.Size.X * .5), (int)(this.Position.Y - this.Size.Y * .5f),
(int)this.Size.X, (int)this.Size.Y);
if (r.Contains((int)vector2.X,(int)vector2.Y))
{
this.IsHover = true;
return true;
}
this.IsHover = false;
return false;
}
}
}
Anyone wanna help me to solving this problem?
If you use CopyTo() c# internally refers to the original array. If you want to create a new instance you have to use Clone().
Example:
object[] src = { "a", "b" };
var dest = src.Clone() as object[];
src[0] = "c";
//// dest still contains "a","b"
Also see: Difference between the System.Array.CopyTo() and System.Array.Clone()
I cannot for the life of me work out how to get the block that is supposed to be drawn with every loop through the array of "Arxl" objects to animate across the grid.
Any suggestions would be really appreciated, not looking for someone to complete the code for me. just a fresh set of eyes.
public partial class Game : Form
{
//attributes
private Bitmap _grid;
private Arxl[,] _cartesianGrid;
private int _arxlAmount;
const int ARXL = 4;
public Game()
{
InitializeComponent();
_arxlAmount = (gridPictureBox.Height / ARXL);//in case height/arxl is not an even number?
_cartesianGrid = new Arxl[_arxlAmount, _arxlAmount];
_grid = new Bitmap(gridPictureBox.Width, gridPictureBox.Height);
int x;
int y;
for (x = 0; x < _arxlAmount; x++)
{
for (y = 0; y < _arxlAmount; y++)
{
_cartesianGrid[x, y] = new Arxl();
}
}
SetSeed(_cartesianGrid);
}
private void SetSeed(Arxl[,] cartesianGrid)
{
_cartesianGrid[1, 1].Active = true;
}
private void DrawArxl(Bitmap _grid, Arxl[,] cartesianGrid,int arxlAmount)
{
int x, y;
x=0;
y=0;
Graphics graphics = Graphics.FromImage(_grid);
graphics.Clear(Color.White);
for (x = 1; x < arxlAmount;x++ )
{
for (y = 1; y < arxlAmount; y++)
{
if (cartesianGrid[x, y].Active==true)
{
cartesianGrid[x, y].Area = new Rectangle(x * ARXL, y * ARXL, ARXL, ARXL);
graphics.FillRectangle(Brushes.Black, cartesianGrid[x, y].Area);
}
else if(cartesianGrid[x,y].Active==false)
{
Pen newPen=new Pen(Color.Black);
cartesianGrid[x, y].Area = new Rectangle(x * ARXL, y * ARXL, ARXL, ARXL);
graphics.DrawRectangle(newPen,cartesianGrid[x, y].Area);
newPen.Dispose();
}
}
}
}
private void timer_Tick(object sender, EventArgs e)
{
//GameOfLife(_cartesianGrid, _arxlAmount);
ScrollBlock(_cartesianGrid, _arxlAmount);
DrawArxl(_grid, _cartesianGrid, _arxlAmount);
gridPictureBox.Image = _grid;
}
private void ScrollBlock(Arxl[,] cartesianGrid, int arxlAmount)
{
int x = 0;
int y = 0;
for (x = 0; x < arxlAmount; x++)
{
for (y = 0; y < arxlAmount; y++)
{
if (_cartesianGrid[x, y].Active == true)
{
if (x>=0)
{
if (x == (arxlAmount-1))
{
_cartesianGrid[x, y].Active = false;
_cartesianGrid[1, y].Active = true;
}
else if(x<(arxlAmount-1))
{
_cartesianGrid[x, y].Active = false;
_cartesianGrid[x+1, y].Active = true;
}
}
}
}
}
}
According to a comment in your code, you want to program the life game. It will not work, if you change the cells in place, because you will have to compute the new state from the unchanged old state. Therefore, you will need to have two game boards, one with the current state and one with the new state. Instead of creating new board all the time, it is better to have two boards and to swap them. In addition, there is no point in storing the Rectangles in the board. Therefore, I declare the boards as Boolean matrix.
const int CellSize = 4;
private int _boardSize;
private bool[,] _activeBoard, _inactiveBoard;
Bitmap _grid;
The form constructor is changed like this
public Game()
{
InitializeComponent();
_boardSize = Math.Min(gridPictureBox.Width, gridPictureBox.Height) / CellSize;
_grid = new Bitmap(gridPictureBox.Width, gridPictureBox.Height);
_activeBoard = new bool[_boardSize, _boardSize];
_inactiveBoard = new bool[_boardSize, _boardSize];
SetSeed();
}
We initialize the game like this (as an example)
private void SetSeed()
{
_activeBoard[0, 0] = true;
_activeBoard[7, 4] = true;
DrawGrid();
}
The timer tick does this
ScrollBlock();
DrawGrid();
The logic in ScrollBlock is completely new. We look at the state on the _activeBoard and set the state of _inactiveBoard. Then we swap the two boards.
private void ScrollBlock()
{
for (int x = 0; x < _boardSize; x++) {
for (int y = 0; y < _boardSize; y++) {
if (_activeBoard[x, y]) {
_activeBoard[x, y] = false;
int newX = x + 1;
int newY = y;
if (newX == _boardSize) {
newX = 0;
newY = (newY + 1) % _boardSize;
}
_inactiveBoard[newX, newY] = true;
}
}
}
SwapBoards();
}
The boards are simply swapped like this
private void SwapBoards()
{
bool[,] tmp = _activeBoard;
_activeBoard = _inactiveBoard;
_inactiveBoard = tmp;
}
And finally DrawGrid draws the _activeBoard
private void DrawGrid()
{
Graphics graphics = Graphics.FromImage(_grid);
graphics.Clear(Color.White);
for (int x = 0; x < _boardSize; x++) {
for (int y = 0; y < _boardSize; y++) {
var rect = new Rectangle(x * CellSize, y * CellSize, CellSize, CellSize);
if (_activeBoard[x, y]) {
graphics.FillRectangle(Brushes.Black, rect);
} else {
graphics.DrawRectangle(Pens.Black, rect);
}
}
}
gridPictureBox.Image = _grid;
}
I've spotted your problem.
The problem is that you're updating a cell position (moving it to the right in this particular initial state), but the next iteration in the for loop finds the updated state from the previous iteration, so it updates the cell again, and again, and when the cycle stops, the cell was scrolled over to its initial cell position!, with no repainting in between.
I'm modifying your code to add an UpdateList that will turn on cells that need to be ON after the grid scan has finished to avoid updating the same "active dot" more than once. This should show a moving dot from left to right.
private void ScrollBlock(Arxl[,] cartesianGrid, int arxlAmount) {
int x = 0;
int y = 0;
List<Point> updateList = new List<Point>();
for( x = 0; x < arxlAmount; x++ ) {
for( y = 0; y < arxlAmount; y++ ) {
if( _cartesianGrid[x, y].Active == true ) {
if( x >= 0 ) {
if( x == (arxlAmount - 1) ) {
_cartesianGrid[x, y].Active = false;
//_cartesianGrid[1, y].Active = true;
updateList.Add(new Point(1, y));
} else if( x < (arxlAmount - 1) ) {
_cartesianGrid[x, y].Active = false;
//_cartesianGrid[x + 1, y].Active = true;
updateList.Add(new Point(x + 1, y));
}
}
}
}
}
foreach( var pt in updateList ) {
_cartesianGrid[pt.X, pt.Y].Active = true;
}
}
In your timer try calling gridPictureBox.Invalidate() after you assign the image to the picturebox. This will force the picturebox to redraw itself.