Feb 15

So I’m using OpenAL to do the audio in Tilt to Live. Over the course of development audio became the bottleneck of my game, both in size and in performance. The following could help you if you’re experience audio performance issues and are somewhat new to OpenAL. Let me preface this with: Don’t blindly optimize. Measure, Measure, MEASURE! Know what your bottleneck is before trying to tune code!

  1. Don’t make more than 32 sources objects in OpenAL. Of course, this number may vary from device to device and what generation the device is. Making any more than the limit the device support makes openAL fail silently, and at this point you’re wasting cycles.
  2. Load your audio buffers and create your source objects ahead of time and re-use them. This is a big one. Don’t generate and delete source objects in the middle of your game update. Instead, it’s much faster to just grab a ‘free’ source that is not playing a sound any longer and attach it to another buffer. I was pre-loading my audio-buffers, but creating/deleting sources on the fly in Tilt to Live. Then I started ‘pooling” and I got a decent gain out of re-using source objects.
  3. Keep OpenAL calls to a minimum. Especially in tight update loops, don’t repeatedly call openAL functions that don’t change frame-to-frame. I found that a good portion of my time was spent doing something as simple as ‘alSourcei()’ on each source ID per frame was causing a significant slow down.
  4. Don’t query openAL state if you don’t have to. In my case, I wrapped openAL Sources in objects with properties to get volume,pitch, etc. Initially those properties simply called the openAL equivalent and returning it instantly. This was hurting my frames due to some some innocent looking “backgroundMusic.volume += someVal” happening each frame along with other audio sources doing the same thing. Save any state you can in instance variables, and as a last resort hit openAL when you need to.
  5. As for size, you should consider compressing your sound FX and music to a reasonable size. If you’re like me, you hate giving up quality; especially if you listen to the full quality one and then the compressed one. It can seem like night and day. But in reality, when your users won’t have a full quality audio file to compare it to, they will not notice the difference.

As a sidenote, you can look at my first dev tip for batch compressing audio files.

Feb 14

We're extremely close to submitting our first title "Tilt to Live". We plan on submitting it this upcoming Friday. After a long and grueling (but awesome fun!) development cycle we've learned a ton. I figured an interesting way to distill our new found knowledge it would be in very short "dev tips" for developing an iPhone game. Today I start out with audio:

Compressing Audio

By the end of development our game weighed in at 16 MB. We wanted to go below the 10MB limit so users could download our game over-the-air. Quickest solution? Compress the audio. Now, we were already using some compression but we have over 60 audio files! The main crux of the problem is we want to be able to quickly compress all our audio to a certain format, build it, look at final build size, and test for quality. For this I wrote a quick script:

#!/bin/bash
rm -rf caff
mkdir caff
for i in *.wav
do
afconvert -f caff -d ima4@22000 -c 1 $i caff/${i%%.*}.caf
done

