Subscribe to GameFromScratch on YouTube Support GameFromScratch on Patreon

12. October 2012

 

I have been following RIM’s Gameplay 3d engine for some time now and they have been improving it at a pretty impressive rate.  Well, they just announced the release of version 1.5.  If you’ve never heard of Gameplay, it is a cross platform mobile game development kit written in C++, but scriptable in Lua.

 

Gameplay3d currently supports the following targets:

  • BlackBerry 10 and PlayBook 2.0 (using BlackBerry Native SDK)
  • Apple iOS 5.1 (using Apple XCode 4.3.2)
  • Google Android 2.3+ (using Google Android NDK, SDK API level 9+)

 

And can be developed using the following platforms/compilers:

  • Microsoft Windows 7 (using Microsoft Visual Studio 2010)
  • Apple MacOS X (using Apple XCode 4.3.2)
  • Linux (using CMake or CodeBlocks 10)

 

As to what’s new in 1.5:

  • Linux support. (tested on Ubuntu 12)
  • CMake support for makefile generation for Linux.
  • CodeBlocks 10 IDE support for Linux.
  • Gamepad controllers support for desktops.
  • Touch gesture support for tap, swipe and pinch.
  • Vehicle phyics support via new PhysicsVehicle and PhysicsVehicleWheel classes.
  • Adds new racer sample (sample06-racer).
  • Adds gameplay-tests project as a test app for various basic engine features with some initial tests.
  • Adds support for Scene files for wildcard identifiers.
  • Adds Visual Studio Plug-in support for BlackBerry PlayBook and BlackBerry 10.
  • Adds configurable multi-sampling anti-aliasing support.
  • Adds updates to latest FBX SDK 2013.3.
  • Adds file formats documenation for game.config .scene, .material, .animation, .physics, .particle
  • Adds Game/Platform::canExit for testing device capabilities to quit. (only ios)
  • Web community forums at http://www.gameplay3d.org/forums.
  • Changed keyTimes from unsigned long[] to unsigned int[]. (breaks compat. in AnimationTarget and Animation::Channel)
  • Fixed inconsistencies from Bundle::getObjectID() to Bundle::getObjectId() (breaks compat. in Bundle)
  • Fixes the texture coordinates of Mesh::createQuad(float x, float y, float width, float height).
  • Fixes line-wise distortion when loading RGB png's into texture that are non-power of two.
  • Fixes inconsitencies in createXXXX methods. (breaks compat. in Scene)
  • Fixes Rectanngle::contains.
  • Fixes Lua print logging.
  • Fixes Lua errors to be treated as runtime warnings.
  • Fixes setVertexData to pointers instead of constant data.
  • Fixed AudioSource so that it doesn't loop by default.
  • Fixes minor UI scrolling issues.

 

And what’s in the works:

  • AI Pathfinding
  • Terrain and Water
  • Asset Pipeline improvements

 

You can download Gameplay on Github.  For a quick guide on getting started check here, it’s a bit old but should still be mostly relevant.

 

Good work Gameplay team!

News , ,

12. October 2012

 

If you followed my Moai tutorial series, you may recall in this post I mentioned that Android keyboard support currently didn’t exist, although there is an effort underway to fix that.  Well, a user over on the Moai forums got sick of waiting and implemented a soft keyboard using MoaiGui, which I covered in this post.

 

It is written as a Lua module and the only dependency is you need to pass in your Moaigui object.  Here is the code:

--utility function
function distance ( x1, y1, x2, y2 )
        return math.sqrt ((( x2 - x1 ) ^ 2 ) + (( y2 - y1 ) ^ 2 ))
end

k = {}

local kboard = nil
local keysWide = 1
local keySize = 5
local boardHeight = keySize
local left = 50 - keySize/2
local top = 100
local xpos = left
local ypos = top
local rowCount = 0
local speed = 1000
local textbox = nil

--release all gui objects
k.destroyKeyboard = function (self, gui)
        if kboard ~= nil then
                for k, v in pairs(kboard) do
                        gui:destroyWindow(v)
                end
                kboard = nil
        end
end

