Entropy's picture

LinkSphere

LinkSphere is my current project.

LinkSphere is a shmup/RTS/music game.

LinkSphere is something like a cross between Rez, Darwinia and Gate 88.

Ideally, it will be playable on-line, and customiseable for music and other aspects of the game.

A friend of mine is handling the actual music - hopefully, while the rest of his course mates are handing in their soundtrack pieces on CD's this coming May, he'll be handing in a (probably single-player) LinkSphere Alpha release and saying "Play this!"

I'm not releasing source code at the moment for a number of reasons -- not least that it's embarrassingly messy and probably follows poor C# conventions (should I really be using so many public static classes?) -- but see the screens below.

At the moment, a separate thread plays a steady "thump, thump, thump, thump" at 120 BPM, with off-beat hiihats playing when the Avatar moves. Eventually, I'd like every entity in the game to play part of a tune when they are close enough to the Avatar, mixing together along with the sound effects to make the music. Most movements and some of the Avatar's more intricate movements will happen on the beat, so the player should feel like they are playing "to the beat".

For on-the-fly music generation, I obviously need a pretty accurate timing thread, so I'll have to be careful to optimise the code. If anyone's feeling helpful, do the have advice on how to keep accurate timing? Obviously, Thread.Sleep(...) isn't the most accurate, and in my experiments is liable to throw everything out of sync if for some reason the thread executes a moment too late. I keep reading that DateTime.Now.Ticks is reportedly inaccurate and slow to call. I'm working on a possible solution right now, which I'll post tonight if I can, as I'd like some feedback on how well people think it'll work.

Edit: Screens (apologies for the glitching - looks like "Print Screen" is capturing the image in mid-refresh)


AttachmentSize
LinkSphere1.jpg32.47 KB
LinkSphere2.jpg27.61 KB
LinkSphere4.jpg52.56 KB

Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
objarni's picture

Cool project!

About music playback - while I'm no expert I do know that traditionally mod-players and I guess modern mp3-decoders do not really work by doing thread sleep or anything such. They simply fill a buffer every now and then with "music to come". That is, they know how long each "frame" (pair of amplitudes for stereo sound) is in milliseconds, and how fast the music should be played, and then do some kind of filter/interpolation to fill the audio buffer as much as possible, following whatever logic the music has (discrete sample playback in the mod case, decoding in the mp3 case).

For example, this mixing can be performed every 10th frame or something (the buffer can be eg. 4 kb long).

Notice that this situation is quite different from that of realtime sample (eg. explosion) playback. In that case, some kind of live mix-into the audio buffer must be performed to get fast sound response when a sample should be played.

For measuring the cpu clock ticks: use StopWatch instead of DateTime. It is much more accurate.

nythrix's picture

Messy code is often the only way to get something to work. Otherwise we'd be programming for ages.

Mincus's picture

I think you might be approaching the music sync from the wrong direction.
Maybe you should be syncing the game world FROM the music, not TO the music, that is using your music decoder to indicate when things should happen in the game world.

Looks good though!
As to the screenshots: take a look at some other applications for grabbing them, just hitting print screen isn't such a good idea where OpenGL is used in my experience.

Entropy's picture

@Mincus: Yeah, sorry, that's intention (don't think I explined it very well). I tried playing music un the game loop itself, but at 30 fps, it obviously wasn't keeping time at all. The Music thread alters a public static boolean field which tells the game loop to call the on-beat events.

@objarni: Thanks for your praise and advice. I was lokoing for System.Diagnostic.Stopwatch the other day and couldn't find it (I assumed that class wasn't supported in Mono for whatever reason) but I had a brainwave after reading your post and realised I hadn't added the required references (d'oh).

I was just about to get to work devising some clever three-thread setup with one Ticker thread, one thread watching it and timing every 4096 ticks using DateTime.Now.Ticks or something (shifting bits rather than dividing, for speed), while the third played the music using the timing of the first two. I figured if I minimised Date.Time calls, it might be more accurate. Now I'm kicking myself for not thinking to add the correct reference... :-p

The way I've discussed doing this with my friend is that I'll probably just play pre-buffered samples (rendered from his sequencer) on top of each other, much like a regular game. I'll look into buffering if this proves problematic somehow.

I originally wanted to synthesize the music on-the-fly based on some MIDI files or similar, which would allow for some pretty cool effects if you can change the synth settings based on game events, but time (this new deadline), system resources (it would be quite CPU-intensive) and common sense conspired against me. One day I'll work on that project, but not as part of this one, I think (hell, I need to place my limits somewhere).

I think my Music thread will have to be a simple high-priority loop which does nothing until the Stopwatch ticks reach the target number, at which point it triggers the on-beat events and changes a global variable to let the Game loop know. I'm not very experienced with multiple threading, but static classes/fields are thread-safe, right? It's not very CPU-efficient, but I think I need to steal a lot of processing time if I' going to make it work.

