查看: 3903|回复: 4

[项目] 石头剪刀布“体感游戏机“

[复制链接]

该用户从未签到

发表于 2016-9-25 20:53 | 显示全部楼层 |阅读模式
本帖最后由 Zoologist 于 2016-9-25 20:55 编辑

如果说每个美国男人心中都有一个超人的话,那么每个中国男人心中都住着一个功夫高手。或者像侠之大者, 为国为民的郭靖,或者是纵横沙场荒淫无度的洪秀全。如同中国菜之于西洋餐一般,各种功夫都会有一个非常响亮的名字,名称上的玄学也是重要的文化特色。
每当至此,我都会想起来那个笑话:一老头说:当日华山论剑,先是用他的黯然销魂掌破了我的七十二路空明拳;然后我改打降龙十八掌,却不防他伸开食指和中指,竟是六脉神剑,又胜我一筹。可见天下武学彼此克制,武学之道玄之又玄!少年听后目眩,正要问,旁边的老太婆骂道:“玩个剪刀石头布都说得那么威风!”。
这次,我的项目是Genuino打造的,用脚玩的“石头剪刀布”游戏机。加之设备展开之后,六瓣踏板,看起来也像花瓣一般。为此,我特地给这次设计的项目起一个充满传统文化的名称:“菊花桩”。
游戏规则是这样的,用脚来对应石头剪刀布,双脚并立表示“石头”,双脚分开表示“布”,前后站立表示“剪刀”。双方根据口令,摆出自己的策略从而完成比赛。
image001.png

完全凭借运气的游戏不好玩,我们也希望减小游戏中的运气成分增加策略成分,在口令下达之后,落地之前玩家可以根据对方的出腿变化选择自己的出腿情况,也可以在跳起来之后用假动作晃点对方的判断。当然,和普通的石头剪刀布游戏有相同的问题:后出的人会有优势。对此,金融精英研发出“分歧终端机”来解决。
image002.png
image003.png


       电影《非诚勿扰》剧照
而对于我们,用 Genuino 来解决问题。
先介绍硬件:我们选择MPR121触摸模块【参考1】,然后用导线连接锡箔敷设出来的踏板,这样我们可以得知玩家出步伐。
image004.png

这是MPR121 触摸板模块,之前的文章中我也多次介绍
    image005.png
刚开始,我用纸壳敷设,使用订书器固定铝箔,发现这是非常方便快捷的做法,先做了六个;
image006.png

随后,我发现用A4纸裁好,同样用订书器固定铝箔是更简单的方法,又做了六个。
下面就是这个项目用到的全部控制设备:硬件上选择Genuino 101,手工焊接了一个简单的板子,将MPR121模块固定起来(不知道为什么,它PCB引脚不是和洞洞板兼容的,我只得选用了一个IC紧锁座),上面的信号线用螺钉式接线端子引出来,方便我们固定导线。此外,因为尚未掌握Genuino 101  BLE 使用方法,所以使用额外的模块,HC06蓝牙串口透传模块。
image007.jpg

连接触摸模块和踏板的导线,我选择的是彩虹硅胶线,这种线特点是柔软,手感很好。一头是杜邦线,可以很方便的固定在螺钉式接线端子上;另一头是鳄鱼夹,容易夹在踏板上。踏板有12个,我也做了12根这样的线。
image008.png

连接好之后就是这样:

image009.jpg
软件方面,除了 Arduino 的代码,还有需要一个上位机程序,我选择的是Delphi来编写运行在 x86 Windows上的程序,使用串口蓝牙模块和Genuino 101连接。语音提示比赛开始之后,判断500ms以内的结果。
我认为代码本身还是非常简洁的,Arduino 部分:

kittenblock中小学创客名师推荐的图形化编程软件

#include <Wire.h>
#include "Adafruit_MPR121.h"
#define DEBUG 1
 
// You can have up to 4 on one i2c bus but one is enough for testing!
Adafruit_MPR121 cap = Adafruit_MPR121();
 
uint16_t currtouched = 0;
 
void setup() {
  Serial.begin(9600);
  if (DEBUG) { Serial.println("Adafruit MPR121 Capacitive Touch sensor test"); }
 
  // Default address is 0x5A, if tied to 3.3V its 0x5B
  // If tied to SDA its 0x5C and if SCL then 0x5D
  if (!cap.begin(0x5A)) {
    if (DEBUG) {Serial.println("MPR121 not found, check wiring?");}
    while (1);
  }
  if (DEBUG) {Serial.println("MPR121 found!");}
}
 
