Last active
August 29, 2015 14:21
-
-
Save GleasonK/cf3b70692c3628b32d31 to your computer and use it in GitHub Desktop.
[Blog] Turn Your Smartphone into an Elegant Gamepad with PubNub and Polymer
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!-- First Import: --> | |
<link rel="import" href="paper-fab.html"> | |
... | |
<!-- Then use: --> | |
<paper-fab icon="send"></paper-fab> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<script src="bower_components/webcomponentsjs/webcomponents.min.js"></script> | |
</head> | |
<body> | |
... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<link href="../bower_components/polymer/polymer.html" rel="import"> | |
<link href="../bower_components/paper-button/paper-button.html" rel="import"> | |
<link href="../bower_components/paper-fab/paper-fab.html" rel="import"> | |
<link href="../bower_components/iron-icons/iron-icons.html" rel="import"> | |
<link href="../bower_components/iron-icons/hardware-icons.html" rel="import"> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<dom-module id="pub-control"> | |
<style> | |
... | |
</style> | |
<template> | |
... | |
</template> | |
</dom-module> | |
<script> | |
// element registration | |
Polymer({ | |
is: "pub-control", | |
properties: { | |
... | |
}, | |
ready: function(){ | |
... | |
} | |
}); | |
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<template> | |
<div id="gamepad" class="card"> | |
<div id="arrow-btns"> | |
<p id="channel" hidden="{{!isConfigured}}">{{channel}}</p> | |
<paper-button data-key="UP" on-down="handleButton"> | |
<iron-icon data-key="UP" icon="hardware:keyboard-arrow-up"></iron-icon> | |
</paper-button> <br> | |
<paper-button data-key="LEFT" on-down="handleButton"> | |
<iron-icon data-key="LEFT" icon="hardware:keyboard-arrow-left"></iron-icon> | |
</paper-button> | |
<paper-button data-key="RIGHT" on-down="handleButton"> | |
<iron-icon data-key="RIGHT" icon="hardware:keyboard-arrow-right"></iron-icon> | |
</paper-button> <br/> | |
<paper-button data-key="DOWN" on-down="handleButton"> | |
<iron-icon data-key="DOWN" icon="hardware:keyboard-arrow-down"></iron-icon> | |
</paper-button> <br> | |
<img src="../pubnub.png" style="width: 225px; margin: 15px;"></canvas> | |
</div> | |
<div id="action-btns"> | |
<paper-fab icon="icons:close" data-key="X" on-down="handleButton"></paper-fab> | |
<paper-fab icon="icons:radio-button-unchecked" data-key="O" on-tap="handleButton"></paper-fab> | |
<br> | |
<paper-fab mini icon="polymer" style="background:black" on-tap="changeChannel"></paper-fab> | |
</div> | |
</div> | |
</template> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<script> | |
// element registration | |
Polymer({ | |
is: "pub-control", | |
properties: { | |
channel: { | |
type : String, | |
value: "demo" | |
}, | |
isConfigured: { | |
type : Boolean, | |
value: false | |
} | |
}, | |
// add properties and methods on the element's prototype | |
handleButton: function(e){ | |
... | |
}, | |
changeChannel: function(e){ | |
... | |
}, | |
ready: function(){ | |
... | |
} | |
}); | |
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
paper-fab { | |
background:red; | |
} | |
... | |
<paper-fab mini icon="polymer" style="background:black" on-tap="changeChannel"></paper-fab> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
... | |
<script src="bower_components/webcomponentsjs/webcomponents.min.js"></script> | |
<link href="element/pub-control.html" rel="import"> | |
</head> | |
<body fullbreed unresolved> | |
<pub-control></pub-control> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<link rel="import" href="bower_components/pubnub-polymer/pubnub-element.html"> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<template> | |
... | |
<core-pubnub | |
publish_key="your_pub_key" | |
subscribe_key="your_sub_key"> | |
<core-pubnub-publish id="pub" channel="{{channel}}" message="Hello"> </core-pubnub-publish> | |
</core-pubnub> | |
</template> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Polymer({ | |
... | |
ready: function(){ | |
this.$.pub.message = {"type":"<type>", "data":"<data>"}; | |
this.$.pub.publish(); | |
} | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Polymer({ | |
is: "pub-control", | |
properties: { | |
... | |
}, | |
// add properties and methods on the element's prototype | |
handleButton: function(e){ | |
this.$.pub.message = {"type":"button", "data":e.currentTarget.dataset.key}; | |
this.$.pub.publish(); | |
}, | |
changeChannel: function(e){ | |
var channel = prompt("Enter a channel: "); | |
if (channel=="" || channel==null) channel="demo"; | |
this.channel = channel; | |
this.isConfigured = true; | |
// Changing this will reveal our <p hidden={{!isConfigured}}>{{channel}}</p> | |
// and display the value of this.channel | |
}, | |
ready: function(){ | |
... | |
} | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Polymer({ | |
... | |
properties: { | |
aX: { | |
type : Number, | |
value: 0 | |
}, | |
aY: { | |
type : Number, | |
value: 0 | |
}, | |
aZ: { | |
type : Number, | |
value: 0 | |
}, | |
xPos: { | |
type : Number, | |
value: 0 | |
}, | |
yPox: { | |
type : Number, | |
value: 0 | |
}, | |
speed: { | |
type : Number, | |
value: 0 | |
}, | |
... | |
} | |
... | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Polymer({ | |
... | |
ready: function(){ | |
// Tilt of canvas | |
var iOS = /(iPad|iPhone|iPod)/g.test( navigator.userAgent ); | |
var lastUpdate = Date.now(); | |
var SHAKE_THRESHOLD = 1250; | |
... | |
} | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
ready: function(){ | |
... | |
if (window.DeviceMotionEvent == undefined) { | |
// No accelerometer is present. Use buttons. | |
alert("no accelerometer"); | |
} | |
else { | |
// Hooray, we have an accelerometer! Attach a decivemotion listener. | |
alert("accelerometer found"); | |
window.addEventListener("devicemotion", accelerometerUpdate, true); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
ready : function(){ | |
... | |
var self = this; | |
function accelerometerUpdate(e) { | |
var timeNow = Date.now(); | |
var timeDiff = timeNow - lastUpdate; | |
if (timeDiff > 100) { | |
lastUpdate = timeNow; | |
var aX = event.accelerationIncludingGravity.x*1; | |
var aY = event.accelerationIncludingGravity.y*1; | |
var aZ = event.accelerationIncludingGravity.z*1; | |
// Fix for iOS | |
aX = iOS ? -1*aX : aX; | |
//The following two lines are just to calculate a | |
// tilt. Not really needed. | |
self.xPos = Math.atan2(aY, aZ); | |
self.yPos = Math.atan2(aX, aZ); | |
var dX = Math.abs(aX - self.aX); | |
var dY = Math.abs(aY - self.aY); | |
var dZ = Math.abs(aZ - self.aZ); | |
console.log("dX:" + dX + " " + "aX:" + aX + " this.aX:" + self.aX); | |
self.speed = Math.abs(aX + aY + aZ - self.aX - self.aY - self.aZ)/ timeDiff * 10000; | |
if (dX > 0.45) { | |
self.aX = Math.floor(aX*2)/2.0; | |
self.$.pub.message = {'type':'aX', 'data':self.aX}; | |
self.$.pub.publish(); | |
} | |
if (dY > 0.25) { | |
self.aY = Math.floor(aY*2)/2.0; | |
//sendData(controller.channel,"aY",controller['aY']); | |
} | |
if (dZ > 0.25) { | |
self.aZ = Math.floor(aZ*2)/2.0; | |
//sendData(controller.channel,"aZ",controller['aZ']); | |
} | |
if (self.speed > SHAKE_THRESHOLD) { | |
self.$.pub.message = {'type':'shake', 'data':self.speed}; | |
self.$.pub.publish(); | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Simulates clicking a key | |
function fireKey(el, key) { | |
if (document.createEventObject) { | |
var eventObj = document.createEventObject(); | |
eventObj.keyCode = key; | |
el.fireEvent("onkeydown", eventObj); | |
} else if (document.createEvent) { | |
var eventObj = document.createEvent("Events"); | |
eventObj.initEvent("keydown", true, true); | |
eventObj.which = key; | |
eventObj.keyCode = key; | |
el.dispatchEvent(eventObj); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pubnub.subscribe({ | |
channel : channelName, | |
message : function(message){ | |
if (message.type == "button") { | |
switch(message.data){ | |
case "UP": | |
fireKey(el, 38); // Up Arrow Key | |
break; | |
case "DOWN": | |
fireKey(el, 40); // Down Arrow Key | |
break; | |
case "LEFT": | |
fireKey(el, 37); // Left Arrow Key | |
break; | |
case "RIGHT": | |
fireKey(el, 39); // Right Arrow Key | |
break; | |
case "X": | |
fireKey(el, 78); // N Key | |
break; | |
case "O": | |
fireKey(el, 78); // N Key | |
break; | |
default: | |
break; | |
} | |
} | |
} | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta name=viewport content="width=device-width, initial-scale=1"> | |
<meta name="apple-mobile-web-app-capable" content="yes"> | |
<meta name="mobile-web-app-capable" content="yes"> | |
<title>PubMote - Smartphone Controller</title> | |
<script src="bower_components/webcomponentsjs/webcomponents.min.js"></script> | |
<link rel="import" href="bower_components/paper-button/paper-button.html"> | |
<link rel="import" href="bower_components/paper-fab/paper-fab.html"> | |
<link rel="import" href="bower_components/core-icons/core-icons.html"> | |
<link rel="import" href="bower_components/core-icons/hardware-icons.html"> | |
<link rel="import" href="bower_components/pubnub-element/pubnub-element.html"> | |
<style> | |
#arrow-btns, #action-btns { | |
text-align: center; | |
} | |
paper-button{ | |
background: rgba(217, 217, 217, 1); | |
padding: 3px 15px; | |
margin: 5px; | |
} | |
paper-fab{ | |
margin: 5px 10px; | |
} | |
.fullscreen { | |
padding-top: 25px; | |
width: 100%; | |
height: 100vh; | |
} | |
.fullscreen img{ | |
margin: 50px; | |
} | |
#channel { | |
padding:20em; | |
font-size: 15px; | |
margin: 0 auto; | |
} | |
.card { | |
margin:auto; | |
margin-top:5px; | |
background-color:#FFFFFF; | |
border-radius:2px; | |
color:#000000; | |
border:1px solid #d8d8d8; | |
padding:10px; | |
text-align:center; | |
position:relative; | |
} | |
</style> | |
</head> | |
<!-- Body tags fullbreed ensures that the template takes the entire screen, unresolved prevents FUOC --> | |
<body fullbreed unresolved> | |
<!-- Create a template to handle the controller's functions --> | |
<template is="auto-binding" id="controller-template"> | |
<paper-shadow id="gamepad" z="1" class="card"> | |
<template if="{{isConfigured}}" id=channel> <!-- Only shows when boolean is true. --> | |
<p style="text-align: center;"><i>Channel: {{channel}}</i></p> | |
</template> | |
<!-- The arrow buttons of the game pad. Use HTML5 data-* to tell which button is clicked --> | |
<div id="arrow-btns"> | |
<!-- Uses Polymer's on-tap to link the template's function handleButton() --> | |
<paper-button data-key="UP" on-tap="{{handleButton}}"> | |
<core-icon data-key="UP" icon="hardware:keyboard-arrow-up"></core-icon> | |
</paper-button> <br> | |
<paper-button data-key="LEFT" on-tap="{{handleButton}}"> | |
<core-icon data-key="LEFT" icon="hardware:keyboard-arrow-left"></core-icon> | |
</paper-button> | |
<paper-button data-key="RIGHT" on-tap="{{handleButton}}"> | |
<core-icon data-key="RIGHT" icon="hardware:keyboard-arrow-right"></core-icon> | |
</paper-button> <br/> | |
<paper-button data-key="DOWN" on-tap="{{handleButton}}"> | |
<core-icon data-key="DOWN" icon="hardware:keyboard-arrow-down"></core-icon> | |
</paper-button> <br> | |
<!-- Your custom controller icon --> | |
<img src="pubnub.png" style="width:100px; margin: 50px;"> | |
</div> | |
<!-- The three floating action buttons, X O and choose channel --> | |
<div id="action-btns"> | |
<paper-fab icon="close" data-key="X" on-tap="{{handleButton}}"></paper-fab> | |
<paper-fab icon="radio-button-off" data-key="O" on-tap="{{handleButton}}"></paper-fab> | |
<br> | |
<paper-fab mini icon="polymer" style="background:black" on-tap="{{changeChannel}}"></paper-fab> | |
</div> | |
</paper-shadow> | |
<!-- The PubNub Polymer element, handles push and sucscribe from one element --> | |
<core-pubnub publish_key="pub-c-3a6515b4-cc50-4515-82ea-80d76f361027" subscribe_key="sub-c-a6b102dc-00a0-11e5-8fd4-0619f8945a4f"> | |
<core-pubnub-publish id="pub" channel="{{channel}}" message="test"></core-pubnub-publish> | |
</core-pubnub> | |
</template> | |
</body> | |
<script src="http://cdn.pubnub.com/pubnub-3.7.1.min.js"></script> | |
<script type="text/javascript"> | |
var controller = document.querySelector('#controller-template'); | |
controller.isConfigured = false; | |
controller.channel="demo"; | |
controller.aX=0; | |
controller.aY=0; | |
controller.aZ=0; | |
controller.speed=0; | |
controller.handleButton = function(e) { | |
this.$.pub.message = {"type":"button", "data":e.target.getAttribute('data-key') }; | |
this.$.pub.publish(); | |
}; | |
controller.changeChannel = function(e){ // Prompt a user to change the channel | |
this.isConfigured = true; | |
var channel = prompt("Enter a channel: "); // Creates the prompt | |
if (channel == "" || channel == null) channel="demo"; | |
this.channel = channel; // Ignores the change if null or empty | |
} | |
controller.ready = function() { // This code executes once the controller template is loaded | |
var iOS = /(iPad|iPhone|iPod)/g.test( navigator.userAgent ); | |
var lastUpdate = Date.now(); // Last update to threshold PubNub publishes | |
var SHAKE_THRESHOLD = 1250; // Change this value depending on what you consider a shake | |
if (window.DeviceMotionEvent == undefined) { | |
//No accelerometer is present. Can only use buttons. | |
alert("no accelerometer"); | |
} | |
else { | |
// Device has an accelerometer. Can use everything. | |
alert("accelerometer found"); | |
window.addEventListener("devicemotion", accelerometerUpdate, true); | |
} | |
function accelerometerUpdate(e) { // The eventListener function that updates the aX value and shake | |
if (Date.now() - lastUpdate > 100) { // Only checks the accelerometer value ever 100ms | |
lastUpdate = timeNow; | |
var aX = event.accelerationIncludingGravity.x*1; // Used for tilt | |
var aY = event.accelerationIncludingGravity.y*1; | |
var aZ = event.accelerationIncludingGravity.z*1; | |
// Since iOS flips the value, this will flip them to an Android standard | |
aX = iOS ? -1*aX : aX; | |
// Calculate the change in X, Y, and Z. Used to check if there was a shake. | |
var dX = Math.abs(aX - controller.aX); | |
var dY = Math.abs(aY - controller.aY); | |
var dZ = Math.abs(aZ - controller.aZ); | |
// Only change if the dX was large enough, avoids noise. Publish if there was a change. | |
if (dX > 0.45) { | |
controller.aX = Math.floor(aX*2)/2.0; // Floor to the nearest half. | |
controller.$.pub.message = {'type':'aX', 'data':controller.aX}; | |
controller.$.pub.publish(); | |
} | |
if (dY > 0.45) controller.aY = Math.floor(aY*2)/2.0; | |
if (dZ > 0.45) controller.aZ = Math.floor(aZ*2)/2.0; | |
// Calculate the controller's total speed. This will tell whether there was a shake. | |
controller.speed = Math.abs(aX + aY + aZ - controller.aX - controller.aY - controller.aZ)/ timeDiff * 10000; | |
// Play around with SHAKE_THRESHOLD. If speed is larger than ST, publish a shake. | |
if (controller.speed > SHAKE_THRESHOLD) { | |
controller.$.pub.message = {'type':'shake', 'data':controller.speed}; | |
controller.$.pub.publish(); | |
} | |
} | |
} | |
} | |
</script> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* CSS rules for your element */ | |
#arrow-btns, #action-btns { | |
text-align: center; | |
} | |
#canvas{ | |
margin: 10px auto; | |
border: 3px solid blue; | |
} | |
paper-button{ | |
background: rgba(217, 217, 217, 1); | |
padding: 3px 15px; | |
margin: 5px; | |
} | |
paper-fab{ | |
margin: 5px 10px; | |
background: rgb(210, 63, 49); | |
} | |
.fullscreen { | |
padding-top: 25px; | |
width: 100%; | |
height: 100vh; | |
} | |
#channel { | |
font-size: 15px; | |
margin: 10px auto; | |
font-style: italic; | |
} | |
.card { | |
margin:auto; | |
margin-top:5px; | |
background-color:#FFFFFF; | |
border-radius:2px; | |
color:#000000; | |
border:1px solid #d8d8d8; | |
padding:10px; | |
text-align:center; | |
position:relative; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment