So I am working on a side scrolling platformer. I changed some code to allow for scrolling backgrounds, which is working perfectly; however now when my levels advance the background stays the same.
This is the class for Layer
class Layer
{
public Texture2D[] Textures { get; private set; }
public float ScrollRate { get; private set; }
public Layer(ContentManager content, string basePath, float scrollRate)
{
// Assumes each layer only has 3 segments.
Textures = new Texture2D[3];
for (int i = 0; i < 3; ++i)
Textures[i] = content.Load<Texture2D>(basePath + "_" + i);
ScrollRate = scrollRate;
}
public void Draw(SpriteBatch spriteBatch, float cameraPosition)
{
// Assume each segment is the same width.
int segmentWidth = Textures[0].Width;
// Calculate which segments to draw and how much to offset them.
float x = cameraPosition * ScrollRate;
int leftSegment = (int)Math.Floor(x / segmentWidth);
int rightSegment = leftSegment + 1;
x = (x / segmentWidth - leftSegment) * -segmentWidth;
spriteBatch.Draw(Textures[leftSegment % Textures.Length], new Vector2(x, 0.0f), Color.White);
spriteBatch.Draw(Textures[rightSegment % Textures.Length], new Vector2(x + segmentWidth, 0.0f), Color.White);
}
}
And this is level constructor in the loading part of class level
public Level(IServiceProvider serviceProvider, Stream fileStream, int levelIndex)
{
// Create a new content manager to load content used just by this level.
content = new ContentManager(serviceProvider, "Content");
timeRemaining = TimeSpan.FromMinutes(2.0);
LoadTiles(fileStream);
// Load background layer textures.
layers = new Layer[3];
layers[0] = new Layer(Content, "Backgrounds/Layer0", 0.2f);
layers[1] = new Layer(Content, "Backgrounds/Layer1", 0.5f);
layers[2] = new Layer(Content, "Backgrounds/Layer2", 0.8f);
// Load sounds.
exitReachedSound = Content.Load<SoundEffect>("Sounds/ExitReached");
}
It looked like this before I changed it to allow for scrolling
layers = new Texture2D[3];
for (int i = 0; i < layers.Length; ++i)
{
// Choose a random segment if each background layer for level variety.
int segmentIndex = random.Next(3);
layers[i] = Content.Load<Texture2D>("Backgrounds/Layer" + i + "_" + segmentIndex);
}
Thank you for your time.
Ok, I note a couple of things:
When you changed it to allow for scrolling, your layers went from one segment to 3.
The image for each single-segment layer was chosen at random (out of three), now each layer has all three segments in order, as loaded by your for loop.
You don't factor your level index in to the selection of your textures.
So, in the past, each level would give you a somewhat randomly selected choice of background, and now you get the same selection each time because you load segment 1, then 2, then 3 in your for loop.
Suggestions:
Re-introduce the randomness
Draw more backgrounds, and factor your level index in to the choice of backgrounds
I didn't quite get what you wanted here:
spriteBatch.Draw(Textures[leftSegment % Textures.Length], new Vector2(x, 0.0f), Color.White);
spriteBatch.Draw(Textures[rightSegment % Textures.Length], new Vector2(x + segmentWidth, 0.0f), Color.White);
More precisely, these two things:
leftSegment % Texture.Length
rightSegment % Textures.Length
Have you tried to draw inserting the Indexes manually? Like...
spriteBatch.Draw(Textures[0]...
spriteBatch.Draw(Textures[1]...
...
Because those leftSegment and rightSegment are dependent of the segmentWidth, right?
int segmentWidth = Textures[0].Width;
Is that width always going to be the same? If yes, there's no need to do this on the texture index on the draw method, because it'll always be the same indexes...
I didn't quite understand what you tried there(I'm sorry, I'm kinda dumb), but it just sounds... strange to me..
I don't know, maybe I'm just saying non-sense stuff...
Related
I need to draw a terrain and have a camera with surfacefollow in XNA, but in some places it doesnt draw what is suposed to draw and draws the terrain behind it. Any suggestions of what it may be?
public void Draw()
{
camera.Effect.TextureEnabled = true;
camera.Effect.VertexColorEnabled = false;
camera.Effect.World = worldMatrix;
camera.Effect.Texture = texture;
camera.Effect.CurrentTechnique.Passes[0].Apply();
device.SetVertexBuffer(vertexBuffer);
device.Indices = indexBuffer;
for (int i = 1; i < (alturas.Height - 1); i++)
{
device.DrawIndexedPrimitives(PrimitiveType.TriangleStrip, 0, 0, heights.Width * 2, (heights.Width * 2) * i, (heights.Width * 2) - 2);
}
}
heights is the texture of the height map. The construction of the vertices is working fine.
So I'm new to XNA and am attempting to draw multiple sprites to the screen in the easiest way. I want each sprite to be incremented in the X axis so that I am left with multiple sprites drawn across the screen.
I have defined:
Vector2 pos;
In the LoadContent function I have:
pos = new Vector2(0, 0);
and in Draw I have:
spriteBatch.Draw (circle, pos, Color.White); //Draws sprite to screen in correct position
spriteBatch.Draw(circle, (pos.X += 1), Color.White); //causes error and doesnt draw
Hopefully I've explained this well enough and you can see what I'm trying to do, the compiler doesn't agree with (pos.X += 50) (me trying to increment the X position by 50).
I know I could go about this a longer way and create a new Vector2 for each draw but that would create multiple lines of what I think surely is unnecessary code and there must be a quick way like this to go about doing it?
The method signature of Draw expects the second parameter to be a Vector2, right?
If so, then (pos.X += 1) isn't a Vector2. It's a statement which increments the X parameter of the pos Vector2 variable, but the statement doesn't return an instance of a Vector2 object.
Edit: Code is as follows:
public void DrawSprites()
{
// setup circle here
// setup spritebatch here
// setup initial pos here
// setup MAX_ITERATIONS here
var INCREMENT_VALUE = 50;
for (var i = 0; i < MAX_ITERATIONS; i++) {
var iteratedPos = pos + new Vector2((INCREMENT_VALUE * i), 0); // per Nikola's comment
spriteBatch.Draw(circle, iteratedPos, 0), Color.White);
}
}
You need to make a list of the draw rectangles of your textures.
//implement Texture2D (called "image") and SpriteBatch (called "spriteBatch")
List<Rectangle> rectangles = new List<Rectangle>();
const int ITERATIONS = 10; //or whatever you want the iterations to be
const int INCREMENT_VALUE = 50; //again, whatever you want it to be
for (int i = 0; i < ITERATIONS; i++)
{
for (int j = 0; j < rectangles.Count; j++)
{
rectangles[j].X += INCREMENT_VALUE * i;
spriteBatch.Draw(image, rectangles[j], Color.White);
}
}
Like said earlier, you need a list of Rectangles that have all the image rectangles in them. Hope I helped, and good luck!
I have some problems on the display of my model with texture.
Everything works perfectly, however, I use a loop to repeat the texture to represent a floor 20 X 20 on the screen. My texture is repeated correctly. But I do not understand why all my textures generate a flicker ...
I noticed that images are superimposed on each other. I'm sure I checked my loop is coded correctly.
see screenshot:
my code (loop function generation ground):
//Function draw - ground land
private void draw_groundLand(Vector3 position_model_origin)
{
//example generation mode 4x4 cubes
int[,,] MatriceWorldCube = new int[1,2,2];
MatriceWorldCube[0, 0, 0] = 1;
MatriceWorldCube[0, 0, 1] = 1;
MatriceWorldCube[0, 1, 0] = 2;
MatriceWorldCube[0, 1, 1] = 1;
int height = MatriceWorldCube.GetLength(0);
int width = MatriceWorldCube.GetLength(1);
int length = MatriceWorldCube.GetLength(2);
Vector3 pos_reference = position_model_origin;
for (int thickness = 0; thickness < height; thickness ++)
{
for (int column = 0; column < width; column ++)
{
for (int line = 0; line < length ; line ++)
{
// Copy any parent transforms.
Matrix[] transforms = new Matrix[model_ground_land1.Bones.Count];
model_ground_land1.CopyAbsoluteBoneTransformsTo(transforms);
// Draw the model. A model can have multiple meshes, so loop.
foreach (ModelMesh mesh in model_ground_land1.Meshes)
{
// This is where the mesh orientation is set, as well
// as our camera and projection.
foreach (BasicEffect effect in mesh.Effects)
{
effect.EnableDefaultLighting();
effect.World = transforms[mesh.ParentBone.Index] *
Matrix.CreateRotationY(cubeGroundLand1_modelRotation) * Matrix.CreateTranslation(position_model_origin);
effect.View = View;
effect.Projection = Projection;
}
// Draw the mesh, using the effects set above.
mesh.Draw();
}
position_model_origin.X = (float)(line +1);
}
position_model_origin.X = pos_reference.X;
position_model_origin.Z = (float)(column +1);
}
position_model_origin.Z = pos_reference.Z;
position_model_origin.Y = (float)(thickness+1);
}
position_model_origin.Y = pos_reference.Y;
position_model_origin = pos_reference;
}
Thank you in advance for your help. I lose patience (over a whole weekend ^ ^)
It's Z-fighting. Z-buffer precision falling with distance so far objects have more "flickers" GPU can't figure out which polygon is on top because tail of the z-buffer value isn't precise enough to distinguish 2 almost equal values.
You have 6 ways to fix it:
Move geometry or do not display part that is below.
Use something like polygon offset in OpenGL
Use CPU z-sorting instead of z-buffer.
Use only one object with 2 textures instead of 2 objects + some shaders (i don't know what exactly you are trying to achieve)
Use larger z-buffer.
Move clip planes closer to each other it will increase precision.
This method that draws my tiles seems to be quite slow, Im not sure exactly whats wrong, it belive my culling method isnt working and is drawing stuff offscreen, but im not completeley sure. Here it is:
// Calculate the visible range of tiles.
int left = (int)Math.Floor(cameraPosition.X / 16);
int right = left + spriteBatch.GraphicsDevice.Viewport.Width / 16;
right = Math.Min(right, Width) + 1; // Width -1 originally - didn't look good as tiles drawn on screen
if (right > tiles.GetUpperBound(0))
right = tiles.GetUpperBound(0) + 1; // adding 1 to get the last right tile drawn
int top = (int)Math.Floor(cameraPosition.Y / 16);
int bottom = left + spriteBatch.GraphicsDevice.Viewport.Height/ 16;
bottom = Math.Min(bottom, Height) + 1; // Height -1 originally - didn't look good as tiles drawn on screen
if (bottom > tiles.GetUpperBound(1))
bottom = tiles.GetUpperBound(1) + 1; // adding 1 to get the last bottom tile drawn
// For each tile position
for (int y = top; y < bottom; ++y)
{
for (int x = left; x < right; ++x)
{
// If there is a visible tile in that position, draw it
if (tiles[x, y].BlockType.Name != "Blank")
{
Texture2D texture = tileContent["DirtBlock_" + getTileSetType(tiles,x,y)];
spriteBatch.Draw(texture, new Vector2(x * 16, y * 16), Color.White);
if (isMinimap)
spriteBatch.Draw(pixel, new Vector2(30+x, 30+y), Color.White);
}
}
}
GetTileSetTypes is a function to get what tiles are around it, for different textures, like DirtBlock_North, DirtBlock_Center, etc.
Tile content is just a class with my block textures.
Try changing SpriteBatch.Begin to defered and combining all of the tiles onto one texture.
See this GameDev question for info about why deferred is most likely the fastest option for you.
Also realize that every time you draw a new texture you have to take the old one out of the GPU and put the new one in. This process is called texture swapping and usually isn't an issue but you are swapping textures twice per tile which is likely to impact performance noticeably.
This can be fixed by combining multiple sprites onto one texture and using the source rectangle argument. This allows you to draw multiple sprites without a texture swap. There are a few OSS libraries for this. Sprite Sheet Packer is my personal favorite.
Unfortunantly without the project and a profiler I'm just guessing; however, these are the two biggest gotchas for rendering tilemaps I know of. I can't really see anything wrong from here. Below is the code I use to draw my tile maps and as you see its very similar to yours.
If all else fails I would suggest using a profiler to figure out which bits are running slowly.
//Init the holder
_holder = new Rectangle(0, 0, TileWidth, TileHeight);
//Figure out the min and max tile indices to draw
var minX = Math.Max((int)Math.Floor((float)worldArea.Left / TileWidth), 0);
var maxX = Math.Min((int)Math.Ceiling((float)worldArea.Right / TileWidth), Width);
var minY = Math.Max((int)Math.Floor((float)worldArea.Top / TileHeight), 0);
var maxY = Math.Min((int)Math.Ceiling((float)worldArea.Bottom / TileHeight), Height);
for (var y = minY; y < maxY; y++) {
for (var x = minX; x < maxX; x++) {
_holder.X = x * TileWidth;
_holder.Y = y * TileHeight;
var t = tileLayer[y * Width + x];
spriteBatch.Draw(
t.Texture,
_holder,
t.SourceRectangle,
Color.White,
0,
Vector2.Zero,
t.SpriteEffects,
0);
}
}
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...