Arduino - Web-based Car Race Brick Game

Arduino - Web-based Car Race Brick Game

Postby Homer » Thu Aug 24, 2017 5:09 pm

I made a similar project using PHPoC Blue here:
https://www.hackster.io/Homer/phpoc-web-based-car-race-brick-game-259027

Demonstration
phpBB [video]

This classic game may make some of us feel childhood nostalgia. The game-play is simple, try to avoid hitting opponent cars and get many score as possible. The car can switch between 5 lanes, and there are 15 different speeds. As speed increases, opponent cars will run faster, making the game more difficult.

Hardware components

Arduino UNO & Genuino UNO × 1
PHPoC Shield for Arduino × 1
[Catalex] Rotary Angle Sensor × 1
USB Wifi dongle × 1

Wiring

Stack PHPoC shield on Arduino.
Connect pin GND, VCC, and SIG of rotary angle sensor to GND, 5V and A0 of Arduino, respectively.
Image

Data Flow

Arduino ---> PHPoC Shield ---> Web browser

User interacts with rotary angle sensor. The input signal value of rotary angle sensor is used to switch the car's position.

Arduino reads the value from rotary angle sensor. The input ADC values are divided into 5 different levels. Switching between levels means changing the lane. Simply, if the input signal is switched to another level, Arduino will send the updated level to PHPoC Shield.

When receiving the value, PHPoC WiFi Shield send it to Web Browser via Websocket. Then, JavaScript function will update the position (lane) of the car. JavaScript program will continuously update position of opponents and game speed as well.

Things To Do

Set up WiFi connection for PHPoC shield (SSID and password)
Upload new UI to PHPoC shield
Upload Arduino code

Set up WiFi connection for PHPoC Shield

See this instruction http://www.phpoc.com/support/manual/p4s-348_user_manual/contents.php?id=network_first_setup

Upload new Web User Interface to PHPoC Shield

Download PHPoC source code remote_racing_game.php.
Upload it to PHPoC shield using PHPoC debugger according to this instruction http://www.phpoc.com/support/manual/phpoc_debugger_manual/contents.php?id=major_upload.

Upload Arduino Code
Install PHPoC Library for Arduino.
Upload Arduino code to Arduino

And Finally
Click serial button on Arduino IDE to get the IP address.
Open web browser, type http://relace_ip_address_here/remote_racing_game.php
Click connect button and enjoy the game.

Arduino code
Code: Select all
#include <Phpoc.h>
#include <PhpocClient.h>
#include <PhpocServer.h>

#include "SPI.h"
#include "Phpoc.h"

PhpocServer server(80);
int adcPin = A0;   
float adcVal = 0;  // variable to store the value coming from the sensor
int lastPos = -1; 
float adcMin = 512-2.5*100;
void setup() {
    Serial.begin(9600);
    while(!Serial)
        ;
   
    Phpoc.begin(PF_LOG_SPI | PF_LOG_NET);

    server.beginWebSocket("game");

    Serial.print("WebSocket server address : ");
    Serial.println(Phpoc.localIP());   
}

void loop() {
    // when the client sends the first byte, say hello:
    PhpocClient client = server.available();
   
    if (client) {
       
        adcVal = 0;
        for(int i = 0; i < 50; i++)
            adcVal += analogRead(adcPin);
           
        adcVal /= 50;
        int pos = (int)((adcVal-adcMin) / 100);
        if (pos<0){
          pos = 0;
        }
        else
          if (pos>4) pos =4; 
             
        if(lastPos != pos){
            String txtMsg = String(pos) + "\r\n"; 
            char buf[txtMsg.length()];
            txtMsg.toCharArray(buf, txtMsg.length());
            server.write(buf, txtMsg.length());
            Serial.println(txtMsg);
           
            lastPos = pos;
            delayMicroseconds(50);

        }
 

    }

   
}


remote_racing_game.php
Code: Select all
<!DOCTYPE html>
<html>
<head>
<title>Arduino - Web-based Game</title>
<meta name="viewport" content="width=device-width, initial-scale=0.7, maximum-scale=0.7">
<style>
body { text-align: center; font-size: 15pt; font-family: Arial, Helvetica, sans-serif;}
h1 { font-weight: bold; font-size: 25pt; }
h2 { font-weight: bold; font-size: 15pt; }
button { font-weight: bold; font-size: 15pt; }
</style>
<script>


