Phaser with TypeScript code samples published to Github

6. November 2014

 

I will admit, I am somewhat late to this party… I’ve shared a project or two to Github in the past, but I’ve never really embraced it.  It’s one of those things I should probably change and I will start to address. 

 

So here we are starting with the code samples for the TypeScript with Phaser series.  For many tutorials I often didn’t keep the solutions I used to create examples, so I have nothing to share.  However for this series, I have done all of the work in a single Visual Studio solution, so it made pushing it up to Github easy.  You will find 90% of the examples from that series are now available on Github.  For a few minor samples, I simply made changes to the base project to come up with the derived code, so you wont find those ones.  I pushed everything up, so that means all the assets used are included too.  Since I continue to use this single solution for the entire project, as I add new examples, they will be available on Github as well.

 

You can access all the samples here on Github.

 

image

 

I will be the first to admit, I know very little about Github as anything other than a consumer, so expect so tales of epic stupid while I get the hang of things.  Right now there are 29 projects in the PhaserTypescript repository, although PhaserArcade is a WIP ( guess what I’m working on… ).

 

Let me know if I’ve made any mistakes here. Hope this code proves useful to somebody.

Programming , ,




Creating a Game on an iPad -- Building an Animation Tool in Codea Part 1

5. November 2014

 

One of the challenges of working on the iPad only is the lack of animation and composition tools.  There are tons of art applications out there as we saw earlier, but nothing for getting them into game ready form.  So I had a bit of an idea and ran with it.

 

The basic idea is I am using iDraw to create individual body parts, which I then add to shape libraries so I can see how it will look all put together.  My game is going to be giant robots, so I can take a mix and match approach to character composition.  By building out the characters from parts, I can then create a variety of characters using minimal art.  Here is the process in iDraw:

IDrawAssembly

 

The problem is, when I export the end result into Codea, I don’t have something that I can use.  I would have to export each frame of animation for each mech combination one by one from iDraw to Codea to get an animated resulted, which ruins almost all of the benefits.

 

So, essentially, what I have to do, at least initially, is reproduce this functionality in Codea, so I can then create animations and variations without a massive increase in body parts.  So that is exactly what I did.  In a second you are going to see massive walls of code that I used to accomplish the task.  First I should make a few things clear.

 

The biggest one is, this is one of those things I am doing for fun, so most of this code was written during intermission between hockey periods, while waiting for something or while falling asleep in bed.  On the one hand, it’s really really cool that you can code at those times.  On the other hand… code quality tends to suffer a bit.

 

The next is along the same lines.  Lua is one of those languages that encourages experimentation… and a language that I haven’t used in a while.  A lot of times I got something to work and refactored later, if at all.  So if you see some things that make you aghast… that is probably why! :)  I am by no means done… at this point I’ve mostly just replicated the functioning above, I still intend to add several features before the tool is usable.  First and foremost I need to keyframe animations.  I added the controls, just no logic.  Of course I also need to save the results so they can be used in another game.  On top I want to polish the functionality by adding parenting, different control modes ( selection, parenting, positioning, animating ).  That all said, you do see the skeleton of a genuinely useful tool written entirely in Lua on an iPad.

 

This is VectorPoser

 

VectorPoser

 

The code, although rather ugly and in need of refactoring, is pretty well commented, so it should require minimal explanation… I hope.

 

Main.lua This is the application entry point.  Most of the touch logic is currently handled here, but should slowly be moved out

 

-- VectorPoser

-- welcome to the league of evil globals
imageList = nil  -- The image selector down the side of the screen
cachedWidth = WIDTH -- The original width, used to check if screen width changes
character = nil -- The character you are working on
selectedPart = nil -- The currently selected part of the character, if any
animationControl = nil -- Functionality, coming soon, stay tuned!

-- Use this function to perform your initial setup
function setup()
    local files = spriteList("Dropbox")
    local vectors = {}
    for k,v in pairs(files) do
        table.insert(vectors,readImage("Dropbox:" .. v))
    end
    
    imageList = ImageList(WIDTH-100,HEIGHT,100,HEIGHT,vectors)
    character = Character(WIDTH/2,HEIGHT/2)
    
    animationControl = AnimationControl(60,0)
end

-- This function gets called once every frame
function draw()
    
    background(40, 40, 50)
    update()  -- put all non rendered updates in a single function so we can eventually decouple
              -- updating from the rendor loop, maybe, someday, I promise. Maybe.
    
    -- This sets the line thickness in pixels I think
    strokeWidth(5)
    
    -- render our image list, which again is the list of images down the right hand side
    imageList:draw()
    
    -- now draw our VCR style animation controls
    animationControl:draw()
    
    -- clip our viewport so it wont draw in the same space as the image list
    clip(0,0,imageList:getPosition())
    
    -- now draw our character
    character:draw()
end