void loop() {
  currtouched = cap.touched();
  char r=0;
  if (currtouched!=0) {
      //Serial.print(currtouched,HEX);    
      if ((currtouched&0x3) !=0) {r=r|1;}
      if ((currtouched&0xC) !=0) {r=r|2;}
      if ((currtouched&0x30) !=0) {r=r|4;}
      if ((currtouched&0xC0) !=0) {r=r|8;}
      if ((currtouched&0x300) !=0) {r=r|0x10;}
      if ((currtouched&0xC00) !=0) {r=r|0x20;}
      //Serial.write(currtouched);  
       Serial.write(r);    
    }
  delay(100);
}

这里再说明一下具体接口:
  
玩家
  
玩家A
玩家B
和上位机接口
BIT0
BIT1
BIT2
BIT3
BIT4
BIT5
BIT6
BIT7
接线柱
0
1
2
3
4
5
6
7
8
9
10
11
踏板
布1
布2
刀1
刀2
石1
石2
布1
布2
刀1
刀2
石1
石2

在触摸板上有12个接线柱(从011),分别连接12个踏板。我简化了数据传送的方式,比如:布,只要踩中一个就认为当前是布,这样可以方便上位机的处理。每次只要发送一个字节即可。
上位机部分:

kittenblock中小学创客名师推荐的图形化编程软件

unit Unit2;
 
interface
 
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, CPortCtl, CPort, MMSystem, ExtCtrls;
 
const
  BIT0 = 1;
  BIT1 = 2;
  BIT2 = 4;
  BIT3 = 8;
  BIT4 = $10;
  BIT5 = $20;
 
