No More waiting for Godot! Godot Engine reaches 1.0 milestone release

16. December 2014

 

Sorry… absolutely can’t resist that pun, no matter how obvious it is.  Anyways… the Godot Game Engine has been on my radar since they announced they were going open source at the beginning of the year.  Today they finally announced their 1.0 release today.godot

 

 

Never heard of Godot?  It’s a Unity-esque game engine, except powered by C++ and scripted using a Python-like scripting language.  It includes a surprising number of tools and most importantly, a complete game editor (pictured right).

 

Godot works in both 2D and 3D, with 2D being a first class citizen, not just 3D minus a D.  Godot runs on Windows, OSX and Linux.  Godot is able to target iOS, Android, Desktops, Googles’ NaCL, PlayStation3 and Vita, as well as HTML5 and Windows Phone coming soon.

 

You can read the complete feature list here.

 

You can browse available documentation here.

 

Godot is an open source project hosted on GitHub.

 

What do you think… are you interested in Godot, would you be interested in seeing GameFromScratch do some more in-depth coverage now that it’s reached such a milestone release?

News , ,




LibGDX Video Tutorial: Gestures–Panning, Zooming, Pinch, Tapping and more

16. December 2014

 

This video tutorial covers handling gestures in LibGDX, this includes:

  • pinch
  • zoom
  • tap
  • pan
  • long press

 

You can view the video in full resolution on Youtube here.  The source is included below.

 

Source from this tutorial example:

package com.gamefromscratch;

import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.input.GestureDetector;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.math.Vector3;

public class GestureDemo extends ApplicationAdapter implements GestureDetector.GestureListener {
   SpriteBatch batch;
   Sprite sprite;
   OrthographicCamera camera;
   GestureDetector gestureDetector;
   
   @Override
   public void create () {
      batch = new SpriteBatch();
      sprite = new Sprite(new Texture(Gdx.files.internal("storm_trooper.png")));
      sprite.setPosition(-sprite.getWidth()/2,-sprite.getHeight()/2);
      sprite.setCenter(0.5f,0.5f);

      camera = new OrthographicCamera(1280,720);
      camera.update();

      gestureDetector = new GestureDetector(this);
      Gdx.input.setInputProcessor(gestureDetector);
   }

   @Override
   public void render () {
      Gdx.gl.glClearColor(0, 0, 0, 1);
      Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
      batch.setProjectionMatrix(camera.combined);
      batch.begin();
      sprite.draw(batch);
      batch.end();
   }

   @Override
   public boolean touchDown(float x, float y, int pointer, int button) {
      return false;
   }

   @Override
   public boolean tap(float x, float y, int count, int button) {
      if(count > 1){
         sprite.setPosition(-sprite.getWidth()/2,-sprite.getHeight()/2);
         sprite.setSize(256f,256f);
         sprite.setRotation(0f);
      }
      else {
         Vector3 touchPos = new Vector3(x, y, 0);
         camera.unproject(touchPos);
         sprite.setPosition(touchPos.x, touchPos.y);
      }
      return true;
   }

   @Override
   public boolean longPress(float x, float y) {

      Vector3 touchPos = new Vector3(x,y,0);
      camera.unproject(touchPos);

      if(sprite.getBoundingRectangle().contains(touchPos.x,touchPos.y)) {
         float alpha = sprite.getColor().a;

         if (alpha >= 0.f)
            sprite.setAlpha(alpha - 0.25f);
         else
            sprite.setAlpha(1f);
      }
      return true;
   }

   @Override
   public boolean fling(float velocityX, float velocityY, int button) {
      return false;
   }

   @Override
   public boolean pan(float x, float y, float deltaX, float deltaY) {
      Vector3 touchPos = new Vector3(x,y,0);
      camera.unproject(touchPos);

      sprite.setPosition(touchPos.x - sprite.getWidth()/2,touchPos.y - sprite.getHeight()/2);

      return true;
   }

   @Override
   public boolean panStop(float x, float y, int pointer, int button) {
      return false;
   }

   @Override
   public boolean zoom(float initialDistance, float distance) {
      sprite.setSize(distance,distance);
      return true;
   }

   @Override
   public boolean pinch(Vector2 initialPointer1, Vector2 initialPointer2, Vector2 pointer1, Vector2 pointer2)
{
float deltaX = pointer2.x - pointer1.x; float deltaY = pointer2.y - pointer1.y; float angle = (float)Math.atan2((double)deltaY,(double)deltaX) * MathUtils.radiansToDegrees; angle += 90f; if(angle < 0) angle = 360f - (-angle); sprite.setRotation(-angle); return true; } }

