Forget the cloth physics, kid
So I’m learning Unity and figured it couldn’t hurt to blog about my experiences while still fresh.
After doing a few of the sexy terrain and physics tutorials which instantly let me do amazing things with a few clicks, I realized I probably couldn’t even write tic tac toe.
OH YEAH?! Be amazed:
Fine, it sucks. But that wasn’t the point, it forced me to come to grips with how objects, scripting and using classes together all worked in Unity and C#.
(And yes, the enemy “ai” is actually Board.GetRandomEmptyCell() … busted! )
Source is here as a zip. (Requires Unity 4.3, despite the intense graphics it doesn’t require pro)
A common trait of programmer-centric devs is are constantly thinking about projects on a “shared” and “app specific” level. They are organizing code into two places, with the more generic stuff meant to be reused. We just can’t help it! It’s why we always end up with engines and middleware.
So in case it’s useful to anybody else, here are some general utilities I wrote and an explanation of why they are useful.
You can download them as a unity package here: rt.unitypackage (they are also in the tic tac toe source above)
I didn’t see a simple way to play a “crap.wav” filename from script (you have to create an object and attach a sound and ..) , so this is a very basic class that takes care of stuff like that.
You add an audio file under Assets/Resources (uncheck 3d sound source on the file in Unity – That one confused me, as there is no error, you just don’t hear anything…) and can play it from anywhere like this:
//don't add the .wav or .mp3 at the end, it's automatic
Or to get fancier:
//Play at 1.0f volume and a pitch mod of 2 (twice as fast)
RTAudioManager.Get().PlayEx("scream", 1.0f, 2.0f);
To play a song (will kill any current song playing)
//plays mysong.ogg or mp3 etc at 1.0f volume and 1.0f pitch mod, looping
RTAudioManager.Get().PlayMusic("mysong", 1.0f, 1.0f, true);
To set it up in a project is easy, just add RTAudioManager.cs to an empty GameObject somewhere. (It will rename the gameobject RTAudioManager if it isn’t already) It uses a singleton style .Get() to give the same instance to everybody.
One of the first things I do in a new programming environment is figure out how to call any function in N seconds with any number of parms .
Even in the crappy Tic Tac Toe above, the difference between everything happening at once and instead using subtle delays (for instance, a delay before the AI moves) makes a huge difference.
If interesting timing is simple to add and tweak in your game, you’re much more like to do it, if you’re as lazy as I am.
Coroutines and other schedulers I found didn’t quite fit my requirements.. plus, I like to waste time re-inventing everything anyway!
Same as RTAudioManager, to set it up you just add it to an empty GameObject in your scene. There is no other setup needed to just start using it.
It uses SendMessage internally and things it sends to must have a unique GameObject name.
Let’s say you want to use the above RTAudioManager to play “pain”(.wav) in 1 second instead of instantly. No need to build a special timer, just use RTEventManager like this:
//schedule the function "Play" to be called in one second, sending a string variable to it.
RTEventManager.Get().Schedule(RTAudioManager.GetName(), "Play", 1.0f, "pain");
It’s a little ugly, but not too bad to understand. The .GetName() is just a function I added to the RTAudioManager class to make it easy to get the GameObject name its attached to, but a string of “RTAudioManager” would have worked just as well, you just have to be sure you know what stuff is named.
Want to call a function called Respawn that is in an attached script on your player GameObject (which is named “Player”) in 5 seconds with no parms?
//note that you don't have to send that extra parm
RTEventManager.Get().Schedule("Player", "Respawn", 5.0);
But what about RTAudioManager.Get().PlayEx(“fileName”, 1.0f, 2.0f); from above? What if we want that to happen in 5 seconds? That needs three parms, how can we schedule that?
Well, I couldn’t figure out a great way, but because the scheduler’s single parm can be of any object type we can work around the problem. One way would be to send a custom class, but I’m far too lazy to be creating classes everywhere so here’s what I came up with instead:
//will play in 5 seconds
"PlayEx", 5.0f, new RTDB("fileName", "scream",
"volume", 1.0f, "pitch", 2.0f));
What’s going on here? RTDB is a simple database key/value (which can be any object type) system that can be created and sent inline with a flexible number of parms. Key “fileName” is a string, key “volume” is a float and so on.
Unfortunately, when doing this, the receiving end must expect what is coming and know the keyname(s) to extract the data. RTAudioManager’s code to receive it looks like this:
public void PlayEx(RTDB db)
It’s just a front end for calling the real function. It will warn if sent the wrong type. If a parm is left out, it will use a default value so it still works. (filename is the only thing absolutely required)
On the bright side, you can easily add support for more parameters without breaking backwards compatibility in places you’ve already used it.
Now, you may be worried about the costs of using SendMessage internally, the name lookups, and inline initializing and usage of RTDB and the like. Well, unless you’re sending 25+ messages a second I wouldn’t worry about it. It’s mostly for controlling program flow and timing, if you’re sending 1000 messages a frame (for instance, trying to control a sprites x/y animation with it) it’s definitely the wrong tool for the job.
One click build and upload
Oh, also included in the zip above is a windows .bat file that builds and uploads the web version of the game. You’d have to edit it a bit, but here it is:
REM get our ftp logon info
:Actually do the unity build
rmdir build\web /S /Q
echo Building project...
%UNITY_EXE% -quit -batchmode -buildWebPlayer build/web .
echo Finished building.
rmdir temp /S /Q
xcopy build\web temp\%FILENAME%\ /E /F /Y
rename temp\%FILENAME%\web.html index.html
if not exist temp\%FILENAME%\index.html beeper.exe /p
if not exist temp\%FILENAME%\web.unity3d beeper.exe /p
ncftpput -u %_FTP_USER_% -p %_FTP_PASS_% -R %_FTP_SITE_% /www/ temp\*
echo File uploaded: http://www.%_FTP_SITE_%/%FILENAME%
:Let's go ahead an open a browser to test it
It requires ncftp to be installed.
Oh, app_info_setup.bat sets the app name, but it also would need to setup the ftp site and password data. Basically you need this somewhere, could even cut and paste it to the top of the .bat:
To get slightly more graphical, here’s a “throwaway breakout” I whipped up (using the old 3d physics, not the new box2d stuff) (Maximize to look slightly less ass, use arrow keys to control):
Was watching the Unite keynote (woah, Richard Garriott!) and believe I heard that 10% of all downloads happening on iOS are now Unity based. That’s just.. wow.
Why Unity and C#? Aren’t you that hardcore C++ guy who made Proton SDK?
Yeah, but that doesn’t mean I have to use C++ for everything. I still use Proton (ahem, Growtopia) and it’s my go-to weapon if I have to port an existing C++ product to mobile. (as with Dink Smallwood)
But let’s face it, supporting the newer graphic technologies on the plethora of platforms and graphics chips out there is a losing battle for a one-person developer. Especially if I’d like to actually make games too.
(Note: Others are still adding new features such as GLES2 support to Proton in separate distributions, which is fine with me)