function touched(touch)
    -- pass along touches to all the touchables so they can be touched, duh!
    imageList:touched(touch)
    animationControl:touched(touch)

    -- TODO, spin the following code out to a function or class so we can have different 
    -- touch handling in different modes 
        
    -- Check to see if there is a currently selected part
    if selectedPart then
       -- if something is already selected, continue until touch up 
        if(touch.state == ENDED or touch.state == BEGAN) then
            -- If the touch ended or paradoxally began, unselect the active part and trigger 
            -- changed so UI updates
            selectedPart = nil
            selectedPartChanged()
        else
            -- otherwise move the selected part to the touched location
            selectedPart.x = touch.x
            selectedPart.y = touch.y
            
            -- now update so parameters get updated
            selectedPartChanged()
        end
    else
        -- There is no currently selected part, check if this touch hits an existing part
        local hits = character:getPartsAtPoint(touch.x,touch.y)

        if #hits > 0 then
            -- find the highest z value and make it selected
            -- yes, this makes touches items behind other items a pain in the ass
            for i,v in pairs(hits) do
                if selectedPart == nil then selectedPart = v
                else
                    if v.z > selectedPart.z then
                        selectedPart = z
                    end
                 end
            end
            -- Notify UI of the change
            selectedPartChanged()
        end
    end
    
    -- if you swipe the right edge right, hide the ui off the screen to the right
    -- on swipe left nring it back on screen
    -- TODO: Move logic into the ImageList
    if touch.x > WIDTH-100 then
      -- clear selection if user clicks off main area
          selectedPart = nil
        if touch.deltaX < -20 then
            --left swipe
            local x,y = imageList:getPosition()
            if x == WIDTH then
                imageList:setPosition(WIDTH-100,HEIGHT)
            end
            end
        if touch.deltaX > 20 then
                -- right swipe
                imageList:setPosition(WIDTH,HEIGHT)
        end
    else
        -- otherwise lets clear selected if touched anywhere else
        local img = imageList:getSelected()
     
        if img then
            character:addPart(VectorPart(img,touch.x,touch.y))
         end
        
        imageList:clearSelected()
    end
    

end

    
-- handle all non graphical updates on a per frame basis
function update()
    checkResize()
end

-- called when device is changed from landscape to portrait and vice versa
-- we need to resize our ImageList
function orientationChanged(orientation)
    if imageList then
        imageList:setDimensions(100,HEIGHT)
    end
end

-- there may be a better way to do this, but check for a change in size, specifically
-- due to parameters windows going away and coming back causing a resize
function checkResize()
    if cachedWidth ~= WIDTH then
        -- resize occured, handle accordingly
        cachedWidth = WIDTH
        -- TODO: This currently brings hidden panel back on screen... probably shouldn't
        imageList:setPosition(WIDTH -100,HEIGHT)
    end
end

-- when the selected part changes update params
function selectedPartChanged()
    if selectedPart == nil then
        parameter.clear()
    else
        -- z axis changed
        parameter.integer("Z order",0,20,selectedPart.z,
        function(val) selectedPart.z = val end)
        
        -- delete button, remove selected part from character
        parameter.action("Delete",function()
            character:removePart(selectedPart)
            selectedPartChanged()
        end)
        
        parameter.number("Rotation",0,360,selectedPart.rotation,
        function(val) selectedPart.rotation = val end)
    end
    
end

 

ImageList.lua This is the control down the right hand side that enables you to select images.  It handles scrolling up and down, as well as flick left to hide flick right to show.  Most of the logic is in displaying the images and mapping from touch to selected image.

-- imagelist is for displaying a number of images for selection
-- supports scrolling via long drag up and down

ImageList = class()

function ImageList:init(x,y,width,height,imgs)
    -- you can accept and set parameters here
    self.x = x
    self.y = y
    self.width = width
    self.height = height
    self.imgs = imgs
    self.IMAGE_HEIGHT = 100
    self.maxVisibleImages = math.floor(self.height/self.IMAGE_HEIGHT)
    self.topImageIndex = 0
    self.selected = nil
    
    self.touchDeltaY = 0
end

function ImageList:draw()
        self:_drawFrame()
        self:_drawImages()
        self:_drawSelection()
end

function ImageList:touched(touch)
    self:_touchToSelected(touch)
    if self:_isTouchInFrame(touch) then
        if touch.state == ENDED then
            self.touchDeltaY = 0
        end
        
        -- as the user swipes up or down within the imagelist track the y delta
        -- if it exceeds 50 increase or decrease the topImageIndex
        -- thus scrolling the available images
        if touch.state == MOVING then
            
            self.touchDeltaY = self.touchDeltaY + touch.deltaY
            
            if self.touchDeltaY > 50 then
                if self.topImageIndex + self.maxVisibleImages <= #self.imgs then
                    self.topImageIndex = self.topImageIndex + 1
                end
                self.touchDeltaY = 0
            end
            
            if self.touchDeltaY < -50 then
                if self.topImageIndex > 0 then
                    self.topImageIndex = self.topImageIndex - 1
                end
                self.touchDeltaY = 0
            end
        end
    end
end



    

