Physical Version Parkour

Physical Version Parkour

Guess Physical version of Flappy Bird, an international hit game, remains lively remembrance to everyone. Today, we’re gonna share with you a similar physical little game: physical version parkour. Let’s proceed right into the business by firstly referring to the APP. We’ll show you how to implement the whole physical game, hope to get your precious suggestions for v2.0 improvement. Our vision is to make a simple-enough-to-assemble development kit.

Parcour APP, flappy bird  

Here comes the base frame and the roller designed by our 3D printer. But it looks quite hilarious that we were unable to find the perfect polish rod, so we use a 2B pencil instead for the moment.

Physical Parkour Frame, physical version parkour, ElecFreaks parkour   Physical Parkour Frame, physical version parkour, ElecFreaks parkour 

The central point of this APP game is to get users control two characters bounces simultaneously, the number of characters increases with the growth in difficulty, maybe four or five. Our prototype v1.0 is currently one character available, but the frame of v2.0 is under designing, ideally should realize multiple game characters bounces and, moreover, allow two gamers to battle against each other. Unfortunately, the 3D design file is still in lock because we’re accelerating the process of v2.0 development and v1.0 graph is covered. But no worry, it’ll be uncovered soon. Just take a glance at v1.0.

The Game

This is a parkour game. There is a matchstick man (sliding block) and a button, you should control the matchstick man to hurdle over obstacles (black line) by using the button, it’s game over if the matchstick man hit any obstacle.

How to Play

  1. Power up, it will display “press key” on LCD1602, then press the key, it’s has two second countdown then game starts;
  2. When LCD1602 displays “Go” on the first line and your game time length on the second line, you’re in the game. The matchstick man will jump when you press the key, which does not support long press, in other words, even though you long press the key, the matchstick just jumps once;
  3. Game over if the matchstick man hits an obstacle. LCD1602 will display “Game over” on the first line and the time length on the second line.

DIY

  1. Prepare: L298 motor driver (you can also use other motor driver), infrared emitting diode and phototransistor module, LCD1602, button, servo, motor, wheel, conveyor(paper), crabstick, UNO and so on;
  2. Assemble:

You can use a 3D print model, then assemble it like this:

physical version of parkour, physical parkour, ElecFreaks parkour   physical version of parkour, physical parkour, ElecFreaks parkour

3. Connect with MCU 3.1 Servo It requires a pulse whose cycle is 20ms to control servo, and high level time is 0.5ms-2.5ms, there is a 180 degree servo as an example, the control map is: 0.5ms--------------0 degree; 1.0ms------------45 degree; 1.5ms------------90 degree; 2.0ms-----------135 degree; 2.5ms-----------180 degree. 3.2 Infrared Emitting Diode and Phototransistor Module physical parkour diagram, Infrared Emitting Diode, Phototransistor Module physical parkour diagram, Infrared Emitting Diode, Phototransistor Module

A small current goes through Q1 when the reflector is black, and the voltage of the second pin of LM358 is close to power voltage, and larger than the reference voltage of  the third pin of LM358.LM358 outputs a low level on the first pin and vice versa. 4. Reference Code We have used two libraries: Servo and LiquidCrystal. We will upload Servo and LiquidCrystal. You can download LiquidCrystal from here, or you can input this address in your browser: http://elecfreaks.com/store/download/SPI_IIC_LCD.zip.

[cce]
#include <Servo.h> 
#include <Wire.h>
#include <LiquidCrystal.h>

#define BIT(x) (1<<(x))

Servo myservo;           // create servo object to control a servo 
int key = 4;              // key's pin
int motor = 5;            // motor's pin
int sliding_block = 2;	  // INT0
int servo = 9;            
int degree = 100;         // servo degree
float game_time = 0;      // game's time
volatile unsigned int delay_counter = 0;  // to mydelay()
boolean game_over = true;
boolean reset = true;     
LiquidCrystal lcd(0);

void int0_init()                   // get sliding_block's data use INT0
{
  DDRD &=~BIT(2);
  PORTD |= BIT(2);
  EICRA &=~BIT(1);
  EICRA |= BIT(0);
  EIFR |= BIT(0);                  // clear flag
  EIMSK &=~BIT(0);		
}

