Forums » News » StepMania 5.1.0 Alpha 2 released

StepMania 5.1.0 Alpha 2 release notes
Bugfixes, NewField improvements, and a new fullscreen backend for linux.

StepMania 5.0.11 release notes
This will probably be the last release in the 5.0.x line. Maintaining two separate branches is a hassle, and the 5.1 branch is in good shape.
< cybik> til Kyzentun fixes bugs for breakfast
--
< maxvg1> shakesoda: then why do i still play lol
<@shakesoda> because you're an ITG player. And thus, a masochist
--
<@shakesoda> Kyzentun: I think you might need to put down the meshes for a bit
Reply
Sweet to see the new mods system out. I liked the concept of just having a function to calculate how to draw a note. Except uh... I don't see any place I can use, well, actual functions? This is what I had in mind when I first skimmed through the docs a while ago. Something along these lines to, for example, make notes from beats 8 to 16 drawn in a different position.

-- inside an OnCommand in an FG change
-- also please for christ's sake name the damn playfields in the editor by default already holy shit

local player = SCREENMAN:GetTopScreen():GetChild('PlayerP1')
local field = player:GetChild('NewField')

for i, col in ipairs(field:GetColumns()) do -- not a fan of snake case ¯\_(ツ)_/¯
col:AddMod(function(params, transform)
if params.beat >= 8 and params.beat <= 16 then
transform.x = transform.x + 100
end

-- sinenotes just for shits and giggles
transform.x = transform.x + math.sin(params.beat) * 50
end)
end


But the system as it's implemented... eh? I kind of have trouble trudging through it. It seems more like it was made to just implement generic silly mods more than it was made to flexibly allow you to do fun stuff with notes at specific times. And I'm no API designer by any means, but I fell like the system I had in mind would have been a thousand times more easy to understand and get into, and even present itself in a sufficiently flexible manner? Rather than trying to combine a bunch of value/input types, think through a bunch of math and hope it'll do a thing... I was hoping I'd be able to just, well, tell it to do a thing instead.

Last edited: 1 April 2016 1:32am

Reply
I considered lua functions, but that would probably drop performance a ton. Especially on something like a hold, where the transform mod has to be evaluated every 4 pixels.
And I really don't want inefficient crap like people checking 100+ if conditions in the mods to handle gimmicks tied to a specific beat because that is *really* something that kills frame rate. That level of poor design is why I included a system for managed mods that are only active between their start and end beats. That way, I can make sure timed mods are handled efficiently.


local function lurnk(scalar, eval_beat, eval_second, music_beat, music_second, dist_beat, dist_second, y_offset)
local ret= 0
if eval_beat > 8 and eval_beat < 100 then
ret= 100
end
return ret + math.sin(y_offset * 0.03125) * 64
end
for i, col in ipairs(newfields[pn]:get_columns()) do
col:get_note_pos_x():add_mod{name= "lurnk", "ModFunctionType_Lua", lurnk}
--col:get_note_pos_x():add_mod{name= "lurnk", "ModFunctionType_Sine", {"ModInputType_YOffset", 0.03125}, 0, 64, 0}
end

The lua function one runs at 42 fps rendering 8 full screen holds. The other one runs at 54 fps.

To be more accurate to what gimmick simfilers actually do, there would have to be 100+ conditions checking the range of eval_beat.

local function lurnk(scalar, eval_beat, eval_second, music_beat, music_second, dist_beat, dist_second, y_offset)
local ret= 0
for i= 1, 100 do
if eval_beat > i and eval_beat < 1+i then
ret= 64+i
end
end
return ret + math.sin(y_offset * 0.03125) * 64
end

This starts out at 29 fps, then gradually drops to 25 fps as the song progresses.


for i, col in ipairs(newfields[pn]:get_columns()) do
local managed_mods= {}
for i= 1, 100 do
managed_mods[i]= {start_beat= i, end_beat= i+1, "ModFunctionType_Constant", {"ModInputType_Scalar", i, 64}}
end
local posix= col:get_note_pos_x()
posix:add_mod{name= "lurnk", "ModFunctionType_Sine", {"ModInputType_YOffset", 0.03125}, 0, 64, 0}
posix:add_managed_mod_set(managed_mods)
end

This runs at a constant 54 fps, not gradually losing speed as the song progresses.

Sure, lua mod functions like you suggested sound good when you think of them, but they come out too inefficient to be practical.

Last edited: 1 April 2016 7:50am

< cybik> til Kyzentun fixes bugs for breakfast
--
< maxvg1> shakesoda: then why do i still play lol
<@shakesoda> because you're an ITG player. And thus, a masochist
--
<@shakesoda> Kyzentun: I think you might need to put down the meshes for a bit
Reply
That's fair. I had performance in mind as well, but there really has to be a more intuitive way of implementing the API.

And let me clarify, if I wasn't clear: I didn't mean for all notes to be rendered with those mods depending on the song beat, I meant giving just those individual notes mods separate from the rest, as to make the kinds of effects possible with DivinEntity possible with the new system.
Reply
I actually implemented lua mod functions to compare the performance fairly after reading your post. I didn't think you'd be posting again so soon, so I just edited the results into the previous post.