-- draw a frame around our control
function ImageList:_drawFrame()
    strokeWidth(2)
    rectMode(CORNER)
    stroke(13, 0, 255, 255)
    line(self.x,self.y,self.x + self.width, self.y)
    line(self.x+self.width,self.y,self.x+self.width,self.y-self.height)
    line(self.x+self.width,self.y-self.height,self.x,self.y-self.height)
    line(self.x,self.y-self.height,self.x,self.y)
    
end

-- draw the visible images within our control
function ImageList:_drawImages()
    
    local currentY = 0
    for i= self.topImageIndex,self.topImageIndex+self.maxVisibleImages do
        local curImage = self.imgs[i]
        if curImage then
            local aspect = curImage.width/curImage.height
            if aspect < 1 then -- taller than wide
            sprite(curImage,self.x +self.width/2,self.y-currentY-self.IMAGE_HEIGHT/2,self.width * aspect
,self.IMAGE_HEIGHT) else -- when its wider than tall, shrink it proportionally local newHeight = self.IMAGE_HEIGHT * aspect - self.IMAGE_HEIGHT sprite(curImage,self.x +self.width/2,self.y -currentY-self.IMAGE_HEIGHT/2,self.width,
newHeight) end currentY = currentY + self.IMAGE_HEIGHT end end end -- draw a line above and below selected image function ImageList:_drawSelection() if self.selected then rectMode(CORNER) strokeWidth(2) stroke(255, 16, 0, 255) local x = self.x local y = self.y - (self.selected -1)* self.IMAGE_HEIGHT fill(255, 4, 0, 255) line(x+5,y,x+self.width-5,y) line(x+ 5,y-self.IMAGE_HEIGHT,x + self.width-5, y-self.IMAGE_HEIGHT) end end -- convert to local coordinates and figure out which item is selected at touch position function ImageList:_touchToSelected(touch) -- convert touch relative to top left of screen if self:_isTouchInFrame(touch) then -- local localX = touch.x - self.x local localY = self.y - touch.y local offset =math.ceil(localY/self.IMAGE_HEIGHT) if offset <= #self.imgs then self.selected = offset else -- self.selected = nil end end end -- check if touch within the bounds of our control function ImageList:_isTouchInFrame(touch) if touch.x >= self.x and touch.x <= self.x + self.width and touch.y <= self.y and touch.y >= self.y - self.height then return true end return false end function ImageList:setPosition(x,y) self.x = x self.y = y end -- handle resize, mostly for orrientation change function ImageList:setDimensions(width,height) self.width = width self.height = height self.maxVisibleImages = math.floor(self.height/self.IMAGE_HEIGHT) end function ImageList:getPosition() return self.x,self.y end -- gets the index into the img array of the selected image function ImageList:getSelectedIndex() if self.selected then -- hack for foggy brain -- if self.topImageIndex > 1 then self.topImageIndex = self.topImageIndex - 1 end return self.selected + self.topImageIndex end return nil end function ImageList:clearSelected() self.selected = nil end -- gets the actual image function ImageList:getSelected() return self.imgs[self:getSelectedIndex()] end

 

Character.lua -- This holds the character we are creating. Mostly just a collection of VectorPart now, but more functionality will be coming in the future

-- a character is composed of several VectorParts.
-- this will ultimately be the class you use to control everything as a single entity
-- AKA, the end result

Character = class()

function Character:init(x,y)
    -- you can accept and set parameters here
    self.x = x
    self.y = y
    self.parts = {}
end

function Character:draw()
    -- order drawing by z order
        function comp(a,b)
            if a.z < b.z then return true end
            return false
        end

    table.sort(self.parts,comp)
    
    for i,v in ipairs(self.parts) do
        v:draw()
    end
end

function Character:touched(touch)
end

function Character:addPart(part)
     part.z = #self.parts +1 -- z order equal order added initially
    table.insert(self.parts,part)
end

function Character:removePart(part)
    if(part) then
        table.remove(self.parts,part.curIndex)
    end
end

function Character:getPartsAtPoint(x,y)
    local results = {}
    for i,v in pairs(self.parts) do
        if x >= v.x - v.img.width/2 and x <= v.x + v.img.width/2 and
            y >= v.y - v.img.height/2 and y <= v.y + v.img.height/2 then
            v.curIndex = i
            table.insert(results,v)
        end
    end
    return results
end

function Character:getParts()
    return self.parts
end

 

VectorPart.lua -- These are the individual parts, right now it's just the image, x,y and rotational data, but in the future it will have pivot and parenting information. Keyframe data will essentially be a snapshot of these values per animation frame

-- A vector part is simply the vector img, plus positional and rotation data
-- will be adding parenting information

VectorPart = class()

function VectorPart:init(img,x,y,z)
    -- you can accept and set parameters here
    self.img = img
    self.x = x
    self.y = y
    self.z = z
    self.rotation = 0
    self.curIndex = nil
end

function VectorPart:draw()
    pushMatrix()
    translate(self.x,self.y)
    rotate(self.rotation)
    sprite(self.img,0,0)
    popMatrix()
end

function VectorPart:touched(touch)
end

 

AnimationControl.lua -- This is a VCR style keyframe animation control, mostly just a placeholder for now