Programming , ,




Why can’t I debug on my Android phone anymore? I think it’s dead Jim

14. December 2014

 

So I recently did a complete nuke of my primary daily use Android phone, it’s just one of those things you have to do over time as it gets slower, hotter and your battery life gets worse.  However, the next time I went to debug on Android, ADB couldn’t find it.

 

This is nothing new of course, on Windows you need to do a bit of a device driver dance sometime to get your phone to work with ADB.  This time however it was different.

 

Plugged in my device, waited a few seconds and:

image

 

Hmmm, that’s not good.  USB device not recognized is certainly a common enough answer, let’s just go install the Google Android USB driver.

image

 

Device Descriptor Request Failed… that’s a new one…

 

Ok, lets Update the driver and point to the Google driver…

 

image

 

image

 

Well sh…. er, that didn’t work.  Well it previously worked, maybe I have an old driver kicking around that needs to be purged.  I download the utility USBDeview from here.

 

image

 

I certainly have some old or questionable drivers kicking around.  I am going to clean house in that regard, the nice thing about USB is you can just install the driver again next time you plug the device in, so very little risk here.  Purged the old drivers, scan for hardware changes… nothing.

 

 

Hmmmm…  Google indicates it might be a Windows 8.1 problem due to a driver change they made…

 

Ok, over to my MacBook, let’s see what happens.

 

Plug in the USB cable… BOOM, instant shutdown.

 

WTF?

 

Ok, this is weird.  Let me try a different USB cable.

 

Instant shutdown.

 

Hmmmm…  I’m basically getting the Mac equivalent of a blue screen of death each time I even plug this device in.  Let’s try a different port…

 

Hey, that worked.  Cool.  Try out “./adb devices”  Nothing.

 

Hmmmm, this is confusing as hell.  Doesn’t find the device on Windows 8.1 at all, even worse Crashes if I plug in to the right port of my MacBook Air, it “works” if I plug it into the Left USB port, but for charging only.  The device simply isn’t found.

 

So, at this point I borrow my wife’s identical HTC One phone and plug it in… HTC Manager works, either port, adb devices shows the device.

 

At this point I can only assume something is wrong with my device…  perhaps the connector is broken, but then I can’t explain why it charges successfully, but data doesn’t work.  Generally that is a cable or driver situation, but when I can get an identical phone to work and detect just fine, I am absolutely puzzled at this point.  The two phones have exactly the same versions… I just did a fresh factory install so I am frankly at wits end.

 

If any of you have experienced this before or have ideas, please let me know!  Otherwise I think I’m going to be on the market for a new phone soon.

Totally Off Topic




Modo Indie released and is available on Steam… on sale of course

12. December 2014

 

Modo, a popular 3D modeling application ( learn more about Modo and more in my Introduction to 3D Applications post, or watch the video ) has just been released on Steam.  Actually, Steam being Steam, it’s actually on sale right now for 25% off!

 

image

 

 

Considering the full price of Modo is about $1,600, the Steam Indie version for $250 CDN is quite a bargain!  So, what’s the catch?

 

Yeah, there’s always a catch isn’t there?  So, what’s the difference between Modo and Modo Indie?  Well…

 

  • Project file (.lxf) linked to Steam account / cannot be shared with other users
  • OBJ and FBX export limited to 100k polys
  • Bake and render resolution limited to 4k
  • Command eval options unavailable
  • Command, scripts, and command history panel results unavailable except “undo” and “history”
  • Python editor, third-party scripts, and third-party plugins unavailable
  • OBJ and FBX export only
  • Can import all formats but can only save in .lxf format
  • Image save formats limited to .png, .jpg, .tiff and .exr

 

So they went the Maya LT route and limited the functionality but not the licensing.  This means you can use Modo Indie regardless to how much money you make or how you use it.  This is the deal breaker for many Indie licenses…  As to the stripped out functionality, I think the first restriction is going to be the most difficult one for many to swallow.

 

Simply put you cannot collaborate on a Modo Indie project!  Only one artist will be able to work on the project, ever.  It’s tied to your Steam account id and cannot be shared with others or distributed, although obviously you can export/import in OBJ or FBX format, so for many this wont be much of a limitation in the end.  However for teams with multiple people working on the same resource, or teams where the artist could change at some point in the future, this is going to be a gigantic deal breaker.

 