Now for a little explanation on my setup:

  • I have a single folder (let's call it 'finalAudio') with all the original, uncompressed wave files used in our game.
  • The script sits inside this 'finalAudio' folder as well and I run 'chmod +x build_audio' where 'build_audio' is the name of my script so I can just click on it in Finder when I'm wanting to re-export my audio.

What the script does:

  1. It removes any folder/file named 'caff' in the current directory
  2. it makes a new directory inside 'finalAudio' called 'caff'. This is where the compressed audio will go
  3. It looks through all wav files inside 'finalAudio' and runs the 'afconvert' utility on each file and puts the resulting compressed file in 'caff'.

I'm not going to go into the details of all the options you have with afconvert and what audio formats the iPhone supports. There's plenty of documentation online covering that already.

Just an interesting sidenote: we took our 13-16MB game and shrunk it to 6.5 MB using ima4@22000 with 1 channel.

Oct 14

I finally got rid of the stupid...

"ld: warning in <foo>.so, file is not of required architecture."

...warning when using iSimulate. If you're not using iSimulate or a similar technology you're losing valuable time even if you don't use the accelerometer or GPS functions. Then again, I might not be the best spokesperson on time since I spent the weekend re-inventing the wheel.

isimulate

I have this need to make sure my projects compile w/o warnings. I let it sit for a while because I'm not an XCode expert so I knew I wanted to conditionally link it in but I couldn't find the UI for it. The process in which they suggest you integrate iSimulate into your XCode project truly is easy, but I felt icky when it came up with warnings when you compile against the actual device.  There are better ways of doing that. Namely, conditional build settings. Of course, it requires a few more steps and a bit more knowledge of the XCode environment so I suspect that wouldn't help their marketing. Regardless, having the iSimulate linker flags and library search paths only be added when you're compiling for a simulator is relatively easy to setup.

Oct 11

It's getting around that time when I'm spending more time working on the game as I realize how many little things need to be fixed, added, tweaked. I've recently given up most of my weekends now in an effort to get more hours in. I'm currently working 6 out of the 7 days a week on the game in some way. It's mostly game programming, sometimes graphics, web development, emailing, phone calls, or getting business forms and other legal stuff squared away.

This week was a rather productive week on several fronts:

  • Got a lot of new art assets from Adam and implemented them
  • Feedback from testers started trickling in and the reaction has been positive along with tons of great suggestions/ideas
  • Fixed a lot of old bugs that were sitting in the queue
  • Improved some memory usage of textures

The week wasn't without it's headaches though. This completion date graph illustrates just how badly I estimated this week's tasks:

Bad Estimates Ahoy!

The amount of tasks was unusually large for the past week, but the amount of tasks wasn't as big an issue as some tasks taking 10 times longer than I wanted.

The biggest one was getting multi-threaded texture loading on the iPhone working. The hours I logged into getting it working is my own personal testament to how a seemingly simple task can blow up to ridiculous portions when you mention the word "multi-threading". Had I known it'd take this long I would've opted for a more basic solution and leave it to a side project. The bright side is now I can load audio and graphics in parallel along with the main thread being able to continue any animations and such. It took about 20 hours to get it working and debugged, which is somewhat embarrassing haha!

Hair-pulling Threading Issues

It started with me wanting to off load the texture loading to another thread. Simple enough. I've done this before on other projects. It took a couple of hours to learn how to get OpenGL setup correctly to allow this. Then I discovered OpenGL wasn't being the meanie, but a function call from core graphics wasn't allowing me to call it from a background thread (sometimes...ugh). I researched it for a couple of days while finishing up other things, but wasn't able to find anything conclusive. I then decided to take the leap and implement my own PNG loader using libpng as a base.  I mean really...how hard could it be? Hah...hah...ha...

Saying it was frustrating would be the understatement of the year, and last year. I usually try to avoid copy/pasting code as I like to read and understand the API I'm using so when it DOES break I'm not utterly clueless. Libpng's interface is rather low-level and not exactly the cleanest. After reading documentation on it for what seemed forever I decided to start with an example program and modify it to fit my needs.

Let's not forget that I'm an XCode/Unix newbie so getting libpng/zlib working as a static library in XCode was a nightmare for me. I ended up building from the source against the correct config's and SDK's for the iPhone to avoid the "invalid architecture" warnings. Next came actually implementing the loading. It looked pretty straight forward in all the examples I saw, except I wanted one feature I didn't see in any of the sample code: be able to load non-power-of-2 textures onto the iPhone. All of the game's assets are of various shapes and sizes, and several are non-power of 2. I haven't taken the time to do proper texture atlasing and I wanted the ability to quickly drop any assets Adam sent me into the game's folder and load it without fiddling with size alignments and the other mess.

This is where things started to get hairy because I was seeing issues crop up that weren't necessarily from my code so I was lead astray a lot. The caveat is the iPhone has a rather special way of "compressing" PNGs. It took me a while to understand the implications of this because I could load the PNGs fine but holy shit were they rendering wrong in OpenGL! It finally dawned on me to change the blend function OpenGL was using. Much to my surprise I couldn't find one that would give me the equivalent alpha blending that I got with premultiplied alpha's. And I wasn't about to go back and re-design all the assets. By this time I was pretty comfortable working with libpng and I decided to just write a custom transform function to premultiply the alpha when loading it to mimic the previous technique of loading:

Finally, you can write your own transformation function if none of the existing ones meets your needs. This is done by setting a callback with

png_set_read_user_transform_fn(png_ptr,
       read_transform_fn);

You must supply the function

void read_transform_fn(png_ptr ptr, row_info_ptr
       row_info, png_bytep data)

See pngtest.c for a working example. Your function will be called after all of the other transformations have been processed.

Alrighty, so now I can load things ~20 hours later. Talk about a huge step backwards. Doing the pre-multiplication at runtime is a trade off of convenience over speed. When it comes time to release the game I might consider doing this prior to compiling so the assets are "cooked". But doing it at runtime adds about 1-1.5 seconds from my unscientific tests.

What's amusing was once this was working it took 15 minutes to get multi-threading working. Now I decided to be adventurous and load the audio and graphics on separate threads from the main thread, for a total of 3. Speed tests show it's on average no faster or slower than having both asset types be loaded in order on a single thread *Shrug*. Oh well, if the iPhone ever gets another core then I'll be set ;) . Ultimately, I now have the freedom to toss asset loading on a background thread while the game continues playing with minimal hit to the frames per second.

This  kind of thing is probably a bit advanced and overkill to do for such a simple and small game as Tilt To Live. If I have time (unlikely) over the next several weeks I'll try to put together a complete code listing for those looking to have more control of their asset loading with libpng on the iPhone.

Sep 13

Tilt To LiveWhen it comes to gui coding I don't typically find it the most glamorous things to do. Particularly when dealing with games. You are left with writing a lot of boiler plate GUI code that is tightly integrated with your rendering pipeline. And even when I do find gui libraries for games they tend to try to emulate the OS in look and feel. When you want something lightweight and very interactive/animated you typically have to write it and integrate it yourself. If someone knows of a library for games that allows for versatile GUI development please leave a link in the comments :) . When left unchecked, GUI code can end up being:

  1. messy and hard to maintain
  2. difficult to change
  3. rigid (basically just "functional" with very minimal style)

The GUI is one of the few remaining "big" items left on Tilt To Live's to-do list. When imagining the GUI I had all sorts of crazy ideas for animations, transitions, interactions. Despite it being a relatively minor part of the game's experience I still felt it important to really make it as polished as possible. Why? The GUI is the first thing a new user sees when launching a game (unless you're Jonathon Blow's Braid). It's important to grab the user's attention as soon as you can to make sure they give your game a fair shake.

Attention! Over Here! Look at Me!

In the mobile market you are fighting for pretty small attention spans. One of the design goals for Tilt To Live is to make it quick to play. Currently it takes one tap to start playing the game. I'm still working on reducing the initial load time. I even contemplated removing the requirement of 'one tap' to play, and to just drop the player into the game. When someone selects 'Tilt To Live' on the iPhone there's a large chance they are simply wanting to play a quick game of Tilt To Live. In fact, other games like Spider: The Secret of Bryce Manor do just that and it's actually quiet welcome. My only reservation is Tilt To Live may expand in the future to multiple game types so the 'play from launch' approach isn't ideal if the desired game mode isn't known with great certainty.

Either way, trying to get that good first impression on the user is very important. In addition to that, reducing the friction required to play your mobile game could possibly raise the game up beyond "I have nothing better to do" status and overtake other forms of gaming.

Ease-e-does-it

So back to GUI animations. Throughout the game I've used a hacked-together curve editing tool I wrote in Blitzmax to produce animations for items spawning, enemies popping up, etc. It works "well enough", but the barrier to creating a new 'curve' was still high and it added to the total filesize of the game and was another asset to add to the app. So I revisited easing functions. I use some throughout the GUI system, but they aren't very interesting. I started looking into alternatives and ended up with a rather nice solution.

Below is a work in progess video of the new easing functions in action on Tilt To Live's menu UI:

A couple of years ago, Shawn Hargreaves had a really nice article explaining curve functions and how to use them in the context of GUI transitions using the XNA framework. It was a really nice primer to getting your hands wet with polynomial functions. Fast forward to today, I still have the basic "exponential" and "sigmoid" functions, but they were pretty boring in the scheme of things. Tilt To Live's aesthetic is kind of cartoony so those functions weren't very lively.

Having worked with Flash many years ago I knew it's easing animation capabilities were very sophisticated, but I wanted to have some subset of that flexibility in my code as well, so off to Google I went. The best resource I found was Timothée Groleau's Easing Function Generator. Not only did it have a nice set of preset easing functions, it generated the code to produce those exact curves! Wow, this was a huge time saver! For anyone that wishes to use these functions as well, the variables used in the function probably require a quick explanation:

  • t - the floating point time value from 0 to 1 (inclusive). If you want the position on the curve half way through the transition then you pass '0.5' through t.
  • b - the starting position. In a one dimensional setting this is the value you would get if t = 0.
  • c - the 'change' in position. So if you want to transition from 34 to 56 then c = (56-34) = 13. (more on this later)
  • d - duration of the transition. If you want the transition to last, for example, 2 seconds then d= 2.

With that actionscript function generator it's pretty trivial to implement it in any other language (as in my case, C). Although my GUI transitions aren't based off of changes in position, but off of start and end positions. So my typical transition function signature looks like Transition(startPosition, endPosition, delta). Below is an example implementation in C:

float EaseOutElastic(float startVal, float endVal, float blend)
{
    float diff = endVal - startVal;
    float tsquared = blend * blend;
    float tcubed = tsquared * blend;
    return startVal + diff*(33*tcubed*tsquared - 106*tsquared*tsquared + 126*tcubed - 67*tsquared + 15*blend);
}

The result:

  • Much more interesting GUI transitions
  • No extra assets as curves are procedural
  • Can re-use curves in different types of animations (position, scaling, rotation, etc)

When I'm doing a polish phase on the GUI of a game or animations, sounds, etc I'm often reminded of the tidbit from Kyle Gabler's  Gamasutra article about prototyping: Make it Juicy. Using easing functions to add a bit of bounciness, juiciness, and overall personality to a game can really help make the game feel more alive...feel more...juicy.

Tilt To Live

Jul 26

I've had this issue in past 2D games all the time. The ability to interpolate 2D rotations for basic animations for anyone that doesn't understand quaternions becomes a rather laborious task. I've done messy if-then statements to try to catch all the cases where rotation hits the '360 to 0' boundary and it's always ugly and seemed like there had to be a more mathematically correct way to interpolate a circular value.

Well for anyone that sticks with it long enough, they undoubtedly run into quaternions. What are quaternions? I'm not about to explain it in detail, but I like to think of them as a way to express a 3D rotations and not having that annoying gimbal lock problem.

So how do quaternions help our 2D rotation needs? Well if you're in a situation where you need to interpolate between the shortest path between 2 angles then Quaternions can help. You have a few choices to solve this problem:

  1. Use if-then clauses to try to catch all the cases when you hit a rotational boundary if you are rotating from, for example, 300 degrees to 2 degrees. You would want to rotate all the way through 360 instead of going backwards.
  2. Use quaternions, which are more applicable to 3D rotations, to interpolate between to angles using spherical interpolation (slerp for short).
  3. Use spinors, which are a very similar concept to quaternions, except they are more catered to 2D rotations.

You'll eventually see that using quaternions is somewhat overdoing it as 2 axises are never used in any calculation in 2D. If you eliminate these 2 axises from the quaternion you end up with a spinor! Thanks to Frank, of Chaotic Box, who helped lead me down that direction for my own iPhone game.

To help test out the concept and make sure it's working I ended up writing a Blitzmax quickie app using what little I know about quaternions. I'm certainly a bit iffy when it comes to dealing with complex numbers, as I don't fully grok them as I do normal vector math, but I make do. So if anyone reads this post and sees some sort of logical error in my code, by all means leave a comment and I'll improve the listing.

So to get started I needed a way to represent a 2D rotation as a spinor. So I created a spinor type with some basic math functions (all of them analogous to how quaternions work):

Type Spinor
    Field real:Float
    Field complex:Float
   
    Function CreateWithAngle:Spinor(angle:Float)
        Local s:Spinor = New Spinor
        s.real = Cos(angle)
        s.complex = Sin(angle)
        Return s
    End Function
   
    Function Create:Spinor(realPart:Float, complexpart:Float)
        Local s:Spinor = New Spinor
        s.complex = complexPart
        s.real = realPart
        Return s
    End Function
   
    Method GetScale:Spinor(t:Float)
        Return Spinor.Create(real * t, complex * t)
    End Method
   
    Method GetInvert:Spinor()
        Local s:Spinor = Spinor.Create(real, -complex)
        Return s.GetScale(s.GetLengthSquared())
    End Method
   
    Method GetAdd:Spinor(other:Spinor)
        Return Spinor.Create(real + other.real, complex + other.complex)
    End Method
   
    Method GetLength:Float()
        Return Sqr(real * real + complex * complex)
    End Method
   
    Method GetLengthSquared:Float()
        Return (real * real + complex * complex)
    End Method
   
    Method GetMultiply:Spinor(other:Spinor)
        Return Spinor.Create(real * other.real - complex * other.complex, real * other.complex + complex * other.real)
    End Method
   
    Method GetNormalized:Spinor()
        Local length:Float = GetLength()
        Return Spinor.Create(real / length, complex / length)
    End Method
   
    Method GetAngle:Float()
        Return ATan2(complex, real) * 2
    End Method
   
    Function Lerp:Spinor(startVal:Spinor, endVal:Spinor, t:Float)
        Return startVal.GetScale(1 - t).GetAdd(endVal.GetScale(t)).GetNormalized()
    End Function
   
    Function Slerp:Spinor(from:Spinor, dest:Spinor, t:Float)
        Local tr:Float
        Local tc:Float
        Local omega:Float, cosom:Float, sinom:Float, scale0:Float, scale1:Float
       
        'calc cosine
        cosom = from.real * dest.real + from.complex * dest.complex
       
        'adjust signs
        If (cosom <0) Then
            cosom = -cosom
            tc = -dest.complex
            tr = -dest.real
        Else
            tc = dest.complex
            tr = dest.real
        End If
       
        ' coefficients
        If (1 - cosom)> 0.001 Then 'threshold, use linear interp if too close
            omega = ACos(cosom)
            sinom = Sin(omega)
            scale0 = Sin((1 - t) * omega) / sinom
            scale1 = Sin(t * omega) / sinom
        Else
            scale0 = 1 - t
            scale1 = t
        End If
       
        ' calc final
        Local res:Spinor = Spinor.Create(0, 0)
        res.complex = scale0 * from.complex + scale1 * tc
        res.real = scale0 * from.real + scale1 * tr
        Return res
    End Function
   
End Type

One caveat in the above code is that any angle from 0-360 is mapped to 0-180 in the Spinor. What does this mean? it means if I want to represent the angle 270 I need to divide it by 2, creating the spinor with the angle 135. Now when I call GetAngle() it will return 270. This allows us to smoothly rotate the whole 360 degrees correctly. This is explained here in more detail.

So now I want to spherically interpolate (slerp) between 2 2D angles. Well, if I call Slerp() on the spinor type it'll do just that, giving me another spinor that sits in between the 2 angles. I've read a couple of articles in the past on slerp and how to implement it, but in order to get things done I just used a publically listed code snippet and ported it to blitzmax. That whole article is worth the read and recommended, even if you're just using 2D.

Now that the concept of spinors is encapsulated in the Spinor type I wanted just a basic convenience function that I can feed 2 angles and get another angle out:

Function Slerp2D:Float(fromAngle:Float, toAngle:Float, t:Float)
    Local from:Spinor = Spinor.Create(Cos(fromangle / 2), Sin(fromangle / 2))
    Local toSpinor:Spinor = Spinor.Create(Cos(toAngle / 2), Sin(toAngle / 2))
    Return Spinor.Slerp(from, toSpinor, t).GetAngle()
End Function

the 't' variable is the time variable from 0 to 1 that determines how far to interpolate between the 2 angles. Giving it a value of 1 means you will get toAngle out, and a value of 0 is equal to fromAngle and anything in-between is just that...in-between.

Now I can call Slerp2D(300, 10, 0.5) And get the correct angle without worrying if I'm interpolating in the right direction :) .

Also, I want to point out that the above code was just used in a quick proof of concept app. It's somewhat sloppy and to be honest, I'm not sure I named the portions of the spinor correctly (real,complex), but it works. For clarity and to save time I didn't inline the Slerp2D functions or the Spinor type methods, so it generates a lot of garbage. You would need to optimize this to use it in a tight loop somewhere. Any suggestions are welcomed.

As for references, I did some searching and got most of the 'missing link' materials I needed to bridge the gap between quaternions and spinors from:

...Adios...

Jul 13

wxMaxThe title of this article says it all. I'm currently working on the next 'iteration' of Gunstyle with a fellow developer and over the past weekend we were in need of a GUI library to help develop our map editor. In the past maxGUI 'worked' more or less....emphasis on the less. The design of maxGUI left me with a bad taste in my mouth. It's a procedural library that is cobbled together with some weak OO on top that didn't provide much flexibility (not to mention it wasn't exactly 'native' UI on different platforms). Either way, it was a good library for small projects and prototypes.