AnimationControl = class()


function AnimationControl:init(x,y)
    self.x = x
    self.y = y
    
    self.buttonFirst = IconButton("First",x+0,y+0,64,64,"Documents:IconFirst","","")
    self.buttonFirst.callback = _firstButtonPressed
    
    self.buttonPrevious = IconButton("Previous",x+66,y+0,64,64,"Documents:IconPrevious","","")
    self.buttonPrevious.callback = _previousButtonPressed
    
    self.buttonPlay = IconButton("Play",x+66*2,0,64,64,"Documents:IconPlay","","")
    self.buttonPlay.callback = _playButtonPressed
    
    self.buttonNext = IconButton("Next",x+66*3,y+0,64,64,"Documents:IconNext","","")
    self.buttonNext.callback = _nextButtonPressed
    
    self.buttonLast = IconButton("Last",x+66*4,y+0,64,64,"Documents:IconLast","","")
    self.buttonLast.callback = _lastButtonPressed
    
    
end

function AnimationControl:draw()
    self.buttonFirst:draw()
    self.buttonPrevious:draw()
    self.buttonPlay:draw()
    self.buttonNext:draw()
    self.buttonLast:draw()
end

function AnimationControl:touched(touch)
    -- Codea does not automatically call this method
    self.buttonFirst:touched(touch)
    self.buttonPrevious:touched(touch)
    self.buttonPlay:touched(touch)
    self.buttonNext:touched(touch)
    self.buttonLast:touched(touch)
    
    
end

function _firstButtonPressed()
    
end
function _previousButtonPressed()
    print"fhdhdj"
end
function _playButtonPressed()
    
end
function _nextButtonPressed()
    
end
function _lastButtonPressed()
    
end

 

Finally I used a library called Cider for the UI controls. You can get Cider here. I used the IconButton class, but it didn't have callback support, so I hacked it in like so:

--# IconButton
IconButton = class(Control)

-- IconButton 
-- ver. 7.0
-- a simple control that centers an image in a frame
-- ====================

function IconButton:init(s, x, y, w, h, img, topText, bottomText)
    Control.init(self, s, x, y, w, h)
    self.controlName = "IconButton"
    self.background = color(255, 255, 255, 255)
    self.font = "HelveticaNeue-UltraLight"
    self.fontSize = 32
    self.img = img
    self.text = s
    self.topText = topText
    self.bottomText = bottomText
    self.textAlign = CENTER
    self.smallFontSize = 12
end

function IconButton:draw()
    local h, w
    w, h = textSize(self.text)
    pushStyle()
    fill(self.foreground)
    stroke(self.foreground)
    self:roundRect(4)
    fill(self.background)
    stroke(self.background)
    self:inset(1,1)
    self:roundRect(4)
    self:inset(-1,-1)
    font(self.font)
    fontSize(self. smallFontSize)
    textMode(CENTER)
    textAlign(CENTER)
    smooth()
    w, h = textSize(self.topText)
    fill(self.textColor)
    text(self.topText, self:midX(), self:top() - h / 2 - 4)
    if self.bottomText then
        text(self.bottomText, self:midX(), self:bottom() + h/2 + 4)
    end
    if self.img == nil then
        fontSize(self.fontSize)
        text(self.text, self:midX(), self:midY())
    else
        sprite(self.img, self:midX(), self:midY())
    end
    
    popStyle()
end

function IconButton:initString(ctlName)
    return "IconButton('"..self.text.."', " ..
        self.x..", "..self.y..", "..
        self.w..", "..self.h..", '"..
        self.img .. "', '"..
        self.topText .. "', '"..
        self.bottomText .. "', '"..
        "')"
end

-- added by mjf... not sure why there wasnt a touch handler on this control
function IconButton:touched(touch)
        if self:ptIn(touch) then
            if touch.state == ENDED then
                if self.callback ~= nil then self.callback() end
                return true
            end
        end
end

 

There is some bugginess around the selectedPart handling that leads to the occasional crash, and I am on occasion crashing Codea if I use lots of images, but for the most parts, it's the skeleton of a downright usable tool. It's never going to compete with the likes of Spline or Spriter, but it should suffice for making game ready models and animations from sprite and vector graphics and at the end of the day, that's all I need. Stay tuned for an update sometime in the future.

Programming




LibGDX Tutorial Part 15: Particles Part One–2D Particles

3. November 2014

 

 

Now we are going to look at using particles in LibGDX.  This is a somewhat complex subject, so I am going to break it over a couple posts.  In this first example, we are going to create a 2D particle system in the editor.  Then we are going to write the code to run the particle system.  This process can be somewhat confusing, especially running the Particle Editor, so I have also done a video version of this tutorial.  Don’t worry, I will always continue to do text versions of all LibGDX tutorials, I am just going to mix in video when I think it will help.

 

So if you prefer, this topic is available in the video below.  Click directly on Youtube to see it in full resolution.  Once again, this video covers exactly the same content the text below does.

 