I haven’t used Modo recently enough to tell if script/plugin support is a big loss or not.  I frankly don’t recall there being any plugins back when I evaluated.  I understand why they do this though, or the very first plugin that would be released would be something to get around the 100K polygon limits.

 

The other limitations seem reasonable.  The 100K export limit precludes you from being able to use Modo as a level editor, but I don’t think many people are doing this anyways.  For game ready assets, 100K polygons and 4K texture limits seem appropriate.  If your needs are much more extreme than that, I can see how you wouldn’t be viewed as an Indie anymore and thus should have funds to purchase the full version.

 

Another affordable 3D option for indie game devs is always welcome, more choice is almost always good.  If you are interested in picking up Modo, the sale ends December 18th/2014.  That said, this is Steam we are talking about, so there will always be another bigger and better sale around the corner!  Oh, they also have a package deal with their MARI Indie texture painting package.  You can purchase both together for $315CDN.

 

Oh yeah, they also released MARI Indie as well… suppose I should mention that.  I have absolutely no experience with Mari, so I figured I would go with their description:

 

MARI indie is the fastest, most artist-friendly way to texture, paint, and detail amazing 3D assets for your game projects. Fine-tuned for individual developers and freelance artists in the game industry, MARI indie is an invaluable toolset that lets you focus on the artistic aspect of 3D game asset painting without getting bogged down by the technical side -- free of any individual commercial restrictions and without breaking your budget!

 

Delivering massive power and flexibility at minimal cost, MARI indie gives you ultimate control over painting and detailing every facet of your 3D models and animations in a way that's quick, intuitive, and highly creative -- all in one complete package that lets you work just the way you want.


Supported by the world’s most advanced layering system, MARI indie is a real workhorse. It gives game artists and content creators all the functionality they need to exactly replicate the look of assets in their games engine.

 

Once again, MARI Indie has no limitations on commerical usage, all limitations are technical:

 

  • Project file (.mra) linked to Steam account / cannot be shared with other users
  • Allowed export formats: .psd, .png, .tga, .jpg
  • Output formats no longer available .exr, .tif, .tiff, .hdr, .dds, and .ptx
  • The patch count is limited to 2 patches
  • The object count is limited to 3 objects
  • The output texture resolution size has been limited to 4k
  • Python scripting disabled

 

With zero experience with MARI I have no opinion on these limitations either way.

 

But WAIT, there’s more!

 

image

 

Yep, there is also a subscription plan available.  And at as low as $11 CDN a month it’s pretty freaking reasonable too.  For example, Maya LT is $30 a month, although they only offer monthly rates.

Art, News ,




LibGDX Video Tutorial: Cameras and Viewports. Handling multiple resolutions and aspect ratios

9. December 2014

 

In this video tutorial we look at using the different types of Cameras available, using a Camera to position your world in a device independent way.  Next we discuss the various Viewport options available for making your render results look best across a number of devices.  Since this video was released at the same time as the text version of the tutorial, I will be linking to those tutorials for code examples.

 

You can see the full 1080p video directly on YouTube or embedded below.

 

 

For the text tutorials, or for the code or assets used in this tutorial, check this tutorial on Cameras and this tutorial on Viewports.

 

Additionally, the text only tutorial also covers converting coordinates too and from your world coordinates, something I forgot to do in the video tutorial.

Programming , , ,




General

A closer look at the Moscrif game development platform

9. November 2012

I recently hosted a guest tutorial post on using Moscrif, a new Javascript based cross platform, game focused development environment.  Thing is, I myself haven't really had a chance to check out Moscrif myself.  In the past I did try out a Moscrif Logosimilar environment Appcelerator Titanium, which for a couple of reasons, I chose not to keep using, many of which aren't actually Appcelerator's fault (the no other word for it, complete crap Android emulator, was the biggest drawback). 

 

Moscrif on the other hand is a bit different.  Like Appcelerator, it's cross platform, mobile focused, JavaScript based and comes with it's own IDE.  Unlike Appcelerator it ships with a simulator you can use for stage 1 debugging, removing the biggest headache.  Perhaps most important of all for readers of this site, Moscrif is also game focused while Titanium is not ( yet ).

 