var cvs_width = 480, cvs_height = 400;

function object(x, y)
{
    this.x = x;
    this.y = y;
}

function racing_car(x, y)
{
   this.last_x = -1;
    this.x = x;
    this.y = y;
}

var ws = null;
var ctx = null;

var user_car = null;
var opponent_cars = null;
var sidelines = null;
var cur_score = null;


var level_up = null;
var cur_level   = null;
var speed_default = null;
var speed_delta   = null;
var updated_speed = null;
var updated_timer = null;
var is_crashed = null;
var is_not_finished = null;

var car_width = null;
var car_height = null;
var line_width = null;
var block_size = null;
var line_distance = null;
var rect_line_width = null;
var blink_times = null;


function init()
{
   
   cur_score = 0;
   
   level_up = 0;
   cur_level = 0;
   speed_default = 300;
   speed_delta = 20;
   updated_speed = get_speed();
   
   is_crashed = false;
   is_not_finished = true;   
   blink_times = 0;
   
   var width = window.innerWidth;
   var height = window.innerHeight;
   
   var ratio_x = (width - 250) / cvs_width;
   var ratio_y = (height - 250) / cvs_height;
   var ratio = (ratio_x < ratio_y) ? ratio_x : ratio_y;
   
   cvs_width *= ratio;
   cvs_height *= ratio;
   
   block_size = cvs_height/24;
   car_width = 3;
   car_height = 4;
   
   line_width = 2*ratio;
   line_distance = 3*ratio;
   rect_line_width = 1*ratio;

   
   var canvas = document.getElementById("remote");
   canvas.width = cvs_width;   
   canvas.height = cvs_height;
   ctx = canvas.getContext("2d");
   ctx.translate(100, 0);
   
   opponent_cars = [];
   for (var i = 0; i<3; i++)
   {
      opponent_cars.push(new object((Math.floor(Math.random() * 5))*car_width, -(8+2*Math.floor(Math.random()))));
      opponent_cars.push(new object((Math.floor(Math.random() * 5))*car_width, -(24+2*Math.floor(Math.random()))));
   }

   sidelines = [];
   
   for (var i = 0; i<7; i++)
   {
      sidelines.push(new object(-2, 4*i));
      sidelines.push(new object(16, 4*i));
   }      
   
   user_car = new racing_car(2*car_width, 20);
      
   draw_car(user_car, false);
   draw_sideline();
   draw_grid();

}

function connect_onclick()
{
   if(ws == null)
   {
      var ws_host_addr = "<?echo _SERVER("HTTP_HOST")?>";
      ws = new WebSocket("ws://" + ws_host_addr + "/game", "text.phpoc");
      document.getElementById("ws_state").innerHTML = "CONNECTING";
      ws.onopen = ws_onopen;
      ws.onclose = ws_onclose;
      ws.onmessage = ws_onmessage;
   }
   else
      ws.close();
}


function ws_onopen()
{
   document.getElementById("ws_state").innerHTML = "<font color='blue'>CONNECTED</font>";
   document.getElementById("bt_connect").innerHTML = "DISCONNECT";
   ws.send("init\r\n");
   updated_timer = setInterval(game_process, updated_speed);   
}


function ws_onclose()
{
   document.getElementById("ws_state").innerHTML = "<font color='gray'>NOT CONNECTED</font>";
   document.getElementById("bt_connect").innerHTML = "CONNECT";
   ws.onopen = null;
   ws.onclose = null;
   ws.onmessage = null;
   ws = null;
   clearInterval(updated_timer);
}


function ws_onmessage(e_msg)
{
   if (is_not_finished == true)
   {
      e_msg = e_msg || window.event;    
      user_car.x = parseInt(e_msg.data)*car_width;
   }
}

function get_speed()
{
   return (speed_default - speed_delta*cur_level)
}


function draw_score()
{
   ctx.font = 1.5*block_size+"px Georgia";
   ctx.fillText("SCORES",18*block_size,10*block_size);
   ctx.font = 1.25*block_size+"px Georgia";
   ctx.fillText(""+ cur_score,20*block_size,12*block_size);      
   
}


