Goal
Show an in-game notification when the player kills an enemy mech, take a screenshot of that glorious moment and upload it to Facebook.
Here's a picture of the mech's ashes after I killed it:
Achievement yay!
Sidenote:
If you noticed the little avatar icon in the top left corner, that's the profile picture of the Facebook user I've logged in with. Before the player logs in, the Facebook logo is displayed instead. When she clicks the logo, the Facebook login page is displayed and she's prompted to grant the sample app permission to post on her behalf. This is what the game looks like before logging in:
Avatar icon before logging in Facebook
After completing the login procedure, the image changes to the profile picture.
Prerequisites
- The AngryBots scene that comes with Unity3D
- Coherent UI for Unity3D (we'll also assume that you've already imported the package in the AngryBots project)
Scene setup
First, we'll have to find a mech that will act as our archnemesis and have it send a signal when it dies so we can activate the achievement logic. This is the one I chose:
The innocent mech that will soon find its doom
Fortunately, the mech GameObject already has a script that emits signals upon death, so the only thing we have to do is add another receiver and configure the action name. Since all the HTML content we need is displayed similar to a HUD, we'll add a CoherentUIView component to the Main Camera and that will be the receiver of the signal. The action name can be anything meaningful for you; I went for "OnEnemyMechDeath".
Note: you can see the last paragraph explained visually in the picture above.
We've got the death notification all sorted out, now we have to configure the receiver, i.e. the Main Camera GameObject. We've already added the CoherentUIView component and you can see its configuration on the next screenshot:
Configuration of the Coherent UI View component
Adding logic in the Main Camera GameObject
The component in the Main Camera GameObject that will be doing the hard work is the SignalReceiver. I'll first show you the whole code for the class and then briefly describe the methods.
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
using UnityEngine; | |
using System.Collections; | |
using Coherent.UI; | |
using Coherent.UI.Binding; | |
using System.IO; | |
public class SignalReceiver : MonoBehaviour { | |
private CoherentUIView m_View; | |
private string m_LocalAppURL; | |
private const string FacebookAppURL = "http://www.coherent-labs.com/sample.html"; | |
// Use this for initialization | |
void Start () { | |
m_View = GetComponent<CoherentUIView>(); | |
m_LocalAppURL = m_View.Page; | |
if (m_View != null && m_View.Listener != null) | |
{ | |
m_View.OnViewCreated += (view) => { view.InterceptURLRequests(true); }; | |
m_View.Listener.URLRequest += OnURLRequestHandler; | |
} | |
} | |
void OnURLRequestHandler (string url, URLResponse response) | |
{ | |
if (url.StartsWith(FacebookAppURL)) | |
{ | |
// change the url, keeping all parameters intact | |
string redirectURL = m_LocalAppURL + url.Substring(FacebookAppURL.Length); | |
response.RedirectRequest(redirectURL); | |
return; | |
} | |
response.AllowRequest(); | |
} | |
void OnEnemyMechDeath() { | |
if (m_View != null && m_View.View != null) | |
{ | |
m_View.View.TriggerEvent("ShowAchievementPopup"); | |
// Uncomment the next line if you want a wall post along with the photo | |
//m_View.View.TriggerEvent("PostOnWall", "If you can see this, it's coming from Unity3D and apparently it's working!"); | |
SaveScreenShotAsync(Application.dataPath + "/mechdeath.png"); | |
} | |
} | |
void SaveScreenShotAsync(string filename) { | |
StartCoroutine(ScreenshotEncode(filename)); | |
} | |
void SavePNGBytes(string filename, byte[] bytes) { | |
// Save to disk code path | |
//File.WriteAllBytes(filename, bytes); | |
// Upload to FaceBook | |
if (m_View != null && m_View.View != null) | |
{ | |
m_View.View.TriggerEvent("UploadImageOnFacebook", bytes, "Victory for mankind! The evil mech was destroyed!"); | |
} | |
} | |
IEnumerator ScreenshotEncode(string filename) | |
{ | |
// wait for graphics to render | |
yield return new WaitForEndOfFrame(); | |
// create a texture to pass to encoding | |
Texture2D texture = new Texture2D(Screen.width, Screen.height, TextureFormat.RGB24, false); | |
// put buffer into texture | |
texture.ReadPixels(new Rect(0, 0, Screen.width, Screen.height), 0, 0); | |
texture.Apply(); | |
// split the process up--ReadPixels() and the GetPixels() call inside of the encoder are both pretty heavy | |
yield return 0; | |
// Added by Karl. - Tell unity to delete the texture, by default it seems to keep hold of it and memory crashes will occur after too many screenshots. | |
DestroyObject( texture ); | |
byte[] bytes = texture.EncodeToPNG(); | |
SavePNGBytes(filename, bytes); | |
} | |
} |
Login flow
That's all that the engine script has to do for the login. The other part is done by JavaScript, which will be discussed shortly.
Next, OnEnemyMechDeath. This method is executed when the mech dies and sends a signal, as we have previously seen. It triggers the JavaScript "ShowAchievementPopup" method and takes a screenshot of the kill site. The screenshot data is collected asynchronously as this article describes so the game doesn't hang for a bit. The sample still has noticable lag so you can apply more sophisticated methods for avoiding that. This data is then forwarded to the JavaScript "UploadImageOnFacebook" function.
The HTML/JavaScript side
We'll start with the HTML code for the page:
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
<html> | |
<head> | |
<script type='text/javascript' src="js/jquery-1.9.1.min.js"></script> | |
<script type='text/javascript' src="js/jquery-ui-1.10.1.custom.min.js"></script> | |
<script type='text/javascript' src="js/coherent.js"></script> | |
<link rel="stylesheet" href="css/achievement.css" /> | |
</head> | |
<body> | |
<div id="achievementPopupMechSlayer" class="achievementPopupBottomPlacer"> | |
<div class="achievementPopup"> | |
<div class="achievementText"> | |
Achievement unlocked | |
<br /> | |
Slayed the mech warrior! | |
</div> | |
</div> | |
</div> | |
</body> | |
<script type='text/javascript' src="js/facebook.js"></script> | |
<script type='text/javascript'> | |
$('#achievementPopupMechSlayer').hide(); | |
function ShowAchievementPopup() { | |
$('#achievementPopupMechSlayer').fadeIn('slow', function() { | |
setTimeout(function() { $('#achievementPopupMechSlayer').fadeOut('slow'); }, 5000); | |
}); | |
} | |
// Subscribe for engine events | |
engine.on('ShowAchievementPopup', ShowAchievementPopup); | |
engine.on('PostOnWall', PostOnWall); | |
engine.on('UploadImageOnFacebook', UploadImageOnFacebook); | |
</script> | |
</html> |
Note: You can examine the stylesheet file at this URL if you're interested.
The facebook.js script
This script does most of the heavy lifting on the JavaScript side. It provides Facebook login functionality and functions for posting messages and photos on your wall. I'll review the initialization and uploading only, but you can check out the whole script here if you're interested.
The first thing that the script does is checking if the user is logged in Facebook. This is done by checking the anchor portion of the page URL - if there is none, we assume the user is not logged in (not the brightest, most secure way of determining that, but it does the job for the sample :)). If the user is not authenticated, a button element is appended to the HTML body that leads to the Facebook login dialog. Upon successful login, Facebook redirects the page to the same local resource that initiated the request (as we discussed previously in the login flow part). This time the window.location.hash.length property is greater than zero and the page assumes the user is logged in.
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
FB.Event.subscribe('auth.statusChange', function(response) { | |
if (response.authResponse) { | |
// user has auth'd your app and is logged into Facebook | |
// request users' first name and profile picture | |
FB.api('/me?fields=picture,first_name', function(me){ | |
SetPortrait(me); | |
userID = me.id; | |
}); | |
} | |
}) | |
function SetPortrait(fbAvatar) { | |
var url = fbAvatar.picture.data.url; | |
$('#fbButton').css("background-image", "url(" + url + ")"); // #fbButton is a <div> | |
} |
If the AJAX request goes as planned, you should see your victory over machines documented :)
The screenshot posted by the Coherent Sample App
As always here's the video of the results:
No comments:
Post a Comment