--setup a new keyboard, starting with only 1 row
k.createKeyboard = function (self, gui, charsWide)
        if kboard ~= nil then destroyKeyboard(gui) end
        kboard = {}
        keysWide = charsWide
        boardHeight = keySize
        left = 50 - keySize*charsWide/2
        xpos = left
        top = 100
        ypos = top
        rowCount = 0
end

 

local function keyClick(event, data)
        --give focus to selected textbox so that it receives the keypress
        if textbox ~= nil then data.g:setFocus(textbox) end
        --pass keypress to gui
        data.g:injectKeyDown(data.character)
        data.g:injectKeyUp(data.character)
end

 

k.addKey = function (self, gui, char)
        if kboard == nil then return end
        --empty string indicates skipping a key slot
        if char ~= "" then
                kboard[char] = gui:createButton()
                kboard[char]:setPos(xpos, ypos)
                kboard[char]:setDim(keySize, keySize)
                kboard[char]:setText(char)
                data = {}

                data.g = gui
                if char == "<" then
                        data.character = 8 --backspace
                else
                        data.character = string.byte(char) --ascii value of character
                end

                kboard[char]:registerEventHandler(kboard[char].EVENT_BUTTON_CLICK, nil, keyClick, data)
        end
        --increment to next key in row
        rowCount = rowCount + 1
        xpos = xpos + keySize
        if rowCount >= keysWide then
                --new row
                xpos = left
                ypos = ypos + keySize
                boardHeight = boardHeight + keySize
                rowCount = 0
        end
end

 

--moves the key to its place at the bottom of the screen (or off the screen if show=false).
--NOTE: only returns once the move has finished
local function moveKey(key, show)
        local x, y = key:getPos()
        --move onto the screen
        local newy = y - boardHeight
        --move off the screen
        if not show then newy = y + boardHeight end
        --not really the best way of getting the target location, but the easiest/quickest solution
        key:setPos(x, newy)
        local tx, ty = key._rootProp:getLoc()
        key:setPos(x,y)
        --calculate a travel time relevant to the distance being traveled
        local travelTime = distance(x, y, x, newy) / speed

        MOAIThread.blockOnAction(key._rootProp:seekLoc(tx, ty, travelTime, MOAIEaseType.LINEAR))
        --the seekLoc only moves the prop, the prop container is not aware of the move, so tell it.
        key:setPos(x, newy)
end

 

--moves all keys to the desired location
k.showKeyboard = function (self, gui, show)
        if kboard == nil then return end
        --only need to pass in show when you want to hide the keyboard
        if show == nil then show = true end
        for k, v in pairs(kboard) do
                --move keys in separate threads
                MOAIThread.new():run(moveKey, v, show)
        end
end

 

k.hideKeyboard = function (self, gui)
        self:showKeyboard(gui, false)
end

k.setTextbox = function(self, tbox)
        textbox = tbox
end

return k

 

 

And here is some code demonstrating the keyboard in action:

 

local kb = require "keyboard"

kb:createKeyboard(gui, 11)
keys = {"1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "<",
        "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "",
        "", "a", "s", "d", "f", "g", "h", "j", "k", "l",  "",
        "", "", "z", "x", "c", "v", "b", "n", "m"}
for k, v in pairs(keys) do
        kb:addKey(gui, v)
end

kb:showKeyboard(gui)

local editBox = nil
local function handleEditBoxGainFocus(self, event)
        self._cursorPos = #self._internalText + 1
        self:_addCursor()
        kb:setTextbox(editBox)
        return self:_baseHandleGainFocus(event)
end

editBox = gui:createEditBox()
editBox :setPos(40, 30)
editBox :setDim(20, 5)
editBox :setText("")
editBox ._onHandleGainFocus = handleEditBoxGainFocus

 

 

None of this code is mine, all of the credit goes to lancew over on the Moai forums.  The only thing I have done is slightly changed the formatting to make it a bit more legible. You can read the original thread right here.

 

Great work Lance!

Programming ,

12. October 2012

Just a heads up, this post has absolutely nothing to do with game development.  It was a little annoyance I ran into today that had a completely unintuitive answer.  I post here so that, thanks to the power of Google, others may learn from my frustration.

 

I have a laptop that is for portability, the one I use when I am out and about ( which is more often then not ), but when I am home, my wife often makes use of it.  For debugging reasons, I hate having unknown processes running in the background, so I always log her account off the next time I log in.  I have done it the same way for years, open Task Manager ( CTRL+SHIFT+ESC ), go to the Users tab, right click the user name and select log off.  As the title suggests, today things didn’t quite work out and I received the error “User username (SessionId=1) could not be logged off.  Access denied.”.  First off, my wife’s name isn’t Bob in case you are wondering.

 

Second, the fix is really easy, just completely confusing why it is a fix at all!

In Task Manager, switch over to the process tab and click “Show processes from all users.”

 

image

 

That’s it, you can now log the other user off.

 

Again, I haven’t got the foggiest idea why this works.  I have that feature checked 99.99% of the time, so I have never run into this problem before.  Obviously more happens behind the scenes than just displaying more processes when you click that button.

 

Anyways, hope that proves useful to someone at some point. 

 

Now back to our regularly scheduled programming.

Totally Off Topic

11. October 2012

Our application hasn’t looked very… applicationy up until this point.  The menu area was basically a space full with “coming soon”.  In this post we will address adding a menu to our HTML app and show how we can pass fire and handle menu click events.

 

First change I suppose, we need a menu.  We will be using the YUI menu plugin MenuNav.  If I am honest, it is unweildy compared to some HTML UI widgets I have used in the past, but since we are using YUI, might as well use it for everything.

 

We make the following changes to mainMenu.Template

 

<div style="width:100%" class="yui3-skin-sam">
    <div id="appmenu" class="yui3-menu yui3-menu-horizontal"><!-- Bounding box -->
        <div class="yui3-menu-content" ><!-- Content box -->
            <ul>
                <li>
                <a class="yui3-menu-label" href="#file">File</a>
                <div id="file" class="yui3-menu">
                    <div class="yui3-menu-content">
                        <ul>
                            <li class="yui3-menuitem" id="menuFileExit">
                                <a class="yui3-menuitem-content" href="#">Exit</a>
                            </li>
                        </ul>
                    </div>
                </div>
                </li>
            </ul>
        </div>
    </div>
</div>

Read the link above for more details about exactly what is going on here.  The key things to notice are the id’s for the menu (appmenu) and menu item (menuFileExit), both of those will be used shortly.  It is also of key importance to give the containing div the class yui3-skin-sam, as this is what brings in all of the YUI3 css and formatting.  You could also add this to the <BODY> tag in editor.View.js, which we may do as we add more YUI controls.  Just be aware that a parent node within the DOM needs to have this class declared.

 

So, that’s is our markup, lets look at the code side of things.  Open up and change mainMenu.View.js

YUI.add('mainMenuView',function(Y){
    Y.MainMenuView = Y.Base.create('mainMenuView', Y.View, [], {
        initializer:function(){
            var results = Y.io('/scripts/views/templates/mainMenu.Template',{"sync":true});
            // No need to compile, nothing in template but HTML 
            // this.template = Y.Handlebars.compile(results.responseText);
            this.template = results.responseText;
        },
        render:function(){
            this.get('container').setHTML(this.template);
            var container = this.get('container');

            var menu = container.one("#appmenu");
            menu.plug(Y.Plugin.NodeMenuNav);

            //Register menu handlers
            var menuFileExit = container.one('#menuFileExit');

            menuFileExit.on("click",function(e){
                alert("Publishing");
                Y.Global.fire('menu:fileExit', {
                    msg:"Hello"
                });
            });

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

            return this;
        }
    });
}, '0.0.1', { requires: ['view','io-base','node-menunav','event','handlebars']});

Here we changed our initializer to load synchronously as well, otherwise the basics are pretty much the same.  Not that we added the ‘node-menunav’ and ‘event’ dependencies to our requires array.  Otherwise the key changes are:

var menu = container.one("#appmenu");
menu.plug(Y.Plugin.NodeMenuNav);

This locates our appmenu div and plugs the NodeMenuNav into it, turning our DIV into a YUI3 style menu.  Basically this is where the magic happens.  Then:

var menuFileExit = container.one('#menuFileExit');
menuFileExit.on("click",function(e){
    alert("Publishing");
    Y.Global.fire('menu:fileExit', {
        msg:"Hello"
    });
});

Next we find our menuFileExit menu item and register an onClick handler for it.  When a click occurs we fire a global event named “menu:fileExit”, with a msg of Hello.  The name menu:fileExit was chosen by me and can be anything.  So, when the user clicks the Exit item in the menu, this event will be fired.  Let’s look at how you handle “catching” this event.  Open up editor.View.js and at the bottom of the initializer() function, add the following code:

Y.Global.on('menu:fileExit', function(e){
   alert(e.msg);
});

Basically, this monitors for a menu:fileExit event being fired, and simple alerts the contents.  This illustrates a simple way to provide a global menu which can be handled across multiple views.

 

Here is our project in action now:

Basically, it is exactly the same as before, but now it has a menu.

 

You can download the complete source code here.

Design, Programming , , ,

9. October 2012

As we saw in the last part, our application is made up of a single view composed of 3 child views.  In this post I am going to focus on the left hand view, which is where the actual map will be drawn.  This is easily the most important part.

 

All I hoped to accomplish today was to get an EaselJS stage integrated in to a YUI View, which with some horrific hacking, I have accomplished.  There are a few very important requirements.

 

First, we need to have a canvas element that EaselJS can work with.

Second, we want the canvas element to take up as much room on the UI as possible.  The right hand view is going to be fixed at 280 pixels in width, so we want the map editing area to consume the rest of the screen.

Finally, I want the whole thing to resize if the window is resized, so our application can support any resolution.

 

To accomplish this, I have altered the person.Template ( the right hand side placeholder for now ), to look like this:

<div style="width:280px;min-width:280px;max-width: 280px;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>

Only real change here is the alteration to the parent div.

 

In editor.View.js I made the following simple change that the bottom of the render() function:

Y.one('body').setStyle("margin",0);
return this;

This is simply overriding the YUI default BODY styling, as I do not want any margins, padding or spaces between elements.

 

Then I altered map.Template as follows:

<div style="margin:0px;float:left;display:block" id="panel">
    <canvas width=300 height=300 id="mainCanvas" style="background-color: black;">
        Your browser doesn't support the canvas tag.
    </canvas>
</div>

I needed a named div to access programmatically, so I created one called “panel”.  I also changed the styling on the canvas so the background color would be black, making debugging a bit easier.  The dimensions passed to the canvas are going to be completely ignored.  Why the heck Canvas didn’t support % layout, I will never understand.

 

Finally, the majority of changes are in map.View.js, which I basically re-wrote:

YUI.add('mapView',function(Y){
    var Instance = null;
    Y.MapView = Y.Base.create('mapView', Y.View, [], {
        events:{
          "#mainCanvas": {
              click:function(e)
              {
                  alert("Blah");
              }
          }
        },
        initializer:function(){
            Instance = this;
            var results = Y.io('/scripts/views/templates/map.Template',{"sync":true});
            template = Y.Handlebars.compile(results.responseText);
        },
        prepareCanvas:function(){
            this.resizeEvent();
            createjs.Ticker.setFPS(30);
            createjs.Ticker.addListener(this.gameloop);

            Y.on('windowresize',this.resizeEvent);
        },
        render:function(){
            if(this.template === null)
                this.initializer();
            this.get('container').setHTML(template());
            this.prepareCanvas();
            return this;
        },
        gameloop:function(){
            Instance.stage.update();
            Instance.stage.getChildAt(0).x++;
            if(Instance.stage.getChildAt(0).x > Instance.stage.canvas.width)
                Instance.stage.getChildAt(0).x = 0;
        },
        resizeEvent:function(){
            var container = Instance.get('container');
            var canvas = container.one("#mainCanvas");
            var panel = container.one('#panel');

            var body = Y.one("body");
            var screenWidth = body.get("clientWidth");
            var screenHeight = body.get("scrollHeight");

            var width = Math.floor(screenWidth -280);
            var height = Math.floor(screenHeight );

            canvas.setStyle("width",width);
            canvas.setStyle("height",height);

            this.stage = new createjs.Stage(canvas.getDOMNode());
            // for some reason, easel doesn't pick up our updated canvas size so set it manually
            this.stage.canvas.width = width;
            this.stage.canvas.height = height;

            var shape1 = new createjs.Shape();
            shape1.graphics.beginFill(createjs.Graphics.getRGB(0,255,0));
            shape1.graphics.drawCircle(200,200,200);

            this.stage.addChild(shape1);
        }
    });
}, '0.0.1', { requires: ['view','event','io-base','handlebars']});

 

