majalah flash&flex

Page 1




Editor’s Note Dear Readers,

December is a special month for everyone. Before you fall into Christmas-shopping spree , check what’s new in Flesh and Flex Developer’s Magazine. Let’s start from the third part of Huw Collingbourne series .He explains how to create family trees of objects by creating class hierarchies. 3D world of Alternativa3D engine has many similarities with flat flash world as well as many differences, so why Yuri Malsev learns us what we shoud know about the Alternativa 3D Platform. Very interesting is also article based on chapter 6 of HTML5 in Action By Robert Crowther, Joe Lennon, and Ash Blue .With Canvas 2D Context, you have all the benefits of Flash and your games will run on mobile devices and without a plugin. Is Adobe still committed to Flash Builder? Will Adobe continue to support customers using Flex? This questions are from Gabor Csomak who wrote for us News about Flash Mobile. Last month we began our journey to the Design Patterns land with the Singleton Pattern. This month we will see the Factory pattern and how can you apply it to simplify our work when dealing to databases connections with Marco Pires. Have you ever thought of watching DVD movies performed by favorite actors on Android devices, such as Android phone, Samsung Galaxy Tab, Fascinate, HTC Desire, etc.? If yes, you shoul read a review of WinX DVD Ripper in our magazine. Becouse this is the last issue before a New Year I wish you all the best in 2012. Thanks to all readers, authors and betatesters who are very helpful. Merry Christmas and a Happy New Year for everyone! Enjoy your reading Adela Kuźniarska and FFD Team

Editor: Adela Kuźniarska adela.kuzniarska@software.com.pl Proofreaders: Garry Schafer, Michael Jovel,Alberto Jose Argon Alvarez, Nick Middleweek, Ranjan Rajendran, Ed Werzyn DTP Team: Ireneusz Pogroszewski ireneusz.pogroszewski@software.com.pl Art Director: Ireneusz Pogroszewski ireneusz.pogroszewski@software.com.pl Senior Consultant/Publisher: Paweł Marciniak Flex/ActionScript 101 Section Editor: Marc Pires marcpiresrj@gmail.com iPhone Development Section Editor: Ryan D’Agostino ActionScript in Action Section Editor: Huw Collingbourne Games Section Editor: Chris Hughes Contributing Editors: Pedro de La Rocque, Ali Raza, Csomák Gábor Publisher: Software Press Sp. z o.o. SK ul. Bokserska 1 02-682 Warszawa Poland Worldwide Publishing If you are interested in cooperating with us, please contact us by e-mail: cooperation@software.com.pl Whilst every effort has been made to ensure the high quality of the magazine, the editors make no warranty, express or implied, concerning the results of content usage.

4

All trade marks presented in the magazine were used only for informative purposes. All rights to trade marks presented in the magazine are reserved by the companies which own them. Thanks to the most active and helping beta testers: Russell TangChoon, Lee Graham, Jassa Amir Lang, Ed Werzyn, Yann SmithKielland, Justus, Csomák Gábor, Kevin Martin, Charles Wong, Ali Raza, Almog Koren, Izcoatl Armando Estanol Fuentes, Lionel Low, Michael J. Iriarte, Paula R. Mould, Rosarin Adulseranee, Sidney de Koning To create graphs and diagrams we used company.

program by

Mathematical formulas created by Design Science MathType™

ATTENTION!

Distributing current or past issues of this magazine – without permission of the publisher – is harmful activity and will result in judicial liability.

DISCLAIMER!

The techniques described in our articles may only be used in private, local networks. The editors hold no responsibility for misuse of the presented techniques or consequent data loss.

12/2011 (30)


CONTENTS InBrief 8 End of Flash Mobile GABOR CSOMAK

Gabor Csomak answers the questions like- does Adobe recommended we use Flex or HTML5 for our enterprise application development? Is Adobe still committed to Flax and Flash Builder? Will Adobe continue to support customers using Flex? And much more in his news column – go and read!

ActionScript in Action 10 Starting ActionScript Programming Part Three HUW COLLINGBOURNE

In the third part of this series, Huw Collingbourne explains how to create family trees of objects by creating class hierarchies. You will learn how to create get/set accessor properties and calling superclass constructors.

Framework For Storage3D 14 Proscenium As A Rapid Prototyping Framework For Stage3D ARMANDO ESTANOL

Stage3d is a marvellous technology that allows many more digital artist to incoporate 3D into Flash. From this article you will get to know about the preview 1 version of the Proscenium framework and libraries for rapid 3D application development and how to make a small animation. You will also cover how the 3D rendering workflow goes on this framework. You should be familiar with programming as3 and finding your way in Flash Builder.

HTML5 in Action 28 Creating a Canvas Game ROBERT CROWTHER, JOE LENNON, AND ASH BLUE

With Canvas 2D Context, you have all the benefits of Flash and your games will run on mobile devices

12/2011 (30)

and without a plugin. In addition, one HTML5 Canvas application can be distributed across multiple platforms through mobile frameworks like PhoneGap.com. This article based on chapter 6 of HTML5 in Action, shows you how to create your first Canvas game, taking advantage of physics, advanced animation, and keyboard/mouse controls.

Alternativa3D 40 What is Alternativa3D YURI MALTSEV

AlternativaPlatform is a company, headquartered in Perm, Russia. It develops and supports technology solutions for browser-based multiplayer projects. Article from Youri Maltsev from who you will know what is Alternativa3D and how to get started with it.

Patterns Factory 46 Applied Design Patterns Factory MARC PIRES

Marc Pires is a Developer located in Brazil, during his career, he has worked in various roles such as Network Administrator, Instructor. Last month we began our journey to the Design Patterns land with the Singleton Pattern. This month we will see the Factory pattern and how can you apply it to simplify our work when dealing to databases connections.

Review 50 WinX Free DVD Ripper MASON

Have you ever thought of watching DVD movies performed by favorite actors on Android devices, such as Android phone, Samsung Galaxy Tab, Fascinate, HTC Desire, etc.? Ideal Solution for Appreciating DVD Movies on Android shows by a freelance writter Mason.

5


Voices That Matter: Android Developers Conference February 9-10, 2012 | San Francisco, Ca http://android2012.voicesthatmatter.com/

November 25, 2011 CONTACT: Lisa Comeau 941-224-1536 lisa@alphastarpartners.comt

Android Developers Conference Taught by the Industry’s Leaders SAN FRANCISCO, CA. –Pearson Education, the world’s largest learning company introduces the premiere of the Voices That Matter: Android Developers Conference, taking place in San Francisco, February 9-10, 2012 at the Julia Morgan Ballroom. Both Google insiders and outside experts will discuss, demonstrate, teach and listen in a series of how-to sessions, application case studies and interactive panels, where attendees will learn all about Android and its latest SDK, called Ice Cream Sandwich. The Voices That Matter: Android Developers Conference will be an intimate, focused and informative event in which attendees will have the opportunity to learn from leading authors and practitioners, while networking with fellow developers. The conference program was developed by co-chairs and authors, Shane Conder and Lauren Darcey. This two-day conference will be discussing the latest and hottest trends in Android application development, covering everything in relation to development. From Succeeding in the Android Market, Game Programming, Understanding the UI Toolkit, to the breakdown of Ice Cream Sandwich: the latest Android SDK, Augmented Reality and many more. For beginner developers, Voices That Matters offers a pre-conference developer boot camp where attendees can learn the ins-and-outs from expert Android developers. To register, visit http://voicesthatmatter.com/android2012/register.aspx. Use Priority Code: ANDR923 to save $150 off the current conference price. Combine it with Early Bird pricing in effect through January 5, 2012, for a total saving of $350! The Pearson Education Voices That Matter Conference series are led by the top leaders in the industry and give attendees access to these experts. These intimate conferences are specifically designed to enable participants to share their experiences and leverage the accumulated wisdom of the entire conference community. For more information, please contact conference manager Barbara Gavin at Barbara.Gavin@Pearson.com or call (617) 848-7026.

6

12/2011 (30)



IN BRIEF

End of Flash Mobile

Adobe is discontinuing work on the mobile Flash Player. What this means in short is that Flash Player 12 will not come to Android. Also there won’t be Flash Player for iOS, and Bada, nor Windows Phone. Unfortunatelly, the global media announced it as the end of Flash, and this has sapped the confidence of a lot of people, customers and developers alike. It is not true, Flash Player is on the 99% of desktop computers, and on the premium category Android phones and tablets, and also on BBX platform. Adobe will focus on AIR for Mobile, instead of Flash Player, because AIR is on all major platforms, and Flash Player is not, and couldn’t be because of iOS. Flash Player will remain to develop expressive content (particularly games and video) in the browser on the desktop. Read the clarifications here: http:// www.mikechambers.com/blog/2011/11/11/clarificationson-flash-player-for-mobile-browsers-the-flash-platformand-the-future-of-flash/?utm_source=feedburner&utm_ medium=feed&utm_campaign=Feed%3A+MikeChamb ers+%28Mike+Chambers%29. Some of the questions:

Is Adobe still committed to Flex?

Yes. We know Flex provides a unique set of benefits for enterprise application developers. We also know that the technology landscape for application development is rapidly changing and our customers want more direct control over the underlying technologies they use. Given this, we are planning to contribute the Flex SDK to an open source foundation in the same way we contributed PhoneGap to the Apache Foundation when we acquired Nitobi. This project will be jointly led by some developers from the Flex SDK engineering team along with key developers from the Flex community, including members of the Spoon Project and contributors from enterprise companies currently using Flex. Flex SDK feature development will continue under a new governance model and Adobe will continue to contribute to the Flex SDK.

continue making significant contributions to open web technologies like WebKit & jQuery, advance the development of PhoneGap and create new tools that solve the challenges developers face when building applications with HTML5.

Is Adobe still committed to Flash Builder?

Yes. Flash Builder will continue to be developed and Adobe will work to ensure Flex developers can use Flash Builder as their development tool with future releases of Flex SDK.

Will Adobe continue to support customers using Flex?

Yes. Adobe will continue to honor existing Flex support contracts.

So, what’s next?

We are close to wrapping up development on Flex 4.6 SDK and it will be released on November 29th 2011. Following this, we will begin the process of moving to the open development model described above. On a personal note, we recognize we could have handled the communication better and promise to share regular updates over the coming weeks and months. We believe these changes to the Flex SDK development model will ensure that the broader community can continue to use and directly enhance Flex for many years to come. source: Adobe Blogs

Sony AIR App Challenge Ended.

Here is the list of the winners, www.airappchallenge.com/ winners.php who proved the awesomness of Adobe AIR for mobile.

Does Adobe recommend we use Flex or HTML5 for our enterprise application development?

In the long-term, we believe HTML5 will be the best technology for enterprise application development. We also know that, currently, Flex has clear benefits for large-scale client projects typically associated with desktop application profiles. Given our experiences innovating on Flex, we are extremely well positioned to positively contribute to the advancement of HTML5 development, starting with mobile applications. In fact, many of the engineers and product managers who worked on Flex SDK will be moving to work on our HTML efforts. We will

8

The winner is a game: Tweet Hunt, Runner ups are Conqu, Conveyor, Kidoodle, Sylvester’s Band, Level, and Cassandra Stand. Show these apps to everybody, to spread the good word for AIR. source: Adobe Blogs

News by Gábor Csomák (@csomak)

12/2011 (30)


12/2011 (30)


ACTIONSCRIPT IN ACTION

Starting ActionScript Programming

part three

In the third part of this series, Huw Collingbourne explains how to create family trees of objects by creating class hierarchies. What you will learn…

What you should know…

• How to create class hierarchies • How to create get/set accessor properties • Calling superclass constructors

• Basics of classes (see last month’s column) • How to write ActionScript code in external �les • How to create objects from classes

I

n my last column, I explained some of the essential features of ActionScript Object Orientation. You might recall that I said that just about everything you use in ActionScript is an object. An object can be thought of as a container that wraps up both data (variables) and behaviour (functions or methods). Each object is defined by a class. You can think of a class as a sort of blueprint. Just as many individual cars can be constructed from a single car blueprint, so many ActionScript objects can be constructed from a single class. You could create a Car class that defines set of data (such as the size, colour and cost) and behaviour (its speed and fuel consumption) common to all cars. You would then be able to then create any number of separate car objects based on that class.

�����������

�����������������

��������������

����������������� �����

����������

����������������� ����������

Figure 1. A Simple Game Class Hierarchy Here the Thing class de�nes name and description. Both the Room and the Treasure class inherit name and description. The Treasure class adds on value. The Room class adds on directions: n, s, e, w

