Subscribe to GameFromScratch on YouTube Support GameFromScratch on Patreon

8. October 2012

 

As per this post I am currently going through the process of creating some simple game creation tools using HTML5, more specifically using the YUI 3 library as well as the EaselJS canvas library.

 

This post illustrates the very skeleton upon which we are going to create our app.  YUI3 provides a full MVC framework which you can use to create your application so I decided to make use of it.  The end result of this code is remarkably minimal, it just creates a single page web application with different views representing different portions of the UI.  Specifically, we will create a top zone where the menu will go, a left hand area where the level editing will occur, then a right hand panel which will change contextually.  I also created a very simple data class, to illustrate how data works within the YUI MVC environment.

 

First off, if you have never heard of MVC, it is the acronym of Model View Controller.  MVC is a popular design practice for separating your application in to logically consistent pieces.  This allows you to separate your UI from your logic and your logic from your data ( the last two get a little gray in the end ).  It adds a bit of upfront complexity, but makes it easier to develop, maintain and test non-trivial applications… or at least, that’s the sales pitch.

 

The simplest two minute description of MVC is as follows.  The Model is your application’s data.  The View is the part of your application that is responsible for displaying to the end user.  The Controller part is easily the most confusing part, and this is the bit that handles communications between the model and view, and is where you actual “logic” presides.  We aren’t going to be completely pure in this level in this example ( MVC apps seldom are actually ), as the Controller part of our application is actually going to be a couple pieces, you will see later.  For now just realize, if it aint a view and it aint a model, it’s probably a controller.

 

It is also worth clarifying that MVC isn’t the only option.  There is also MVVM ( Model-View-ViewModel ) and MVP ( Model-View-Presenter ), and semantics aside, they are all remarkably similar and accomplish pretty much the same thing.  MVC is simply the most common/popular of the three.

 

Put simply, it will look initially more complex ( and it is more complex ), but this upfront work makes life easier down the road, making it generally a fair trade off.

 

Alright, enough chatter, now some code!  The code is going to be split over a number of files.  A lot of the following code is simply the style I chose to use, and is completely optional.  It is generally considered good practice though.

 

image 

At the top level of our hierarchy we have a pair of files, index.html and server.js.  server.js is fairly optional for now, I am using it because I will (might?) be hosting this application using NodeJS.  If you are running your own web server, you don’t need this guy, and won’t unless we add some server-side complexity down the road.

 

index.html is pretty much the heart of our application, but most of the actual logic has been parted out to other parts of the code, so it isn’t particularly complex.  We will be looking at it last, as all of our other pieces need to be in place first.

 

Now within our scripts folder, you will notice two sub-folders models and views.  These predictable enough are where our models and views reside.  In addition, inside the views directory is a folder named templates. This is where our moustache templates are.  Think of templates like simple HTML snippets that support very simple additional mark-up, allowing for things like dynamically populating a form with data, etc.  If you’ve ever used PHP, ASP or JSP, this concept should be immediately familiar to you.  If you haven’t, don’t worry, our templates are remarkably simple, and for now can just be thought of as HTML snippets.  The .Template naming convention is simply something I chose, inside they are basically just HTML.

 

If you are basing your own product on any of this code, please be sure to check out here, where I refactored a great deal of this code, removing gross hacks and cleaning things up substantially!

 

Let’s start off with our only model person.js, which is the datatype for a person entry.  Let’s look at the code now:

 

person.js

YUI.add('personModel',function(Y){
    Y.Person = Y.Base.create('person', Y.Model, [],{
            getName:function(){
                return this.get('name');
            }
        },{
            ATTRS:{
                name: {
                    value: 'Mike'
                },
                height: {
                    value: 6
                },
                age: {
                    value:35
                }
            }
        }
    );
}, '0.0.1', { requires: ['model']});

The starting syntax may be a bit jarring and you will see it a lot going forward.  The YUI.add() call is registering ‘personModel’ as a re-usable module, allowing us to use it in other code files.  You will see this in action shortly, and this solves one of the biggest shortcomings of JavaScript, organizing code.

 

