Hangouts
Against
Humanity
(viewing link: http://bit.do/hah2014)
Cards Against Humanity
A party game for horrible people
Pick a card
Repeat
And Repeat
2-card hands
3-card hands
Google+ Hangouts
A cross-browser, cross-OS platform for video chat applications
Extension Apps
All HTML/JS/CSS
Extension Apps
The Server
How to deal with multiple card sets?
Lay out card sets on the server and deliver them with Go (Google App Engine hosted because REASONS)
https://hangouts-against-humanity.appspot.com/white?sets=base+first+second
https://hangouts-against-humanity.appspot.com/black?sets=base+first+second
Dealing with Game State
Many items to watch:
- What's the score?
- What's the current question?
- Can I still submit cards?
- Am I the current reader?
- Who has submitted questions?
- Who won the round?
gapi.hangout.data
State managed through this namespace and methods
gapi.hangout.data.getState()
> Object {
Holmes : 'Elementary, my dear Watson',
Terminator : 'Hasta la vista, baby'
}
State represented as JSON-based key-value store
gapi.hangout.data
submitDelta lets you set several keys at once
gapi.hangout.data.submitDelta({
foo: 'bar',
lorem: 'ipsum'
});
gapi.hangout.data.getState();
> Object {
Holmes : 'Elementary, my dear Watson',
Terminator : 'Hasta la vista, baby',
foo : 'bar',
lorem : 'ipsum'
}
gapi.hangout.data
submitDelta can also optionally remove keys
gapi.hangout.data.submitDelta({}, ['Holmes', 'Terminator']);
> undefined
gapi.hangout.data.getState();
> Object {
foo : 'bar',
lorem : 'ipsum'
}
gapi.hangout.data
Helper methods:
gapi.hangout.data.getValue('foo');
> 'bar'
gapi.hangout.data.setValue('foo', 'baz');
> undefined
gapi.hangout.data.getState();
> Object { foo : 'baz' , lorem : 'ipsum' }
gapi.hangout.data.clearValue('foo');
> undefined
gapi.hangout.data.getState();
> Object { lorem : 'ipsum' }
gapi.hangout.data
How to listen to changes in the State
gapi.hangout.data.onStateChanged.add(function(ev) {
console.log(ev.state);
});
ev object contains the new state, as well as valuable metadata
ev = {
addedKeys: [ Metadata for all added/changed keys in this update ],
removedKeys: [ All removed keys in this update ],
state: { Key-value store of the current shared State },
metadata: [ Metadata for all keys in the state ]
}
onStateChanged and Angular
Watch all items I want to track for game state:
gapi.hangout.data.onStateChanged.add(function(evt) {
var questionChanged = false,
readerChanged = false;
angular.forEach(evt.addedKeys, function(metadata) {
questionChanged = (metadata.key === QUESTIONKEY ? true : questionChanged);
readerChanged = (metadata.key === READERKEY ? true : readerChanged);
});
if(questionChanged || readerChanged) {
$rootScope.$apply(function() {
gameState.question = evt.state[QUESTIONKEY];
gameState.reader = evt.state[READERKEY];
});
}
});
Meanwhile, in the Controller
function AmICurrentReader() {
return gameState.reader === LOCAL_PARTICIPANT_ID;
}
function CurrentQuestion() {
return gameState.question;
}
$scope.$watch(AmICurrentReader, function(IAmCurrentReader) {
if(IAmCurrentReader) {
// You're the reader, do the right thing
}
});
$scope.$watch(CurrentQuestion, function(newQuestion) {
// Question has changed, update the viewmodel
});
Things not covered
Angular-time vs. Non-Angular-time
(Who $applys, after all?)
(Who $applys, after all?)
Removing Listeners
State oddities
(Timing, not true JSON representations, abstracting away)
(Timing, not true JSON representations, abstracting away)
gapi.hangout
General-purpose namespace describing information about the Hangout, participants in the Hangout, and other information
gapi.hangout.getStartData();
gapi.hangout.hasAgeRestriction();
gapi.hangout.onChangeTopic.add(eventHandler);
Participants
Need to track participants who are engaged in the app, and
differentiate from those not engaged (ie. enabled)
differentiate from those not engaged (ie. enabled)
Need to react to people being enabled or disabled
gapi.hangout
getEnabledParticipants()
gapi.hangout.getEnabledParticipants();
> [ Array of Participants who have enabled the application ]
Participants
participant = {
id : 'Unique Identifier for this Participant in this Hangout',
displayIndex: '0-based index of participant in the filmstrip',
person: {
id: 'Google+ ID of person',
displayName: 'Display name of participant',
image: {
url: 'URL of participant's avatar'
}
}
}
gapi.hangout
getLocalParticipant()
gapi.hangout.getLocalParticipant();
> { The Participant who is running the app }
getParticipantById(HANGOUT_ID)
gapi.hangout.getParticipantById(HANGOUT_ID);
> { The Participant who is identified by HANGOUT_ID };
gapi.hangout.onApiReady
Fires when API is fully initialized
Used to trigger Angular code
gapi.hangout.onApiReady.add(function(eventObj) {
if (eventObj.isApiReady) {
angular.bootstrap(document, ['ModuleName']);
}
});
Audio/Video effects
Play sounds locally or globally
Add visual information to the outgoing video feed
Face-and-hand-tracking info
Control aspects of the local participant's audio/video
gapi.hangout.av
Lets you make modifications to the user's A/V stream, including camera, microphone, speakers and volume levels
gapi.hangout.av.muteParticipantMicrophone(HANGOUT_ID);
gapi.hangout.av.isAudible();
gapi.hangout.av.onMicrophoneMute.add(function() { ... });
gapi.hangout.av.effects
Add effects to a participant's A/V stream
Get an Image Resource
gapi.hangout.av.effects.createImageResource('http://google.com/favicon.ico');
Get an Audio Resource
gapi.hangout.av.effects.createAudioResource('http://myapp.com/wah-waaah.wav');
Image Resources
Apply ImageResources to the video feed with Overlays
googleFavIconResource.showOverlay({
position: { x: 0.5, y : 0.5 },
rotation: Math.PI / 2,
scale: {
magnitude: 0.1,
reference: gapi.hangout.av.effects.ScaleReference.WIDTH
}
}
Multiple Overlays per Resource are possible!
Face-Tracking Images!
Face-Tracking Overlays
googleFavIconResource.showFaceTrackingOverlay({
trackingFeature: gapi.hangout.av.effects.FaceTrackingFeature.MOUTH_CENTER,
offset: { x: 0, y: 0 },
rotateWithFace: true,
rotate: 0,
scaleWithFace: true,
scale: {
magnitude: 0.15,
reference: gapi.hangout.av.effects.ScaleReference.HEIGHT
}
});
Audio Resources
Play AudioResources on the audio stream with Sounds
wahWaaahResource.createSound({ localOnly: false, loop: true, volume: 0.5 });
wahWaaahSound.play();
Sadly, only very specific audio files are recognized
(at this time)
Putting it all together
(demo-time!)
Things I've Learned
(developer bomb-throwing ahead!)
vim is the only editor sane people use
Two space indentation or GTFO
Java is bloated, verbose, and difficult to grok.
It's days are numbered.
Javascript is a perfectly fine language.
Your framework sucks.
References
API Documentation
Show these guys some love
Thanks!
Hangouts against humanity
By Mike McElroy
Hangouts against humanity
- 3,273