RhythmPG Mac OS

Find Rhythm games for macOS like Friday Night Funkin', Rhythm Doctor, Lo-Fi Room, Friday Night Funkin' (Ludum Dare Prototype), Songbird Symphony v0.2 on itch.io, the indie game hosting marketplace. A fun game that will help students of all ages learn how to read some basic music rhythms. 60 entertaining levels that get progressively more challenging. This game has been developed in collaboration with professional musicians and educators.

Fumiaki Kinoshita (part-time employee of IIJ-II) fumiexcel@gmail.com

日本語版 is available. Thank you, @stefafafan!

A library of over 125,000 free and free-to-try software applications for Mac OS.

RhythmPG

Introduction

Rhythm games, also known as rhythm actions, are very popular genre in Japan. Konami released Dance Dance Revolution (DDR) in 1998 -- it is the best successful game among the genre. Another famous one, Taiko no Tatsujin(literally, Expert of Japanese drum) is being immensely supported by broad age-group of people. Today, various forms of rhythm games have been released one after another.

However, there are few tutorials to create such kind of games. Even if there are, they might be too old, or written in non-English, or/and work only in Windows.

This tutorial focuses on creating a rhythm game without pain. Don't be surprised, we use Haskell to do that.

This tutorial has two parts.

  • Part I contains an instruction to set up environment required for Part II and III.
  • In Part II, we build a very simple rhythm game. We use the Call engine to develop.
  • Part III introduces some technical backgrounds (graphics, audio) that support Part II.

I'd be happy if this tutorial helps your curiosity to create a game.

Part I: Preparation

We need to ensure that you have installed GHC. Haskell Platform is an easy way to install GHC.

On Unix or Mac, install libportaudio19.

Note: Currently Call doesn't draw bitmaps well on Mac OS X. Please help me figure out what goes wrong.

The source code used in this tutorial is packed in rhythm-game-tutorial package. You can download it and set it up by:

cabal install --only-dependencies installs a bunch of packages, including two vital packages: objective and call.

  • objective establishes abstraction for stateful objects. It is not neccessary strictly, though it kills the pain of state significantly.
  • call is a cross-platform multimedia library. While it is small and simple, the essentials of games (2D/3D graphics, audio, input handing from keyboard, mouse and gamepad) is assurable.
  • binding-portaudio is low-level audio APIs.

On windows

bindings-portaudio provides built-in source for installation ease. Unfortunately, due to a GHC bug, it is sometimes unstable. Note that using 32-bit version of GHC is safer to avoid problems if your platform is Windows x64.

If it throws up something messy, please report to me.

Part II: Creating a game

Here we bang! -- Wada-don, 'Taiko no Tatsujin'

Now, think of a very simple game: There's a circle at the bottom of the window, and another circle(s) is approaching. You hit the space key in exact timing when the another circle overlapped the original one.

How do we implement this? The structure of the program can be derived by writing components down:

  • Sound: a music is playing through the game.
  • Graphics: pictures depend on the time.
  • Interaction: the score changes when the player hit the space key.

We will explain these in order.

Playing a music

Groove is important. It's time to play a music. Our first game is as follows(src/music-only.hs):

Let's execute it:

Can you hear the music? Note that it takes a moment to load a music.

Let's investigate the code. The following functions are provided by Call engine.

In Call, actions are performed on System s monad. runSystemDefault converts System s into IO. stand does nothing, preventing termination of the program.

The signatures of prepareMusic and playMusic are as follows:

These functions will be defined later.

Drawing a picture

Let's construct a graphical part of the game.

linkPicture :: (Time -> System s Picture) -> System () is the only function provided by Call to actually draw something.linkPicture f repeatedly calls f and draws the result of f to the window. The argument of f is the time difference between frames, it is often negligible though.

Due to its game system, we need to prepare the set of times. Let us introduce a new notation to represent timings which is more readable than just the list of decimals.

This notation is consist of a number of packets, representing a sequence of bars. Each packets contain several lines. A bar is divided by the length of line. '.' and '-' represents a note and a rest.

The implementation of the parser is not so interesting.

Given timings and 'life span' of circles, we can compute positions of visible circles from the time.

Create a function to render circles. Since Picture is a monoid, we can use foldMap or mconcat to combine pictures. translate (V2 x y) shifts the picture into (x, y). bitmap b turns a Bitmap into a Picture.

unsafePerformIO, which has the type IO a -> a, looks really uncanny function. The use of unsafePerformIO must be limited to passive, virtually constant operations like getArgs, readBitmap, etc.

renderLane passes the result of phases into circles. color changes a color of a picture.

Here is an updated main.

There is a serious problem in this program. The graphics and music may diverge when the program has stumbled accidentally. We need to use the musical time instead of the real one.

Component: prepareMusic

A music is essential for rhythm games.

readWAVE loads a sound from .wav file.source .~ sampleSource wav $ Deck.empty is a bit tricky.

Deck is an utility to play a music. source is a Lens which is purely functional representation of accessors. new $ variable $ v instantiates a music. Regard linkAudio $ playbackOf i as a cliché for now.

Component: getPosition and playMusic