The line Y.Person = Y.base.create() is creating a new object type in the Y namespace, named ‘person’ and inheriting all of the properties of Y.Model.  This is YUI’s way of providing OOP to a relatively un-OOP language.  We then define a member function getName and 3 member variables name, height and age, giving each of the three default values… just cause.  Of course, they aren’t really member variables, they are entries in the object ATTRS, but you can effectively think of them as member variables if you are from a traditional OOP background.  Next we pass in a version stamp ( 0.0.1 ), chosen pretty much at random by me.  Next is a very important array named requires, which is a list of all the modules ( YUI, or user defined ) that this module depends on.  We only need the model module.  YUI is very modular and only includes the code bits you explicitly request, meaning you only get the JavaScript code of the classes you use.

 

So that is the basic form your code objects are going to take.  Don’t worry, it’s nowhere near as scary as it looks.  Now let’s take a look at a view that consumes a person model.  That of course would be person.View.js.  Again, the .View. part of that file name was just something I chose to do and is completely optional.

person.View.js

YUI.add('personView',function(Y){
        Y.PersonView = Y.Base.create('personView', Y.View, [], {
        initializer:function(){
            var that=this,
                request = Y.io('/scripts/views/templates/person.Template',{
                    on:{
                        complete:function(id,response){
                            var template = Y.Handlebars.compile(response.responseText);
                            that.get('container').setHTML(template(that.get('model').getAttrs(['name','age','height'])));
                        }
                    }
                });
        },
        render:function(){
            return this;
        }
    });
}, '0.0.1', { requires: ['view','io-base','personModel','handlebars']});

Just like with our person model, we are going to make a custom module using YUI.add(), this one named ‘personView’.  Within that module we have a single class, Y.PersonView, which is to say a class PersonView in the Y namespace.  PersonView inherits from Y.View and we are defining a pair of methods, initializer() which is called when the object is created and render() which is called when the View needs to be displayed.

 

In initializer, we perform an AJAX callback to retrieve the template person.Template from the server.  When the download is complete, the complete event will fire, with the contents of our file in the response.responseText field ( or an error, which we wrongly do not handle ).  Once we have our template text downloaded, we “compile” it, which turns it into a JavaScript object. The next line looks obscenely complicated:

that.get('container').setHTML(template(that.get('model').getAttrs(['name','age','height'])));

A couple things are happening here.  First we are using that because this is contextual in JavaScript.  Within the callback function, it has a completely different value, so we cached the value going in.  Next we get the property container  that every Y.View object will have, and set it’s HTML using setHTML().  This is essentially how you render a view to the screen.  The parameter to setHTML is also a bit tricky to digest at first.  Essentially the method template() is what compiles a moustache template into actual HTML.  A template, as we will see in the moment, may be expecting some data to be bound, in this case name, age and height which all come from our Person model.  Don’t worry, this will make sense in a minute.

 

Our render method doesn’t particularly do anything, just returns itself.  Again we specify our modules dependency in the requires array, this time we depend on the modules view, io-base, personModel and handlebars.  As you can see, we are consuming our custom defined personModel module as if it was no different than any of the built-in YUI modules.  It is a pretty powerful way of handling code dependencies.

 

Now let’s take a look at our first template.

person.Template

<div style="width:20%;float:right">
    <div align=right>
        <img src=http://www.gamefromscratch.com/image.axd?picture=HTML-5-RPG_thumb_1.png 
alt="GameFromScratch HTML5 RPG logo" />
    </div>
    <p><hr /></p>
    <div>
        <h2>About {{name}}:</h2>
        <ul>
            <li>{{name}} is {{height}} feet tall and {{age}} years of age.</li>
        </ul>
    </div>
</div>

As you can see, a template is pretty much just HTML, with a few small exceptions.  Remember a second ago when we passed data in to the template() call, this is where it is consumed.  The values surrounded by {{ }}  ( thus the name moustache! ) are going to be substituted when the HTML is generated.  Basically it looks for a value by the name within the {{ }} marks and substitutes it into the HTML.  For example, {{name}}, looks for a value named name, which it finds and substitutes it’s value mike in the results.  Using templates allows you to completely decouple your HTML from the rest of your application.  This allows you to source out the graphic work to a designer, perhaps using a tool like DreamWeaver, then simply add moustache markup for the bits that are data-driven.

 

