Subscribe to GameFromScratch on YouTube Support GameFromScratch on Patreon

9. May 2012

 

As you may know, if you want to publish on MacOS or on any iOS devices, you need to use animage Apple computer.  This can represent a rather large expense to many Indie developers ( no, I am not going to get into the ancient Mac price war argument! ).  What if you want to test with a Mac, but can’t justify the cost ( or space like in my office ) of another computer?  Wouldn’t it be great if you could rent a Mac as you needed it?

 

Well, that is exactly what MacInCloud offers!  Basically they rent out Mac machines over the internet on a time share basis.  Even better, these Macs are obviously targeted towards (game) developers, as it comes pre-loaded with a ton of game development software. This sounds wonderful to me, as even though I own a Mac ( an iMac ), I am more often on the road than not and I have no desire to lug around two laptops with me.  I’ve tried remote desktop’ing into my home Mac on occasion, but the experience was never all that nice.  Let’s take a look at how well MacInCloud pull it off!

 

 

If you are the type that flips ahead to ruin the ending, click here to jump to the verdict.

What does it cost?

 

Well that depends.  There are 4 ways to pay, and each one has a serious impact on the cost.  First and most expensive, you can pay as you go, which as of the time this was written is 1$ per hour.  Next, you can rent for the entire day for 8$.  You can also rent by the week and month, these prices all depend on the amount of time you need per day.

 

Here is a breakdown of prices for renting per week and month.

 

  Weekly rental Monthly rental
3 hours a day usage 12$ 20$
5 hours a day usage 15$ 25$
8 hours a day usage 19$ 30$
Unlimited usage 22$ 49$

 

Weekly and monthly rentals both include the first day for free ( well, 1 cent ), you can think of this as a trial period.

Note, and this is very important. You will be rebilled automatically until you cancel your subscription using PayPal! Not a big deal, but something you should be aware of upfront.  This applies to daily, weekly and monthly plans, but not to pay as you go plans. I will cover canceling your subscription later on, the process is pretty simple but a tad confusing.

 

So picking the right pricing scheme is a very tricky process.  If you just need a couple hours, pay as you go is obviously the ideal plan, but gets pricey quick.  For example on the 8 hours monthly plan, assuming you maxed your usage out, your hourly rate would be just 13 cents an hour!  On the other hand, paying for hours you aren’t going to use is just stupid.  It’s a tricky balancing act deciding which plan is right for you.  Initially I am going to go with the monthly 5 hours a day plan.

 

Sign up process

 

Sign up is almost as simple as it gets.  First you need to create a site account here.  That is just a website account, your email and password only, not a subscription.  In my case I am going with a monthly subscription, and its simply a matter of going to the Pricing menu, selecting the plan you want  then click Subscribe.  This will bring you to PayPal ( which is the only payment option ).

 

Here is the line item as it appears in PayPal:

image

 

You initially are billed only a penny for your first day.  A bit of a warning, this kind of stuff can be problematic with Visa/Mastercard.  I’ve received calls from fraud protection when really small transactions are charged against my card.  Don’t sweat it, it’s not a bad thing if you receive this call, it’s just the banks computers being cautious, just warning you it might happen.  If you cancel in the first day, you will not be billed for the service.

image

 

You will now be redirected back to macincloud.com and presented with your Mac server name as well as your username and password.  This information will also have been mailed to whatever email account is linked with your PayPal account.  You are now signed up.

 

Logging In

 

There are two ways to log in to MacInCloud.  The easiest is to use their browser based client, which recommends you use Google Chrome.  I will start with this method.  Save your credentials from the the welcome email/page and head on over to http://www.macincloud.com/member-area/browserconnect.

 

image

 

This part of the process isn’t as obvious as it should be, as you currently have two sets of user names and passwords, the one you got when you signed up for the site and the one you got issued after paying.  They did a really poor job of telling you which username/password to use.  Coincidentally, it’s the site ( your email ) password you use here, not the one they assigned you.  Note to MacInCloud, you should really add some text to explain this!

 

Anyways, once you have logged in, you will be brought to this screen:

image

 

This is where you use the credentials they assigned you.

Somewhat annoyingly, these are the resolutions you are limited to:

image

 

My native resolutions are 1920x1080 and 1366x864, so these selections aren’t ideal for me.  This was one of the problems I had when remoting my personal iMac and I did find it irritating.  Also, given the fact you are using credentials that were assigned to you, it would be really nice if this screen had a “remember my username and password” option like the prior screen where you need it less.

 

You are greeted by this screen initially.

image

 

 

Then…

 

Well, then it doesn’t work.  It worked exactly once, I resized the window and I got a Java EOF error.  Since then I have been unable to log back in using the webclient.  I tried in Firefox and same problem, then I tried in IE9 and it simply isn’t supported.

 

So, if you are here for the web client… go elsewhere, it simply isn’t ready for prime time.  Plus in the very brief period of time I was connected, it was exceedingly slow.

 

 

Connecting using Remote Desktop

This is the main way to connect.  You download the zipped connection file in the welcome email, extract it and you are presented with the following options. 

 

image

 

Double click the selection that is most appropriate to you and your connection.

 

image

 

Use the provided username and password to log in.

 

And you are now at your desktop.  Can you tell by the fact the iPhone simulator is loaded already exactly what kind of developers they are targeting?

 

image

 

 

So what is installed already?

 

This is one area where MacInCloud really shines.  The following software was pre-installed:

  • Adobe DreamWeaver CS 5.5
  • Adobe Fireworks CS 5.1
  • Adobe Flash CS 5.5
  • Adobe Illustrator CS 5.1
  • Adobe InDesign CS 5.5
  • Adobe Media Encoder CS 5.5
  • Adobe Photoshop CS 5.1
  • Cocos2d 1.0.1
  • Corona SDK stable and development versions
  • Dropbox
  • Eclipse
  • Firefox
  • Chrome
  • GameSalad
  • Office 2011
  • MonoDevelop
  • OmniGraffle Professional
  • RhoStudio
  • Stonetrip
  • TestFlightSDK
  • TextWrangler
  • Titanium Studio
  • Xcode

 

Curiously absent?  Unity or any 3D applications.  Otherwise it’s pretty much a whose who of game development applications.

 

What the heck is going on here?

 

This may be one of those weird karma issues, but in my brief period trying to test this, my connection dropped… a lot.  Within an hour I had 10 disconnects from my ISP ( Rogers in Canada ).  For the record I haven’t had my internet connection drop in months, and I’ve experienced 10 in the last two hours?

 

Again, as I said, it might be horrifically bad timing…

 

Or MacInCloud and my ISP don’t play well together.  I know not to blame RDP, as I literally use it every day to remote control my servers.  However, when I used my Phone’s LTE connection, I didn’t have a single issue.

 

 

What’s the performance like?

 

Well, this is the biggie, isn’t it?  How well does it actually perform.

 

I tested with two devices, my home cable connection and my phone LTE connection.  The speed of your connection is obviously going to be a big factor, but both connections should be considered above average.

 

I can’t really describe how it performs, so instead I will screen capture both in action and let you judge for yourself.  I start each with a speedguide test so you can gauge my connection against your own.  To contrast, I remote desktop to one of my servers ( ironically enough, hosted by the same company MacInCloud use ) to compare against remote controlling a Windows server.  HD movie here.

 

Evaluating MacInCloud performance

One word, disappointing.

 

How do I quit?

 

Log in to your PayPal account.

 

On the main screen, locate your “My Recent Activity” part, click the details link:

image

 

Now in the subscription details, at the very top you should see:

image

 

Click the Cancel link

image

 

Click the Cancel Profile button.

 

And…

image

 

Your PayPal subscription is now canceled.    If you go back to your recent activity, it should now look like:

 

image

 

And now you will no longer be billed.

 

 

So then, what’s the verdict?

 

Not great I am afraid to say.  While making the screenshots on how to cancel, I went ahead and cancelled.  When I signed up I fully intended to go for the full month but the performance was just so appallingly bad.  Frankly when I remote controlled my iMac from home the performance was actually quite a bit better than with MacInCloud.com.

 

It is possible I just caught them on a bad bandwidth day, so you have little to lose for trying it yourself.  Perhaps in your case it will work better.  Both of my connections are quite quick and neither was even close to functional.

 