The implementation of getPosition and playMusic is as follows:

You notice two new operators: use and .=. These comes from the lens library. This package contains types and utilities to deal with various accessors.

Rhythmpg Mac Os X

pos, playing are Lens. Given Lens' s a, you can take and modify a value a from s.

use and (.=) are getting/setting operators that work on stateful monads.

With lens, we can access a specific element of a structure easily, allowing you manipulate them just like 'fields' in OOP languages. However, the state of the deck is packed in music in gameMain so these can't be used directly. The (.-) operator, provided by objective package, executes an action within a context held by a left operand.

getPosition m returns an accurate time (in seconds) elapsed from an origin of a music m.

Putting them together, we got src/tutorial-passive.hs.

It is not a game though -- simply because it has no score, no interaction.

Handling inputs

Let's deal with inputs. Now introduce two components, rate and handle.

rate calculates a score from a time lag. handle returns a score and updated timings. viewNearest :: (Num a, Ord a) => a -> Set a -> (a, Set a) is a function to pick up the nearest value from a set. If we fail to attend to remove a nearest one, flamming the button causes undesired score increment.

And the following code actually handles events:

Note that a few variables has instantiated.

After linkKeyboard is called, the engine passes keyboard events Key. Key is wrapped by Chatter to indicate that a key is pressed, or released. When the space key is pressed, it computes the time difference from the nearest timing and increment the score by accuracy.

We need to load a Font as we want to show players the current score. Call.Util.Text.simple generates a function that renders a supplied text.

Just add text (show sc) to renderGame. src/tutorial-active.hs is the updated source we made interactive. It's a game, yay!

tutorial-active

Extending the game

However, when you actually play this, you may feel dissatisfied. It is because the interaction is still poor. If it would have more showy effects, it'll be exciting. Most rhythm games shows the recent evaluation of the accuracy immediately. So, players can notice whether their playing is good or bad.

Thanks to purely functional design, we can extend lanes so easily(tutorial-extended.hs)!

ix i is a lens that points an i-th element of a list. Just arrange the result of forM using translate.

Another interesting feature, transit, is convenient to create animations.

The argument t varies from 0 to 1, for 0.5 seconds. To instantiate this, put this object into a list:

And effects .- gatherFst id (apprises (request dt)) returns Picture, removing expired animations automatically. It benefits from objective much. Here is the complete linkPicture section.

There is no difficulty around input.

Moreover, with LambdaCase GHC extension, you can replace ev -> case ev of with case.

The overall game goes in only 123 lines!

Part III: Technical background

Graphics

Monoid is the general term for composable stuff which has 'empty'. A picture is one of the monoids since there is an empty picture and pictures can be composed by overlaying. The standard library base provides a typeclass for monoids:

Call uses free monoid to represent picture.

In de-CPSed form,

Its Monoid instance is trivial.

Using free monoid, we can isolate the drawing process from Scene. Think of drawScene :: Scene -> IO () which calls concrete APIs to draw Scene. For empty picture, we don't do nothing. Combine a b is equivalent to calling drawScene a >> drawScene b.

So the implementation of drawScene will be as follows:

Rhythmpg

where drawPrimitive, applyVFX, withMatrix are environment-dependent.

In other words, free structures are kinds of DSL which encourages the reusability of programs. Andres Löh's Monads for free! is a great introduction for free structures.

Call puts together a few kinds of transformation in Affine class. Thanks to type families, we can use the same operation for both 2D and 3D. Normal is the normal vector, which is 3-dimensional vector in 3D but it is just Float in 2D.

Audio

Currently, there are few packages for audio that work in common platforms and are easy to install. I choosed portaudio for now which supports a bunch of backends. Humans are so sensitive about sound; 20 miliseconds of latency is noticable for us.

Thus, it is important to minimize latency when it comes to audio. This is the main reason of why call relies on callback. The call library aims to be small and concrete, leaving abstraction to objective.

Acknowledgements

Special thanks to Kazuhiko Yamamoto for guidance of the architecture of this tutorial.

Today is the 20th anniversary of the release of Mac OS X. I wrote a bit about it in my Macworld column this week, and also put together a little Mac OS X timeline.

I’ve written a lot about Mac OS X over the years. Compiling that timeline reminded me of that. I was a features editor at Macworld when Apple began shipping OS X precursors, and so I edited most of our early coverage. Beginning with Mac OS X 10.1, I wrote most of Macworld’s big feature stories covering each release.

I’ve lived in the same house since 1999, so I have spent many springs and summers sitting out in my yard under our redwood tree writing and editing articles about Mac OS X, OS X, and now macOS.

Rhythmpg Mac Os Download

How many? This many:

  • OS X Prehistory (compiled by me from multiple Macworld features)

Wow, that’s a lot of operating-system releases. Here’s to the next uncountable number of them.

(While I wrote shorter reviews for Macworld, John Siracusa was always reviewing OS X at length for Ars Technica. Here’s a list of all his reviews.)

If you appreciate articles like this one, support us by becoming a Six Colors subscriber. Subscribers get access to an exclusive podcast, members-only stories, and a special community.