What you may be asking yourself is, how the hell did the PersonView get it’s model populated in the first place?  That’s a very good question.

 

In our application, our view is actually going to be composed of a number of sub-views.  There is a view for the area the map is going to be edited in, a view for the context sensitive editing will occur ( currently our person view ), then finally a view where our menu will be rendered.  However, we also have a parent view that holds all of these child views, sometimes referred to as a composite view. This is ours:

editor.View.js

YUI.add('editorView',function(Y){
    Y.EditorView = Y.Base.create('editorView', Y.View, [], {
        initializer:function(){

            var person = new Y.Person();
            this.pv = new Y.PersonView({model:person});
            this.menu = new Y.MainMenuView();
            this.map = new Y.MapView();
        },
        render:function(){
            var content = Y.one(Y.config.doc.createDocumentFragment());
            content.append(this.menu.render().get('container'));

            var newDiv = Y.Node.create("<div style='width:100%'/>");
            newDiv.append(this.map.render().get('container'));
            newDiv.append(this.pv.render().get('container'));

            content.append(newDiv);
            this.get('container').setHTML(content);
            return this;
        }
    });
}, '0.0.1', { requires: ['view','io-base','personView','mainMenuView','mapView','handlebars']});

The start should all be pretty familiar by now.  We again are declaring a custom module editorView. This one also inherits from Y.View, the major difference is in our initializer() method, we create a Y.Person model, as well as our 3 custom sub-views, a PersonView, a MainMenuView and a MapView ( the last two we haven’t seen yet, and are basically empty at this point ).  As you can see in the constructor for PersonView, we pass in the Y.Person person we just created.  This is how a view gets it’s model, or at least, one way.

 

Our render() method is a bit more complicated, because it is responsible for creating each of it’s child views.  First we create a documentFragment, which is a chunk of HTML that isn’t yet part of the DOM, so it wont fire events or cause a redraw or anything else.  Basically think of it as a raw piece of HTML for us to write to, which is exactly what we do.  First we render our MainMenuView, which will ultimately draw the menu across the screen.  Then we create a new full width DIV to hold our other two views.  We then render the MapView to this newly created div, then render the PersonView to the div.  Finally we append our new div to our documentFragment.  Finally we set our view’s HTML to our newly created fragment, causing all the views to be rendered to the screen.

 

Once again, we set a version stamp, and declare our dependencies.  You may notice that we never had to include personModel, this is because personView will resolve this dependency for us.

 

Lets quickly look at each of those other classes  ( mainMenuView and mapView ) and their templates, although all of them are mostly placeholders for now.

 

mainMenu.View.js

YUI.add('mainMenuView',function(Y){
    Y.MainMenuView = Y.Base.create('mainMenuView', Y.View, [], {
        initializer:function(){
            var that=this,
                request = Y.io('/scripts/views/templates/mainMenu.Template',{
                    on:{
                        complete:function(id,response){
                            var template = Y.Handlebars.compile(response.responseText);
                            //that.get('container').setHTML(template(that.get('model').getAttrs(['name','age','height'])));
                            that.get('container').setHTML(template());
                        }
                    }
                });
        },
        render:function(){
            return this;
        }
    });
}, '0.0.1', { requires: ['view','io-base','handlebars']});

mainMenu.Template

<div style="width:100%">This is the area where the menu goes.  It should be across the entire screen</div>

 

map.View.js

YUI.add('mapView',function(Y){
    Y.MapView = Y.Base.create('mapView', Y.View, [], {
        initializer:function(){
            var that=this,
                request = Y.io('/scripts/views/templates/map.Template',{
                    on:{
                        complete:function(id,response){
                            var template = Y.Handlebars.compile(response.responseText);
                            that.get('container').setHTML(template());
                            //that.get('container').setHTML(template(that.get('model').getAttrs(['name','age','height'])));
                        }
                    }
                });
        },
        render:function(){
            return this;
        }
    });
}, '0.0.1', { requires: ['view','io-base','handlebars']});

map.Template

<div style="width:80%;float:left">
    This is where the canvas will go
</div>

Now, we let’s take a quickly look at server.js.  As mentioned earlier, this script simply provides a basic NODEJS based HTTP server capable of serving our app.