Now, if you just want to log into a Mac to compile your code and publish it to the AppStore, this service might prove useful.  However, to actually do any development, I just couldn’t see anyone having the patience. Actually debugging a running game I think would be simply impossible.

 

I hope MacInCloud.com massively increase the speed or that I caught them on a bad day, as their idea is brilliant.  The execution however leaves a lot to be desired.

 

You can of course try it for yourself, it won’t cost you a cent!  Well… it will cost you exactly one cent.

General

9. May 2012

 

 

A few days back I was contacted about translating some of my Vita tutorials into Spanish.  Well the results of that conversation are available here on IndiceVita.es.  This is my imagespritesheet tutorial translated into Spanish.  I believe they are going to translate more tutorials in the future, so if you prefer reading Spanish to English, be sure to check it out! You need to register to be able to access the forum.

 

Is the translation good?  Beats the heck out of me!  My Spanish abilities begin and end with “La cerveza, por favor”, which frankly has served me well! Smile

 

On this topic, if you are interested in translating any of my content to another language, either hosted on your own site or hosted here, feel free!  The only thing I ask in return is a link back to the original source tutorial ( AKA, credit ) and a link to the finished product, either emailed to me ( mike [at] gamefromscratch [dot] com ) or as a comment.  It’s always cool seeing something you’ve written in a different language… even if you are reading it through a half illegible Google translation!

General

9. May 2012

 

This post is waaayyyyyy off the topic of game development but it’s one of those things I have been fighting with for months so I figured I’d share the solution in case any of you are having the same issue.

 

Basically with one of my laptops I would often see my computer crawl to a screeching halt, to the point of nearly uselessness.  Generally the culprit would be MsMpEng.exe chewing away at my CPU, generally maxing out a core completely.  On occasion it would be Thunderbird ( my email client ) maxing out a core, but frankly this was pretty typical Thunderbird behavior( when indexing or downloading mail it used a lot of CPU ), so I would generally ignore it an expect it to go away.  Normally I would simply kill off the MsMpEng task as well as the msseces.exe ( which would simply restart MsMpEng if not killed ) and the problem would go away.  Not ideal killing off my anti-virus, but it’s one of those “I’ll look into it later” things.

 

Of course, later always became later until this morning when I simply got too fed up with it.  I was about half an inch away from just uninstalling Microsoft Security Essentials and going with a different anti-virus app.  This morning, both Thunderbird AND MsMpEng were taking turns maxing out my cores, so I finally put one and one together…  The fix is remarkably easy, in Microsoft Security Essentials, go to the Settings tab, select Exclude processes and add the Thunderbird.exe process like such:

 

 

image

 

 

Problem… finally… solved.

 

Alright, now that my computer is no longer acting like a 386 with the turbo button disabled, back to making games!

Totally Off Topic

9. May 2012

 

I logged in to my Safari account this morning as part of my daily ritual to check which newbookcover books have been added and I discover they’ve added Beginning iOS 3D Unreal Games Development.  So what’s so special about this book?  Well, there are a couple of things… First off, although there are a half dozen books on using UDK on the market, this is to my knowledge the first book focused specifically on mobile, the area I am most interested in.  Second, I’ve been meaning to look closer into the Unreal engine and this book looks intriguing.  So this book has certainly been added to my reading list.

 

For those of you that don’t yet have a Safari account, the book is also available on Amazon.com and is shockingly inexpensive right now.  It’s currently $24.34 for the dead tree version and $17.59 for the kindle version.  If only all tech books were priced like this I wouldn’t have to subscribe to Safari! Smile

 

Anyone out there doing mobile development with UDK?  I would be curious to hear what your experiences have been.

General

7. May 2012

 

As the title suggests, this thread is going to be about using sprite sheets with PS Studio.  For those of you unfamiliar with the term, a spritesheet is a single image with multiple sprites.  You generally group your sprites together on a single sheet as it is much more efficient for loading and generally performs better than loading one texture per sprite.

 

EDIT(5/11/2012): If you are working with an actual Vita device and using the beta SDK, there is a bug on the Vita XML that prevents this from working. Read this post for a simple workaround. Hopefully in time Sony fixes this and you no longer need to apply the bug fix. Note, the problem only occurs on an actual device.

 