Mincus's picture

Static classes/fields are only thread-safe if they don't change data external to them (as I understand it).
Take a look at Monitor.Enter() / Monitor.TryEnter() / Monitor.Exit() (using System.Threading). Thread safety is (imo) a lot easier than many claim. Just takes a little thinking ahead over which variables require multi-thread access, then grouping them if you have a collection that will all be accessed in the same bit of code and using a lock for each group.
Depending on what you're doing exactly, TryEnter can be more useful than Enter. It will attempt a lock and return true/false depending on whether it acheived it. It also has a timeout (although I'm unsure as to how accurate that is). It's also useful for debugging if you start missing lots of locks you don't think you should be, try using TryEnter() with a high timeout value (100ms say) and kick out an error to the debug window / console if that fails to lock.

Bear in mind I'm completely self-taught when it comes to multi-threading, so I've no idea whether other people use things like this, but I've not had a problem using it like this in C# or with C & SDL.

Entropy's picture

Well I think my method should be thread-safe, given that I'm only changing the value of one external field from the separate Music thread. That field will be looked at by the game thread, and changed back, but that's as close as the two threads will ever come to clashing.

The only possible hiccup here would occur if the game loop took more than say a quarter-beat (or whatever the smallest increment will be) to complete for some reason - if the game dropped to less than approximately 8-10fps or so, at 120 BPM. At that speed, the music thread would change the static field faster that it was being read by the Game thread, which would result in the events not being triggered.

I'll think about it, but I don't want to fire those events directly from the music thread, as I need to keep that one as minimal as possible if I want it to accurately play the sounds on time.

Inertia's picture

Having problems imagining what the final gameplay and goals of the game are like.

I've downloaded the Darwinia demo to take a look, but I don't really get the connection between your screenshots (which imply that the red prism is a player controlled entity) and Darwinia, besides the wireframe looks. Care to explain? :)

Entropy's picture

Link Rez and Darwinia, LinkSphere takes place in cyberspace.

Gameplay takes place around a network of Spheres (also known as Systems), which are connected at specific Uplink Nodes. The player takes the part of a hacker, controlling their Avatar, which flies around roughly like an aircraft (though there are plenty of intricacies in the movement of it which would be impossible for an aircraft). The avatar can build RTS-like units (in game, "InterCeptors", maybe) which are, for the most part, fully automated and also customisable for size/speed/hit points). They can build structures ("Static Modules" - generally analogous to standard RTS structures). The Avatar is also the players' primary form of offence, as units can't leave the Sphere that currently supports them unless the target one is capable of supporting them (that's where the shmup element comes in).

The RTS-style resource is Runtime. Avatars create Worms, which crawl over the surface of the Sphere, claiming territory vertex by vertex (in-game lingo: "node by node"). Claimed territory is the player's colour, and generates Runtime. Runtime is consumed producing units and supporting them (which is why a players units are often entirely defensive). It acts like you'd expect too - you produce units faster if you have more free Runtime (though I'll have to work carefully to balance that for gameplay).

As mentioned above, mobile units are customiseable in a number of ways. More powerful units, however, require more Runtime to support. I'd also like the player to be albe to cusomise their behaviour, to a lesser extent. The upshot of all this is that when the player is is building defences, they are basically creating shoot-'em-up scenarios for the other player, then taking off on offensive forays into enemy-owned territory, playing the scenarios that have been created for them.

Winner is most likely the first to claim a given fraction of the total Runtime in the Network (75%, maybe -- but that kind of figure is the last thing that goes in, and only gets finalised after extensive playtesting).

In general, the gameplay is analogous to Gate 88, but combat will take place more like Rez, but with more control over the Avatar. I named Darwinia for the aesthetic (which is obviously still in very early stages), and the RTS element. There are different camera positions depending on whether you're in combat, or managing your defences.

Like Rez, it all happens to a tune, though I want to give the gameplay a certain rhythm to it as well. Defensive units change position every couple of beats, resting in between, so the player targets them in between. Certain key combinations will perform useful movents, which might wait until the beat to execute. This shouldn't be frustrating if enemy attacks happen to the beat as well, and at 120 BPM (picked for the fast human heartbeat tempo - I'm hoping to get the adrenaline pumping) the beats are reasonably fast. You dodge while they move.

If it's not an eyesore, colours will pulse and entities will throb to the beat.

Yes, I know it's ambitious. But you won't get far if you don't aim high, right? :-)

Entropy's picture

@mincus: Okay, I did some reading, and I guess my method ISN'T thread-safe without locking. Thanks for your pointers.

Inertia's picture

Thanks for the explanation, makes sense now. Looking forward to give the demo a try :)