Now we're working on something of a little bit bigger in scale, and needing a more heavy duty gui library to go with it. So we chose Brucey's wxMax GUI Module for Blitzmax. Just picking it up over the weekend has been a joy to work with. It's object-oriented from the get go so it's a bit easier to integrate into already established OO code.  It also boasts being cross-platform (Win/Linux/Mac), but I've yet to test or need this functionality yet. It's also wrapping a rather solid and established library, wxWidgets.

One of the main requirements was for us to develop a GUI around a game framework that would allow us to switch in and out of an editor on the fly. To test this type of functionality I whipped up a basic test file that switches from the standard Blitzmax graphics window to a wxMax GUI with a GLCanvas and back again several times. Below is the actual source for doing that type of switching, in case anyone wants to do something similar:

SuperStrict

Framework wx.wxApp
Import brl.glmax2d
Import brl.standardio
Import wx.wxFrame
Import wx.wxPanel
Import wx.wxGLCanvas
Import wx.wxglmax2d
Import brl.max2d
Import wx.wxTimer
Import brl.pngloader

Global app:TestApp = New TestApp
Global imgTest:TImage
Global imgPath:String = "myTestImage.png"

SetGraphicsDriver(brl.GLMax2D.GLMax2DDriver()) ' needed in order for the normal gfx driver to run
SetGraphics(Graphics(800, 600)) ' set context back to main window