Instead of showing you how to generate a spritesheet, I am going to recycle a previous post I made on creating a spritesheet using Daz3D.  Fortunately Daz Studio is still available for free if you want to follow along.  Of course you can create your spritesheet however you want, or can simply download a freely available spritesheet such as those available at opengameart.org.  Or of course you can just use my sheet which will be available later.

 

In this example, I am going to use a different program than the GIMP for assembling the spritesheet.  The end result of my Daz tutorial is a directory full of 128x96 images like these:

 

Walk_left00Walk_left01Walk_left02

 

You can download a zipped copy of the sprites I rendered right here.

 

I generated 19 frames of walking animation in each direction.  This time I am going to use the free version of the tool TexturePacker to generate my sheet.  Download and fire up TexturePacker and you will be greeted with this interface:

 

image

 

The first thing you want to do is add your sprites to the sprite palette. You can either drag and drop the folder ( using Windows Explorer ) containing your sprites to the sprite panel on the right or hit the Add Sprites button and select the sprites individually.  Add all of the sprites you just created in Daz3D ( or the folder you downloaded and unzipped ) using either method.  Dropping a folder will automatically add those sprites in a folder by that name keeping things a bit more organized.  Here is the results of me dropping my walkCycle folder on the sprites panel:

 

image

 

As you can see, it added all of the sprites under a folder named WalkCycle and automatically layed out our sprite sheet as efficiently as possible.  As this point though, we don’t really care all that much about efficiency, so we are going to make a few small changes.  In the Texture Settings panel we want to fill in a couple options.  First drop down DataFormat and change it to “Generic XML”.  Next, under Data File, pick a directory and filename to save your sprite sheet, I choose c:\temp\walk.xml.  This will automatically set the texture file to c:\temp\walk.png.  Otherwise, these are the settings I used:

 

image

 

 

You of course can use whatever settings you want, but if you want exactly the same results as me, use the above.  Now that you are ready, hit the publish button.

image

 

The end result is a PNG file and an XML document. 

 

The resulting image file:

 

walk

 

You can download the generated image file here.

 

It also generated an XML file with all of the sprite details.  The contents of that XML file look like:

 

<?xml version="1.0" encoding="UTF-8"?> <!-- Created with TexturePacker http://texturepacker.com--> <!-- $TexturePacker:SmartUpdate:a6e51795dbe6b13cd639951a1f67241c$ --> <!--Format: n => name of the sprite x => sprite x pos in texture y => sprite y pos in texture w => sprite width (may be trimmed) h => sprite height (may be trimmed) oX => sprite's x-corner offset (only available if trimmed) oY => sprite's y-corner offset (only available if trimmed) oW => sprite's original width (only available if trimmed) oH => sprite's original height (only available if trimmed) r => 'y' only set if sprite is rotated --> <TextureAtlas imagePath="walk.png" width="512" height="960"> <sprite n="Walk_left00" x="0" y="0" w="128" h="96"/> <sprite n="Walk_left01" x="128" y="0" w="128" h="96"/> <sprite n="Walk_left02" x="256" y="0" w="128" h="96"/> <sprite n="Walk_left03" x="384" y="0" w="128" h="96"/> <sprite n="Walk_left04" x="0" y="96" w="128" h="96"/> <sprite n="Walk_left05" x="128" y="96" w="128" h="96"/> <sprite n="Walk_left06" x="256" y="96" w="128" h="96"/> <sprite n="Walk_left07" x="384" y="96" w="128" h="96"/> <sprite n="Walk_left08" x="0" y="192" w="128" h="96"/> <sprite n="Walk_left09" x="128" y="192" w="128" h="96"/> <sprite n="Walk_left10" x="256" y="192" w="128" h="96"/> <sprite n="Walk_left11" x="384" y="192" w="128" h="96"/> <sprite n="Walk_left12" x="0" y="288" w="128" h="96"/> <sprite n="Walk_left13" x="128" y="288" w="128" h="96"/> <sprite n="Walk_left14" x="256" y="288" w="128" h="96"/> <sprite n="Walk_left15" x="384" y="288" w="128" h="96"/> <sprite n="Walk_left16" x="0" y="384" w="128" h="96"/> <sprite n="Walk_left17" x="128" y="384" w="128" h="96"/> <sprite n="Walk_left18" x="256" y="384" w="128" h="96"/> <sprite n="Walk_right00" x="384" y="384" w="128" h="96"/> <sprite n="Walk_right01" x="0" y="480" w="128" h="96"/> <sprite n="Walk_right02" x="128" y="480" w="128" h="96"/> <sprite n="Walk_right03" x="256" y="480" w="128" h="96"/> <sprite n="Walk_right04" x="384" y="480" w="128" h="96"/> <sprite n="Walk_right05" x="0" y="576" w="128" h="96"/> <sprite n="Walk_right06" x="128" y="576" w="128" h="96"/> <sprite n="Walk_right07" x="256" y="576" w="128" h="96"/> <sprite n="Walk_right08" x="384" y="576" w="128" h="96"/> <sprite n="Walk_right09" x="0" y="672" w="128" h="96"/> <sprite n="Walk_right10" x="128" y="672" w="128" h="96"/> <sprite n="Walk_right11" x="256" y="672" w="128" h="96"/> <sprite n="Walk_right12" x="384" y="672" w="128" h="96"/> <sprite n="Walk_right13" x="0" y="768" w="128" h="96"/> <sprite n="Walk_right14" x="128" y="768" w="128" h="96"/> <sprite n="Walk_right15" x="256" y="768" w="128" h="96"/> <sprite n="Walk_right16" x="384" y="768" w="128" h="96"/> <sprite n="Walk_right17" x="0" y="864" w="128" h="96"/> <sprite n="Walk_right18" x="128" y="864" w="128" h="96"/> </TextureAtlas>

 