server.js

var express = require('express'),
    server = express();

server.use('/scripts', express.static(__dirname + '/scripts'));

server.get('/', function (req, res) {
    res.sendfile('index.html');
});

server.listen(process.env.PORT || 3000);

 

I wont really bother explaining what’s going on here.  If you are going to use Node, there is a ton of content on this site already about setting up a Node server.  Just click on the Node tag for more articles.

 

Finally, we have index.html which is the heart of our application and what ties everything together and this is the file that is first served to the users web browser, kicking everything off.

index.html

<!DOCTYPE html>

<html>
<head>
    <title>GameFromScratch example YUI Framework/NodeJS application</title>
</head>
<body>

<script src="http://yui.yahooapis.com/3.5.1/build/yui/yui-min.js"></script>
<script src="/scripts/models/person.js"></script>
<script src="/scripts/views/person.View.js"></script>
<script src="/scripts/views/map.View.js"></script>
<script src="/scripts/views/mainMenu.View.js"></script>
<script src="/scripts/views/editor.View.js"></script>

<script>
    YUI().use('app','editorView', function (Y) {

        var app = new Y.App({
            views: {
                editorView: {type: 'EditorView'}
            }
        });

        app.route('/', function () {
            this.showView('editorView');//,{model:person});
        });

        app.render().dispatch();
    });
</script>


</body>
</html>

 

This sequence of <script> tags is very important, as it is what causes each of our custom modules to be evaluated in the first place.  There are cleaner ways of handling this, but this way is certainly easiest.  Basically for each module you add, include it here to cause that code to be evaluated.

 

Next we create our actual Y function/namespace.  You know how we kept adding our classes to Y., well this is where Y is defined.  YUI uses an app loader to create the script file that is served to your clients browser, which is exactly what YUI.use() is doing.  Just like the requires array we passed at the bottom of each module definition, you pass use() all of the modules you require, in this case we need the app module from YUI, as well as our custom defined editorView module.

 

Next we create a Y.App object.  This is the C part of MVC.  The App object is what creates individual views in response to different URL requests.  So far we only handle one request “/”, which causes the editorView to be created and shown.  Finally we call app.render().dispatch() to get the ball rolling, so our editorView will have it’s render() method called, which will in turn call the render method of each of it’s child views, which in turn will render their templates…

 

Don’t worry if that seemed scary as hell, that’s about it for infrastructure stuff and is a solid foundation to build a much more sophisticate application on top of.

 

Of course, there is nothing to say I haven’t made some brutal mistakes and need to rethink everything! Smile

 

Now, if you open it up in a browser ( localhost:3000/ if you used Node ), you will see:

image

 

Nothing too exciting as of yet, but as you can see, the menu template is rendered across the top of the screen, the map view is rendered to the left and the Person view is rendered on the right.  As you can see from the text, the data from our Person model is compiled and rendered in the resulting HTML.

 

You can download the complete project archive right here.

Design, Programming , , ,

5. October 2012

I am about to embark on a bit of a sidetrack here on GameFromScratch.com, hopefully a few of you find it interesting.

 

Basically I am going to start looking at developing game tools using HTML5.  Tooling is a massive part of the game development process and traditionally i've used RAD client only tools like C# + WinForms in this capacity, but HTML5 is becoming increasingly appealing.  I have done a great deal of traditional web development, but nothing really like this.

 

My first project is going to be a dry run on a level editing tool.  I have NO intentions of actually using the end result in a production environment, it is as much a learning experience for me as anything else.  This unshackles me from spending time on stuff like… design, as I intend to throw away everything anyways ( famous last words? ).  Besides, until you have a solid grasp of what you are doing and what problems you are going to face, it's pretty hard to create much of a design anyways.  Now, if I enjoy the experience and the results, I will put a great deal of thought into the design of the finished product.  But when just trying things out, design mostly just gets in the way.

 

This is not the first time I have gone down this road, I've started a number of times and run in to the same problem over and over.  Too much choice, not enough knowledge to make the decision.  I always end up looking at frameworks, design patterns, libraries, toolkits etc, and always end up getting nowhere.  Even picking a client side JS library can consume weeks of your life, throw in a persistence framework, MVC or MVVM framework and months of your life are gone.

 

