Coding
Product
Prototype
Physical Computing

C-UBI

C-UBI is a game involving two or more players. Born as a reinterpretation of the classic hide-and-seek, it is based on small cubes and integrates sound as a means of support to the finding.
Project purpose
University
Time
(weeks)
1
Delivery date
January 2022
Authors
Alice Brovelli, Matteo Subet
Role
Developer & Product Designer
Activities
My role during this project, developed together with my colleague, was to follow the source code development process and the prototyping of the final product. Specifically, I developed the code to make the two products work and communicate using two Arduino Nano RP2040 Connect with Micropython. In addition, I followed the prototyping phase started by my colleague.
No items found.

/ Abstract
C-UBI is a game involving two or more players. Born as a reinterpretation of the classic hide-and-seek, it is based on small cubes and integrates sound as a means of support to the finding. One player hides one cube, the other player/s must find it following the sound that the hidden cube emits. The game is timed, whoever finds the hidden cube first wins. In case time runs out before the cube is found, the hidden cube will emit a constant sound so that it can be found easily.

/ Interface and Interaction Modality
Users are guided in the use of the game by to two displays, one for each cube, which progressively give instructions to them. Once the main cube is hidden, players must make use of both sight and hearing to find the hidden cube. In the main cube, in fact, a buzzer is inserted, which progressively emits "beep" sounds, closer and closer together over time. The player's display, once the game has started, shows the countdown within which players can find the hidden cube. If no player finds the cube, the cube itself will emit a constant sound so that it will be easily found.

/ User Experience
C-UBI is a multi-player game that, using two or more cubes, engage the senses of both sight and hearing to find the hidden cube. There is only one switch on the cubes to turn them on. Information and countdown are presented to users through two displays, one per each cube. All other dynamics are activated by the players through by moving closer or apart the cubes. C-UBI has in fact a system of magnetic sensors that ensure that the different phases of the game are activated following the user's action. Intuitive drawings on one side of the cubes make it easy for players to understand how to trigger the game.

/ Research and Development Context
The prototype has some features dependent on functional needs of the electronic elements used. Imagining to bypass these limitations, possible developments of the game could include:
* the reduction of the size of the object;
* the resizing of the display so as to have a larger area to be dedicated to texts.
C-UBI could also be made entirely of a colored material, maintaining the cube shape but softening some of the edges so as to make the objects more usable by younger children.

/ Source Code (MicroPython)



#----------------IMPORT FROM LIBRARY----------------
from machine import I2C, Pin
from time import sleep_ms
import sh1107

#----------------SET HARDWARES----------------
#Setting OLED
DISPLAY_WIDTH = 128
DISPLAY_HEIGHT = 128
i2cbus = I2C(0)
oled = sh1107.SH1107_I2C(DISPLAY_WIDTH, DISPLAY_HEIGHT, i2cbus)
#Setting magnet
magnet = Pin(26, Pin.IN)

#----------------DEFINING VARIABLES----------------
#Game State:
    # 0 = Intro
    # 1 = On game
    # 2 = Gameover
    # 3 = Reset
game_state = 0
#Gameover cause:
    # 0 = No reason
    # 1 = Countdown
    # 2 = Magnet
gameover = 0
#Check the initial state of magnet
player_check = 0
#During game state 1 measures the time passed
on_game_time = 0
#During game state 1 defines the timing
deadline_game = 60 * 2 #sec

#----------------DEFINING FUNCTIONS----------------
#Instructions to start
def to_start_txt():
    #to clear the screen use .fill(0) for black and .fill(1) for white
    oled.fill(0)
    #to show the changes you have to call the .show() propriety
    oled.show()
    #.text() allow a maximun of 16 characters per row
    #.text('Content',xCoord, yCoord: lineSpacing*lineNumber, white(0)/black(1))
    oled.text('To start playing', 0, 12*4, 1)
    oled.text('place the cubes', 0, 12*5, 1)
    oled.text('sides near.', 0, 12*6, 1)
    oled.show()
    #this sleep is needed to decrease the flickering caused by the screen refresh rate conbined with a while function
    sleep_ms(800)
    return