You can download the xml file here. This XML file contains the details about how our original sprites are arranged within the sprite sheet and will prove useful in a moment.

 

Alright, now that we have our spritesheet and our sprite map XML file, lets fire up PSSuite and get down to some coding.  This tutorial assumes you have already gone through my earlier tutorials, at the very least the two Hello World tutorials.

 

Now create a new solution in PlayStation Studio, I called mine SpriteSheet.  We need to add a couple of references right away, add a reference to GameEngine2D, System.Xml and System.Xml.Linq. Now add your spritesheet png and xml files to the project, right click them and set their build action to content.

 

 

First we are going to create our gameloop in AppMain.cs.  I want to point something out right away… this is not the way you handle game events!  So the way things are done in this example are *NOT* the way we will do things in the future.  I went this route because you are already familiar with most of the code here.  In a (very near) future tutorial, I will show the “proper” way to handle updating game objects.

 

With that warning in place, lets take a look at AppMain.cs

 

using System; using System.Collections.Generic; using Sce.Pss.Core; using Sce.Pss.Core.Environment; using Sce.Pss.Core.Graphics; using Sce.Pss.Core.Input; using Sce.Pss.HighLevel.GameEngine2D; namespace SpriteSheet { public class AppMain { private static Walker walker; public static void Main (string[] args) { Director.Initialize(); Director.Instance.GL.Context.SetClearColor(255,255,255,0); walker = new Walker("walk.png","walk.xml"); var scene = new Scene(); scene.Camera.SetViewFromViewport(); var sprite = walker.Get("Walk_left00"); sprite.Position = scene.Camera.CalcBounds().Center; sprite.CenterSprite(); sprite.Scale = new Vector2(2,2); scene.AddChild(sprite); Director.Instance.RunWithScene(scene,true); System.Diagnostics.Stopwatch timer = new System.Diagnostics.Stopwatch(); int spriteOffset = 0; timer.Start(); bool walkLeft = true; while(true) { if(timer.ElapsedMilliseconds > 100f) { string spriteName; if(walkLeft) spriteName= "Walk_left" + spriteOffset.ToString("00"); else spriteName= "Walk_right" + spriteOffset.ToString("00"); sprite.TileIndex2D = walker.Get (spriteName).TileIndex2D; if(spriteOffset >= 18) { spriteOffset = 0; walkLeft = !walkLeft; } else spriteOffset++; timer.Reset(); timer.Start(); } Sce.Pss.HighLevel.GameEngine2D.Director.Instance.Update (); Sce.Pss.HighLevel.GameEngine2D.Director.Instance.Render(); Sce.Pss.HighLevel.GameEngine2D.Director.Instance.GL.Context.SwapBuffers(); Sce.Pss.HighLevel.GameEngine2D.Director.Instance.PostSwap(); } } } }