' test switching between max2D and wxMax
' while loading an image that is shared between the two modes
If FileType(imgPath) = 0 Then
    Notify "invalid path for image. file does not exist"
    EndGraphics()
    End
End If

imgTest = LoadImage(imgPath)
SetBlend(ALPHABLEND)

RunBmaxGraphics()
app.Run()
RunBmaxGraphics()
app.Run()

Function RunBmaxGraphics()
    SetGraphicsDriver(brl.GLMax2D.GLMax2DDriver()) ' needed in order for the normal gfx driver to run
    SetGraphics(Graphics(800, 600)) ' set context back to main window
    While Not(KeyHit(KEY_ESCAPE))
        Cls
        If KeyHit(KEY_D) Then
            Exit
        End If
        DrawImage(imgTest, 100, 100)
        Flip
    WEnd
    EndGraphics()
End Function

Type TestApp Extends wxApp
    Field frame:wxFrame
    Field panel:wxPanel
    Field canvas:TMax2DCanvas
   
    Method OnInit:Int()
        frame = New wxFrame.Create(,, "Test", 0, 0, 1024, 768)
        frame.Center()
       
        panel = New wxPanel.Create(frame, wxID_ANY, 160, 0, 1024, 768)
        canvas = TMax2DCanvas(New TMax2DCanvas.Create(panel, wxID_ANY, GRAPHICS_BACKBUFFER | GRAPHICS_DEPTHBUFFER, 0, 0, 1024, 768))
       
        frame.Show()
       
        Return True
    End Method
   