So, I've decided to take a bit closer look at Moscrif, over a couple of posts.  First off, this is *not* a review.  I certainly haven't put enough time in with Moscrif to even consider reviewing it.  Consider it instead a tour of features available as well as my initial impressions.

 

So, what then is Moscrif anyways?

The above description is pretty much accurate.  Moscrif is a cross platform JavaScript based IDE/language/library for creating mobile applications with a focus on games.  If you've ever used Appcelerator Titanium, you've got a pretty good idea of what you are getting here, just with a more game oriented focus.  The tools run on Windows, Mac and Linux, while you can target iOS, Android and oddly enough… Bada, with a single code base.

 

Installation process

Moscrif install was easy enough, but theres a catch.  You need to create an account (which you can do here), and like the Corona SDK and Appcelerator, you will need to log in to do anything, making an always on connection pretty much a much.  Unfortunately this login process is required even if you are just using the basic version.

Otherwise the install process is pretty much a no-brainer.  In my case I am using Mac OS.  You simply download the package and run a pair of installs.  Moscrif depends on a particular version of the Mono framework, which you need to install first.  It is included in the downloaded package, so simply download, run the Mono installer then the Moscrif installer, that's about it.  You can download (and register) right here.

Once you've finished installing, the first time you run Moscrif you will have to log in with your Moscif account.

 

A first look at the IDE

Load up and log in to Moscrif and you will be greeted by the screen:

IDE Screenshot

 

As you can see, it's a pretty complete and traditional IDE.  Your project management window is on the left, your console and debug windows are across the bottom and the remains of the window is for code editing.  You can use the icons at the top right to toggle sections of the IDE on and off.  I do have one immediate annoyance though, I am running on MacOS and there is no ability to maximize the window, or whatever that feature is called.  Considering that single feature is what makes Mac tolerable to me, hopefully support is added soon!

 

Hello World

To get a feel for the application, let's create an extremely simple Hello World project.

To get started, select File->New->New Project.

In the resulting dialog, choose 2D Game and name your project, I went with Hello World. Like so:

NewImage

 

Now in the next dialog, keep Game2D selected and choose Finish.

NewImage

It will have created a project for you like the one pictured to the left.

 

Main.ms is the file we are most interest in, it is the main entry point of your project.

 

 

 

 

 

 

 

 

Now replace the code in main.ms with the following:

include "lib://game2d/game.ms"

 

class HelloWorld : Game {

    function start()

    {

        super.start();

        this.paint = new Paint();

        this.paint.textSize = 72;

    }

 

    function draw(canvas)

    {

        canvas.clear(0xffffffff);

        canvas.color = 0x000000ff;

        var (w,h) = this.paint.measureText("Hello world")

 

        canvas.drawText("Hello world",System.width/2 -w/2,System.height/2 -h/2, this.paint);

    }

 

}

 

new HelloWorld().run();

 

As you can see, Moscrif is very similar to JavaScript with some obvious extensions, such as class or multiple return values.

Now let's run our new application, which brings us to...

 

The Simulator

Across the top of our screen are the simulator controls:

NewImage

 

Simply click Run, and the iPhone4 simulator will run.  Voila:

NewImage

 

You can control the Simulator with the following hotkeys, although the rotation keys don't work if you don't support landscape changes in code.  I also had a bit of a fight getting F11 and F12 working on Mac, but that is more an OS issue than a Moscrif issue.

 

Operation Mac OS Linux Windows Moscrif symbol
Take screenshot F12 coming soon F12  
Rotate left F11 F11  
Rotate right F11 F11  
Left functional key F1 F1 #left
Right functional key F2 F2 #right
Send / Green / Talk F3 F3 #send
End / Red F4 F4 #end
Back Backspace Backspace #back
Ok / Centre key / Confirm Return Return #ok
Volume Up F6   #volumeUp
Volume Down F7   #volumeDown
Camera F5   #camera
Power     #power
Menu F8   #menu
Home F9   #home
Zoom In Cmd + (=) Ctrl I, Ctrl +  
Zoom Out Cms - Ctrl O, Ctrl -  

 

As you can see from the display options, there is a decent number of profiles pre-configured.

NewImage

 

I did run in to a few issues, when I switched from Iphone3 to iPad, then implemented screen rotation, only a 480x320 portion of the screen was rendered.  That said, having a simulator layer massively improves turn around time, especially when working on Android where the emulator is pure garbage.

 

Publishing

