Subscribe to GameFromScratch on YouTube Support GameFromScratch on Patreon
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 , , ,

blog comments powered by Disqus
 

Month List

Popular Comments

Programmer Art: Blender For Programmers Part 1 -- The Introduction
Subscribe to GameFromScratch on YouTube Support GameFromScratch on Patreon

Home > >

18. January 2013

 

Introduction

Welcome to our first Programmer Art tutorial series.  The ultimate goal in these tutorials is to instil  a programmer with the capacity to create their own art assets, in this case using Blender.  One thing I want to make clear right off the hop, this won't teach you how to create *GOOD* art, that part takes many years of practice.  What it will do however, is cover all of the bits you need to know to create "programmer art" for your game.  Who knows, in the end way may even make an artist of you!

 

Over the series we will hopefully be covering general Blender navigation, modelling, texturing, animating and possibly rendering.  Each of these topics could actually fill a book, so the focus here is purely functional in scope.  I would say think of this as the Coles notes version of Blender, but apparently outside of Canada, that expression means nothing…  So instead, think of this as Blender for Dummies, where I am in fact assuming you Dummies are actually rather clever! :)  This post is going to be an overview of Blender itself, the interface can be a bit daunting at first. If you are already somewhat comfortable with Blender, you may want to skip ahead.

 

Meet Blender

Blender for those of you that may not have encountered it before, is a free and open-source 3D modelling application available for Mac OS, Windows and Linux ( and more… ).  It is a remarkably capable package containing tools for modelling, animating, paint, texturing, rendering and even editing video.  Heck, Blender even includes a full featured Python powered game engine!

Blender has a bit of a reputation for being… difficult.  In many ways, especially since more recent UI overhauls, this is mostly an unfair reputation.  On the other hand, Blender is certainly different.  If you have some experience with other 3D applications, you will find Blender a bit daunting at first.  Don't worry, once it clicks it gets a lot better.  One of the big issues with Blender is a lot of the documentation out there is for Blender 2.4 and earlier and those versions were massively different.

Obviously before we begin, we need to get a copy of Blender.  Simply head over to the download page and download the most recent version for your platform of choice.  In this tutorial, I will be using 2.65a, simply because it is the most recent version.  Blender updates on generally a 6 week schedule, so if your version is slightly newer, it shouldn't be a huge difference.

So, if you haven't already, download and install Blender.  Once it's installed, load it.  

 

Welcome to the UI

So, now that you have fired up Blender, you should see:

Blender Start Screen

 

Clicking once will remove the splash screen.  This leaves you with the default starting scene below, minus the pretty blue annotations that is.

Scene Details

 

Lot's of stuff in there, each item labeled above.

The point light and camera are simply graphical icons for non-visible scene attributes, the Camera is used to render the scene from and a Point light, which you can think of similar to a light bulb in the real world.  The transform widget is a very handy little guy that we will cover shortly.

The Cube is simply a 3D model so your scene doesn't start outempty.  The cursor can be thought of as a pivot point that transform are performed relative to, we will see it in detail later.  Behind it all is a grid.  This is simply for reference and can be turned off if you want.  The thing to keep in mind, the grid isn't actually part of your scene, its just there to assist you, like the rulers in Photoshop.

There are two very important things to be aware of right now:

1- The camera you see on screen, isn't the same as the camera ( or perspective ) you use to view your scene.  The camera ( seen above ), is used for rendering your 3D scene into an image or movie.  If you are simply modelling or are ultimately exporting to a game engine like Unreal or Unity, you don't need the camera at all.  You manipulate your view of the world through a different means, that we will cover very soon.  Simply put, if you aren't rendering to an image or movie, you don't really need a camera, although they can be useful for visualizing your results.

2- This is just the starting scene.  At any time you can delete everything here.  Go ahead if you went.  Click in the scene and press the 'A' key, which is the same as Select/DeSelect All.  Then hit X to delete the selected items.  You will see a prompt box like so:

Delete Prompt Blender

 

 

 

Either press X again, or click Delete, and voila:

Empty Blender Scene

 

A completely empty scene, minus of course the cursor/pivot and the grid.

 

If you prefer Blender start with an empty scene every time you start up, you can now save your changes.  Select File->Save User Settings ( or CTRL + U ).

Blender Save User Settings

 

Keep in mind, Save User Settings will save all changes you have made to Blender as your defaults, such as key binding changes, window location, visibility and size.  To go back to the original default if you mess something up, simply select Load Factory settings.

 

If you want to customize the Blender interface, take a look at this video I put together earlier.  Just remember, if you mess things up, you can always start fresh by choosing Load Factory Settings.  The following video is completely optional, as the process can be a little confusing.  That said, the UI is amazingly flexible, as you will see.  The video is available in 1080p if the text is hard to read.

 

 

A look around the interface

Blender is an amazingly customizable interface, you can turn it into just about whatever you want, but it is also very easy to screw things up.  Any window can be split appear, changed, merged together, etc.   The following video I made earlier shows how you can customize the interface to your liking.  For now, I wouldn't bother too much, it can be a rather confusing process and this tutorial will assume you are using the default settings.

Blender Screen Aspects

 

Screen Layout Selection

The Screen Layout selection can be used to select between various different layouts.  The one displayed above is 'Default'.  As you are working on different aspects, you will switch between different modes, for example, the Animation layout is optimized towards animation tasks, UV Editing is for texturing tasks, etc.  

 

Tools and Properties Panel

As mentioned in the diagram, you can toggle the left and right panel ( Tools and Properties ) by hitting the T or N key.  The Tools panel will be used quite commonly, when you use a tool ( like a bevel for example ), if it requires input or has options, they will appear on the tools menu.  The Screen Properties menu is used much less commonly, mostly to configure screen viewport settings, like texturing modes, size and scale settings, etc.  You can also use it to fine tune the position of objects numerically.

 

Animation Timeline

This timeline is used during the animation process, we will cover it in detail later.  You can dragon the window above it down or close it completely for now if you want.  Refer to the earlier video for details on how to resize, split and merge windows.

 

Properties Windows

Yeah, it's kinda confusing having two windows named properties, I know…  This guy is important and you will be using it a lot.  This is where textures are handled, special effects, modifiers, render settings and more are created, accessed and applied.  We will deal with it various times in later tutorials.

 

Scene Graph

This is your scene in hierarchical form.  You can use it to hide, select and delete various objects within your scene.  Your scene is ultimately a tree of nodes, which in turn contain other nodes.   In a complex scene, this will be a godsend.  For simpler scenes and these tutorials, we will rarely use it.  

 

 

Hotkeys used in this tutorial

KeyAction

KeyAction
A Select/Deselect All
X Delete selected
T Hide/Show Tools panel
N Hide Show properties panel

 

Coming Next

In the next tutorial we will learn how to navigate around the Blender UI.

 

 

On to next part

blog comments powered by Disqus
 

Month List

Popular Comments