End Type

Type GLFrame Extends wxFrame
    Field canvas:TMax2DCanvas
   
    Method OnInit()
        canvas = TMax2DCanvas(New TMax2DCanvas.Create(Self, -1, GRAPHICS_BACKBUFFER | GRAPHICS_DEPTHBUFFER))
        ConnectAny(wxEVT_CLOSE, OnClose)
       
    End Method
   
    Function OnClose(event:wxEvent)
        GLFrame(event.parent).canvas.timer.Stop()
        event.Skip()
       
    End Function
End Type

Type TMax2DCanvas Extends wxGLCanvas
    Field timer:wxTimer
   
    Method OnInit()
        SetBackgroundStyle(wxBG_STYLE_CUSTOM)
        timer = New wxTimer.Create(Self)
        wx.wxglmax2d.GLMax2DDriver().SetBlend(ALPHABLEND)
        timer.Start(100)
        ConnectAny(wxEVT_TIMER, OnTick)
    End Method
   
    Method OnPaint(event:wxPaintEvent)
        Render()
    End Method
   
    Method Render()
        SetGraphics CanvasGraphics2D(Self)
       
        Cls
            DrawImage(imgTest, 100, 100)
        Flip
       
    End Method
    Function OnTick(event:wxEvent)
        wxWindow(event.parent).Refresh()
    End Function
