Many of you already know that two weeks ago, divVerent and me entered Function2012 demoscene party and competition with "Scripted Symphony", an 8 minutes long demo presented by The Xonotic Bot Orchestra.
If you haven't already seen it, watch it below:
If you're unfamiliar what "demos" or what the demoscene is, here's a very good, entertaining documentary about it:
Moleman 2 - Demoscene - The Art of the Algorithms (2012)
http://www.youtube.com/watch?v=iRkZcTg1JWU
So, what is Scripted Symphony?
According to our file_id.diz:
That says it all. Really
Scripted Symphony is nothing, but a large abuse of the Darkplaces engine. During development we had to implement new features (eg. a new cheat mode, sv_cheats 3. With that, you can even force spectators not to fly, but WALK on the map, like regular players, just not interacting with anything. Expect the server crap itself though...) to achieve what we wanted, but that'll come a bit later.
While creating the demo, I've learned even more about using netradiant and it's undocumented features, that a mapper can use to make a map more interesting, more interactive. In the following few threads, I'd like to show you all, how we made Scripted Symphony.
Those who'd like to tinker around with the sources have to check out the following branches (and recompile DP afterwards ofc.):
xonotic-maps.pk3dir/cbrutail/function2012
http://git.xonotic.org/?p=xonotic/xonoti...nction2012
xonotic-data.pk3dir/cbrutail/function2012
http://git.xonotic.org/?p=xonotic/xonoti...nction2012
The demo itself (exe) can be downloaded from here:
http://www.scene.org/search.php?search=t...d_symphony
Part1: The stage.
In the first part, I'd like to show you how we created the stage itself, that houses the orchestra event.
Some interesting parts:
The base entity layout for a bot orchestra stage is described in the Bot Orchestra manual on the Xonotic development wiki:
http://dev.xonotic.org/projects/xonotic/..._Orchestra
Bobbing and pulsating rings
Those who played on my map 8bit arena should be familiar with this thing. It's a simple set of bobbing rings, that pulsate. But why do I highlight this, it's a simple map entity called func_bobbing, isn't it? No, it is not. The movement of the rings are completely done with a shader:
The important parts are deformVertexes move and tcMod scroll.
Let's see what q3map2 shader manual has to say about deformVertexes move:
So, why is it more rewarding to make this thing in a shader, and not with a func_bobbing? If you make it nonsolid, it's essentially the same, isn't it? Well, not really. The thing is, func_bobbing is a server side entity: It means, that it's movement and position is networked to all clients. Now, this small map itself has 15 of these moving rings, meaning if these were func_bobbings, their movement should been calculated by the server (putting lots of pressure on it), and also networked to the clients, resulting in a huge amount of data traffic. But since the bobbing displacement of the rings are done with a shader, it's totally client side (and also a lot less resource hungry).
The pulsating behaviour was achieved by clever use of tcMod scroll (as name suggests, scroll the texture on the surface), and a gradient texture, that starts as green, fades out, and fades in to green again:
Glow-up floortiles
The Tron-like glow-up on step effect is also reused from 8bit arena. The mechanism is a clever use of triggerable door entities (the floor tiles). When a player touches ("steps") on a trigger (yellow bricks), it makes the glowy floor tiles rise up from a hidden room in an instant. Previously, the instant movement was done with setting the speed value of the func_doors to -1. However, due to recent code changes that no longer works, we had to use some insane speed like 100000. The floor tiles are of course textured with a shader, that has the nonsolid keyword, so players don't bump up when the door rises. To make the tile remain in place for a brief time and move back after the player stepped off, the "wait" key of the door is set to 1 (second).
_skybox
This is nothing particular, it's only a normal _skybox entity. However, I thought I'd show this too, because I personally think this is an awesome feature of q3map2, you can produce very nice looking environment for your map with it, however, it's very underused by mappers.
_skybox is a "dot" entity, notice the small, purple box on the editor view. That is it.
Lasershows #1 and #2
The sideways movement of Lasershow #1 was achieved by targetting a misc_laser to an invisible func_bobbing. Worth noting that I also used "spawnflags 5" on the laser, which translates to "start on" and "notrace". Start on doesn't require any explenation, but let's see what notrace does:
Perfect, faster rendering and no collision calculation, it's just what we need here!
Laserhow #2 was done a bit trickier, but not much more. It's obvious that we used a similar trick. There's func_bobbing for sideways sweeping movement, but how did we do the vertical one, and how did we combine the two?
The answer is an entity named "func_vectormamamam":
The multiple given entities were the previously mentioned func_bobbing (in Y axis), and a func_train entity moving up and down between two path_corners. Then we targetted the misc_laser to the func_vectormamamam that combined the horizontal movement of the func_bobbing and the vertical movement of the func_train.
Worth noting, that we also used "movetype 1" key on the func_train, that gave it a hard stop/start linear movetype on the path_corners (compared to the default "fine" one, that means sinus movetype). To get the right effect, we used 3 of these systems (with 3 misc_lasers), and set the "phase" key of the func_bobbings to 0, 0.25 and 0.75)
Designer note: notice how the path_corners are shifted relative to the func_train both in the exploded and the assembled view! That is because the func_train doesn't support origin brushes, but it uses the smallest xyz coordinates of it's geometry as the center of it's movement. So if you want to make a func_train, be sure to either move all the path_corners to where the smallest coordinates will be of the func_train when it reaches that place, or make an invisible tringle tip under the func_train.
Also notice that the func_vectormamamam has an origin brush! Though it is undocumented it needs to have one.
The visualiser bars
This is maybe the most important and exciting part of the stage itself. To understand how it works, first we need to learn a bit about the inner works of the Bot Orchestra and some special commands of Darkplaces used for debugging.
The Bot Orchestra plays music by reading specially generated config files, that command the bots what to do, for example switch to tuba, press fire1 and sidestep to left, release firebutton, etc. Here's an actual example:
Most of these commands make sence by just reading them, but here's a bit more in-depth look to them. To run a bot orchestra performance, one has to enter the map with sv_cheats 1, to be able to utilise some debugging commands of DP. According to the bot orchestra manual, you have to start a performance with these commands:
The idea is very simple: one loads up a config file, that has a bunch of timed commands to send to the bots about what to do.
Are you still with me? Good. Let's talk bit about DP and entities on the map. There are a bunch of map entities that require another entity to trigger them to perform some operation. Think about a button and a lift's case. You push the button (you trigger it), and the lift gets triggered by the button, causing it to rise. But is it really necessary to have "physical" interaction (note: passing an invisible trigger wall on the map counts as physical interaction too!) on the players side to trigger an entity chain? With recent code changes made especially for the demo, not at all With sv_cheats 1 enabled, it is now actually possible to trigger any entity on a map with "cmd usetarget foo"
Knowing this, there's a command in that config above that might make you suspicious: sv_cmd bot_cmd 9 cc usetarget indicator_laser1. Woah, isn't that a command to poke around with entities on the map? Yes, that it. From DP's point of view, bots are just like any other connected clients. It accepts any command that the client can do. So... why not command the bots to poke around with server side entities? Like, to trigger entities that move other entities on the map?
This is where we finally get to the part where the secret of the visual equaliser bars gets revealed
The bars are special kind of solid brush entities, called func_wall. Tha fact that these are actually entities gives us tons of possibilities to manipulate them. The idea was to make these bars move up when any of the bots play the right instrument; the more the instrument is played, the higher the bar goes. For the melodic instruments we also took in count different tones (4 bars according to how low or high tone is played by the melody bots). The bars go through the stage (they actually pierce through it, so later I had to be carefull with camera movement)
Let's see how the bars look like in the editor and ingame:
Notice the small common/origin textured cubes on the tips of the bars, it'll get important later!
But what does all this mean? If we look at the entity description in netradiant, we'll learn that "movetype 6" translates to TOSS, "solid 2" equals to BBOX (bounding box, this'll get important!). Pushable is the first undocumented feature, but it's prety obvious what it does.
The first big hack is mins and maxs. In our setup This sets the BBOX size of the entity of a 8qu large cube (4+4x4+4x4+4, right where it's origin brush is located!) - that's why we also set solid=2! The bars pierce through the solid stage, they'd just fall down to the abyss because of their movetype TOSS: they won't stop falling down until they hit something solid. But since we modified the bars' BBOXes (and the center of their "solidity"), they now actually lodge in the stage at their tip (think of the BBOX and origin brush hack as an invisible hook )!
Ok, but how did we get them to move up?
The entity I'm going to introduce you is actually the very heart of the whole performace. This very handy entity is called:
target_spawn
We'll use it later sooo much for different (though essentially the same) tasks, that I was tempted to name the demo "target_spawn abuse", but obviously nobody else would have understood the joke. You can see them as bright purple point entites on the editor screenshot above.
According to it's entity description, target_spawn spawns or modifies an entity when triggered. And by that, there's one thing the description forgets to tell: you can actually modify EVERY attribute of ANY kind of entity!
Let's see how a target_spawn for a visualiser bar looks like:
The key part is "message". That's the attribute target_spawn sets for it's target when it gets triggered. In our case right now, it gives upward (z) velocity to the bars.
This is where it becomes clear why I told you all before about cmd usetarget. To move the bars when an instrument is played, the bot executes the command "cc usetarget indicator_foo", which triggers the target_spawn "foo", that in turn gives upward velocity to the bar it targets, in the end making it rise.
Ok, but there's a tiny problem here: if an instrument is played too much, the bar just flies away, and won't come down at all! to solve this problem, we used multiple rows of trigger_impulse brush entities targetted at a target_position through the bars:
To simulate linear falloff, the different layers of the trigger brushes have incrementing strenght values from lower to top - the higher the bar flies, the stronger the opposite force needs to be to push it down.
That's all folks! I hope you've learned some new tips and tricks on this first issue of How we did it. Next time, I'll explain the title image, and the Super Mario Land lvl1 camera!
Stay tooned!
If you haven't already seen it, watch it below:
If you're unfamiliar what "demos" or what the demoscene is, here's a very good, entertaining documentary about it:
Moleman 2 - Demoscene - The Art of the Algorithms (2012)
http://www.youtube.com/watch?v=iRkZcTg1JWU
So, what is Scripted Symphony?
According to our file_id.diz:
Code:
Scripted Symphony is a demo made in the heavily modified Quake engine Darkplaces.
It features massive server and client side entity abuse to produce stage effects, and
bot AI forced with scripts to play music with special weapons used as instruments.
That says it all. Really
Scripted Symphony is nothing, but a large abuse of the Darkplaces engine. During development we had to implement new features (eg. a new cheat mode, sv_cheats 3. With that, you can even force spectators not to fly, but WALK on the map, like regular players, just not interacting with anything. Expect the server crap itself though...) to achieve what we wanted, but that'll come a bit later.
While creating the demo, I've learned even more about using netradiant and it's undocumented features, that a mapper can use to make a map more interesting, more interactive. In the following few threads, I'd like to show you all, how we made Scripted Symphony.
Those who'd like to tinker around with the sources have to check out the following branches (and recompile DP afterwards ofc.):
xonotic-maps.pk3dir/cbrutail/function2012
http://git.xonotic.org/?p=xonotic/xonoti...nction2012
xonotic-data.pk3dir/cbrutail/function2012
http://git.xonotic.org/?p=xonotic/xonoti...nction2012
The demo itself (exe) can be downloaded from here:
http://www.scene.org/search.php?search=t...d_symphony
Part1: The stage.
In the first part, I'd like to show you how we created the stage itself, that houses the orchestra event.
Some interesting parts:
The base entity layout for a bot orchestra stage is described in the Bot Orchestra manual on the Xonotic development wiki:
http://dev.xonotic.org/projects/xonotic/..._Orchestra
Code:
To do a bot orchestra performance, a "stage" map for the orchestra is required. It needs the following entities, if you use the default midi2cfg-ng.conf:
a target_position called tVocals for the vocalist
a target_position called tPercussion which is where the bots will aim. It's a good idea to have a noimpact surface behind it so shots don't make a noise on their impact.
32 target_position entities called tUba1 to tUba32 for where bots with tubas/accordeons should walk to start their performance
32 target position entities called tChr1 to tChr32 for where percussion bots should walk to start their performance. They will then aim at tPercussion. These targets must not be on a nosteps or metalsteps surface!
3 target_position entities called tMetalSteps1 to tMetalSteps3 for bots to jump on for metal step sounds
4 target_position entities called tNoSteps1 to tNoSteps4 for jetpack bots so they don't make an unwanted landing sound
info_player_deathmatch spawnpoints with "restriction" "1" for where bots are to spawn
info_player_deathmatch spawnpoints with "restriction" "2" for where humans are to spawn
you probably want to make it so the tUba bots can't leave their area using various means; see the opera map for a quite safe approach involving a "shootable" trigger, but you could also make a pit you can fall into but not get out, or a teleport, or similar tricks to keep them enclosed.
also you may want to look at opera's mapinfo settings: it sets _independent_players 1 and bot_navigation_ignoreplayers 1 to help bot navigation. If you can get it to work without these hacks, it'd be better though.
Bobbing and pulsating rings
Those who played on my map 8bit arena should be familiar with this thing. It's a simple set of bobbing rings, that pulsate. But why do I highlight this, it's a simple map entity called func_bobbing, isn't it? No, it is not. The movement of the rings are completely done with a shader:
Code:
textures/map_arena/pulsegreen_move22_1
{
qer_editorimage textures/map_arena/gfx/pulsegreen.tga
surfaceparm nolightmap
surfaceparm nomarks
surfaceparm nonsolid
surfaceparm trans
deformVertexes move 0 0 22 sin 0 1 0 0.1
cull none
qer_trans 0.6
{
map textures/map_arena/gfx/pulsegreen.tga
blendfunc blend
tcMod scroll 0 2
}
}
The important parts are deformVertexes move and tcMod scroll.
Let's see what q3map2 shader manual has to say about deformVertexes move:
Code:
3.3.4 deformVertexes move <x> <y> <z> <func> <base> <amplitude> <phase> <freq>
This keyword is used to make a brush, curve patch or md3 model appear to move together as a unit. The <x> <y> and <z> values are the distance and direction in game units the object appears to move relative to it's point of origin in the map.
The <func> <base> <amplitude> <phase> and <freq> values are the same as found in other wave form manipulations.
The product of the function modifies the values x, y, and z. Therefore, if you have an amplitude of 5 and an x value of 2, the object will travel 10 units from its point of origin along the x axis. This results in a total of 20 units of motion along the x axis, since the amplitude is the variation both above and below the base.
It must be noted that an object made with this shader does not actually change position, it only appears to.
So, why is it more rewarding to make this thing in a shader, and not with a func_bobbing? If you make it nonsolid, it's essentially the same, isn't it? Well, not really. The thing is, func_bobbing is a server side entity: It means, that it's movement and position is networked to all clients. Now, this small map itself has 15 of these moving rings, meaning if these were func_bobbings, their movement should been calculated by the server (putting lots of pressure on it), and also networked to the clients, resulting in a huge amount of data traffic. But since the bobbing displacement of the rings are done with a shader, it's totally client side (and also a lot less resource hungry).
The pulsating behaviour was achieved by clever use of tcMod scroll (as name suggests, scroll the texture on the surface), and a gradient texture, that starts as green, fades out, and fades in to green again:
Glow-up floortiles
The Tron-like glow-up on step effect is also reused from 8bit arena. The mechanism is a clever use of triggerable door entities (the floor tiles). When a player touches ("steps") on a trigger (yellow bricks), it makes the glowy floor tiles rise up from a hidden room in an instant. Previously, the instant movement was done with setting the speed value of the func_doors to -1. However, due to recent code changes that no longer works, we had to use some insane speed like 100000. The floor tiles are of course textured with a shader, that has the nonsolid keyword, so players don't bump up when the door rises. To make the tile remain in place for a brief time and move back after the player stepped off, the "wait" key of the door is set to 1 (second).
_skybox
This is nothing particular, it's only a normal _skybox entity. However, I thought I'd show this too, because I personally think this is an awesome feature of q3map2, you can produce very nice looking environment for your map with it, however, it's very underused by mappers.
_skybox is a "dot" entity, notice the small, purple box on the editor view. That is it.
Code:
Compiler-only entity that specifies a the origin of a sky box (a wholly contained, separate area of the map), similar to some games' portal skies. When compiled with Q3Map2, the sky box surfaces will be visible from any place where sky is normally visible. It will cast shadows on the normal parts of the map, and can be used with cloud layers and other effects. As it is compiler-only, it can't "scale up" entities in its box.
To use this, carve a small box in some larger structure on your map, place this entity inside that box hole, and make a small version on what should be seen in the sky there.
Lasershows #1 and #2
The sideways movement of Lasershow #1 was achieved by targetting a misc_laser to an invisible func_bobbing. Worth noting that I also used "spawnflags 5" on the laser, which translates to "start on" and "notrace". Start on doesn't require any explenation, but let's see what notrace does:
Code:
NOTRACE : the laser passes through solid (faster rendering on clientside); non-FINITE lasers then never display their impact effect "mdl"!
Perfect, faster rendering and no collision calculation, it's just what we need here!
Laserhow #2 was done a bit trickier, but not much more. It's obvious that we used a similar trick. There's func_bobbing for sideways sweeping movement, but how did we do the vertical one, and how did we combine the two?
The answer is an entity named "func_vectormamamam":
Code:
Solid entity that moves according to the movement of multiple given entities (max 4)
The multiple given entities were the previously mentioned func_bobbing (in Y axis), and a func_train entity moving up and down between two path_corners. Then we targetted the misc_laser to the func_vectormamamam that combined the horizontal movement of the func_bobbing and the vertical movement of the func_train.
Worth noting, that we also used "movetype 1" key on the func_train, that gave it a hard stop/start linear movetype on the path_corners (compared to the default "fine" one, that means sinus movetype). To get the right effect, we used 3 of these systems (with 3 misc_lasers), and set the "phase" key of the func_bobbings to 0, 0.25 and 0.75)
Designer note: notice how the path_corners are shifted relative to the func_train both in the exploded and the assembled view! That is because the func_train doesn't support origin brushes, but it uses the smallest xyz coordinates of it's geometry as the center of it's movement. So if you want to make a func_train, be sure to either move all the path_corners to where the smallest coordinates will be of the func_train when it reaches that place, or make an invisible tringle tip under the func_train.
Also notice that the func_vectormamamam has an origin brush! Though it is undocumented it needs to have one.
The visualiser bars
This is maybe the most important and exciting part of the stage itself. To understand how it works, first we need to learn a bit about the inner works of the Bot Orchestra and some special commands of Darkplaces used for debugging.
The Bot Orchestra plays music by reading specially generated config files, that command the bots what to do, for example switch to tuba, press fire1 and sidestep to left, release firebutton, etc. Here's an actual example:
Code:
sv_cmd bot_cmd 9 wait_until 7.147429
sv_cmd bot_cmd 9 cc usetarget indicator_laser1
sv_cmd bot_cmd 9 debug_assert_canfire 1
sv_cmd bot_cmd 9 presskey attack1
sv_cmd bot_cmd 9 wait_until 7.197429
sv_cmd bot_cmd 9 debug_assert_canfire 0
sv_cmd bot_cmd 9 releasekey attack1
sv_cmd bot_cmd 9 wait_until 7.247429
Most of these commands make sence by just reading them, but here's a bit more in-depth look to them. To run a bot orchestra performance, one has to enter the map with sv_cheats 1, to be able to utilise some debugging commands of DP. According to the bot orchestra manual, you have to start a performance with these commands:
Code:
sv_cmd bot_cmd load bot_performace_config.cfg
The idea is very simple: one loads up a config file, that has a bunch of timed commands to send to the bots about what to do.
Are you still with me? Good. Let's talk bit about DP and entities on the map. There are a bunch of map entities that require another entity to trigger them to perform some operation. Think about a button and a lift's case. You push the button (you trigger it), and the lift gets triggered by the button, causing it to rise. But is it really necessary to have "physical" interaction (note: passing an invisible trigger wall on the map counts as physical interaction too!) on the players side to trigger an entity chain? With recent code changes made especially for the demo, not at all With sv_cheats 1 enabled, it is now actually possible to trigger any entity on a map with "cmd usetarget foo"
Knowing this, there's a command in that config above that might make you suspicious: sv_cmd bot_cmd 9 cc usetarget indicator_laser1. Woah, isn't that a command to poke around with entities on the map? Yes, that it. From DP's point of view, bots are just like any other connected clients. It accepts any command that the client can do. So... why not command the bots to poke around with server side entities? Like, to trigger entities that move other entities on the map?
This is where we finally get to the part where the secret of the visual equaliser bars gets revealed
The bars are special kind of solid brush entities, called func_wall. Tha fact that these are actually entities gives us tons of possibilities to manipulate them. The idea was to make these bars move up when any of the bots play the right instrument; the more the instrument is played, the higher the bar goes. For the melodic instruments we also took in count different tones (4 bars according to how low or high tone is played by the melody bots). The bars go through the stage (they actually pierce through it, so later I had to be carefull with camera movement)
Let's see how the bars look like in the editor and ingame:
Notice the small common/origin textured cubes on the tips of the bars, it'll get important later!
Code:
"classname" "func_wall"
"targetname" "bar_crylink1"
"pushable" "1"
"movetype" "6"
"solid" "2"
"mins" "-4 -4 -4"
"maxs" "4 4 4"
But what does all this mean? If we look at the entity description in netradiant, we'll learn that "movetype 6" translates to TOSS, "solid 2" equals to BBOX (bounding box, this'll get important!). Pushable is the first undocumented feature, but it's prety obvious what it does.
The first big hack is mins and maxs. In our setup This sets the BBOX size of the entity of a 8qu large cube (4+4x4+4x4+4, right where it's origin brush is located!) - that's why we also set solid=2! The bars pierce through the solid stage, they'd just fall down to the abyss because of their movetype TOSS: they won't stop falling down until they hit something solid. But since we modified the bars' BBOXes (and the center of their "solidity"), they now actually lodge in the stage at their tip (think of the BBOX and origin brush hack as an invisible hook )!
Ok, but how did we get them to move up?
The entity I'm going to introduce you is actually the very heart of the whole performace. This very handy entity is called:
target_spawn
We'll use it later sooo much for different (though essentially the same) tasks, that I was tempted to name the demo "target_spawn abuse", but obviously nobody else would have understood the joke. You can see them as bright purple point entites on the editor screenshot above.
According to it's entity description, target_spawn spawns or modifies an entity when triggered. And by that, there's one thing the description forgets to tell: you can actually modify EVERY attribute of ANY kind of entity!
Let's see how a target_spawn for a visualiser bar looks like:
Code:
{
"classname" "target_spawn"
"origin" "-3108.000000 -432.000000 288.000000"
"target" "bar_crylink1"
"message" "velocity_z 400"
"targetname" "indicator_crylink1"
}
The key part is "message". That's the attribute target_spawn sets for it's target when it gets triggered. In our case right now, it gives upward (z) velocity to the bars.
This is where it becomes clear why I told you all before about cmd usetarget. To move the bars when an instrument is played, the bot executes the command "cc usetarget indicator_foo", which triggers the target_spawn "foo", that in turn gives upward velocity to the bar it targets, in the end making it rise.
Ok, but there's a tiny problem here: if an instrument is played too much, the bar just flies away, and won't come down at all! to solve this problem, we used multiple rows of trigger_impulse brush entities targetted at a target_position through the bars:
Code:
An accelerator/dampener/wind field.
Can be used in two ways:
"dampener field": just set strength to a value from 0 to 1. Entering the field will slow down to this factor.
"accelerator field": just set strength to a value from 1 to infinity. Entering the field will accelerate by this factor.
"wind field": set strength to the amount of velocity to add per second, and target a target_position. The field will apply force in the direction from its own origin to the target (use an origin brush to specify its own origin, or this will fail) when touched.
"gravity field": set strength to the amount of velocity to add per second at the center, and set radius to the radius of the field. Set falloff to the desired falloff characteristics.
-------- KEYS --------
target : "wind field": points to the target_position to which the player will get pushed.
strength : "wind field", "gravity field": amount of force per second to apply. "dampener/accelerator field": slowdown/speedup factor.
Code:
"classname" "trigger_impulse"
"target" "target_percussions0"
"strength" "4800"
To simulate linear falloff, the different layers of the trigger brushes have incrementing strenght values from lower to top - the higher the bar flies, the stronger the opposite force needs to be to push it down.
That's all folks! I hope you've learned some new tips and tricks on this first issue of How we did it. Next time, I'll explain the title image, and the Super Mario Land lvl1 camera!
Stay tooned!
"One should strive to achieve; not sit in bitter regret."