灯灯灯 发表于 2017-5-15 16:02:15

【CurieNano项目2】重力感应平衡球游戏

概述:
      平衡球游戏在手机平台上很常见,玩家操控手机的平衡来控制平衡球运动,到达指定地点。为了实现平衡球游戏,我使用Arduino 101作为游戏手柄,将101的加速度计数据通过串口或BLE发给PC机,在PC机上实现一个平衡球游戏的界面。(因为没学过美工,也没有时间去认真做这个游戏,现在界面很简陋,仅仅作为一个抛砖引玉).



目的:      使用Arduino 101作为手柄,用PC机作为界面,通过串口和BLE两种方式完成101与PC机的交互,实现平衡球游戏。



硬件/软件需求:
       硬件需求:CurieNano/Arduino 101、电脑
       软件需求(对于串口版本):Arduino 101 1.x+库、 Python、pygame
       软件需求(对于蓝牙版本):Arduino 101 1.x+库、Visual Studio2015+



使用串口传输的版本:
      这个版本没有使用BLE,需要把101和电脑通过USB连接。我写了一个Python2.7的脚本,调用串口库获取101发来的串口数据,调用pygame显示界面。
      需要:Python2.7 、pygame 、Arduino 101 、Arduino 101 1.0.x+库

Arduino 101代码:#include "CurieIMU.h"
int ax, ay, az;         // accelerometer values
int gx, gy, gz;         // gyrometer values

const int ledPin = 13;      // activity LED pin
boolean blinkState = false; // state of the LED

int calibrateOffsets = 1; // int to determine whether calibration takes place or not

void setup() {
Serial.begin(9600);
while (!Serial);
CurieIMU.begin();

if (calibrateOffsets == 1) {
    delay(1000);
    CurieIMU.autoCalibrateGyroOffset();
    CurieIMU.autoCalibrateAccelerometerOffset(X_AXIS, 0);
    CurieIMU.autoCalibrateAccelerometerOffset(Y_AXIS, 0);
    CurieIMU.autoCalibrateAccelerometerOffset(Z_AXIS, 1);
}
pinMode(ledPin, OUTPUT);
}

int8_t buf;

void loop() {
while(Serial.read()!='g');
CurieIMU.readMotionSensor(ax, ay, az, gx, gy, gz);
*((int16_t*)buf) = ax;
*((int16_t*)(buf+2)) = ay;
Serial.write(buf);
Serial.write(buf);
Serial.write(buf);
Serial.write(buf);

blinkState = !blinkState;
digitalWrite(ledPin, blinkState);
}Python2.7代码:#coding: utf-8
#Version:   Python2.7

from time import sleep
from serial.tools import list_ports
from serial import Serial
from struct import unpack
import pygame,sys

class MySerial():
    def __init__(self):
      port_list = list(list_ports.comports())
      if len(port_list) <= 0:
            self._ser = None
            return
      self._ser = Serial(list(port_list),9600)
      sleep(1)
    def opened(self):
      return self._ser!=None
    def GetAngle(self):
      self._ser.write('g')
      s = self._ser.read(size=4)
      return unpack('hh',s)
         
         