The variable Instance is a horrible hack, that I intend to replace at some point in the future.  That’s the joy of exercises like this, I can refactor out my hacks later on.  So, why does it exist… well you see, I make a couple of functions that are called back by external code, which completely clobber my this pointer.  I suppose it’s a poor mans singleton of sorts.

 

The end result of this code:

 

 

First thing I did within MapView is declare an event if someone clicks on our canvas ( which needs the id mainCanvas… this is another hackish solution that should possibly be factored away, although frankly, I am OK with requiring the canvas tag to have a certain ID, so I probably wont ) this function is called.  It was simply written to figure out how YUI views handled events.  All it does is pops up an alert with the text Blah.  As you can see, handling element level events in relatively simple, although sadly I couldn’t figure out how to capture document level events here.  Another thing on the todo list.

 

In the initializer function I take a copy of the this pointer in the Instance variable ( *hack* *hack* ), and have changed the template fetching code to no longer be asynchronous, to completely remove some unnecessary race conditions that can result from a network delay retrieving the template. Frankly in this case, async bought us nothing but headaches.

 

prepareCanvas is the method responsible for setting up the easelJS integration.  It starts off by calling resizeEvents, which is where the bulk of the actual work is done.  resizeEvents was factored out to a separate function, because this logic is the same on initial creation as it is when the window is resized.  When resizeEvent() is called, we first find the BODY tag, and get its width and height using clientWidth and scrollHeight.  You would think the obvious value would be clientHeight, but you would be wrong, this is just one of those ways that HTML sucks.  Once we have the width and height, we then calculate our view dimensions, by subtracting the space needed for the other views ( or… will soon for height that is ).  We then set the canvas to those dimensions using setStyle(), which resizes the CANVAS in the browser.  We then create our Stage object from our canvas.  One thing to keep in mind, YUI get() and one() functions return YUI Node objects, not actual DOM objects, so when dealing with 3rd party libraries, you need to access the actual DOM item the node contains, that can be done with .getDOMNode().  Next we manually update the stage.canvas width and height, because of what I can only assume is a bug, EaselJS doesn’t pick up the modifications we made to the Canvas dimenions… who knows, there might be something else going on behind the scenes.  Next we create a circle… just so we have something visible on screen, and add it to our stage.

 

Now that resizeEvents is done, back in prepareCanvas we then set up a Ticker, which is an EaselJS callback mechanism, somewhat like setTimeout.  This is the heartbeat of your application, and due to the setFPS(30) call, it *should* be called 30 times per second.  This is your traditional game loop within the application and will probably be used quite a bit in the future.  Finally we handle windowresize events using the Y.on() handling mechanism, to catch the case the user resizes the screen, and if they do we call resizeEvents ( which being an eventhandler, will clobber all over our this pointer ).

 

Finally, we have the aforementioned gameloop, which is a function that is going to be called every time createjs.Ticker, um… Ticks.  For now we simply update our stage, then find the one and only item on our stage with getChildAt(0), which is our circle, and increment it’s X value until it scrolls off the screen.

 

It seems a bit more complicated than it is, but the basics of most of what we are going to want to deal with is now in place.  We can handle UI events via YUI, can render to the Canvas using the EaselJS library and best of all, take full advantage of the screen resolution, no matter how big or small, and gracefully handle changes in size, something Canvas doesn’t do easily.

 

Figuring out how everything interacted was more of a headache than I expected, but I am reasonably happy with this setup for now.  Of coure, I am going to need to add real functionality to the map view, and have it feed by a Map model instead of just drawing a circle on screen, but all things in time.

 

You can download the complete project as of this point right here.

You can see the project in action by clicking here.  As you resize, so will the canvas element.

Programming, Design , , ,

Month List

Popular Comments