#Instructions text
def instr_one():
    oled.text('Follow the sound', 0, 12*3, 1)
    oled.text('to find the', 0, 12*4, 1)
    oled.text('hidden cube', 0, 12*5, 1)
    oled.text('before the', 0, 12*6, 1)
    oled.text('countdown ends.', 0, 12*7, 1)
    oled.show()
    return

#Countdown to hide the box
def countdown_hide(countdown_sec):
    #this function contain a variable countdown_sec to control in an easy way the timing to hide the cube
    #this text formatting (f'Text {varibale}') is good to merge variables and string in an easy way
    oled.text(f'Wait {countdown_sec} sec and', 0, 12*4, 1)
    oled.show()
    oled.text('start looking', 0, 12*5, 1)
    oled.show()
    oled.text('for the cube', 0, 12*6, 1)
    oled.show()
    #Here we initialize the countdown's variable
    i = 0
    while i<=countdown_sec:
        oled.fill(0)
        oled.show()
        oled.text(f'Wait {countdown_sec - i} sec and', 0, 12*4, 1)
        oled.show()
        oled.text('start looking.', 0, 12*5, 1)
        oled.show()
        oled.text('for the cube', 0, 12*6, 1)
        oled.show()
        #The sleep timer here is setted to 1000 ms (1 sec) because we need to increase the timer previously created by one per each sec
        sleep_ms(1000)
        i += 1
    return

#Text display if you lose
def you_lose():
    oled.fill(0)
    oled.show()
    oled.text('Time is over', 0, 12*4, 1)
    oled.text('you lose', 0, 12*5, 1)
    oled.text('the game, NOOB!', 0, 12*6, 1)
    oled.show()
    sleep_ms(800)
    return

#Text display if you win
def you_win():
    oled.fill(0)
    oled.show()
    oled.text('YOU FOUND', 0, 12*4, 1)
    oled.text('THE CUBE!', 0, 12*5, 1)
    oled.text('PRO PLAYER', 0, 12*6, 1)
    oled.show()
    sleep_ms(800)
    return

#Reset function & text display during the reset
def reset_message():
    global restart_countdown
    oled.fill(0)
    oled.show()
    oled.text('To restart the', 0, 12*4, 1)
    oled.text(f'game split', 0, 12*5, 1)
    oled.text(f'the cubes...', 0, 12*6, 1)
    oled.show()
    sleep_ms(800)
    return

#Looping function
def game():
    global game_state, player_check, on_game_time, deadline_game, gameover
    
    if game_state == 0:
        #INTRO
        if magnet.value() == 0:
            #The two cubes are near so we can print the instructions
            player_check = 1
        elif magnet.value() == 1 and player_check == 0:
            #The two cubes are not connected so we have to wait until the magnet trigger the sensor
            to_start_txt()
        
        if magnet.value() == 0 and player_check == 1:
            #The two cubes are near and we can print the instructions
            player_check = 2
            oled.fill(0)
            oled.show()
            instr_one()
            sleep_ms(1000)
            return
        elif magnet.value() == 1 and player_check == 2:
            #The cubes are disconnected and we can start to count down and hide the box
            oled.fill(0)
            oled.show()
            countdown_hide(30)
            #Inside the countdown_hide function you can choose how many second(s) you want to wait to hide the box
            game_state = 1
            return
    
    if game_state == 1:
        #ON GAME
        if magnet.value() == 1:
            #Magnet is detached, we are on game
            if on_game_time < deadline_game:
                #You can continue game
                oled.fill(0)
                oled.show()
                oled.text(f'Find the hidden', 0, 12*4, 1)
                oled.text(f'cube in {deadline_game - on_game_time} sec', 0, 12*5,1)
                oled.show()
                sleep_ms(900)
                on_game_time += 1
            else:
                #Time is over
                gameover = 1
                oled.fill(0)
                oled.show()
                game_state = 2
        else:
            #Magnet is attached, is gameover state because the player found the cube
            oled.fill(0)
            oled.show()
            oled.text(f'You found the cube', 0, 12*4, 1)
            oled.text(f'in {deadline_game - on_game_time} secs', 0, 12*5,1)
            oled.show()
            sleep_ms(900)
            gameover = 2
            oled.fill(0)
            oled.show()
            game_state = 2
    
    if game_state == 2:
        #GAMEOVER
        if gameover == 1:
            #Gameover because of time
            if magnet.value() == 1:
                you_lose()
                sleep_ms(800)
            else:
                oled.fill(0)
                oled.show()
                game_state = 3
        if gameover == 2:
            #Gameover because of magnet
            you_win()
            sleep_ms(5000)
            oled.fill(0)
            oled.show()
            game_state = 3
            
    if game_state == 3:
        #RESET STATE
        if magnet.value() == 1:
            #The two cubes are detached so we can restart the game
            player_check = 0
            gameover = 0
            on_game_time = 0
            game_state = 0
        else:
            #We ask to the players to detach the two cubes to continue
            reset_message()
    return

