Sunday, July 26, 2009

Sounds

After a year-and-a-half of taking part in Metaplace during alpha and beta, I've finally added sounds to one of my worlds (I don't count the crwth_effects world, as the sounds are only played when the "player" tries them out, and not as part of the world itself.) I'd say the biggest reason is that I rarely finish a world, and something like sound is a finishing touch.

I'm not really sure what made me do it, since the world I've added them to, my Ultima Online workspace world, is nowhere near done (being a lab world, it'll never be done as such). For some reason this past week, I got to thinking about the music of UO, or the sounds of UO... I'm not sure, but I decided to figure out how to pull all of the UO sounds from the datafiles. Once I had those, I browsed through them, playing them to bring back memories (which is silly, since my UO accounts only just expired) and to see the large portion of them that never appeared in the game. Some of the sounds include the player's footsteps on a variety of terrain, and I was thus compelled to add them into the UO world.

The funny thing is that right now, my UO world doesn't have the UO avatars for the players, but the Metaplace ones instead -- the only non-UO thing in the world gets the only UO sounds! For each terrain type (grass, pavement, sand, etc.) there are two sound files, allowing you to alternate the foot falls per-foot; they could have taken the approach of a sound file that played a two-step sound, but it wouldn't match up if you took a single step with your avatar.

This poses a challenge right off the bat, since there's no way in Metaplace to say "play these two sounds in a loop"; all sounds, whether to an individual or the whole Place, or whether always-on or based on distance, are on their own. Because we want to play a different sound right after one finishes -- to give the clip-clop walking effect, we have one of two tools available to us: we can either ask to be informed when a sound ends (and then act to start up the next one), or we can try to time playing the next sound based on the length of the current one.

Trigger sound_done

As part of the PlaySound*() series of API functions (but not exhaustively documented), you may provide an object pointer as a callback object, telling the system that once this sound finishes, this callback object will have a "sound_done" Trigger fire at it. This lets you play the next sound at the right time -- in theory. Of course, being an online world, you have the latency of the internet to contend with, which means that once the sound finishes, your client tells the server (after X milliseconds), the server acts upon this information by firing the sound_done trigger at the specified object, the object decides to play a new sound, and the client is told, again after X milliseconds. All told, these milliseconds can add up, between the latency and the script logic to determine that another sound should be played. This delay is probably fine for playing background music (which I also added to the UO world), because a half-second delay between soundtracks is fine. A half-second between footsteps, when you're stepping every quarter-second, is not.

Here are the basics of this method:

Trigger sound_done()
local movesounds={
{"feetpvmta","0:19"},
{"feetpvmtb","0:18"}
}

self.move_number=self.move_number+1
if self.move_number>#movesounds then
self.move_number=1
end
local moverec=movesounds[self.move_number]
self.move_sound=PlaySoundTo(self,moverec[2],255,0,self)
end


SendTo()

While we can't completely eliminate the latency problem, we can at least reduce it by not waiting to react to a message from the client, stating that the current sound has finished playing. Instead, after starting the current sound, we'll fire a delayed Trigger to our sound-playing code to just start playing when it makes sense to do so. This means that we need to know how long our sounds are to know how long to wait (for playing a series of background tracks), or we have to know that a given delay will be plenty of time for each of the sounds to play and finish (such as short footstep sounds).

Trigger movesound()
local movesounds={
{"feetpvmta","0:19"},
{"feetpvmtb","0:18"}
}

self.move_number=self.move_number+1
if self.move_number>#movesounds then
self.move_number=1
end
local moverec=movesounds[self.move_number]
self.move_sound=PlaySoundTo(self,moverec[2],255,0)
self.soundtrigger=SendTo(self,"movesound",250)
end

I have to admit, I was prepared to trial-and-error the delay value to see what looked good -- in theory, it should be based off of the framerate of your animation, and the number of frames between each foot lands on the ground in the animation), but I really lucked out with my first attempt at 250ms -- go take a look at the world and see if you agree.

That's right, I've gone with this second approach, for the reasons specified -- I've reduced the effect of latency on my sounds loop by not relying on the client to report when the sounds finish, and I'm able to do this because the clip-clop of walking is very regular. And what about my background music? Well, in the end I used to delayed-Trigger method, there, too:

Trigger playmusic()
local music={
{"britain1","http://pages.cpsc.ucalgary.ca/~crwth/metaplace/uo/music/Britain1.mp3",39},
{"Britainpos","http://pages.cpsc.ucalgary.ca/~crwth/metaplace/uo/music/Britainpos.mp3",53},
{"Stones1","http://pages.cpsc.ucalgary.ca/~crwth/metaplace/uo/music/Stones1.mp3",135},
{"Walking","http://pages.cpsc.ucalgary.ca/~crwth/metaplace/uo/music/Walking.mp3",343},
{"Medieval","http://pages.cpsc.ucalgary.ca/~crwth/metaplace/uo/music/Medieval.mp3",150},
{"Festival","http://pages.cpsc.ucalgary.ca/~crwth/metaplace/uo/music/Festival.mp3",128},
}
self.music_number=self.music_number+1
if self.music_number>#music then
self.music_number=1
end
local musicrec=music[self.music_number]
self.background_music=PlaySoundRefTo(self,musicrec[2],100,0,self)
SendTo(self,"playmusic",(musicrec[3]+1)*1000)
end


Note the list of tracks also contains the length, needed because they're of variable length, unlike the footsteps. Why use this method instead of the sound_done() Trigger? I think the main reason is because, when trying to handle both the music and the sound effects, I had code that looked like this:

Trigger sound_done(userid,handleid)
if handleid==self.background_music then
SendTo(self,"playmusic",1000)
elseif handleid==self.move_sound then
SendTo(self,"movesound",0)
end
end

I can't imagine all the conditionals when I've got dozens of sound effects, such as spell effects and combat sounds. Sure, I could separate them into different scripts each with their own sound_done() Trigger, but the conditional code would still be the same. One way I see this being a bit more useful is being able to supply not only the callback object, but the callback Trigger as well.

Dynamic footsteps

The other thing I decided to do, because of the variety of footstep sounds available, was to change the sound based on the terrain the player walked upon.

In general, having different events based on the tiletype that a player stands on isn't "easy", because tiles themselves don't support Triggers or events, so you can't just attach a script to a grass tile that sets the player's footstep sound to "feetgrssa" and "feetgrssb". For many cases of tile-based effects, you'd have to have some sort of tick-based Trigger, checking if the tile has changed and then changing the effect in question.

The handy thing about the footsteps firing every 250ms, however, is that this provides its own "tick", and thus every time we're to start a new sound, we can decide if the sound should change.

Trigger movesound()
if self.moving==0 then return end

local tile=GetTileAt(self.x,self.y)
local tilename=stylesheet.places["0:"..GetPlace().placeId].tiles[tile].name
local movesounds={
stone={
{"feetpvmta","0:19"},
{"feetpvmtb","0:18"}
},
grass={
{"feetgrssa","0:21"},
{"feetgrssb","0:20"}
},
dirt={
{"feetgrvla","0:25"},
{"feetgrvlb","0:22"}
},
sand={
{"feetsanda","0:23"},
{"feetsandb","0:24"}
},
water={
{"feet15a","0:27"},
{"feet15b","0:28"},
{"feet15c","0:29"},
{"feet15d","0:26"},
}
}
local tilesounds=movesounds[tilename]
if tilesounds then
self.move_number=self.move_number+1
if self.move_number>#tilesounds then
self.move_number=1
end
local moverec=tilesounds[self.move_number]
self.move_sound=PlaySoundTo(self,moverec[2],255,0,self)
end
self.soundtrigger=SendTo(self,"movesound",250)
end

