Not long ago I released a new and unconventional Imphenzia track named “Time Travel” – check out the video of the track:
This track rocketed to the #1 chart at soundclick.com and it also received great feedback on facebook. It is, however, a very different style compared to most other Imphenzia tracks released in the past.
The track, the feedback, and some recent expansion pack purchases for Nexus got me inspired to create more music.
I’ve decided to publish the scripts that I use to watermark and encode music audio files into MP3 and OGG files with meta tags. I hope this will help someone out there who needs to frequently watermark and/or encode audio files into WAV, MP3 and OGG files at different bit rates.
Maybe the simplest way to start off is to demonstrate how the scripts work with this video:
This is a VBScript that will loop through a directory full of WAV audio files and mix them with watermark audio files. The script uses the free application Sound eXchange (SoX) to achieve this.
The script is also designed to take the duration of the audio file to be watermarked and apply a watermark with a suitable frequency.
Shorter audio files will be watermarked frequently and longer audio files will have less frequent watermarks to make it less annoying.
You can customize the watermark files to contain anything you want. If you want to change the name or format of them you’ll have to modify the script appropriately.
You can download the entire script package and demo files at the top of this post, but if you wish to have a look at the script itself, here it is:
' #############################################################################
' IMPHENZIA SOUNDTRACK WATERMARKING SCRIPT http://soundtrack.imphenzia.com
' #############################################################################
' ## DESCRIPTION ##############################################################
' This VB script uses Sound eXchange (SoX) (http://sox.sourceforge.net) to
' create watermarked WAV files.
' Based on the duration of the WAV files you wish to watermark, the script
' will select a suitable frequency of the watermark. By default the following
' logic is applied:
' 0-5 second music file: watermark occurs every 2.5 seconds
' 5-15 second music file: watermark occurs every 5 seconds
' 15-30 second music file: watermark occurs every 10 seconds
' 30-1200 second music file: watermark occurs every 30 seconds
' > 20 minutes files are not supported - change the script if necessary.
' ## INSTRUCTIONS #############################################################
' 1. Put your original WAV files that you wish to watermarked in the
' subfolder "input-wav-originals"
' 2. Execute this script and it will create watermarked WAV files in the
' subfolder "output-wav-watermarked"
' ## CHANGING THE WATERMARK ###################################################
' The default watermark says the word "preview" at different intervals
' depending on the duration of the WAV file. You can change the watermark
' audio files but I recommend that you keep the name and ogg format of the
' watermark files so you don't have to modify the script.
' To change the watermark, replace the audio in the watermark files. Use
' trial and error to find a suitable volume (I recommend peaks at around
' -8 dB) and frequency of the watermark.
' ## LICENSE ##################################################################
' You're free to use this script and the watermark OGG files free of charge.
' If this script saves you a lot of time, a donation to paypal@imphenzia.com
' is very welcome =)
' SoX is a free application available at the URL in the description. This
' script is very dependent on the exact output SoX generates to read
' information so a version of SoX is included in the archive.
' (You may not sell the script or any of the files)
Dim source_folder, destination_folder
Dim watermark_folder, watermark_file, log_filename
' ## CUSTOMIZABLE VARIABLES ###################################################
source_folder = "input-wav-originals"
destination_folder = "output-wav-watermarked"
watermark_folder = "input-watermarks"
log_filename = "watermark-log.txt" ' Filename for log file (overwritten)
use_volume = true ' Watermark volume based on original
' You shouldn't need to edit any of the below script
' -----------------------------------------------------------------------------
Dim wshshell, filesys, folder, files_collection, filecount, filename
Dim outputfile, logoutput, duration, volume, volumedecimal, n
' ## Create Filesystem Objects
Set wshshell = WScript.CreateObject("WScript.Shell")
Set filesys = CreateObject("Scripting.FileSystemObject")
Set folder = filesys.GetFolder(source_folder & "\.")
Set files_collection = folder.Files
Set logoutput = filesys.OpenTextFile(log_filename, 2, True)
' ## Ensure that the output directory exists, if not then create it
if not filesys.FolderExists( destination_folder ) then _
filesys.CreateFolder destination_folder
' ## Create header in log file
logoutput.WriteLine("Source File, Source Duration, Watermark, " & _
"Source Amplitude, Watermark Amplitude")
' ## Loop through all original wav files
filecount = files_collection.Count
for each file in files_collection
filename = file.name
if lcase(right(filename,4))=".wav" then
' ## If a watermarked version does not exists, create one
if not filesys.FileExists( destination_folder & "\" & filename ) then
' ## Select appropriate watermarked file based on duration of source file
duration = CLng(runCMD("sox --info -s """ & _
source_folder & "\" & filename & """"))
if duration <= 220500 then _
watermark_file = watermark_folder & "\preview-0-5sec.ogg"
if duration > 220500 and duration <= 661500 then _
watermark_file = watermark_folder & "\preview-5-15sec.ogg"
if duration > 661500 and duration <= 1323000 then _
watermark_file = watermark_folder & "\preview-15-30sec.ogg"
if duration > 1323000 then _
watermark_file = watermark_folder & "\preview-30-1200sec.ogg"
' ## If volume is used, set volume based on amplitude of source wav file
if use_volume then
volume = runCMDAlt("sox """ & source_folder & "\" & filename & _
""" -n stat")
volume = mid(volume,123,8)
volumedecimal = CDbl(replace(volume, ".", ","))
else
volumedecimal = 1.0
end if
' ## Mix source wav file and watermark file to create a watermarked file
wshshell.Run "sox -m -v 1 """ & source_folder & "\" & filename & _
""" -v " & replace((volumedecimal*volumedecimal),",",".") & _
" """ & watermark_file & """ """ & destination_folder & _
"\" & filename & """ trim 0s " & duration & "s",0,true
' ## Write the action to log file
logoutput.WriteLine(source_folder & "\" & filename & "," & _
watermark_file & "," & replace((CDbl(duration)/44100),",",".") & "," & _
volume & "," & replace((volumedecimal*volumedecimal),",","."))
n=n+1
end if
end if
next
if n=0 then
wscript.echo "No Files Created " & vbCrLf & _
"If watermarked files already exist they are not recreated"
else
wscript.echo "Done! " & vbCrLf & n & " Watermarked File(s) Created "
end if
' Supporting function to capture the output of command line
function runCMD(strRunCmd)
Set objExec = wshshell.Exec(strRunCmd)
strOut = ""
Do While Not objExec.StdOut.AtEndOfStream
strOut = strOut & objExec.StdOut.ReadLine()
Loop
runCMD = strOut
End Function
' Supporting function to capture error output (workaround)
Function runCMDAlt(strRunCmd)
Set objExec = wshshell.Exec(strRunCmd)
strOut = ""
strOut = objExec.StdErr.ReadAll()
runCMDAlt = strOut
End Function
Encode_files.vbs Explained
This is a script that will loop through two directories, one full of original WAV audio/music files and one full of watermarked WAV files (if you previously ran the Create_watermarks.vbs script). As the script loops through all the available WAV files it will encode them into MP3 and OGG files and also update the meta tags containing information such as title, artist, publisher, copyright info, comments, URL, etc.
The script is designed so that you can enter meta tags and customize bit rate that you wish to encode your files with. Once you’ve got this running you can quickly re-encode your files into any bitrate or modify the meta tags without having to do it all manually.
You can download the entire script package and demo files at the top of this post, but if you wish to have a look at the script itself, here it is:
' #############################################################################
' IMPHENZIA SOUNDTRACK ENCODING SCRIPT http://soundtrack.imphenzia.com
' #############################################################################
' Version: 1.0
' ## DESCRIPTION ##############################################################
' This scripts encodes the wav files in the folder "input-wav-originals" and
' "output-wav-watermarked" into MP3 and OGG files.
' The script also creates meta tags for the MP3 and OGG files based on the
' source file name and additional customizable in this script.
' You can set the bitrate of the MP3 and OGG files in this script as well.
' ## INSTRUCTIONS #############################################################
' 1. Ensure that the WAV files in the "input-wav-originals" folder and the
' "output-wav-watermarked" folders contain files named according to the
' following naming standard:
' Arist Name_Song Name_Music Genre_Year
' Example:
' Imphenzia Soundtrack_Fog of War (Main Part Loop)_Orchestral_2011.wav
' 2. Customize the encoding bitrates for the original and watermarked MP3
' and OGG files (see CUSTOMIZABLE ENCODING BITRATES)
' 3. Customize the MP3 and OGG meta tags (see CUSTOMIZABLE MP3 and OGG TAGS)
' 4. Double click on the script to begin the encoding process. The script
' will create MP3 and OGG files in the appropriately named output folders.
' ## LICENSE ##################################################################
' You're free to use this script and the watermark OGG files free of charge.
' If this script saves you a lot of time, a donation to paypal@imphenzia.com
' is very welcome =)
' This script uses the following freeware and/or open source applications:
' lame.exe - http://www.mp3dev.org
' metamp3.exe - http://www.hydrogenaudio.org/forums/index.php?showtopic=49751
' oggenc.exe - http://www.rarewares.org/ogg-oggenc.php
' tag.exe - http://wiki.hydrogenaudio.org/index.php?title=Tag_(tagger)
' (You may not sell the script or any of the files)
Dim source_folder, source_watermarked_folder
Dim destination_mp3_original, destination_mp3_watermarked
Dim destination_ogg_original, destination_ogg_watermarked
Dim mp3_original_bitrate, mp3_watermarked_bitrate
Dim ogg_original_bitrate, ogg_watermarked_bitrate
Dim tag_encoder_name, tag_publisher_name, tag_copyright_name, tag_url
Dim tag_composer_name, tag_comment, tag_album, tag_watermarked_prefix
' ## CUSTOMIZABLE FOLDERS #####################################################
source_folder = "input-wav-originals"
source_watermarked_folder = "output-wav-watermarked"
destination_mp3_original = "output-mp3-original"
destination_mp3_watermarked = "output-mp3-watermarked"
destination_ogg_original = "output-ogg-original"
destination_ogg_watermarked = "output-ogg-watermarked"
' ## CUSTOMIZABLE ENCODING BITRATES ###########################################
mp3_original_bitrate = "160" ' Bitrate in kbps
mp3_watermarked_bitrate = "96" ' Bitrate in kbps
ogg_original_bitrate = "128" ' Bitrate in kbps
ogg_watermarked_bitrate = "64" ' Bitrate in kbps
' ## CUSTOMIZABLE MP3 and OGG TAGS#############################################
tag_encoder_name = "ENCODER NAME HERE"
tag_publisher_name = "PUBLISHER NAME HERE"
tag_copyright_name = "COPYRIGHT HOLDER NAME HERE"
tag_url = "http://YOUR URL HERE"
tag_composer_name = "COMPOSER NAME HERE"
tag_comment = "Encoded using script by http://soundtrack.imphenzia.com"
tag_album = "TEXT FOR ALBUM FIELD HERE"
tag_watermarked_prefix = "Preview: "
' You shouldn't need to edit any of the below script
' -----------------------------------------------------------------------------
' Ensure that cscript is executed instead of wscript - otherwise status
' messages will appear in popups instead of console window.
If LCase(Right(Wscript.FullName, 11)) = "wscript.exe" Then
strPath = Wscript.ScriptFullName
strCommand = "%comspec% /k cscript """ & strPath & """"
Set objShell = CreateObject("Wscript.Shell")
objShell.Run(strCommand), 1, True
Wscript.Quit
End If
Dim wshshell, filesys, folder, files_collection, filecount, filename
Dim mp3counter, oggcounter, outputfile
Dim tag_artist, tag_title, tag_genre, tag_year
oggcounter = 0
mp3counter = 0
wscript.echo "Begin encoding process."
Set wshshell = WScript.CreateObject("WScript.Shell")
Set filesys = CreateObject("Scripting.FileSystemObject")
' ## Ensure that the output directories exists, if not then create them
if not filesys.FolderExists( destination_mp3_original ) then _
filesys.CreateFolder destination_mp3_original
if not filesys.FolderExists( destination_ogg_original ) then _
filesys.CreateFolder destination_ogg_original
if not filesys.FolderExists( destination_mp3_watermarked ) then _
filesys.CreateFolder destination_mp3_watermarked
if not filesys.FolderExists( destination_ogg_watermarked ) then _
filesys.CreateFolder destination_ogg_watermarked
' ## Encode Original (non-watermarked) files ##################################
Set folder = filesys.GetFolder(source_folder & "\.")
Set files_collection = folder.Files
filecount = files_collection.Count
for each file in files_collection
' ## Only process WAV files
filename = file.name
if lcase(right(filename,4))=".wav" then
' ## Set tags based on the filename
' ## Source File Name Format: Artist Name_Song Name_Music Genre_Year
tag_artist = GetParam(filename,0)
tag_title = GetParam(filename,1)
tag_genre = GetParam(filename,2)
tag_year = GetParam(filename,3)
' ## Create web friendly name for output file (artist-name-song-name)
outputfile = WebFriendlyName(GetParam(filename,0) & " - " & _
GetParam(filename,1) ,0,0)
' ## MP3 Encoding ---------------------------------------------------------
' ## Create option string for MP3 meta tags
optstr = ""
optstr = optstr & " --title " & """" & tag_title & """"
optstr = optstr & " --artist " & """" & tag_artist & """"
optstr = optstr & " --comment " & """" & tag_comment & """"
optstr = optstr & " --genre " & """" & tag_genre & """"
optstr = optstr & " --year " & """" & tag_year & """"
optstr = optstr & " --album " & """" & tag_album & """"
optstr = optstr & " --user-url " & """" & tag_url & """"
optstr = optstr & " --frame TCOM:" & """" & tag_composer_name & """"
optstr = optstr & " --frame TENC:" & """" & tag_encoder_name & """"
optstr = optstr & " --frame TPUB:" & """" & tag_publisher_name & """"
optstr = optstr & " --frame TCOP:" & """" & tag_copyright_name & """"
' ## Only encode the MP3 file if it doesn't already exist
if not filesys.FileExists( destination_mp3_original & "\" & _
outputfile & ".mp3" ) then
wscript.echo "Encoding Original mp3 with a bitrate of " & _
mp3_original_bitrate & "kbps: " & vbCrLf & _
" " & outputfile & ".mp3" & vbCrLf
' ## Use Lame to encode the mp3 file
wshshell.Run "lame.exe -b " & mp3_original_bitrate & _
" -c """ & source_folder & "\" & filename & """ " & _
"""" & destination_mp3_original & "\" & outputfile & _
".mp3""",0,true
' ## Use Metamp3 to create the mp3 tag
wshshell.Run "metamp3.exe " & optstr & " """ & _
destination_mp3_original & "\" & outputfile & _
".mp3""",0,true
mp3counter = mp3counter + 1
end if
' ## OGG Encoding ---------------------------------------------------------
' ## Create option string for OGG meta tags
optstr = ""
optstr = optstr & " --title " & """" & tag_title & """"
optstr = optstr & " --artist " & """" & tag_artist & """"
optstr = optstr & " --comment " & """" & tag_comment & """"
optstr = optstr & " --genre " & """" & tag_genre & """"
optstr = optstr & " --year " & """" & tag_year & """"
optstr = optstr & " --album " & """" & tag_album & """"
optstr = optstr & " -t URL=" & """" & tag_url & """"
optstr = optstr & " -t COMPOSER=" & """" & tag_composer_name & """"
optstr = optstr & " -t PUBLISHER=" & """" & tag_publisher_name & """"
optstr = optstr & " -t COPYRIGHT=" & """" & tag_copyright_name & """"
' ## Only encode the OGG file if it doesn't already exist
if not filesys.FileExists( destination_ogg_original & "\" &_
outputfile & ".ogg" ) then
wscript.echo "Encoding Original ogg with a bitrate of " & _
ogg_original_bitrate & "kbps: " & vbCrLf & _
" " & outputfile & ".ogg" & vbCrLf
' ## Use Oggenc to encode the ogg file
wshshell.Run "oggenc.exe -b " & ogg_original_bitrate & _
" --output=""" & destination_ogg_original & "\" & _
outputfile & ".ogg"" """ & source_folder & "\" & _
filename & """",0,true
' ## Use Tag to create the ogg tag
wshshell.Run "Tag.exe " & optstr & " """ & destination_ogg_original & _
"\" & outputfile & ".ogg""",0,true
oggcounter = oggcounter + 1
end if
end if
next
' ## Encode Watermarked files #################################################
Set folder = filesys.GetFolder(source_watermarked_folder & "\.")
Set files_collection = folder.Files
filecount = files_collection.Count
for each file in files_collection
' ## Only process WAV files
filename = file.name
if lcase(right(filename,4))=".wav" then
' ## Set tags based on the filename
' ## Source File Name Format: Artist Name_Song Name_Music Genre_Year
tag_artist = GetParam(filename,0)
tag_title = tag_watermarked_prefix & GetParam(filename,1)
tag_genre = GetParam(filename,2)
tag_year = GetParam(filename,3)
' ## Create web friendly name for output file (artist-name-song-name)
outputfile = WebFriendlyName(GetParam(filename,0) & " - " & _
GetParam(filename,1) ,0,0)
' ## MP3 Encoding ---------------------------------------------------------
' ## Create option string for MP3 meta tags
optstr = ""
optstr = optstr & " --title " & """" & tag_title & """"
optstr = optstr & " --artist " & """" & tag_artist & """"
optstr = optstr & " --comment " & """" & tag_comment & """"
optstr = optstr & " --genre " & """" & tag_genre & """"
optstr = optstr & " --year " & """" & tag_year & """"
optstr = optstr & " --album " & """" & tag_album & """"
optstr = optstr & " --user-url " & """" & tag_url & """"
optstr = optstr & " --frame TCOM:" & """" & tag_composer_name & """"
optstr = optstr & " --frame TENC:" & """" & tag_encoder_name & """"
optstr = optstr & " --frame TPUB:" & """" & tag_publisher_name & """"
optstr = optstr & " --frame TCOP:" & """" & tag_copyright_name & """"
' ## Only encode the MP3 file if it doesn't already exist
if not filesys.FileExists( destination_mp3_watermarked & "\" & _
outputfile & ".mp3" ) then
wscript.echo "Encoding Watermarked mp3 with a bitrate of " & _
mp3_watermarked_bitrate & "kbps: " & vbCrLf & _
" " & outputfile & ".mp3" & vbCrLf
' ## Use Lame to encode the mp3 file
wshshell.Run "lame.exe -b " & mp3_watermarked_bitrate & _
" -c """ & source_watermarked_folder & "\" & filename & """ " & _
"""" & destination_mp3_watermarked & "\" & outputfile & _
".mp3""",0,true
' ## Use Metamp3 to create the mp3 tag
wshshell.Run "metamp3.exe " & optstr & " """ & _
destination_mp3_watermarked & "\" & outputfile & _
".mp3""",0,true
mp3counter = mp3counter + 1
end if
' ## OGG Encoding ---------------------------------------------------------
' ## Create option string for OGG meta tags
optstr = ""
optstr = optstr & " --title " & """" & tag_title & """"
optstr = optstr & " --artist " & """" & tag_artist & """"
optstr = optstr & " --comment " & """" & tag_comment & """"
optstr = optstr & " --genre " & """" & tag_genre & """"
optstr = optstr & " --year " & """" & tag_year & """"
optstr = optstr & " --album " & """" & tag_album & """"
optstr = optstr & " -t URL=" & """" & tag_url & """"
optstr = optstr & " -t COMPOSER=" & """" & tag_composer_name & """"
optstr = optstr & " -t PUBLISHER=" & """" & tag_publisher_name & """"
optstr = optstr & " -t COPYRIGHT=" & """" & tag_copyright_name & """"
' ## Only encode the OGG file if it doesn't already exist
if not filesys.FileExists( destination_ogg_watermarked & "\" &_
outputfile & ".ogg" ) then
wscript.echo "Encoding Watermarked ogg with a bitrate of " & _
ogg_watermarked_bitrate & "kbps: " & vbCrLf & _
" " & outputfile & ".ogg" & vbCrLf
' ## Use Oggenc to encode the ogg file
wshshell.Run "oggenc.exe -b " & ogg_watermarked_bitrate & _
" --output=""" & destination_ogg_watermarked & "\" & _
outputfile & ".ogg"" """ & source_watermarked_folder & _
"\" & filename & """",0,true
' ## Use Tag to create the ogg tag
wshshell.Run "Tag.exe " & optstr & " """ & _
destination_ogg_watermarked & _
"\" & outputfile & ".ogg""",0,true
oggcounter = oggcounter + 1
end if
end if
next
' ## Finished - echo how many files were encoded
if mp3counter + oggcounter = 0 then
wscript.echo "No files were encoded."
wscript.echo "If the output files already exist they are not overwritten."
wscript.echo "Delete them first if you wish re-encode them."
else
wscript.echo "Encoding script reporting that all is done!"
wscript.echo mp3counter & " mp3 files and " & oggcounter & " ogg files encoded"
end if
' Supporting function that gets a parameter from a file name using the
' character _ as a delimiter
Function GetParam(thestring, theparam)
dim returnstring, currentparam
for i=1 to len(thestring)
if mid(thestring,i,1) = "_" then
currentparam=currentparam+1
else
if theparam = currentparam then
returnstring=returnstring + mid(thestring,i,1)
end if
end if
next
if lcase(right(returnstring,4)) = ".wav" then _
returnstring=left(returnstring,len(returnstring)-4)
GetParam = trim(returnstring)
end function
' Ugly supporting function that washes the file name to a "web friendly" file
' name... I would use regexp for this but VBScript is quite limited by default.
Function WebFriendlyName(filename, prefixlength, extlength)
WebFriendlyName = lcase(filename)
WebFriendlyName = right(WebFriendlyName , len(WebFriendlyName )-prefixlength)
WebFriendlyName = left(WebFriendlyName , len(WebFriendlyName )-extlength)
WebFriendlyName = replace(WebFriendlyName , " - ", "-")
WebFriendlyName = replace(WebFriendlyName , "[", "")
WebFriendlyName = replace(WebFriendlyName , "]", "")
WebFriendlyName = replace(WebFriendlyName , "(", "")
WebFriendlyName = replace(WebFriendlyName , ")", "")
WebFriendlyName = replace(WebFriendlyName , ".", "")
WebFriendlyName = replace(WebFriendlyName , " ", " ")
WebFriendlyName = replace(WebFriendlyName , " ", " ")
WebFriendlyName = replace(WebFriendlyName , " ", " ")
WebFriendlyName = replace(WebFriendlyName , " ", "-")
End Function
So far this week I have spent most of my hours awake either in traffic on my way to or back from work, or in work. The few hours I’ve had to spare at home I’ve put on composing exclusive music for the three game developers that have contacted me. I’ve also uploaded some more stuff to the Unity Asset Store and played some Modern Warfare 2. Today I reactivated my Eve Online account (free for 5 days) and the game is truly looking awesome but I simply won’t have time to play it unfortunately.
First week of “normal” work following my paternity leave is now in the past and I am enjoying a very welcome weekend.
Motocross practice at Uringe
Today I spent a few hours at Uringe, a great sand track south of Stockholm, where I tried to improve my motocross skills. It went fairly well with only one minor crash but plenty of fun. Bike collected some sand as you can see in the picture below – quite easy to clean with the pressure washer though….
Making Exlusive Music for Three Games
I’ve also recently had requests to create exclusive music for three different games. Two games for the mobile platform (Android + iPhone) and one game is aimed at computers and consoles.
Quite exciting because the music needed is of very different genres and it’s a lot of fun to let the creativity take over and see where it leads.
Astrofighter.Net
As for Astrofighter.Net I’ve managed to make slight progress here as well. I’ve imported the low poly ship versions into Unity and I’m currently placing all the thruster and weapon mount points. Next will be texture mapping the ships and adding script code for the remaining weapon types.
Today was my first day back in my “normal” job as an IT Security Consultant and I’m coming back from a 7 month long paternity leave. It is with mixed emotions that I return because I’ve really enjoyed spending the time with my family and it has also enabled me to focus a lot on Imphenzia Music/Soundtrack/Games. But then again, you have to earn a living…
Anyway, I’ve finished creating the 8 types of spaceships to be featured in the game ranging from a light scout to a heavily armored and well equiped beast. The 7 weapon types I’m considering for the game is Blaster, Cannon (Gatling Gun), Plasma Gun, Rail Gun, Laser, Missile Launcher, and Mortar. Here is a preview video:
Today I modeled the 8th spaceship and all the weapons. I created a low poly and a high poly version of each weapon so I can render “Normal” maps (bump maps) making the weapons appear to have a lot more polygons than they really do. I’ll be doing the same for the spaceships as well.
Of course, the ships won’t look like this in the game. These are just colored wireframe renders of all the prototype ships in low detail.
Top down view as they will be seen in the game (click for high res picture)
Here is a perspective view of the spaceships as well:
Finally, here is a detailed look at the weapons I created today:
Texturemapping these objects will take a lot longer than it took to model them. Each ship only took about 15-20 minutes each and the weapons took about the same amount of time. I can imagine it will take me a few hours to make the texture maps for each object though, ugh.
Today I took a break from programming and decided to make some low polygon spaceships for the game. I don’t have a lot of modelling experience but it’s a lot of fun to let the imagination take over. Here are the 7 ship models I’ve come up with so far:
When I create these models, I just start with a normal box, about 4x2x1 meters. Then I just extrude, extrude, extrude the faces in different directions to create the wings, the engines, the cockpit, and so forth. In addition to extruding I also move the vertices to create angles, but that’s pretty much it. I use the “Symmetry” modifier in 3DS Max so I only have to model one side of the ship.
These models are very primitive, as you see, but for my top down view I think they will be quite sufficient. They will need texture mapping, normal mapping, and specular mapping as well which should make them quite decent.
I did 5 of these models tonight, the other 2 I created previously. A model of this low detail takes me around 15 minutes or so to create.
Off to bed now… I’ll have to do some thinking as I’m considering two paths ahead for the game:
Should all the ships be quite similar in character but have small variations in terms of speed, armor, energy, weapon capabilities…
… or …
Should the ships be very different in character making a defender ship slow and heavily armored with loads of weapons where as a scout is on the other end of the range being really light, fast, with low armor and weaker weapons…
I’m tired now. It’s 1:53AM and I only sleep about 4 hours per night. Too excited to go a sleep =)
It’s now been roughly five weeks since I started with Astrofighter.Net and I estimate that I may have put in 150-200 hours during the five weeks. I’ve been off on paternity leave but that still requires a lot of work with a 1.5-year-old and a 2 month old, so it’s mainly been when the kids have gone a sleep that I can work on the project, between 8pm and 2-3am.
I realize that I haven’t made it easy for myself since I am, with this project, learning Unity, learning C#, learning network game development, and learning object oriented development all at once. The only object oriented development I have played around with before is BlitzMax, but I never really created anything with it.
Since my last update I’ve got a lot of development done. It’s not so much game-play wise because I decided to ensure that I got a menu system and game state machine running so I don’t bump into any troubles further down the line. To be more specific, this is what I’ve done the past 2 weeks:
Design a custom GUI for Unity to suit my space-look
Developed a menu system which scales to any screen resolution
(still a lot of menu items to put in, but the menu framework is all there)
Developed Pilot account management with a PHP/MySQL backend so you can create and login to accounts seamlessly from within Astrofighter.Net
Account creation and session validation for Pilot accounts so a server can verify a token for the pilot in order to secure the integrity of statistics for pilots
Developed a Game State Manager that takes care of transitioning between scenes and game states, such as Menu –> Host Game –> Start Server –> Load Level –> Prepare For Match –> Play Match –> Show Results.
The Server issues all the state changes and the clients follow.
Redesigned the class structures separating out a GameStateManager class (state management), GameManager class (player management), MatchManager (match management), etc.
Developed a flexible Settings class so game settings can be loaded / saved / accessed in the game and in the menu system
Today I also had a look at UnityPark uLink that are offering a nice scalable solution for networking in Unity. Ironically enough it does much of what I’ve put a lot of effort into developing in terms of account management and backend server communication. I may, or may not, decide to switch to uLink depending on findings down the road… but I may have to pre-purchase the uLink indie license because they have a limited deal on it until end of October.
Here’s a video from today showing some of the menu GUI, player account management (with PHP/MySQL backend), and game state handler switching from menu, to “prepare”, to “match”:
Today I’ve spent a lot of time trying to get a nice resizable menu for Astrofighter.Net and it’s been a bit of a struggle so far.
My first approach was to use Matrix4x4.TRS to create a scalable 4:3 ratio menu GUI based on a coordinate system of 1600 x 1200. For wide screen resolutions I would simply add an offset to make sure the 4:3 menu system is always centered. It worked quite good in higher resolutions but as soon as a fairly mid-range to low resolution was used smaller fonts became pixelated and unreadable. How unfortunate because the matrix scaling was a one line solution.
I’m now working on my own approach. It basically still uses the 4:3 ratio menu with padding being added to the sides for widescreen resolutions. The difference is now that I recalculate the Rect positioning and width/height using my own function basically doing the same as the Matrix4x4.TRS scaling. The difference is that I now also manually set the font size using the same scale, but with an integer, so the font always looks crisp.
The status now is that I’ve got a nice resizable GUI with crisp fonts in any resolution. It’s quite a bit of extra work but I think it’s worth it.
If you are interested in the code – please let me know. I’ll probably make it available in the Unity wiki or forums once I’m finished.
Yesterday I participated in an enduro race called “Ränneslättsloppet”. It’s a competition with thousands of participants in different classes. I raced the recreational class for men aged between 30-39.
The weather was great and the trail was dry but not dusty. I got a decent start but my lack of experience in competitions such as these lead me to ride defensively in the beginning so I more or less just joined the flow. During the first lap we saw some traffic jams and no less than two bikes catching fire =)
One lap was 20 km in length and the goal is to do as many laps as possible during 155 minutes. The winner of my class did 5 laps and I am very satisfied with the 3 laps I completed being so novice at this.
My only other previous experience was when I participated in Stångebroslaget earlier this year and that was a truly painful experience trying to get my motocross bike (KX450F) to take me through extremely muddy forest terrain and it kept stalling and draining my energy having to kick start it all the time. For this race I had bought a WR250F enduro bike instead and that made all the difference in the world. I didn’t stall it once while riding, only 3-4 times when I crashed.
Pre-race activities
05:30 – Alarm clock goes off. Time to get some stuff out of the fridge and pack it into the cooling bags to meet up with my race mate.
06:30 – Trailer with bike hooked up to the car and we head off on the 320km journey down to Eksjö where the competitions is being held.
10:00 – Arrival at Eksjö. Registration and carried all the stuff to the pit area for the service stops. We brought 2 spare wheels in case of flat tires, petrol, water, power drinks, tools, extra gloves, extra goggles, environmental mats, and some other bits and pieces.
11:30 – Rode our bikes to the inspection area where they were approved for the race. Then headed off to the start area where we lined our bikes up along side another 1000 bikes.
12:15 – Got dressed and packed our camel backs. I skipped my motocross top and just had a t-shirt and body armor. I overheated big-time at Stångebro so I needed to to all I could to keep the temp down. I also packed an extreme amount of fluids. More than anyone else with a small backpack filled with 2 x 2L water and 2 x 0.5L Powerade. I wasn’t going to dehydrate this time!
12:30 Arrived back at our bikes at the starting line. 15 minutes to go.
The Race
I was placed in the last line of the 30-39 year-olds, and then we had the 40-49 and 50-65 year old riders starting 5 minutes after us. As the start went I got a clean launch and settled into the tempo fairly well. It was quite a few traffic jams and pileups the first half lap so I got to eat some mud, watch a bike on fire, and pushing and shoving my way past every here and there.
The terrain was quite dry but also quite rocky. Sometimes the front wheel would catch a rock nearly sending me off the bike but I completed the first lap without any crashes. I also found out that my new bike, a WR250F, was extremely kind to me and I didn’t have to work nearly as hard as at Stångebro which also meant that I didn’t have to drink any water during the first lap… Great, 5L fluids on my back for nothing =)
I skipped the service stop on the first lap. I had all the water I needed and the bike didn’t need any petrol yet so I decided to press on and save myself from pushing the bike through the service area in the blazing sun. Better to stop somewhere in the shade later on to drink saving me energy and time. So I did, about half way into lap 2 I found a nice shaded area just off the trail so I stopped there, drank water and a Powerade, then continued.
There is a section of the track called “Berget” (Mountain) which is a steep and very slippery section which is definitely the most challenging part of the track. The first lap I chose to take the “easy” way around it because it was heaps of traffic and I didn’t want to get stuck in it, or cause additional jams for that matter. Now, on the other hand, I decided to go for it on the second lap and I cleared the hard path quite easily.
Not long after “Berget” my front wheel caught a rock on a descent which sent me and the bike flying. I tried to stay on the bike but failed and my knee got twisted to a point where it hurt a bit, but not excessively much. When I picked the bike up and carried on I tried to lift my leg back to the foot pegs and it felt really really strange… I couldn’t make out if the bike had been damaged or if my leg was totally messed up. Maybe adrenaline was keeping me from feeling that the leg is wrecked I thought…
The awkward feeling remained and something just didn’t feel right. I managed to turn my foot back and forth, wiggle my toes, and lift my leg despite the strange feeling and as I rid another few km I came to the conclusion that it must just be a combination of the bike having had a slight deformation by the foot peg and me being tired that caused the awkward feeling.
I reached the service area after lap two and as I got off the bike I contemplated whether to call it a day and be satisfied with completing two laps or go into the service area and fuel up me and the bike for a third lap. I chose to the latter. I didn’t come all this way to give up with time to spare.
I stayed in the pit area for about 15 minutes. Dropped my backpack and replaced it with a single camel back with 2L of water. Ate a snickers bar and some “Dextrosol”. Poured half a bottle of water over my head and took off again.
On the third and final lap I was starting to get really exhausted. My arms weren’t strong and my legs started to cramp up when I was standing so I had to sit down a lot more which meant my ass took a beating on the rocky sections. I can feel that today (the day after.) I kept quite a slow tempo but managed to speed up every now and then. A lot of riders completing their 4th and 5th lap were lapping me at this stage and I always knew when they came as they shouted from behind to make their presence known.
I reached “Berget” for the last time and I had already decided to take the easy route – but for some reason I didn’t. I launched myself up the hard section of slippery rocks and about half way up the bike slid to the side and I had to jump off. No harm done to me but the previously awkward feeling foot peg was standing straight up and I though, great all this way to mess up the bike so close to the finish… turned out it was just stuck a bit so after giving it a good kick it was back to just being awkward again.
Not long to go now… I can hear the speaker making announcements in the background. I come to a long whoop section along a concrete barricade where I had done some wheelies over the whoops the previous laps. Shifted up to fourth and gave the throttle a good twist. I saw two spectators standing by the concrete barricades about 100m ahead of me and just as I approach them I jump off a whoop just to see my front wheel hitting the next one sideways… I wobble big time and nearly go over the handlebars. In the corner of my eyes I see the two spectators jumping back to avoid potentially being hit by a bike and an exhausted rider. Somehow I regain control over the bike and I managed to glance over at the spectators and give them a nervous smile just as I pass them. That’s it now – no heroics from now on.
I rolled over the final whoop sections and I enter the last windy section on the big grass field that leads up to the checkered flag. I did it… and despite being very far from the top of my class I am happy with my result. The winner did 5 laps in 2 hours 58 minutes and I did 3 laps in 3 hours and 18 minutes. I finished in 232 place.
Here is a helmet camera that someone wore during the first lap in my class. The rider wearing the camera finished in 250th place but I can’t spot myself anywhere in the footage. “Berget” can be seen at around 24 minutes in to the video. And it always looks easy on video – try it before you decide whether it really is easy or not =)