#----------------OUTPUT CODE----------------
while(1):
    game()



#----------------IMPORT FROM LIBRARY----------------
from machine import I2C, Pin
from machine import I2C, Pin
from time import sleep_ms
import sh1107

#----------------SET HARDWARES----------------
#Setting OLED
DISPLAY_WIDTH = 128
DISPLAY_HEIGHT = 128
i2cbus = I2C(0)
oled = sh1107.SH1107_I2C(DISPLAY_WIDTH, DISPLAY_HEIGHT, i2cbus)
#Setting magnet
magnet = Pin(26, Pin.IN)
#Setting buzzer
buz = Pin(17, Pin.OUT)

#----------------DEFINING VARIABLES----------------
#Game State:
    # 0 = Intro
    # 1 = On game
    # 2 = Gameover
    # 3 = Reset
game_state = 0
#Gameover cause:
    # 0 = No reason
    # 1 = countdown
    # 2 = magnet
gameover = 0
#Check the initial state of magnet
player_check = 0
#During game state 1 measures the time passed
on_game_time = 0
#During game state 1 defines the timing
deadline_game = 60 * 2 #sec
#Check how many time(s) the beep is emitted for every state
already_sound_1 = 0
already_sound_2 = 0
already_sound_3 = 0
already_sound_4 = 0

#----------------DEFINING FUNCTIONS----------------
#Instructions to start
def to_start_txt():
    #to clear the screen use .fill(0) for black and .fill(1) for white
    oled.fill(0)
    #to show the changes you have to call the .show() propriety
    oled.show()
    #.text() allow a maximun of 16 characters per row
    #.text('Content',xCoord, yCoord: lineSpacing*lineNumber, white(0)/black(1))
    oled.text('To start playing', 0, 12*4, 1)
    oled.text('place the cubes', 0, 12*5, 1)
    oled.text('sides near.', 0, 12*6, 1)
    oled.show()
    #this sleep is needed to decrease the flickering caused by the screen refresh rate conbined with a while function
    sleep_ms(800)
    return

#Instructions text
def instr_one():
    oled.text('Follow the sound', 0, 12*3, 1)
    oled.text('to find the', 0, 12*4, 1)
    oled.text('hidden cube', 0, 12*5, 1)
    oled.text('before the', 0, 12*6, 1)
    oled.text('countdown ends.', 0, 12*7, 1)
    oled.show()
    return

#Countdown to hide the box
def countdown_hide(countdown_sec):
    #this function contain a variable countdown_sec to control in a easy way the timing to hide the cube
    #this text formatting (f'Text {varibale}') is good to merge variables and string in easy way
    oled.text(f'You have {countdown_sec} sec', 0, 12*3, 1)
    oled.show()
    oled.text('to hide this cube.', 0, 12*4, 1)
    oled.show()
    #Here we set the countdown's variable to hide the cube
    i = 0
    while i<=countdown_sec:
        oled.fill(0)
        oled.show()
        oled.text(f'You have {countdown_sec - i} sec', 0, 12*4, 1)
        oled.show()
        oled.text('to hide the cube.', 0, 12*5, 1)
        oled.show()
        #The sleep timer here is setted to 1000 ms (1 sec) because we need to decrease the timer previously created by one per each sec
        sleep_ms(1000)
        i += 1
    return