type
  TForm2 = class(TForm)
    GroupBox1: TGroupBox;
    ComPort1: TComPort;
    Button1: TButton;
    Button2: TButton;
    AKnife1: TLabel;
    AKnife2: TLabel;
    AStone1: TLabel;
    AStone2: TLabel;
    ACloth1: TLabel;
    ACloth2: TLabel;
    GroupBox2: TGroupBox;
    GroupBox3: TGroupBox;
    BCloth1: TLabel;
    BCloth2: TLabel;
    BKnife1: TLabel;
    BKnife2: TLabel;
    BStone1: TLabel;
    BStone2: TLabel;
    Button3: TButton;
    GroupBox4: TGroupBox;
    Memo1: TMemo;
    Button4: TButton;
    Timer1: TTimer;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure ComPort1RxChar(Sender: TObject; Count: Integer);
    procedure Button3Click(Sender: TObject);
    procedure Button4Click(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
 
var
  Form2: TForm2;
  Mode:integer;   //测试模式=1,比赛模式=2
                  //测试模式下即时显示触摸结果
  Touched:integer;
  NeedResult:Boolean;
implementation
 
{$R *.dfm}
 
procedure TForm2.Button1Click(Sender: TObject);
begin
  //显示串口设置
  ComPort1.ShowSetupDialog;
end;
 
procedure TForm2.Button2Click(Sender: TObject);
begin
  //连接端口
  ComPort1.Connected:=true;
  Memo1.Lines.Add('连接到 '+ComPort1.Port);
  //默认是测试模式
  Mode:=1;
 
  NeedResult:=False;
end;
 
 
procedure TForm2.Button3Click(Sender: TObject);
begin
  if (Button3.Caption='比赛模式') then
     begin
        //当前为比赛模式
        Mode:=2;
        Memo1.Lines.Add('进入比赛模式');
        Button3.Caption:='测试模式';
        Button4.Enabled:=True;
     end
  else
    begin
        //当前为测试模式
        Mode:=1;
        Memo1.Lines.Add('进入测试模式');
        Button3.Caption:='比赛模式';
        Button4.Enabled:=False;
    end;
end;
 
procedure TForm2.Button4Click(Sender: TObject);
begin
  //播放开始的声音
  sndPlaySound('Start.wav',SND_SYNC);
  sndPlaySound(nil, 0); // Stops the sound
 
  //开始之后延时300ms
  Timer1.Interval:=300;
  Timer1.Enabled:=True;
 
  Button4.Enabled:=False;
  NeedResult:=False;
end;
 
procedure TForm2.ComPort1RxChar(Sender: TObject; Count: Integer);
var
  s:byte;
  a,tmpA,tmpB:integer;
begin
  //取得串口接收字符串
  ComPort1.Read(s,1);
  a:=s;
  //输出转化后的结果
  Memo1.Lines.Add('Hex '+IntToHex(a,2));
 
  if (Timer1.Enabled) and (a<>0) then
      Touched:=a;
  if (Mode=2) and (NeedResult) then a:=Touched;
 
  //根据输入的结果显示状态
  if (a and BIT0) <>0 then
     begin
        ACLOTH1.Font.Color:=clRed;
        ACLOTH2.Font.Color:=clRed;
     end
  else
      begin
        ACLOTH1.Font.Color:=clSilver;
        ACLOTH2.Font.Color:=clSilver;
      end;
  if (a and BIT1 ) <>0 then
      begin
        AKnife1.Font.Color:=clRed;
        AKnife2.Font.Color:=clRed;
      end
  else
      begin
        AKnife1.Font.Color:=clSilver;
        AKnife2.Font.Color:=clSilver;
      end;
  if (a and BIT2 ) <>0 then
      begin
        AStone1.Font.Color:=clRed;
        AStone2.Font.Color:=clRed;
      end
  else
      begin
        AStone1.Font.Color:=clSilver;
        AStone2.Font.Color:=clSilver;
      end;
 
  if (a and BIT3 ) <>0 then
     begin
        BCLOTH1.Font.Color:=clGreen;
        BCLOTH2.Font.Color:=clGreen;
     end
  else
     begin
        BCLOTH1.Font.Color:=clSilver;
        BCLOTH2.Font.Color:=clSilver;
     end;
  if (a and BIT4 ) <>0 then
     begin
        BKnife1.Font.Color:=clGreen;
        BKnife2.Font.Color:=clGreen;
     end
  else
     begin
        BKnife1.Font.Color:=clSilver;
        BKnife2.Font.Color:=clSilver;
     end;
  if (a and BIT5 ) <>0 then
     begin
        BStone1.Font.Color:=clGreen;
        BStone2.Font.Color:=clGreen;
     end
  else
     begin
        BStone1.Font.Color:=clSilver;
        BStone2.Font.Color:=clSilver;
     end;
 
  if (Mode=2) and (NeedResult) then
    begin
      Memo1.Lines.Add(IntToHex(a,4));
      //有效性检查
      //1.A 踩中的情况只能是 布 刀 石,其他都是错误
        tmpA:=0;
        if ((a and $7)=$1) then tmpA:=1; //布
        if ((a and $7)=$2) then tmpA:=2; //刀
        if ((a and $7)=$4) then tmpA:=3; //石
        //如果出现同时踩中两种的情况,tmpA=0
 
      //2.B 踩中的情况只能是 布 刀 石,其他都是错误
        tmpB:=0;
        if (((a shr 3) and $7)=$1) then tmpB:=1;  //布
        if (((a shr 3) and $7)=$2) then tmpB:=2;  //刀
        if (((a shr 3) and $7)=$4) then tmpB:=3;  //石
 
      //3.比较结果
        if (tmpA=tmpB)then
           begin
              Memo1.Lines.Add('平局!');
              sndPlaySound('Draw.wav',SND_SYNC);
              sndPlaySound(nil, 0); // Stops the sound
           end;
         if ((tmpA=1) and (tmpB=3)) or
            ((tmpA=2) and (tmpB=1)) or
            ((tmpA=3) and (tmpB=2)) or ((tmpA<>0)and(tmpB=0))  //最后一种是B出错了,但是A没有错误
            then
           begin
              Memo1.Lines.Add('A 是胜利者!');
              sndPlaySound('WinnerA.wav',SND_SYNC);
              sndPlaySound(nil, 0); // Stops the sound
           end;
         if ((tmpA=1) and (tmpB=2)) or
            ((tmpA=2) and (tmpB=3)) or
            ((tmpA=3) and (tmpB=1)) or ((tmpA=0)and(tmpB<>0))  //最后一种是A出错了,但是B没有错误
            then
           begin
              Memo1.Lines.Add('B 是胜利者!');
              sndPlaySound('WinnerB.wav',SND_SYNC);
              sndPlaySound(nil, 0); // Stops the sound
           end;
        NeedResult:=False;
    end;
    ComPort1.ClearBuffer(True,True);
end;
 
procedure TForm2.Timer1Timer(Sender: TObject);
begin
  Timer1.Enabled:=False;
  Button4.Enabled:=True;
  NeedResult:=True;
end;
 
end.

启动之后,上位机有按钮可以打开串口配置界面。然后代码定义了两种模式:一种是测试模式,在这个模式下实时显示触摸的信息。比如:踩中石头,马上会在界面上用不同颜色标记当前已经踩中;另外,一种是比赛模式,进入比赛模式后,会语音播放一个倒计时,然后显示500ms内的双方踩踏状态,最后输出结果: A 胜利,B胜利,平局。
上位机开发界面,用到了CPORT VCL 作为串口通讯用途:
image010.png


最后放一个实际游戏的视频,可以看到妹子玩的很开心:


总结:
这套装置存在下面一些缺点:
1.       1.游戏者必须赤脚才可以进行游戏
2.       2.线比较多
优点:
1.      1。 没有机械结构不容易损坏,经常接触的只是锡箔部分,结构简单便于维护;
2.       2.识别准确,每次的接触识别还是挺准的,没有发现过干扰之类的问题;
3.       3.成本低,除了MPR121触摸模块之外,用到的都是取自日常生活常见的物品;

游戏的视频,有妹子看到了踏板的照片之后惊呼莫非是“电极”?这倒是提醒了我,也许我会考虑在后面的改进版本中加入电击功能,惩罚输家…….可以这样使用触摸之后,还可以设计出来打地鼠游戏,摸高游戏等等。有兴趣的朋友赶紧入手 Genuino充分发挥自己的想象做出自己的交互设备吧!

参考:

打赏作者鼓励一下!

1人打赏

该用户从未签到

 楼主| 发表于 2016-9-25 20:55 | 显示全部楼层
本帖最后由 Zoologist 于 2016-9-25 21:01 编辑

上位机源代码 Delphi 2010编译通过 DaisySource.rar (536.31 KB, 下载次数: 0)
打赏作者鼓励一下!
  • TA的每日心情
    无聊
    2017-6-24 14:20
  • 签到天数: 3 天

    [LV.2]偶尔看看I

    发表于 2016-9-30 08:51 来自手机 | 显示全部楼层
    (* ̄rǒ ̄)下一个版本再改进下,给系统加上惩罚执行装置。猜错了干冰喷雾伺候,或者是充气锤头。
    打赏

    该用户从未签到

     楼主| 发表于 2016-10-10 22:58 | 显示全部楼层
    整体作品展示图
    IMG_20161010_224130.jpg
    打赏作者鼓励一下!

    该用户从未签到

     楼主| 发表于 2016-10-16 23:02 | 显示全部楼层
    IMG_20161016_222502.jpg

    纪念品合影~
    打赏作者鼓励一下!
    您需要登录后才可以回帖 登录 | 立即注册  

    本版积分规则

    热门推荐

    【Arduino】108种传感器模块系列实验(55)---DHT11温湿度传感器
    【Arduino】108种传感器模
    37款传感器与模块的提法,在网络上广泛流传,其实Arduino能够兼容的传感器模块肯定是
    【Arduino】108种传感器模块系列实验(40)---旋转编码器模块
    【Arduino】108种传感器模
    37款传感器与模块的提法,在网络上广泛流传,其实Arduino能够兼容的传感器模块肯定是
    基于机器视觉的智能分拣机械臂
    基于机器视觉的智能分拣机
    本作品是一个基于机器视觉的智能机械臂操作平台,其采用atmega32单片机作为主控制系统
    请问HMC5883L的三个数据到底代表什么,如何做成指南针?
    请问HMC5883L的三个数据到
    或者哪位朋友有资料给个连接?网上愣是没找到。都只介绍了如何读取数据。我想做成图
    关于atmega328p-pu 地的问题
    关于atmega328p-pu 地的问
    请问图中ADC的地和VCC的地在单片机内部有连接吗?本人初学者,因为需要控制模拟电路,
    Copyright   ©2015-2016  Arduino中文社区  Powered by©Discuz!   ( 蜀ICP备14017632号-3 )
    快速回复 返回顶部 返回列表