In this tutorial we are going to use one of the tutorials included with LibGDX, the particle editor.  In order to follow along you need to install the gdx-tools in your project.  Fortunately this is a pretty easy process.  When you run the setup utility to create your project, make sure you set tools to true.

 

image

 

This will result in the particle editor being included in your project dependencies.  Now it’s a matter of running the ParticleEditor, which isn’t as obvious as you would think.

 

You can run it from the command line, but this presents a certain special challenge, since LibGDX moved to a Gradle based setup…  so, where exactly is gdx.jar these days?  That’s a good question, and the answer is very very well hidden.

 

First make sure you’ve built your project at least once, so then all of the dependencies have been resolved ( AKA, Gradle downloaded LibGDX ).  Now LibGDX will be located somewhere in your Maven repository, which is a hidden folder.  The actual directory it will be in is random thanks to a GUID key.  On my computer on Windows its:

C:\Users\Mike\.gradle\caches\modules-2\files-2.1\com.badlogicgames.gdx\gdx-tools\1.4.1\SOMEREALLYLONGGUID

 

Obviously on your computer this will be somewhere else, and it will change with each version.  Now that you located it, you can run it.  Fortunately there is a much easier option for running the particle editor if you use either Eclipse or IntelliJ IDEA.  I will cover each in turn below ( and also show both in the video above ):

 

IntelliJ

 

Once you’ve loaded your project into IntelliJ IDEA.  Now in your Projects window, locate gdx-tools.jar ( there will be a version number too, currently 1.4.1 ).

 

image

 

Expand it until you find particleeditor, then locate the class ParticleEditor.

 

Now right click ParticleEditor and select Run ParticleEditor… main():

 

image

 

The first time you need to set some configuration values:

image

 

Really, all you need to do is set the Use classpath value to your desktop module.  Now click Run:

image

 

One working Particle Editor.

 

Eclipse

 

Eclipse is actually much simpler… if it works, which is a big if.

Simply import your project.  Then right click your desktop project, select Run As or Debug As->Java Application.

 

image

 

It should now prompt you to select which Java Application to run:

image

Select ParticleEditor – com.badlogic.gdx.tools.particleeditor and click OK.

The Particle Editor will now load.

 

Your First Particle Effect

 

Now that you’ve got the particle editor open, let’s save an effect to work with. 

In this particular tutorial we are just going to go with the default as it is already configured, which is a flame looking effect.  Locate and click the save button:

image

We want to save the resulting file to our assets folder.  If you have an Android project it will be located at [project]/android-proj/assets otherwise it will be in [project]core-proj/assets.  Save the file there, the extension doesn’t matter.  For this code example I am using particles.party.

 

Code Time

 

Now let’s take a look at the code required to display this effect:

 

package com.gamefromscratch;

import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.ParticleEffect;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;

public class Particles extends ApplicationAdapter {
   SpriteBatch batch;
   ParticleEffect pe;

   @Override
   public void create () {
      batch = new SpriteBatch();

      pe = new ParticleEffect();
      pe.load(Gdx.files.internal("Particles.party"),Gdx.files.internal(""));
      pe.getEmitters().first().setPosition(Gdx.graphics.getWidth()/2,Gdx.graphics.getHeight()/2);
      pe.start();
   }

   @Override
   public void render () {
      Gdx.gl.glClearColor(0, 0, 0, 1);
      Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

      pe.update(Gdx.graphics.getDeltaTime());
      batch.begin();
      pe.draw(batch);
      batch.end();
      if (pe.isComplete())
         pe.reset();
   }
}

 

When you run this app you should see:

 

Results

 

It’s pretty consistent with the way most other systems work in Android.  You create the ParticleEffect then load it from file at the root of your assets folder.  We then position the emitter at the center of the screen.  Particle emitters are the source of particles and are often used to control over all behavior.  We then start the particle system running.

 

In render, just like with physics systems, we need to advance the particle effect using update().  This causes the simulation to calculation the position, colour, etc… of all the particles it controls.  Next we draw the ParticleEffect using draw() and drawing to our sprite batch.   Finally we check to see if the effect is complete, and if it is, we restart it.

 

Obviously we glossed over the Particle Editor tool, as well as 3D particle effects, so expect more tutorials soon.

 

Programming ,




Guide to Creating a Game On iPad -- Vector Graphics in Codea

31. October 2014

 

Now that we know how to create vector graphics on iPad, let's take a look at the process of getting them into and using them in Codea.  This part is insanely easy, in fact if this was all there was to it, I could cover this entire process in a single Tweet!  Let’s do it.

 

First of course you need to create a vector graphic in some application and save it as PDF to your Dropbox folder.

Then fire up Codea, create a new project with the following code:

function draw()
    background(40, 40, 50)
    sprite("Dropbox:Jet",WIDTH/2,HEIGHT/2)
end

 

Yeah... that's it.  Select the image from your Dropbox, run it and you will see:

 

Untitled5

 

So basically vectors are treated exactly the same as bitmaps as far as Codea is concern.  Well, with a few exceptions… you can scale them as much as you want and never lose visual fidelity.

 