#Buzzer loop during the game
def countdown_sound(sec):
    global already_sound_1, already_sound_2, already_sound_3, already_sound_4
    #The logic consist in 4 different states based on the time, the difference between them is how many sound we want to generate per second
    if sec=int(deadline_game/5) and sec=int(deadline_game/2) and sec=int(deadline_game/1.2):
        if already_sound_4%1 == 0 or already_sound_4 == 0:
            buz.on()
            sleep_ms(100)
            buz.off()
        already_sound_4 += 1
    return

#Text display if you lose
def you_lose():
    oled.fill(0)
    oled.show()
    oled.text('Time is over', 0, 12*4, 1)
    oled.text('you lose', 0, 12*5, 1)
    oled.text('the game, NOOB!', 0, 12*6, 1)
    oled.show()
    sleep_ms(800)
    return

#Text display if you win
def you_win():
    oled.fill(0)
    oled.show()
    oled.text('GOOD JOB', 0, 12*4, 1)
    oled.text('You found me', 0, 12*5, 1)
    oled.text(f'in {deadline_game - on_game_time} sec', 0, 12*6, 1)
    oled.show()
    sleep_ms(800)
    return

#Reset function & text display during the reset
def reset_message():
    global restart_countdown
    oled.fill(0)
    oled.show()
    oled.text('To restart the', 0, 12*4, 1)
    oled.text(f'game split', 0, 12*5, 1)
    oled.text(f'the cubes...', 0, 12*6, 1)
    oled.show()
    sleep_ms(800)
    return

#Looping function
def game():
    global game_state, player_check, on_game_time, deadline_game, gameover
    
    if game_state == 0:
        #INTRO
        if magnet.value() == 0:
            #The two cubes are near so we can print the instructions
            player_check = 1
        elif magnet.value() == 1 and player_check == 0:
            #The two cubes are not connected so we have to wait until the magnet trigger the sensor
            to_start_txt()
        
        if magnet.value() == 0 and player_check == 1:
            #The two cubes are near and we can print the instructions
            player_check = 2
            oled.fill(0)
            oled.show()
            instr_one()
            sleep_ms(1000)
            return
        elif magnet.value() == 1 and player_check == 2:
            #The cubes are disconnected and we can start to count down and hide the box
            oled.fill(0)
            oled.show()
            countdown_hide(30)
            #Inside the countdown_hide function you can choose how many second(s) you want to wait to hide the box
            game_state = 1
            return
    
    if game_state == 1:
        #ON GAME
        if magnet.value() == 1:
            #Magnet is detached, we are on game
            if on_game_time < deadline_game:
                #You can continue game
                oled.fill(0)
                oled.show()
                oled.text(f'Game ends in', 0, 12*4, 1)
                oled.text(f'{deadline_game - on_game_time} sec', 0, 12*5,1)
                oled.show()
                countdown_sound(on_game_time)
                sleep_ms(900)
                on_game_time += 1
            else:
                #Time is over
                gameover = 1
                oled.fill(0)
                oled.show()
                game_state = 2
        else:
            #Magnet is attached, is gameover state because the player found the cube
            gameover = 2
            oled.fill(0)
            oled.show()
            game_state = 2
    
    if game_state == 2:
        #GAMEOVER
        if gameover == 1:
            #Gameover because of time
            if magnet.value() == 1:
                buz.on()
                you_lose()
                sleep_ms(800)
            else:
                buz.off()
                oled.fill(0)
                oled.show()
                game_state = 3
        if gameover == 2:
            #Gameover because of magnet
            you_win()
            sleep_ms(5000)
            oled.fill(0)
            oled.show()
            game_state = 3
            
    if game_state == 3:
        #RESET STATE
        if magnet.value() == 1:
            #The two cubes are detached so we can restart the game
            player_check = 0
            already_sound_1 = 0
            already_sound_2 = 0
            already_sound_3 = 0
            already_sound_4 = 0
            gameover = 0
            on_game_time = 0
            game_state = 0
        else:
            #We ask to the players to detach the two cubes to continue
            reset_message()
    return

#----------------OUTPUT CODE----------------
while(1):
    game()
    
 
Code
License
CC BY-NC