10

Class Hierarchies

There is more to object orientation than creating objects from unrelated class definitions, however. The real power of object orientation resides in its ability to create related classes. So you might, for instance, create a generic Car class with wheels, colour and speed. Then you could create a new class, let’s say a RollsRoyce class, as a descendent of the Car class. The RollsRoyce class automatically inherits all the features of the Car class so you don’t have to program them all over again. But it also adds on a few new features such as, for example, a cocktail cabinet and leather upholstery (I’m guessing here. Sad to say, I am not, in fact, a Rolls Royce owner). Let’s look at another example. Let’s assume you are programming a game of some sort. The game is full of all kinds of ‘things’ including Treasures and Rooms. In other words, a Treasure is one type of Thing and a Room is another type of Thing. In order to write this in code I need to create a Thing class and make the Room and Treasure classes descend from it. Now I can code the behaviour of Things in general in the Thing class. The Treasure class will automatically ‘inherit’ all the features of the Thing class, so I won’t need to code those features all over again. The Treasure class will then add one or more additional features which are specific to Treasure objects – for example, a value. The Room class will add features that are specific to Rooms – for example, the exits at the north, west, east

12/2011 (30)


Starting ActionScript Programming

and south directions. You can see a plan of this class hierarchy in Figure 1. As a general rule, when creating a class hierarchy, the classes with the most generalised behaviour are higher up the hierarchy than classes with more specialist behaviour. So a Thing class which is relatively simple, having only a name and a description, is the ancestor of a Treasure class which is a bit more complex since it has a name, a description and, additionally, a value. The Thing class here is also the ancestor of the Room class which has a name, a description and exits in the four compass directions.

Coding Classes in ActionScript

Now let’s look at the actual code we need to write in order to create this class hierarchy. First, the Thing class (Listing 1). This defines two private variables, _name and _description and provides public get/set accessors for Listing 1. The Thing Class package { public class Thing { private var _name: String;

private var _description: String; public function Thing( aName:String, aDescription:

String ) {

_name = aName; }

_description = aDescription;

them. In ActionScript functions that use the word get after function can be used to return a value. Those that use set can be used to assign a value. The advantage of get/set accessors over regular functions is that they permit any code that wants to refer to the function name to do so using a syntax that is similar to that used when referring to variables. When you define private variables with public accessor methods, these may be called properties. So, to assign and retrieve a value to and from the name property you can write this: var aName:String = thing1.name; TextArea1.text = aName;

As explained last month, a function which has the same name as the class and the code file which contains the class, is called a constructor. It runs when a new object is created from the class and you may optionally add arguments to the constructor definition so that variables are assigned values whenever an object is created. that’s what I’ve done in the Thing constructor: public function Thing( aName:String, aDescription:String ) { _name = aName;

}

_description = aDescription;

Listing 2. The Treasure Class descends from Thing package { public class Treasure extends Thing {

public function get name( ): String {

}

private var _value:Number;

return _name;

public function Treasure( aName:String,

public function set name( value: String ): void {

}

aDescription:String, aValue: Number ) {

_name = value;

super( aName, aDescription );

_value = aValue;

public function get description( ): String {

}

}

public function get value ():Number {

return _description;

public function set description( value: String ):

void {

}

_description = value;

}

public function set value (value:Number):void {

}

} }

12/2011 (30)

return _value;

_value = value;

} }

11


ACTIONSCRIPT IN ACTION

When I want to create a descendent class, such as Thing I add the word extends to the class definition followed by the name of the class’s ancestor or superclass. Here Treasure descends from Thing so Thing is the superclass of Treasure (Listing 2). This is how I indicate that fact in the definition of the Treasure class: Listing 3. The Room Class also descends from Thing package {

public class Treasure extends Thing

So now the Treasure class inherits the name and description properties of its superclass, Thing. Notice that the Treasure constructor receives arguments called aName and aDescription but it doesn’t assign these itself. Instead it passes these to the constructor of superclass by calling super() in this way: super( aName, aDescription );

private var _n:int;

The Treasure constructor takes a third argument, aValue, which it can’t pass to its superclass since the Thing class does not have a value property. Treasure assigns this to its own _ value variable:

private var _e:int;

_value = aValue;

public class Room extends Thing {

private var _s:int; private var _w:int; public function Room( aName:String, aDescription:

String, nDir:int, sDir:int, eDir:int, wDir:int ) {

super( aName, aDescription );

_n = nDir; _s = sDir; _e = eDir;

}

_w = wDir;

} }