You can scale by passing in the width and height to the sprite call, like so:

sprite("Dropbox:Jet",WIDTH/2,HEIGHT/2,100,100)

 

So then, vectors are easy to load, look great, scale well are pretty easy to draw... perfect no?

Well… no, there is a catch.  Performance.

Let’s take a quick look at how vectors perform, using the following code:

 

-- VectorTest
function setup()
    fontSize(160)
    fill(255, 0, 25, 255)
end

function draw()
    background(40, 40, 50)

    local outString = "fps:" .. math.floor( 1/DeltaTime)
    local dimensions = textSize(outString)
    
    strokeWidth(5)

    for i= 1,100 do
        local x = math.random(WIDTH)
        local y = math.random(HEIGHT)
        sprite("Dropbox:Jet 2",x,y)
    end
    
    text(outString,WIDTH-dimensions/2,80)
end

 

Here it is running on my iPad Air with drawing 100 vectors:

 

1

 

60 FPS is as good as we can get, so so far so good!  Now lets see what happens when we crank the number up to 500.

 

2

Oh… 

 

From my tests, you can draw about 200 vector images before framerate starts to drop down.  You can easily draw twice that many normal sprites without seeing a noticeable drop in framerate.  

 

Right now our simple frame rate counter is changing far too often to be of much use.  So instead I am going to sample and average across minutes.  Basically every frame we take a sampling of framerate, after a second elapses we move that sample to other array, average and display the results.  So basically we sample the data per frame, but average it out over the span of a second.  This should give us a much smoother frame rate total.  Here is the updated code:

 

 

-- VectorTest
frameRateSamples = { 60 }
fpsSampledPerSecond = { 60 }
elapsedTime = 0
    
function setup()
    fontSize(160)
    fill(255, 0, 25, 255)
end


function mean( t )
  local sum = 0
  local count= 0

  for k,v in pairs(t) do
    if type(v) == 'number' then
      sum = sum + v
      count = count + 1
    end
  end


  return math.floor(sum / count)
end

