I am building a 2D platform game. But the guy making the videos just showed how to create a map and tile class and loaded the level from an array he made. I heard that's a bad practice and people should load their levels from files... I know how to load files with the stream reader but since that's not my code, I was following the tutorial and I have difficulties in implementing it.
So here's the code I have for the level:
private Map map;
// This is in my LoadContent() method
map.Generate(new int[,]
{
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1},
{2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 2, 2},
{2, 2, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 1, 0, 0, 0, 0, 2, 2},
{2, 2, 0, 0, 0, 0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 2, 1, 0, 0, 0, 2, 2},
{2, 0, 0, 0, 0, 0, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 2, 2},
{2, 0, 0, 0, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2},
{2, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2},
{2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2},
{2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2},
}, 64);
So I'm loading tiles like this:
// Tiles.cs class
public CollisionTiles(int i, Rectangle newRectangle)
{
texture = Content.Load<Texture2D>("Graphics/Tiles/tile_" + i);
this.Rectangle = newRectangle;
}
And each one of the above integers are added to the string of the filename and diretly loaded and drawn on the screen (like this: map.Draw(spriteBatch);). The 64 int is the size the actual tiles are drawn into. That's what I want to avoid, having to write these arrays for each level, instead I need a way of loading levels from files. When loading the tiles of the level, the tile size shouldn't be forgotten in the .txt file. Maybe have it stay alone on the first or the last line of the level file? That way each level can have different size of tiles if needed.
Here are my Map.cs and Tiles.cs classes. I didn't paste them on SO directly it would make the question too long...
So how would you approach this? What kind of solution is best? I think loading levels similar to tiles like this: "Levels/level_" + i is also I nice way and just have the game increment the int i every time the player finishes a level (I should be able to do this myself), but I'm just asking how you would read a file. I will make any suggested modifications to my code and also I think Splitting the contents of the file by , is opinion based, could also use spaces , but since that's an array in the class I had to use ,s. In the text file I should also remove the { } braces. Anyway I thank you for any feedback and code examples with an explanation would be great!
EDIT:
So should I add the tiles to collisionTiles like this?
public int[,] LoadLevelData(string filename)
{
using (var streamReader = new StreamReader(filename))
{
var serializer = new JsonSerializer();
Generate((int[,])serializer.Deserialize(streamReader, typeof(int[,])), 64);
return (int[,])serializer.Deserialize(streamReader, typeof(int[,]));
}
}
So how would you approach this? What kind of solution is best?
The best solution to this problem really depends on how you plan to edit your levels.
With your current approach (storing level data in code) you can actually edit the level manually by hand. This is not spectacular, but it is manageable for small games.
The next approach is to store levels in a text file. I answered a similar question about this the other day. Many games have used this approach in the past with great success.
If you want to keep things really simple, you could take the data you've got now and use JSON.NET to deserialize it. The cool thing is, the data represented in JSON looks almost exactly as it appears in code and can be edited by your favorite text editor.
[
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1],
[2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 2, 2],
[2, 2, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 1, 0, 0, 0, 0, 2, 2],
[2, 2, 0, 0, 0, 0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 2, 1, 0, 0, 0, 2, 2],
[2, 0, 0, 0, 0, 0, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 2, 2],
[2, 0, 0, 0, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2],
[2, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2],
[2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2],
[2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]
]
The load method is really simple too.
public int[,] LoadLevelData(string filename)
{
using (var streamReader = new StreamReader(filename))
{
var serializer = new JsonSerializer();
return (int[,])serializer.Deserialize(streamReader, typeof(int[,]));
}
}
To use the method, you can pass the result into your Generate method:
// This is in my LoadContent() method
var levelData = LoadLevelData(filename);
map.Generate(levelData, 64);
That'll work just fine if you're always using size 64 blocks, although, you could store the block size in the JSON file as well if you like. It's a bit more complicated though, and probably too much for this answer. The JSON.NET library has excellent documentation.
Since you're using MonoGame you probably want to use TitleContainer.OpenStream instead of a StreamReader to keep things working on all platforms. Alternately, you could write a content reader for the MonoGame Pipeline tool but that's beyond the scope of this question.
The last, but not least, thing you might want to consider is using 3rd party level editor software like Tiled. There's a great Tiled map loader in my MonoGame.Extended library or a number of other existing map loaders to choose from.
Whatever approach you choose, my suggestion is to keep things simple. Each approach has pros and cons, and simple solutions are usually the easiest to get started. Once you've got the basics working you can upgrade to the the complex approaches for added benefits.
Loading levels and binary data, I like to use the BinaryReader and BinaryWriter. For arrays, I write the size first, and then the data. Like this:
void Write(string fileName, int[,] data)
{
using (BinaryWriter writer = new BinaryWriter(File.OpenWrite(fileName)))
{
writer.Write(data.GetLength(0));
writer.Write(data.GetLength(1));
for (int x = 0; x < data.GetLength(0); x++)
{
for (int y = 0; y < data.GetLength(1); y++)
{
writer.Write(data[x, y]);
}
}
}
}
int[,] Read(string fileName)
{
using (BinaryReader reader = new BinaryReader(File.OpenRead(fileName)))
{
int width = reader.ReadInt32();
int height = reader.ReadInt32();
int[,] result = new int[width, height];
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
result[x, y] = reader.ReadInt32();
}
}
return result;
}
}
Related
I'm trying to pass an array that comes from the Base class in the constructor of the Child class, however I feel like there might be conflict in terms of constructors getting called as the getFullArray() method of the Base class returns null.
Base class looks like this:
protected Byte[,] _byteTileArray; //is going to be implemented in child class
private Object[,] objectTileArray;
public Level_Manager(ContentManager content)
{
CreateTileArray();
objectTileArray = new Object[_byteTileArray.GetLength(0), _byteTileArray.GetLength(1)];
CreateWorld(content);
}
protected abstract void CreateTileArray();
private void CreateWorld(ContentManager content)
{
for(int row = 0; row < _byteTileArray.GetLength(0); row++)
{
for (int col = 0; col < _byteTileArray.GetLength(1); col++)
{
switch (_byteTileArray[row,col])
{
case 0: objectTileArray[row, col] = null; break;
case 1: objectTileArray[row, col] = new Cube(new Vector2(col * (852/12), row * (480/7)), content, 124, 33, new Vector2(0, 0)); break;
case 2: objectTileArray[row, col] = new Ladder(new Vector2(col * (852 / 12), row * (480 / 7)-33), content, 50, 107, new Vector2(0, 0)); break;
}
}
}
}
public Object[,] getFullArray(){
return objectTileArray;
}
I need the objectTileArray from getFullArray()
This is the Child class constructor:
private Object[,] arrayObjects;
#endregion
public Level1(ContentManager content, GraphicsDevice device) : base(content)
{
this.content = content;
arrayObjects = getFullArray();
hero = new Hero(new Vector2(1, device.Viewport.Height-40), this.content, arrayObjects);
}
arrayObjects is null here.
EDIT:
Apparently I misunderstood the debugging and my arrayObjects wasn't null but the elements inside were.
protected override void CreateTileArray()
{
base._byteTileArray = new Byte[,]
{
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0},
{0, 2, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0},
{1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0},
{0, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1},
{1, 1, 0, 2, 1, 0, 1, 1, 1, 1, 1, 2},
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
};
}
this was the byteTileArray I was passing on to the Parent class, it was inserting null on the 0's (intentionally) but this was giving me a NullReferenceException in another part which was fixed by just adding if not null clause.
Thank you to everyone and sorry!
I made a tile level system that uses a multidimensinal array, for example:
new int[,]{
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0,},
{0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},
{0, 0, 0, 0, 2, 0, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0,},
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,},
Every specific number represents a block(tile).
This is just in code. I want to code a system that can load a level(an multidimensinal int array)
That array needs to be converted from a string. How do I do that?
public static int[,] getLvl(string lvlName = "")
{
string readed = "";
using (StreamReader read = new StreamReader(path))
{
readed = read.ReadToEnd();
}
return null; //What to put here?!?!?!
}
EDIT: I do not have yet a format for the file to read. So you can be flexible in that.
I don't think you really need to or should bother trying to serialize it into XML or some other format, since your data storage is so simple.
One easy way is to just store your array in a text file as comma separated values. So one level might have:
0,0,0,1
0,1,1,0
0,1,1,3
3,3,4,1
The String.Split() method is really useful for parsing something simple like this. It allows you to split a string into an array of substrings based on a certain delimiting character.
Step by step:
First you can use var RowArray = MyString.Split('\n') (the newline character) to split your string into an array of rows. This leaves you with the array:
[0]: "0,0,0,1"
[1]: "0,1,1,0"
[2]: "0,1,1,3"
[3]: "3,3,4,1"
You can sort of see what Split() does here and why that's useful for your case. You can in turn run split each row on ',' leaving with you an array of arrays, which you can very easily convert to exactly the 2D array you're looking for.
The one pitfall here is somewhere, depending on your design needs, one invariant might have to be that in the file all rows will be of the same length. Or if you can't guarantee that, you'll have to write some code so that when turning the below text into an array from an array of rows, you make the width equal to the longest row and fill in blanks with 0s or some other method.
0,0,0,1,6,4
0,1,1,0
0,1,1,3,2,6,3,7,1
3,3,4,1,2,4
The shortest method is using linq, for this format:
0,0,0,0,0
1,0,1,0,1
....
You can use this sentence:
int[][] Data = File.ReadAllLines( path )
.Select( s => s.Trim())
.Where( s => !string.IsNullOrempty(s))
.Select( s => s.Split( ',' )
.Select( token => int.Parse( token ) )
.ToArray( ) )
.ToArray( );
var DataToOneDim = Data.SelectMany( a => a).ToArray();
var Result = new int[Data[0].Length, Data.Length];
Buffer.BlockCopy( DataToOneDim, 0, Result, 0, DataToOneDim.Length );
I believe there are a few other posts regarding this, but you can use either XML and parse through each possible dimension or serialize everything at once using the SoapFormatter class. Below is a link to a similar question with some examples:
Convert Multi-Dimensional Array to String and Back
I make it for you fast you can make it better do not Forget the vote :_)
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
internal class Program
{
private static void Main(string[] args)
{
var array =
"{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,} , {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,}";
string input = array;
string pattern = #"{|}";
var index = 0;
var results = new int[100,100];
foreach (var result in Regex.Split(input, pattern))
{
var sp = result.Split(',');
if (sp.Length <4)
continue;
for (int i = 0; i < sp.Count(); i++)
{
if (!string.IsNullOrEmpty(sp[i]))
results[index,i] = Convert.ToInt32(sp[i]);
}
index++;
}
}
}
}
I'm trying to draw a Seven Segment display using this following method, I can't see why, even when I run through the debugger, but for somereason it doesn't display the numbers. What is wrong here? You can ignore the large array, it's just to show how I store the values.
private void DrawScore(SpriteBatch spriteBatch, int score, int playerNumber)
{
int[,,] numbers =
{
// Zero
// Output:
// [ ][.][ ] [.] = white square [ ] = black square
// [.][ ][.]
// [ ][.][ ]
// [.][ ][.]
// [ ][.][ ]
{
{0, 1, 0},
{1, 0, 1},
{0, 0, 0},
{1, 0, 1},
{0, 1, 0}
},
{
{0, 0, 0},
{0, 0, 1},
{0, 0, 0},
{0, 0, 1},
{0, 0, 0}
},
{
{0, 1, 0},
{0, 0, 1},
{0, 1, 0},
{1, 0, 0},
{0, 1, 0}
},
{
{0, 1, 0},
{0, 0, 1},
{0, 1, 0},
{0, 0, 1},
{0, 1, 0}
},
{
{0, 0, 0},
{1, 0, 1},
{0, 1, 0},
{0, 0, 1},
{0, 0, 0}
},
{
{0, 1, 0},
{1, 0, 0},
{0, 1, 0},
{0, 0, 1},
{0, 1, 0}
},
{
{0, 1, 0},
{1, 0, 0},
{0, 1, 0},
{1, 0, 1},
{0, 1, 0}
},
{
{0, 1, 0},
{0, 0, 1},
{0, 0, 0},
{0, 0, 1},
{0, 0, 0}
},
{
{0, 1, 0},
{1, 0, 1},
{0, 1, 0},
{1, 0, 1},
{0, 1, 0}
},
{
{0, 1, 0},
{1, 0, 1},
{0, 1, 0},
{0, 0, 1},
{0, 0, 0}
}
};
for (int i = 0; i < numbers.GetLength(1); i++)
{
for (int j = 0; j < numbers.GetLength(2); j++)
{
Debug.WriteLine("Score: {0}", score);
Debug.WriteLine("\ti, j: {0}", numbers[score, i, j]);
if (playerNumber == 1)
{
spriteBatch.Draw(numbers[score, i, j] == 0 ? _scoreSegmentTexBlack : _scoreSegmentTexWhite,
new Vector2(
(Graphics.PreferredBackBufferWidth/2) - _scoreSegmentTex.Width*(3 + i),
_scoreSegmentTex.Height*j + 1),
Color.White);
}
if (playerNumber == 2)
{
spriteBatch.Draw(numbers[score, i, j] == 0 ? _scoreSegmentTexBlack : _scoreSegmentTexWhite,
new Vector2(
(Graphics.PreferredBackBufferWidth / 2) + _scoreSegmentTex.Width*(1 + i),
_scoreSegmentTex.Height*j + 1),
Color.White);
}
}
}
}
In Java:
public class Digit {
protected int value;
protected List<Segment> segmentList;
public Digit (int value, Segment... segments) {
this.value = value;
this.segmentList = Arrays.asList( segments);
}
public void draw (int x, int y) {
for (Segment seg : segmentList) {
seg.draw( x, y);
}
}
}
public enum Segment {
TOP (0, 0, 1, 0), // x0,y0, x1,y1
LT (0, 0, 0, 1),
RT (1, 0, 1, 1),
MID (0, 1, 1, 1),
LB (0, 1, 0, 2),
RB (1, 1, 1, 2),
BOT (0, 2, 1, 2);
private Segment (int x0, int y0, int x1, int y1) {
// assign x0,y0 & x1,y1 to fields.
}
public draw (int xofs, int yofs) {
// draw..
}
}
// setup the Digits somewhere.. then:
public void drawScore (int number, int xofs, int yofs) {
int remain = number;
int digitI = 0;
while (remain > 0 || digitI == 0) {
int digit = (remain % 10);
remain /= 10;
// draw the digit.
//
int xpos = digit * DIGIT_WIDTH;
digits[digit].draw( xpos, SCORE_YPOS);
}
}
I decided to make a whole class to handle this instead.
Pictured below is '0' drawn at (0, 0). (x = 0, y = 0) Using the code below:
SevenSegmentDisplay myDisplay = new SevenSegmentDisplay(0, 0);
// Inside the game loop somewhere.
if (playerScoreCondition) {
// player.Score++;
// In this case score is still 0.
myDisplay.Update(player.Score);
}
// Draw
myDisplay.Draw(spriteBatch, segmentTexture, scoreDisplayX, scoreDisplayY, Color.White, new Color(30, 30, 30, 255));
internal class SevenSegmentDisplay
{
private int a, b, c, d, e, f, g;
private readonly int[,] numbers;
public SevenSegmentDisplay()
{
numbers = new[,] {
/* Format is A - G, see:
* https://en.wikipedia.org/wiki/Seven-segment_display
*/
// 0
{1, 1, 1, 1, 1, 1, 0},
// 1
{0, 1, 1, 0, 0, 0, 0},
// 2
{1, 1, 0, 1, 1, 0, 1},
// 3
{1, 1, 1, 1, 0, 0, 1},
// 4
{0, 1, 1, 0, 0, 1, 1},
// 5
{1, 0, 1, 1, 0, 1, 1},
// 6
{1, 0, 1, 1, 1, 1, 1},
// 7
{1, 1, 1, 0, 0, 0, 0},
// 8
{1, 1, 1, 1, 1, 1, 1},
// 9
{1, 1, 1, 1, 0, 1, 1}
};
// Initialize each segment to 0 (black)
a = 0;
b = 0;
c = 0;
d = 0;
e = 0;
f = 0;
g = 0;
}
private void Update(IList<int> i)
{
// Update each segment
a = i[0];
b = i[1];
c = i[2];
d = i[3];
e = i[4];
f = i[5];
g = i[6];
}
public void Update(int i)
{
Update(IntToSevenSegment(i));
}
private int[] IntToSevenSegment(int i)
{
int[] temp = new int[7];
for (int counter = 0; counter < 7; counter++)
temp[counter] = numbers[i, counter];
return temp;
}
public void Draw(SpriteBatch spriteBatch, Texture2D texture, int x, int y, Color on, Color off)
{
// Texture should be a white square, to handle the drawing of each segment.
// Handle each segment A - G and draw them according to their positions depending on the texture size.
Rectangle a = new Rectangle(x + texture.Width, y, texture.Width*2, texture.Height);
Rectangle b = new Rectangle(x + texture.Width*3, y + texture.Height, texture.Width, texture.Height*2);
Rectangle c = new Rectangle(x + texture.Width*3, y + texture.Height*4, texture.Width, texture.Height*2);
Rectangle d = new Rectangle(x + texture.Width, y + texture.Height*6, texture.Width*2, texture.Height);
Rectangle e = new Rectangle(x, y + texture.Height*4, texture.Width, texture.Height*2);
Rectangle f = new Rectangle(x, y + texture.Height, texture.Width, texture.Height*2);
Rectangle g = new Rectangle(x + texture.Width, y + texture.Height*3, texture.Width*2, texture.Height);
spriteBatch.Draw(texture, a, this.a == 1 ? on : off);
spriteBatch.Draw(texture, b, this.b == 1 ? on : off);
spriteBatch.Draw(texture, c, this.c == 1 ? on : off);
spriteBatch.Draw(texture, d, this.d == 1 ? on : off);
spriteBatch.Draw(texture, e, this.e == 1 ? on : off);
spriteBatch.Draw(texture, f, this.f == 1 ? on : off);
spriteBatch.Draw(texture, g, this.g == 1 ? on : off);
}
}
I've been looking through the other questions but just can't seem to figure this out.. I'm using XNA in a custom tilemap loader. Here is the code.
http://pastebin.com/cuatQHTb
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
namespace Pressure
{
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
Texture2D player;
Vector2 pos = Vector2.Zero;
Vector2 playersp = new Vector2(50.0f, 50.0f);
Texture2D road1;
Texture2D road2;
Texture2D brickwall;
Texture2D floor1;
Texture2D floor2;
Texture2D floor3;
Texture2D grass;
Texture2D sidewalk1;
Texture2D wood;
Texture2D road3;
private Vector2 origin;
KeyboardState currentState;
Camera camera = new Camera();
Vector2 motion;
List<Texture2D> tiles = new List<Texture2D>();
static int tileWidth = 64;
static int tileHeight = 64;
int tileMapWidth;
int tileMapHeight;
static int screenWidth;
static int screenHeight;
static int mapWidthInPixels;
static int mapHeightInPixels;
int[,] map = {
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 5, 6, 5, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},
{0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, 7, 5, 6, 5, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},
{0, 0, 0, 0, 0, 8, 1, 1, 1, 2, 2, 1, 8, 7, 5, 6, 5, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},
{0, 0, 0, 0, 0, 8, 1, 1, 1, 2, 2, 1, 8, 7, 5, 5, 5, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},
{0, 0, 0, 0, 0, 8, 1, 1, 1, 1, 1, 1, 8, 7, 5, 5, 5, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},
{0, 0, 0, 0, 0, 8, 1, 3, 3, 3, 1, 1, 8, 7, 5, 6, 5, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},
{0, 0, 0, 0, 0, 8, 8, 1, 1, 8, 8, 8, 8, 7, 5, 6, 5, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},
{7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 5, 6, 5, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,},
{5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,},
{9, 9, 9, 5, 5, 9, 9, 9, 5, 5, 9, 9, 9, 5, 5, 5, 5, 5, 9, 9, 9, 5, 5, 9, 9, 9, 5, 5, 9,},
{5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,},
{7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},
};
public static int ScreenWidth
{
get { return screenWidth; }
}
public static int ScreenHeight
{
get { return screenHeight; }
}
public static int MapWidthInPixels
{
get { return mapWidthInPixels; }
}
public static int MapHeightInPixels
{
get { return mapHeightInPixels; }
}
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
protected override void Initialize()
{
base.Initialize();
}
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
player = Content.Load<Texture2D>("haz");
origin.X = player.Width /2;
origin.Y = player.Height /2;
grass = Content.Load<Texture2D>("grass");
floor1 = Content.Load<Texture2D>("floor1");
floor2 = Content.Load<Texture2D>("floor2");
floor3 = Content.Load<Texture2D>("floor3");
wood = Content.Load<Texture2D>("wood");
road1 = Content.Load<Texture2D>("road1");
road2 = Content.Load<Texture2D>("road2");
sidewalk1 = Content.Load<Texture2D>("sidewalk1");
brickwall = Content.Load<Texture2D>("brickwall");
road3 = Content.Load<Texture2D>("road3");
tiles.Add(grass); //0
tiles.Add(floor1);//1
tiles.Add(floor2);//2
tiles.Add(floor3);//3
tiles.Add(wood);//4
tiles.Add(road1);//5
tiles.Add(road2);//6
tiles.Add(sidewalk1);//7
tiles.Add(brickwall);//8
tiles.Add(road3);//9
tileMapWidth = map.GetLength(1);
tileMapHeight = map.GetLength(0);
mapWidthInPixels = tileMapWidth * tileWidth;
mapHeightInPixels = tileMapHeight * tileHeight;
screenWidth = GraphicsDevice.Viewport.Width;
screenHeight = GraphicsDevice.Viewport.Height;
}
protected override void UnloadContent()
{
}
private float RotationAngle;
private float oldx;
private float oldy;
protected override void Update(GameTime gameTime)
{
oldx = playersp.X;
oldy = playersp.Y;
currentState = Keyboard.GetState();
pos = playersp;
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
IsMouseVisible = true;
MouseState Mouses = Mouse.GetState();
Vector2 mouseLook = new Vector2(Mouses.X, Mouses.Y);
motion = Vector2.Zero;
Vector2 direction = (playersp ) - mouseLook;
float angle = (float)(Math.Atan2(direction.Y, direction.X));
RotationAngle = angle ;
if (currentState.IsKeyDown(Keys.W))
{
if (playersp.Y > screenHeight /4)
{
playersp.Y = playersp.Y - 1;
}
else
{
ScrollUp();
}
}
if (currentState.IsKeyDown(Keys.A))
{
if (playersp.X > screenWidth / 4)
{
playersp.X = playersp.X - 1;
}
else
{
ScrollLeft();
}
}
if (currentState.IsKeyDown(Keys.S))
{
if (playersp.Y > screenHeight / 1.5f)
{
ScrollDown();
}else{
playersp.Y = playersp.Y + 1;
}
}
if (currentState.IsKeyDown( Keys.D))
{
if (playersp.X > screenWidth / 1.5f)
{
ScrollRight();
}
else
{
playersp.X = playersp.X + 1;
}
}
if (motion != Vector2.Zero)
{
motion.Normalize();
camera.Position += motion * camera.Speed;
}
base.Update(gameTime);
}
private void ScrollUp()
{
motion.Y = -0.5f;
}
private void ScrollRight()
{
motion.X = 0.5f;
}
private void ScrollDown()
{
motion.Y = 0.5f;
}
private void ScrollLeft()
{
motion.X = -0.5f;
}
private Point VectorToCell(Vector2 vector)
{
return new Point(
(int)(vector.X / tileWidth),
(int)(vector.Y / tileHeight));
}
private Vector2 ViewPortVector()
{
return new Vector2(
screenWidth + tileWidth,
screenHeight + tileHeight);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
DrawMap();
base.Draw(gameTime);
}
private void DrawMap()
{
Point cameraPoint = VectorToCell(camera.Position);
Point viewPoint = VectorToCell(camera.Position +
ViewPortVector());
Point min = new Point();
Point max = new Point();
min.X = cameraPoint.X;
min.Y = cameraPoint.Y;
max.X = (int)Math.Min(viewPoint.X, map.GetLength(1));
max.Y = (int)Math.Min(viewPoint.Y, map.GetLength(0));
Rectangle tileRectangle = new Rectangle(
0,
0,
tileWidth,
tileHeight);
spriteBatch.Begin();
for (int y = min.Y; y < max.Y; y++)
{
for (int x = min.X; x < max.X; x++)
{
tileRectangle.X = x * tileWidth - (int)camera.Position.X;
tileRectangle.Y = y * tileHeight - (int)camera.Position.Y;
spriteBatch.Draw(tiles[map[y, x]],
tileRectangle,
Color.White);
spriteBatch.Draw(player, pos, null, Color.White, RotationAngle,
origin, 1.0f, SpriteEffects.None, 0f);
}
}
spriteBatch.End();
}
}
}
How would I detect the tiles position and make sure the player doesn't enter that tile? thanks!
You could do something like this:
private static float scalingFactor = 10;
private static float mapSizeX = 19;
private static float mapSizeY = 29;
in Update:
if (playersp.Y > screenHeight /4) {
int mapX = (int) (playersp.X / scalingFactor);
int mapY = (int) (playersp.Y / scalingFactor) - 1;
if (isMovable(mapX, mapY)) {
playersp.Y = playersp.Y - scalingFactor;
}
} else {
ScrollUp();
}
and a new method:
public bool isMovable(int mapX, int mapY)
{
if (mapX < 0 || mapX > 19 || mapY < 0 || mapY > 29) {
return false;
}
int tile = map[mapX, mapY];
if (tile == 4 || tile == 8) {
return false;
}
return true;
}
Similarly for the other directions.
The above code calls the function isMovable to decide whether the player can move to the new location based on the type of tile stored in the map at that position. The decision is false (the player cannot move there) if it is wood or brick wall, otherwise it is true.
The scaling factor is to map between the screen position (captured in playersp) and the tile map. In this case each tile is equivalent to 10 screen "pixels" (You can break it into two separate scales if you want to: one for X dimention, the other for Y).
Note, you need to make sure the values mapSizeX and mapSizeY are correct.
It would be best if you introduced named constants for the type of tile instead of using the numbers -- it will make your code more readable (for you in the future and for others reading it).
EDIT: updated code & explanation
I am trying to setup a 2d array in C# to act as a maze to move a character around, I am having a few issues initialising the array, I am trying to do the below
but the InitialiseMaze method is saying the maze is not declared
Can anyone advise
thanks
simon
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace GameMan
{
public class Maze
{
#region Variables
static int[,] maze;
#endregion
#region Constructors/Destructors
public Maze()
{
InitaliseMaze();
}
~Maze()
{
}
#endregion
#region Methods
public void InitaliseMaze()
{
maze = {
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0},
{0, 0, 3, 0, 0, 2, 0, 0, 0, 2, 0, 2, 0, 0, 0, 2, 0, 0, 3, 0, 0},
{0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0},
{0, 0, 2, 0, 0, 2, 0, 2, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0, 2, 0, 0},
{0, 0, 2, 2, 2, 2, 0, 2, 2, 2, 0, 2, 2, 2, 0, 2, 2, 2, 2, 0, 0},
{0, 0, 0, 0, 0, 2, 0, 0, 0, 1, 0, 1, 0, 0, 0, 2, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 2, 0, 1, 1, 1, 4, 1, 1, 1, 0, 2, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 2, 0, 1, 0, 0, 0, 0, 0, 1, 0, 2, 0, 0, 0, 0, 0},
{1, 1, 1, 1, 1, 2, 1, 1, 0, 0, 0, 0, 0, 1, 1, 2, 1, 1, 1, 1, 1},
{0, 0, 0, 0, 0, 2, 0, 1, 0, 0, 0, 0, 0, 1, 1, 2, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 2, 0, 1, 1, 1, 1, 1, 1, 1, 0, 2, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 2, 0, 1, 0, 0, 0, 0, 0, 1, 0, 2, 0, 0, 0, 0, 0},
{0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0},
{0, 0, 2, 0, 0, 2, 0, 0, 0, 2, 0, 2, 0, 0, 0, 2, 0 ,0, 2, 0, 0},
{0, 0, 3, 2, 0, 2, 2, 2, 2, 2, 5, 2, 2, 2, 2, 2, 0, 2, 3, 0, 0},
{0, 0, 0, 2, 0, 2, 0, 2, 0, 0, 0, 0, 0, 2, 0, 2, 0, 2, 0, 0, 0},
{0, 0, 2, 2, 2, 2, 0, 2, 2, 2, 0, 2, 2, 2, 0, 2, 2, 2, 2, 0, 0},
{0, 0, 2, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0, 0, 0, 0, 0, 2, 0, 0},
{0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
};
}
#endregion
}
}
You can't initialize an array like that other than in a variable declaration. However, the change is simple:
maze = new int[,] {
// As before
};
As asides:
It looks like maze should be an instance variable rather than a static variable. After all, you're initializing it each time you create an instance of Maze
You have a finalizer for no reason. Finalizers are very rarely required (or indeed advisable) in C#
Ok, well here is some extract from the msdn :
int[,] myArray = {{1,2}, {3,4}, {5,6}, {7,8}};
extracted from MSDN multidimensional arrays
you should also read up concerning Destructors, finalizers etc ... , I bet your coming from C++ ? Differences between the 2 languages arent always obvious :).
Just to make Jon's post a bit clearer:
maze = new int[,]{
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0},
{0, 0, 3, 0, 0, 2, 0, 0, 0, 2, 0, 2, 0, 0, 0, 2, 0, 0, 3, 0, 0},
{0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0},
{0, 0, 2, 0, 0, 2, 0, 2, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0, 2, 0, 0},
{0, 0, 2, 2, 2, 2, 0, 2, 2, 2, 0, 2, 2, 2, 0, 2, 2, 2, 2, 0, 0},
{0, 0, 0, 0, 0, 2, 0, 0, 0, 1, 0, 1, 0, 0, 0, 2, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 2, 0, 1, 1, 1, 4, 1, 1, 1, 0, 2, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 2, 0, 1, 0, 0, 0, 0, 0, 1, 0, 2, 0, 0, 0, 0, 0},
{1, 1, 1, 1, 1, 2, 1, 1, 0, 0, 0, 0, 0, 1, 1, 2, 1, 1, 1, 1, 1},
{0, 0, 0, 0, 0, 2, 0, 1, 0, 0, 0, 0, 0, 1, 1, 2, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 2, 0, 1, 1, 1, 1, 1, 1, 1, 0, 2, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 2, 0, 1, 0, 0, 0, 0, 0, 1, 0, 2, 0, 0, 0, 0, 0},
{0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0},
{0, 0, 2, 0, 0, 2, 0, 0, 0, 2, 0, 2, 0, 0, 0, 2, 0 ,0, 2, 0, 0},
{0, 0, 3, 2, 0, 2, 2, 2, 2, 2, 5, 2, 2, 2, 2, 2, 0, 2, 3, 0, 0},
{0, 0, 0, 2, 0, 2, 0, 2, 0, 0, 0, 0, 0, 2, 0, 2, 0, 2, 0, 0, 0},
{0, 0, 2, 2, 2, 2, 0, 2, 2, 2, 0, 2, 2, 2, 0, 2, 2, 2, 2, 0, 0},
{0, 0, 2, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0, 0, 0, 0, 0, 2, 0, 0},
{0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
};
Boy, that was a big maze array...
You need to declare maze
numbers = new int[X,Y]; where X and Y are how big it is
Two-Dimensional Arrays
The simplest form of the multidimensional array is the 2-dimensional array. A 2-dimensional array is a list of one-dimensional arrays.
A 2-dimensional array can be thought of as a table, which has x number of rows and y number of columns. Following is a 2-dimensional array, which contains 3 rows and 4 columns −
Two Dimensional Arrays in C#
Thus, every element in the array a is identified by an element name of the form a[ i , j ], where a is the name of the array, and i and j are the subscripts that uniquely identify each element in array a.
Initializing Two-Dimensional Arrays
int [,] a = new int [3,4] {
{0, 1, 2, 3} , /* initializers for row indexed by 0 */
{4, 5, 6, 7} , /* initializers for row indexed by 1 */
{8, 9, 10, 11} /* initializers for row indexed by 2 */
};
Explain above code:
new int [**3**,4] **3** denoting to rows like how may object in array
eg:
{0, 1, 2, 3} ,
{4, 5, 6, 7} ,
{8, 9, 10, 11}
new int [3,**4**] **4** denoting to columns like total value in object (4 columns)
eg:
{0, 1, 2, 3}
Let us check the program to handle a two dimensional array
using System;
namespace ArrayApplication {
class MyArray {
static void Main(string[] args) {
/* an array with 5 rows and 2 columns*/
int[,] a = new int[5, 2] {{0,0}, {1,2}, {2,4}, {3,6}, {4,8} };
int i, j;
/* output each array element's value */
for (i = 0; i < 5; i++) {
for (j = 0; j < 2; j++) {
Console.WriteLine("a[{0},{1}] = {2}", i, j, a[i,j]);
}
}
Console.ReadKey();
}
}
}
Output:
a[0,0]: 0
a[0,1]: 0
a[1,0]: 1
a[1,1]: 2
a[2,0]: 2
a[2,1]: 4
a[3,0]: 3
a[3,1]: 6
a[4,0]: 4
a[4,1]: 8