In the past I went down this road, I looked in to technologies ( such as YUI, Kendo, jQuery, Backbone, Moustache, Dojo, etc… ) and found strengths and faults with all of them.  What I didn't find is success…  This is an area that is anathema to me.  I like to research things, know my options going in and make a decision from a position of some knowledge.  Problem is, there are just too damned many frameworks and options.

 

So I am doing something I never do.  I am picking the technologies up front, and come hell or high water, I am sticking with them until the end.  Therefore for my upcoming project I am going to work with:

- YUI 3 for UI, controls, data, program flow, etc...

- EaselJS for graphics.

 

 

Why YUI 3?  Well because it is pretty much an all encompassing framework, it does about everything, taking decisions away from me.  I don't need to pick a client side technology like jQuery, nor a mobile suite like jQueryMobile, nor a back end like Backbone or ember, package system like require, etc…  YUI pretty much has it all out of the box.

 

Why EaselJS instead of a plethora of game/animation HTML5 libraries?  God knows there are enough of them!  Well, because I haven't used it and I intended to look in to it.

 

So, I am not saying either of these technologies are better than their alternatives, I simply don't have the experience yet to make such a judgement.  I am going with them because, well, they are there.

 

Hope you find the process interesting.

Design, General, Programming

4. October 2012

 

Earlier this morning, this news article came across my desk and caught my interest for a number of reasons.  Basically as the title states, Appcelerator - the makers of the popular Titanium mobile development suite have funded a game development technology startup Lanica.

 

Lanica Logo

So, why should you care?

 