function draw_car(obj, is_broken)
{
   
   ctx.save();
   ctx.translate(obj.x*block_size,obj.y*block_size);
   if (is_broken == false)
   {
      ctx.fillStyle = "#020202";
   }
   else
   {
      ctx.fillStyle = "#202020";
   }
    ctx.fillRect(0, block_size, 3*block_size, block_size);
   ctx.fillRect(block_size, 0, block_size, 3*block_size);
    ctx.fillRect(0, 3*block_size, block_size, block_size);
    ctx.fillRect(2*block_size, 3*block_size, block_size, block_size);
   
   draw_rectangle(1,0);
   draw_rectangle(1,1);
   draw_rectangle(1,2);
   draw_rectangle(0,1);
   draw_rectangle(2,1);
   draw_rectangle(0,3);
   draw_rectangle(2,3);
    ctx.restore();
}


function draw_rectangle(x,y)
{
   
   ctx.beginPath();
   ctx.lineWidth= rect_line_width;
   ctx.strokeStyle="#FFFFFF";
   ctx.rect(x*block_size+line_distance,y*block_size+line_distance,block_size-2*line_distance,block_size - 2*line_distance);
   ctx.stroke();
}



function draw_grid()
{
   
   ctx.save();
   ctx.translate(-2*block_size,0);
   ctx.lineWidth = line_width;     
   ctx.strokeStyle = "#FCFCFC";

   for( var x_id = 0; x_id <= 19; x_id++)
   {
      ctx.beginPath();
      ctx.moveTo(x_id*block_size,0);
      ctx.lineTo(x_id*block_size,block_size*24);
      ctx.stroke();
   }
            
   for( var y_id = 0; y_id <= 24; y_id++)
   {
      ctx.beginPath();
      ctx.moveTo(0, y_id*block_size);
      ctx.lineTo(block_size*19, y_id*block_size);
      ctx.stroke();
   }   
   ctx.restore();
}


function draw_sideline()
{
   var move_down = 1;
   if (is_not_finished == false)
   {
      move_down = 0;
   }
   for( var y_id = 0; y_id < 14; y_id++)
   {
      ctx.fillStyle = "#020202";
      ctx.fillRect(sidelines[y_id].x*block_size , sidelines[y_id].y*block_size , block_size, 2*block_size);
      draw_rectangle(sidelines[y_id].x,sidelines[y_id].y);
      draw_rectangle(sidelines[y_id].x,sidelines[y_id].y+1);
      sidelines[y_id].y += move_down;
      if (sidelines[y_id].y > (user_car.y + car_height))
      {
         sidelines[y_id].y -= 7*car_height;
      }
   }

}


function move_opponent_cars()
{   
   for (var car_id = 0; car_id < 6; car_id++)
   {
      cur_y = opponent_cars[car_id].y;
      if (cur_y <= (user_car.y + 3))
      {
         opponent_cars[car_id].y += 1;
      }
      else
      {
         cur_score += 1;         
         opponent_cars[car_id].x = (Math.floor(Math.random() * 5))*car_width;
         opponent_cars[car_id].y += (-8*car_height+1);
      }
      
   }
   
   level_up = Math.floor(cur_score/10);
   
   if (level_up>cur_level)
   {
         clearInterval(updated_timer);
         cur_level = level_up;
         updated_speed = get_speed();      
         if (updated_speed == 0)
         {
            game_over();
            return;
         }   
         updated_timer = setInterval(game_process, updated_speed);

   }
                        
   while (shuffle_needed())
   {
      for (var car_id = 0; car_id < 5; car_id++)
      {
         for (var car_id_2nd = car_id+1; car_id_2nd < 6; car_id_2nd++)
         {
            
            while ((opponent_cars[car_id_2nd].y == opponent_cars[car_id].y)&&
            (Math.abs(opponent_cars[car_id_2nd].x - opponent_cars[car_id].x)<= car_width))
               {
                  
               if (Math.random()>0.5)
               {
                  opponent_cars[car_id_2nd].x = Math.floor(Math.random()* 5)*car_width;
               }
               else
               {                  
                  opponent_cars[car_id_2nd].y -= Math.ceil(Math.random())*car_height;
               }
               }
                  
            while ((opponent_cars[car_id_2nd].x == opponent_cars[car_id].x)&&
            (Math.abs(opponent_cars[car_id_2nd].y - opponent_cars[car_id].y)< 2*car_height))
               {
                  opponent_cars[car_id_2nd].y -= Math.ceil(Math.random())*2;
               }
                        
   
         }
      }
      
   }
            
}



