Parallax
Wednesday, 21st September 2005
Here's a nice little graphical enhancement...
...and here's the video for it! (I disabled collision detection for that video, so no silly comments, alright? )
Now, there's a little problem with this system - well, a number of problems. The worst is this:
Oh dear. I'm now writing to the sprite table when the display is active, which (understandably) it does not like. On the Game Gear hardware, you can see this in the vertical bar of flickering stars towards the left hand side of the screen. I am not sure how I shall fix this, but I'm hoping that altering the sprite drawing routines to write to a RAM shadow then dumping the entire RAM shadow in the frame is faster than my current method which is to manually draw the sprites in a number of different routines.
The next problem is the detection of when to show/hide stars. Currently, it only does this based on tile index - so only tiles marked as being 0 are "transparent" and enable the stars (they're being drawn as sprites). This leads to them vanishing a good distance away from the edge of land.
Finally, there's the even weirder issue where the stars move at almost the same rate as the landscape but are shifted in alternate frames. If a star is on the boundary between tiles, one frame it is over tile 0 so is shown, next it is over tile X so is hidden, and this oscillates. Hopefully by fixing up a pixel-perfect "show/hide" function, I can fix up this problem as showing and hiding would be about 4 pixels away from the boundaries.
I have also started work on "Segue", a simple music system for Game Gear games. I'll support instruments (they just modulate the amplitude of notes when they are played) that can be bound to the different channels. The song itself will be a list of instructions - "play this tone X on channel Y" (where X is the period of the tone), "delay for X frames", "assign instrument X to channel Y" - that sort of thing.
EDIT: This journal is now low-res non-horizontal-scrolling friendly.
Title screen goodness
Tuesday, 20th September 2005
I spent some time yesterday doing some 'fun' work on Fire Track - that's not to say that the other stuff is not fun, but I find the graphics work less head-hurting than the programming work.
Hence the spiffy title screen above! It was fun trying to fit that into 16 colours and able to be broken down into 256 tiles, but with a bit of shuffling around it's a mere 254 tiles and the GIF exporter in Photoshop has done a great job dropping down the colours.
The display on the Game Gear blends adjacent pixels together very nicely - on hardware, the title looks pretty flash. (It's difficult to get a good picture of Game Gear - you try taking a photo of a TV set and you'll see what I mean! As clear and sharp as your TV looks out the photo will usually look washed out or fuzzy). See, here is a screenshot of the game running in an emulator:
Not quite so smooth, is it? (Not to mention it's a different aspect ratio!) Still, it's a little more colourful than the original:
(Mine is a cleaned up version of the box cover with the text redrawn.)
As you can imagine, a title screen such as the above takes up a lot of space. 254 8x8 tiles means 254*8=2032 rows of pixels, each row is made up of 4 bytes (four colour bitplanes) giving us 8128 bytes or 7.94KB. Seeing as the ROM I'm using is only 32KB, things are going to get tight - not to mention that even without the title picture I get the following:
Building... (Sega Game Gear)
Setting base location 'D:\My Documents\Game Gear\Dev\Fire Track\'
Running batch job '"D:\My Documents\Visual Studio Projects\Latenite\bin\compile\Sega Game Gear.bat" ftrack.z80 ftrack "D:\My Documents\Visual Studio Projects\Latenite\bin\compile"'
Build process complete!
Bank 00 has 03729 bytes (11.38%) free.
Free space at $0029-$0065.
Free space at $7191-$7fdf.
Free space at $7ff8-$7ff9.
Free space at $7ffc-$7ffe.
3729 unused bytes of total 32768.
Erk! That's not much room at all, and I still need more space for the music. I need to jump to using 2 ROM banks - that'd give me a healthier 64KB to work in, and 48KB of that is set up to be directly accessible without any paging as default. Or so it goes in theory, but WLA-DX's syntax for defining memory locations/ROM bank setup has me totally stumped. Looks like I'll need to pester the SMS Power! chaps again.
Anyway, what is going on in Fire Track itself? I did do some coding last night as well as slacking off with some image editing, and these features are now implemented:
- Final attack pattern is in place - small ships appear then attack you by setting a direction when at a certain Y coordinate and just heading into you in a straight line. This is slightly buggy, some ships appear to have drunk pilots and veer off in completely the wrong location. Not sure why that is, but most seem to hit the target (read as: ME).
- Bugfixes/tweaks on a couple of the enemies. The ones that randomly drift towards you (the dumbell-shaped blockers and another formation) now only start drifting in X once they are on-screen, else they all appear on screen in a vertical column and it looks really silly.
- If you complete a level it doesn't just automatically jump to the next level, you lose control of your ship and it flies up and off the top of the screen. I shall probably also stop the end face from looping so that when you finish and have flown the background doesn't just disappear, it's as if you accelerate and it moves downwards off screen to the blackness of empty space.
- The sound! How could I forget that? It now works on volume rather than the hard-coded priority, and thus sounds very much better. (The loudest sound is the one that is played, as I can only set one period/amplitude as a sound effect at any one time).
- Fixed a bug on the title screen where sound channel #2 was not being set (channel #1 was set twice by accident).
Rather than just see a list of what's done, have a list of what is left to do as I am feeling generous.
- Get ROM paging correctly up and running so I can dump more data onto a second 32KB ROM chip.
- Add sounds for me exploding (uses the noise channel so isn't just a case of adding a new sound effect).
- Adding the "mine" enemy. This is a small spiky blobby thingummy (technical term) that drifts in a straight line until it hits the edge of the screen, at which point it reappears at a random location and drifts in a new, random direction. If it hits you, of course, you lose. You can see it in this screenshot of the BBC Micro version:
It's the thing above the two lower explosions. - Music. I need music. I also need a music system, so defining instruments (attack fade, play effect (vibrato?), release fade) and notes, as well as all sorts of other nasty music things.
- Scoring.
- Parallax stars? (Maybe, hopefully!)
- Lots of other things I must have forgotten.
Until next time! Incidentally, my journal is now listed as the News section on my website.
Minor progress
Monday, 19th September 2005
I've spent the last few days up North for a cousin's wedding, so not much has gone on regarding Fire Track.
A few things have been added, though!
The sound is now "mixed" (for want of a better word) much better. It's still not perfect, and I'd like to see how it sounds when favouring amplitude over priority. That'll be my next tweak!
I have added the cheat screen (also useful for testing sound!) which will appear if you hold down certain keys as the game starts. There are a new set of enemies...
They just drift towards you - and you can't shoot them (they block bullets, though).
Also, the "final platform" for a few levels has been implemented. This is for the levels where you don't finish up with a face to shoot the eyes on, just a platform:
You need to drift past it to beat the level.
When you have finished the last level, the epilogue now plays too.
Through extensive testing (playing?) the original, I have worked out the purpose of the question mark blocks:
Shoot out a certain number and you get a funny noise and are awarded an extra life!
Motion blur filters are so cool.
Bloop-bleep, it's the 80s!
Wednesday, 14th September 2005
Sound! I am still musicless, but never fear, for sound effects will fill the gap. Screenshots are pretty poor for sound , so here's the MP3.
Simple as it sounds, that takes a mere 163 lines of ASM code to work, and even then it's a fairly crap effect!
Each sound is stored as a series of "fades". Each fade contains the following data:
- Starting period of the wave.
- Starting amplitude of the wave.
- Length of the wave in frames.
- Amount to add to volume each frame.
- Amount to add to period each frame.
Using these simple fades and joining them together you can very easily produce a fairly complex sound. There are currently only two sound effects, and both are a single "fade" long - one for firing, the other for when a tile on the ground has been hit.
Each sound effect is issued with a priority, in a little table, so that the most important sound is played over the top of the least important one. In this case, if you play a more important one over a less important one (higher pitched bleeps) it cuts the less important one out for good - which is rubbish, really. What I will have to do is to extend the system to act over a list of current sound effects that can be added to at any time. That way, which tone is sent to the PSG (the sound chip) can be decided at that point, based on priority (and maybe volume, I'll have to see how it sounds).
I fixed Latenite's ugly XP icon issue, and here are the results:
The one on the left is the Debug build. The one on the right is the Release build. I have no idea why it is doing that on the right, or how I can fix it.
It gets better.
This is what it looks like in Release mode when running in the VS.NET IDE.
I shall now resist the urge to make an massive journal post this time. Honest. Just watch me.
Old projects and ugly XP icons
Tuesday, 13th September 2005
This is going to be a very boring update, I'm afraid. I wasn't feeling too good so haven't got much done.
First of all, Latenite updates. With the project thingummy, I thought it would be nicest if it displayed the proper Windows icons for file types rather than the ones I had hardcoded in. Here is a function I wrote to take a filename and return an icon (in the form of a .NET Bitmap) based on it and whether you requested a large or small icon.
It works by going into the registry and looking up the ".ext" key. Some keys like that contain a DefaultIcon property and a filename/index number of the icon to use, some just point at another subkey with that DefaultIcon property.
After I found that, I use the rather nasty ExtractIconEx to retrieve icon pointers from the DLL/ICO/EXE, select the one I want, copy that to an Icon (and then to a Bitmap) then clean up.
Here's a screenshot of it in action:
You'll notice the crud around the final icon. It seems that any XP-style icons with an alpha channel get mucked up by the ExtractIconEx ripping out the icon's data. If possible, I would force it to retrieve the 256 colour version (at 16x16, that's still a colour for every pixel!) but don't possess enough Win32 wizardry to achieve this. If anyone could help me, I'd really appreciate this.
Here is the module, in case someone finds it useful. Sorry if it appears a bit hacky, but it seems to work fine for me.
Module mdlIcons Private Declare Function ExtractIconEx Lib "shell32.dll" Alias "ExtractIconExA" (ByVal lpszFile As String, ByVal nIconIndex As Int32, ByRef phiconLarge As Int32, ByRef phiconSmall As Int32, ByVal nIcons As Int32) As Int32 Private Declare Function DestroyIcon Lib "user32.dll" (ByVal hIcon As Int32) As Int32 Function getIconFromFilename(ByVal filename As String, ByVal largeIcon As Boolean) As Bitmap ' Grab the extension: Dim getExtension() As String = filename.Split(".") ' Look up the file type: Dim searchKey As Microsoft.Win32.RegistryKey = Microsoft.Win32.Registry.ClassesRoot.OpenSubKey("." & getExtension(UBound(getExtension)), False) ' Was anything found? If searchKey Is Nothing Then Return Nothing ' Go through until you hit the end: Dim getDefaultIcon As Microsoft.Win32.RegistryKey Do getDefaultIcon = searchKey.OpenSubKey("DefaultIcon", False) If Not (getDefaultIcon Is Nothing) Then Exit Do searchKey = Microsoft.Win32.Registry.ClassesRoot.OpenSubKey(searchKey.GetValue("")) If searchKey Is Nothing Then Return Nothing Loop ' Get the details: Dim fileDescription As String = searchKey.GetValue("") Dim iconPath As String = getDefaultIcon.GetValue("") ' Close the registry keys: getDefaultIcon.Close() searchKey.Close() ' Now we have that data, we need to convert a "xxxx,0" path into a "xxxx" and a "0" Dim getPlainIconDetails() As String = iconPath.Replace("""", "").Split(",") Dim iconIndex As Integer = 0 Dim plainIconName As String = getPlainIconDetails(0) For i As Integer = 1 To UBound(getPlainIconDetails) - 1 plainIconName &= "," & getPlainIconDetails(i) Next If iconPath.Replace("""", "").ToUpper.EndsWith(".ICO") Then If UBound(getPlainIconDetails) <> 0 Then plainIconName &= getPlainIconDetails(UBound(getPlainIconDetails)) Else iconIndex = Val(getPlainIconDetails(UBound(getPlainIconDetails))) End If ' Now we have all that info, let's grab the icon: Dim iconLarge As Int32 Dim iconSmall As Int32 If (ExtractIconEx(plainIconName, iconIndex, iconLarge, iconSmall, 1) > 0) Then Dim iconPtr As IntPtr If largeIcon = True Then iconPtr = New IntPtr(iconLarge) Else iconPtr = New IntPtr(iconSmall) End If Dim iconRes As Icon = Icon.FromHandle(iconPtr) Dim returnBitmap As Bitmap = iconRes.ToBitmap iconRes.Dispose() If iconLarge <> 0 Then DestroyIcon(iconLarge) If iconSmall <> 0 Then DestroyIcon(iconSmall) Return returnBitmap Else Return Nothing End If End Function End Module
The C# source code I saw that used the ExtractIconEx call used ExtractIconExA instead of ExtractIconEx. I thought that this meant that A = alpha, so hopefully swapped the A in but VB.NET complained with a squiggly blue line. If only Win32 was as easy as that.
As far as Fire Track itself goes, there's nothing to really show for it - if you die, the game continues for a short way (with you invisible and uncontrollable) before decrementing your lives by one and sticking you back at the zone info screen. When you press START you get stuck back in the game at your old position in the level and you start again.
As dull as that sounds, there is a lot of work going on to achieve that - namely swapping palettes and tilesets to RAM and ROM and back again as required.
Here's a silly pair of screenshots. I played around with some of the more interesting VDP mode bits last night, and this is the "sprite zoom" one. By changing a couple of lines of the sprite routines with .ifdef blocks I can now set a switch in my config.inc file before compiling and all sprites are double size. Not sure why one would want to do that, but it's nice to know that the option is there.
Finally, here are some old shots I could have shown in here a while back - I thought I'd teach myself some OpenGL, what with SDL just sitting around doing nothing much for me.
As ever, click for bigger. Yep, it's the generic starter app of a terrain engine. (The Perlin noise for heights was new to me, I'd only used a precalculated one from Photoshop before). I couldn't quite work out how to do ambient lighting, hence the black sides to the hills. Not to mention that the tiling of the ground texture is blatently visible. The water moves up and down slowly to "wave", and the entire water texture scrolls past slowly.
I wish those shots were "real" - in face, there are two major flaws in my engine. First up is the very poor culling for terrain chunks outside the view - it's purely 2D, so looking down at the ground looks like this:
...which isn't quite right. I also had to tweak the LOD calculation for the above shots - here it is with the LOD tuned the other way;
Lots of nasty holes. I couldn't quite work out the skirting myself, so consigned the project to the bins once again (as I do with about 49 out of 50 projects). It runs at ~150FPS in debug mode, which is something to be pleased with I suppose.
I will release Fire Track, though. I hope. If someone could help with the music.
Lost projects... now there's an idea for a Lounge thread. I think I'll spare that by posting a few in my journal instead. Some of these started out as Really Good Ideas at 3AM when I couldn't sleep, but never seemed to get anywhere:
Album Art Viewer - for those covers that Windows Media Player displays, which Windows Explorer hides.
WAD Reader - DOOM WADfile reader/viewer. Has support for all graphics ("pictures" and raw flats), DOS end screens, PC speaker sound effects. Can view pictures in special viewer where you can rotate the sprites or watch their animations (it has to guess a lot, so that's not perfect). Can export all lumps as raw data or convert to PNG (graphics) or RTF (DOS end screens). Can also play the PC speaker (not wave) sound effects if your PC has a PC speaker on the motherboard. VB.NET, but quite fast. Full screen pictures take about 2 seconds to render as properly, though. However, once rendered all are cached internally for instant retrieval.
Lemming Syndrome - (click to download 194KB file, requires .NET runtimes) - wow! I forgot about this one. It was meant to be for the 1W1B contest - a challenge to work on a game for one week, with the requirement that you only used one button to control it. So, er, try and bounce me over the river in your lifeboat. There is no "game", as such, (unfinished), so it'll go on for ever.
MySQL Manager - I was feeling cheap and my trial to MySQL-Front was about to expire, so I started work on a MySQL clienty thing. It's massively faster at downloading/displaying data, but that's about all it actually does after I decided it was too much hassle to try and recreate a full tool, so I forked out for a license of MySQL-Front instead.
Anyway, this is scaring me, I just found a garishly coloured TETRIS.EXE that I swear I never wrote, but the code style is mine and it has my name on it, so I guess I actually did... I have been feeling a bit ropey for the last year-and-a-bit, so I guess it slipped my radar. Or I wrote it in my sleep. Or something. Just be glad I don't have an internet connection myself and have to rely on one at work, so you're safe from me posting in my sleep!