This assumes that your tiles are nicely named, as mine are (they're computationally-generated from the Ultima Online datafiles, which conveniently provide names), though this is all in the hands of the world builder anyway. The above is the exact function I use for my footsteps.

Of course, this solution wouldn't work for sounds that have a long "tick" -- Ultima Online played different background music depending on your region, so you'd get some spookier music in the jungle or a dungeon, compared to a forest or city. You wouldn't want to wait three minutes for your current cycle of "city" themed music to end before realizing that you're in a dungeon and should be a little more on edge.

When to walk

The only thing missing is knowing when to start and stop the walking sound loop at all. The last snippet gave a hint on how the stopping is done -- using a property called self.walking. It's used to stop the sound-playing loop, above, but how is it set? By patching into the path_begin()/path_not_found()/path_end() series of Triggers that are part of the pathfinding system.

Trigger path_begin()
StopSound(self,self.move_sound)
if self.soundtrigger~=0 then CancelSend(self,self.soundtrigger) end
self.moving=1
SendTo(self,"movesound",0)
end

Trigger path_end()
self.moving=0
StopSound(self,self.move_sound)
end

Trigger path_not_found()
self.moving=0
end

The extra code in the path_begin() Trigger is necessary because of the delayed-Trigger method being used; if someone was walking and then clicked elsewhere, they'd start a new pathfinding session but wouldn't have stopped the last delayed-Trigger, causing two (or three, or four) sound loops to play until the player finally stopped moving. This was an interesting effect, but definitely not desired!

Some readers might be thinking that such a problem could be avoided if I had just used SendToRepeat() instead of a recursive delayed Trigger. Then I could just let the SendToRepeat() continue as it would, but I could really do the same thing with the recursive Trigger. The reason I didn't use the SendToRepeat() is because of the possibility of a variable delay for the sounds; while the footsteps are a regular 250ms apart, I might get sounds that aren't so regular, and would thus want to be able to vary the delay, much as done in the music loop. For this use, I could go either way, but I like to code generally, even if Dorian and Sean would rather I didn't.


I'm happy with how the sound turned out, considering it was really a whim. It makes me want to get more sounds into the world, even without the mechanics that really require them (the sounds of combat, the sounds of crafting, monsters roaring...) I also realized, as writing this post, that the footstep sounds are solely for the ears of the maker -- this is a bit silly, since we have PlaySoundRefRadius() to emanate sound from an object and to get the feature of distance affecting the volume built-in. Perhaps I'll go take a look at that now.

Friday, July 10, 2009

UiXML

A year ago this week, the Metaplace folks flew four alpha testers, myself included, down to San Diego to meet the team and get a little insight into what was coming. It was a fantastic experience, very much appreciated, not only to meet the team (and the other three testers -- Chooseareality, Rboehme, and Scopique) but to see where they work (though they've moved since then) and how they worked (in which we got to participate). We also got a sneak-peak at a new feature that was released shortly after that, which was the UiXml() system.

The idea of this system is that instead of calling lots of UI API functions, you can create an XML segment, in a string, that will be passed to a single UiXml() function and will generate all of your UI elements for you. So, instead of typing

local outerrect=UiRect(0, "outer rect", 10, 10, 220, 140)
UiColor(outerrect, 125, 2, 2, 0.9)
local innerrect=UiRect(outerrect, "inner rect", 1, 1, 218, 138)
UiColor(innerrect, 50, 130, 130, 0.8)
local label=UiLabel(innerrect, "name", 1, 1, "label text")
UiColor(label, 255, 234, 0, 1)
UiAttachUser(self,outerrect)

you can type

local rectxml=[[
<rect name="outer rect" x="10" y="10" width="220" height="140" red="125" green="2" blue="2" alpha="0.9">
<rect name="inner rect" x="1" y="1" width="218" height="138" red="50" green="130" blue="130" alpha="0.8">
<label name="name" x="1" y="1" text="label text" red="255" green="234" blue="0" alpha="1"/>
</rect>
</rect>
]]
UiAttachUser(self,UiXml(rectxml))

What does this gain you? Quite a few things, actually. As you can see, you can set the colour of a UI element at the same time as defining it, instead of as a separate command. Not really a biggie. But the above rectxml string doesn't have to be created all in one go, and THAT makes things powerful.

For instance, you could have a loop that builds up the string, based on ... well, based on whatever: the number of entries in a table, whether a checkbox is or isn't checked, or the user's name. But, to be honest, you could do the same thing with if-, while- or for-statements and the standard Ui*() functions.

You could have objects which themselves know how to render the pertinent UI elements, and each might just have a property, "myui", which you can read at any time to insert into a block of the XML. But, of course, you could have a function or Trigger on an object which calls the related Ui*() functions.

Okay, you could call an external web service, which would supply the UI XML to render whateveritis that that web service wants you to render -- instead of having to fetch some text or JSON data or XML and then process it in your world, wouldn't it be nice if that service knew how to "talk UiXml()", and could give you a window, with buttons, and sliders, and textboxes, all laying out the data? Yes, even THIS could be done with Ui*() functions, but we're seeing some usefulness...

XML is processable; with an appropriate XML/XSLT library, or hell, even some interesting use of string.gsub(), I can change all of my UiRect()s to UiOval()s; decrease all of my red colours by 10; or widen all of my elements by 10%. Yes, some of these could be variables that get modified, and others could be done with if-chains or lookup tables, but I still say processing a string is easier...


Still not convinced?

UiXML supports "layouts"; these are basically containers for arranging UI elements (similar to Java Layouts if you know those). From the wiki:

<layout style="grid" grid_x="2" grid_y="1" padding_x="1" padding_y="2">
<image name="itemimage" width="32" height="32" spriteId="0:2"></image>
<rect width="68">
<layout style="grid" grid_x="1" grid_y="2" padding_x="0" padding_y="0">
<label name="itemname" text="Name" red="0" green="0" blue="0" ></label>
<label name="itemqty" text="Qty" red="0" green="0" blue="0"></label>
</layout>
</rect>
<layout>

See the grid_x and grid_y? This set up the organization of the elements as they were added. They even nested, so inside one of the grid elements was another layout. Oh, but there's a UiLayout() function (I bet I'm the only one who has ever used it).

Fine. How about "components"? Again, from the wiki:

component = [[
<ui xmlns:mp='http://www.metaplace.com/schema/ui'>
<define_component name="data_field">
<rect name="back" width="100" height="20" red="131" green="131" blue="131" expand="true">
<layout style="grid" grid_x="2" grid_y="1" padding_x="1" padding_y="1">
<label name="data_name" text="name:" width="100" />
<text_field name="data_value" command=" " text="value" red="0" green="0" blue="0"/>
</layout>
</rect>
</define_component>
</ui>
]]

-- Add this XML to the UiXml stack
UiXml(component)


Using A Component:
myUI=[[
<ui xmlns:mp='http://www.metaplace.com/schema/ui'>
<window name="item_detail" x="10" y="10" width="400" height="140" style="20">
<layout style="grid" grid_x="2" grid_y="2" padding_x="1" padding_y="5">
<component type="data_field" name="df1" height="20" width="200"/>
<component type="data_field" name="df2" height="20" width="200"/>
<component type="data_field" name="df3" height="20" width="200"/>
<component type="data_field" name="df4" height="20" width="200"/>
</layout>
</window>
</ui>
]]
UiXml(myUI)

Basically, components are "templates" for UI layout, allowing you to group together multiple UI items into a single conceptual block, and then reuse them as often as you like. This example provides a rectangle with a label and a textfield as a nice convenient unit that can be placed anywhere with a single line. Note how the height and width can be supplied afterwards, leaving some variability to the component's configuration.

In fact, you could go even further with the <override/> tag:

<component type="data_field" height="20">
<override target="data_name" text="Strength:"/>
<override target="data_value" text="100"/>
</component>

Fantastic! Customized label/field pairs, at your fingertips. Sure, I could write a function that provided this functionality -- the function could take any number of parameters that it would then use to make a specific component, and return a handle... which do you think is easier to use?

Okay, if you're still not convinced about the power of UiXML over regular UI function calls, Data Binding will win you over. Data binding allows you to specify an alternate source to a constant for filling in values. So instead of specifying a static value of "100" for an element's colour,

<rect name="red rect" x="0" y="0" width="100" height="100" red="100" blue="0" green="0"/>

we can use a constant in our script:

<rect name="red rect" x="0" y="0" width="100" height="100" red="{RED_VALUE}" blue="0" green="0"/>

Or even something more advanced:

<rect name="red rect" x="0" y="0" width="100" height="100" red="{stylesheet.places["0:0"].red_value}" blue="0" green="0"/>

Anything you can imagine that you can code, could be put into that { } definition. (This has the unfortunate distinction, however, of being one of the few ways to hide malicious code -- a future blog post). I hope you can get an idea of how powerful that is. Sure, whatever code you put in there could also be run separately, and then passed to a Ui*() function as a parameter, but this string of code itself can be generated dynamically - which is why it can also be dangerous.

There are also other types of data binding. Use "% %" for values passed in during the UiXml() call, such as

<ui xmlns:mp='http://www.metaplace.com/schema/ui' scriptId='0:8'>
<component type="data" height="20">
<override target="data_name" text="Strength:"/>
<override target="data_value" text="%foo%"/>
</component>
</ui>

values = {foo='a value here'}
winId = UiXml(xml, parentWindowId, values)

And even better, use "# #" to pull data from object properties:

<ui xmlns:mp='http://www.metaplace.com/schema/ui'>
<component type="data" height="20">
<override target="data_name" text="Health:"/>
<override target="data_value" text="#health#"/>
</component>
<component type="data" height="20">
<override target="data_name" text="Mana:"/>
<override target="data_value" text="#mana#"/>
</component>
</ui>

winId = UiXml(xml)
UiBindObject(winId, self)

the only thing that would make the above better is if the #values# automatically updated any time the properties changed; as it stands, you have to call UiBindObject() every so often to have the changes reflected.


So, have I convinced you that UiXml() is THE way to do UI in Metaplace? I hope not. That's right, I hope not, because... they just removed it.


Apparently I was the only person using it, and instead of letting me go on doing so, it got removed. I agree, it had some problems - some of the later UI elements, such as sliders, weren't supported, and you couldn't set the text on certain items from within the XML - but it also worked well for the parts that did work. Even more stinging is the fact that a new system, UI Styles, was introduced as a replacement to UI XML. It's not the same thing at all (though it's an interesting system in its own right, one that I may blog about).