End Type

The main benefit of the above setup is seen in the switching between 'RunBmaxGraphics()' and 'app.run()'. I can switch from my normal game logic, to an editor logic, but still use the same rendering routines! Now I'm not 100% positive that resources such as TImage will persist after a graphics change (in my case they do, but I don't think that's universal) so you may be required to reload textures.

Jul 19

I've been working on a few projects that require blitzmax and a web-server (php/mysql) to communicate. Along came the need to hash files. This code archive entry was very useful :) . I decided to wrap it into  an object and add a new method to it. Even though it's a series of functions I still tend to make types with functions in them to help me organize them (it's like a cheap way of  namespacing). I needed the ability to hash a file using Sha-1 hashing. Seeing that php has a function called sha1_file($pathToFile) I figured I'd implement the equivalent on blitzmax. With this THasher type, I've added a SHA1_File(path) function also.

Why hash files?

There are many reasons to use hashes, but recently I used it to check for file changes. If you've ever wanted to find out if some arbitrary file has 'changed' since the last time you've opened it this can prove to be useful. If you save the hash beforehand then recompute the hash now and if they are different, then something's changed! Some applications of this are:

  • Write a file-updater to quickly find out which new files need to be downloaded. Just comparing hashes would be sufficient in most cases, instead of having to go individually byte-by-byte or traversing the file structure to find a difference.
  • help stop cheating. If you don't want users changing texture files to gain an advantage or don't want them 'mucking' with any other data file. If you check the current hash against a previously approved one and it doesn't check out then the file has more than likely been tampered with.

How Do I Use THasher?

If you want to hash a string using SHA-1 then:

Local hashedFoo:String = THasher.sha1("Foo")

Want to hash a whole file? Then:

Local filehash:String = THasher.SHA1_File("someFile.txt")

Keep in mind you can hash any file, not just text files like in the above example.

Anyway, read on for the whole file. With the exception of SHA1_File() I did not write these hashing functions for Blitzmax. They were simply taken from Yan's very helpful code archives post :) .
Read the rest of this entry »

Jun 28

Yes, yes I know the documentation is lacking at the moment for Farseer.BMX. I'm still working on it on a few fronts. First, I'm trying to do api documentation (bbdoc mostly). Secondly, I'm trying to write tutorials/guides that help show how to use some of farseer's features. I had hoped that releasing the demo application source code would've covered this aspect as the demos cover pretty much each feature, but I guess since the demo source is a bit more complex in design (handling a lot of graphics related mumbo jumbo), the actual 'needed content' wasn't particularly easy to find. So I'm going to try to write up very simplified versions of some of farseer's basic features (minimal graphics, minimal code).

I've uploaded the first of these tutorials to the articles section. It covers creating concave polygons in farseer :) . More to come soon...

Oct 4

Hey again! Two updates in less than a month! Wonder if I can keep this up :) . Anyway, just finished another article for Ziggy's site. This one shows you how to do those cool, seamless ribbon trails you sometimes see trailing behind melee weapons (swords, sticks, etc) and other objects. I've also included a sample with full source code that works on both the PC and Xbox 360. So go check it out!

January 28th, 2010 Update:

Ziggyware.com seems to be down indefinitely. As a temporary solution until I get back around to hosting this tutorial myself you can find the Google cached version of it here. If you wish to look at the sample code and illustrations in full you can download the XNA sample from my site here. Note, that the XNA sample was built on a very old version of XNA so my guess is it will not compile out of the box, but the general logic for the ribbon trails should be the same regardless of XNA version.

« Previous Entries