Introducing Vibration in Quorum

In this tutorial, we will learn how to use vibration in Android apps that we write in Quorum. Vibrations are useful for providing an element of user feedback through the sense of touch, letting users know that they are doing something. For example, in this app, we will make a simple game with rectangles at each corner of the screen, which will vibrate in different ways when the user touches one of them.

Game Setup

Before we do anything with Vibrations, we need to set up our game. To start, create a new Quorum Game Application. Our main class will need to inherit both the Game and TouchListener classes. We will overload the Game's Main and CreateGame actions, as well as the TouchListener's ContinuedTouch class. Additionally, the class will need two integers as class variables for the screen's width and height, as well as a Vibration object. For now, just declare these variables, and we'll go over their usage later. Finally, we will need the libraries for TouchListener, TouchEvent, Array, Vibration, VibrationArray, Drawable, and Color, giving us the following template:

use Libraries.Game.Game
use Libraries.Interface.Events.TouchListener
use Libraries.Interface.Events.TouchEvent
use Libraries.Containers.Array
use Libraries.Vibration.Vibration
use Libraries.Vibration.VibrationArray
use Libraries.Game.Graphics.Drawable
use Libraries.Game.Graphics.Color

class Main is Game, TouchListener

    integer width = 0
    integer height = 0
    Vibration vibrator

    action Main
        StartGame()
    end

    action CreateGame
    end

    action Update(number seconds)
    end

    action ContinuedTouch(TouchEvent event)
    end

end

Now, we'll add the rectangles to our game using Drawables. There will be four rectangles, one for each corner. Each rectangle will also be given the main as a TouchListener, allowing them to call our ContinuedTouch action when touched. You can alter their sizes and positions, but for the purposes of this tutorial, we recommend that you simply copy and paste the following CreateGame action:

action CreateGame
    AddTouchListener(me)

    width = GetScreenWidth()
    height = GetScreenHeight()

    Color color

    Drawable box1
    Drawable box2
    Drawable box3
    Drawable box4

    box1:LoadFilledRectangle(width/4, height/4, color:Black())
    box2:LoadFilledRectangle(width/4, height/4, color:Black())
    box3:LoadFilledRectangle(width/4, height/4, color:Black())
    box4:LoadFilledRectangle(width/4, height/4, color:Black())

    box1:SetPosition(0, 0)
    box2:SetPosition(4*(width/5), 0)
    box3:SetPosition(0, 4*(height/5))
    box4:SetPosition(4*(width/5), 4*(height/5))

    box1:AddTouchListener(me)
    box2:AddTouchListener(me)
    box3:AddTouchListener(me)
    box4:AddTouchListener(me)

    Add(box1)
    Add(box2)
    Add(box3)
    Add(box4)
end

Adding Vibrations

Now that we have our class set up, we need to add vibrations to the ContinuedTouch action. To do this, we will start by creating a series of conditional statements for checking if the TouchEvent parameter was triggered inside of any of our rectangles. We will do this using the TouchEvent's "GetX()" and "GetY()" actions. Templated out, this gives us the following:

action ContinuedTouch(TouchEvent event)
    if (event:GetX() > 0 and event:GetX() < width/4) and (event:GetY() > 0 and event:GetY() < height/4)
        // while touching within the area of box1

    elseif (event:GetX() > 3*width/4 and event:GetX() < width) and (event:GetY() > 0 and event:GetY() < height/4)
        // while touching within the area of box2

    elseif (event:GetX() > 0 and event:GetX() < width/4) and (event:GetY() > 3*height/4 and event:GetY() < height)
        // while touching within the area of box3

    elseif (event:GetX() > 3*width/4 and event:GetX() < width) and (event:GetY() > 3*height/4 and event:GetY() < height)
        // while touching within the area of box4

    else
        // while touching outside the area of any rectangle

    end
end

Preset Vibrations

In Quorum, the simplest way to use vibration is to call one of the preset vibrations. These presets are Vibrate, QuickPulse, SlowPulse, and Rumble. Each of these actions requires you to provide the number of seconds to vibrate, or you can cause them to vibrate continuously by appending the word "Forever" to the action call, which removes the need for a specified duration. As an example, the following code adds a continuous vibration to the first drawable, which is in the bottom-left corner of our application:

if (event:GetX() > 0 and event:GetX() < width/4) and (event:GetY() > 0 and event:GetY() < height/4)
    // while touching within the area of box1

    vibrator:VibrateForever()

There is a fifth preset vibration, Knock, which is based on a pattern with varying wait durations and vibrate durations. As such, rather than repeating for a specified number of seconds, the Knock preset repeats for a specified number of repetitions. The Knock preset has two alternative actions, KnockOnce and KnockForever, neither of which have parameters. For the purposes of this tutorial, the Knock preset is close enough to the other presets that we won't add it to our game, but feel free to experiment with the other presets by replacing the VibrateForever call in the code we wrote above.

Frequency Vibrations

The Quorum Programming Language also allows you to create Vibrations at a given frequency, measured in Hertz (Hz), which represents the number of cycles to vibrate per second, each cycle consisting of a period of wait time and a period of vibration time. For example, at a frequency of 1 Hz, a cycle will vibrate for half a second, then wait for half a second.

To provide a frequency for vibration, we will call the "VibrateAtFrequency" action, which accepts the number of seconds to vibrate and a second number for our frequency. To repeat the vibration until canceled, the VibrateAtFrequencyForever action is used, which only requires one number for the frequency.

Returning to our project for this tutorial, the following code is an example of a frequency vibration, adding a 2.5 Hertz vibration to the second drawable, in the bottom-right corner of the application:

elseif (event:GetX() > 3*width/4 and event:GetX() < width) and (event:GetY() > 0 and event:GetY() < height/4)
    // while touching within the area of box2

    vibrator:VibrateAtFrequencyForever(2.5)

Note that, depending on the motor in your device, the maximum frequency may change. In general, we recommend against using frequencies higher than 50 Hz, as some motors start skipping slightly, providing inconsistencies in the vibration. Additionally, it becomes increasingly difficult to distinguish between vibrations at higher frequencies, so many people will not feel a noticeable difference between, for instance, 50 Hz and 75 Hz. On the contrary, most people will be able to distinguish between 50 Hz and 25 Hz.

As a final note on frequency vibrations, keep in mind that durations are approximate. This is because frequency vibrations create cycles of wait times and run times to work properly, so there may be some rounding involved while the action is determining how many total cycles will fit into the specified duration. In most cases, this approximation is negligible, but it may be noticeable at very low frequencies or very low durations. Generally, higher frequencies or durations will have a smaller margin of error.

Intensity Vibrations

In addition to frequency, Quorum also allows programmers to create Vibrations at a specified intensity, a number between 0.0 and 1.0, representing a percentage of the maximum vibration intensity of a device. This is done by providing the Vibrate action with an additional number: the first is still the duration in seconds, and the second is the intensity. For a continuous intensity vibration, provide an intensity for the VibrateForever action.

Returning to our project for this tutorial, the following code is an example of an intensity vibration, adding a half-intensity vibration to the third drawable, which is in the top-left corner of the application:

elseif (event:GetX() > 0 and event:GetX() < width/4) and (event:GetY() > 3*height/4 and event:GetY() < height)
    // while touching within the area of box3

    vibrator:VibrateForever(0.5)

Using VibrationArrays

For the most direct control over vibrations, we use VibrationArrays. The VibrationArray is very similar to the normal Quorum Array, but it is specifically designed to store vibration patterns of varying durations and intensities. Each item in a VibrationArray holds a duration, measured in seconds, and an intensity between 0.0 and 1.0. To insert a new item, we use the Add action with either one or two numbers: the first is the number of seconds to vibrate, and the second, if provided, is the intensity. If an intensity is not provided, it will default to 1.0, or maximum intensity. Meanwhile, to retrieve a duration or intensity, we use the GetDuration and GetIntensity actions, respectively, which both require the index or location where the item is stored.

Additionally, the VibrationArray also contains Remove, RemoveAll, and GetSize actions, which function identically to normal Arrays: the Remove action deletes an item at the specified index or location, the RemoveAll action clears the VibrationArray of all items, and the GetSize action returns the number of items stored in the VibrationArray.

Once you have finished adding items to a VibrationArray, you can play it by using the VibrationArray as an argument. Like the Knock preset, there are three actions for vibrating with a VibrationArray: VibrateOnce, Vibrate, and VibrateForever. The VibrateOnce action only requires the VibrationArray, and will play through the pattern one time. The Vibrate action requires the VibrationArray and an integer for the amount of times you want the pattern to repeat. The VibrateForever action requires only the VibrationArray and will play it until canceled.

Returning to our project for this tutorial, let's use a VibrationArray for box4, which is in the top-right corner of the screen. Here is an example:

elseif (event:GetX() > 3*width/4 and event:GetX() < width) and (event:GetY() > 3*height/4 and event:GetY() < height)
    // while touching within the area of box4

    VibrationArray array

    array:Add(0.25)
    array:Add(0.5, 0)
    array:Add(0.5, 0.5)
    array:Add(1.5, 0)

    vibrator:VibrateForever(array)

Canceling Vibrations

In the previous sections, all of our example vibrations repeat indefinitely. While they can be overwritten by triggering another vibration by touching another rectangle, we currently don't have a way to cancel an ongoing vibration. Fortunately, vibrations can easily be canceled in Quorum by calling the Vibration's "Stop" action. Let's use the final else statement to cancel any existing vibration if the user touches an area with no rectangle, giving us the following code:

else
    // while touching within the area of box4

    vibrator:Stop()
end

With that, our ContinuedTouch action is complete. Using the examples we provided above, the resulting action should be like this:

action ContinuedTouch(TouchEvent event)
    if (event:GetX() > 0 and event:GetX() < width/4) and (event:GetY() > 0 and event:GetY() < height/4)
        // while touching within the area of box1

        logger:Log("Box1 Touched", "Main Class")
        vibrator:VibrateForever()
    elseif (event:GetX() > 3*width/4 and event:GetX() < width) and (event:GetY() > 0 and event:GetY() < height/4)
        // while touching within the area of box2

        vibrator:VibrateAtFrequencyForever(2.5)
    elseif (event:GetX() > 0 and event:GetX() < width/4) and (event:GetY() > 3*height/4 and event:GetY() < height)
        // while touching within the area of box3

        vibrator:VibrateForever(0.5)
    elseif (event:GetX() > 3*width/4 and event:GetX() < width) and (event:GetY() > 3*height/4 and event:GetY() < height)
        // while touching within the area of box4

        VibrationArray array

        array:Add(0.25)
        array:Add(0.5, 0)
        array:Add(0.5, 0.5)
        array:Add(1.5, 0)

        vibrator:VibrateForever(array)
    else
        // while touching outside the area of any rectangles

        vibrator:Stop()
    end
end

Now that our application is complete, we can test it by sending it to an Android Application, as demonstrated in the Android Mobile Support tutorial. Recall that the application will be named "Default," and it will overwrite applications by the same name.