Click here to download AppMain.cs

 

Most of the concepts in this code we’ve seen in a previous tutorial, so I will only highlight the new details.  First thing is we declare a Walker object, this is a class we are going to create shortly that is going to handle our sprite sheet.  As you can see, the Walker constructor takes the file name of the sprite texture and xml file you created earlier as parameters. 

 

Most of the remainder of this initial code is a matter of setting up our scene and sprite objects just like we did in Hello World.  A key difference is we are getting the sprite from our walker object instead of creating it from scratch or loading it from file.  The line:

sprite.Scale = new Vector2(2,2);

Is simply to double the size of our sprite in the viewport to see it better, you can easily remove this if you wish.

 

We create a Stopwatch ( a .NET object ) and start it counting up, then enter our infinite game loop.  Lets take a look at the new logic in our game loop

 

if(timer.ElapsedMilliseconds > 100f) { string spriteName; if(walkLeft) spriteName= "Walk_left" + spriteOffset.ToString("00"); else spriteName= "Walk_right" + spriteOffset.ToString("00"); sprite.TileIndex2D = walker.Get (spriteName).TileIndex2D; if(spriteOffset >= 18) { spriteOffset = 0; walkLeft = !walkLeft; } else spriteOffset++; timer.Reset(); timer.Start(); }

 

This is basically the “guts” of our game loop.  What we are doing here is waiting for the timer to reach 1/10th of a second.  Every tenth of a second we want to move on to the next frame of animation.  However we have only 19 ( walk_left00 to walk_left18 ) frames of animation in each direction and once we hit the end of our walk cycle, we want to walk in the other direction.  So what we do here is loop through each frame of animation until we hit walk_left18 or walk_right18, at which point we flip directions by inverting the value of the walkLeft bool.  This results in changing the spriteName text prefix, otherwise we simply increment to the next frame.  Then we start our timer over again to begin the process again a tenth of a second later.  One more time, this is not the proper way to handle updates!

 

The key line in all of that was:

sprite.TileIndex2D = walker.Get (spriteName).TileIndex2D;

 

This line actually tells our sprite to refer to a different sprite within our spritesheet, causing the animation.  That will make more sense in a second when we look at the code for Walker.cs.  Speaking of which, lets create it now!

 

To create a new cs file we either click the New icon ( image) or hit CTRL + N.  In the resulting dialog select General on the left, Empty Class on the right and name it Walker.cs, like such:

 

image

 

Now open up Walker.cs and enter the following code:

 

using System; using System.Linq; using System.Xml; using System.Xml.Linq; using System.Collections.Generic; using Sce.Pss.Core.Graphics; using Sce.Pss.HighLevel.GameEngine2D; using Sce.Pss.HighLevel.GameEngine2D.Base; namespace SpriteSheet { public class Walker { private TextureInfo _textureInfo; private Texture2D _texture; private System.Collections.Generic.Dictionary<string,Sce.Pss.HighLevel.GameEngine2D.Base.Vector2i> _sprites; public Walker (string imageFilename, string imageDetailsFilename) { XDocument doc = XDocument.Load ("application/" + imageDetailsFilename); var lines = from sprite in doc.Root.Elements("sprite") select new { Name = sprite.Attribute("n").Value, X1 = (int)sprite.Attribute ("x"), Y1 = (int)sprite.Attribute ("y"), Height = (int)sprite.Attribute ("h"), Width = (int)sprite.Attribute("w") }; _sprites = new Dictionary<string,Sce.Pss.HighLevel.GameEngine2D.Base.Vector2i>(); foreach(var curLine in lines) { _sprites.Add(curLine.Name,new Vector2i((curLine.X1/curLine.Width),9-(curLine.Y1/curLine.Height))); } _texture = new Texture2D("/Application/" + imageFilename,false); _textureInfo = new TextureInfo(_texture,new Vector2i(4,10)); } ~Walker() { _texture.Dispose(); _textureInfo.Dispose (); } public Sce.Pss.HighLevel.GameEngine2D.SpriteTile Get(int x, int y) { var spriteTile = new SpriteTile(_textureInfo); spriteTile.TileIndex2D = new Vector2i(x,y); spriteTile.Quad.S = new Sce.Pss.Core.Vector2(128,96); return spriteTile; } public Sce.Pss.HighLevel.GameEngine2D.SpriteTile Get(string name) { return Get (_sprites[name].X,_sprites[name].Y); } } }