The simulator is nice, but at the end of the day you are going to want to run on an actual device.  That process is actually quite simple.

Simply select the menu Project->Publish

The following dialog will appear:

Publish Dialog

 

 

Select your OS as a tab at the top, click a checkbox beside a skin and click the Publish button.  You can totally tell a programmer wrote this dialog… Reset Matrix?  Really? ;)  Coincidentally Reset Matrix is simply De-select, if you are curious.

After clicking Publish, Moscrif will churn away for a little bit and a Finder/Explorer window will pop up with your APK file ready for deployment.  I have to admit, this process is quite impressive, as it doesn't require you to install the Android tools, mess with any environment variables or any of the typical fun.  If you are just starting out, this is about the easiest way to generate an APK file I've ever seen.  Coincidentally, the APK was about 4MB in size, which is pretty impressive.  When using Titanium, a Hello World measured in closer to 10MB.

There is however a downside (one Appcelerator Titanium shares), you can't currently debug on device, which sucks.  You can log/trace back to the IDE as your program runs, but that's it. There are a whole class of bugs, especially on Android, that only express themselves on an actual device, so this can get a bit annoying.  Let's hope the simulator does a very good job of, well, Simulating.  Fortunately, on device debugging is a very near term item on the roadmap of features.  Even more confusing, it doesn't appear you can debug in the simulator either, other than debug logging.  I have become so used to being able to set breakpoints and step through code, it feels like a major omission when I cant. Hopefully this functionality gets added, at least to the simulator. Or perhaps I simply overlooked the functionality somewhere.

 

Other stuff and closing thoughts

The IDE is fairly easy to come to terms with.  What you see is pretty much what you get, there aren't a hundred menu items or nested dialogs, and there really doesn't have to be.  The code editing tools are pretty impressive, with good legible errors displayed in realtime.  There is code completion, it's quick and appears to work exactly as you would expect it to. Otherwise it's a fairly barebones but functional code editor.  There is smart tabbing, find/replace, code suggestions/completion, hover-over code tips and code folding and thats about it. For more advanced editing, such as refactoring, you need to move to a more dedicated text editor.  Truth is, in 99% of occasions, the IDE is a perfectly capable and even enjoyable place to edit your code.

Next is the area of support and community.  Let me start with the bad, community.  I am not saying the community is bad, more… missing.  One of the downsides of being a fairly new product is the lack of information online.  When you run in to a problem on Moscrif, there isn't yet a large community to turn to and Google won't come to save you.

Now, the good.  Moscrif's documentation ROCKS.  It staggers me how good of a job they have done.  Theres a pretty good step by step documentation that walk you through many of the features with code examples.  Where Moscrif really shines though is the reference materials. It's comprehensive, complete, timely and almost always comes with a code sample.  The reference material goes a long way to minimizing the lack of community…  you don't have as many people out there to answer your questions… but you have such great documentation that you don't really have any questions in the first place!  If you are middleware publisher and want to know how to do documentation… look no further!

I haven't really gotten the chance to really dive in and code.  That said, what I saw in my initial inspection certainly have my interest piqued, so I will be looking at the code side of things shortly.  So stay tuned for a more detailed hands on with the code in the future.  I am impressed by what I've seen so far.

 

Pros

  • it's JavaScript based
  • no external dependencies. No need to install other tools or SDKs. Fastest way I've seen to build an Android app yet
  • small executable size for the type of tool it is
  • EXCELLENT reference materials
  • all in one, easy to get started, reasonably complete
  • simulator makes for fast dev turn around times
  • JavaScript language enhancements that fix some of JavaScript's warts
  • Fast OpenGL based performance

Cons

  • it's JavaScript based 
  • lack of online presence, forum software is terrible and finding information not in the documentation is difficult
  • internet connection required
  • debugger needs improvement and basic debugging features ( breakpoints, inspection, etc ) added
  • it's young and relatively unproven 

 

Stay tuned for a later post when I look at Moscrif from a coding perspective.

 

General, Programming , , ,




Formatted code samples on MacOS using MarsEdit and SublimeText

8. November 2012

As I've been working more and more on Mac these days, I've been looking for a Mac based blogging solution and I ended up purchasing MarsEdit.  Everything worked out pretty well, except pasting highlighted source code.

 

I finally solved that final piece of the equation using a product in the middle, SublimeText.  Before continuing, you need to install the plugin Sublime Highlight.  To install, simply git clone the package into your Sublime Text packages folder, like so:

cd ~/Library/Application Support/Sublime Text 2/Packages/
git clone https://github.com/n1k0/SublimeHighlight.git

Now start up SublimeText and you should see an additional option in the Edit menu, Highlight:

 

Sublime Text edit menu

 

Now all you need to do is edit your sublime preferences, choose Preferences->Package Settings->Sublime Highlight->Settings User.

It will open an empty config file, simply add the following:

{

    "theme": "murphy",

    "linenos": "inline",

    "noclasses": true

}

 

The most important value there is noclasses, as otherwise MarsEdit will strip the formatting when you paste your code excerpt.  Theme is the syntax highlighting theme to use and can be one of the following:

  • autumn
  • borland
  • bw
  • colorful
  • default
  • emacs
  • friendly
  • fruity
  • manni
  • monokai
  • murphy
  • native
  • pastie
  • perldoc
  • rrt
  • tango
  • trac
  • vim
  • vs
Now simply paste your code example in Sublime Text, select the portion you want to copy and choose Edit->Highlight->Copy to Clipboard as RTF.  In MarsEdit you simply paste the results while in RTF mode.  Now, presto… formatted code samples!
 

server.get('/imageSize/:name',function(req,res){

   im.identify(files[req.params.name].path,function(err,features){

       console.log("image/" + req.params.name);

       if(err) throw err;

       else

        res.json({ "width":features.width, "height":features.height });

   });

});

 

server.get('/getPhotos', function(req,res){

    res.json(files);

 

});

 

Mission complete, I am now fully functional blogging on Windows and Mac. :)

General




Gamefromscratch is now on Twitter

7. November 2012

Ok, if I am completely honest, I opened the account in April, but it has sat there doing absolutely nothing.  This is about to change.  I have been noticing that more and more of this sites traffic is coming from t.co and it started to feel a bit like I had my head buried in the sands when it comes to this whole social media thing.

 

So, if you like GameFromScratch the site, try GameFromScratch the Twi… er, Tweet.

 

Basically, follow us on Twitter.

 

I won't promise you it will be funny, or informative or even interesting.  But I do promise you it won't ever contain a single cat picture.

 

Unless of course that cat creates a damned good game that is.  Even then, probably not.  Stupid cats.

 

Are you a Twitterer?  Any recommendations of must follow game development Twitterers, well other of course than @GameFromScratch that is!

General, Totally Off Topic




And the best offline blogging software for Mac OS is…….. MarsEdit

29. October 2012

 

Since I've been working on a Mac more and more often these days, I've been looking for an alternative to the wonderful Windows Live Writer for Windows.  I started with a trial of MarsEdit, which I really liked, except it's inability to display formatted source code ( a big deal for me ), its lack of tagging ( not a really big deal, but a nuisance ) and the lack of image formatting options.

 

Then I found and discovered Adobe Contribute, which has a 100$ price tag.  It started off strong but quickly took a jump off a cliff into the land of truly awful.  It seems to be poorly supported by Adobe, has documented features that don't appear to exist anymore, has some gigantic bugs ( such as the Paste menu never being enabled! ).  It did however have incredible table formatting tools!  I know in the age of CSS, table is a bit of a swear word, but damned if a lot of your data isn't in a tabular format.  The image formatting options were easily the best of all packages I've used, including Live Writer.  But good tables and good image positioning tools don't even come close to making up for the brutal flaws, especially at a 100$ price point.

 

I then tried out Qumana, a completely free option:

Qumana screenshot

At first glance, it pushes all the buttons.  It doesn't support tags ( only Live Writer seems to ), but image formatting is decent, and you can directly edit the HTML of your post, so if you know HTML, the sky is the limit.  I attempted to write the previous post with Qumana and ran in to a gigantic deal breaker attempting to insert an IFrame… you can't.  I pasted the HTML code for a Youtube video, then when I switch back to WYSIWG view, poof gone.  Game over.

 

So then, back to MarsEdit, which I went ahead and purchased.  In the end, MarsEdit was easily the best option of what is available for Mac.  The biggest sellable point was the ability to drop to and edit HTML, which is nicely preserved when switching back to rich text mode.

 

Now, Red Sweater, if you are listening, please add the ability to create plugins, or failing that, the ability to post formatted source code.  Also, tag support would be nice!

 

If you are a MarsEdit user, and currently post code samples on your blog, how are you doing it?

General




Creating game creation tools using HTML5: Adding a sprite sheet upload dialog

17. October 2012

 

A level is made up of sprites and sprites come from somewhere.  In our editor, we are going to allow the user to “upload” multiple image files containing sprite sheets.  However, are server is not required and that is going to require a bit of work.  Also, we are going to need some form of UI where users can upload the spritesheet, without cluttering our main UI too much, so we will implement it as a modal dialog box.

 

Well, let’s get to it.  First lets create a data type for holding our sprite sheet collection.  For now, a spritesheet is simply an image, the dimensions of each sprite and a name.  In your models folder create a new file named spriteSheet.js

spriteSheet.js

 

YUI.add('spriteSheet',function(Y){
    Y.SpriteSheet = Y.Base.create('spriteSheet', Y.Model, [],{
            count:function(){
                return this.get('spritesheets').length;
            },
            add:function(name,width,height,img){
                this.get('spritesheets').push({name:name,width:width,height:height,img:img});
            }
        },{
            ATTRS:{
                spritesheets: {
                    value: []
                }
            }
        }
    );
}, '0.0.1', { requires: ['model']});

Nothing really special.  Our spritesheets attribute is just an empty array for now.  We also included a pair of methods, add, for adding a new spritesheet and count for getting the current count of spritesheets already declared.  Everything else here should already be familiar at this point.

 

Now we want to create a dialog that will be displayed when the user wants to add a spritesheet.  As a bit of a spoiler, here is what we are going to create:

image

This isn’t a View and it isn’t a model, so we create a new folder called classess and create the long-winded file named AddSpriteSheetDialog.js

AddSpriteSheetDialog.js

YUI.add('addSpriteSheetDialog', function(Y){

    Y.AddSpriteSheetDialog = new Y.Base();
    var spriteSheets = null;
    Y.AddSpriteSheetDialog.show = function(ss,onComplete){
        spriteSheets = ss;
        var panel = new Y.Panel({
            width:500,
            height:300,
            centered:true,
            visible:true,
            modal:true,
            headerContent:'Select the image file containing your sprite sheet',
            bodyContent:Y.Node.create(
                "<DIV>\
                <input type=file id=spritesheet /> \
                <br /> <div id=imgName style='padding-top:25px;padding-bottom:25px'> \
                Click above to select a file to download</div>\
                <br />Sheet name:<input type=Text id=name size=30 value=''> \
                <br />Sprite Width:<input type=Text id=width size=4 value=32> \
                Sprite Height:<input type=Text id=height size=4 value=32> \
                <br /><input type=button id=done value=done />\
                </DIV>\
                "
            ),
            render:true
        });

        var fileUpload = Y.one("#spritesheet");
        fileUpload.on("change", Y.AddSpriteSheetDialog._fileUploaded);

        var buttonDone = Y.one("#done");
        buttonDone.on("click", function(){
            panel.hide();
            onComplete();
        })
        panel.show();

    };

    Y.AddSpriteSheetDialog._fileUploaded = function(e){
        if(!e.target._node.files[0].type.match(/image.*/)){
            alert("NOT AN IMAGE!");
            return;
        }
        var selectedFile = e.target._node.files[0];
        var fileReader = new FileReader();

        var that=this;
        fileReader.onload = (function(file){
            return function(e){
                if(e.target.readyState == 2)
                {
                    var imgData = e.target.result;
                    var img = new Image();
                    img.onload = function(){
                        Y.one('#imgName').set('innerHTML',selectedFile.name + " selected");
                        var name = Y.one('#name').get('value');
                        var width = Y.one('#width').get('value');
                        var height = Y.one('#height').get('value');
                        spriteSheets.add(name,width,height,img);
                    }
                    img.src = imgData;
                }
            };

        })(selectedFile);
        fileReader.readAsDataURL(selectedFile);

    };


},'0.0.1', {requires:['node','spriteSheet','panel']});

The editorView owns the spritesheet collection, and passes it in to the show() method of AddSpriteSheetDialog.  We also pass in a callback function that will be called when we are done.

We start off creating the panel which is a Y.Panel.  Most of the properties should be pretty straight forward, headerContent is the title and bodyContent is either the ID of the object to render the panel in, or in our case, we actually create a new node with our dialog HTML.  We then wire up a change handler on our file upload button, this will fire when a file is uploaded and call the _fileUploaded function.  We then wire up the Done button’s on click handler to hide the panel then call the callback function that was passed in.  Finally we display the panel.

 