So why did I blog about it at all? Well, I have a Google Doc which is a long list of future blog posts, and that was on it from before they stole my baby from me. And, to be honest, I had already started working on a replacement version which DOES support sliders, and text initialization (but which, can't do the cool things such as the data binding) before it was taken away, so those who might want this functionality can have it once I publish my version. And, perhaps, just perhaps, all of the bitching I do about this (and other things) will get others to bitch (about this, and other things), and my beloved UiXml() might be returned to me.

Hey, stop laughing. I can dream.

Wednesday, July 8, 2009

Embedding

A week or so ago, Metaplace released the ability to embed your Metaplace world into pretty much any webpage. As long as your page can support the IFRAME tag, you're probably set; there has been mention of various modules for Wordpress and other blogs, and I now regularly sit in chat from an embedded version of the PlainOldChat world inside an iGoogle gadget. You can also see the PlainOldChat world embedded at the bottom of this page (I'm too lazy to figure out how to change the blog's template to make it fit up near the top).

The main purpose of this embed, I suppose, is to allow people to share their worlds in a different environment: instead of having people "go" to Metaplace to see your world, they can find it right at your blog, or your company's website, or on your guild's page. This is a nice way to get people with common interests together in a "live" setting, giving a virtual environment that's a little more interactive than your flat webpage or forum.

Not long after the embed was introduced, there was talk about using them as banner ads. I'm surprised we haven't seen this yet, actually. Of course, it would be nice to get rid of the little bit of non-world stuff from the embed, like the Help/Logout stuff, so that JUST the world is shown. Also, there's currently no automatic anonymous or guest user support as yet, so only people who choose to create an account, or choose to log in, are going to be subjected to the advertising.

Adding support for anonymous or guest access would also allow for mini-games to be added to a web page; nothing as elaborate as a full virtual world, but just a casual game of slot machines, or a shoot-em-up, or sudoku, or a little RPG.

But the one thing I think will make embedding fun, cool, and powerful is that the surrounding webpage can communicate with the embedded world, by using Metaplace's design of every world being its own web server. In this way, the encompassing website, using AJAX or something similar, could display information about the world or about the user, right in the page instead of in the embedded view. You could have your health bar outside of the play area, the high score list, the help commands, your character's inventory...

Why not just put these things inside the Metaplace world, you ask? Because they take up screenspace. But don't they still take up screenspace, just in the webpage portion, you ask? Yes, but: one of the great things about the embed is that you can finally force a size for your world view; back in alpha, we started off with a 640x480 view, and could expand to fullscreen, but now it's the other way around, and there's no way to make someone leave fullscreen view. Some world- and game ideas can rely on the player only seeing a certain amount of the world, and while we do have a little bit of distance culling in Metaplace, it's done as a radius distance instead of a square, and it doesn't affect tiles. Being able to restrict the size of the world means that every player is on even footing, and no one benefits from having a 30" widescreen monitor over those of us with just a 20".

Displaying information from the world is only the beginning of what can be done with the embeds and the web triggers, but I'll save other ideas for another post. For now, I'll enjoy being able to chat in Metaplace alongside my Twitter feed alongside my email, all in one browser tab, and will save the interesting embed tricks for another day (one in which I've brushed up on a some Javascript).