Listing 4. Creating Thing, Treasure and Room objects private function init ():void { var thing1:Thing; var room1:Room;

The Room class also descends from Thing. It too inherits name and description but it adds integer variables for the four directions (in a real game, these might refer to Room objects at given indexes in an array of Rooms). As I don’t want the exits to change during the game play, I make the variables private and I don’t provide any public accessors (Listing 3). So now I have the class hierarchy shown in the diagram (Figure 1). All that remains to be done is to create some objects from them. Refer to Listing 4 to see an example of this. One deficiency of my class hierarchy at this point is that I have created all the classes in the top-level disk directory of my application. In ActionScript the top level directory of the class is indicated by the fact that the class definition is enclosed between the keyword package and a pair of curly braces: package { }

var room2:Room;

var treasure1:Treasure; var treasure2:Treasure;

thing1 = new Thing("a wotsit", "a vague thingummy" );

In a complex program, the top level directory would soon become pretty cluttered if all the source files were kept there. In next month’s column, I’ll explain how you can organise your code into subdirectories and named packages.

room1 = new Room( "the Troll Room", "a dank

dungeon", -1, -1, 3, -1 );

room2 = new Room( "the Dragon's Lair", "a crystalencrusted cave", 2, -1, 4, 3 );

treasure1 = new Treasure( "emerald ring", "a

magical Elvish ring", 400 );

treasure2 = new Treasure( "golden amulet", "an }

12

amulet of power", 1200 );

HUW COLLINGBOURNE Huw Collingbourne is Director of Technology at SapphireSteel Software. Over more than 20 years, he has programmed in languages ranging from Ruby to C# and has been a technical columnist for computer magazines such as PC Pro and PC Plus. He is the software lead of the Amethyst Designer, the Flex user interface design environment of the Amethyst IDE for Visual Studio. SapphireSteel Software: http:// www.sapphiresteel.com/

12/2011 (30)


Bringing fresh AIR to UML

With this code you can buy Crocus Modeller license for as low as $34.3 Use the code at crocusmodeller.com/buy.html (you'll be asked for it just before checking out) The code is valid until 2011-11-15 00:00:00 MST

CrocusModeller.com | Fresh AIR in UML


FRAMEWORK FOR STORAGE3D

Proscenium As A Rapid Prototyping Framework For Stage3D Stage3D is a marvellous technology that allows many more digital artists to incorporate 3D into Flash. Having GPU accelerated support; we are now able to create very complex scenes and animations without using the CPU or seeing a degradation of performance with very few elements on screen. What you will learn…

What you should know…

• You will get to know about the preview 1 version of the Proscenium framework and libraries for rapid 3D application development and how to make a small animation. We will also cover how the 3D rendering work�ow goes on this framework. You will learn some things about 3D along the way. This will become the foundation to understand more complex projects under the Proscenium framework..

• You should be familiar with programming as3 and �nding your way in Flash Builder.

S

ome 3D frameworks have been created over the years with the idea of giving 3D animation capabilities to Flash. While these were pretty well done, the CPU limitations were reached very early before our creative spirit had just started to explore. Stage 3D has come along with some new technologies to compensate for this, and the good news is that the time invested on many of those frameworks is not lost. Many classic 3D frameworks have been moved into Stage3D and are now available with more power than ever. I started looking at Stage 3D and some inner working basics. We now know how a basic Stage3D application is created to incorporate the GPU streamlined pipeline.

The pipeline requires a couple of data sets: one for vertexes describing our objects, and one for describing relationships between these vertexes, in order to conform the triangles that describe our 3D objects. A couple of AGAL programs (Vertex and Fragment Shader) are needed to modify the final look of our output. While knowing all of that is important to a deep understanding of what is really going into Stage3D, but there are times when we really need to pitch out a fast prototype, or when we just need to test a concept. Making a complete Stage3D implementation from square one is a time consuming task, while on the other hand, the same could go for a full framework

����������������

������������ ���������

���������

����� �������

��������

������������ �������� ��������

����������������

����������

���������

������������� �������������

��������������� ���������

��������� ������������� �����������

��������� ������������

����������������

���������������

�������������

�������������������

������������ �������� ��������

Figure 1. Proscenium setup sequence

14

12/2011 (30)


Proscenium As A Rapid Prototyping Framework For Stage3D

���������� �������

����

����

����

�����

���������

��������������

�������

����������

������������ �����������������

������������

Figure 2. Proscenium download folder structure

implementation. In this scenario, we need to have access to a fast and simple way to meet our goal. That solution is being worked by Adobe, and its name is Proscenium. That is, its code name, since it is still an Adobe Labs creation, so it is to be treated more as a proof of concept at this moment of time. Adobe even disclaims that it could not make it into a real product, so you can use it on your own, but I believe that for fast setups, it makes a great job. Proscenium is available at http://labs.adobe.com/ technologies/proscenium/ where you can download it. You must also take into consideration that if you are also intending to play with Pixel Bender 3D (which we will not cover by now), you must make a couple of additional downloads and installations, for the Pixel Bender 3D beta and the Pixel Bender SDK, which is needed by the first. Proscenium is a set of libraries that build on top of Stage3D and which are intended to make a fast start as a means to rapid development. The download package

Figure 4. Adding the PLAYERGLOBAL variable to our project

contains the ActionScript 3 library files, a complete documentation (up to its current state), and a group of sample and tutorial codes to help you. If you want to dig a little more into build instructions, and more details of the current preview release of Proscenium, you can do it at http://labs.adobe.com/ wiki/index.php/Proscenium. To start with the development, there are three specific elements that should be installed in your system before Flex Builder is able to incorporate the Proscenium libraries in our projects. These are: • • •

Latest Flash Player Content Debugger (http:// www.adobe.com/support/flashplayer/downloads.html) Latest Video Drivers (available in ATI or NVidia site) In Windows, you should also check if you have the latest DirectX runtime (at Windows downloads site)

After installing these elements, we can now proceed to configure the last details inside Flash Builder. Under Windows > Preferences we will add variables to link the Proscenium and Pellet libraries, and the Flash Player Content Debugger version. • •

Under General > Workspace > Linked Resources add a new PLAYERGLOBAL variable and search the copy of playerglobal.swc. Add a PROSCENIUM _ LIBS variable pointing to the libs folder that you downloaded.

As a last step, under Flash Builder > Debug select the copy of the Flash Player Content Debugger that you downloaded. There are some additional steps that should be done, for those of us who want to play with Pixel Bender 3D. We will not do those by now. The folder structure that is included in the Proscenium package download contains 4 folders: • Figure 3. Creating an ActionScript project

12/2011 (30 )

Code – this includes 3 subdirectories for Proscenium tutorial files, ActionScript3 examples, and a Flex

15


FRAMEWORK FOR STORAGE3D

Figure 5. Adding the PROSCENIUM_LIBS variable to our project

• • •

example (which shows how it is easily integrated in an MXML application). Docs – this contains the library documentation. Libs – this contains the Proscenium library and the Pellet library for rapid prototyping and physics simulation on your application. Tools – this has an empty Pixel Bender directory, used for Pixel Bender 3D support to extend the shaders available on Proscenium.

To start our way into the Proscenium framework, we must start a new Flash ActionScript project. You can also make a Flex project, but in our example we will exclude MXML programming to keep things simple. Select Next. In this next screen, we will add the environment variables that were configured early in the Flash Builder application. Add a new SWC and use the PLAYERGLOBAL variable to point to the playerglobal.swc file. If you are not allowed to continue (Ok is greyed out) check your variable name and take notice that it is between curly braces and not parenthesis.

Figure 7. ActionScript Compiler settings

Add a new SWC Folder (remember that it is a folder, because we have in it the Proscenium and Pellet libraries) and use the PROSCENIUM_LIBS variable. The project window should look like this now that we have included the SWC and SWC Folder: Figure 6. When Flash Builder has finished creating our new project, there is just a last step to configure, so we are able to use the Stage3D platform, which is the base for the Proscenium framework. Under the ActionScript Compiler settings, you must specify the –swf-version=13 argument to the compiler. Remember to access the Properties of your project, by right clicking your mouse in your project on the Package Explorer window. Now everything is ready to build our first Proscenium application. I have planned a really small program that will help us understand the basic building blocks of the Proscenium library. The scene will show a plane, which will make as a ground reference. In that manner we will perceive better the 3D effect. The scene also consists of a camera, a couple of objects (a cube and a sphere) and a light. These are the very basic elements that a Proscenium application should contain. Making an analogy to the real word, we are putting a spectator, a point of interest and a light that will allow the spectator to observe the objects (just as in the real life, you would not be able to look to an object without a light). ������� �����

�������

������

Figure 6. Project con�gured for Proscenium framework usage

16

Figure 8. Basic scene elements

12/2011 (30)


Proscenium As A Rapid Prototyping Framework For Stage3D

Listing 1. General view of code package{

// import calls public class ProsceniumTest extends Sprite{

// define constansts and variables

public function ProsceniumTest():void{

// Setup Stage3D, Context3D and event handlers for content creation events

}

private function contextCreated( event:Event ):void{

// Setup Proscenium Instance3D object and animation event handlers // Call camera, lights and object creation functions

}

private function setCamera():void{

// Setting up the camera

}

private function setObjects():void{

// Setting up the scene with some objects

}

private function setLight():void{

// Setting up the light

}

private function resizeMe(event:Event=undefined):void{

// Event handler for resizing of flash movie

}

protected function animationLoop(event:Event):void{

// Here we change the objects position and send new image to screen

}

private function contextCreationError(error:ErrorEvent):void{

// Output error information

}

private function resize(width:int,height:int):void{

// Configure the video buffer memory to display our viewport

}

}

}

���������������

����������

������������ ���������� ������

�������������� ���������� �������

����������������

������������ �������� ������������

������������� ���������� ��������������

����������������

����������������� ��������������� ���������������

�����������

������������� �������������� ����������

����������������������

������������

������ ��������� ��������������

������������� ��������������� ������� ����������

������������ ���������������� �������

Figure 9. Program �ow

12/2011 (30 )

17


FRAMEWORK FOR STORAGE3D

These elements will have certain attributes associated to them. This will help us to describe their position, colour, material, intensity, etc. Our example will be based on a simple flow and the Tutorial #5 that comes with the Proscenium library Listing 2. Metadata for our Flash movie [SWF(width="640",height="480",backgroundColor="0x333 333",frameRate="125")]

Listing 3. Constants for the 3D world and animation private const ORIGIN:Vector3D=new

Vector3D();

private const CAM_ORIGIN:Vector3D=new

Vector3D(0,0,20);

private const AMPLITUDE:Number=3.5;

Listing 4. The basic 3D objects in this Proscenium example private var myS3D:Stage3D;

private var myProsceniumInstance3D:

Instance3D;

Listing 5. Main objects declaration private var myCamera:SceneCamera;

private var myMainLight:SceneLight; private var myCubeObject:SceneMesh;

package. We will setup our Stage3D object as usual, and then we will incorporate our basic elements (camera, objects and light) to the scene. There are a couple of event handlers to support resizing the screen, to avoid object distortion. The other handler is for the frame event, and it will be used to create the main animation loop cycle. The animations that the objects execute are sine and cosine based movements along a couple of axis for both objects. It demonstrates how you can manipulate elements inside the scene. The general schematic of the code flow looks is shown on the following diagram. The code structure is represented in the following Listing 1. There is an important library that is included in our demo program. The scenegraph library, which gives support to the Proscenium framework, contains all that we need to play with it. We include a small metadata before our class just to test our application out of the browser with an initial VGA sized screen and define its background colour. We must also change the Project properties under the ActionScript Compiler section. Just remove the tick from the Generate HTML Wrapper file setting. This will just publish the swf file and play it on the standalone Flash Player. We start by defining inside of our class some constants and variables needed to define things in our program. The first group consists of the constants that declare the relative origin of our 3D world. This position defined as (0,0,0) will become the absolute position to

private var mySphereObject:SceneMesh; private var myPlaneObject:SceneMesh;

private var myMaterial:MaterialStandard;

������� �����

Listing 6. Constructor function public function ProsceniumTest():void{

this.stage.scaleMode=StageScaleM

ode.NO_SCALE;

this.stage.align=StageAlign.TOP

_LEFT;

�����������

Figure 10. A point light is a distant light far in the distance

myS3D=stage.stage3Ds[0]; myS3D.x=10;

������������������ ����������������

myS3D.y=10;

myS3D.addEventListener(Event.CON

TEXT3D_CREATE,contextCreated);

myS3D.addEventListener(ErrorEven

t.ERROR,contextCreationError);

myS3D.requestContext3D(Context3D

}

RenderMode.AUTO);

������� ������ ������������������ ���������������

Figure 11. Showing append versus prepend

18

12/2011 (30)


Proscenium As A Rapid Prototyping Framework For Stage3D

which our 3D objects will be relative to. We then declare the camera origin point 20 units above the ground level (again, relative to the absolute virtual world origin). And finally, the amplitude will define how far our objects will travel in distance. Try playing with this variable to see our objects move farther or closer from their initial position. Since we will be working with Proscenium as a framework over Stage3D technologies, then we must initialize an Stage3D object and an Instance3D which will receive the Stage3D Context3D requested later, to work with it. We need 3 basic elements to visualize anything on our virtual world: a camera, an object to be seen and light to be able to see it. This is what makes a great

difference between using Proscenium and Stage3D directly. In Stage3D, we must worry about the inner workings of the 3D render pipeline. But under Proscenium framework, we are proposed a more natural 3D environment. These objects will have several attributes associated with them. Some more specialized, like the material of our objects on scene, which is only an attribute of this kind of objects and is not shared with the camera, or the intensity that only applies to lights. But some are common to the three of them, and these are the 3 main transform attributes: translation, rotation and scaling. These are the only 3 operations that you can make to a 3D object and become the first thing you know when using a 3D program. It is important to have

Listing 7. Context3D request is successful private function contextCreated( event:Event ):void{

myProsceniumInstance3D=new Instance3D(myS3D.context3D); resize(stage.stageWidth,stage.stageHeight);

stage.addEventListener(Event.ENTER_FRAME,animationLoop); stage.addEventListener(Event.RESIZE,resizeMe); setCamera();

setObjects();

}

setLight();

Listing 8. Setting up the camera private function setCamera():void{

myCamera=myProsceniumInstance3D.scene.activeCamera; myCamera.identity();

myCamera.position=CAM_ORIGIN;

myCamera.appendRotation(-15,Vector3D.X_AXIS); }

myCamera.appendRotation(-25,Vector3D.Y_AXIS,ORIGIN);

Listing 9. Creating objects for our scene private function setObjects():void{

myPlaneObject=MeshUtils.createPlane(50,50,20,20,null,"plane"); myPlaneObject.transform.appendTranslation(0,-2,0);

myProsceniumInstance3D.scene.addChild(myPlaneObject);

myCubeObject=MeshUtils.createBox(8,2,12,null,"cube"); myCubeObject.appendTranslation(0,3,0);

myProsceniumInstance3D.scene.addChild(myCubeObject); myMaterial=new MaterialStandard;

myMaterial.diffuseColor.set(.8,.2,.3);

mySphereObject=MeshUtils.createSphere(4,32,32,myMaterial,"sphere"); mySphereObject.appendTranslation(4,10,0); }

12/2011 (30 )

myProsceniumInstance3D.scene.addChild(mySphereObject);

19


FRAMEWORK FOR STORAGE3D

this in mind, since we will be using it later when we make our animation. In our example, we will have 1 camera (our main camera), 1 light (our main illumination source) and 3 objects. These 3 objects will be a simple plane, a slanted cube or box and a sphere. The plane will simulate the ground level and will serve as a reference

object for the viewer to help his brain into creating a more efficient 3D effect. The box and the sphere will be moving along some sinusoidal paths to create a simple animation. The camera will represent our virtual position in the 3D world. It will contain some other properties that are the counterparts of its physical optical equivalent, like

Listing 10. Creating light and adding shadows private function setLight():void{

myMainLight=new SceneLight();

myMainLight.color.set(.5,.5,.5);

myMainLight.appendTranslation(-20,20,20); myMainLight.kind="distant";

myMainLight.transform.prependRotation(-45,Vector3D.Y_AXIS);

myMainLight.transform.prependRotation(-70,Vector3D.X_AXIS); myMainLight.shadowMapEnabled=true;

myMainLight.setShadowMapSize(256,256);

myProsceniumInstance3D.scene.addChild(myMainLight);

myMainLight.addToShadowMap(myCubeObject);

}

myMainLight.addToShadowMap(mySphereObject);

Listing 11. Event handler for resizing stage action private function resizeMe(event:Event=undefined):void{

}

resize(stage.stageWidth,stage.stageHeight);

Listing 12. Main animation loop protected function animationLoop(event:Event):void{

myCubeObject.setPosition(

AMPLITUDE*Math.cos(2*getTimer()/10000), 2,

AMPLITUDE*Math.sin(15*getTimer()/10000));

mySphereObject.setPosition(

AMPLITUDE*Math.cos(3*getTimer()/10000),

AMPLITUDE*Math.sin(8*getTimer()/10000), 0);

myProsceniumInstance3D.render(0,false);

/*

myMainLight.shadowMap.showMeTheTexture( myProsceniumInstance3D, myProsceniumInstance3D.width, myProsceniumInstance3D.height, 0, 0, 200); */ }

20

myProsceniumInstance3D.present();

12/2011 (30)


Proscenium As A Rapid Prototyping Framework For Stage3D

the aspect ratio. The light is necessary to allow us to look at the objects. Unlike its physical counterpart, the virtual lights work in an inverse direction. Light normally comes out from its source, travels in a media and hits the object. The resultant bouncing light hits our eyes and makes the impression of figures, colours and textures on them. The virtual light system generates a ray coming from the camera position and into every relative pixel position direction on the screen. That is, it asks what my eyes are seeing in each pixel position. When those rays hit an object, they bounce and continue their trajectory until they hit further objects and bounce again (seen as reflections) or they hit a light. Of course, this could be an unending task, so our virtual rays are limited by number of bounces or distance. The resulting shade of colour on screen is the output of how material properties on the objects affect the direction our rays bounce and the properties of our light. The light object that we will use on this example is called distant. A distant light source has all its rays perceived as parallel to each other. For example, the Sun emits its rays from a point source and they go out in every direction from out of its surface. But those rays that hit the Earth have travelled so far that it appears to us as if those rays are parallel. In our 3D world the distant light source type has real parallelism on its rays. We will also declare an additional object of MaterialStandard type. This will be used to give the sphere a different look to differentiate it from the other objects in the scene which will use the default material. Under the constructor function we setup the general scene attributes, to make our Flash scene not to scale when it is resized. This setting will be used in conjunction with the resize event handler to control how our 3D scene looks and to avoid distortions. We have to obtain the first Stage3D object from the Stage as usual, and we position its viewport 10 pixels down and to the right. The idea behind this is to take

the illusion that our 3D scene needs to have just one viewport occupying the entire screen. We need 2 event listener functions before we request our Context3D object. One will listen to the success in 3D context creation; the other one is in place to catch any error during the procedure. This could happen if the GPU is in use by another application and has no resources available. When the requested Context3D object is created we need to use it to create an Instance3D object. This will be our entry point to the Proscenium framework. We will have to use a custom made resize function that will be explained later. Its function will be to setup our 3D scene with the correct aspect ratio, according to the current Stage size. The function ends by calling the 3 main functions that are in charge of setting up the main elements of our 3D scene. The setCamera function is in charge of getting the active camera from the Proscenium Instance3D object. It then positions it with a clear matrix, through the identity function. Setting objects on our scene will require the use of the MeshUtils library. We will use the createPlane, createBox and createSphere functions. We will be assigning a name to each object just to identify them, but these names will not be used by our program. We will append a translation to each object, to position them before adding them to the scene through the Instance3D object. Before creating the sphere object, we will create a MaterialStandard object, which will allow us to create a new material with a reddish colour. This material is then assigned to the sphere when we create it. The other two objects are assigned a null material, which means that they will use the default material. We need a light source in order to view anything that is on our 3D scene. We create it by using a SceneLight object with a grey colour. By appending a translation vector, we position our light in the scene.

Listing 13. Error output private function contextCreationError(error:ErrorEvent):void{

}

trace(error.errorID+": "+error.text);

Listing 14. Taking care of aspect ratio and video buffer size private function resize(width:int,height:int):void{

myProsceniumInstance3D.configureBackBuffer(width,height,2,true);

myProsceniumInstance3D.scene.activeCamera.aspect=width/height; }

12/2011 (30 )

myProsceniumInstance3D.render();

21


FRAMEWORK FOR STORAGE3D

Listing 15a. Complete source code with comments package

{

private var myCubeObject:SceneMesh;

// We import Proscenium namespace to access its libraries import com.adobe.scenegraph.*; import flash.display.*;

import flash.display3D.*; import flash.events.*; import flash.geom.*;

private var mySphereObject:SceneMesh; private var myPlaneObject:SceneMesh;

// Proscenium allows us to create a standard material with ease private var myMaterial:MaterialStandard; public function ProsceniumTest():void

{

import flash.utils.*;

Flash

// This metadata tag is used to control

e.NO_SCALE;

//

swf movie size, VGA in this case

//

html page background color (medium gray

this.stage.scaleMode=StageScaleMod this.stage.align=StageAlign.TOP_

LEFT;

for our example) //

frame rate (try this as a performance

// We work with the lowest Stage3D

indicator)

object on the display stack and

[SWF(width="640",height="480",backgroundColor="

position it

0x333333",frameRate="125")]

// This will leave our viewport slightly moved down and to the

// We extend the Sprite class to gain more

right

control with sprites on Stage

//

public class ProsceniumTest extends Sprite

{

myS3D=stage.stage3Ds[0]; myS3D.x=10;

// We need a relative origin point in

myS3D.y=10;

space for all our 3D elements Vector3D();

// Create 2 event handlers for

private const CAM_ORIGIN:Vector3D=new

success and error on content

// This const controls how far objects

// We then request a Context3D

Vector3D(0,0,20);

creation

move

surface for our Stage3D screen

private const AMPLITUDE:Number=3.5;

myS3D.addEventListener(Event.CONTE

XT3D_CREATE,contextCreated);

myS3D.addEventListener(ErrorEvent.

// We declare our basic Stage3D element private var myS3D:Stage3D;

ERROR,contextCreationError);

//

nderMode.AUTO);

myS3D.requestContext3D(Context3DRe

// We need an Instance3D element Compare this with the Context3D that is used without Proscenium private var myProsceniumInstance3D:

Instance3D;

}

// Beware, Adobe warns that context3DCreate event can happen at any time,

// To create a scene we need these minimum requirements //

//

// These function is executed if there is no problem with assigning a 3D

lights - if the room is dark you cannot see //

context

objects - there must be something to view

private var myCamera:SceneCamera;

private var myMainLight:SceneLight;

such as when the hardware resources are taken by another process

camera - our eyes in the virtual world

22

It just demonstrates the

viewport versatility

private const ORIGIN:Vector3D=new

//

// Standard Stage management in

private function contextCreated( event:

{

Event ):void

12/2011 (30)


Proscenium As A Rapid Prototyping Framework For Stage3D

Listing 15b. Complete source code with comments objects

// We ask for a 3D context and pass it into the Proscenium

//

instance object

reference to the viewer

myProsceniumInstance3D=new Instanc

//

in size, and will contain a 20x20

// This function call initializes

//

our viewport and camera settings

coordinates //

// We add the event handlers

//

necessary to create the animation

the origin myPlaneObject=MeshUtils.createPlan myPlaneObject.transform.appendTran

stage.addEventListener(Event.ENTER

slation(0,-2,0);

_FRAME,animationLoop);

//

stage.addEventListener(Event.RESIZ

We finally add it to our scene

myProsceniumInstance3D.scene.addCh

E,resizeMe);

ild(myPlaneObject);

// We need to setup camera,

something to show

lights, objects

//

setCamera();

the origin

setLight();

,2,12,null,"cube");

//

setObjects();

We position it relative to

myCubeObject.appendTranslation(0,

3,0); //

private function setCamera():void

We finally add it to our scene

myProsceniumInstance3D.scene.addCh

ild(myCubeObject);

// Setting up the camera //

A simple cube just to have

myCubeObject=MeshUtils.createBox(8

// Here we setup the camera attributes

//

We need to select the active

Our next object will have a

different material assigned

camera in our scene

//

We use a standard material

myCamera=myProsceniumInstance3D.sc

with a redish color

ene.activeCamera;

myMaterial=new MaterialStandard;

matrix to an identity matrix

,.3);

//

myMaterial.diffuseColor.set(.8,.2

We set the camera transform

myCamera.identity(); //

//

The relative position of the

A simple sphere just to have

something to show

camera to the virtual world origin

//

//

the origin

(x,y,z) relative to (0,0,0)

myCamera.position=CAM_ORIGIN;

We position it relative to

mySphereObject=MeshUtils.createSph

myCamera.appendRotation(-

ere(4,32,32,myMaterial,"sphere");

myCamera.appendRotation(-

4,10,0);

15,Vector3D.X_AXIS);

mySphereObject.appendTranslation(

25,Vector3D.Y_AXIS,ORIGIN);

// Here we setup the objects that configure our 3D scene private function setObjects():void

12/2011 (30 )

We position it relative to

e(50,50,20,20,null,"plane");

support to changes on the

viewport size

{

We do not assign a material

to it

//

}

subdivision in the U and V

geHeight);

and give

{

The plane will be 50x50 units

e3D(myS3D.context3D);

resize(stage.stageWidth,stage.sta

}

A plane used as a ground

// Setting up the scene with some

//

We finally add it to our scene

myProsceniumInstance3D.scene.addCh

}

ild(mySphereObject);

// Here we setup the light attributes private function setLight():void

{

23


FRAMEWORK FOR STORAGE3D

Listing 15c. Complete source code with comments myMainLight.setShadowMapSize(256,

// Setting up the light //

256);

Since this is a demo light we

//

use the default parameters //

ild(myMainLight);

with no id myMainLight=new SceneLight(); //

// Since our lights emit shadows,

we add our objects to the shadow

We use a medium gray in

//

(r,g,b) format //

Object);

default level myMainLight.color.set(.5,.5,.5);

myMainLight.addToShadowMap(mySphe

The relative position of the

light to the virtual world origin myMainLight.appendTranslation(-

render node

myMainLight.addToShadowMap(myCube

The intensity of the light is a 100% alpha

//

We finally add it to our scene

myProsceniumInstance3D.scene.addCh

point light, not named and

}

reObject);

20,20,20);

// This event handler calls the resize

change the light type to distant

private function resizeMe(event:

// //

function

To cover all the scene, we This is similar to the light

of the sun, since we assume //

that the distance of the

{

light is far away, this kind of //

light emits parallel light

rays

}

Event=undefined):void

resize(stage.stageWidth,stage.sta

geHeight);

myMainLight.kind="distant";

// Here goes the code needed on your main

to the light.

protected function animationLoop(event:

// //

We must prepend the rotation This is to operate this

transformations before any other //

in the stack of

transformations //

It is important to remember

program loop

{

Event):void

// Here we change the objects position to generate an animation //

by using the time since

that the order of the

Flash started as an input to

//

//

transformations affects the

sine and cosine equations

final result in 3D

// We use AMPLITUDE constant to

myMainLight.transform.prependRotat

control how far the objects move

ion(-45,Vector3D.Y_AXIS);

myCubeObject.setPosition(

ion(-70,Vector3D.X_AXIS);

er()/10000),

myMainLight.transform.prependRotat //

We want this light to emit

AMPLITUDE*Math.cos(2*getTim

2,

AMPLITUDE*Math.sin(15*getTi

shadows //

The shadow map size is

important, because it afects on //

the render budget and

consumes GPU memory resources //

The shadow map size must be

set in powers of 2 //

mySphereObject.setPosition(

AMPLITUDE*Math.cos(3*getTim

er()/10000),

AMPLITUDE*Math.sin(8*getTimer()/10000), 0);

A number too low and your

shadows will look squary

// We need to clear and generate

//

the 3D image

A number too high and you

will consume huge amounts of

// This is the first call, but

//

it will be called each time the

resources

myMainLight.shadowMapEnabled=true;

24

mer()/10000));

screen

12/2011 (30)


Proscenium As A Rapid Prototyping Framework For Stage3D

Listing 15d. Complete source code with comments //

private function resize(width:int,height:

is resized

// We do not call the "present"

{

function (we pass the false parameter) //

so we can insert additional

// We set aside the video buffer memory to display our viewport myProsceniumInstance3D.configureBac

data over the image. In this case,

kBuffer(width,height,2,true);

we //

int):void

are allowing to show the

// We adapt the aspect ratio of

light shadow map before showing on

our camera in the viewport to its

screen

current size

myProsceniumInstance3D.render(0,f

myProsceniumInstance3D.scene.activ

alse);

eCamera.aspect=width/height;

// The following function renders

// We need to clear and prepare

the shadow buffer texture on

the image generated

screen

// By default it calls the

/*

"present" function to show the

myMainLight.shadowMap.showMeTheTe

scene on screen

xture( myProsceniumInstance3D, myProsceniumInstance3D.wi dth, myProsceniumInstance3D.he

}

}

}

myProsceniumInstance3D.render();

ight, 0, 0, 200); */ // Here we transfer the 3D scene to the screen after the shadow map is // }

overlayed

myProsceniumInstance3D.present();

// There are sometimes when we get a 3D context creation error // Maybe some other program is accesing the GPU private function contextCreationError(error

{

}

:ErrorEvent):void

trace(error.errorID+":

"+error.text);

// This function is in charge of arranging the necessary procedures to //

setup our scene in the GPU and gives support to a resizing scenario

12/2011 (30 )

25


FRAMEWORK FOR STORAGE3D

The type of light will be distant. This will allow us to have a homogeneous source of light that will affect all of our objects in the same manner, independent of the light position. The rotation is prepended in order to execute this transformation before any other. In this case, the rotation is executed before the translation. In this way, we rotate the light in its centre and then translate it. Otherwise, the light would be moved first, and the rotation would have been executed over its original centre, giving an additional translation effect. The effects of this can be seen on the following graphic, where the difference can be seen with a different position on the resulting square after applying a prepended rotation and an appended rotation. We enable the shadow map, which is a texture rectangle that represents and projects the shadow of the objects, as seen by the light. The size of it determines the resolution of the shadow casted by the light. Normally, you would want to give this texture as much resolution as you can. The problem is that it affects directly on the lighting budget. That is, it consumes resources that might affect how many other lights, textures and shaders could be used on your scene before some are excluded or the performance is adversely affected. The shadow map is preferably used with measures that are a power of 2 (256,512, 1024, etc). This is because some GPU operations execute faster and we avoid using extra operations, which again, might go against our real time execution budget. Remember that a shadow map is nothing else than a rectangular texture projected from the light source point of view. After declaring that our light will use a shadow map, we have to add the list of objects that will be taken into account when calculating such a map. The resizeMe event handler calls out custom resize function with the current stage height and width parameters. The main function for this example program is the animation loop, which is the frame entered event handler. In this function we take care of animating things a little. Here we change the position according to some sinusoidal curves, so that our objects come back to their original position cyclically. We make use of the AMPLITUDE constant that we can change on the source code to make this movement broader or narrower. When the position of the objects is changed, we then call the Proscenium render function, but we pass the false parameter so that present is not called yet. We do this to have the opportunity to alter in some manner the image present on the 3D buffer, before it gets into the video buffer. This could be to apply some kind of 2D post effect or filter like bloom, glow or blur. In our example, we did this to give space to overlay

26

the shadow map over our image. We then proceed to use the Proscenium present function to copy the final image into the video buffer, so the user can see the final image. If anything goes wrong with the Context3D object creation, we need to know for debugging purposes. This is our custom resize function. In here, we take care of reconfiguring our video back buffer, with the new size of our screen and correct aspect ratio. This will take care of any distortion introduced by the resizing of the screen. As a last step, we proceed to render and present this new generated image. Of course, this function would not be necessary if we did not want to implement a full screen viewport. As we have seen, the Proscenium framework has allowed us to simplify the creation of a simple scene with animation. In contrast to the manipulation of vertexes and triangles that was necessary with the pure Stage3D technology. There is also the Vertex Shader and Fragment Shader assembly lines of code, that, even with the miniassembler utility, were complex to achieve to the entry level programmer. The abstraction into a camera, object and light model allow us to reach farther with more or less the same code lines. There are more powerful capabilities to explore in the framework, such as the loading of standard 3D object formats, such as Collada, or OBJ. We can also use the Pellet libraries to achieve some simple object collision and other physics based effects. These are a few to name some, but the possibilities of Proscenium as a rapid development library are really exciting. Of course, there will be some occasions when Proscenium will not be fit for the task, but this is only a preview version and will require some time to mature. Although, we must also have in mind that as Adobe mentions, this library could end up just as some kind of curiosity if something better comes abroad. The entire code, with my comments, follows: Listing 15.

IZCÓATL ARMANDO ESTANOL FUENTES Izcóatl Armando Estanol Fuentes is labouring at Universidad La Salle, México as the IT Service Delivery Chief. He is passionate about his family, 3D and Flash animation, �lm making and technologies applied to education. You can �nd him around having a good time with his family, researching or making VFX for BYP UK. Contact: iaef@ulsa.mx

12/2011 (30)


����� ����

������������������� ��������������� ���������

������������ ������������� ����������� ���������������

��������������� ��������������������


HTML5 IN ACTION

HTML5 in Action Excerpted from

HTML5 in Action By Robert Crowther, Joe Lennon, and Ash Blue With Canvas 2D Context, you have all the benefits of Flash and your games will run on mobile devices and without a plugin. In addition, one HTML5 Canvas application can be distributed across multiple platforms through mobile frameworks like PhoneGap.com. This article based on chapter 6 of HTML5 in Action, shows you how to create your first Canvas game, taking advantage of physics, advanced animation, and keyboard/mouse controls. You may also be interested in…

For Source Code, Sample Chapters, the Author Forum and other resources, go to www.manning.com/crowther2

Creating a Canvas Game

I

n the past, you’d develop browser games like Breakout, shown in Figure 1, through Flash. While Flash runs across all browsers, support is terrible on mobile devices, plus it requires a plugin. Now, with Canvas 2D Context, you have all the benefits of Flash, and your games will run on mobile devices and without a plugin. In addition, one HTML5 Canvas application

Listing 1. Default JavaScript var canvas = document.getElementById(‘canvas’); var canvasAtt = canvas.attributes;

var canvasW = canvasAtt.getNamedItem(‘width’).value;

var canvasH = canvasAtt.getNamedItem(‘height’).value; var canvasRun;

if (canvas.getContext){

var context = canvas.getContext(‘2d’);

}

background();

function background() { var img = new Image();

img.src = ‘background.jpg’;

Figure 1. Breakout’s objective is to bounce a ball via paddle to break bricks. When the ball goes out of bounds the game shuts down. You can play the game now at html5inaction.com and download all the �les needed to complete your own Breakout game from manning.com

28

}

context.drawImage(img,0,0);

12/2010 (30)


HTML5 in Action

can be distributed across multiple platforms through mobile frameworks like PhoneGap.com. Your first Canvas game will take advantage of physics, advanced animation, and keyboard/mouse controls. While physics and advanced animation may sound scary, no prior knowledge is necessary, and we’ll walk you through each step of the way. Before you create Breakout, download the HTML5 in Action’s complimentary files from manning.com. From html5inaction.com you can test-drive the game and see all the cool features. For the rest of this article you’ll inflate a ball, carve a paddle, lay down the bricks, and then bring it all together. But first, you need to lay down the foundation – setting up your file and Canvas. Let’s get started!

Listing 3. Paddle creation var padX; var padY; var padW; var padH; var padR; if (canvas.getContext){

var context = canvas.getContext(‘2d’);

background(); ballInit(); ballDraw();

Listing 2. Ball creation var ballX;

#A

var ballY;

#A

var ballR;

padInit(); }

function padInit() {

if (canvas.getContext){

padX = 100;

var context = canvas.getContext(‘2d’);

padY = 210; padW = 90;

background();

padH = 20;

ballInit(); }

}

ballDraw();

function ballInit() {

ballX = 120;

#B

context.beginPath();

context.moveTo(padX,padY);

context.arcTo(padX+padW, padY, padX+padW,

ballR = 10;

padY+padR, padR);

context.beginPath();

padR,padY+padH, padR);

context.arcTo(padX, padY+padH, padX, padY+padH-

#C

padR, padR);

context.arc(ballX, ballY, ballR, 0, 2 * Math.PI,

context.arcTo(padX, padY, padX+padR, padY, padR);

context.fillStyle = ballGrad();

context.fillStyle = padGrad();

false);

context.fill();

context.closePath();

}

function ballGrad() {

var ballG = context.createRadialGradient(ballX,b

context.fill();

var padG = context.createLinearGradient(padX,pad

Y,padX,padY+20);

ballG.addColorStop(0, ‘#eee’);

padG.addColorStop(0, ‘#eee’);

return ballG;

return padG;

ballG.addColorStop(1, ‘#999’);

#B Initial setup

#C Animates the object

12/2010 (30)

#B

function padGrad() {

allY,2,ballX-4,ballY-3,10);

}

#A

context.arcTo(padX+padW, padY+padH,padX+padW-

function ballDraw() {

}

padR = 9;

function padDraw() {

ballY = 120; }

padDraw();

padG.addColorStop(1, ‘#999’); }

#A Sets the paddle’s spawn origin

#B Closing paths can prevent buggy behavior

29


HTML5 IN ACTION

The Foundation

Set up the game’s HTML in a file called breakout.html and make the <canvas> tag 408 by 250 pixels so there’s lots of room to work with. After that add an inline style to center everything: <body style=“text-align: center”>

<canvas id=“canvas” width=“408” height=“250”> Download FireFox to play this game now!

</canvas>

<script type=“text/javascript” src=“game.js”></ </body>

Listing 4. Brick array creation var bricks; var bGap; var bRow; var bCol; var bW; var bH; if (canvas.getContext){

var context = canvas.getContext(‘2d’);

script>

background(); brickInit();

Next, create and point to a new JavaScript file called game.js (show in Listing 1). With a solid HTML setup, you can use the DOM to collect default variables to speed up game development. You’ll find these variables useful when retrieving the <canvas> width and height for calculating equations. In addition, lay down a simple background image that takes up the whole play area.

}

Inflating a Ball

function brickInit() {

Instead of leaving the ball’s background plain, use a gradient to add some interest. The catch is you have to dynamically generate the gradient at the ball’s position. Otherwise, the gradient won’t follow the object when animated.

brickDraw(); ballInit(); ballDraw(); padInit();

bGap = 2; bRow = 3; bCol = 5; bW = 80; bH = 15;

Carving a paddle

To create a paddle follow, listing 3 to combine four arcs into a pill shape. TUse arcTo(x1,y1,x2,y2,radius), which allows circular shapes to be created as part of a path for an object. In addition, add a gradient like the ball, except add a linear version with createLinearGradient (x1,y1,x2,y2).

padDraw();

bricks = new Array(bRow); for (i=0; i<bRow; i++) {

}

}

bricks[i] = new Array(bCol);

function brickDraw() {

for (i=0; i<bRow; i++) {

#A

#A

#A

#B

for (j=0; j<bCol; j++) {

if (bricks[i][j] !== false) {

context.fillStyle = ‘blue’;

#B

#B

context.fillRect(brickX(j),brickY(i),

}

}

}

}

bW,bH);

#1

#A Array creation loop

#B Array execution loop

Figure 2. With a total of 4 gaps at 2px each. The totaling 8px needs to be subtracted from the <canvas> width leaving 400px. Distribute the remaining width to each brick, leaving 80px for each (400px / 5 bricks = 80px)

30

12/2010 (30)


�������� ������������������� ����������������������

��

�������������������������������� �����������������������������������

��������� ���������

������������� ������

�������� � ����������

���

�������������� ����� ������������������������ ����������������������� ����������������������� ������������������������������������������������������� ��������������������������������������������������������� ����������������������������������� �������������� ��������������������������������

���� �������

��� �������

����� �����

����� ������

���� ������

����� ��������

��������� �������

���� ������

���� �������

������ �������

��� �����

����� ��������

������ �����

����� �������

������ ������

�������� �����

���� �����

������� ����

��� ��������

������� ���������

���� ���

���� ������

������� �������

������� �������

����� ������

����� �������

���� ������

���� �����

���� ������

����� �����

����������������������������������������������������

����������������������������� �����������������������������������������

����� � � � � � � � � �� ��� ���

����������������������������� �������������������������������������

�������������������������������������������������������

������� ������������������������������

������������������ ���������������� ��������������������������������������

����������������������������������������������������� � �� ����� �����

����������������������������������


HTML5 IN ACTION

Laying Down Bricks

To get the width for each brick, you’ll need to do some calculations. Since you have 5 bricks with a total of 4 gaps that need spacing, place a 2px gap between each brick to take up a total of 8px (4 gaps x 2px). Remove the 8px from the 408px Canvas width for a total length of 400px. Since 5 bricks need to fit inside the remaining width, you’re looking at 80px per brick (400px / 5 bricks = 80px). Want to use 9 bricks instead? You’ll have to remove the 8 2px gaps from the width, then adjust the <canvas> width to a number that’s perfectly divisible by 9 bricks. If you don’t adjust the width, you’ll end up with a decimal that offsets your Breakout game. Having trouble following our logic? Look at the diagram in figure 2 for a visual explanation. Bricks could be placed by rewriting a basic shape command over, and over, and over. Instead, create a two dimensional array as seen in listing 4 to hold each brick’s row and column. To lay down the bricks, loop through the array data and place each according to its row and column. While outputting the bricks, you need to be specific about the x and y coordinates. Add two functions (#1) to handle the column and row setup with the following code snippet.

function brickX(row) { }

return (row * bW) + (row * bGap);

function brickY(col) { }

return (col * bH) + (col * bGap);

Since the brick placement is taken care of, why don’t you make them a little more colorful? Change up the graphics by coloring each brick based upon its row via a switch statement. In order to make listing 5 function correctly, replace context.fillStyle = ‘blue’; with context.fillStyle = brickColor(i);, otherwise the color won’t change.

Assembling a Static Game

Since you just wrote a lot of code make sure the draw() and init() functions look identical to Listing 6. Your JavaScript file should be structured so variable declarations reside at the top, all object initialization function placed inside init(), and all draw functions placed inside draw(). Doing so will properly prepare objects to be animated and interacted with in the next section. Before proceeding, make sure that your current progress is identical to Figure 3.

Listing 5. Coloring bricks function brickColor(row) { var brickG = context.createLinearGradient(0,y,0,y+bH); switch(row) { case 0:

brickG.addColorStop(0,’#bd06f9’);

brickG.addColorStop(1,’#9604c7’); break;

case 1: brickG.addColorStop(0,’#F9064A’);

brickG.addColorStop(1,’#c7043b’); break;

case 2: brickG.addColorStop(0,’#05fa15’);

} }

#A

#A

#B

#B

#C

brickG.addColorStop(1,’#04c711’); break;

#C

default: brickG.addColorStop(0,’#faa105’);

#D

brickG.addColorStop(1,’#c77f04’); break;

#D

return context.fillStyle = brickG;

#A Row 1 red

#B Row 2 purple #C Row 3 green

#D Row 4+ orange

32

12/2010 (30)


HTML5 in Action

So far in this game development journey, you’ve created all the core pieces of Breakout. Because nothing moves, the game is as useless as a rod without a reel. Next, we’ll teach you how to bring your game’s static design to life!

Breathing Life Into Breakout

Currently your game looks really cool, but it doesn’t do anything. Pulling out several different tools from our JavaScript and Canvas toolbox we’ll show you how to get your new objects running. In fact, we’ll even show you how to integrate physics, and move the paddle with a controller.

Animating Game Elements

Now, that you’ve created all the needed objects and divided code into init and draw functions, it’s time to begin animating. Your moving objects will be visible, but you won’t be able to control them via keyboard and mouse until you work through the collision and controller setup in the following sections. Before we get started, you need to animate Canvas objects by adding return canvasRun = setInterval(draw, 12); to the end of the init() function. It’s imperative you add the following code snippet at the end so it fires after all the initialization functions have setup their default properties. Now, you’re ready to begin. First, let’s make the paddle move from left to right. Next, we’ll make the ball move diagonally.

Rowing The Paddle

To make the paddle move adjust the x axis each time it’s drawn. Making x positive will draw the paddle forward while a negative value will pull it back. First, add an undeclared variable at the top for your paddle’s speed. Second, give the variable a value of 4 in padInit() to make things run a little faster than you normally would for players. Finally, add an incremental

Listing 6. HTML5 game coding template if (canvas.getContext){

var context = canvas.getContext(‘2d’);

}

init();

function init() {

brickInit(); ballInit(); padInit();

}

draw();

function draw() {

context.clearRect(0,0,canvasW,canvasH);

#A

background(); brickDraw(); ballDraw(); }

padDraw();

#A Draw functions overlay from bottom up

Listing 7. Paddle animation var padSpeed; function padInit() {

padY = 210; padW = 90; padH = 20; padR = 9;

}

padSpeed = 4;

function padDraw() {

padX += padSpeed;

#1

context.beginPath();

context.moveTo(padX,padY);

context.arcTo(padX+padW, padY, padX+padW, padY+padR, padR); context.arcTo(padX+padW, padY+padH,padX+padWpadR,padY+padH, padR);

context.arcTo(padX, padY+padH, padX, padY+padHpadR, padR);

context.arcTo(padX, padY, padX+padR, padY, padR); context.closePath();

context.fillStyle = padGrad();

Figure 3. Using the previous code snippets, you created a ball, a paddle, and bricks with gradients. After refreshing your browser, the current result should look like this

12/2010 (30)

}

context.fill();

33


HTML5 IN ACTION

equation at the beginning of the paddle’s draw function. Listing 7 puts it all together for you. Now, refresh your page. Oh no! The paddle swims off into oblivion due to a lack of a movement limiter (#1). The only way to keep your paddle from vanishing is to integrate collision detection. First though, the ball needs to be pitched.

Throwing The Ball

Making the ball move is almost exactly the same as moving the paddle. The only difference is that the x axis and y axis are incremented each time they’re drawn. Run the code in Listing 8. You can tweak the ball’s trajectory by adding a few extra points to the Y axis. While optional, such an adjustment will make the ball move at a sharper vertical angle. After refreshing, the ball and paddle should fly off the screen and disappear. While their disappearance may leave you depressed and lonely, do not fear! You will soon retrieve them through some basic physics integration.

Detecting Collisions

In simple 2D games, using physics could be stated as testing for object overlap. These checks need to be performed each time the interval refreshes and draws an updated set of objects. If an object is overlapping,

Real Game Physics

Sad to say, but these physics aren’t real in a mathematical sense. If you’re interested in learning how to make games more lifelike please see Glenn Fiedler’s robust article on game physics at http:// gafferongames.com/game-physics/. Start building collisions detection into Breakout by keeping objects contained inside the play area. After taming objects, you’ll focus on using the paddle to bounce the ball at the bricks. After the ball is bouncing back, the game’s bricks need to be removed when touching a ball. Lastly, create rules to determine when the game is shut down. Let’s get started.

Making Interactive Objects

To prevent your mouse and paddle from flying off the screen, they’ll need to be checked against the <canvas> width and height. Use the canvasW and canvasH variables to keep your objects in check. Go to the padDraw() function and replace padX += padSpeed; with the equation in the snippet that follows to check if the paddle has a positive X coordinate and is within the <canvas> width. If it is, then update the paddle’s

Listing 8. Ball animation

Listing 9. Ball edge detection

var ballSX;

if (ballY < 1) {

function ballInit() {

}

var ballSY;

ballY = 1;

else if (ballY > canvasH) { }

ballR = 10;

if (ballX < 1) {

ballSX = 2; }

ballX = 1;

ballSY = -2;

}

function ballDraw() {

ballX += ballSX;

ballX = canvasW - 1;

ballX += ballSX;

context.fill();

#C

#D

ballSX = - ballSX;

context.beginPath();

context.fillStyle = ballGrad(); }

else if (ballX > canvasW) {

}

false);

#B

ballSX = - ballSX;

ballY += ballSY;

context.arc(ballX, ballY, ballR, 0, 2 * Math.PI,

#A

ballSY = -ballSY;

ballX = 120; ballY = 120;

34

then you need to activate some logic to handle the situation. For instance, if the ball and paddle overlap that should cause the ball to bounce off.

ballY += ballSY; #A Top edge #B Bottom #C Left

#D Right

12/2010 (30)



HTML5 IN ACTION

X coordinate as normal. If performed correctly, you’ll notice that the paddle stops at both the left and the right edge.

When the ball’s x and y coordinates overlap the paddle, bounce the ball in the opposite direction by reversing the y axis with the ballSY variable.

if ((padX > 0) && (padX < (canvasW – padW))) {

if (ballX >= padX && ballX <= (padX + padW) && ballY >=

padX += padSpeed;

}

padY && ballY <= (padY + padW)) {

Since the paddle is taken care of, stop the ball from dropping out of gameplay by adding Listing 9 to the top of your ballDraw() function. Create a test for the ball’s Y coordinates and another for the X coordinates. If the ball is overlapping make the speed negative by replacing ballX += ballSX; and ballY += ballSY;. In addition to reversing the ball you’ll need to put it back inside the play area; otherwise, you run the risk of it getting stuck at higher movement speeds. Use the following code snippet to make the ball repel off the gameplay area’s sides. With the ball ricocheting, you’ll need to use the paddle to deflect it back toward the bricks. Since the ball changes direction on impact and the paddle stays stationary, put your deflection logic inside ballDraw() by ballx += ballSX and ballY += ballSY;, as seen in the following snippet.

ballSY = -ballSY;

}

ballX += ballSX; ballY += ballSY;

Listing 11. Ending the game function ballDraw() { if (ballY < 1) {

ballY = 1;

}

ballSY = -ballSY;

else if (ballY > canvasH) {

clearInterval(canvasRun);

canvasRun = setInterval(goDraw, 12); canvas.addEventListener(‘click’, restartGame, false);

}

#A #A

#A

Listing 10. Removing bricks function brickDraw() {

ballX = 1;

for (i=0; i<bRow; i++) {

for (j=0; j<bCol; j++) {

}

if (bricks[i][j] !== false) {

else if (ballX > canvasW) {

if (ballX >= brickX(j) && ballX <=

ballX = canvasW - 1;

(brickX(j) + bW) && ballY >=

}

bricks[i][j] = false;

ballX += ballSX;

ballSY = -ballSY;

#A

ballY += ballSY;

#B

if (ballX >= padX && ballX <= (padX + padW) &&

ballY >= padY && ballY <= (padY

brickColor(i);

+ padW)) {

ballSX = 7 * ((ballX - (padX+padW/2))/padW);

context.fillRect(brickX(j),brickY(i)

}

}

}

}

ballSX = - ballSX;

brickY(i) && ballY <= (brickY(i) + bH)) {

}

ballSX = - ballSX;

ballSY = -ballSY;

,bW,bH);

} context.beginPath();

context.arc(ballX, ballY, ballR, 0, 2 * Math.PI, false);

#A Collision test

#B If true brick = false

context.fillStyle = ballGrad(); }

context.fill();

#A Add these lines

36

12/2010 (30)


HTML5 in Action

If you’ve played Breakout, you’ll notice that the paddle’s deflection appears to be broken. When the ball hits it isn’t dynamically bouncing back based upon where it hits the paddle. To make collisions dynamic, modify the equation to take into account the ball’s X axis based upon where it impacted the paddle, as follows: if (ballX >= padX && ballX <= (padX + padW) && ballY >= padY && ballY <= (padY + padW)) {

ballSX = 7 * ((ballX – (padX+padW/2))/padW); }

ballSY = -ballSY;

Removing Objects

With the paddle dynamically generating the ball’s trajectory the bricks need to start disappearing. Use the brickDraw() function in Listing 10 to test if the ball is overlapping when a brick is drawn. If so, reverse the ball’s y-axis and set the brick’s array data to false so it won’t be drawn or interact with the ball any longer. To finish up Breakout’s detection, there needs to be a point where a game over occurs. In order to stop the game add logic in the ballDraw() function for when the ball goes below the play area. The game over occurs in Listing 11 with a clearInterval() that stops Listing 12. Keyboard listeners var keyL;

#1

var keyR;

#1

$(document).keydown(function(evt) { if (evt.keyCode === 39) {

}

keyL = true;

else if (evt.keyCode === 37) {

});

}

keyR = true;

$(document).keyup(function(evt) {

if (evt.keyCode === 39) {

}

keyL = false;

else if (evt.keyCode === 37) {

});

}

keyR = false;

#A Checks if left arrow key

#B Checks if right arrow key #C Resets keyL and keyR

12/2010 (30)

#A

#B

all animation. After that, you’ll notice two mystery functions that take care of the game over screen. Now that you can deflect the ball back towards bricks, players have the ability to defend themselves from the dreaded game over. Well, not exactly... We still haven’t explained how to let players control the paddle. Whipping up a little bit of jQuery magic we’ll show you a few simple code recipes for setting up keyboard and mouse functionality.

Creating Keyboard and Mouse Controls

To create an interactive game experience, keyboard or mouse input is required. The sad news is that browser support for detecting keyboard events is nothing short of terrible. DOM level 3 is supposed to help standardized keyboard monitoring, but support is currently dodgy. Although you’ve gotten away without using it so far, you’ll need the jQuery library to detect keyboard events. Download the library from jquery.com and insert it right above the Canvas game’s JavaScript file. We highly recommend that you download the minified production file to minimize the game’s size (the smaller the size, the faster it loads). <script type=“text/javascript” src=“jquery.js”></script> <script type=“text/javascript” src=“game.js”></script>

With jQuery locked-in and loaded you can set up a flexible control scheme where the player has options. For the first option, you’ll create keyboard controls with the left and right arrow keys. For those who wield the power of a mouse, you’ll create a JavaScript mouse listener that monitors cursors movement and places the paddle there. Lastly, we’ll give you a few tips on best practices for integrating controls based upon techniques that produce happy customers.

Mastering The Keyboard #C

To create a keyboard detector you need two new variables near the top of the JavaScript file. One for the left and another for the right keyboard arrows. Think of these monitoring devices as switches for activating left or right movement. To turn keyboard variables on and off you’ll need two jQuery listeners. One that detects when keys pressed down and another that monitors key release. For detecting the keys use keycodes for the most consistent and simple way to detect a user’s keyboard input.

More Keycodes!

If you would like to know more about the state of keyboard detection and get a complete list of key codes, please see Jan Wolter’s article JavaScript Madness: Keyboard Events at http://unixpapa.com/js/key.html.

37


HTML5 IN ACTION

Based on the current state of the monitored keys, the paddle is shifted left or right. Dump the code from listing 12 at the very bottom of your JavaScript file. Refreshing the page, you’ll notice that the paddle refuses to acknowledge your commands. With the keyL and keyR variables’ storing keyboard input (#1), the padDraw() function needs to be updated to detect whether to move left or right. The movement function will need to be rewritten into two parts – one that moves the paddle to the right and stops against the right wall and another that does the same for the left. Replace if ((padX > 0) && (padX < (canvasW – padW))) {} with the following code snippet to make the paddle react to the variables. if (keyL && (padX < (canvasW – padW))) { }

padX += padSpeed;

else if (keyR && padX > 0) { }

padX += -padSpeed;

Manipulating The Mouse

jQuery does more than hijack keyboard keys; it also makes mouse movement easy to monitor. To get the current mouse location, create a listener at the bottom of your JavaScript file. Note that placing listing 13 at the bottom of your JavaScript file will override the keyboard listener you integrated earlier.

Control Input Considerations

In the past couple years, JavaScript keyboard support for applications and websites has grown in leaps and bounds. YouTube, GMail, and other popular applications

var mouseX;

var canvasPosLeft = Math.round($("#canvas").posi

#A

var canvasPos = e.pageX - canvasPosLeft; var padM = padW / 2;

if (canvasPos > padM && canvasPos < canvasW -

padM) {

mouseX -= padW / 2;

});

}

padX = mouseX;

Download FireFox to play this game now!

</canvas>

<p>LEFT and RIGHT arrow keys or MOUSE to move</p> <script type=“text/javascript” src=“jquery.js”></script> <script type=“text/javascript” src=“game.js”></script>

Summary

$(‘#canvas’).mousemove(function(e){

mouseX = canvasPos;

<canvas id=“canvas” width=“408” height=“250”>

Congratulations! You’ve just completed an HTML5 game from beginning to end.

Listing 13. Mouse controls

tion().left);

use keyboard shortcuts to increase user productivity. While allowing users to speed up interaction is useful, it can quickly create problems. You need to be careful when declaring keyboard keys in JavaScript. You could override default browser shortcuts, remove operating system functionality (copy, paste, and so on), and even crash the browser. The best way to avoid angering players is to stick to the arrow and letter keys. Specialty keys, such as the space bar, can be used, but overriding shift or caps lock could have unforeseen repercussions. If you must use a keyboard combination or specialty key, ask yourself, “Will this cause problems for my application’s users in some way? When playing videogames, people don’t want to spend their first 10 minutes randomly smashing keys and clicking everywhere. Put your game’s controls in an easy-to-find location and use concise wording. For instance, placing the controls directly under a game is a great way to help users. To add a control description to Breakout, add a simple <p> tag directly below <canvas>. It should say LEFT and RIGHT arrow keys or MOUSE to move. If you really want, you could create a graphical illustration that’s easier to see, but for now we’ll just use text for simplicity.

#B

While 2D games can be fun to make, they aren’t exactly making record sales in the game market. In addition, most 2D-based game and animation studios have closed down or converted over to 3D. If browser interfaces want to compete with professional tools, such like 3D Studio Max, Maya, and After Effects, then 3D must be capable in the browser. On the other hand, 2D applications can be fairly cheap to produce and casual gaming on the internet has grown tremendously in the past ten years, which is resulting in high demand for 2D games (especially on mobile devices).

#A Game position from left in pixels

#B Centers paddle on mouse

38

12/2010 (30)



ALTERNATIVA 3D

Alternativa3D AlternativaPlatform is a company, headquartered in Perm, Russia. It develops and supports technology solutions for browser-based multiplayer projects. What you will learn…

What you should know…

• What is Alternativa3D. • How to get started with Alternativa3D

• Prior experience programming with ActionScript 3 is required. • A previous background working with 3D graphics is also recommended.

F

lash-3D-engine, physical engine, GUI library and high-performance server-side, developed by company, allows to create a unique browser-based games, social networking applications, promotional projects and demonstrations. One of the key projects is browser-based multiplayer realtimeaction Tanki Online (Figure 2). Now the second version of the game is in development (Figure 3). It is optimized for social networks. Also company works on a few other online games (alone and as a technology partner). In

addition, AlternativaChina company is established in Shanghai, and dedicated to localizing and releasing games for the Chinese market.

Alternativa3D

Alternativa3D engine is intended for displaying 3D graphics in the Flash Player environment. The possibilities with Alternativa3D are comprehensive and diverse. The technology is widely used in different spheres ranging from 3D websites creating

Figure 1. AlternativaPlatform Logo

40

12/2011 (30)


Alternativa3D

to developing multiplayer browser-based games and applications for social networks in full 3D. The purpose of the appearance of Alternativa3D was creating high-performance 3D-renderer for using in new projects of AlternativaPlarform company. Version 5 of the engine was released in may 2008. It was a public version, which is available for using in any non-profit projects. It used the BSP-tree for scene building and could very quickly and correctly render greater levels. Due to these facts Alternativa3D 5 got widespread among flash-developers. In 2010 the next generation of Alternativa3D with number 7 became available. This version used API of Flash Player 10, so improved the quality of display objects in perspective. Also the engine architecture was rewritten. At that time the focus was on efficient processing and sorting dynamically changing scenes polygons. Current version 8 of the engine uses videocard GPU to render 3D graphics. Also, you can use Alternativa3D 8 and Alternativa3D 7 for free in any projects, even in commercial ones.

• • •

What You Should Know About 3D World of Alternativa3D

The general comparison can get some comprehension and for deeper comprehension make deeper comparison between heart of 2d and 3D visual objects. Usual flash graphics are built on points with lines which can be straight or curved. Object itself can consist of strokes, fills or both. Not all Object3D is visual and now we will talk about main visual Object3D class – mesh. It also built on points called vertices, but a vertex can hold additional data (such as texture coordinates, normals and tangents and so on), not only its own coordinates. Another difference is that verticies can be linked with direct lines only to make a triangle.

3D world of Alternativa3D engine has many similarities with flat flash world as well as many differences. Let’s start with calling familiar things: • • • •

both worlds have hierarchical structure all objects of each world (include root object) have one base class (DisplayObject and Object3D) if object should be visible it needs to be added in hierarchy almost any node of hierarchy can be parent for objects

• • •

it can be done by calling the addChild() method each object has its own coordinate space all child objects keep their position in parent’s coordinate space properties holding object’s position are familiar (such as x}, {{y, z, scaleX, rotationY and so on) coordinates of an object in its own space are always equal 0, 0, 0 even all visible parts visual content can be created in code as well as loaded from external file or embedded assets

and differences: • • •

root object of the flat world is an application itself but you should create a root object for 3D world simplest DisplayObject can’t have children but Object3D can as well as DisplayObjectContainer flat world is ready with an empty application but 3D needs some preparation (minimu set of created objects and calls)

Figure 2. Tanki Online

12/2011 (30)

41


ALTERNATIVA 3D

Sequence of these triangles form a surface.The surface is like a usual flash shape which has its own one fill but does not have to be flat. The Fill in 3D world associated with material. Material differs from a fill as follows: besides color information for each pixel material can hold a lot more information concerned with lightning. This information defines the behavior of an object exterior depending on the point of view and the disposition and the features of lights presented in the world. Different materials depending on their complexity can demand different sets of data to be successful. The simplest material is FillMaterial – it acts as a solid fill in pure flash. It does not need any additional data and the surfaces with this material will ingore lights at all. The most complex and extensive material in alternativa world is StandardMaterial. So it require maximum additional data in textures as well as in vertex. For the Surface to work with StandardMaterial it should have the following vertex data: • • •

texture coordinates vertex normals complex data type consisting of tangent and binormal.

Besides the additional vertex data and usual diffuse map which defines with which image surface fills, StandardMaterial needs normal map – texture defines deviations between geometry surface and surface which will act in light calculations in order to make the surface look more detailed than its geometry. So flat surface can look rough due to normal map. Also you can set to StandardMaterial additional textures which are not required but can make the surface more attractive and realistic. There are: glossiness map and specular map.

Types of Object3D

As we already note, not all Object3D are visual, and visual ones not limited with Mesh. So they are listed and described below.

Visual Classes

– static 3D object consists of surfaces. Primitives (Box, GeoSphere, Sphere and Plane) – subclasses of mesh which include built on visual form generated in creation according to it’s name. Skin – subclass of mesh which include joints (bones) in order to aid moving and animating parts of solid object.

Mesh

Figure 3. Tanki Online 2.0

42

12/2011 (30)


Alternativa3D

Decal – subclass of Mesh with a z-fighting suppression engine. Usual usage – additional marks which overlays existing mesh such as bullet holes, bloodsplashes occurs in games during gameplay and so on. WireFrame – stroke analogue in 3D world which shows lines (e.g edges of mesh or axis) SkyBox – cubic primitive meant for showing the environment. So it has a few differences with Box such as normals turned inside and permanently staying at the bottom layer so it does not overlap any other object. Sprite3D and AnimSprite are flat pictures/animations which live in 3D world but always turned to the camera so it can not have any perspective distortion.

Non-visual Classes

Camera3D – Object3D which determines what we will see on screen: point of view, field of view, direction. Light3D – Base class for all lights in Alternativa3D: OmniLight, DirectionalLight, AmbientLight. It lights surfaces with appropriate materials such as VertexLightMaterial and StandardMaterial. Joints – also known as bones, Object3D meant for moving (animating) parts of solid mesh. Joint act like container for a set of vertices but 1) one vertex can be handled by up to 8 joints and 2) this vertex has value of power of affect of each joint.

Listing 1. Application structure

Hardware API and resources

Since the new flashplayer has hardware acceleration for rendering 3D, we should follow some restriction to be compatible. First we should know,a hardware rendered image does not included in classic DisplayTree. There is a special stage – stage3D. Any application can have 4 stage3D, all of them placed as layers on each other but below the DisplayTree so any flat flash object will overlap the 3D image. Second and most important thing in dealing with hardware API is that all data required to render image with GPU should be loaded into video memory. Such data associated with resources in Alternativa3D. There are 2 general types of resources: vertex data and textures. Alternativa3D gives several ways of dealing with resources but it is possible to start with the simplest – gathering all resources of the object and all its children by using the method getResources(true). Sometimes as you use TexturesLoader to load textures from external files it cares about uploading texture resources to video memory. Another thing you should know about textures – its width and height should be to power of 2. 512x256 – good example, but there is no way to use texture 200x300.

Application Structure

[SWF(backgroundColor = "0x909090", width = "800",

Usual flash application is inherited from DisplayObject like Sprite or MovieClip so it has all working requirements done.The Alternativa application needs in creation of 3D world and some parts connecting it with flat screen. First we need the root object of the 3D world. It can be just an Object3D instance, lets call it rootContainer.

public class HelloBox extends Sprite {

rootContainer = new Object3D();

package {

import alternativa.engine3d.core.Camera3D; import alternativa.engine3d.core.Object3D; import alternativa.engine3d.core.View; import flash.display.Sprite;

height = "600")]

private var camera:Camera3D;

private var rootContainer:Object3D; public function Template() {

camera = new Camera3D(0.01, 10000000000); camera.view = new View(stage.stageWidth, stage.stageHeight, false,

0x404040, 0, 4);

addChild(camera.view);

rootContainer = new Object3D();

}

}

rootContainer.addChild(camera);

}

12/2011 (30)

Then we need the Camera3D added somewhere in rootContainer hierarchy. camera = new Camera3D(10, 1000);

rootContainer.addChild(camera);

When creating a camera instance we should to pass 2 arguments: nearClipping and farClipping which determine the depth of the space camera can be observed. It is because of the z-buffer used in the hardware rendering engine. The rendering engine draws objects one by one and should know if some part of already drawn objects are placed nearer than those parts that are in processing in the same part of the screen, and also that part that needs not to be drawn as it should be hided. In order to know that the engine keeps distance to the camera simultaneously

43


ALTERNATIVA 3D

with drawing each pixel or, if for the presented distance value closest to camera for this pixel, the engine just leaves this pixel as it is. GPU has a fixed size of memory to keep such distance to camera (this is z-buffer). Therefore with increasing depth of space between nearClipping and farClipping the precision of distance falls. So it will be better to keep nearClipping and farClipping closest to each other. While rendering objects placed that are close to each other visual artifacts can occur (Z-fighting). Since overlapping marks should be very close to other surfaces,to prevent visual artifacts Decal class was created. Last thing we need to create is viewport of the camera. It determines the size of a visible area, color of the background and so on.

camera.view = new View(stage.stageWidth, stage.stageHeight,

false, 0x404040, 0, 4);

And view should be in regular need to call

DisplayTree

so we also

addChild(camera.view);

And if you want to see something, you should add it to rootContainer hierarchy too. Take all it together in the order which they should be presented in application (Listing 1). Any created object at the begining has 0,0,0 coordinates by default which are associated with the

Listing 2. Project example package {

addChild(camera.view);

import alternativa.engine3d.controllers.SimpleObjectCo

rootContainer = new Object3D();

ntroller;

import alternativa.engine3d.core.Camera3D;

rootContainer.addChild(camera);

import alternativa.engine3d.core.Object3D; import alternativa.engine3d.core.Resource;

box = new Box();

import alternativa.engine3d.core.View;

box.setMaterialToAllSurfaces(new

import alternativa.engine3d.primitives.Box;

rootContainer.addChild(box);

import flash.display.Sprite;

stage3D = stage.stage3Ds[0];

import alternativa.engine3d.materials.FillMaterial;

FillMaterial(0xFF0000));

import flash.display.Stage3D;

stage3D.addEventListener(Event.CONTEXT3D_

import flash.events.Event;

CREATE, init);

stage3D.requestContext3D();

[SWF(backgroundColor = "0x909090", width = "800",

}

height = "600")]

public class HelloBox extends Sprite { private var stage3D:Stage3D;

private function init(event:Event):void {

private var camera:Camera3D;

for each (var resource:Resource in rootContaine

private var rootContainer:Object3D;

r.getResources(true)) {

private var controller:SimpleObjectController; private var box:Box;

}

addEventListener(Event.ENTER_FRAME,

public function HelloBox() {

}

camera = new Camera3D(0.01, 10000000000); camera.y = -300;

void {

camera.z = 100;

controller.update();

controller = new SimpleObjectController(stage,

controller.lookAtXYZ(0,0,0);

camera.view = new View(stage.stageWidth,

stage.stageHeight, false, 0x000000,

enterFrameHandler)

private function enterFrameHandler(event:Event):

camera.x = -50;

camera, 200);

resource.upload(stage3D.context3D);

}

}

camera.render(stage3D);

}

0, 4);

44

12/2011 (30)


Alternativa3D

origin of it’s parent. Thus the camera in the code given above placed in the center of the rootContaner and looking upwards. So even if we place some geometry to rootContainer, the camera would not see anything since the new geometry occurs in the center too and will surround the camera. All geometry in Alternativa3D has only one side and can not be seen from inside (the visible side is determined by the normals of the surface but from the inside unlike from the outside it will invisible – SkyBox acts like this). In the light of this fact we should move and turn the camera. We can do so just by setting up coordinates and rotation properties of the camera but it doesn’t look so obvious to calculate these values. Usually we want to handle the camera by the keyboard and the mouse or just make it look at a given object. It is not always true. However it looks right for most simple application such as what we are talking about. There is SimpleObjectController class in Alternativa3D for these cases. It makes the camera fly by using the keyboard and mouse and has set of methods to handle the camera from the code (like lookaAt()).

stage3D.requestContext3D();

As we get context3D, we can upload resources and start rendering. private function init(event:Event):void {

for each (var resource:Resource in rootContainer. getResources(true)) {

}

resource.upload(stage3D.context3D);

addEventListener(Event.ENTER_FRAME, enterFrameHandler)

}

camera.y = -300; controller = new SimpleObjectController(stage,

private function enterFrameHandler(event:Event):void {

camera.z = 100;

camera, 200);

controller.lookAtXYZ(0,0,0);

To be successful with the controller you should make a few notes:

stage3D.addEventListener(Event.CONTEXT3D_CREATE, init);

As long we should render every frame, we place this call in enterFrameHandler with controller.update() together.

camera.x = -50;

is needed to get context3D as a parameter meant as destination of this upload. You can be surprised that this stage3D does not have the ready to work context3D, yet. We should ask for it, and start to do something with context3D after it is done.

You should let the controller know about the need of change of the position of the camera every time. It can be done just by calling controller.update(). Just add this call to enterFrame handler if you are going to use a keyboard and a mouse. If a controller performs a relative move such as step forward, it uses coordinates which it keeps in itself. So if you change the camera placement through its properties like x, y, z, it will dropped during the next controller.update() calling. To avoid this you should change the camera placement through controller.set Position().

Friendship with GPU

Having done preparing the 3D world so let to meet our application with GPU. What we will need first is Stage3D and it’s property conext3D which is associated with the video memory. stage3D = stage.stage3Ds[0];

As we already said, all resources should be in such memory. It can be loaded by upload() method which

12/2011 (30)

controller.update();

}

camera.render(stage3D);

Base application done. You can use it as a clear template for yours so it is the reason of Template class name. But there is not any visible object yet. A cube is a good option for the first time so lets add it and rename our class to HelloBox. box = new Box();

box.setMaterialToAllSurfaces(new

FillMaterial(0x804080));

rootContainer.addChild(box);

No object can be visible without material so we just assign one. You should get something like this: Listing 2.

YURI MALTSEV Yuri Maltsev is a consultant at the AlternativaPlatform training center. He graduated from the Perm State Pedagogical University, where he studied computer science and pedagogy. Yuri writes technical documentation for the Alternativa3D, communicates with the developers community on issues involving the Alternativa3D engine.

45


PATTERNS FACTORY

Applied Design Patterns Factory Last month we began our journey to the Design Patterns land with the Singleton Pattern. This month we will see the Factory pattern and how can you apply it to simplify our work when dealing to databases connections. What you will learn…

What you should know…

• • • •

• • • •

Learn about the Factory Pattern Begin to identify the various Design Patterns Use a xml �le to hold con�g options

T

he example app is intended to connect to vary myriad of databases, as Oracle, MySQL and son on. For the example app for this article let’s suppose that this application will connect directly to a database server and perform some operation on it.

A good understanding of AS3 and OO Principles Be curious Love to learn new topics

Listing 1. Con�g.xml �le <?xml version="1.0" encoding="utf-8"?> <connection>

<database>

Project Structure

<adapter>MySQL</adapter>

Let’s begin creating our application structure, create a new ActionScript project called Factory and in the src folder create the Classes folder. Also create a xml file called config. See Figure 1.

<host>localhost</host> <port></port>

<user>root</user>

<password>test</password> <db>test</db>

The Config File

To simplify connection configuration for this example, I’ll be using a xml file that will store all the parameters needed for database connection. See Listing 1.

</database>

</connection>

Listing 2. Interface that all DB classes must implement package Classes.Adapters

{

public interface iDBAdapter

{

function save():void;

function find(params:Array = null):void; function initWithConnection(host:

String,user:String,pwd:

Figure 1. ProjectStructure

46

}

}

String,db:String):void;

12/2011 (30)


Applied Design Patterns Factory

Using this xml file to config our connection type and so on, we ensure that we can change the adapter from MySQL to Oracle for example, without the need to modify any code. We will se this later in the article

Database Adapters

Listing 4. Simple Factory class package Classes

{

As all databases share common attributes such as host name, connection port and etc, I began creating an interface (IDBAdapter) and an abstract class (DataBase). Despite I could create the DataBase class without implementing this interface, doing so ensure that any

public class DBConnectionFactory

{

import Classes.Adapters.DataBase; public function DBConnectionFactory()

{ }

Listing 3. DataBase abstract class

public function create(adapter:String):DataBase

package Classes.Adapters

{

{

switch(adapter)

public class DataBase implements iDBAdapter

{

var dbClass:DataBase;

{

{

dbClass = new MySQL();

}

break;

public function save():void

{

}

case 'Oracle':

import Classes.Adapters.Oracle;

throw new Error('Children must implement

save method');

dbClass = new Oracle();

break;

case 'PostgreSQL':

public function find(params:Array=null):void

{

}

import Classes.Adapters.PostgreSQL;

dbClass = new MySQL();

throw new Error('Children must

break;

implement find method');

case 'SqlServer':

import Classes.Adapters.SqlServer;

public function close():void

{

}

dbClass = new MySQL();

break;

throw new Error('Children must

case 'PostgreSQL':

implement close method');

import Classes.Adapters.PostgreSQL;

dbClass = new MySQL();

break;

public function initWithConfig(host:String,user:

case 'SQLite':

String,pwd:String,db:String):

{

import Classes.Adapters.SQLite;

void

dbClass = new SQLite();

break;

throw new Error('Children must

implement the initWithConfig

}

}

}

method');

}

12/2011 (30 )

case 'MySQL':

import Classes.Adapters.MySQL;

public function DataBase()

}

}

}

return dbClass;

47


PATTERNS FACTORY

Listing 5. MySQL adapter implementation package Classes.Adapters

{

public class MySQL extends DataBase

{

public function MySQL()

{ }

override public function

initWithConnection(host:String, user:String, pwd:String,

Figure 2. AdaptersAndInterface

class that inherits from the DataBase class have to implement the methods defined in the interface. See Listing 2 and 3. Another procedure I made in the DataBase class is that in each method, throw an error that notifies the developer that these methods must be implemented by its children. That’s because in this king of situation any database might deal with each of these tasks differently, but as they implement the same methods defined in the interface, we have a solid API for interaction with of these database servers thus hiding the details of the implementation.

The Little Factory

Now let’s see how our little factory class works, which is pretty straight forward, so it’s simple factory. I begin importing the DataBase class as we will return an instance of the this class, then I defined the create method, where we check the king of database adapter must be return, instantiate it and finally return it. The kind of the adapter to be used is obtained from the config.xml file, if a create another adapter type, the only places I will have to change after creating the new adapter class is this simple factory and then specify that I want to use this adapter in the config file.

{

db:String):void trace('Connecting to the '

+ db +' database on mySQL server ' + host + 'with'

+ 'User: ' + user }

{

}

+ ' and Password: ' + pwd);

override public function save():void

trace('Inserting data into MySQL database');

override public function find(params:Array = null):

{

void

trace('Query parameters: ' +

params.toString());

Figure 3. Output Information

48

12/2011 (30)


Applied Design Patterns Factory

Adapters Implementation

For the example of this article, the database adapters don’t really connect to any database, they just print out its information on the Flex Builder console. See Figure 2 and Figure 3.

Putting it all together

Now that we have our database adapters and our factory, it’s time to put it all to work as shown in the excerpt of the Factory class – our app main class. Listing 5 shows how to load the config information store inside the xml file using URLRequest and URLoader classes. Then it’s assigned an event listener to the onComplete where I get the loaded data from the xml file, assign the result to the config variable. After that a create an instance of the DBConnectionFactory class and pass the type of the database adapter must be created assigning the returned adapter to the db variable and issue the adapter methods. See Listing 6. Listing 6. Loading the con�g �le var config:XML = new XML(); var XML_URL:String = "config.xml";

Figure 4. Data Base Class In complete Implementation

Note

That I could have defined the create method as static, so the line where the factory instance is created would be no necessary. Thus it’s not common that an application developed with Flex to connect directly to a database, but does it via a backend via AMF for example, I tough it would be a nice example to illustrate the concept. I also left some sharp edges in the example, that I will be approaching next article. So that’s all for now. Any question about this article drop me a line.

var XMLURL:URLRequest = new URLRequest(XML_URL); var loader:URLLoader = new URLLoader(XMLURL);

loader.addEventListener(Event.COMPLETE, onComplete);

Listing 7. Using the factory to get an adapter instance function onComplete(event:Event):void

{

config = XML(loader.data); var factory : DBConnectionFactory = new

DBConnectionFactory();

var db : DataBase;

db = factory.create(config.database.adapter); db.initWithConfig(config.database.host,

config.database.user,

config.database.password, config.database.db)

db.find([':all']); db.close();

}

12/2011 (30 )

MARC PIRES Marc Pires is a Developer located in Brazil, during his career, he has worked in various roles such as Network Administrator, Instructor. He has a strong passion for working with and researching new technologies. Specialized on RIA, Iphone Development, Restful web services and other interesting things, he’s one of the main developers of the Esus System and is beginning his startup specialized on mobile development. Contact information: @MarcPires (Twitter) | marcpiresrj@gmail.com | IChat: marcpiresrj@aim.com

49


REVIEW

WinX Free DVD Ripper Ideal Solution for Appreciating DVD Movies on Android Have you ever thought of watching DVD movies performed by favorite actors on Android devices, such as Android phone, Samsung Galaxy Tab, Fascinate, HTC Desire, etc.?

Y

es, put DVD to Android devices and you can appreciate DVD movies anytime and anywhere. Plus, watching DVD movies on Android phone can be a visual enjoyment due to the Android touch screen design and high resolution which well fit for video viewing. If you have no idea how to put DVD to Android devices, WinX Free DVD Ripper can be an ideal solution to rip DVD to Android devices. Here we would like to show how to rip DVD to HTC via this totally free DVD ripper. Free download WinX Free DVD Ripper and install it http://www.winxdvd.com/dvd-ripper/.

Step 1

Launch this software and insert your home DVD or commercial DVD, click DVD disc to load DVD.

Step 2

Click Browse to in output settings area to select a destination of output video.

Step 3

Click HTC and choose an optimal setting from profile settings.

Step 4

Hit the Start button and go! This totally free DVD ripper published by Digiarty Software for you to backup DVD to MP4, WMV, MPEG, H.264, Android phone, iPhone/ 4S, iPod, Apple TV, Samsung, HTC, etc. In addition, you can try WinX DVD Ripper Platinum, the advanced version of WinX Free DVD Ripper to backup DVD to ISO image for later burning, or fully enjoy DVD movies on iPad regardless of all known copy protections, even the most complicated Disney X-project DRM.

Know more about WinX Free DVD Ripper • •

• • •

WinX Free DVD Ripper delivers faster speed than similar free DVD rippers in current market due to its embedded Hyper-threading engine. Support both homemade and commercial DVDs and work stably to convert DVD regardless of CSS encryption, DVD region code, Sony ARccOS, UOPs, RCE, etc. With corresponding profile settings, it can be much easier even for beginners to rip DVD to various video formats and portable devices without quality loss. Freely capture image from DVD video to save as BMP, JPEG pictures. Flexible audio/video settings allow you to balance video quality and file size.

In brief, this free DVD ripping software can be an ideal solution to rip protected DVD to Android phone, iPhone/4S, iPod, Apple TV, Samsung, HTC, etc.

MASON

Figure 1. Rip DVD to HTC Android phone as a simple example

50

Mason is a freelance writer. His goal is writing and sharing anything about apps or software closely related to DVD, CD, Blu-ray, Android and Tab or so, aiming at bringing real & useful message to readers. Contact email: masoncliton@yahoo.com

12/2011 (30)




Turn static files into dynamic content formats.

Create a flipbook
Issuu converts static files into: digital portfolios, online yearbooks, online catalogs, digital photo albums and more. Sign up and create your flipbook.