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);
}
}
I just do if x + w > other.x and x < other.x + other.w etc… is it really that resource draining? I'm assuming with the collision detection you're going to have, say, the player/bullet/etc not evenly alligned to the grid, RIGHT? Doesn't that make it…the same speed?
The > and < operators can't really be that slow, right? They don't honestly check every possible value… right? That can't be right from what I've noticed…This is probably just a case of misunderstanding… or something…Game Maker: draw_sprite_ext
XNA: HellC++: Don't fucking askThough, I thought you could do it in less code, but it's been long enough that I've pretty much forgotten.Very cool. Hope more progress comes!
@Rob: No, the comparison is the same. But it's the lookup, finding out what the collision data for the current tile is from an arbitrarily sized array. My code jumps directly to the element, thanks to the way it's arranged. No having to loop through an unnecessary amount of data. Same for the draw-code.
< and > map more or less to a CMP + JG/JL command in assembly. No difference.@Kilin:Not really that much different, except that GM nicely handles animation. The color blending and sprite blending, origin setting and rotations can be easily done in both XNA and SFML (And drawing the sprite data).Great, now you made me want to try MS XNA >:(
How hard is it to set up, anyway?And is it free or commercial? I remember my Microsoft C++ Whateverstudio expiring :(And why is your sprite so blurry?Older versions of Visual Studio are free, Cesque (or just the "Express" versions), and XNA is free anyway, unless you pay some annual subscription to be listed as a developer. If you're a student and your school is listed in MSDNAA, you can get the latest version of Visual Studio Pro for free.
XNA, if all you've done is GM, is kind of a bitch to get used to, but it's much easier than making a game in C++ from the very start. It gives you tons of tools and premade objects, functions, etc. This includes things like SpriteBatch (helps you draw, resize, animate, and do anything to sprites), Texture2D (used to define the image itself), a simplified vector system for coordinates, and shit like that.It's a big step up from GM, and doesn't have any sort of GUI, but it's an excellent development tool. Because another nice thing about Visual Studio is how easy it is to make your own programs, so you can make your own sprite/object/room editors, export them to files, and read them accordingly. No more relying on GM's limited capabilities. You decide your own.And if you're an MS-phobe, then you shouldn't be using XNA in the first place!