-
Packaging ANE/SWC/ZIP extensions in FlashDevelop - https://vimeo.com/32551703
-
Android Native Extensions - Part 1 - http://gotoandlearn.com/play.php?id=148
-
Android Native Extensions - Part 2 - http://gotoandlearn.com/play.php?id=149
-
Adobe Air SDK - http://www.adobe.com/devnet/air/air-sdk-download.html
-
FlashDevelop - IDE - http://www.flashdevelop.org/
-
FlashBuilder - http://www.adobe.com/products/flash-builder.html
-
Flixel - Open source game-making library - http://flixel.org
-
Quick Guide to Creating and Using SWC - http://dev.tutsplus.com/tutorials/quick-guide-to-creating-and-using-swcs--active-1211
-
ANEBUILDER - http://as3breeze.com/anebuilder/
-
ANE-Wizard - https://github.com/freshplanet/ANE-Wizard
-
Adobe Scout - Profiler Application
-
Adobe Scout APK - Sideload the Adobe Scout APK for profiing with Adobe Scout
When compiling FlashDevelop
, Flex
, and the AdobeAirSDK you'll want to increase your Java Virtual Machine memory size. Normally the default is 384MB
but you can increase to 1GB
or higher java.args=-Xmx1024m
.
There are several locations where the jvm.config
is configured.
C:\Users\[username]\AppData\Local\FlashDevelop\Apps\flexairsdk\4.6.0+16.0.0\bin\jvm.config
C:\Program Files\Adobe\Adobe Flash Builder 4.7 (64 Bit)\sdks\3.6.0\bin\jvm.config
C:\Program Files\Adobe\Adobe Flash Builder 4.7 (64 Bit)\sdks\4.6.0\bin\jvm.config
- The FlashDevelop FAQ needs your
JAVA_HOME
to point theJDK6
(32-bit).
ANE Extensions wrap Java libraries so they can be invoked from Adobe Air ActionScript.
The Java JAR needs to be compiled first before it can be wrapped in an ANE Extension.
Gradle will compile the Java Extension code into AirOuyaPlugin.jar
.
gradlew clean build copyJar copyNativeArmeabi copyNativeArmeabiV7a copyNativeArmeabiX86
The AirOuyaPlugin.jar
will output to the jar extension folder.
The jar
folder also contains FlashRuntimeExtensions.jar
(from the AdobeAirSDK download) and ouya-sdk.jar
(from the ODK).
The extension interface is the piece between Java and ActionScript and was created using a FlashBuilder project.
After the builder project has imported, Adobe Builder
will auto compile the SWC
file which is needed any time ActionScript is changed in the ANE
.
- In
Flash Builder
choose theFile->Import Flash Builder Project
menu item.
- Choose
Project Folder
, browse to theAdobeAir/OuyaNativeExtension/lib
folder, and clickOK
.
-
After making changes to
ActionScript
switch back toFlash Builder
which will auto-generate a newSWC
file. -
Building the
ANE
from script will embed theSWC
file.
build_ane.cmd will package the OuyaNativeExtension.ane
on Windows. Be sure to customize the paths for JDK
and AIR_SDK
pointing at the AdobeAirSDK in the build script.
The OuyaNativeExtension.ane should be used as a library after placing in the following folders in a FlashDevelop project.
lib\OuyaNativeExtension.ane
extension\release\OuyaNativeExtension.ane
For the debug extension folder, extract the contents of OuyaNativeExtension.ane
into a subfolder named OuyaNativeExtension.ane
.
extension\debug\OuyaNativeExtension.ane\catalog.xml
extension\debug\OuyaNativeExtension.ane\library.swf
Add the OuyaNativeExtension.ane
extension to your application's extensions.
<extensions>
<extensionID>tv.ouya.sdk.ouyanativecontext</extensionID>
</extensions>
Add the ANE extension's MainActivity
within the application
section of the application.xml
. This will be a second activity after the existing Activity
block.
<activity android:name="tv.ouya.sdk.MainActivity"
android:theme="@android:style/Theme.Translucent.NoTitleBar"/>
Import the packages to use the OUYA Native Extension.
import tv.ouya.console.api.OuyaController;
import tv.ouya.sdk.OuyaNativeInterface;
Initialize the OuyaNativeInterface
to use OUYA-Everywhere Input. The OuyaNativeExtension.ane
extension must be added to your project.
trace( "Initialize OUYA Extension..." );
var ouyaNativeInterface:OuyaNativeInterface = new OuyaNativeInterface();
ouyaNativeInterface.OuyaInit();
IsAnyConnected
will return true
if any controllers are connected, otherwise false
.
var anyConnected:Boolean = ouyaNativeInterface.IsAnyConnected();
IsConnected
will return true
if the playerNum
controller is connected.
var playerNum:int = 0;
var isConnected:Boolean = ouyaNativeInterface.IsConnected(playerNum);
GetAxis
will return the Number
value for the supplied playerNum
controller and axis
.
var playerNum:int = 0;
var axis:int = OuyaController.AXIS_LS_X;
var val:Number = ouyaNativeInterface.GetAxis(playerNum, axis);
The supported axis
values are below.
OuyaController.AXIS_LS_X
OuyaController.AXIS_LS_Y
OuyaController.AXIS_RS_X
OuyaController.AXIS_RS_Y
OuyaController.AXIS_L2
OuyaController.AXIS_R2
GetAnyButton
will return true
if any controller is pressing the button
.
var button:int = OuyaController.BUTTON_O;
var pressed:Boolean = ouyaNativeInterface.GetAnyButton(button);
The supported button
values are below.
OuyaController.BUTTON_O
OuyaController.BUTTON_U
OuyaController.BUTTON_Y
OuyaController.BUTTON_A
OuyaController.BUTTON_L1
OuyaController.BUTTON_R1
OuyaController.BUTTON_L3
OuyaController.BUTTON_R3
OuyaController.BUTTON_DPAD_UP
OuyaController.BUTTON_DPAD_DOWN
OuyaController.BUTTON_DPAD_RIGHT
OuyaController.BUTTON_DPAD_LEFT
OuyaController.BUTTON_MENU
BUTTON_MENU
should only be used with ButtonDown
and ButtonUp
events.
GetAnyButtonDown
will return true
if any controller detected a pressed event on the last frame for the button
.
var button:int = OuyaController.BUTTON_O;
var pressed:Boolean = ouyaNativeInterface.GetAnyButtonDown(button);
GetAnyButtonUp
will return true
if any controller detected a released event on the last frame for the button
.
var button:int = OuyaController.BUTTON_O;
var released:Boolean = ouyaNativeInterface.GetAnyButtonUp(button);
GetButton
will return true
if the playerNum
controller is pressing the button
.
var playerNum:int = 0;
var button:int = OuyaController.BUTTON_O;
var pressed:Boolean = ouyaNativeInterface.GetButton(playerNum, button);
GetButtonDown
will return true
if the playerNum
controller detected a pressed event on the last frame for the button
.
var playerNum:int = 0;
var button:int = OuyaController.BUTTON_O;
var pressed:Boolean = ouyaNativeInterface.GetButtonDown(playerNum, button);
GetButtonUp
will return true
if the playerNum
controller detected a released event on the last frame for the button
.
var playerNum:int = 0;
var button:int = OuyaController.BUTTON_O;
var released:Boolean = ouyaNativeInterface.GetButtonUp(playerNum, button);
ClearButtonStatesPressedReleased
will clear the detected pressed and released states to allow detection for the next update frame.
ouyaNativeInterface.ClearButtonStatesPressedReleased();
The following steps setup the Main.as
ActionScript file to load at start-up to initialize the OUYA
native extension.
- Switch to the
selection tool
andleft-click
on the stage to select the document.
-
In the
Property
window change theClass
field toMain
to reference theMain.as
. -
Also set the document size to
1920x1080
. -
To the right of the
Class
field there's an edit button to open the existingMain.as
or use the file menu to create a newMain.as
action script document. -
Create a bare-bones
Main
class. When the application starts, the first thing that will be called is theMain
constructor.
package
{
import flash.display.MovieClip;
public class Main extends MovieClip
{
public function Main()
{
}
}
}
- Add code to load the
OUYA
native extension and save a reference to the native interface.
package
{
import flash.display.MovieClip;
import tv.ouya.sdk.OuyaNativeInterface;
public class Main extends MovieClip
{
// reference to the native interface
var _mOuyaNativeInterface: OuyaNativeInterface;
public function Main()
{
// create the native interface and initialize the OUYA native extension
_mOuyaNativeInterface = new OuyaNativeInterface();
// initialize the OUYA plugin
_mOuyaNativeInterface.OuyaInit();
}
}
}
- Open the
File->Air 17.0 for Android Settings
menu item.
- On the
Deployment
tab, theCertificate
field must be set. You can either browse to an existingp12
certificate or create one with theCreate
button. This field must be set before clicking thePublish
button. IfRemember password for this session
is set you can publish directly from theFile->Publish
menu item. Publishing will produce anAPK
file which can be installed on the command-line. All theFlash
examples useandroid
for the certificate password.
adb install -r application.apk
- On the
General
tab, theApp ID
field should match thepackage identifier
that was created in the developer portal. Notice thatAdobe-Air
apps are always prefixed withair
.
-
Select the
ARM
processor and click the+
button to browse to theOuyaNativeExtension.ane
copied to your project folder and clickOK
. -
When publishing, the
extensions
section will automatically appear in your project'sApplicationName-app.xml
file and that section cannot be edited manually.
<extensions>
<extensionID>tv.ouya.sdk.ouyanativecontext</extensionID>
</extensions>
- Edit your project's
ApplicationName-app.xml
to add the following manifest additions.
<application xmlns="http://ns.adobe.com/air/application/17.0">
<android>
<manifestAdditions>
<![CDATA[
<manifest>
<uses-permission android:name="android.permission.WAKE_LOCK" />
<application>
<activity>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
<category android:name="tv.ouya.intent.category.GAME"/>
</intent-filter>
</activity>
<activity android:name="tv.ouya.sdk.MainActivity"
android:theme="@android:style/Theme.Translucent.NoTitleBar">
<intent-filter>
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
</application>
</manifest>
]]>
</manifestAdditions>
</android>
- Open the
File->Open ActionScript Settings
menu item.
- On the
Library Path
tab, click theBrowse to Native Extension
button to select theOuyaNativeExtension.ane
file in the project folder.
- With the
OuyaNativeExtension.ane
added to the library path, clickOK
. This setting uses an absolute path and if the path is different then the path will need to be reset in order to publish.
The Flash Virtual Controller example shows 4 images of the OUYA Controller which moves axises and highlights buttons when the physical controller is manipulated.
- The Virtual Controller example modifies the
Main.as
ActionScript to add an update event that will later control sprite visibility.
package
{
import flash.display.MovieClip;
import flash.events.Event;
import tv.ouya.sdk.OuyaNativeInterface;
public class Main extends MovieClip
{
// reference to the native interface
var _mOuyaNativeInterface: OuyaNativeInterface;
// frame event listener
private function fl_EnterFrameHandler_1(event:Event):void
{
}
public function Main()
{
// create the native interface and initialize the OUYA native extension
_mOuyaNativeInterface = new OuyaNativeInterface();
// initialize the OUYA plugin
_mOuyaNativeInterface.OuyaInit();
// add an event listener for each frame
addEventListener(Event.ENTER_FRAME, fl_EnterFrameHandler_1);
}
}
}
- In the update event,
ClearButtonStatesPressedReleased
will clear the button pressed and release states each update frame.
package
{
import flash.display.MovieClip;
import flash.events.Event;
import tv.ouya.sdk.OuyaNativeInterface;
public class Main extends MovieClip
{
// reference to the native interface
var _mOuyaNativeInterface: OuyaNativeInterface;
// frame event listener
private function fl_EnterFrameHandler_1(event:Event):void
{
// clear the button pressed and released states each frame
_mOuyaNativeInterface.ClearButtonStatesPressedReleased();
}
public function Main()
{
// create the native interface and initialize the OUYA native extension
_mOuyaNativeInterface = new OuyaNativeInterface();
// initialize the OUYA plugin
_mOuyaNativeInterface.OuyaInit();
// add an event listener for each frame
addEventListener(Event.ENTER_FRAME, fl_EnterFrameHandler_1);
}
}
}
- The update event can fire hundreds of times per second, where the input can be checked far less often. Use a time limitor to throttle-down checking input.
package
{
import flash.display.MovieClip;
import flash.events.Event;
import tv.ouya.sdk.OuyaNativeInterface;
public class Main extends MovieClip
{
// the amount of time to wait in milliseconds between checking input
var INTERVAL_MS_INPUT:Number = 10;
// reference to the native interface
var _mOuyaNativeInterface: OuyaNativeInterface;
// a timer to throttle checking input
var _mInputTimer:Number = 0;
// frame event listener
private function fl_EnterFrameHandler_1(event:Event):void
{
// use the date to access time
var date:Date = new Date();
// check the time interval
if (_mInputTimer < date.getTime())
{
// add an input delay
_mInputTimer = date.getTime() + INTERVAL_MS_INPUT;
// clear the button pressed and released states each frame
_mOuyaNativeInterface.ClearButtonStatesPressedReleased();
}
}
public function Main()
{
// create the native interface and initialize the OUYA native extension
_mOuyaNativeInterface = new OuyaNativeInterface();
// initialize the OUYA plugin
_mOuyaNativeInterface.OuyaInit();
// add an event listener for each frame
addEventListener(Event.ENTER_FRAME, fl_EnterFrameHandler_1);
}
}
}
- The
VirtualController
object is used to create sprites for each of the virtual controllers on screen.
package
{
import flash.display.MovieClip;
import flash.events.Event;
import tv.ouya.sdk.OuyaNativeInterface;
public class Main extends MovieClip
{
// the amount of time to wait in milliseconds between checking input
var INTERVAL_MS_INPUT:Number = 10;
// reference to the native interface
var _mOuyaNativeInterface: OuyaNativeInterface;
// a timer to throttle checking input
var _mInputTimer:Number = 0;
// Virtual controller references
var _mVirtualController1: VirtualController;
var _mVirtualController2: VirtualController;
var _mVirtualController3: VirtualController;
var _mVirtualController4: VirtualController;
// frame event listener
private function fl_EnterFrameHandler_1(event:Event):void
{
// use the date to access time
var date:Date = new Date();
// check the time interval
if (_mInputTimer < date.getTime())
{
// add an input delay
_mInputTimer = date.getTime() + INTERVAL_MS_INPUT;
// update the virtual controller sprites each frame
_mVirtualController1.Update();
_mVirtualController2.Update();
_mVirtualController3.Update();
_mVirtualController4.Update();
// clear the button pressed and released states each frame
_mOuyaNativeInterface.ClearButtonStatesPressedReleased();
}
}
public function Main()
{
// create the native interface and initialize the OUYA native extension
_mOuyaNativeInterface = new OuyaNativeInterface();
// initialize the OUYA plugin
_mOuyaNativeInterface.OuyaInit();
// create the virtual controller sprites on start and specify the playerNum along with where to place on screen
_mVirtualController1 = new VirtualController(this, _mOuyaNativeInterface, 0, 15.65, -75.1);
_mVirtualController2 = new VirtualController(this, _mOuyaNativeInterface, 1, 1232.55, -75.1);
_mVirtualController3 = new VirtualController(this, _mOuyaNativeInterface, 2, 15.65, 495.75);
_mVirtualController4 = new VirtualController(this, _mOuyaNativeInterface, 3, 1232.55, 495.75);
// add an event listener for each frame
addEventListener(Event.ENTER_FRAME, fl_EnterFrameHandler_1);
}
}
}
- The
VirtualController
object uses the native interface in theUpdate
method to access axis and button values for the controller.
public function Update():void
{
UpdateVisibility(_mButtonO, _mOuyaNativeInterface.GetButton(_mPlayerNum, OuyaController.BUTTON_O));
UpdateVisibility(_mButtonU, _mOuyaNativeInterface.GetButton(_mPlayerNum, OuyaController.BUTTON_U));
UpdateVisibility(_mButtonY, _mOuyaNativeInterface.GetButton(_mPlayerNum, OuyaController.BUTTON_Y));
UpdateVisibility(_mButtonA, _mOuyaNativeInterface.GetButton(_mPlayerNum, OuyaController.BUTTON_A));
UpdateVisibility(_mButtonL1, _mOuyaNativeInterface.GetButton(_mPlayerNum, OuyaController.BUTTON_L1));
UpdateVisibility(_mButtonL3, _mOuyaNativeInterface.GetButton(_mPlayerNum, OuyaController.BUTTON_L3));
UpdateVisibility(_mButtonLS, !_mOuyaNativeInterface.GetButton(_mPlayerNum, OuyaController.BUTTON_L3));
UpdateVisibility(_mButtonR1, _mOuyaNativeInterface.GetButton(_mPlayerNum, OuyaController.BUTTON_R1));
UpdateVisibility(_mButtonR3, _mOuyaNativeInterface.GetButton(_mPlayerNum, OuyaController.BUTTON_R3));
UpdateVisibility(_mButtonRS, !_mOuyaNativeInterface.GetButton(_mPlayerNum, OuyaController.BUTTON_R3));
UpdateVisibility(_mButtonDpadDown, _mOuyaNativeInterface.GetButton(_mPlayerNum, OuyaController.BUTTON_DPAD_DOWN));
UpdateVisibility(_mButtonDpadLeft, _mOuyaNativeInterface.GetButton(_mPlayerNum, OuyaController.BUTTON_DPAD_LEFT));
UpdateVisibility(_mButtonDpadRight, _mOuyaNativeInterface.GetButton(_mPlayerNum, OuyaController.BUTTON_DPAD_RIGHT));
UpdateVisibility(_mButtonDpadUp, _mOuyaNativeInterface.GetButton(_mPlayerNum, OuyaController.BUTTON_DPAD_UP));
var date:Date = new Date();
if (_mOuyaNativeInterface.GetButtonUp(_mPlayerNum, OuyaController.BUTTON_MENU))
{
_mMenuTimer = date.getTime() + 1000;
}
UpdateVisibility(_mButtonMenu, date.getTime() < _mMenuTimer);
var lsX = _mOuyaNativeInterface.GetAxis(_mPlayerNum, OuyaController.AXIS_LS_X);
var lsY = _mOuyaNativeInterface.GetAxis(_mPlayerNum, OuyaController.AXIS_LS_Y);
var rsX = _mOuyaNativeInterface.GetAxis(_mPlayerNum, OuyaController.AXIS_RS_X);
var rsY = _mOuyaNativeInterface.GetAxis(_mPlayerNum, OuyaController.AXIS_RS_Y);
var l2 = _mOuyaNativeInterface.GetAxis(_mPlayerNum, OuyaController.AXIS_L2);
var r2 = _mOuyaNativeInterface.GetAxis(_mPlayerNum, OuyaController.AXIS_R2);
UpdateVisibility(_mButtonL2, l2 > DEADZONE);
UpdateVisibility(_mButtonR2, r2 > DEADZONE);
//rotate input by N degrees to match image
var degrees:Number = 135;
var radians:Number = degrees / 180.0 * 3.14;
var cos:Number = Math.cos(radians);
var sin:Number = Math.sin(radians);
MoveBitmap(_mButtonL3, AXIS_SCALAR * (lsX * cos - lsY * sin), AXIS_SCALAR * (lsX * sin + lsY * cos));
MoveBitmap(_mButtonLS, AXIS_SCALAR * (lsX * cos - lsY * sin), AXIS_SCALAR * (lsX * sin + lsY * cos));
MoveBitmap(_mButtonR3, AXIS_SCALAR * (rsX * cos - rsY * sin), AXIS_SCALAR * (rsX * sin + rsY * cos));
MoveBitmap(_mButtonRS, AXIS_SCALAR * (rsX * cos - rsY * sin), AXIS_SCALAR * (rsX * sin + rsY * cos));
}
- The sprites used by the example are first imported to the library from
PNG
files. Use theFile->Import->Import to Library
menu item to select the images in the project folder.
- Switch to the
library
tab andright-click
the imported images to selectProperties...
.
- Switch the the
ActionScript
tab and enableExport for ActionScript
andExport in frame 1
. Set theClass
field to a name and be sure to capitalize the first letter by convention. ClickOK
. Repeat for all of the image sprites.
- The
VirtualController
constructor keeps a reference to theMain
object and theOuyaNativeInterface
object. TheMain
object is needed to dynamically add children to the stage document. ThePlayerNum
argument corresponds to theOUYA
controller number. Thex
andy
parameter indicates where to instantiate theBitmap
objects on the stage.
package
{
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.MovieClip;
import flash.display.PixelSnapping;
import flash.geom.Matrix;
import tv.ouya.console.api.OuyaController;
import tv.ouya.sdk.OuyaNativeInterface;
public class VirtualController extends MovieClip
{
// reference to the Main document
var _mMain: Main;
// reference to the native interface
var _mOuyaNativeInterface: OuyaNativeInterface;
// player number for the virtual controller sprite collection
var _mPlayerNum:int = 0;
// X-position for the sprite controller
var _mX:Number = 0;
// Y-position for the sprite controller
var _mY:Number = 0;
// constructor for the Virtual Controller
public function VirtualController(main:Main, ane:OuyaNativeInterface, playerNum:int, x:Number, y:Number)
{
_mMain = main;
_mOuyaNativeInterface = ane;
_mPlayerNum = playerNum;
_mX = x;
_mY = y;
}
}
}
- The
VirtualController
constructor uses a helper method to add scaled sprites to the stage document.
private function AddBitmap(bitmap : Bitmap) : Bitmap
{
var scale:Number = 2;
var matrix:Matrix = new Matrix();
matrix.scale(scale, scale);
var resizedBitmapData:BitmapData = new BitmapData(bitmap.width * scale, bitmap.height * scale, true, 0x000000);
resizedBitmapData.draw(bitmap, matrix, null, null, null, true);
var resizedBitmap = new Bitmap(resizedBitmapData, PixelSnapping.NEVER, true);
resizedBitmap.x = _mX;
resizedBitmap.y = _mY;
_mMain.addChild(resizedBitmap);
return resizedBitmap;
}
- The
VirtualController
constructor instantiates the sprite objects from the library.
// sprite references
var _mController:Bitmap;
var _mButtonO:Bitmap;
var _mButtonU:Bitmap;
var _mButtonY:Bitmap;
var _mButtonA:Bitmap;
var _mButtonL1:Bitmap;
var _mButtonL2:Bitmap;
var _mButtonL3:Bitmap;
var _mButtonR1:Bitmap;
var _mButtonR2:Bitmap;
var _mButtonR3:Bitmap;
var _mButtonLS:Bitmap;
var _mButtonRS:Bitmap;
var _mButtonDpadDown:Bitmap;
var _mButtonDpadLeft:Bitmap;
var _mButtonDpadRight:Bitmap;
var _mButtonDpadUp:Bitmap;
var _mButtonMenu:Bitmap;
public function VirtualController(main:Main, ane:OuyaNativeInterface, playerNum:int, x:Number, y:Number)
{
_mMain = main;
_mOuyaNativeInterface = ane;
_mPlayerNum = playerNum;
_mX = x;
_mY = y;
_mController = AddBitmap(new Bitmap(new ImageController()));
_mButtonO = AddBitmap(new Bitmap(new ImageO()));
_mButtonU = AddBitmap(new Bitmap(new ImageU()));
_mButtonY = AddBitmap(new Bitmap(new ImageY()));
_mButtonA = AddBitmap(new Bitmap(new ImageA()));
_mButtonL1 = AddBitmap(new Bitmap(new ImageL1()));
_mButtonL2 = AddBitmap(new Bitmap(new ImageL2()));
_mButtonL3 = AddBitmap(new Bitmap(new ImageL3()));
_mButtonR1 = AddBitmap(new Bitmap(new ImageR1()));
_mButtonR2 = AddBitmap(new Bitmap(new ImageR2()));
_mButtonR3 = AddBitmap(new Bitmap(new ImageR3()));
_mButtonLS = AddBitmap(new Bitmap(new ImageLS()));
_mButtonRS = AddBitmap(new Bitmap(new ImageRS()));
_mButtonDpadDown = AddBitmap(new Bitmap(new ImageDpadDown()));
_mButtonDpadLeft = AddBitmap(new Bitmap(new ImageDpadLeft()));
_mButtonDpadRight = AddBitmap(new Bitmap(new ImageDpadRight()));
_mButtonDpadUp = AddBitmap(new Bitmap(new ImageDpadUp()));
_mButtonMenu = AddBitmap(new Bitmap(new ImageMenu()));
}
- The
Update
method uses a helper method to updateBitmap
visibility.
private function UpdateVisibility(bitmap:Bitmap, show:Boolean) : void
{
if (show)
{
bitmap.alpha = 1;
}
else
{
bitmap.alpha = 0;
}
}
- The
Update
method uses a helper method to move the left and right stick sprites.
private function MoveBitmap(bitmap : Bitmap, offsetX : Number, offsetY : Number) : void
{
bitmap.x = _mX + offsetX;
bitmap.y = _mY + offsetY;
}
The In-App-Purchases example uses the ODK to access gamer info, purchasing, and receipts.
- Log into the
developer portal
to record yourdeveloper UUID
to configure in-app-purchases.
-
In the
Games
section of thedeveloper portal
create an entry matching yourpackage name
matching theair
prefix. Download thesigning key
for the game entry into yourFlash
project folder. -
Use the
File->AIR Android Settings..
menu item to add the signing key.
- On the
General
tab click the+
button to add thekey.der
to the included files to reference thesigning key
. And then clickOK
.
- The IAP example modifies the
Main.as
ActionScript to add an update event that will control the stage text content. TheMain
constructor initializes the ANE interface and initializes the ANE.
package
{
// Import the MovieClip namespace
import flash.display.MovieClip;
// The OuyaController keycodes are used by input events
import tv.ouya.console.api.OuyaController;
// Import the namespace for the ANE
import tv.ouya.sdk.OuyaNativeInterface;
// The Main document extends from MovieClip
public class Main extends MovieClip
{
// The developer id displays when you log into the http://devs.ouya.tv developer portal
static var DEVLEOPER_ID:String = "310a8f51-4d6e-4ae5-bda0-b93878e5f5d0";
// save a reference to the ANE interface
var _mOuyaNativeInterface: OuyaNativeInterface;
// The main constructor
public function Main()
{
// create an instance of the ANE interface
_mOuyaNativeInterface = new OuyaNativeInterface();
// Initialize the ANE by passing your developer id
_mOuyaNativeInterface.OuyaInit(DEVLEOPER_ID);
}
}
}
- In order to receive IAP extents, use the ANE interface
context
to add a listener to get status events.
// callback for context status events
private function onStatusEvent( _event : StatusEvent ) : void
{
// status events have a String code
_mOuyaNativeInterface.LogInfo("Code: " + _event.code );
// status events have a String level
_mOuyaNativeInterface.LogInfo("Level: " + _event.level );
}
// The main constructor
public function Main()
{
// create an instance of the ANE interface
_mOuyaNativeInterface = new OuyaNativeInterface();
// Initialize the ANE by passing your developer id
_mOuyaNativeInterface.OuyaInit(DEVLEOPER_ID);
// Add a status event listener on the ANE context
_mOuyaNativeInterface.GetExtensionContext().addEventListener( StatusEvent.STATUS, onStatusEvent );
}
}
Context
status events provideIAP
callbacks and latency free input. Theevent
code indicates the type of event. And theevent
level was used to pass aJSON
string with the data for the event. The example includes all the parsing logic for theJSON
responses.
private function onStatusEvent( _event : StatusEvent ) : void
{
if (_event.code == "Axis") {
Axis(_event.level);
return;
} else if (_event.code == "ButtonDown") {
ButtonDown(_event.level);
return;
} else if (_event.code == "ButtonUp") {
ButtonUp(_event.level);
return;
}
// Display status events in the example label
LblStatus.text = "STATUS: "+_event.code;
if (_event.code == "RequestGamerInfoOnSuccess") {
RequestGamerInfoOnSuccess(_event.level);
} else if (_event.code == "RequestGamerInfoError" ||
_event.code == "RequestProductsError" ||
_event.code == "RequestPurchaseError" ||
_event.code == "RequestReceiptsError" ||
_event.code == "RequestGamerInfoOnFailure" ||
_event.code == "RequestProductsOnFailure" ||
_event.code == "RequestPurchaseOnFailure" ||
_event.code == "RequestReceiptsOnFailure" ||
_event.code == "RequestGamerInfoOnCancel" ||
_event.code == "RequestProductsOnCancel" ||
_event.code == "RequestPurchaseOnCancel" ||
_event.code == "RequestReceiptsOnCancel") {
OnGenericError(_event.code, _event.level);
} else if (_event.code == "RequestProductsOnSuccess") {
RequestProductsOnSuccess(_event.level);
} else if (_event.code == "RequestPurchaseOnSuccess") {
RequestPurchaseOnSuccess(_event.level);
} else if (_event.code == "RequestReceiptsOnSuccess") {
RequestReceiptsOnSuccess(_event.level);
} else {
_mOuyaNativeInterface.LogInfo("Code: " + _event.code );
_mOuyaNativeInterface.LogInfo("Level: " + _event.level );
}
}
IAP
methods use the reference to the ANE
interface and the context
status events to get responses.
RequestProducts
takes a JSONArray
of String
product identifiers to get the details about the entitlement
or consumable
.
var jsonData:String = "[\"long_sword\",\"sharp_axe\",\"__DECLINED__THIS_PURCHASE\"]";
_mOuyaNativeInterface.RequestProducts(jsonData);
RequestPurchase
takes a String
identifier for the entitlement
or consumable
being purchased.
var identifier:String = "long_sword";
_mOuyaNativeInterface.RequestPurchase(identifier);
RequestReceipts
takes no arguments and the callback gets a list of receipts that the logged in gamer has purchased associated with the package.
_mOuyaNativeInterface.RequestReceipts();
RequestGamerInfo
gets the GamerInfo
for the logged in gamer. The GamerInfo
holds the gamer UUID
and Username
.
_mOuyaNativeInterface.RequestGamerInfo();
Shutdown
cleanly shuts down the ANE
interface before exiting.
Import the NativeApplication
namespace to get access to the singleton and exit method.
import flash.desktop.NativeApplication;
Shutdown the ANE
interface before exiting the application.
_mOuyaNativeInterface.Shutdown();
NativeApplication.nativeApplication.exit();
Adobe Scout
is a free profiling tool from Adobe Creative Cloud
that allows you to find memory issues and performance bottlenecks in your Air
apps running on Android
.
-
You will need to sideload
Adobe Scout
on to theForge TV
to install the profiler. -
The sideloaded
Adobe Scout
will appear inSettings->Apps->Downloaded apps
.
-
You will need to plug in a
USB mouse
into theForge TV
in order to navigate theAdobe Scout
application. -
Use the
mouse
to check the box next toEnable
.
-
You will need to manually enter the
IP Address
of yourdesktop/laptop
runningAdobe Scout
which should be running. -
In
Flash
use theFile->Publish Settings
menu item.
- On
Publish->Flash (.swf)
be sure to enable theEnable detailed telemetry
and clickOK
.
- Republish and sideload the application and upon launch
Adobe Scout
will auto connect.
Using the profiler, it turns out that using the ANE
interface to check axis and button states negatively affected performance. ANE
calls have a whole lifecycle triggered by each call. And requesting all the axis and buttons for 4
controllers added 36
millisecond of lag each update frame. The lag introduced bad frame rates and input queueing. Instead, a context
status event was added to provide latency free axis and button events. The following JSON
response parsing can be used for input without any lag.
- Add the
context
status event in theMain
constructor.
public function Main()
{
// create an instance of the ANE interface
_mOuyaNativeInterface = new OuyaNativeInterface();
// initialize the ANE
_mOuyaNativeInterface.OuyaInit(DEVLEOPER_ID);
// Add the status event
_mOuyaNativeInterface.GetExtensionContext().addEventListener( StatusEvent.STATUS, onStatusEvent );
}
- Add the status event handler.
private function onStatusEvent( _event : StatusEvent ) : void
{
if (_event.code == "Axis") {
Axis(_event.level);
return;
} else if (_event.code == "ButtonDown") {
ButtonDown(_event.level);
return;
} else if (_event.code == "ButtonUp") {
ButtonUp(_event.level);
return;
}
}
- Add JSON parsing methods for input.
private function Axis(jsonData:String):void
{
var json:Object = JSON.parse(jsonData);
var playerNum:int = json.playerNum;
var axis:int = json.axis;
var val:Number = json.value;
// logs the input event, comment out this logging in your actual game
_mOuyaNativeInterface.LogInfo("Axis: playerNum:"+playerNum+" axis:"+axis+" value:"+val);
if (axis == OuyaController.AXIS_LS_X) {
} else if (axis == OuyaController.AXIS_LS_Y) {
} else if (axis == OuyaController.AXIS_RS_X) {
} else if (axis == OuyaController.AXIS_RS_Y) {
} else if (axis == OuyaController.AXIS_L2) {
} else if (axis == OuyaController.AXIS_R2) {
}
}
private function ButtonDown(jsonData:String):void
{
var json:Object = JSON.parse(jsonData);
var playerNum:int = json.playerNum;
var button:int = json.button;
// logs the input event, comment out this logging in your actual game
_mOuyaNativeInterface.LogInfo("ButtonDown: playerNum:"+playerNum+" button:"+button);
if (button == OuyaController.BUTTON_O) {
} else if (button == OuyaController.BUTTON_U) {
} else if (button == OuyaController.BUTTON_Y) {
} else if (button == OuyaController.BUTTON_A) {
} else if (button == OuyaController.BUTTON_L1) {
} else if (button == OuyaController.BUTTON_L3) {
} else if (button == OuyaController.BUTTON_R1) {
} else if (button == OuyaController.BUTTON_R3) {
} else if (button == OuyaController.BUTTON_DPAD_DOWN) {
} else if (button == OuyaController.BUTTON_DPAD_LEFT) {
} else if (button == OuyaController.BUTTON_DPAD_RIGHT) {
} else if (button == OuyaController.BUTTON_DPAD_UP) {
} else if (button == OuyaController.BUTTON_MENU) {
}
}
private function ButtonUp(jsonData:String):void
{
var json:Object = JSON.parse(jsonData);
var playerNum:int = json.playerNum;
var button:int = json.button;
// logs the input event, comment out this logging in your actual game
_mOuyaNativeInterface.LogInfo("ButtonUp: playerNum:"+playerNum+" button:"+button);
if (button == OuyaController.BUTTON_O) {
} else if (button == OuyaController.BUTTON_U) {
} else if (button == OuyaController.BUTTON_Y) {
} else if (button == OuyaController.BUTTON_A) {
} else if (button == OuyaController.BUTTON_L1) {
} else if (button == OuyaController.BUTTON_L3) {
} else if (button == OuyaController.BUTTON_R1) {
} else if (button == OuyaController.BUTTON_R3) {
} else if (button == OuyaController.BUTTON_DPAD_DOWN) {
} else if (button == OuyaController.BUTTON_DPAD_LEFT) {
} else if (button == OuyaController.BUTTON_DPAD_RIGHT) {
} else if (button == OuyaController.BUTTON_DPAD_UP) {
} else if (button == OuyaController.BUTTON_MENU) {
}
}