function shuffle_needed()
{
   for (var car_id = 0; car_id < 5; car_id++)
   {
      for (var car_id_2nd = car_id+1; car_id_2nd < 6; car_id_2nd++)
      {

         if ((opponent_cars[car_id_2nd].x == opponent_cars[car_id].x)&&
         (Math.abs(opponent_cars[car_id_2nd].y - opponent_cars[car_id].y)< 2*car_height))
            return true;
            
         if ((opponent_cars[car_id_2nd].y == opponent_cars[car_id].y)&&
         (Math.abs(opponent_cars[car_id_2nd].x - opponent_cars[car_id].x)<= car_width))   
            return true;            
   
      }
   }
   return false;
   
}



function check_crash()
{
   var crash_car_id = null;
   for (var car_id = 0; car_id < 6; car_id++)
   {
      cur_y = opponent_cars[car_id].y;
      if ((cur_y > (user_car.y - car_height)) && (cur_y < (user_car.y + car_height)-1))
      {

         if (((opponent_cars[car_id].x < user_car.x) && (opponent_cars[car_id].x > user_car.last_x)) ||
         ((opponent_cars[car_id].x>user_car.x) && (opponent_cars[car_id].x < user_car.last_x)) || (opponent_cars[car_id].x == user_car.x))
         {

            is_not_finished = false;
            crash_car_id = car_id;   
            
            if ((Math.abs(opponent_cars[car_id].x - user_car.last_x)) <= (Math.abs(opponent_cars[crash_car_id].x - user_car.last_x)))
            {
               crash_car_id = car_id;   
               user_car.x = opponent_cars[crash_car_id].x;
            }
                                 
         }
         
      }            
   }

   return (!is_not_finished);   
}


function game_over()
{
   ctx.clearRect(-100, 0, cvs_width, cvs_height);
      
   ctx.font = 2*block_size+ "px Georgia";
   
   if (is_crashed == true)
   {
      ctx.fillText("GAME OVER", 5*block_size, 10*block_size);   
   }
   else
   {
      ctx.fillText("CONGRATULATION", 2*block_size, 10*block_size);
   }

   ctx.font = 1.5*block_size + "px Georgia";
   ctx.fillText("SCORES", 8*block_size, 12*block_size);
   ctx.font = 1.25*block_size + "px Georgia";
   ctx.fillText(""+ cur_score, 10*block_size, 14*block_size);
      
         
}


function blinking()
{
      
   ctx.clearRect(-100, 0, cvs_width, cvs_height);

   for (var car_id = 0; car_id < 6; car_id++)
   {
      draw_car(opponent_cars[car_id], false);
   }
      
   draw_score();
   draw_sideline();      
   draw_grid();
      
   if ((blink_times % 2)==0){         
      draw_car(user_car, true);                  
   }

   if (blink_times > 4)
   {      
      game_over();
   }
   else
   {
      setTimeout(function(){blinking();}, 500);
      blink_times ++;
   }
            
}


function update_scene()
{
   if (is_not_finished == true)
   {
      ctx.clearRect(-100, 0, cvs_width, cvs_height);

      
      for (var car_id = 0; car_id < 6; car_id++)
      {
         draw_car(opponent_cars[car_id],false);
      }
      
      draw_car(user_car,false);      
      draw_score();      
      draw_sideline();      
      draw_grid();

      user_car.last_x = user_car.x;   
   }
}


function game_process()
{
   if(ws != null)
   {      
      
      
      is_crashed = check_crash();   

      
      if (is_crashed==true)
      {         
         clearInterval(updated_timer);
         blinking();
         
         return;
      }      
                     
      move_opponent_cars();   
      update_scene();
   

   }
}


window.onload = init;
</script>
</head>

<body>
<center>
<p>
<h1>Arduino - Car Race </br> Brick Game</h1>
</p>
<canvas id="remote" width="500" height="400" Horizontal-align="middle";></canvas>
<h2>
<p>
WebSocket : <span id="ws_state">null</span>
</p>
<button id="bt_connect" type="button" onclick="connect_onclick();">CONNECT</button>
</h2>
</center>
</body>
</html>
Homer
 
Posts: 9
Joined: Thu Jul 20, 2017 10:08 am

Return to Project

Who is online

Users browsing this forum: No registered users and 1 guest