Well first off, Appcelerator Titianium is a very nice development environment.  For a few different reasons I chose not to develop with Titanium, the inability to debug on Android devices being the biggest hindrance ( and mostly Google's fault for making such a terrible emulator ), however this fault may have been removed!  ( I will be looking into this very shortly ).

 

Next, while working in HTML5 on mobile is nice, the question still remains if it is feasible for high performance games.  PhoneGap brings HTML5 to mobile devices in native app form, but it doesn't bring the performance.  There are a number of other technologies working on this problem, but one more is always a nice thing.

 

Finally Lanica does have some pedigree.  One of the two founders is Carlos Icaza, who among other things, was the founder and first CEO of Ansca mobile, the people that make the popular Lua based Corona SDK.  ( Recently featured in this comparison of Lua game engines ).

 

So, they have funding, they have experience and they are creating a game engine using a technology I am interested in.  You can certainly colour me intrigued.

 

A little bit ( so far as I can gather ) about the engine itself, Platino:

Platino allows Titanium users to build casual to high-end games in JavaScript using a carefully optimized, C++ native-built, cross-platform game engine.

But wait, there’s more:

  • OpenGL ES 2.0:  We’re bringing you the industry standard in hardware-accelerated, cross-platform API for 2D and 3D graphics.  The OpenGL engine is a flexible and powerful low-level interface between software and graphics acceleration, so you get the best performance with the most efficient (i.e. lowest possible) use of resources.
  • Isometric Tile Engine:  We have developed our own internal isometric engine, written entirely in C++.  The best part? It features direct hardware rendering and occlusion detection for your maps to be rendered at full hardware speed, giving them near real-time interactivity.
  • Sprite Sheets:  We also have developed our own internal sprite sheet engine, which also is optimized for performance and rendering.
  • Box2D:  The same physics engine that drives Angry Birds and some of the most popular mobile games on the market is embedded in the Platino game engine.
  • Particles:  We know you’re too creative for a “one size fits all” approach. So, using our Particle Designer plug-in, you can now create stunning visual effects at hardware speeds by customizing every aspect of your particle emitters.
  • Shaders:  Our game engine leverages OpenGL’s shading language to calculate rendering effects on images, vertices, and pixels – giving you a high degree of flexibility and rendering capabilities for your games.
  • 2.5D Support:  Built natively (yet again!) to give your game that extra “depth” needed to create pseudo-3D effects.
  • Native Interface Access:  Full access to your creations, so you can tinker with the native UI, mouse events, accelerometer, and more through Titanium’s powerful JavaScript methods and classes.


So if you like to work in JavaScript, want to create a game targeting iOS and Android but with native performance, Lanica/Platino/Titanium are certainly worth keeping an eye on!

 

As of right now, they appear to be in the pre-beta stage, although there is a sign up page here.  Hopefully I am able to get more access to Platino shortly, so I can bring you a bit more information. Neither pricing nor a release date have been announced yet.

News , , ,

3. October 2012

After a release candidate, followed by another release candidate, Blender 2.64 is finally here.

 

For game developers, the big release was actually the last one, this release is more focused of VFX, but there is still plenty to get excited about.  Here are details of the release in the Blender Foundation’s own words:

 

The focus was on creating a full VFX pipeline, with improved motion tracking using a planar tracker, easier green screen keying, and a new mask editor. A new tile based compositing system was added, along with more advanced color management. Cycles rendering got dozens of smaller features and improvements resulting from its use in production.

Sculpting now supports masks, and a skin modifier was added to quickly create a model from skeletons. The game engine got improved shadows and physics options, and Collada export now has more options to tune for exporting to other game engines.

 

The key changes, from a game developer’s perspective, are probably:

Mesh Tools

Bevel and inset now are modal operator with mouse control, a wire frame tool was added to turn edges into wireframe tubes, and vertex/edge/face sorting tools were improved.

Collada

The Collada exporter has been improved for better support of export to game engines, with more fine grained options to control which data is exported.

Sculpting

Sculpting has received some major improvements such has masking to control which areas of the mesh are influenced by sculpting, new brush map modes to control how textures are projected onto the model, and an input stroke averaging option to make brush strokes smoother.

Skin Modifier

The Skin modifier takes a skeleton made up of vertices and edges as input and generates a polygon skin as output, consisting mostly of quads aligned along the edges. The output is mostly quads, although some triangles will appear around intersections.

Game Engine

Lamps and shadows were improved with support for variance shadow maps, shadow color, sun lamp shadows and lamp textures. Non-power of two textures and compressed textures will now load faster and use less memory. A new Character physics type was added, designed for player controlled characters.

 

Of course there were also a ton of bug fixes.

 

Good job on the release Blender team!

 

You can go download the newest release right here.  Of course, expect a bit of a wait as their servers get pummelled.

News , ,

3. October 2012

 

Not to make any assumptions about your mother’s computing abilities of course!  My mother is flummoxed by powering a computer on and its all down hill from there, so perhaps not quite easy enough *my* mother could do it.  But it is easy, really easy.  Really.  Easy.  This is just one of those areas Moai really shines.

 

If you are unfamiliar with the concept, a tilemap is pretty straight forward.  You have an image or series of images that hold the art for your game.  In this example, we are going to use a regular top down ( not isometric ) perspective and our image map is composed of tiles 32x32 pixels in size.  Your game map is then made up of a grid that says “at this location, draw this tile”.

 

First, the topic of the image itself.  That one, in the spirit of open source, I am ripping off someone else’s work! Smile  You may remember a while back Mozilla released a completely free HTML5 based MMO, BrowserQuest.  Well, we are going to make use of one of the tilesheets from that project.  This is the file right here (it is big).  This is massive overkill, but I like massive overkill.  Here is a shrunk down version of the image we are going to be working with:

image

 

The image is composed of 20 columns of 32x32 images and 98 rows of them, for a grand total of 1,960 tile images or cells, in a single image.  So, that covers our image, let’s take a look at the code:

screenWidth = MOAIEnvironment.screenWidth
screenHeight = MOAIEnvironment.screenHeight
if screenWidth == nil then screenWidth =1280 end
if screenHeight == nil then screenHeight = 800 end

MOAISim.openWindow("Window",screenWidth,screenHeight)

local viewport = MOAIViewport.new()
viewport:setSize(screenWidth,screenHeight)
viewport:setScale(480,320)

local layer = MOAILayer2D.new()
layer:setViewport(viewport)
MOAIRenderMgr.pushRenderPass(layer)

local map = MOAIGrid.new()
map:initRectGrid(15,10,32,32)
map:setRow(10,0x0b,0x0c,0x0d,0x0b,0x0c,0x0b,0xc7,0xc7,0xc7,0xc7,0x0d,0x0b,0x0c,0x0d,0xc7)
map:setRow(9,0x2e,0x2f,0x30,0x31,0x32,0x0b,0x0c,0x0d,0xc7,0x0c,0x0d,0x0b,0x0c,0x0d,0x0b)
map:setRow(8,0x41,0x42,0x43,0x44,0x45,0x0b,0x0c,0x0d,0x0b,0x0c,0x0d,0x0b,0x0c,0x0d,0x0b)
map:setRow(7,0x55,0x56,0x57,0x58,0x59,0x0b,0x0c,0x0d,0x0b,0x0c,0x0d,0x0b,0x0c,0x0d,0x0b)
map:setRow(6,0x69,0x6a,0x6b,0x6c,0x6d,0x0b,0x0c,0x0d,0x0b,0x0c,0x0d,0x0b,0x0c,0x0d,0x0b)
map:setRow(5,0x7d,0x7e,0x7f,0x80,0x81,0x0b,0x0c,0x0d,0x0b,0x0c,0x0d,0x0b,0x0c,0x0d,0x0b)
map:setRow(4,0x91,0x92,0x93,0x94,0x95,0x0b,0x0c,0x0d,0xc7,0x0c,0x0d,0x0b,0x0c,0x0d,0x0b)
map:setRow(3,0xa5,0xa6,0xa7,0xa8,0xa9,0x0b,0x0c,0xc7,0xc7,0x0c,0x0d,0x0b,0x0c,0x0d,0x0b)
map:setRow(2,0xb9,0xba,0xbb,0xbc,0xbd,0xc7,0xc7,0xc7,0xc7,0xc7,0x0d,0x0b,0x0c,0x0d,0x0b)
map:setRow(1,0xc2,0xc2,0xc2,0xc2,0xc2,0xc2,0xc2,0xc2,0xc2,0xc2,0xc2,0xc2,0xc2,0xc2,0xc2)


local mapTiles = MOAITileDeck2D.new()
mapTiles:setTexture("tilesheet.png")
mapTiles:setSize(20,98)

local prop = MOAIProp2D.new()
prop:setDeck(mapTiles)
prop:setGrid(map)
prop:setLoc(-480/2,-320/2)
-- functionally the same as above
-- prop:setPiv(480/2,320/2)

layer:insertProp(prop)

And if we run this code, we see:

 

image

 

Ok, that’s a pretty cool amount of stuff on screen, for not so much code!  Let’s take a look at exactly what we did here.  Once again from the top ( one of these examples I am going to do from the bottom! ).  Once again, we are only going to look at the new bits, so if I fail to cover something, check out the prior tutorials, chances are I covered it then.

 

local viewport = MOAIViewport.new()
viewport:setSize(screenWidth,screenHeight)
viewport:setScale(480,320)

We’ve seen this many times before, but always in the past I’ve set setSize and setScale to the same value, this time I didn’t… why?  Well the biggest reason is, I defined the grid of map values by hand, which was a gigantic pain in the butt for a screen made up of a 15x10 array of tiles ( 480/32==15, 320/32==10 ), let alone a 1280x800 screen!  By setting the viewport to the actual resolution of your device, but setting the scale to your target resolution ( in this case, the iPhone 3g’s native resolution ), MOAI automatically handles the scaling for you.  This is a nice easy way to support a number of different screen resolutions without having to code each one by hand.

 

Speaking of the map values:

local map = MOAIGrid.new()
map:initRectGrid(15,10,32,32)
map:setRow(10,0x0b,0x0c,0x0d,0x0b,0x0c,0x0b,0xc7,0xc7,0xc7,0xc7,0x0d,0x0b,0x0c,0x0d,0xc7)
map:setRow(9,0x2e,0x2f,0x30,0x31,0x32,0x0b,0x0c,0x0d,0xc7,0x0c,0x0d,0x0b,0x0c,0x0d,0x0b)
map:setRow(8,0x41,0x42,0x43,0x44,0x45,0x0b,0x0c,0x0d,0x0b,0x0c,0x0d,0x0b,0x0c,0x0d,0x0b)
map:setRow(7,0x55,0x56,0x57,0x58,0x59,0x0b,0x0c,0x0d,0x0b,0x0c,0x0d,0x0b,0x0c,0x0d,0x0b)
map:setRow(6,0x69,0x6a,0x6b,0x6c,0x6d,0x0b,0x0c,0x0d,0x0b,0x0c,0x0d,0x0b,0x0c,0x0d,0x0b)
map:setRow(5,0x7d,0x7e,0x7f,0x80,0x81,0x0b,0x0c,0x0d,0x0b,0x0c,0x0d,0x0b,0x0c,0x0d,0x0b)
map:setRow(4,0x91,0x92,0x93,0x94,0x95,0x0b,0x0c,0x0d,0xc7,0x0c,0x0d,0x0b,0x0c,0x0d,0x0b)
map:setRow(3,0xa5,0xa6,0xa7,0xa8,0xa9,0x0b,0x0c,0xc7,0xc7,0x0c,0x0d,0x0b,0x0c,0x0d,0x0b)
map:setRow(2,0xb9,0xba,0xbb,0xbc,0xbd,0xc7,0xc7,0xc7,0xc7,0xc7,0x0d,0x0b,0x0c,0x0d,0x0b)
map:setRow(1,0xc2,0xc2,0xc2,0xc2,0xc2,0xc2,0xc2,0xc2,0xc2,0xc2,0xc2,0xc2,0xc2,0xc2,0xc2)

In the immortal words of Douglas Adams DON’T PANIC!  This looks a lot scarier than it actually is.  We are creating our map, which is a variable of type MOAIGrid.  Think of a grid as a 2d array of sorts that holds references to the tiles at each location.  Really that’s about what it does.  So we start of by initializing with a call to initRectGrid() passing in the number of tiles ( 15x10 ) and the width and height of each tile ( 32,32 ).  The next 10 lines are simply populating the grid with calls to setRow(), the first value is the row index, then what follows is a list of 32bit indexes in hexidecimal.  This is where the scary comes in.

 

Basically the top left tile in our image map is at 1 ( 0x01 ) and there are 20 tiles per row in the image we used, so the first image in the second row is therefore at 21 ( row count * row width + offset == 1 * 20 + 1 == 21 ), which is then translated in to hex, which is 0x15. 

 

So, basically we “draw” our map using image locations ( as a 1d index using the formula above, converted to hex ).  The MOAIGrid itself, row 1 is the bottom most row, which is why our grid counts down from 10.  Now let’s take a look at how we created our source image:

 

local mapTiles = MOAITileDeck2D.new()
mapTiles:setTexture("tilesheet.png")
mapTiles:setSize(20,98)

Pretty simple over all.  We create a MOAITileDeck2D.  Remember, a Deck is parlance for an image source, and in this case our source is a single image composed of dozens of tiles.  We set the actual image with a call to setTexture() and pass in our filename tilesheet.png.  We then set it’s size, letting it know it is made up of 20 columns and 98 rows of tiles.

 

local prop = MOAIProp2D.new()
prop:setDeck(mapTiles)
prop:setGrid(map)
prop:setLoc(-480/2,-320/2)
-- functionally the same as above
-- prop:setPiv(480/2,320/2)

Neither MOAITileDeck2D or MOAIGrid are display items, for the we need to create a MOAIProp2D.  We set it’s deck ( or source ) to our newly created MOAITileDeck2D mapTiles and it’s grid to our MOAIGrid map.  Finally we set it’s position with a call to setLoc.  This is an area of some possible confusion ( and why I showed the commented setPiv() as well ), because by default our prop will be at (0,0), which is the dead center of the screen.  However, the pivot point of our prop in the case is actually the bottom left corner of our map.  Therefore, we either need to move the pivot point to the center ( what the commented out code does ), or we position the prop offset from the center of the screen by half the width and height of the viewport.

 

You can actually accomplish a heck of a lot with just a bit of code.  Obviously in your case, you want want to use a tool to generate the MOAIGrid indices, doing it by hand is a PITA ( trust me! ).  Really though, the visual rendering of a top down RPG game just boiled down to a dozen or so lines of code!  Perhaps most impressively, this just scratched the surface of what MOAIGrid can do.

 

Coming Soon

General, Programming , ,

Month List

Popular Comments