void timer0_init()                 // control motor
{  
  OCR0A = 100;  
  OCR0B = 10;  

  TCCR0A |= BIT(COM0A0) | BIT(COM0B1) | BIT(WGM01) | BIT(WGM00);     
  TCCR0B |= BIT(WGM02) | BIT(CS00);  
}

void timer2_init()                 // to mydelay(), because it seems like delay() can't be interrupted by other interrupt such as INT0 
{
  TCCR2A = 0;
  TCCR2B = BIT(CS22) + BIT(CS21) + BIT(CS20);     // 1024 divide
  TCNT2 = 99;                                     //10ms 
  TIMSK2 &= ~BIT(TOIE2);     
}

void lcd_init()
{
  lcd.begin(16, 2);
  lcd.setCursor(0, 0);
  lcd.print("   press key!   ");
  lcd.setBacklight(HIGH);
}

void setup() 
{ 
  myservo.attach(servo);      // attaches the servo on pin 9 to the servo object 

  pinMode(motor, OUTPUT);
  timer0_init();              //motor

  pinMode(key, INPUT);
  pinMode(13, OUTPUT);        //LED
  digitalWrite(13, LOW);

  timer2_init();              //mydelay
  pinMode(sliding_block, INPUT);
  int0_init();                //sliding_block

  lcd_init();
  sei();
} 

void mydelay(unsigned int x, void (*fun)(void))                  //min=10ms
{
  unsigned int time_count = x/10;
  volatile unsigned int comp_count = delay_counter + time_count; //comp_count and delay_counter must is a volatile variable

  TIMSK2 |= (1<<TOIE2);
  while(delay_counter < comp_count){                             //in order to not waste CPU, you can execute your function in this loop
    if(0 != fun)
      fun();
  }
  TIMSK2 &=~(1<<TOIE2); 
}

void lcd_display(void)
{
  lcd.setCursor(6, 1);
  lcd.print(game_time);
}

void loop() 
{ 
  static int key_time = 0;
  if(!game_over){ 
    if(!digitalRead(key)){
      if(key_time < 2)
        degree = 140;
      else
        degree = 100;
      key_time++;			// save press time
    }
    else{
      degree = 100;
      key_time = 0;
    }      
    myservo.write(degree);                  // sets the servo position according to the scaled value
    if(degree > 120){
      mydelay(80, lcd_display);
      game_time += 0.1;
      mydelay(80, lcd_display);
      game_time += 0.1;
    }
    else{
      game_time += 0.1;
      mydelay(80, lcd_display);                           // waits for the servo to get there 
    }    
  }else{			   // system reset and game over will enter
    if(!reset){
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("   Game Over    ");
      lcd.setCursor(5, 1);
      lcd.print(game_time);
      mydelay(3000, 0);
    }
    reset = false;    
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("   press key!   ");

    while(digitalRead(key)){
      myservo.write(degree);                  // sets the servo position according to the scaled value 
      mydelay(32, 0);                           // waits for the servo to get there 
      OCR0B = 10;
    }    
    mydelay(64, 0);
    while(!digitalRead(key));

    lcd.clear();
    lcd.setCursor(8, 0);
    lcd.print("2");
    mydelay(1000, 0);
    lcd.clear();
    lcd.setCursor(8, 0);   
    lcd.print("1");
    mydelay(1000, 0);
    lcd.clear();
    lcd.setCursor(7, 0);
    lcd.print("go");

    game_over = false;
    game_time = 0;
    OCR0B = 60;
    EIFR |= BIT(0);
    EIMSK |= BIT(0);		//enable extern interrupt 0
  }
} 

ISR(INT0_vect)
{
  EIMSK &=~BIT(0);
  OCR0B = 10;
  game_over = true;
  TIMSK1 &=~BIT(TOIE1);
}

ISR(TIMER2_OVF_vect)
{
  TCNT2 = 99;                  //10ms
  delay_counter++;
}

[/cce]