-
Notifications
You must be signed in to change notification settings - Fork 11
Pinball Game using Servos Motors & Unity
roboLea edited this page Jun 28, 2019
·
7 revisions
This tutorial explains how to build a Pinball machine using augmented reality with Unity, Raspberry Pi, and SpaceBrew.
- From IDP 2018: this one to make servo 1 move servo 2
- From IDP 2018
- Pinball Tutorial Part I
- Pinball Tutorial Part II
- Objects follow a path
- Tutorial on Physics Events in Unity
- MCP3008
- Raspberry Pi
- PWM Pi Hat
- Analog feedback servos 2x
- 1 X 5V - 2A power plug
- Breadboard
- Jumper wires
- For the Raspberry Pi: Current distro with Python 2.7 and Spacebrew, WiFi/network is assumed to be working
- For the AR scene: Unity 2017.3.0f3 with Vuforia and the YUXI examples
- One Spacebrew server (we had a local instance running at
192.168.1.191
)
- Get the two servos to move
- Get one servo to move the other one (see last year's tutorial for this step)
#!/usr/bin/env python
# Never mix tabs and spaces.
##############################################################
# This script uses 2 buttons wired to pin 4 and pin 17.
# It publishes the button presses to Spacebrew to turn on the
# virtual pinball handler.
##############################################################
from __future__ import division
import sys
import time
from pySpacebrew.spacebrew import Spacebrew
import RPi.GPIO as GPIO
import Adafruit_PCA9685
pwm = Adafruit_PCA9685.PCA9685()
# Configure min and max servo position
servo_min1 = 150 # Min servo1 position
servo_max1 = 400 # Max servo1 position
servo_min2 = 400 # Min servo2 position
servo_max2 = 150 # Max servo2 position
# Helper function to make setting a servo pulse width simpler.
def set_servo_pulse(channel, pulse):
pulse_length = 1000000 # 1,000,000 us per second
pulse_length //= 60 # 60 Hz
print('{0}us per period'.format(pulse_length))
pulse_length //= 4096 # 12 bits of resolution
print('{0}us per bit'.format(pulse_length))
pulse *= 1000
pulse //= pulse_length
pwm.set_pwm(channel, 0, pulse)
pwm.set_pwm(channel, 1, pulse)
# Set frequency to 60hz, good for servos.
pwm.set_pwm_freq(60)
# Setup Spacebrew with Publishers and Subscribers
brew = Spacebrew("Servo_Button", description="Python Light and Button controller", server="192.168.1.191", port=9000)
brew.addSubscriber("flipLight", "boolean")
brew.addPublisher("button1Press", "boolean")
brew.addPublisher("button2Press", "boolean")
CHECK_FREQ = 0.1 # How often to check the hardware
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
#GREEN_LED = 1
#RED_LED = 4
#GPIO.setup(GREEN_LED, GPIO.OUT)
#GPIO.setup(RED_LED, GPIO.OUT)
GPIO.setup(17, GPIO.IN) #down is False
GPIO.setup(4, GPIO.IN) #down is False
lightOn = False
alreadySent = False # to 'debounce' the button
def handleBoolean(value):
global lightOn
print("Received: "+str(value))
if (value == 'true' or str(value) == 'True'):
lightOn = not lightOn
# for handling messages coming through spacebrew
brew.subscribe("flipLight", handleBoolean)
try:
brew.start()
print("Press Ctrl-C to quit.")
while True:
# GPIO.output(GREEN_LED, False)
if (GPIO.input(17) == False):
if (alreadySent == False):
print("Button1 Pushed")
brew.publish('button1Press', True)
alreadySent = True
# Move servo on channel O between extremes.
pwm.set_pwm(4, 0, servo_min1)
time.sleep(0.1)
pwm.set_pwm(4, 0, servo_max1)
time.sleep(0.2)
brew.publish('button1Press', False)
if (GPIO.input(4) == False):
if (alreadySent == False):
print("Button2 Pushed")
brew.publish('button2Press', True)
alreadySent = True
# Move servo on channel O between extremes.
pwm.set_pwm(8, 0, servo_min2)
time.sleep(0.1)
pwm.set_pwm(8, 0, servo_max2)
time.sleep(0.2)
brew.publish('button2Press', False)
# GPIO.output(GREEN_LED, lightOn)
time.sleep(CHECK_FREQ)
if (GPIO.input(17) == True):
alreadySent = False
if (GPIO.input(4) == True):
alreadySent = False
finally:
GPIO.cleanup()
brew.stop()
The unity scene is based on YUXI's BaseBoARd_LightsButtons
scene.
To connect to Spacebrew:
using UnityEngine;
using System.Collections;
public class SpacebrewEvents : MonoBehaviour {
SpacebrewClient sbClient;
bool lightState = false;
public GameObject LeftPaddle;
public GameObject RightPaddle;
/*
Ok, let's do this all in one script?
BaseExample: None?
HelloWorld
P: None
S: launchSatellite, string
LightsButtons
P: buttonPress, boolean
S: flipLight, boolean
SenseHat
P: letter, string
S: direction, string
up,down,left,right,middle (default)
S: layer, string
p:2
s:4
*/
// Use this for initialization
void Start () {
GameObject go = GameObject.Find ("SpacebrewObject"); // the name of your client object
sbClient = go.GetComponent <SpacebrewClient> ();
// register an event with the client and a callback function here.
// COMMON GOTCHA: THIS MUST MATCH THE NAME VALUE YOU TYPED IN THE EDITOR!!
sbClient.addEventListener (this.gameObject, "launchSatellite");
sbClient.addEventListener (this.gameObject, "flipLight");
sbClient.addEventListener (this.gameObject, "letters");
}
// Update is called once per frame
void Update () {
if (Input.GetKeyDown ("space")) {
//print ("Sending Spacebrew Message");
// name, type, value
// COMMON GOTCHA: THIS MUST MATCH THE NAME VALUE YOU TYPED IN THE EDITOR!!
sbClient.sendMessage("buttonPress", "boolean", "true");
}
// foreach (char c in Input.inputString) {
// print("Just pressed: "+c.ToString());
// if (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z') {
// sbClient.sendMessage("letters", "string", c.ToString());
// GameObject go = GameObject.Find ("MatrixContainer"); // the name of your client object
// MatrixMaker grid = go.GetComponent <MatrixMaker> ();
// grid.ParseIncomingLetter(c.ToString());
// grid.delayLayer();
// //grid.CreateLayer(true);
//
// }
// }
if (Input.touchCount > 0 && Input.touches[0].phase == TouchPhase.Began) {
sbClient.sendMessage("buttonPress", "boolean", "true");
}
if(Input.GetMouseButtonDown(0)){
sbClient.sendMessage("buttonPress", "boolean", "true");
}
}
public void OnSpacebrewEvent(SpacebrewClient.SpacebrewMessage _msg) {
print ("Received Spacebrew Message");
print (_msg);
// Look for incoming Satellite messages
if (_msg.name == "launchSatellite") {
if (_msg.value == "true") {
GameObject go = GameObject.Find("BaseBoARd/YourObjectsGoHere/CenterOfUniverse");
OrbitManager om = go.GetComponent <OrbitManager> ();
om.makeSatellite();
print("Tried to launch Satellite");
}
}
// Look for messages to turn the virtual lamp light on
if (_msg.name == "letters") {
GameObject go = GameObject.Find ("MatrixContainer"); // the name of your client object
MatrixMaker grid = go.GetComponent <MatrixMaker> ();
grid.ParseIncomingLetter(_msg.value[0].ToString());
grid.delayLayer();
}
// Look for messages to turn the virtual lamp light on
if (_msg.name == "flipLeft") {
// Debug.Log ("flipLight!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
// if (_msg.value == "true") {
LeftPaddle = GameObject.Find("BaseBoARd/YourObjectsGoHere/LeftPaddle");
// Debug.Log (LeftPaddle);
LeftPaddle.GetComponent<leftPaddleScript> ().flipState ();
// GameObject go = GameObject.Find("BaseBoARd/YourObjectsGoHere/LeftPaddle");
// lightState = !lightState;
// go.gameObject.SetActive(lightState);
// Input.GetKeyDown("d");
// }
}
if (_msg.name == "flipRight") {
// Debug.Log ("flipLight!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
// if (_msg.value == "true") {
RightPaddle = GameObject.Find("BaseBoARd/YourObjectsGoHere/RightPaddle");
// Debug.Log (RightPaddle);
RightPaddle.GetComponent<rightPaddleScript> ().flipState ();
// GameObject go = GameObject.Find("BaseBoARd/YourObjectsGoHere/RightPaddle");
// lightState = !lightState;
// go.gameObject.SetActive(lightState);
// Input.GetKeyDown("d");
// }
}
//if (_msg.name == "letters") {
//print(go);
//if (_msg.value == "true") {
// GameObject go = GameObject.Find ("MatrixContainer"); // the name of your client object
// MatrixMaker grid = go.GetComponent <MatrixMaker> ();
// grid.CreateLayer(true);
// grid.ParseIncomingString(_msg.value);
}
}
To move the right paddle
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class rightPaddleScript : MonoBehaviour {
public float restPosition = 0f;
public float pressedPosition = 45f;
public float hitStrenght = 10000f;
public float flipperDamper = 150f;
bool rightPaddlePressed = false;
// public string inputName;
HingeJoint hinge;
// Use this for initialization
void Start () {
hinge = GetComponent<HingeJoint> ();
hinge.useSpring = true;
}
public void flipState(){
rightPaddlePressed =!rightPaddlePressed;
Debug.Log ("button Pressed!!!!!!!!!!!!!!!!!!!!!!");
Debug.Log (rightPaddlePressed);
}
// Update is called once per frame
void Update () {
JointSpring spring = new JointSpring ();
spring.spring = hitStrenght;
spring.damper = flipperDamper;
// if (Input.GetKey ("space")){
// Debug.Log("spacebar");
// spring.targetPosition = pressedPosition;
// }else{
// spring.targetPosition = restPosition;
// }
// Debug.Log (rightPaddlePressed);
if (rightPaddlePressed == true){
// Debug.Log("rightFlip");
spring.targetPosition = pressedPosition;
}else{
spring.targetPosition = restPosition;
}
hinge.spring = spring;
hinge.useLimits = true;
}
}
To move the left paddle
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class leftPaddleScript : MonoBehaviour {
public float restPosition = 0f;
public float pressedPosition = 45f;
public float hitStrenght = 10000f;
public float flipperDamper = 150f;
bool leftPaddlePressed = false;
// public string inputName;
HingeJoint hinge;
// Use this for initialization
void Start () {
hinge = GetComponent<HingeJoint> ();
hinge.useSpring = true;
}
public void flipState(){
leftPaddlePressed =!leftPaddlePressed;
Debug.Log ("button Pressed!!!!!!!!!!!!!!!!!!!!!!");
Debug.Log (leftPaddlePressed);
}
// Update is called once per frame
void Update () {
JointSpring spring = new JointSpring ();
spring.spring = hitStrenght;
spring.damper = flipperDamper;
// if (spring.angle > pressedPosition){
// flipState ();
// }
// if (Input.GetKey ("space")){
// Debug.Log("spacebar");
// spring.targetPosition = pressedPosition;
// }else{
// spring.targetPosition = restPosition;
// }
// Debug.Log (leftPaddlePressed);
if (leftPaddlePressed == true){
// Debug.Log("leftFlip");
spring.targetPosition = pressedPosition;
}else{
spring.targetPosition = restPosition;
}
hinge.spring = spring;
hinge.useLimits = true;
}
}
Flipper script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FlipperScript : MonoBehaviour {
public float restPosition = 0f;
public float pressedPosition = 45f;
public float hitStrenght = 10000f;
public float flipperDamper = 150f;
public string inputName;
HingeJoint hinge;
// Use this for initialization
void Start () {
hinge = GetComponent<HingeJoint> ();
hinge.useSpring = true;
}
// Update is called once per frame
void Update () {
JointSpring spring = new JointSpring ();
spring.spring = hitStrenght;
spring.damper = flipperDamper;
if (Input.GetAxis (inputName) == 1) {
spring.targetPosition = pressedPosition;
}else{
spring.targetPosition = restPosition;
}
hinge.spring = spring;
hinge.useLimits = true;
}
}
Generating new balls when they fall off
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AddElements : MonoBehaviour {
public GameObject startGo;
public GameObject copyGo;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
if (Input.GetMouseButtonDown(0))
{
Debug.Log("spacebar");
Vector3 startPos;
Quaternion startRotation;
startPos = startGo.transform.position;
startRotation = startGo.transform.rotation;
GameObject go = Instantiate(copyGo,startPos,startRotation);
go.transform.localScale += new Vector3 (1.0f, 1.0f, 1.0f);
}
}
public void makeObject ()
{
//Debug.Log("Make object called");
Vector3 startPos;
Quaternion startRotation;
startPos = startGo.transform.position;
startRotation = startGo.transform.rotation;
GameObject go = Instantiate(copyGo,startPos,startRotation);
go.transform.localScale += new Vector3 (1.0f, 1.0f, 1.0f);
}
}
Collision event
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CollisionEvents : MonoBehaviour {
public GameObject cloneScript;
// Use this for initialization
void Start () {
}
void OnCollisionEnter(Collision collision){
Debug.Log("colliion started");
}
void OnCollisionStay (Collision collision){
Debug.Log("Stay occuring..");
}
void OnCollisionExit (Collision collision)
{
if(collision.gameObject.name == "Base")
{
// Debug.Log("Exit called...");
cloneScript.GetComponent<AddElements>().makeObject();
}
}
// Update is called once per frame
void Update () {
}
}
Required:
- Make sure that the Raspberry Pi is powered up and running.
- Make sure that the unity scene is running.
Then:
- Connect the left and right buttons (Raspberry) to their equivalent flipper (Unity).