Click here to download Walker.cs

 

Our Walker class is in charge of loading and handling our spritesheet.  TextureInfo and Texture2D you have already been exposed to and in this case, nothing is different here.  Next up we declare a Dictionary of <string,Vector2i> named _sprites.  The Vector2i type is declared in GameEngine2D.Base and is simple a pair of ints. _sprites is used to store the x,y location of each individual sprite in our spritesheet, accessed by the sprites filename.

 

Let’s take a look first at the constructor.  This code is pretty straight forward if you have ever worked with Linq or XML.  We are simply opening our XML file, the filename of which we passed in from AppMain.  Note that we added “application/” to our paths, all your files on Vita are located under this subdirectory.  Next we read the XML file and extract each <sprite> entry into a new anonymous type composed of Name, X1, Y1, Height and Width.  Name represents the file name of the source image, X1 and Y1 represent the pixel coordinates within the generated spritesheet, while Height and Width are the dimensions of the sprite within the sprite sheet.  Given that all of our sprites are the same size, these values are of little importance.

 

Now that we’ve parsed out our XML file, we populate our _sprites dictionary using these values ( minus height and width which we don’t need past this point ).  However, we don’t actually want the x and y pixel coordinates of our image, but instead the offset within the spritesheet texture.  We can determine this value by dividing the X and Y values by the sprite Width and Height respectively.  Keep in mind, this works because all our sprites are the same size ( 128x96 ) and would require different logic if you had sprites of differing sizes in your sprite sheet.  You may notice I subtract 9 from the height value… this is because GameEngine2D SpriteTile’s locations start from the bottom left instead of the top left!  I prefer top right and this is how I generated the sheet, so invert the values.  If you design your sprite sheets to start at the bottom left ( so the first frame is at the bottom left corner ), you wont need to perform this calculation.  Now you may be wondering… why 9?  Well that’s the number of sprites we have in each row on the sprite sheet (10, counting from 0 equals 9).

 

Finally we load our texture using the filename we passed in to the constructor.  Again we prepend “Application/” to our filename.  The last thing of note here is the second value in our TextureInfo constructor.  This Vector2i informs the TextureInfo the dimensions of our sprite sheet, telling it at there are 4 columns of 10 rows of sprites.  It doesn’t matter that the last row isn’t full of sprites ( there are only 2 in the spritesheets bottom row ), if you try to access them you will simply get an empty space or whatever the background colour of your spritesheet is.

 

Our destructor is nothing special, just cleans up like a good little citizen should.  Remember it’s your responsibility to dispose of any objects you own and are no longer using.  C# is garbage collected, but its still easy to run out of memory in a hurry if you don’t keep things tidy.

 

Finally we have a pair of Get() methods.  The one takes a sprite name, looks it up in our _sprite dictionary, retrieves the X and Y offset of the sprite within the texture, then passes those values into the other Get() method.  This Get() method then creates a new SpriteTile assigning it our already created _textureInfo.  Next it sets the tiles index within the texture using the passed in coordinates to represent our currently selected sprite, sets the tiles dimensions to 128x96 (pixels) and returns our sprite.  Please notice there is absolutely no error checking or handling here in order to keep things short.  You really should have a wee bit more error checking in your own code! Winking smile

 

The end result of all this activity:

 

walkcycle

 

 

Click here to download the entire project source. In addition to the full project source, the zip also includes all the image files used in this tutorial.

Programming , , , , ,

Month List

Popular Comments