Woohoo, another massive Mega wall-of-text that is going to be TL;DR'd by the majority of the internet!
The reason I've been offline for a while is twofold, but both sides to the problem lie in the fact that I don't have my own internet connection, I use my dad's. Independance is great, but requires gainful employment first. And I still haven't got the latter. So basically, because of an argument/dispute/I'mRightSoShutUp I had a few days before writing this, I was told:"OK. Sure. You're right, but you can't use the internet now for <x> amount of time." (Where x is an unknown variable that depends on whether my parents decide I'm being condescending enough to deserve my time on the internet).Anyway, it doesn't hurt me. Since I do most of my work offline, I got a lot done. I did kinda just disappear from the Minecraft server, which was annoying (I had just started building a shelter/outhouse when I was 'kicked' by Windows >_>). Anyway… disclaimer:This blog may contain traces of Error, a sprinkling of Length, and just a Dash of Awesome…As the title explains, I'm now working with XNA (Which is made a lot easier now that I understand the ins and outs of the C# language).So I sat down this evening and opened the VC# Express 2010 IDE, and started working on a platformer. As per tradition, I'm using placeholder art, but even so, to my dismay I discovered that there is no built in support for animated sprites. So the first thing I had to do? Make a class that can load and draw animated sprites with all the bells and whistles (Origin, Scaling, Flipping). And I did. The result is as follows:Source codeclass MXSprite
{
private Texture2D spriteData;
private int x;
private int y;
private int width;
private int height;
private int origin_x;
private int origin_y;
private int numframes;
private double speed;
private double scale;
private bool flip_x;
private bool flip_y;
private bool isLoaded;
// Current Frame and Frame Counter
private int c_frame;
private double fcount;
// Status values and properties
private bool isFrameEnd;
private bool IsFrameEnd { get { return this.isFrameEnd; } }
private bool isFrameStart;
private bool IsFrameStart { get { return this.isFrameStart; } }
// A sprite that can handle multiple frames in a single image.
// I'll also make a MXListSprite that uses a container.
public MXSprite()
{
spriteData = null;
x = y = 0;
width = height = 0;
numframes = 0;
scale = speed = 1.0;
flip_x = false;
flip_y = false;
isLoaded = false;
}
public void Load(ContentManager cmanager, string resName)
{
if (isLoaded) return;
spriteData = cmanager.Load<Texture2D>(resName);
isLoaded = true;
}
public void Setup(int width, int height, double speed = 1.0, int origin_x = 0, int origin_y = 0)
{
this.width = width;
this.height = height;
this.speed = speed;
this.origin_x = origin_x;
this.origin_y = origin_y;
this.numframes = spriteData.Width / this.width;
this.c_frame = 0;
this.fcount = 0.0;
}
public void Step()
{
// Perform a frame step. Adjust if necessary
fcount += speed;
if (fcount >= 1.0)
{
fcount = 0.0; // This could be clipped instead of clamped
// while(fcount >= 1.0)fcount -= 1.0; // This will work too, and retain the fractional part.
// But it's a potential bottleneck if somebody accidentally sets
// the Speed property to something stupid like 500.
c_frame += 1;
}
if (c_frame == numframes - 1)
{
isFrameEnd = true;
}
else isFrameEnd = false;
if (c_frame > numframes - 1)
{
c_frame = 0;
}
if (c_frame == 0)
{
isFrameStart = true;
}
else isFrameStart = false;
}
// Methods
public void Draw(SpriteBatch spriteBatch)
{
Rectangle srcRect = new Rectangle(this.c_frame * this.width, 0, this.width, this.height);
SpriteEffects fx = (flip_x ? SpriteEffects.FlipHorizontally : 0) | (flip_y ? SpriteEffects.FlipVertically : 0);
spriteBatch.Draw(spriteData, new Vector2(x, y), srcRect, Color.White, (float)0.0, new Vector2(origin_x, origin_y), new Vector2((float)
scale,(float)scale), SpriteEffects.None, (float)0.0);
}
// Properties
public int X { get { return this.x; } set { this.x = value; } }
public int Y { get { return this.y; } set { this.y = value; } }
public int Width { get { return this.width; } }
public int Height { get { return this.height; } }
public int NumFrames { get { return this.numframes; } }
public int CurrentFrame { get { return this.c_frame; } set { this.c_frame = (value < this.numframes ? value : numframes-1); } }
public double Speed { get { return this.speed; } set { this.speed = value; } }
public double Scale { get { return this.scale; } set { this.scale = value; } }
public bool FlipX { get { return this.flip_x; } set { this.flip_x = value; } }
public bool FlipY { get { return this.flip_y; } set { this.flip_y = value; } }
public bool IsLoaded { get { return this.isLoaded; } }
}
// This array stores the tiles.
Tile[] tiles; // Size varies depending on map size
// This is the absolute view coordinate being 'mapped' to tile-space
// I offset it by (-1,-1) to render the border tiles
int tvx = (view_x / tile_width)-1;
int tvy = (view_y / tile_height)-1;
// Calculate the width and height of the view in tiles
int tvw = view_width/tile_width;
int tvh = view_height/tile_height;
// Calculate the start index
// map_width is the width of the map in tiles, _not the absolute value_
int start_index = tvy * map_width + tvx;
// Number of tiles to process + 2 extra rows|columns
int p_count = (tvw*tvh) + (map_width*2);
// Clamp values
if(start_index < 0)start_index = 0;
if(start_index > tiles.count()-1)break; // Out of bounds
for(int i = start_index; i < start_index + p_count; i++)
{
// Check values for validity and break loop if out of bounds
if(i > tiles.count()-1)break;
// Otherwise process
DrawTile(i);
}
public void Draw(SpriteBatch sb, Rectangle? viewRect = null)
{
int w = 32;
int h = 32;
Rectangle rr;
if (viewRect != null)
{
rr = (Rectangle)viewRect;
}
else rr = new Rectangle(0, 0, 512, 480);
int tvx = (rr.X / w)-1;
int tvy = (rr.Y / h)-1;
int tvw = (rr.Width / w)+2;
int tvh = (rr.Height / h)+2;
for (int iy = tvy; iy < tvy + tvh; iy++)
for (int ix = tvx; ix < tvx + tvw; ix++)
{
int i = iy * width + ix;
if (i > numtiles - 1)
{
i = (i % numtiles);
if (i > numtiles - 1) i = numtiles - 1;
}
if (i < 0)
{
i = -i;
i = numtiles - (i % numtiles);
}
int x = ix * w;
int y = iy * h;
x -= rr.X;
y -= rr.Y;
if (i > numtiles - 1) i = numtiles - 1;
if (backlayer[i].t >= 0) myTileset.Draw(sb, backlayer[i].t, x, y, 1.0);
if (midlayer[i].t >= 0) myTileset.Draw(sb, midlayer[i].t, x, y, 0.9);
if (frontlayer[i].t >= 0) myTileset.Draw(sb, frontlayer[i].t, x, y, 0.1);
}
}
The TL:DR section was harder to read than the blog. lol
Which is precisely why I made it. :3
What a coincidence… I'm trying to make levels too for some school project (SFML/C++). I recalled you mentioning TileStudio a few months ago, but since I'm
stupidstubbornawesome I didn't use it and instead am making my really shitty, basic level editor. Hurray! Except I'm only storing the index/x/y for each tile AND THAT'S IT. (I'm not going for limitations on a fixed tile size, so the x/y are not in tile coordinates, but in pixels.)My tiles aren't limited in size, the tile dimensions are stored in the file along with everything else, meaning I can quickly swap between 16x16, 32x32 or even 16x24 if I wanted to.
Ah, I see what you mean. I do have a layer offset that's absolute, but… yeah. I could add in another value for an offset for each tile, which might be handy. But personally, I don't see myself using it. Also, using the tile-coordinate system means that my collision detection code for the tile bounds is extremely quick.
=PIs there that much of a difference between checking different values for variables if they're the same size? (eg 4 vs 128, etc) They'd be ints either way. I always thought it was the same or negligible… But I'm not exactly an EXPERT PROGRAMMER…
I could store the value as a float, yes, and it is the same size as a float, but: TileStudio doesn't support anything but grid-values, so what's the point? :P
Also, I never use offset tiles. Objects, sure, but tiles? No.…what? I was asking about the speed difference for using, say, ints ranging in the 100s vs in the 1000s. (like if you did it based on the tile grid compared to the raw pixel. like using 1 instead of 16, or 50 instead of 800)
Ah. Misread. Minecraft does that to me. Eh… No. There's no difference in using lower valued ints or higher valued ints, unless I was iterating through an array of length <x> where x is an int that is thousands of elements long. Technically, reading in the int values as 32-bit ints can take slightly longer, but… no. No difference. Except that, in collision detection code, I want to perhaps check the entire width of the object, which might be 16, which might mean that to get an accurate collision, I'd have to potentially iterate 16 times (I'd actually do three checks: min, mid and max.