It is possible to make mods that affect individual notes, though it does require some inventiveness. I used the method for it in my Per-quantization speeds video.

Funny story: I thought up the basis for this system while at a pool party last summer. I swam for a while, and thought on the problem, then my father wanted to talk so I hopped into the hot tub with him.
Some random dude came by and talked to us for a few minutes, but I couldn't make sense of anything he said because I was thinking about this system, and my father was talking about something too.
After talking for a bit, I got out and sat in a chair staring into the empty pool and thinking. When I went to leave a couple hours later, I found that the random dude had stolen my shirt.
So I literally lost my shirt over this system.
< cybik> til Kyzentun fixes bugs for breakfast
--
< maxvg1> shakesoda: then why do i still play lol
<@shakesoda> because you're an ITG player. And thus, a masochist
--
<@shakesoda> Kyzentun: I think you might need to put down the meshes for a bit
Reply
Might I ask what your average FPS is in this scenario, without mods at all? Because 54fps with completely C++ managed mods seems a little odd to me, when the old system almost never had performance hurdles.

I'll also add two semi important points: a situation where you'd actually have eight fullscreen holds is fairly rare, and any over the top gimmicks have been known to cause performance issues anyway, but none in terms of playability, notably my playfield "ghosts" in my shadient sim.

Additionally, it would probably be more performant to attach the function to a specific range of notes or columns - a filter if you will - instead of always calling the function for every note.
Reply
With no mods, I get 64 for my system, and 84 for the old system. This is probably mostly due to my system rendering more verts. Part of it is due to the old system having a much simpler task when deciding what mods need to be evaluated, and generally having fewer types of mods to evaluate.

In this video you can watch the frame rate drop to ~44 as I turn on more mods. (you can also see poor behavior like drunk having a moving effect while the game is paused)
If I use a sine wave mod on every aspect of the note transform and alpha and glow, like this:

for i, col in ipairs(newfields[pn]:get_columns()) do
for n, getter in ipairs{
"get_note_pos_x", "get_note_pos_y", "get_note_pos_z",
"get_note_rot_x", "get_note_rot_y", "get_note_rot_z",
"get_note_zoom_x", "get_note_zoom_y", "get_note_zoom_z",
"get_note_alpha", "get_note_glow"} do
local mod= col[getter](col)
mod:add_mod{"ModFunctionType_Sine", {"ModInputType_YOffset", 1/64}, i, 1, 0}
end
end

I get ~44 fps in the new system. The visual appearance is rather different, but it's the same idea of performing lots of computation for every note.

Also notable is that I'm measuring this on a debug build. Release builds tend to run substantially faster.

I use 8 fullscreen holds for my test case because it's the worst case scenario. If the worst case is acceptable, then the common and best cases will take care of themselves.
Holds are the worst case because the engine has to calculate many verts to have the shape of the hold affected by the mods. In the old system, a hold body is a symmetric quad strip. One mod evaluation every 16 pixels, to calculate 3 verts, which comes out to 4 triangles each step. If you turn on Bumpy or Twirl, it switches to one 4 pixels per step. Each step is like rendering a note, it needs position, alpha, and glow. (there's a slight gain from ignoring x and z rotation for hold steps)
My hold rendering is similar, but I always use 4 pixels per step because the abstraction of the mod system means it's not easy to answer whether the extra smoothness is worth the cost. I use a normal quad strip instead of a symmetric quad strip, which means half the triangle count for the same number of steps. (but with no mods, I make 4x the steps, so the end result is 2x the triangle count)

In the end, it comes down to doing more in the new system. The field has 16 mods for itself (pos, rot, zoom, receptor and explosion alpha and glow, fov, vanish x and y), and each column has 35 mods (time offset, quantization multiplier and offset, speed, lift trail length, reverse offset, reverse scale, center percent, note transform, column transform, hold normal, note alpha and glow, receptor alpha and glow, explosion alpha and glow). Only the note ones are actually evaluated for every note, but it all adds up.
< cybik> til Kyzentun fixes bugs for breakfast
--
< maxvg1> shakesoda: then why do i still play lol
<@shakesoda> because you're an ITG player. And thus, a masochist
--
<@shakesoda> Kyzentun: I think you might need to put down the meshes for a bit
Reply
I'm not denying the usefulness and functionality extents of the API at all, but I'll just be frank: it's ugly as hell, not user-friendly in the slightest, and doesn't fit in at all with the rest of the theming API. Actors are pretty straight-forward to deal with a lot of the time, but this and the spline math have always gotten me stumped whenever I try to play around with them and do anything worth a shit. I'm probably just stupid, but I can't really see anyone getting too deep into mod functions without a lot of time spent reading. I guess for now it's fine enough that ArrowDefects exists for an interface using the old mods, but I was kind of excited to do some crazy DivinEntity level stuff without a buggy noteskin actor hack.

Additionally, it would probably be more performant to attach the function to a specific range of notes or columns - a filter if you will - instead of always calling the function for every note.
Comment on this, though?

EDIT: Also, I don't want my main argument here to be seen as "it's too hard to use" because that applies to theming and modding as a whole (and is a dumb argument in general), just suggesting that maybe it could be applied in a way that's easier to explain and document.

Last edited: 1 April 2016 4:23pm

Reply