When the user clicks the Choose File button, _fileUploaded is called.  First thing we check to make sure it is an image that is uploaded and error out if it isn’t.  We then want to read the selected file, which we do with the FileReader api.  Word of warning, this isn’t completely supported in every browser… frankly though, I don’t care about supporting IE in a project like this, cross browser support takes all of the fun out of web app development! Smile

 

Next is well… JavaScript at it’s most confusing. We are registering an onload event that will be fired once the file has been loaded, which in turn fires off an anonymous method.  It checks the readystate of the file to make sure it is ready and if so, our “uploaded” file will be in e.target.result.  We then create an Image object, then register yet another onload handler, this one for when the image has completed loading.  Once the user has uploaded the file, its finished loading and populated in our newly create Image, we then get the width, height name and our newly populated image and at it to the screenSheets object we passed in during show().  Yes, this is a bit screwy of an interface, in that you need to populate the text fields before uploading the interview.  I will ultimately clean that up ( and add edit ability ), but it would needlessly complicate the code for now.  Finally, no that our fileReader.onload() event is done, we actually read the file now with readAsDataUrl() the file that was chosen, which fires off the whole onload event handler in the first place.   Welcome to asynchronous JavaScript programming!  Don’t worry, if this is new to you, thinking async will come naturally soon enough…

 

So, that is how you can create a modal dialog to edit app data.  Now we wire it up and deal with a bit of a gotcha.

 

The gotcha first…  the Panel dialog requires a parent HTML element in the DOM to have a YUI skin CSS class declared.  At the bottom on the render function in editor.View.js add the following code:

Y.one('body').setStyle("margin",0);
Y.one('body').setStyle("overflow","hidden");
// The below needs to be added as some controls, such as our add sprite dialog, require a parent container
// to have the YUI skin defined already
Y.one('body').setAttribute("class","yui3-skin-sam");
return this;

This adds the yui3-skin-sam class to the page’s body, which brings in all the styling for the Panel ( and other YUI widgets ).

 

While we are in editor.View.js, we wire up a menu handler for when the user clicks the add spritesheet button ( we will add in a second ).  That handler is basically the same as the menu:fileExit handler we created earlier.  Right below that handler in the initializer function, add the following:

 

var that = this;
Y.Global.on('menu:fileAddSpriteSheet',function(e){
    var dialog = Y.AddSpriteSheetDialog.show(that.spriteSheets,function(){
        var sheet = that.spriteSheets.get("spritesheets")[0];
        console.log(sheet);
    });
});

There is the that=this hack again, there are alternatives ( you can pass the context in to the Y.Global.on event handler ), but this is a fair bit easier at the end of the day, as we would lose this again when the callback is called.  Otherwise, when the menu:fileAddSpriteSheet event is received, we simply call AddSpriteSheetDialog.show(), passing in our spritesheet and the function that is called when the panel is complete.  For now we simply log the spritesheet out to the console to prove something changed.

We also need to add the SpriteSheet to our editor.View.js, like so:

 

 Y.EditorView = Y.Base.create('editorView', Y.View, [], {
        spriteSheets:new Y.SpriteSheet(),
        initializer:function(){

 

Now we need to add the menu item.  First add it to the template mainMenu.Template,like so:

<ul>
    <li class="yui3-menuitem" id="menuFileAddSpriteSheet">
        <a class="yui3-menuitem-content" href="#">Add SpriteSheet</a>
    </li>
    <li class="yui3-menuitem" id="menuFileExit">
        <a class="yui3-menuitem-content" href="#">Exit</a>
    </li>
</ul

And we wire it up in the mainMenu.View.js, add the bottom of render() add the following code:

var menuFileAddSpriteSheet = container.one('#menuFileAddSpriteSheet');
            menuFileAddSpriteSheet.on("click", function(e){
                Y.Global.fire('menu:fileAddSpriteSheet', {msg:null});
            });

Oh, and our newly added script AddSpriteSheetDialog.js is added to index.html to guarantee it gets loaded and evaluated.

 

And done.  We now added a dialog for adding sprite sheet images, and can store the image results locally without requiring any server interaction at all.

 

Here is the end result, select File->Add Spritesheet to bring up the newly created dialog:

 


You can download the entire updated source code here.

One step closer to a full web based game editor, one very tiny step. Smile

Programming, General , , ,