class Ball():
    def __init__(self):
      self.reset()
      self.radius = 20.0
    def reset(self):
      self.x = 100.0
      self.y = 100.0
      self.vx = 0.0
      self.vy = 0.0
    def move(self,a):
      self.vx += a/131072.0
      self.vy += a/131072.0
      self.x -= self.vx
      self.y += self.vy
         
      if self.x<self.radius:
            self.vx = 0.0
            self.x = self.radius
         
      if self.x>600.0-self.radius:
            self.vx = 0.0
            self.x = 600.0-self.radius
         
      if self.y<self.radius:
            self.vy = 0.0
            self.y = self.radius
            
      if self.y>600.0-self.radius:
            self.vy = 0.0
            self.y = 600.0-self.radius
         
      if self.y>200.0-2*self.radius and self.y<200.0 and self.x<400.0:
            self.vy = 0.0
            self.y = 200.0-2*self.radius
         
      if self.y<200.0+2*self.radius and self.y>200.0 and self.x<400.0:
            self.vy = 0.0
            self.y = 200.0+2*self.radius
            
      if self.y>400.0-2*self.radius and self.y<400.0 and self.x>200.0:
            self.vy = 0.0
            self.y = 400.0-2*self.radius
         
      if self.y<400.0+2*self.radius and self.y>400.0 and self.x>200.0:
            self.vy = 0.0
            self.y = 400.0+2*self.radius
         
      if (465-self.x)**2+(465-self.y)**2 < (1.4*self.radius)**2:
                  return 1
         
      for i in xrange(0,4):
            for j in xrange(0,4):
                hole_x = 150*i + 15
                hole_y = 150*j + 15
                if (hole_x-self.x)**2+(hole_y-self.y)**2 < (1.4*self.radius)**2:
                  return -1
      return 0
   
def main():
    ser = MySerial()
    if not ser.opened():
      print "No Serial"
      return
    else:
      print "Serial OK"
    ball = Ball()
    pygame.init()
    screencaption = pygame.display.set_caption('Balance Ball')
    screen = pygame.display.set_mode()
    screen.fill()
    pygame.display.update()

    while True:
      for event in pygame.event.get():
            if event.type==pygame.QUIT:
                return
      screen.fill()
      pygame.draw.rect(screen,,,3)
      pygame.draw.rect(screen,,,3)
      for i in xrange(0,4):
            for j in xrange(0,4):
                hole_x = 150*i + 15
                hole_y = 150*j + 15
                pygame.draw.circle(screen,,,int(ball.radius),int(ball.radius))
               
      pygame.draw.circle(screen,,,int(ball.radius),int(ball.radius))
      pygame.draw.circle(screen,,,int(ball.radius),int(ball.radius))
      pygame.display.update()
         
      result = ball.move(ser.GetAngle())
         
      if result == -1 :
            print "You Lose!"
            ball.reset()
      elif result == 1:
            print "You Win!"
            ball.reset()

if __name__ == '__main__':
    main()运行方式:
上传程序后,确保Arduino 101已连接电脑,且不要使用其它COM口。再运行Python脚本,这个脚本是自动寻找Arduino101的串口的,若找到串口,会在屏幕上显示"Serial Ready",然后弹出游戏界面,就可以玩了:
http://www.arduino.cn/data/attachment/forum/201705/01/165622s5izn9u60mghgg16.png
图:有线连接的平衡球游戏








使用BLE传输的版本:
      这个版本使用了BLE,不需要把101和电脑通过USB连接,只需要给101供电就行(比如充电宝)。在PC端我是修改的一个UWP应用,还没有编写墙壁、洞口等场景,仅有空的地图。
需要:Windows10 、Visual Studio 2015+ 、Arduino 101 、Arduino 101 1.0.x+库

代码:
      算上UWP工程后,文件极大,因此可前往git库下载:
https://git.ustclug.org/WangXuan.c/101-UWP-balanceball并按照ReadMe.txt操作。

运行方法:
       上传Arduino101代码后,给101上电(不必用电脑供电),在Win10的蓝牙设置里配对Arduino 101。编译运行UWP程序,可以看到以下界面。依次点击“101 Detected! Press to Start”和“{00002A37-0000-1000-8000-00805F9B34FB}” 。晃动101,就能看到黄色的圆球随之运动了。
http://www.arduino.cn/data/attachment/forum/201705/01/170830z7lq7ntlntsmbbp8.png
图:BLE版本的平衡球


feixiang20 发表于 2017-12-22 10:07:24

我也喜欢设计游戏
页: [1]
查看完整版本: 【CurieNano项目2】重力感应平衡球游戏