function draw()
    background(40, 40, 50)

    elapsedTime = elapsedTime + DeltaTime
    if(elapsedTime < 1) then
        table.insert(frameRateSamples,math.floor(1/DeltaTime))
    else
        print("Second elapsed")
        table.insert(fpsSampledPerSecond,mean(frameRateSamples))
        framerateSamples = {}
        elapsedTime = 0
        -- ever 360 seconds reset the table so it doesnt grow forever
        if #fpsSampledPerSecond > 360 then
            fpsSampledPerSecond = { 60 }
        end
    end

    local outString = "fps:" .. mean(fpsSampledPerSecond)
    local dimensions = textSize(outString)
    
    strokeWidth(5)

    for i= 1,200 do
        local x = math.random(WIDTH)
        local y = math.random(HEIGHT)
        sprite(“YourImageHere",x,y)
    end
    
    text(outString,WIDTH-dimensions/2,80)
end

    

 

Now when we run it, we get the follow values, after letting each run for over 60 seconds to take the seed value out of the equation:

Image Type FPS at 100 FPS at 250 FPS at 500 FPS at 1000 FPS at 1500 FPS at 2000
Vector 58 38 21 23 15 9
Bitmap 58 37 21 21 17 10

 

Hmmm, that's interesting.  So once the framerate sampling is smoothed out, the performance between vector and bitmap graphics is basically identical.  In both cases too, oddly enough, 500 sprites caused a drop in performance compared to 1,000…  very very very odd.

 

Now let’s take a look at if scaling is a factor.  In this test we will scale the vector down to 32x32, vs drawing a fixed 32x32 sprite and see if it impacts performance.

 

As a 32x32 pixel image, at 2000 sprites, the framerate a solid 57.

As a vector scaled down to 32x32 in the sprite call the framerate at 2000 sprites is 57.

 

There you have it, contrary to my initial findings, the performance between identically sized bitmap and vector images in Codea is about the same.  Internally, Codea must be rasterizing vectors before drawing them.

 

So, why did my initial tests show a massive improvement in bitmap performance over vectors as the count increased?  Well, that’s because I compared apples to oranges… I used a “nearly the same dimensions” graphic for the test, and that was a huge mistake. Something critical you can take away from this experiment is the size of the sprite being blitted has a huge impact on performance, much more so than the type of sprite.  As you saw above, we can blit > 2000 32x32 pixel images without a drop in performance, while the same number of 200x200 pixel images brings it to it’s knees.

 

So, in a nutshell, its equally as valid to use a vector with code than it is a bitmap.  And as you can see, it uses the exact same code.  Oh, and if you are by chance wondering why we were toping out at 57-58FPS instead of 60 you would expect, that is simply because I floored() the results (aka, rounded down) to make the numbers prettier.  Since I was measuring relative performance, not overall performance, the actual framerate really didn’t matter all that much.

Programming ,




Adventures in Phaser with TypeScript–Tilemaps Part 2 Now with more stuff!

29. October 2014

 

 

In the previous tutorial we took a look at creating, loading and displaying a tilemap in Phaser and for many simple projects this may be enough.  That said, there are two very common scenarios this doesn’t really address, and we will look at those in this tutorial.  The first is positioning a sprite within a tilemap.  The second is collision detections between tiles and sprites.

 

Placing a sprite within a tilemap is a pretty common behavior, but the way you deal with it isn’t immediately obvious in Phaser.  To understand what I am going on about it’s good to understand how layers of tilemaps work.  You may recall in our previous example, our tilemap was composed on three layers, one for the background, one for the midground (a word I apparently made up) and one for the foreground.  Basically the background is where you would put the stuff that happens behind the scene, things like clouds or the blue sky for example.  The midground is generally where the bulk of the stuff in your world would go.  The foreground then is the stuff drawn on top of the midground.

 

Quite often your playersprite will then be drawn in front of the foreground.  That said, what happens when your player is behind something in the world, such as say… a shrubbery?  How do we deal with this?  Well first, let’s visualize how the layers of a tilemap come together.

 

No better way than an image, right?

 

Layers

 

Think of each layer of tiles as sheets laid one over the next.  The top most layer obscuring the contents behind it, and that layer obscuring the contents behind it.

 

So, in Phaser, what determines draw order?  Simple, the order they were added in.  Change the order layers are drawn, you change what draws on top of what.  Know this then, how do we could we for example draw our character sprite between the foreground and midground layer so it appears behind the chandelier, like so:

 

 

Layers2

 

 

The answer is actually quite simple but possibly a bit confusing, let’s look at the code:

 

/// <reference path="phaser.d.ts"/>
class SimpleGame {
    game: Phaser.Game;
    map: Phaser.Tilemap;
    player: Phaser.Sprite;
    
    constructor() {
        this.game = new Phaser.Game(640, 480, Phaser.AUTO, 'content', {
            create: this.create, preload:
            this.preload, render: this.render
        });
    }
    preload() {
        this.game.load.tilemap("ItsTheMap", "map.json", null, Phaser.Tilemap.TILED_JSON);
        this.game.load.image("Tiles", "castle_0.png");
        this.game.load.image("Decepticon", "decepticonLarge.png");
    }
    render() {

    }
    create() {
        this.map = this.game.add.tilemap("ItsTheMap", 32, 32, 50, 20);
        this.map.addTilesetImage("castle_0", "Tiles");

        this.map.createLayer("Background").resizeWorld();
        this.map.createLayer("Midground");
        this.map.createLayer("Foreground");

        this.player = new Phaser.Sprite(this.game,200,50,"Decepticon");
        this.game.world.addAt(this.player, 2);
    }
}

window.onload = () => {
    var game = new SimpleGame();
};

 

And when we run it:

image

 

Note the order we called createLayer matches the order we want the layers drawn, from back to front.  After that we load our sprite then do something a bit different from what we’ve done in the past.  Previously we just added Sprites using this.game.add().  Now however we want a bit more control of where they are added.  This is why we created our Sprite instead using new, then explicitly added it to the world using addAt().  By adding it at index 2 ( the 3rd item ) this means we will be between the mid and foreground in the world list.

 

You should be aware however that this is incredibly fragile an approach.  You can only rely on layers being where they are because they are the only other objects we’ve created.  In a non-trivial example, you will dozens or hundreds of other objects in your scene, this may not be as easy to figure out.  There are of course alternatives.  One of the easiest, and possibly crudest options, would be too iterate through the world, find the index of layer you want to insert before and then insert like so:

 

        var index: number;
        this.game.world.forEach((child) => {
            index++;
            if (child.layer.name = "Foreground")
                index = child.index;
        }, this, false);
        this.game.world.addAt(this.player, index);

 

I suppose I should point out this particular solution iterates through every item in the world, even if the first child is what you are looking for. Also, it doesn't take into account the possibility that the given child doesn't even exist, which is probably a bad idea. That all said, I think you get the gist of how this works.  As you may notice, the world variable is pretty important, as essentially it is the scene graph, the source of all things to be rendered.  At the end of the day though, it’s just a group that we covered earlier.

 

Ok… that was a really long way of saying the order you add things to the world is really important… let’s move on, collisions.

 

Quite often you are going to want to test to see if “stuff” (scientific term there… ) hits other “stuff”.  In this particular case, we want to see if our game sprite collides with the “floor”.  First lets take a look at adding physics to our game sprite.  I’ve used physics in past tutorials, but havent gone into detail how these things work… and I am not going to here either.  For now just think of it as the work of moon wizards.  I will cover physics at a later point, I promise!

 

So, back to enabling physics and gravity for game sprites.  Here’s how:

 

    create() {
        // Enable the physics system in Phaser
        this.game.physics.startSystem(Phaser.Physics.ARCADE);
        this.map = this.game.add.tilemap("ItsTheMap", 32, 32, 50, 20);
        this.map.addTilesetImage("castle_0", "Tiles");

        this.map.createLayer("Background").resizeWorld();
        var world = this.map.createLayer("Midground");
        this.map.createLayer("Foreground");

        this.player = new Phaser.Sprite(this.game, 200, 50, "Decepticon");
        // Apply physics to our game sprite
        this.game.physics.enable(this.player, Phaser.Physics.ARCADE);
        // Turn on gravity.  10pixels / second along the y axis
        this.player.body.gravity.y = 10;

        this.game.world.addAt(this.player, 3);
    }

 

The comments explain the basics of what’s going on, like I said, I will go into a bit more detail later.  Now if you run the code with the above changes the player sprite ( the Decepticon logo ) will slowly fall to the bottom of the screen, to infinity and beyond.

 

Now what we would prefer to see is the sprite stop when it collided with the tiles composing the floor.  This takes a bit of understanding of how tilemaps are actually stored as data.  Open up map.json and you will see a layer contains an array called data full of tiles that make up the layer, like so:

 

image

 

Each of those indices present a location within your sprite image.  Let’s take a look back in Tiled for a second.

 

image

 

You can see the ID of each tile by simply clicking it in the Tilesets window.  In this case we are going to want the ID of each red topped brick.  Specifically we want the ID of the tiles that make up these particular tiles

 

image

 

In my particular case, the IDs were 32,33,34,35 and 36.  Now that we know the IDs of the tiles we want to collide with, lets set that up.  Code time!

 

/// <reference path="phaser.d.ts"/>
class SimpleGame {
    game: Phaser.Game;
    map: Phaser.Tilemap;
    layer: Phaser.TilemapLayer;
    player: Phaser.Sprite;
    
    constructor() {
        this.game = new Phaser.Game(640, 480, Phaser.AUTO, 'content', {
            create: this.create, preload:
            this.preload,update:this.update, render: this.render
        });
    }
    preload() {
        this.game.load.tilemap("ItsTheMap", "map.json", null, Phaser.Tilemap.TILED_JSON);
        this.game.load.image("Tiles", "castle_0.png");
        this.game.load.image("Decepticon", "decepticonLarge.png");
    }
    update() {
        // You actually need to perform the collision test between the map and player sprite
        this.game.physics.arcade.collide(this.player, this.layer);
    }
    render() {
        // Display the outline of the physics body
        this.game.debug.body(this.player);
    }
    create() {
        // Enable the physics system in Phaser
        this.game.physics.startSystem(Phaser.Physics.ARCADE);
        this.map = this.game.add.tilemap("ItsTheMap", 32, 32, 50, 20);
        this.map.addTilesetImage("castle_0", "Tiles");

        this.map.createLayer("Background").resizeWorld();
        this.layer = this.map.createLayer("Midground");
        this.map.createLayer("Foreground");

        this.player = new Phaser.Sprite(this.game, 200, 40, "Decepticon");
        // Apply physics to our game sprite
        this.game.physics.enable(this.player, Phaser.Physics.ARCADE);
        // Turn on gravity.  10pixels / second along the y axis
        this.player.body.gravity.y = 10;
        this.player.body.collideWorldBounds = true;

        // Now setup the tiles in our midground layer for collisions
        // When using multiple layers it is critical you specify the layer the collisions occur on!
        this.map.setCollisionBetween(32, 36,true,this.layer.index,true);
        

        this.game.world.addAt(this.player, 2);

        // Add a keyboard handler on 'R' pressed the resets the sprite to it's starting position
        this.game.input.keyboard.addKey(Phaser.Keyboard.R).onUp.add(() => {
            this.player.position.set(200, 40);
        });
    }
}

window.onload = () => {
    var game = new SimpleGame();
};

 

 

Here is the code running.

 

 

Click in the IFrame and press R to reset the sprite to it’s starting position.

 

Most of the important bits are commented in the source code, but a few things are worth noting.  The most important is how you add collision tests to individual tiles within the world.  As mentioned earlier, you need the tile ID of the tile within the layer.  You then use setCollision methods to mark which tiles perform collisions.  In this example we used setCollisionBetween() which allows you to specify a range of tiles to collide with.  There is also a version that takes an individual tile ID as well as one that takes an array.  The critical part of this call is you have to provide the layer where the collision takes place.  Also of importance is the call to physics.arcade.collide() which tells Phaser to check for a collision between that layer and your player sprite.

 

One other thing you may notice is in render(), I call game.debug.body(this.player).  This causes the renderer to draw a bounding box around the physics shape of our sprite.  This is important because it exposes a very common bug that I thought I would address.  Notice when you run the applicaion, once gravity is done, our image is resting above the ground like this:

 

image

 

This is because our source image has some empty pixels.  The physics bounding volume is going to be around the entire sprite, not just drawn pixels.  If you are using physics, be sure your image is cropped as much as possible!

 

So that illustrates how to insert a sprite between tilemap layers and how to perform collisions against tiles.  That should be the majority of what you need to get started using tilemaps.

 

Before we continue, I should answer a commonly asked question.  What about isometric maps?  Yes, Tiled can export isometric tilemaps, but currently Phaser does not support them.  There are some third party code samples to load them, but nothing in the core as of yet.

 

Programming , ,