Create a Whack-a-Mole Game in HTML5 With EaselJS – Active Premium
A couple of months ago, Carlos showed Premium members how to create a Whack-a-Mole game using Flash and AS3. Now, following on from Monday’s introduction to EaselJS, he’ll explain how to use that library to create the same game with the HTML5 canvas and JavaScript.
Preview
Let’s take a look at the final result we will be working towards:
You can read the first five steps of the tutorial over on Tuts+ Premium, or join Premium to read the whole thing.
Active Premium Membership
We run a Premium membership system which periodically gives members access to extra tutorials, like this one! You’ll also get access to Psd Premium, Vector Premium, Audio Premium, Net Premium, Ae Premium, Cg Premium, Photo Premium, and the new Mobile Premium too. If you’re a Premium member, you can log in and download the tutorial. If you’re not a member, you can of course join today!
Also, don’t forget to follow @envatoactive on twitter and grab the Activetuts+ RSS Feed to stay up to date with the latest tutorials and articles.
View full post on Activetuts+



A couple of months ago, Carlos showed Premium members how to create a Whack-a-Mole game using Flash and AS3. Now, following on from Monday’s introduction to EaselJS, he’ll explain how to use that library to create the same game with the HTML5 canvas and JavaScript.
Preview
Let’s take a look at the final result we will be working towards:
Click to try it out!
You can read the first five steps of the tutorial over on Tuts+ Premium, or join Premium to read the whole thing.
Active Premium Membership
We run a Premium membership system which periodically gives members access to extra tutorials, like this one! You’ll also get access to Psd Premium, Vector Premium, Audio Premium, Net Premium, Ae Premium, Cg Premium, Photo Premium, and the new Mobile Premium too. If you’re a Premium member, you can log in and download the tutorial. If you’re not a member, you can of course join today!
Also, don’t forget to follow @envatoactive on twitter and grab the Activetuts+ RSS Feed to stay up to date with the latest tutorials and articles.
In this series finale I’m excited to introduce you to the features that make TimelineMax so special. I’m also going to touch on some special tricks you can do with TimelineLite so that you can apply wild ease effects to the total play back speed of a timeline that includes hundreds of tweens and nested timelines. You have to see it to believe it.
TimelineMax in Action
The swf below contains a single TimelineMax with a repeat value of 1 and yoyo of true. The
totalTimeandtotalProgressvalues reflect the amount of time it takes for the entire sequence to run twice. TimelineMax’saddCallback()method is used to call a function that handles swapping the depths of the blob and the bush so that the blob can jump behind the bush. This swapping happens instantly when the blob finishes his upward movement regardless of whether or not the timeline is playing backwards or forwards.You can find all the files used to create the SWF above in the source files for this tutorial.
Watch the Screencast
Don’t like ads? Download the screencast, or subscribe to Activetuts+ screencasts via iTunes!
TimelineMax Special Properties
TimelineMax’s most popular feature is that you can easily tell it how many times to repeat. There are a number of special properties that can be passed into the TimelineMax constructor’s
varsobject that help you fine tune exactly how the repeat works:tl = new TimelineMax( {repeat: 2, repeatDelay: 1, onRepeat: doSomethingCool, yoyo: true } );The code above will create a timeline that does the following:
doSomethingCool()will fire and the timeline will play backwards instantaneously.doSomethingCool()will fire and the timeline will play forwards instantaneously.Duration, Time and Progress
Once a timeline is told to repeat it is helpful to be able to access a number of properties that take into consideration the duration of all the iterations and delays:
It’s worthwhile to note that TimelineLite does have
totalDurationandtotalTimeproperties but since TimelineLite doesn’t repeat, they offer the most value when used with TimelineMax.Calling a Function From TimelineMax
TimelineMax makes it easy to call functions from any point in time. You may be familiar with the process of adding an
onComplete()callback to an individual tween in a timeline, butaddCallback()offers two advantages:addCallback()functions will be fired when the timeline is playing forwards AND in reverse.addCallback()doesn’t need to be locked to a particular tween endingaddCallback() Method signature:
tl = new TimelineMax(); tl.append( TweenLite.to( mc, 5, {x:500} ) ); tl.addCallback ( doSomethingCool, tl.duration-1, ["Steve"]) ); function doSomethingCool(someValue:String):void{ trace("Hi " + someValue); }The code above will cause the phrase “Hi Steve” to appear in the output panel one second before the previous tween ends.
Calling a Function From TimelineLite
TimelineLite offers similar functionality by way of appending or inserting a TweenLite.delayedCall() method but the code is a bit more verbose.
someTimelineLite.append( TweenLite.to( mc, 5, {x:500} ) ); someTimelineLite.append( TweenLite.delayedCall( 0, doSomethingCool, ["Steve"] ), -1 );Tweening the Playback of a Timeline
One of the best kept secrets of the GreenSock Tweening Platform is that you can tween a TweenLite, TweenMax, TimelineLite and TimelineMax. In the video I focus on tweening the playback of the “timeline brothers”. The benefit of tweening a tween or timeline is that you can adjust the speed at which it plays back and also apply easing.
Tween the timeScale of a TimelineLite/Max
This method is great for getting an animation to slowly come to stop at any point in time or resume playing with a nice ease-in. Notice the nice ease that is applied when entering or leaving a paused state.
Combine TimelineMax’s tweenTo() With an Ease
This method works well for applying an ease to the playback speed of an entire timeline or just a segment. Notice the radical change in playback of the timeline when the Circ In Out and Bounce Out buttons are pressed.
Hopefully the SWF above will entice you find out more by watching the video and downloading the source files.
Conclusion
There are more things that TimelineMax can do but I chose to focus on the most commonly used features. I gave you a look at TimelineMax’s
currentLabelproperty andgetLabelAfter()method in Part 4: Working with Labels. As always I recommend reading and bookmarking the TimelineMax documentation to be sure you are getting the most out the tools you choose to use. I still learn something new every time I poke around in there.It has been an amazing experience for me to share with you nearly three hours of TimelineLite/Max training. I am confident that if you take the time to watch through each video and experiment with the source files you will have an incredibly strong foundation to start building upon. The potential for what you can achieve with TimelineLite/Max is virtually limitless. I truly hope you are as excited as I am about what can be done with these amazing tools.
For those of you who want to continue your TimelineLite/Max education I am sure you will find the following tutorials very useful:
I have to give special thanks to Jack Doyle of GreenSock for creating these amazing tools and also offering his support throughout the creation of these videos. Never in my life have I come across a business or product creator that goes to the lengths that Jack does to keep his customers and legions of enthusiasts so darn happy.
Thanks so much for watching this series and providing feedback. If you would like to contact me with any questions related to TimelineLite/Max please comment below or send an email. You can find my email address at the end of the video.
-Carl
Stumped for what to put on your Christmas list? Or perhaps you’re out of ideas for what to get that special web developer in your life? Let us help! This bumper roundup of gift ideas is two lists in one: the first half is to help developers figure out what to ask for, and the second is to help their friends figure out what to get them!
Serious Business
Being at the end of the year, Christmas is a great time to prepare for the future. The first half of this list is aimed at web developers that are making a list for Santa or their friends and family; if you’re buying a present for another web developer, skip to the Just For Fun section!
General Web Development
JavaScript: The Good Parts
No, I’m not jumping on the “Flash is dead” bandwagon. But there’s no use denying that JavaScript is a very important language for any web developer today, and will become even more so over 2012. If you’ve been holding back from learning it, take a look at this book; it’s a great and highly-respected overview that might change your mind.
Also check out JavaScript Patterns – it’ll help you see how to make JavaScript less messy and more organised. Even AS3 developers could learn a lot from this.
Buy JavaScript: The Good Parts on Amazon
Essential ActionScript 3.0
If you want to stay relevant as a Flash developer, you’ll need to have very sharp AS3 skills. Colin Moock’s excellent book will fill in any gaps in your knowledge, covering everything in “classic” AS3 (meaning there’s nothing on Stage3D and the other newer features).
Warning: This book is not for beginners!
Buy Essential ActionScript 3.0 on Amazon
Head First C#
C# is a flexible language; it’s directly relevant to us because it can be used to develop Unity games and Silverlight apps, but it can also be used to make server-side apps (through ASP.NET), mobile and desktop apps (using Visual Studio), and Xbox 360 games (with XNA). From my own experiences, it’s also a fun language to use, and has a lot in common with AS3. If you’re looking to experiment with something new, it’s a great choice.
Head First C# is a great guide to get you up and running with the language as fast as possible; however, if you’re an experienced programmer already, you may find it a little slow-going. In that case, check out C# in Depth, as well.
Buy Head First C# on Amazon
Head First Design Patterns
If you work with any class-based OOP languages (AS3, C#, etc.) you must read this book. The only exception is anyone that read, absorbed, and fully understood the original GoF book.
Head First Design Patterns is the book that let me understand when the heck we’d ever use an
Interface. It takes the theoretical elements of OOP (inheritance, polymorphism, and all those other definitions from Wikipedia) and makes them practical. And it does that in the brain-friendly way that’s typical of the Head First series, so you’ll actually understand it.Buy Head First Design Patterns on Amazon
Adobe Web Premium CS5.5
I’ll admit: this is expensive. If you’ve managed without Web Premium so far, you’ll probably continue to manage just fine – though that’s not to say this isn’t useful. I own Web Premium CS5 and get a lot of value out of it (particularly Photoshop, Illustrator, and Flash Pro).
If you’re currently using CS4, I recommend upgrading – not because CS5.5 is so much better, but because when CS6 comes out in a few months, you won’t be eligible for upgrade pricing otherwise. See this article on Psdtuts+ for more info.
Buy Adobe Web Premium CS5.5 on Amazon
WebStorm (HTML/CSS/JavaScript IDE)
Sure, you can write HTML, JavaScript, and CSS in a plain text editor like Notepad++ or TextMate, but a decent IDE makes the task so much easier, so much less tedious, and so much less prone to silly typos.
WebStorm is great for browser-based apps and games; check out Jeffrey Way’s thoughts over at Nettuts+ for more info.
Buy WebStorm from JetBrains
App Development
Head First HTML5 Programming
If you’re a little confused about what HTML5 actually is (besides a buzzword, apparently), and you’re only familiar with HTML…4, then this book will clear everything up while teaching you how to develop HTML5 apps along the way.
As with many Head First books, it’s great for beginners but perhaps a little slow for experienced developers. If you’re already very familiar with AS3 and JavaScript, consider HTML5 Up and Running (also available for free online as Dive Into HTML5), which covers what’s new in HTML5 from that perspective.
Buy Head First HTML5 Programming on Amazon
Design for Hackers
Programmers do not have a reputation for being good at design. This book aims to remedy that, by teaching the basic principles of design as they apply to web apps and mobile apps, in a way that can be understood by hackers.
Another excellent book on the topic is the Non-Designer’s Design Book, although that focuses on print and static web design, rather than interactive apps.
Buy Design for Hackers on Amazon
Head First jQuery
jQuery is an incredibly popular JavaScript library – view the source of the next few web pages you visit, and see whether they use it. It’s designed to make coding JavaScript web applications easier, by simplifying how you access the DOM, use events, and create animations. (My simple description here does not do it justice.)
I would have picked jQuery: Novice to Ninja for this roundup, and that’s still a great choice – but I suspect you’d be better off waiting for the new edition of that book, which is due on February 22, and reading Head First jQuery in the meantime.
Buy Head First jQuery on Amazon
Guide to Robotlegs
Robotlegs is to AS3 as jQuery is to JavaScript – not in terms of its scope, exactly, but rather in that it’s an AS3 framework that makes developing web apps much simpler and more manageable. This book, by Stray and Joel Hooks, explains how to use Robotlegs to develop two example apps, simultaneously teaching good app architecture and Robotlegs itself. Check out Stray’s post for more details.
Buy Guide to Robotlegs on Amazon
Game Development
Unity Game Development Essentials
This is the definitive book for Unity beginners. Throughout the book, you’ll build a single 3D game, adding new features and learning about new aspects of Unity in each chapter.
Check out our interview with Will Goldstone, the author.
Buy Unity Game Development Essentials on Amazon
Unity Game Development by Example
This is the other definitive book for Unity beginners. It takes a different approach to Will Goldstone’s; Ryan’s writing style is very different (you’ll know what I mean if you’ve read his blog), and he builds up a selection of games in both 2D and 3D, rather than focusing on one big project. If you’re new to Unity, get both!
Check out our interview with Ryan Henson Creighton, the author.
Buy Unity Game Development by Example on Amazon
Making Things Move!
The full titles of this book is Foundation ActionScript Animation 3.0: Making Things Move! – but don’t be fooled, this isn’t about hand-drawn, frame-by-frame animation or tweens. This is about making things move through code: billiard ball physics, kinematics, collision detection, momentum, gravity, and other such topics are covered, as well as the relevant areas of maths and physics that you might not remember from school.
There are plenty of books about Flash Game Development, but in my opinion this is the first one you should read – at least if you’re making a platformer, a shoot-’em-up, a breakout clone, or anything else involving motion. For card games, strategy games, hidden object games and the like, you might want to look elsewhere.
Buy Making Things Move! on Amazon
Flash Stage3D Game Programming
HTML5 may be catching up to Flash in certain areas, but the Flash Platform isn’t standing still. The most impressive new feature of Flash Player 11 is Stage3D, which adds awesome 3D rendering capabilities to the Player. Like Will Goldstone’s Unity book, this will take you through the process of building a 3D game from start to finish, adding more each chapter.
Check out the author’s page for a look at the final game!
Buy Flash Stage3D Game Programming on Amazon
The Art of Game Design
This book takes a look at game design in general terms, without being tied down to a specific platform – indeed, one section of the book recommends building some physical board games or card games, to get to the heart of what makes game mechanics work.
I can’t praise this book highly enough. It’s fascinating and fun to read, as well as being packed with practical information. By the end of it, you’ll be able to say, “I am a game designer.”
Buy The Art of Game Design on Amazon
A Theory of Fun
Another excellent book on game design that’s not tied down to a platform. This book centers around one concept – that the fun in games comes from the player attempting to fully understand a pattern – and explores it in depth, with a ton of examples and illustrations.
Take a look at the early comic form of the book to see whether it’s the sort of thing you’d enjoy.
Buy A Theory of Fun on Amazon
Subscriptions
Tuts+ Premium
I’d be remiss if I didn’t mention Tuts+ Premium. The amount of material available on the site today is staggering: ebooks, full courses, and of course all the tutorials. Check out my earlier post for the reasons why I recommend signing up. You can buy a year’s subscription at a 20% discount compared to paying monthly.
Sign Up to Tuts+ Premium
Safari Books Online
I’ve written a full review of Safari Books Online on my own blog; in summary, it’s the Netflix of technical books. Almost every book listed in this roundup is available for you to view in your browser, meaning you can read them on your computer, your tablet, or your phone.
There are different payment plans: the cheapest lets you read up to five different books a month, while the most expensive lets you read as many as you like, and gives you full text search across the contents of the entire catalog.
Sign Up to Safari Books Online
Dropbox
Dropbox takes a section of your hard drive and shares it across all your devices, so you can access your files on any computer or smartphone and via their web app. What’s really impressive is how it remains so invisible: it just sits in the background and syncs everything, without you having to think about it or do anything.
The only problem with the free version is that it’s limited to just 2GB of space. A subscription can bump that up to 50GB or 100GB, and also allow you to access previous versions of files, and files that were deleted, via the Pack-Rat add-on.
Get a Dropbox Pro Account
Evernote
I love Evernote. Like Dropbox, it syncs things across different devices, but unlike Dropbox, it’s focused specifically on notes rather than general files. At its core, it gives you an interface for typing in bits of text, and letting you search across all of them later. But there are plenty more bells and whistles, like tagging, the ability to extract text from images, rich text editing, and so much more. I don’t use bookmarks any more: I use the browser extension to clip the most interesting parts of the page, and tag it so that I can find it later.
The Premium program gives you more space, lets you view previous revisions of notes, and gives you the ability to share notes with other people, as well as some other enhancements.
Go Premium with Evernote
Just For Fun
Okay, that covers stuff that developers need. Now, how about stuff they’ll want? If you’re a developer reading this, maybe you should send the article to your family and friends, and stop reading here so that you don’t spoil the surprise…
Hobbies
Cooking for Geeks
Cooking is great fun; it’s creative, and at the end of it, you have something delicious to eat. This book makes cooking accessible to geeks that don’t know their way around the kitchen yet, but contains plenty of inspiration for those that have been cooking for years.
It also contains plenty of ideas for cookware and gadgets to buy for those that love to cook.
Buy Cooking for Geeks on Amazon
Arduino
Arduino is like LEGO Mindstorms for grown-ups. In brief, it makes it much easier to design and build electronics. This means it can take programming away from computers, and into the physical world.
The start kit suggested here is just one of many that are available; check out the Arduino homepage and search Google for others. There are plenty available to fit different budgets and interests.
Buy the Adafruit Arduino Experimentation Kit
The Best of Instructables
Instructables is a website for Do-It-Yourself projects that aren’t just about putting shelves up. Food, furniture, toys, and costumes are covered here, so there’s plenty of inspiration for someone wishing to get hands-on with their creativity.
Buy The Best of Instructables on Amazon
Geocaching Phone Starter Kit
Geocaching is modern day treasure hunting. Over a million “caches” are hidden across the world, and geocachers use the website and a GPS device to find them, logging their name when they do.
Most web developers will already have a phone with GPS, which is enough to play the game, so this kit just adds a bit more fun to the hunt (as well as making it easier to participate in bad weather).
Buy Geocaching Phone Starter Kit from Geocaching.com
If you’re buying a gift for a photographer (hobbyist or professional), check out The Official 2011 Phototuts+ Holiday Gift Guide on our sister site Phototuts+. For illustrators and designers, Vectortuts+ has a great gift guide too.
Leisure
Skyward Sword
A lot of amazing games came out in the last couple of months, let alone the last year: Skyrim, Arkham City, Uncharted 3, Battlefield 3, Assassin’s Creed 3, and many more. Rather than turning this into a “best games of 2011″ roundup, I’ll mention my two personal favourites. The first is the latest Zelda game, Skyward Sword.
This is an RPG without the grind; it’s part adventure and part puzzle. The controls make full use of the WiiMotion Plus (so it requires the right attachment or controller): as well as letting you aim and pilot by pointing and tilting the Wiimote, it also moves Link’s sword to follow the Wiimote, leading to some great puzzles. I can’t do it justice in this small space; read Edge’s review for more details.
Buy Skyward Sword on Amazon
Sonic Generations
Sonic Generations complements Skyward Sword nicely: Skyward Sword is only on Wii, while Sonic Generations is on every platform but Wii; Skyward Sword is best enjoyed in chunks of an hour or two at a time, while Sonic Generations is still fun in ten-minute doses; Skyward Sword requires a lot of thinking, backtracking, and solving puzzles, while Sonic Generations is fast-paced, almost like an arcade racing game.
Sonic Team has put out some real stinkers over the past decade, but Sonic Generations finally gets it right. It features revamped levels from previous games – Chemical Plant in full 3D! – so there’s a great nostalgia kick to it as well.
Buy Sonic Generations on Amazon
In the Plex
Again, with so many non-tech books released in a year, it’s impossible to write a comprehensive roundup, so I’ve just picked my favourite.
In the Plex is essentially Google’s biography, from the founders’ days at Stanford, through their hiring of a CEO, past the invention of Gmail and the troubles in China, up to the present day with the launch of Google+. It’s a fascinating story for anyone that’s interested in the web.
Buy In the Plex on Amazon
The Complete Calvin and Hobbes
I’ve never met anyone that dislikes Calvin and Hobbes, but geeks seem even more aware of it and fond of it than the general public. This collection contains almost every single comic (in glossy full colour where appropriate), with an introduction and commentary by Bill Watterson.
For someone that’s new to the pair, The Essential Calvin and Hobbes would be a better (and cheaper, and easier to read) introduction, but for a long-time fan, the Complete box set is a wonderful thing to own.
Buy The Complete Calvin and Hobbes on Amazon
Go the F–k to Sleep
This is a book for parents with young children and a sense of humour. As you can probably guess by the title, it’s a children’s bedtime story with a lot of profanity. Even better: the audiobook version is narrated by Samuel L. Jackson.
Buy Go the F–k to Sleep on Amazon
Community
The Wire, Breaking Bad, Misfits, Game of Thrones… again, plenty of great TV box sets to choose from. So why Community? Because it’s consistently smart and funny. The first two series are some of the best TV I’ve ever seen.
Buy Community Series 1 and Series 2 on Amazon
Accessories
Amazon Kindle (E-Ink)
With all the books recommended above, perhaps it’s time to go digital. I’ve had a Kindle 3 for just over a year now, and I love it. It doesn’t sound like an amazing invention – “it’s basically a cut-down tablet, right?” – but anyone that enjoys reading would benefit from owning one.
There’s no hassle in it being electronic: the display uses actual ink that’s rearranged electronically, which makes it sharp and clear (and not like staring at a lightbulb), and even with daily reading, it can last a few weeks without needing to be recharged. But it reduces some of the hassle you probably don’t even notice is in a book: any book you order is delivered in seconds; no matter how many books you own, it doesn’t take up any more space in your bag; you can look up the meaning of any word with a few key presses, and you can highlight passages and keep notes on them that get synced to the internet.
The model shown here is a Kindle Touch, though there are others available, with different designs and at different price points.
Buy Amazon Kindle on Amazon
Tablet
Some tech sites speculate that tablets are going to cut in to PC sales, with more and more people buying a new tablet instead of a new laptop. I don’t think this is going to be the case for web developers, so a tablet still remains a luxury as far as I’m concerned – although one could justify such a purchase to the tax man, since it’s handy for cross-browser testing.
The iPad 2 is still the device to beat here, but some very nice Android alternatives are popping up: there’s the Transformer Prime, with optional keyboard dock that turns it into a netbook, and the Samsung Galaxy Tab’s 8.9″ and 10.1″ models. For those on a budget, the Kindle Fire is probably the best, although reviews have expressed disappointment.
iPad 2, Kindle Fire, Transformer Prime, Galaxy Tab
Messenger Bag
For years I carried my computer in a cheap old laptop bag that looked awful and hurt my shoulder. Splashing out on a decent bag felt like overspending on a luxury, and the amount of choice was paralysing. I’m very glad I got past that to buy a Timbuk2 D-Lux (pictured), because now I take it everywhere. It carries my shopping, it has a Napoleon pocket for my Kindle, it’s my in-flight bag when I go on aeroplanes… I love it.
Like I say, there’s a lot of choice available – take a look at Rands’s excellent post on choosing a bag to see what I mean – but this might make it a perfect gift.
Buy a Timbuk2 D-Lux (pictured)
Charity
Kiva
I’m not going to prioritise one cause over any other, but as a gift, a Kiva card makes a lot of sense, because the recipient gets to make a personal choice over what to donate it to.
Actually, “donate” isn’t quite the right word; Kiva works through micro-loans. A Kiva user might lend $25 to a third-world school that needs to buy books, or a market seller who needs to buy his initial stock, or a farmer that needs to buy new livestock. Over time, these entrepreneurs make the money back, and repay the loan – which can then be lent to another worthy cause. This means it’s literally a gift that keeps on giving.
Buy a Kiva gift card
I hope that’s helped inspire you this Christmas! Don’t forget to check out the other Tuts+ sites for their gift guides over the next few days.
If you’ve got any other gift ideas, please share them in the comments!
There’s been some resistance from Flash developers to our new HTML5 content. In this article – aimed at experienced AS3 coders – we’ll look at EaselJS, a JavaScript library that makes working with the HTML5 canvas very similar to working with the Flash display list.
The HTML5 Scene
The “war” between Flash and HTML5 isn’t news to anyone involved in browser or mobile development and as Michael points out there’s no harm in learning HTML5 even if you know Flash and ActionScript.
HTML5 is a new and evolving technology and there are currently no full-featured tools quite like the Flash IDE covering the whole workflow to create games or applications, but if you’re familiar with Flash Builder or FlashDevelop it shouldn’t be hard to code in any text editor using external files as your assets.
(Editor’s Note: There are plenty of JavaScript and HTML editors, though – take a look at JetBrains WebStorm for a great example.)
Introducing EaselJS, Tween JS and SoundJS
Luckily for us, Grant Skinner has developed a JavaScript library that will make our learning less complicated. In his own words:
EaselJS uses a similar syntax to ActionScript; it has a Display List, Stage, Graphics and even Filters, this will make working with the canvas easier for us Flash developers.
Additionally we can complete our easel development with the TweenJS and SoundJS scripts which, as the names suggest, handle animations and sound.
The Display List
The Display List works very similar to ActionScript:
In this code, the
myStagevariable is the linked reference of the canvas to the Stage class in EaselJS. More on that in the example at the end of this tutorial.Mouse Events
The way to add Mouse Events couldn’t be easier:
myChild.onPress = myFunction myFunction(){console.log('pressed');}Text
This code adds a Text object and places it in the stage.
var myText = new Text('Activetuts+', 'Bold 15px Helvetica', #FFF); myText.x = 25; myText.y = 25; myStage.addChild(myText); myStage.update();Tickers
The Ticker class provides a centralized tick or heartbeat, broadcast at a set interval; the
tick()event can be used as a substitute for an AS3 Timer or EnterFrame event.The following code sets the frame rate to 30 and deines the stage as the listener for the ticks.
Tweens
The Tween class is an external addition to EaselJS which is available by adding the TweenJS script to our document. It works very similarly to tween engines in ActionScript.
Sounds
We can play a sound by adding the SoundJS script to our document and writing the following code:
SoundJS.add('mySound', 'mySound.mp3', 1); SoundJS.play('mySound');Hello World!
An introduction to a programming library would not be complete without a hello world example! Follow these steps to create a very simple HTML5 Canvas hello world containing images, mouse events, text and more.
Step 1: HTML Structure
Let’s prepare our HTML document, it is a simple html structure to begin writing our app.
Step 2: Hide Mobile Highlight
Let’s add a little bit of CSS too: this line will remove the default highlight when you tap on an element using a mobile browser; without this the mobile experience would decrease drastically.
<!DOCTYPE html> <html> <head> <title>Hello World</title> <style>*{-webkit-tap-highlight-color: rgba(0, 0, 0, 0);}</style> </head> <body> </body> </html>Step 3: JavaScript Libraries
The following code adds the necessary JavaScript libraries for our app to work.
<!DOCTYPE html> <html> <head> <title>Hello World</title> <style>*{-webkit-tap-highlight-color: rgba(0, 0, 0, 0);}</style> <script src="easel.js"></script> <script src="Tween.js"></script> <script src="HelloWorld.js"></script> </head> <body> </body> </html>Step 4: Call Main Function
In the next lines we call our constructor, this is the main function that will be created later in our javascript code.
<!DOCTYPE html> <html> <head> <title>Hello World</title> <style>*{-webkit-tap-highlight-color: rgba(0, 0, 0, 0);}</style> <script src="easel.js"></script> <script src="Tween.js"></script> <script src="HelloWorld.js"></script> </head> <body onload="Main();"> </body> </html>Step 5: Canvas Tag
The Canvas is added in this line, we assign an ID to reference it later and also set its width and height.
<!DOCTYPE html> <html> <head> <title>Hello World</title> <style>*{-webkit-tap-highlight-color: rgba(0, 0, 0, 0);}</style> <script src="easel.js"></script> <script src="Tween.js"></script> <script src="HelloWorld.js"></script> </head> <body onload="Main();"> <canvas id="HelloWorld" width="480" height="320"></canvas> </body> </html>Step 6: JavaScript
Let’s begin our app creation!
Open your preferred JavaScript editor (a basic text editor will work, but you won’t have syntax highlighting) and prepare to write your awesome app. Remember to save the file with a js extension in your project folder.
Step 7: Define Canvas
We’ll start by defining all the graphic and logic variables.
The next variables represent the HTML canvas element and the stage that will be linked to it. The stage variable will behave in a similar way to the AS3 stage.
Step 8: Background
This variable stores the background image.
Step 9: Button
Another variable to store the button image.
Step 10: Variables
These are the variables we’ll use, read the comments in the code to know more about them:
Step 11: Constructor
The constructor is a function that runs when an object is created from a class; this particular function will be the first to execute when the web page is loaded.
function Main() { //code... }Step 12: Link Canvas
This code gets the HTML canvas ID and links it to the EaselJS Stage class. This will make the stage variable behave like the Stage class in AS3.
/* Link Canvas */ canvas = document.getElementById('HelloWorld'); stage = new Stage(canvas);Step 13: Enable Mouse Events
Mouse Events are disabled by default in EaselJS to improve performance. Since we need those in the application, we’ll add the following line:
Step 14: Load Graphics
This code is used to preload the graphics with the help of a function that we’ll write later. It sets the Image object we created before to the source png file in our document folder. A name is given to detect which image is loaded and lastly the function that handles the loaded images is called.
You’ll need to download the images from above (or make your own) in order for this to work.
Step 15: Set Ticker
The following code sets the frame rate to 30 and defines the stage as the listener for the ticks.
The TweenJS class will listen to this tick to perform the animations.
Step 16: Preload Graphics
Every time a graphic is loaded this function will run. It will assign each image to a bitmap object and check that all the elements are loaded before proceeding.
function loadGfx(e) { if(e.target.name = 'bg'){bg = new Bitmap(bgSrc);} if(e.target.name = 'button'){btn = new Bitmap(btnSrc);} gfxLoaded++; /* Display graphics until all of them are loaded */ if(gfxLoaded == 2) { buildInterface(); } }Step 17: Build Interface
This code places the graphics on the stage and adds a mouse listener to the button.
function buildInterface() { btn.x = centerX - 40; btn.y = centerY - 12; stage.addChild(bg, btn); stage.update(); // Very Important /* Add button listener */ btn.onPress = showText; }Step 18: Show Text
The function that will run when the button is pressed, explained in the code commentary.
function showText() { console.log('This works like trace!'); /* Remove Listener */ btn.onPress = null; /* Create Text */ var msg = new Text('Hello World!', 'Bold 25px Arial', '#EEE'); msg.x = centerX - 70; msg.y = centerY; stage.addChild(msg); msg.alpha = 0; /* Animation */ Tween.get(btn).to({y:centerY + 50}, 300); Tween.get(msg).wait(400).to({alpha:1}, 400); }That’s it! Click here to see this simple demo in action.
Conclusion
Congratulations! You just made an HTML5 canvas application compatible with all major browsers, including mobile. Stay tuned for more here on Activetuts+.
I hope you liked this tutorial, thank you for reading!
Stop using static menus! Most players immediately base their initial impression of a Flash game on the menu that they see when they load it. Stand out from the crowd with a dynamic menu!
Final Result Preview
Introduction: Static to Dynamic
The word “static” essentially means lacking in change. The majority of menus we see throughout web games are lacking in change, you simply press Play and the game starts. Menus like that are overused and show little creativity or innovation.
To make a menu “dynamic” we must continuously cause change. So in this tutorial that is exactly what we are going to accomplish: a menu that continuously changes.
Step 1: Setting Up
The first thing we are going to need to create is a new Flash File (ActionScript 3.0). Set its width to 650px, its height to 350px, and the frames per second to 60. The background color can be left as white.
Now save the file; you can name it whatever you please, but I named mine
menuSlides.fla.In the next section we will create the nine MovieClips used in the menu. For reference, here is a list of all the colors used throughout the tutorial:
Step 2: Creating the Slide MovieClips
To start with we will create the slides used in the transitions, but before we begin let’s turn on some very useful Flash features.
Right-Click the stage and select Grid > Show Grid. By default it will create a 10px by 10px grid across the stage. Next, right-click the stage again and this time select Snapping > Snap to Grid.
Now we can begin drawing! Select the Rectangle Tool and draw a Light Gold rectangle, 650px wide and 350px tall (you can Alt-click on the stage to make this easier). Now change the color to Gold and draw groups of three squares, each 20x20px, to form the shape of an L in each corner,:
Select the whole stage, right-click and choose Convert to Symbol. Name the MovieClip
goldSlideand make sure that the type is MovieClip and the registration is top-left.To save time and make things a whole lot easier, right-click the
goldSlideMovieClip in the Library and select Duplicate Symbol three times to make three more copies. Change the colors in the new MovieClips to blue, green and red, then rename the MovieClips toblueSlide,greenSlideandredSlide.Before we continue we should add some text to each slide. On goldSlide write PLAY, on blueSlide write INSTRUCTIONS, on greenSlide write OPTIONS and on redSlide write CREDITS.
Now that we have the text in place we can break it apart by right-clicking on it and selecting Break Apart twice; this will break the text down to a fill which will transition more smoothly. Plus as a bonus there will be no need to embed a font if you are just using it for the menu!
The Buttons
Now that we have drawn the 4 slides we can focus on the
sideButtonMovieClip that is used to move the slides either left or right.First, draw a rectangle 30x60px with only a stroke (no fill), then draw diagonal lines 45 degrees from the top-right and bottom-right corners until they snap together in the middle of the opposite side. Now apply a Matte Grey fill to the triangle:
Next, delete the lines, then right-click the triangle and select Convert to Symbol. Name it
sideButton, set the type to Button and make sure the registration is in the top-left corner.Now insert 3 keyframes in the timeline by right-clicking the timeline and selecting Insert Keyframe. On the Up frame, select the fill of the triangle, go to the Windows tab and select Color. Change the Alpha to 50%. On the Over Frame repeat the same process, but this time set the alpha to 75%.
Now we can begin on the four numbered circle buttons, for jumping directly to a particular slide.
To start draw a white 30px circle with no stroke. Convert it to a symbol, name it
circleOne, and set its type to Button and its registration point to the center. Insert three keyframes like we did before and then go to the Up frame.Draw a black 25px circle with no stroke and center it to the middle through the coordinates or by using the Align menu. Next deselect the black circle, then reselect it and delete it. You should now have a white ring remaining. Now grab the text tool and put a white “1″ in the center of the ring. Then break this number apart until it is a fill.
Go to the Over frame and draw a black “1″. Center it and break it apart until it becomes a fill. Now deselect and reselect the fill, then delete it. Select everything on the frame and copy it, then go to the Down frame, select everything on it and hit delete. Paste in what we have copied.
Now create three more circle MovieClips, following the same process, for the numbers 2, 3 and 4.
Step 3: Positioning the MovieClips
Okay, we’re almost half-way done! First drag all of the slides onto the stage and position them with the following coordinates:
Now drag and drop two copies of the sideButton. The first copy should be positioned at (10,145); before we can position the second copy we must first flip it!
Select the second copy and press Ctrl-T. Change the left-right to -100% and leave the up-down at 100%. Now move the second copy to (640,145).
Finally drag and drop the four circle MovieClips and position them as so:
Your stage should now look like this:
The blue, green and red slides are hidden just off to the right of the stage. Now select everything on the stage and convert to a symbol. Name it
menuSlidesMC, set the type to MovieClip and the registration to the top-left corner, and export it for ActionScript asMenuSlidesMC.Before we finish we must give each of the MovieClips inside
menuSlidesMCan instance name. Select each slide in the order they appear from the left and name themslide1,slide2,slide3andslide4respectively. Name the circle buttonsone,two,threeandfour, and finally name the side buttonsleftandright.Step 4: Setting Up the Classes
Now that all of our MovieClips have been created we can start setting up the two classes we are going to use.
First go to your Flash file’s Properties and set its class to
menuSlides; then, create a new ActionScript 3.0 file and save it asmenuSlides.as.Now copy the following code into it; I will explain it after:
package{ import flash.display.MovieClip; import flash.events.Event; import flash.events.MouseEvent; public class menuSlides extends MovieClip{ public var menuSlidesMC:MenuSlidesMC = new MenuSlidesMC(); public function menuSlides(){ addChild(menuSlidesMC); } } }Pretty basic – it’s a document class, into which we imported the MovieClips and Events we will use. Then we created an instance of
MenuSlidesMC, and added it to the stage.Now create a new ActionScript 3.0 file for the
menuSlidesMCinstance. Save it asMenuSlidesMC.asand copy the following code into it:package{ import flash.display.MovieClip; import flash.events.Event; import flash.events.MouseEvent; public class MenuSlidesMC extends MovieClip{ public var speed:Number = new Number(); public var activeSlide:Number = new Number(); public function MenuSlidesMC(){ speed = 10; activeSlide = 1; addEventListener(MouseEvent.CLICK, slidesClick); addEventListener(Event.ENTER_FRAME, slidesMove); } } }Just like last time, we imported what we are going to need, but we created two number variables. The first variable,
speed, is actually how many pixels the slides are moved by each frame. (Note: this number has to evenly divide into your stage’s width to give a smooth transition). The second variable,activeSlide, tells us which slide is currently set to be on screen.We also added two event listeners for functions we are going to create; one of them is called on a mouse click, and the other is called at the beginning of every frame.
Step 5: Creating the Event Handler Functions
To begin we will get the mouse click function out of the way. Start by creating a public function named
slidesClick():public function slidesClick(event:MouseEvent):void { }Next we will create some if-statements regarding the
event.target.name. Basically, this property stores the name of the object that was targeted by the mouse click. We can use this to check which button is pressed:if(event.target.name == "left"){ if(activeSlide>1){ activeSlide-=1; } }else if(event.target.name == "right"){ if(activeSlide<4){ activeSlide+=1; } } if(event.target.name == "one"){ activeSlide=1; }else if(event.target.name == "two"){ activeSlide=2; }if(event.target.name == "three"){ activeSlide=3; }else if(event.target.name == "four"){ activeSlide=4; }The code above goes in the
slidesClick()function. The first set of if-statements are for the left and right side buttons; they increase or decrease the value ofactiveSlide, but never allow the value to become less than 1 or greater than 4 (since we only have four slides). The second set of if-statements are for the circle buttons; instead of just incrementing or decrementing the value ofactiveSlidethey set it to the selected value.Now let’s begin with the
ENTER_FRAMEhandler function:public function slidesMove(event:Event):void { }Add the
slidesMove()function below yourslidesClick()function and we’ll start adding some code to it. First, we’ll use aswitchto check which slide should be on the screen, based on the value ofactiveSlide:switch (activeSlide){ case 1: break; case 2: break; case 3: break; case 4: break; }Now in each case we will create an if/else block that will check that slide’s current x-position, and move all of the slides either left, right, or not at all, depending on where the desired slide currently sits.
The first case looks like this:
if(slide1.x<0){ slide1.x+=speed; slide2.x+=speed; slide3.x+=speed; slide4.x+=speed; }else if(slide1.x>0){ slide1.x-=speed; slide2.x-=speed; slide3.x-=speed; slide4.x-=speed; }Now all we have to do is repeat the same process for the other cases! After you are done your swtich should look like this:
switch (activeSlide){ case 1: if(slide1.x<0){ slide1.x+=speed; slide2.x+=speed; slide3.x+=speed; slide4.x+=speed; }else if(slide1.x>0){ slide1.x-=speed; slide2.x-=speed; slide3.x-=speed; slide4.x-=speed; } break; case 2: if(slide2.x<0){ slide1.x+=speed; slide2.x+=speed; slide3.x+=speed; slide4.x+=speed; }else if(slide2.x>0){ slide1.x-=speed; slide2.x-=speed; slide3.x-=speed; slide4.x-=speed; } break; case 3: if(slide3.x<0){ slide1.x+=speed; slide2.x+=speed; slide3.x+=speed; slide4.x+=speed; }else if(slide3.x>0){ slide1.x-=speed; slide2.x-=speed; slide3.x-=speed; slide4.x-=speed; } break; case 4: if(slide4.x<0){ slide1.x+=speed; slide2.x+=speed; slide3.x+=speed; slide4.x+=speed; }else if(slide4.x>0){ slide1.x-=speed; slide2.x-=speed; slide3.x-=speed; slide4.x-=speed; } break; }And that’s it! We are all finished with the code and the menu should be working great right now.
Conclusion
Thank you for taking the time to read through the tutorial, I hope it was helpful and that you learned a little something about dynamic menus.
Twice a month, we revisit some of our readers’ favorite posts from throughout the history of Activetuts+. This tutorial was first published in April, 2009.
In this tutorial we’ll create a magnifying glass effect, demonstrating use of the displacementMapFilter. The effect can be achieved in a relatively short space of time and with very little code.
Final Result Preview
Let’s take a look at what we’re aiming for:
Step 1: Brief Overview
We’re going to work with two layers, plus an additional optional layer. The first will hold an image which will contain the visual graphics, this can be anything. The second layer will be the color map which will control the pixel pushing. The third layer will hold the ActionScript.
An optional fourth layer will be an overlaying graphic acting as the frame or lens surround.
Lets look into it!
Step 2: Document Setup
First thing we need to do is make a new ActionScript 3.0 Flash file – make the document size 530px X 400px with a framerate of 30fps.
Step 3: Import Resources
Next we need to import an image that we can use for this effect – I found a cool, freely available desktop image at 1024px X 768px.
Import this to stage and name the layer "Image".
Now let’s scale the image down to 50% and center it.
Step 4: Scripting the Filter
Create a new layer on top and call it "Actions". Then let’s bring the actions panel out and start coding the effect. First we need the filter for the image so let’s create a new filter object and call it "dFilter". We’ll leave the filter free of parameters as there are quite a few to set.
Next we need to set these filter parameters in listed view.
Step 5: Effect Scale
Let’s start with the easiest ones and set the scales to around 50. This is the amount to which the magnifier will zoom in. It can also be set to a negative value, but in this case we need it to zoom in, not out.
Additional lines: 3,4
Note: this is not the actual order in which the filter normally accepts the parameters. However, in this case we can add them as we wish because we’re using the listed view for a better overview.
Step 6: Color Channel Components
Next we’ll set the component color channels for X and Y – this dictates which colorchannels in the control map (which we’ll create in a second) the filter will listen to.
If you’re familiar with the RGB hex code #RRGGBB, we can choose from BitmapDataChannel.RED, BitmapDataChannel.GREEN and BitmapDataChannel.BLUE. To make it easier we can also just write 1 (red), 2 (green) or 4 (blue) – (and no I didn’t make a spelling mistake, the blue is 4; this is set from the actual channel position in the hex code). In this example we’ll just stick to red (1) and green (2)- but we’ll come back to more about this when we design the actual displacement map.
Additional lines: 5,6
Step 7: Displacement Mode
Next we need to set the mode to determine how the pixels will react if they are pushed further than the image boundaries. Here we can choose from:
DisplacementMapFilterMode.COLOR / DisplacementMapFilterMode.WRAP / DisplacementMapFilterMode.CLAMP / DisplacementMapFilterMode.IGNORE
Again we can simplify this by writing "color", "clamp", "wrap", "ignore". I won’t get any further into these in this tutorial, so lets just use “color” which works best in most cases.
Additional lines: 7
This mode allows pixels to continue beyond the image boundary (in case the filter pushes the pixels further than the edge of the image).
Step 8: Surrounding Color and Alpha
Now let’s set the surrounding color to 0×000000 and alpha to 0. This is 100% transparent, so nothing is displayed outside the image except the source pixels.
Additional lines: 8,9
Step 9: Filter Effect Position
Now we need to set the position where the filter will affect the image; our lens position. This has to be set as a Point object containing the x and y value. We’ll begin by creating the point object so it’s ready for use when we assign it to the displacementMapFilter. Let’s call it "dPoint" and set it to 0, 0 as initial values. We’ll come back to this in a moment when we need to instruct this point to follow the mouse.
Next we assign "dPoint" to the "dFilter’s" point position.
Additional lines: 1,11
Step 10: BitmapData
Last but not least we need to assign the control map to the filter. This is the map which contains the colored pixels that the componentX and Y listen to.
Additional lines: 13
Here we also need a BitmapData object to act as a data container for our color-map.
Step 11: Designing the Color Map
First we create a 100px X 100px, red to black, linear gradient square. This will take the current pixels and push them left and right as we set the componentX to red. Let’s make this a movieclip called "redMap"
Then we do the same again – but this time with a green to black linear gradient square, again 100px X 100px. This time we’ll also rotate it 90°. You might remember we set the component for the Y axis displacement as green (componentY = 2) so the gradient goes along the y axis. Once again we’ll convert it to a movieclip, this time calling it "greenMap"
Step 12: Preparing the Map for Capture Phase
We now have two separate color images; we need just one, so set the blendmode of the greenMap to "screen". Every color from the greenMap will then shine through on the redMap. Place the greenMap on top of the redMap and make sure they align correctly.
Select both movieclips by clicking on the layer "Map" and convert the two into one movieclip called "colorMap". Then set the instance name to "colorMap_mc".
Step 13: Map Container
Now let’s return to the code and continue by capturing the colorMap movieclip in a bitmapData.
Go to the top of the code and create a new BitmapData object. Let’s call it "dMap" and set the size of it to match the size of our colorMap (in this case 100px X 100px, but this can be almost anything). We’ll set transparent to "true" and color to 0×808080. This ensures that any remaining pixels in the bitmapData are neutral.
Additional lines: 3
Step 14: Capture Phase
We need to draw the colorMap’s content into the bitmapData. Once that’s done, we’ll be able to use script to delete the colorMap from the stage. This is possible as the colorMap image will be contained within the bitmapData code.
Additional lines: 5, 7
Step 15: Assigning the Map to the Filter
Add the bitmapData dMap to the displacementMapFilter by setting the last parameter in the list (mapBitmap) to "dMap".
Modified lines: 19
Step 16: Add Filter to Image
The filter is complete! We now need to add it to the image, so select the image and make sure it has an instance name – lets call it "Image_mc". That done, we’re able to set the filter on the image. We do this at the end of the code by setting the Image filters parameter as an array like this:
Image_mc.filters = [dFilter]
Additional lines: 21
OK, let’s export the movie and see how the filter is affecting the image. It should look something like this:
Step 17: Interactivity
What we have so far isn’t very exciting, so let’s try to make the lens follow the mouse.
First we add the "enterFrame" loop code like this:
Additional lines: 23,25,27
var dPoint:Point = new Point(0, 0); var dMap:BitmapData = new BitmapData(colorMap_mc.width, colorMap_mc.height, true, 0x808080) dMap.draw(colorMap_mc) removeChild(colorMap_mc) var dFilter:DisplacementMapFilter = new DisplacementMapFilter (); dFilter.scaleX = 50 dFilter.scaleY = 50 dFilter.componentX = 1 dFilter.componentY = 2 dFilter.mode = "color" dFilter.color = 0x000000 dFilter.alpha = 0 dFilter.mapPoint = dPoint dFilter.mapBitmap = dMap Image_mc.filters = [dFilter] Image_mc.addEventListener(Event.ENTER_FRAME, onFrame) function onFrame(e:Event){ }Step 18: Follow the Mouse
Next we set the values of our dPoint’s X and Y to follow the mouse. Additionally, we’ll reassign the newly changed dPoint to the dFilter again and reassign the filter to the image.
Additional lines: 26,27,28,29
var dPoint:Point = new Point(0, 0); var dMap:BitmapData = new BitmapData(colorMap_mc.width, colorMap_mc.height, true, 0x808080) dMap.draw(colorMap_mc) removeChild(colorMap_mc) var dFilter:DisplacementMapFilter = new DisplacementMapFilter (); dFilter.scaleX = 50 dFilter.scaleY = 50 dFilter.componentX = 1 dFilter.componentY = 2 dFilter.mode = "color" dFilter.color = 0x000000 dFilter.alpha = 0 dFilter.mapPoint = dPoint dFilter.mapBitmap = dMap Image_mc.filters = [dFilter] Image_mc.addEventListener(Event.ENTER_FRAME, onFrame) function onFrame(e:Event){ dPoint.x = mouseX dPoint.y = mouseY dFilter.mapPoint = dPoint Image_mc.filters = [dFilter] }Lets test it again. It should look like this:
Step 19: Finalizing
It’s still not exactly how we want it, so lets make the center of the displacement follow the mouse and also add a small easing to the movement. To do that we change the following code:
dPoint.x = mouseXdPoint.y = mouseY
Modified lines: 26,27
var dPoint:Point = new Point(0, 0); var dMap:BitmapData = new BitmapData(colorMap_mc.width, colorMap_mc.height, true, 0x808080) dMap.draw(colorMap_mc) removeChild(colorMap_mc) var dFilter:DisplacementMapFilter = new DisplacementMapFilter (); dFilter.scaleX = 50 dFilter.scaleY = 50 dFilter.componentX = 1 dFilter.componentY = 2 dFilter.mode = "color" dFilter.color = 0x000000 dFilter.alpha = 0 dFilter.mapPoint = dPoint dFilter.mapBitmap = dMap Image_mc.filters = [dFilter] Image_mc.addEventListener(Event.ENTER_FRAME, onFrame) function onFrame(e:Event){ dPoint.x += ((mouseX-colorMap_mc.width/2)-dPoint.x)*0.3 dPoint.y += ((mouseY-colorMap_mc.height/2)-dPoint.y)*0.3 dFilter.mapPoint = dPoint Image_mc.filters = [dFilter] }To sum up: we subtract half the size of the map from the map position, so it centers. Then we add a basic tweening function, which can be written like this:
this += (that-this)*speedStep 20: Adding Custom Graphics
To top it off, I added a magnifying glass graphic that I prepared in photoshop. I converted it into a movieclip, gave it an instance name and made it follow the point that we use for the displacemenMapFilter.
This is achieved by setting the new lens_frame_image X and Y position equal to the dPoint position. Then subtract the offset for the graphics edge, so that it aligns perfectly with the filter effect.
Additional lines: 4,5
function onFrame(e:Event){ dPoint.x += ((mouseX-colorMap_mc.width/2)-dPoint.x)*0.3 dPoint.y += ((mouseY-colorMap_mc.height/2)-dPoint.y)*0.3 lens_mc.x = dPoint.x-8 lens_mc.y = dPoint.y-8 dFilter.mapPoint = dPoint Image_mc.filters = [dFilter] }Now our result should look like this:
Conclusion
When you have learned to create this effect yourself it shouldn’t take more than 15 minutes to set up. Remember; if you forget what the parameters for the displacementMapFilter are you can always look them up on "help". There you will get the listed order and what each parameter does.
For quick experimentation you can go to my website and look in the "flash" section under "test / labs" – I have a bunch of displacementMapFilter test environments you can try out.
I hope you can find use for this filter in your creative work!
I’m excited to reveal to you the most powerful tools in the entire GreenSock Tweening Platform. Everything I have discussed in the last four videos has been custom tailored to prepare you for the awesome power you are about to behold. I’m going to show you two methods that will change the way you approach any script-based animation from this point forward.
With
appendMultiple()andinsertMultiple()you will have precise control over the sequencing and timing of hundreds of tweens and timelines with very little code.TimelineLite in Action
In the SWF below, the parent timeline that is being controlled by the scrubber only has two lines of code, yet there are literally over a hundred tweens taking place.
You can find all the files used to create the SWF above in the source files for this tutorial.
Watch the Screencast
Don’t like ads? Download the screencast, or subscribe to Activetuts+ screencasts via iTunes!
Meet appendMultiple()
TimelineLite’s
appendMultiple()method allows you to add many tweens and/or timelines to a timeline and have precise control over the relative timing of all the tweens/timelines. What makes this method so powerful is that it accepts an array of tweens/timelines and treats them all as single group that can be offset and staggered however you like.In order to get most out of
appendMultiple()you must understand all four parameters.appendMultiple(tweens:Array, offset:Number = 0, align:String = "normal", stagger:Number = 0):Arraytweens:Array — an Array containing any or all of the following: TweenLite, TweenMax, TimelineLite, and/or TimelineMax instances
offset:Number (default = 0) — Amount of seconds (or frames for frames-based timelines) to offset the insertion point of the tweens from the end of the timeline. For example, to start appending the tweens 3 seconds after the end of the timeline (leaving a 3-second gap), set the offset to 3. Or to have the tweens appended so that the insertion point overlaps with the last 2 seconds of the timeline, set the offset to -2. The default is 0 so that the insertion point is exactly at the end of the timeline.
align:String (default = “normal”) — determines how the tweens will be aligned in relation to each other before getting appended. Options are: TweenAlign.SEQUENCE (aligns the tweens one-after-the-other in a sequence), TweenAlign.START (aligns the start times of all of the tweens, ignoring delays), and TweenAlign.NORMAL (aligns the start times of all the tweens, honoring delays). The default is NORMAL.
stagger:Number (default = 0) — staggers the tweens by a set amount of time (in seconds) (or in frames for frames-based timelines). For example, if the stagger value is 0.5 and the “align” property is set to TweenAlign.START, the second tween will start 0.5 seconds after the first one starts, then 0.5 seconds later the third one will start, etc. If the align property is TweenAlign.SEQUENCE, there would be 0.5 seconds added between each tween. Default is 0.
The above descriptions were taken directly from the GreenSock Documentation.
In the video I go over a number of ways to create the array of tweens that
appendMultiple()will accept. Below are the two most popular approaches.Create an Array of Unique Tweens
tl = new TimelineLite(); //bg tween tl.insert( TweenMax.to( bg, .5, {alpha:1}); tl.appendMultiple( [TweenLite.from( meet_mc, .5, {x:-100, rotation:-90, scaleX:0, scaleY:0, ease:Back.easeOut} ), TweenLite.from( the_mc, .5, {y:-100, rotation:-90, scaleX:0, scaleY:0, ease:Back.easeOut} ), TweenLite.from( blobs_mc, .5, {x:700, rotation:90, scaleX:0, scaleY:0, ease:Back.easeOut} ) ], 0, "sequence", .5);In the code above three tweens added via
appendMultiple()will be added immediately after the bg tween ends because the offset value is 0. Each tween will start .5 seconds after the previous tween ends because the align parameter is set to “sequence” and the stagger is set to .5.Create an Array of Similar Tweens Using allFrom()
TweenMax.allFrom()creates an array of tweens based on an array of objects. It applies the same values to tweens of all the objects in the array. You can also useTweenMax.allTo()if you want to specify where objects should tween to as opposed to where they should tween from.//create an array of objects to be tweened allBlobs = [blob1_mc, blob2_mc, blob3_mc, blob4_mc, blob5_mc, blob6_mc, blob7_mc] tl = new TimelineLite(); //use allFrom() to tween every blob FROM the same set of properties with the same duration. tl.appendMultiple( TweenMax.allFrom( allBlobs, .3, {scaleX:0, scaleY:0, x:"100"} ), 0, "normal", .1 )The code above will tween each blob for .3 seconds from the values supplied in the vars object (
{scaleX:0, scaleY:0, x:"100"}). Each tween will start .1 second after the previous tween has started because the align mode is set to “normal” and the stagger is .1.It’s important to note that
allFrom()returns an array of tweens and that is why we can use it as the value for thetweensparameter in theappendMultiple(). If you were to useallFrom()in anappend()you would get an error as it requires a single tween rather than an array of tweens.TweenAlign Visualizer
Align modes are a bit difficult to understand without seeing them in action. I built the TweenAlign visualizer to give you an easy way to test how they work with a combination of various tween delays, staggers and offsets.
Use the swf above to perform the following tests:
Test 1: Understand the difference between
TweenAlign.NORMALandTweenAlign.STARTTweenAlign.NORMALandTweenAlign.STARTNotice how the delay is ignored with
TweenAlign.STARTTest 2: See how the stagger works differently depending on align mode
Notice how stagger works differently with TweenAlign.NORMAL and TweenAlign.START:
TweenAlign.NORMAL, stagger is the amount of time between the start of each tween.TweenAlign.SEQUENCE, stagger is the amount of time between the end of a tween and the next tween.Go crazy combining different settings and keep an eye on how they effect the start time of each tween and the overall duration of the timeline. The TweenAlign Visualizer is also available in the source files for this tutorial.
Additional Notes
Although I reference
appendMultiple()quite a bit in this text,insertMultiple()works virtually the same way. The only difference withinsertMultiple()is that the offset parameter refers to an exact time or label at which the tweens/timelines should be added. Read more in the TimelineLite Documentation.When specifying the align mode you can reference the static contsant value in the TweenAlign class or the string value.
TweenAlign.NORMAL is the same as using “normal”.
Conclusion
Once you get a handle on how
appendMultiple()andinsertMultiple()work you basically have limitless potential for creating elaborate and flexible animations with very little code. I truly hope the demonstration of nesting multiple TimelineLites inspires you to create some truly wonderful effects. By adding just a touch of randomness to each timeline you can really start to take your animations to the next level and maintain an insane amount of control.Perhaps now you have the knowledge and fortitude to attempt the “starburst” effect that was featured in the Introduction to TimelineLite video? Come to think of it, I’m assigning that challenge to you as homework.
In the next tutorial I am going to be showing some features exclusive to TimelineMax and some other tricks you can do with all your timelines.
If you have any questions or comments on this tutorial simply post a comment below.
Thanks for watching!
Are you an RIA developer? Do you make Flash games or Unity games? Are you always on top of the latest industry news? You sound like the perfect person to contribute to Activetuts+!
We’re on the lookout for new authors, so read on to find out more…
What We’re Looking For
Right now, we’re putting out a general call for bloggers.
Okay, technically everything on Activetuts+ could be considered a blog post. What we mean, specifically, is the more chatty, conversational posts; articles that aren’t focused on building a specific type of app, or implementing a certain effect.
The best way to illustrate this is through a few examples from other Tuts+ sites. Some articles clear up misconceptions, offer advice, or discuss a pertinent issue:
Other posts help readers discover useful and inspirational content that’s relevant to their niche:
Payment will depend on the length, depth, and subject of the article, but you can expect to earn from $50 to $150 per post.
Pitch a Blog Post
Got an idea? Great! Pitch it to us (in about a paragraph or so) using the following form:
Pitch your Quick Tip idea
As long as you’ve got an original, useful idea and can string a decent English sentence together, there’s a high chance your pitch will be accepted, so don’t be shy.
No specific ideas? That’s okay, we’re still interested – just submit some relevant examples of previous writing you’ve done, whether it’s on your own blog, on forums, or even on Google+.
Of course, this is also open to existing Activetuts+ writers, past and present – you know how to get in touch.
We’re looking forward to reading your pitches!
In AS3 projects, you often need to load external assets like images and other SWFs. While loading these assets you have to keep track of several events for successful execution of your application. You might have been using some of these events like
ProgressEvent,COMPLETEevent. This tutorial shows you how to get info about the assets you’re loading withLoader.load(), using its associated events, all in one place. So if you’re having difficulty loading such assets, or you have less time in hand to debug, take a look at this.(Preview image courtesy of VisualPharm.)
Final Result Preview
Let’s take a look at the final result we will be working towards:
This SWF loads an image, showing details about the loading process (progress, whether it’s complete, and more).
It won’t work embedded in this page (which shows you the responses in case of error) but it will work if you load the SWF directly.
Step 1:The need to load assets
Hey, if you are in a hurry, you can skip this step. But, if you are relaxed, with a cup of coffee in hand, have a read.
In the early days when I started learning Flash 5 and AS1, I used to have all objects on the stage at once, and made them visible/invisible as required. Eventually as I started developing apps, I became more familiar with Flash’s system. Wow, now I was able to use “linkage identifier” to load objects from library. Getting more advanced, I soon realised the need for external assets. But unfortunately, there was no such powerful architecture. Meanwhile, Flash was going through drastic changes. Now “AS2″ was capable of loading external assets. Finally, when it lined up in Adobe’s family, the new event-based architecture “Action Script 3.0″ was introduced. In AS3, the need for loading external assets is much better addressed than in AS2.
So, when working with Flash we have three ways to manage our assets:
The first method is not versatile. As the number of objects increases, the size of the SWF also increases (especially when there are lots of images). Imagine how much time it can take to load on user’s machine? Also, the performance of your application might degrade. But, still it can be handy enough for creating lightweight apps. (E.g. developing a simple game, in which player needs to find out key objects from the given image).
The second method is very much similar to the first one. Instead of having objects on the stage, now they are residing only in the library. Again, if there are lots of objects, then the issues with the SWF’s size and performance remain. But with this approach, you can easily create multiple instances of objects. Plus, the objects are much easier to handle than the first method. (E.g. games like “Tic Tac Toe”, “Tetris”, and “Snake” can be developed as they require fewer objects).
The third method (loading assets externally) is much more flexible than the first two. The power of this method is, without having a single object in the library you can still develop big games (e.g. “RPG” style games). As objects are loaded externally, they are loosely tied to your main SWF, so you do not have to go back to Flash every time you want to replace or edit your assets. One more advantage of this method is, it allows you to load and display objects on an “On Demand” basis. Meaning, you can load your objects only when they are required and unload them when you are completely done. (E.g. if your game has 10 levels, then you do not need to load all assets needed for all 10 levels. Instead, load and unload them level by level.) Thus, the overall performance of the application is improved.
Note: The above “On Demand” method may not be suitable in case where assets are loaded and unloaded frequently (e.g. in an image viewer app).
Okay, so now we can conclude that, of the three methods, the third one is more flexible. But, as the system gets more advanced, it becomes more complicated to manage. Similarly, if you decide to go with third method, you will need to handle assets as one separate operation. You may call it “Asset Management” which can be divided into two parts:
This tutorial covers the second part, so let’s look at that now.
As our program loads assets externally, it is now dependent on those assets. If any of our assets fail to load for some reason, then our entire application will be hampered. To avoid such failures, we really have to provide “Decision Making” power to our program.
Here’s one example. You have a coin-flipping game, where the coin is an externally loaded image. Now, if this image fails to load, then what next? This is the perfect point where your program will take some decision(s). It might load another image for the coin, stored as a proxy. It might load the stand-by coin if you have placed it in the Flash library. This can be a lightweight coin made up of vector graphics and strokes converted to fill to keep the size of the main SWF as small as possible. If you do not plan to have stand-by coin, then your program could warn the user about failure.
Of course, this was just one case. For a complete game, you will need to use a mature “Asset Management” system.
Step 2: Which Type of Loader?
In an AS3 project, assets can be any of the following,
These assets have their own methods of loading as they are different in nature. You cannot see the sound, and you cannot hear images, so it is quite acceptable to have different techniques for these different types.
Hence, you cannot load sounds the way you load images and vice versa. You have to pick the right method for the selected assets.
The following are the appropriate ways of loading these assets :
load()” method of “Loader” class is used to load images and SWFs. Our tutorial is dedicated to this section.load()” method of “Sound” class is used to load external MP3 sound files.FLV video format or a video format that uses standard H.264 encoding is supported by AS3.
Video without FLV component can be loaded and streamed using classes like “
NetConnection” and “NetStream“.Video with FLV component can be loaded and streamed using “
source” method asmyFLVComponent.source("MyVideo.flv").To load such type of data, you have to use the
URLLoaderclass, which loads data from the specified URL. Any type of data can be loaded but in the form of text or raw binary data or encoded variables.e.g. You can also load an image in the form of raw binary data and further use it for processing with “
ByteArray” class.So, let us end the theoretical part here and move towards the practical stuff. Now we have enough knowledge about assets. In the rest of the tutorial, we will be dealing with the
load()method (of “Loader” class) and its associated events, to monitor the loading process of an image and a SWF.Step 3: Events for Monitoring the Loading Process
Before moving towards Loader class, first let us look at several events that are needed to monitor the loading operation:
Our first task will be to check the availability of the specified target (i.e the “loadee”).
Sometimes, the target is not found due to incorrect file names or perhaps because the file itself no longer exists at the location. Even, in some cases, assets may not be readable. If everything is okay at this level, then we can jump to monitoring the loading process.
As soon as the loading is triggered, this event is dispatched. Here, we can initialize a preloader (e.g. set up the Progress indicator), before the PROGRESS event is dispatched. This OPEN event is the first thing to be dispatched in a (successful) loading operation.
This event depends on the loading environment. Meaning, if you are loading images remotely (say, over the Internet, rather than from the user’s hard drive) and if the connection is okay but the file is not found, even now this event will be dispatched. So, be careful if you are initializing a preloader here – or try to make your program very strict in handling an
IO_Errorevent.We have got the target and now the loading is in progress. But we can’t just stay quiet and wait for the progress event to finish, because it takes some time to load the image (or SWF), especially when the image is heavy or being transferred online. So, to keep the user engaged, we must keep informing him about the state of the load. Now, what to inform?
While is in progress, we can display the message saying “Please wait. File is loading”. We can also display the “current bytes loaded” on the screen using information from the
PROGRESSevent.We can do such tasks while this event is processing. Hence, it is at second position.
This event is best suited for downloading SWF files from the Internet.
If you are loading SWF from local machine itself and the SWF is lightweight, then it is almost identically timed to the COMPLETE event. Many a time you will find no difference between INIT and COMPLETE events, because within no time, you are in a state of COMPLETE, immediately leaving INIT state.
But the situation changes when you are loading the SWF(s) from internet. In addition, if the SWF (having animations or lots of objects) is the target, then it will take more time to download. In this case the INIT becomes handy.
As soon as the SWF header is readable, this event is dispatched and the document class for the main SWF is instantiated. The first frame is available and you can do some preloading actions or you can define some properties while the rest of the part is still downloading.
This event is the third to fire in a successful load.
When loading assets over the Internet (using an HTTP request), the HTTP status code (e.g. 200) is returned if the connection is OK. Otherwise, 0 is returned.
If the connection is okay, but the file is not found, then 404 is returned.
For more information on HTTP Status Messages, follow the link below:
HTTP Status Messages
Although this event checks the status of the connection to the remote server, it is trigerred even in case of loading assets locally – but in this case it always returns 0. This event fires fourth.
As the name suggest, this event will be dispatched when an image (or SWF) is completely loaded on the user’s machine. At this point, the image or SWF is completely available for further process. Hence, this event is at last place in the loading operation.
There are also some other events like so:
This error occurs when a SWF, running asynchronous code (like AS2), is loaded.
This error occurs when Flash Player’s security model is violated during a loading process.
For the latest info on the security model of Flash Player, take a look at Flash Player Security.
For more info on
SecurityErrorEvent.SECURITY_ERRORspecifically, here are some links to follow:Although we can skip some of these events – like “OPEN”, “HTTPStatus”, “INIT” – when loading assets locally, tracking all of them ensures a high level of monitoring a loading operation.
Step 4: Preparing the Document Class
After knowing all necessary event types, now its time for loading assets.
First, we shall create a basic structure of our document class.
For detailed explanation about the document class check out this Quick Tip.
Download all source files required for this assignment by clicking the “Download Source Files” button available at the top of the page.
The folder contains “LoaderClassExample.fla”, “MyImage.png”.
Also, there is “MyLoader_Complete.as” file, from the end of the tutorial. We are going to build this class, step by step, for better understanding. At the end of this tutorial, we will have our own version of this class.
Open Flash and create new “ActionScript 3.0 Class” file and name it “MyLoader”.
You will have the basic structure of the class like so:
package { public class MyLoader { public function MyLoader() { // constructor code } } }Save this class file as “MyLoader.as” in the same downloaded folder.
Then open the downloaded “LoaderClassExample.fla” file. Don’t worry about the objects on the stage. We shall see their roles in coming steps.
So, first we must change the document class from “MyLoader_Complete” to “MyLoader”, in the Property Inspector of the FLA.
Now, the Class name must read like so:
Step 5: Loading an Image
To start with, we will load the image “MyImage.png” placed in the downloaded folder.
The basic requirements can be listed down, like so:
So, to fulfill above requirements, modify the “MyLoader.as” like so:
package { import flash.display.Sprite; import flash.display.Loader; import flash.events.MouseEvent; import flash.net.URLRequest; public class MyLoader extends Sprite { private var myURL:String; private var loader:Loader; public function MyLoader() { myURL = "MyImage.png"; loader = new Loader(); addChildAt(loader,0); loadButton.addEventListener(MouseEvent.CLICK, loadAsset, false, 0, true); } private function loadAsset(evt:MouseEvent):void { loader.load(new URLRequest(myURL)); removeChild(loadButton); loadButton.removeEventListener(MouseEvent.CLICK, loadAsset); } } }Test the movie and click the “Start Loading” button. The image is loaded and displayed.
Since, the target was an image, we needed
LoaderClass to create Loader Object.Hence, in our code we put the lines like so:
Above line made the
Loaderclass available to our program.Above line declared the
loadervar to storeLoaderobject.Above line created the
Loaderobject and assigned it to aloadervar.Now, we can access
load()method ofLoaderclass.In our code, we accessed
load()method like so:The
load()method is most crucial part of our code.We passed only one parameter as
URLRequestto this method. Actually it can take two parameters. The second parameter isLoaderContextobject. TheURLRequestis required whileLoaderContextis optional parameter.For example,
loader.load( URLRequest, LoaderContext);Let’s look at brief info about
URLRequestandLoaderContextin next couple of steps.Step 6: URLRequest and Paths
In our code, we created the instance of
URLRequestobject like so:We used a shorthand method for creating an instance of
URLRequest, which is okay for our example but not a good practice in general. It should not be used for big projects (e.g. a framework). The problem is that it leaves no reference to the URLRequest that we can use again later.Instead, use this approach:
var urlRequest:URLRequest = new URLRequest();urlRequest.url = myURL;loader.load(urlRequest);Now you can refer to
urlRequestany time you want.Now, let us learn about
URLRequest, absolute paths, and relative paths.URLRequest: This object gives us the ability to address the target by specifying a path.
This path can be one of two types:
Here are some examples of Absolute Path:
http://SomeDomain/assets/MyImage.pngD://MyProject/Assets/MyImage.pngAbsolute Path is used mostly to download assets from other domains over the Internet. Being hard-coded, these paths do not give a flexible approach. Hence, for portable projects (especially for desktop apps) do not plan to use such paths.
It uses standard URI schemes like so:
Check this link for more info on Http and Https.
Relative Path can be divided into two parts like so:
MyImage.png(we used this path in the above code as myURL.)Assets/MyImage.png../Assets/MyImage.png../../Assets/MyImage.pngUse case of above two paths depends on your project. But, in most cases you will end up using “Document Relative” path instead of “Root Relative” path.
Document Relative path is illustrated like so:
(The SWF is in a folder parallel to the Assets folder.)
Root Relative path is illustrated like so:
Step 7: Loader Context
We did not used this parameter in our example, but having information about it will add the value.
Loader Context: This optional parameter gives access to the properties like so:
Let us see some brief info about these properties.
loaderContext.securityDomain = SecurityDomain.currentDomain;This forces the loadee SWF (from other domain) to be placed into the loader SWF’s domain. By default the loadee SWF is placed in its own security domain (due to Flash Player’s security model).
Also, a policy file (e.g. crossdomain.xml) must be available on the loadee SWF’s server in order to established the trust between different servers. Thus, ensuring the interoperability between SWFs.
When loading images, this property is not required.
Once, everything is in your own security domain, then you can specify
loaderContext.applicationDomain.e.g.
loaderContext.applicationDomain = ApplicationDomain.currentDomain;It allows cross scripting between SWFs. e.g. The loadee SWF can access the class associated with parent SWF, when set as
ApplicationDomain.currentDomain.This property is not needed while loading images.
Check this example which allows the access to class definition of loadee SWF.
ApplicationDomain.currentDomain.getDefinition("com.SomeFolder.SomeAS3Class");ApplicationDomain.currentDomain.getDefinition("SomeLibrarySymbol") as Class;(Note: Be updated with Adobe’s policy about accessing external scripts. It is related to security model. Adobe is constantly improving Security model. So keep track of Adobe’s documentation.)
Read the following links for more info on
Application Domain:Application Domain
Loader Context Application Domain
This property can be used for both (SWF and an image).
e.g.
loaderContext.checkPolicyFile = true;When this property is set to true, then it always checks for policy file (crossdomain.xml) on server side. If it is not found, a security sandbox error is thrown.
Set this property to true only when you want to do more actions than just displaying that content on the screen. Otherwise keep this property as a false.
e.g. When you are loading an image from the secure server and if you want to use
BitmapData.drawmethod on that loaded content, then set this property to true.Following are some more links related to Flash Player Security:
Security sandboxes
Permission controls
In this step we completed a very simple task of loading an image. But while loading we did not track the information about loading. We just watched and waited to have the image on the screen. In the next step we will see what is needed to monitor the loading progress of an image.
Step 8: loaderInfo and contentLoaderInfo
Friends, in the above step we loaded an image without tracking the loading information. This time we will start monitoring the loading process of this image. For that we will make use of various events we discussed in Step 3.
But we still don’t know how to associate these events with Loader class. So, let us explore it.
The
Loaderclass comes with a very important property:contentLoaderInfo.This property is needed to associate the loading events with the
load()method ofLoaderclass.The following is one example, which tracks the
COMPLETEevent of loading process.loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadComplete);When the target image is completely loaded, the “loadComplete” method is called. This method can have your after-loading logic, e.g. adding that image to the stage.
We are definitely going to use this property very soon. But, before that, let us see what exactly it does with
Loaderclass.The
Loaderclass does not have any information about loading progress. In fact, this information is retrieved from thecontentLoaderInfowhich further utilizes theLoaderInfoobject.What is loaderInfo? What is contentLoaderInfo?
To understand these terms, we shall experiment with our “MyLoader.as” Class.
This time we shall actually use
COMPLETEevent. So, let us add our new functionloadCompleteand some more lines to the code like so:The above line makes the
Eventclass available to our program. Thus we can useCOMPLETEevent.private function loadComplete(evt:Event):void { trace("Load Complete"); trace(evt.target); }This
loadCompletefunction will be called when the target image is completely loaded. It will trace “Load Complete” to make sure that this function is invoked. It will also trace the type of the target (Loader).Now, to associate this
COMPLETEevent with the Loader, we shall modify the constructor method in our code like so:public function MyLoader() { myURL = "MyImage.png"; loader = new Loader(); addChildAt(loader,0); loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadComplete); loadButton.addEventListener(MouseEvent.CLICK, loadAsset, false, 0, true); }Test the movie and press the “Start Loading” button.
The output window will show the results, like so:
Load Complete[object LoaderInfo]It returns
LoaderInfoobject as a target. So, we can say that,contentLoaderInfoutilizes theLoaderInfoobject.What is loaderInfo?
So, let us experiment more.
Modify the
loadCompletemethod like so:private function loadComplete(evt:Event):void { trace("contentLoaderInfo:" + loader.contentLoaderInfo.height); trace("loaderInfo:" + loader.loaderInfo.height); }Test the movie and press “Start Loading” button.
The output window will show the result like so:
contentLoaderInfo:600loaderInfo:640Here,
contentLoaderInforeturned the height (600) of the target image.And,
loaderInforeturned the height (640) of the main (root) SWF.If you want to experiment more, replace “height” property with “url”
This will make
loaderInfogive the information about root SWF andcontentLoaderInfogive the information about the content being loaded by the Loader object. But both utilize theLoaderInfoobject.OK, now we know how to associate the loading events using
contentLoaderInfowith loader object. Next we shall start exploring all those events one by one in the proper sequence.Step 9: Which Events to Use?
We already have discussed the required events in Step 3. But, I am listing them again, just for a revision.
In the previous step, we used
Event.COMPLETE. But remember, this is the last event to be fired, so we must track other events beforeEvent.COMPLETE.Also, while loading an image there is no need to track all the above events.
Loading an image (locally) will require just these events:
If image is loaded remotely (via HTTP request) add
HTTPStatusEvent.HTTP_STATUSto the above list.But, for our example, we shall track all above events for the sake of learning. Also, you can any time change the URL to load a SWF – because, most of the time, tracking the loading process of a SWF requires all these events.
So, let us start with
IOErrorEvent.IO_ERROR.Step 10: IOErrorEvent.IO_ERROR
To track
IOErrorEvent.IO_ERROR, first add an import statement like so:We shall add a new method
loadErrorlike so:private function loadError(evt:IOErrorEvent):void { monitor.errorEventTF.text = "URL: '" + myURL + "' not found"; }In case the target image is not found, then
IOErrorEventwill be triggered. The aboveloadErrormethod will be invoked. This method will display a message about the URL in the dynamic textfield named “errorEventTF” which is inside the “monitor” MovieClip, already placed on the stage.We shall also modify the constructor method to associate it with the
loaderobject.public function MyLoader() { myURL = "MyImage.png"; loader = new Loader(); addChildAt(loader,0); loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, loadError); loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadComplete); loadButton.addEventListener(MouseEvent.CLICK, loadAsset, false, 0, true); }To test the
IOErrorstatus, put an incorrect url (e.g.myURL = "MyImage1.png").Test the movie and click “Start Loading” button to see the status of
IOErrorevent. The monitor is displaying the incorrect URL.Step 11: Event.OPEN
To track
Event.OPEN, we shall add a new methodloadStart()like so:private function loadStart(evt:Event):void { monitor.openEventTF.text = String("Path opened"); }We shall also modify the
constructormethod to associate it with theloaderobject.public function MyLoader() { myURL = "MyImage.png"; loader = new Loader(); addChildAt(loader,0); loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, loadError); loader.contentLoaderInfo.addEventListener(Event.OPEN, loadStart); loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadComplete); loadButton.addEventListener(MouseEvent.CLICK, loadAsset, false, 0, true); }Test the movie and click “Start Loading” button to see the status of
OPENevent.Step 12: ProgressEvent.PROGRESS
We shall track loading progress of target image by updating the total number of bytes currently loaded.
To track
ProgressEvent.PROGRESSevent, first we shall add a new variabletotBytesLoadedlike so:Then, add import statement for
ProgressEventClass like so:Now, we shall add a new method
loadProgresslike so:private function loadProgress(evt:ProgressEvent):void { totBytesLoaded = Math.ceil(evt.bytesLoaded / 1024); monitor.progressEventTF.text = "Bytes loaded :" + String(totBytesLoaded) + "kb"; }We shall also modify the
constructormethod to associate it with theloaderobject.public function MyLoader() { myURL = "MyImage.png"; loader = new Loader(); addChildAt(loader,0); loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, loadError); loader.contentLoaderInfo.addEventListener(Event.OPEN, loadStart); loader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, loadProgress); loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadComplete); loadButton.addEventListener(MouseEvent.CLICK, loadAsset, false, 0, true); }Test the movie and click “Start Loading” button to see the status of
PROGRESSevent.You might not see any visible load progress, since we are loading the target image locally. But, if it is going to be loaded remotely then you will have a visible loading progress.
You can still check the same local image, as if it is being loaded remotely. But, How?.
In Flash IDE test player, there is an option “Simulate Download”. This option must be checked to see how the download progress will look like for the same local image. This is just a simulation. It will give you only a rough idea about progress.
You can also simulate various connection speeds under “Download Settings”, available below “Simulate Download”.
Once “Simulate Download” is checked, click the “Start Loading” button. Now you will have the visible loading progress. Observe the “Progress” reading in our monitor window.
You can also add a progress bar for visual appearance of a loading progress.
I have not added a progress bar, but if you wish to add it, then here are some lines to add a progress bar.
Before coding for progress bar, drag the ProgressBar component from the Component panel to the Library to make it available for us.
After that, add import statement for ProgressBar like so:
import fl.controls.ProgressBar;Then, declare a var to hold progress bar like so:
var myProgressBar:ProgressBar;Now, instantiate it and specify a source as
contentLoaderInfoof aloaderobject like so:myProgressBar = new ProgressBar();myProgressBar.source = loader.contentLoaderInfo;myProgressBar.x = stage.stageWidth/2 - myProgressBar.width/2;myProgressBar.y = stage.stageHeight/2 - myProgressBar.height/2;addChild(myProgressBar);You can add these lines inside
constructormethod or you can create your own method and call it inside aconstructormethod.Do not forget to remove this progress bar inside
loadCompletemethod like so:removeChild(myProgressBar);Step 13: Event.INIT
To track
Event.INITevent, we shall add a new methodinitialised()like so:private function initialised(evt:Event):void { monitor.initEventTF.text = String("Initialized"); }We shall also modify the
constructormethod to associate it withloaderobject.public function MyLoader() { myURL = "MyImage.png"; loader = new Loader(); addChildAt(loader,0); loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, loadError); loader.contentLoaderInfo.addEventListener(Event.OPEN, loadStart); loader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, loadProgress); loader.contentLoaderInfo.addEventListener(Event.INIT, initialised); loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadComplete); loadButton.addEventListener(MouseEvent.CLICK, loadAsset, false, 0, true); }Test the movie and click “Start Loading” button to see the status of
INITevent.Step 14: HTTPStatusEvent.HTTP_STATUS
This
HTTPStatusEventis not required for loading images locally. We can simply replace the local path with a remote URL to check the status of this event.For the sake of completeness, we shall associate this event, even for loading images locally (in which case it will return “0″ as a status code). Check Step 3 for more info on this event.
To associate
HTTPStatusEvent, add the import statement like so:Add a new line inside
constructormethod like so:Now, put the new function
httpStatuslike so:private function httpStatus(evt:HTTPStatusEvent):void { monitor.httpEventTF.text = String(evt.status); }Test the movie and click on “Start Loading” button. The status will show “0″ since the target is local.
Step 15: Event.COMPLETE
Finally, it’s time for
Event.COMPLETEevent. We already have added thisCOMPLETEevent in Step 8, while learningLoaderInfoandcontentLoaderInfo.This is our destination. Treat this as an entry point for the code after loading is complete. Here you can start further processing.
Important tasks can be done here are listed down,
ENTER_FRAMEevent).Here, we shall modify the
loadComplete()method by adding a line,addChildAt(loader,0);We already have added this line in a
constructormethod. But, instead of adding it in aconstructormethod, we must add it after the loading process is completed. This is a good practice.So, we shall modify the
constructormethod, by removing the statementaddChildAt(loader,0);…to add the loaded image to the bottom of the display list.
The modified
constructorwill look like so:public function MyLoader() { myURL = "MyImage.png"; loader = new Loader(); loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, loadError); loader.contentLoaderInfo.addEventListener(Event.OPEN, loadStart); loader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, loadProgress); loader.contentLoaderInfo.addEventListener(Event.INIT, initialised); loader.contentLoaderInfo.addEventListener(HTTPStatusEvent.HTTP_STATUS, httpStatus); loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadComplete); loadButton.addEventListener(MouseEvent.CLICK, loadAsset, false, 0, true); }Now, modify
loadCompletemethod like so:private function loadComplete(evt:Event):void { monitor.completeEventTF.text = String("Load Complete"); addChildAt(loader,0); }Test the movie and click on “Start Loading” button to see the status of
COMPLETEevent.Thus, we have the complete setup for monitoring the loading process of an image as well as SWF.
Also, you can simply replace the address path to load an image or SWF from any accessible remote server.
Step 16: Loading From Internet
Now, we shall see how to load an image from internet. Of course you must have a working Internet connection.
First, we shall change our
myURLlike so:Test the movie and click on “Start Loading” button. It will take some time to load.
Since, being “Test Player”, it allowed to load the target image over internet. But, if you run “SWF” outside Flash IDE, the Flash Player Security dialog box will tell you to go to Settings.
To prevent from this warning, we have two ways around it:
1. Set “Access network only” for Local playback security under Publish Settings.
2. If you want to keep “Access local files only” for Local playback security under Publish Settings and still want to load a target from internet, then, you need to register that SWF as “Trusted” file. To make it “Trusted“, run SWF outside Flash. Right click inside player and click on Global Settings from the context menu.
Then go to the “Advanced” tab and click “Trusted Location Settings”.
Click “Add” under “Trusted_Location_Settings”.
Now, under “Add Site”, click “Add File” and browse for this same SWF (i.e LoaderClassExample.swf). After specifying the path for this SWF, click on “Confirm” button.
Now close all boxes and re-open SWF. Click on “Start Loading” button and the loading is started successfully.
If suitable, you can also create “Projector.exe” to avoid all above settings. Projectors are always registered as trusted files.
So Why Doesn’t the Example Load?
Editor’s note:
You may be wondering why the example SWF (at the top of the page) doesn’t load the image when embedded in the page, but does load it when loaded in the browser directly.
This is because we host our SWFs and images on a different domain to the tutorial; they’re on s3.com rather than tutsplus.com. When the SWF tries to load the image, it’s looking for it at
http://active.tutsplus.com/tutorials/actionscript/MyImage.png, because it’s using a relative path to this page.However, the image is actually somewhere on s3.com, which means the SWF can’t find it. To get around this, we can use the SWF’s own
loaderInfo.urlproperty to find the URL of the actual SWF, and use this to construct a path to the image.(Alternatively, we could hard-code in an absolute path to the image, but that’s pretty inflexible.)
For more info, take a look at Step 21 onwards of this tutorial.
Conclusion:
Friends, that’s it for now. In this tutorial we saw how to load assets (in our case, an image) and monitored the loading process. We saw various events along with
contentLoaderInfo,loaderInfo, absolute/relative paths andLoaderContext. We also experimented with loading an image from internet.I hope you found this information helpful. Please add comments to improve it more.
So far, we’ve got a never-ending stream of enemies that our avatar must avoid; one touch, and it’s game over. But so what? Since there’s no way to track the player’s progress, they have no idea whether they did better in their latest round than they ever did before. In this tutorial, you’ll learn how to keep score, how to display it on the canvas, and how to let the player know when they’ve beaten their own records.
Refresher
In the first and second parts of this tutorial, we’ve covered a number of concepts: drawing images to the canvas, detecting mouse actions, using
ifandwhilestatements, storing variables in arrays, and the idea of variable scope.You can download the source files from the series up to this point if you’d like to dive straight in here, though I recommend reading all parts in order.
Our game’s JavaScript file initialises a bunch of variables (including two arrays to store enemy x- and y-coordinates) outside of all functions, so that their contents are available to all functions. It also contains a function called
setUpGame()which is run when the player first clicks the canvas; this loads the images, starts listening for any mouse movements, and sets up a “tick” function to be run every 25 milliseconds.When the mouse is moved, we move the position of the avatar, as stored in two variables – one for the x-coordinate and one for the y-coordinate – but we don’t immediately redraw the avatar’s image at this new location; all redrawing is handled in the tick function.
The tick function does four things:
All clear?
Warm Up Challenge
If it’s been a while since you read the second part of the series (or if you just want to check that you understand what’s going on), have a go at these little exercises. They’re completely optional and separate to the actual tutorial, so I recommend working on a copy of your project rather than the original. You can complete all of these exercises using only information from the series so far.
Easy
Swap the avatar and enemy images around so that the player controls a smiley face that’s avoiding the falling skulls.
(How many different ways can you figure out how to do this? I can think of three, off the top of my head.)
Medium
At the moment, there’s a flat one-in-twenty chance that a new enemy will be created in any given tick. I want you to make it so that there’s a one-in-one chance on the first tick, a one-in-two chance on the second, a one-in-three chance on the third, and so on.
To make this more challenging, do it in reverse: a one-in-1,000 chance on the first tick, a one-in-999 chance on the second, a one-in-998 chance on the third, and so on. (After the thousandth tick, make it a steady one-in-one chance.)
Hard
Rather than waiting for the enemies to appear one by one, have the game start with twenty enemies already on screen.
To make this more challenging, make them spread out around the canvas, and don’t allow them to overlap the avatar or each other.
Keeping Time
What’s the simplest way to measure how well the player is doing this round? The easiest thing I can think of is to keep track of how long it’s been since they hit an enemy. And since hitting an enemy means game over, we only need to keep track of how long they’ve been playing.
To do this, we’ll just create a variable, set it to 0 when the round starts, and increment it every tick. Let’s call this variable
ticksSurvived. And think: since we need to access it over and over again, it needs to be defined outside of all the functions, at the top of the JS file:Now, we’ll give
handleTick()yet another task to do: incrementticksSurvived. Put this after the collision detection; after all, if the avatar hits an enemy, it hasn’t actually survived the tick:function handleTick() { //... currentEnemyNumber = 0; while (currentEnemyNumber < numberOfEnemies) { if ( ( (avatarX < enemyXPositions[currentEnemyNumber] && enemyXPositions[currentEnemyNumber] < avatarX + 30) || (enemyXPositions[currentEnemyNumber] < avatarX && avatarX < enemyXPositions[currentEnemyNumber] + 30) ) && ( (avatarY < enemyYPositions[currentEnemyNumber] && enemyYPositions[currentEnemyNumber] < avatarY + 33) || (enemyYPositions[currentEnemyNumber] < avatarY && avatarY < enemyYPositions[currentEnemyNumber] + 30) ) ) { alert("You hit an enemy!"); } currentEnemyNumber = currentEnemyNumber + 1; } ticksSurvived = ticksSurvived + 1; }To display this, for now, we’ll just alter the alert that appears when the avatar hits an enemy:
function handleTick() { //... currentEnemyNumber = 0; while (currentEnemyNumber < numberOfEnemies) { if ( ( (avatarX < enemyXPositions[currentEnemyNumber] && enemyXPositions[currentEnemyNumber] < avatarX + 30) || (enemyXPositions[currentEnemyNumber] < avatarX && avatarX < enemyXPositions[currentEnemyNumber] + 30) ) && ( (avatarY < enemyYPositions[currentEnemyNumber] && enemyYPositions[currentEnemyNumber] < avatarY + 33) || (enemyYPositions[currentEnemyNumber] < avatarY && avatarY < enemyYPositions[currentEnemyNumber] + 30) ) ) { alert("You hit an enemy! Ticks survived: " + ticksSurvived); } currentEnemyNumber = currentEnemyNumber + 1; } ticksSurvived = ticksSurvived + 1; }That’s a bit weird; we’ve used the
+operator to add a string to a number. We can add another string to the end:function handleTick() { //... currentEnemyNumber = 0; while (currentEnemyNumber < numberOfEnemies) { if ( ( (avatarX < enemyXPositions[currentEnemyNumber] && enemyXPositions[currentEnemyNumber] < avatarX + 30) || (enemyXPositions[currentEnemyNumber] < avatarX && avatarX < enemyXPositions[currentEnemyNumber] + 30) ) && ( (avatarY < enemyYPositions[currentEnemyNumber] && enemyYPositions[currentEnemyNumber] < avatarY + 33) || (enemyYPositions[currentEnemyNumber] < avatarY && avatarY < enemyYPositions[currentEnemyNumber] + 30) ) ) { alert("You hit an enemy! You survived " + ticksSurvived + " ticks."); } currentEnemyNumber = currentEnemyNumber + 1; } ticksSurvived = ticksSurvived + 1; }Maybe this doesn’t seem odd to you, but some programming languages hate this. JavaScript doesn’t care. It knows that
"some" + "string"equals"somestring", and assumes that you want to treatticksSurvivedas a string in this situation.(Some things to try out:
12 + 6"12" + "6""12" + 612 + "6"Do they all do what you expect them to do?)
Anyway, let’s give this new code a try.
Click to try it out.
Great!
As before, when you click OK, the alert box appears again, because all it does is pause the ticks rather than actually stopping them. But note that the number in the box increases by one; this is proof that JavaScript doesn’t turn the number into a string permanently; it only uses it as a string for the purposes of adding it to another string.
While we’re on the subject of this alert box: don’t you find it annoying?
Try Again
At the moment, the only way to start a new round is to refresh the page. Let’s make it easier and less irritating to have another go, by making the “OK” button on the alert box reset the game.
Since the alert effectively pauses the game, whatever we put on the line after the alert will run once the player hits OK. Let’s make it call a new function,
startNewGame():currentEnemyNumber = 0; while (currentEnemyNumber < numberOfEnemies) { if ( ( (avatarX < enemyXPositions[currentEnemyNumber] && enemyXPositions[currentEnemyNumber] < avatarX + 30) || (enemyXPositions[currentEnemyNumber] < avatarX && avatarX < enemyXPositions[currentEnemyNumber] + 30) ) && ( (avatarY < enemyYPositions[currentEnemyNumber] && enemyYPositions[currentEnemyNumber] < avatarY + 33) || (enemyYPositions[currentEnemyNumber] < avatarY && avatarY < enemyYPositions[currentEnemyNumber] + 30) ) ) { alert("You hit an enemy! You survived " + ticksSurvived + " ticks."); startNewGame(); } currentEnemyNumber = currentEnemyNumber + 1; }So what do we need
startNewGame()to do?Perhaps we should just call
setUpGame()again – but no, nothing in that needs doing twice: we don’t need to load the images or add the mouse event listener or set up the ticks again.Have a think about what would be needed, and try out your ideas. My solution is below; take a look when you’re ready.
function startNewGame() { enemyXPositions = []; enemyYPositions = []; ticksSurvived = 0; }That’s all. You can do more if you wish, but this is the minimum required.
Note that we don’t have to do anything with the canvas – we don’t need to clear it, or draw anything to it, or manipulate the images – because this is all done in
handleTick(). We don’t have to go through all the elements in the arrays, one by one, either, because the arrays are simply a list of coordinates that we use to stamp the enemy images onto the canvas; the enemies don’t exist as actual objects.Click to try it out.
So this is great – the player can play the game over and over again to keep trying for a better score. Except… how do they know whether they’ve beaten their previous score? At the moment, they just have to remember they top score so far, or write it down. We can do better than that.
Remember the Best Score
How should we store the top score? In another variable, of course!
Naturally, it has to live outside of the functions; by now, I’m sure you understand why.
To begin with, we set it to 0, of course – the player hasn’t even completed a round. When the avatar hits an enemy, let’s update the new score as required:
while (currentEnemyNumber < numberOfEnemies) { if ( ( (avatarX < enemyXPositions[currentEnemyNumber] && enemyXPositions[currentEnemyNumber] < avatarX + 30) || (enemyXPositions[currentEnemyNumber] < avatarX && avatarX < enemyXPositions[currentEnemyNumber] + 30) ) && ( (avatarY < enemyYPositions[currentEnemyNumber] && enemyYPositions[currentEnemyNumber] < avatarY + 33) || (enemyYPositions[currentEnemyNumber] < avatarY && avatarY < enemyYPositions[currentEnemyNumber] + 30) ) ) { alert("You hit an enemy! You survived " + ticksSurvived + " ticks."); if (ticksSurvived > mostTicksSurvived) { mostTicksSurvived = ticksSurvived; } startNewGame(); } currentEnemyNumber = currentEnemyNumber + 1; }Let’s also tell the player that they beat their old high score:
while (currentEnemyNumber < numberOfEnemies) { if ( ( (avatarX < enemyXPositions[currentEnemyNumber] && enemyXPositions[currentEnemyNumber] < avatarX + 30) || (enemyXPositions[currentEnemyNumber] < avatarX && avatarX < enemyXPositions[currentEnemyNumber] + 30) ) && ( (avatarY < enemyYPositions[currentEnemyNumber] && enemyYPositions[currentEnemyNumber] < avatarY + 33) || (enemyYPositions[currentEnemyNumber] < avatarY && avatarY < enemyYPositions[currentEnemyNumber] + 30) ) ) { alert("You hit an enemy! You survived " + ticksSurvived + " ticks."); if (ticksSurvived > mostTicksSurvived) { alert("New high score!"); mostTicksSurvived = ticksSurvived; } startNewGame(); } currentEnemyNumber = currentEnemyNumber + 1; }Take a look:
Click to try it out.
Excellent. Now let’s give the player a little more information on how much better they did:
while (currentEnemyNumber < numberOfEnemies) { if ( ( (avatarX < enemyXPositions[currentEnemyNumber] && enemyXPositions[currentEnemyNumber] < avatarX + 30) || (enemyXPositions[currentEnemyNumber] < avatarX && avatarX < enemyXPositions[currentEnemyNumber] + 30) ) && ( (avatarY < enemyYPositions[currentEnemyNumber] && enemyYPositions[currentEnemyNumber] < avatarY + 33) || (enemyYPositions[currentEnemyNumber] < avatarY && avatarY < enemyYPositions[currentEnemyNumber] + 30) ) ) { alert("You hit an enemy! You survived " + ticksSurvived + " ticks."); if (ticksSurvived > mostTicksSurvived) { alert("You beat your old high score by " + (ticksSurvived - mostTicksSurvived) + " ticks!"); mostTicksSurvived = ticksSurvived; } startNewGame(); } currentEnemyNumber = currentEnemyNumber + 1; }Note here that
ticksSurvivedandmostTicksSurvivedare treated as numbers when subtracting one from the other, but the resulting expression(ticksSurvived - mostTicksSurvived)is treated as a string when added to the other strings! This can get really confusing, if you’re not careful.Click to try it out.
All right, so now the player knows how well they were doing, after they get game over, we should give them an indication of how well they are doing, while they’re still playing.
Drawing the Score to the Canvas
It’s really easy to write text into a canvas – and no, we don’t need to piece it together from different images of different letters!
Remember that to draw an image to the canvas we call
canvas.getContext("2d").drawImage(). Writing text is very similar: we callcanvas.getContext("2d").fillText().We must pass three arguments to
fillText():There’s an optional fourth argument:
…but we won’t worry about that for now.
To test this out, head to
handleTick(), find the line where we draw the avatar to the canvas, and draw a sample line of text just afterwards:gameCanvas.width = 400; //this erases the contents of the canvas gameCanvas.getContext("2d").drawImage(avatarImage, avatarX, avatarY); gameCanvas.getContext("2d").fillText("Testing, testing, one, two, three.", 100, 100);Try it out:
Click to try it out.
Note that, since this gets drawn after the avatar and before the enemies, the enemies get drawn on top of it, while the avatar goes underneath:
Click to try it out.
Like I say, the first argument required is a string, and – as we’ve seen – we can construct a string by adding a string to a number. So, this should work:
gameCanvas.width = 400; //this erases the contents of the canvas gameCanvas.getContext("2d").drawImage(avatarImage, avatarX, avatarY); gameCanvas.getContext("2d").fillText("Score: " + ticksSurvived, 100, 100);…and indeed it does!
Click to try it out.
But it’s a bit of a mess, floating there underneath the enemies with that tiny font. Let’s sort that out.
Tidy Up the Text
First, let’s move the text so that the enemies move underneath it. This just means drawing it after all of the enemies are drawn, so move the relevant line down:
gameCanvas.width = 400; //this erases the contents of the canvas gameCanvas.getContext("2d").drawImage(avatarImage, avatarX, avatarY); currentEnemyNumber = 0; while (currentEnemyNumber < numberOfEnemies) { gameCanvas.getContext("2d").drawImage(enemyImage, enemyXPositions[currentEnemyNumber], enemyYPositions[currentEnemyNumber]); currentEnemyNumber = currentEnemyNumber + 1; } gameCanvas.getContext("2d").fillText("Score: " + ticksSurvived, 100, 100);Now, let’s move it to the top-left of the screen. That’s,
(0, 0)so this should work, right?gameCanvas.width = 400; //this erases the contents of the canvas gameCanvas.getContext("2d").drawImage(avatarImage, avatarX, avatarY); currentEnemyNumber = 0; while (currentEnemyNumber < numberOfEnemies) { gameCanvas.getContext("2d").drawImage(enemyImage, enemyXPositions[currentEnemyNumber], enemyYPositions[currentEnemyNumber]); currentEnemyNumber = currentEnemyNumber + 1; } gameCanvas.getContext("2d").fillText("Score: " + ticksSurvived, 0, 0);Hmm. Nope. We can’t see the score at all. This is because it’s using
(0, 0)to place the bottom-left corner of the text. We need to move the text down a bit, then:gameCanvas.width = 400; //this erases the contents of the canvas gameCanvas.getContext("2d").drawImage(avatarImage, avatarX, avatarY); currentEnemyNumber = 0; while (currentEnemyNumber < numberOfEnemies) { gameCanvas.getContext("2d").drawImage(enemyImage, enemyXPositions[currentEnemyNumber], enemyYPositions[currentEnemyNumber]); currentEnemyNumber = currentEnemyNumber + 1; } gameCanvas.getContext("2d").fillText("Score: " + ticksSurvived, 0, 10);Check it out:
Click to try it out.
Okay, so far so good. Now, let’s change the font itself. We do this by setting the canvas’s context’s
fontproperty to a CSS string representing the font. If you’re not familiar with CSS, don’t worry; at this stage, all you need to know is that it contains the font’s size and the fontface:gameCanvas.width = 400; //this erases the contents of the canvas gameCanvas.getContext("2d").drawImage(avatarImage, avatarX, avatarY); currentEnemyNumber = 0; while (currentEnemyNumber < numberOfEnemies) { gameCanvas.getContext("2d").drawImage(enemyImage, enemyXPositions[currentEnemyNumber], enemyYPositions[currentEnemyNumber]); currentEnemyNumber = currentEnemyNumber + 1; } gameCanvas.getContext("2d").font = "10px Impact"; gameCanvas.getContext("2d").fillText("Score: " + ticksSurvived, 0, 10);(“px” stands for “pixels”.)
Note that we have to set the font before drawing the text!
Take a look:
Click to try it out.
It works – but the Impact font is really hard to read at that size. Let’s make it bigger:
gameCanvas.getContext("2d").font = "18px Impact"; gameCanvas.getContext("2d").fillText("Score: " + ticksSurvived, 0, 10);Click to try it out.
Unfortunately, this doesn’t work so well, because the font is still 10px from the top of the canvas, but now it’s 16px tall!
We could keep changing the y-position of the font to fix this, but there’s an alternative: we’ll make it so that the position we specify determines the top-left corner of the text, rather than the bottom-left corner. It’s easy:
gameCanvas.getContext("2d").font = "18px Impact"; gameCanvas.getContext("2d").textBaseline = "top"; gameCanvas.getContext("2d").fillText("Score: " + ticksSurvived, 0, 10);Of course, now, there’s a 10px gap between the top of the text and the top of the canvas:
Click to try it out.
…but that’s easy to fix now:
gameCanvas.getContext("2d").font = "18px Impact"; gameCanvas.getContext("2d").textBaseline = "top"; gameCanvas.getContext("2d").fillText("Score: " + ticksSurvived, 0, 0);Well, actually, I like a bit of padding, so let’s do this:
gameCanvas.getContext("2d").font = "18px Impact"; gameCanvas.getContext("2d").textBaseline = "top"; gameCanvas.getContext("2d").fillText("Score: " + ticksSurvived, 5, 5);Click to try it out.
Much better.
Different Fonts
Perhaps you’re wondering whether there’s a list of fonts we can use. Well, yes and no. See, if you pick a font that the player doesn’t have installed on their computer, then the text will just be displayed in the default font. Remember, JavaScript is drawing the text on the fly, using the player’s computer’s resources.
We’re fine using Impact, because it’s installed on every computer – but does that mean we can’t use any font other than the few in this list?
Fortunately, no. We can use any font we like, as long as we give the user access to it somehow. And for this, we’re going to use that CSS file – you know, the one we haven’t touched since the start of the series.
Suppose we have a font called “Really-Awesome”. This will exist on your computer, somewhere, as a file – probably a .TFF file (“True Type Font”). Let’s suppose that file is called
ReallyAwesomeFont.ttf.Now suppose you upload this font to your website – reallyawesomewebsite.com – so that there’s a direct URL to it:
http://reallyawesomewebsite.com/fonts/ReallyAwesomeFont.ttf.You can then let the player’s browser know about it by adding this to your CSS file:
@font-face { font-family: 'Really-Awesome'; src: url('http://reallyawesomewebsite.com/fonts/ReallyAwesomeFont.ttf'); }With this line in your CSS, you can alter your code like so:
gameCanvas.getContext("2d").font = "18px Really-Awesome"; gameCanvas.getContext("2d").textBaseline = "top"; gameCanvas.getContext("2d").fillText("Score: " + ticksSurvived, 5, 5);…and it’ll work, because their browser will look up the “Really-Awesome” font in the stylesheet, and find the URL to the TTF. Great!
I’m not going to demonstrate this because I don’t own the redistribution rights to any fonts; if I upload some and give you the link to the TTF as part of this tutorial, that’s not really fair. But there is an alternative…
Google Web Fonts
Google has assembled a large collection of fonts that you can use in your project in your CSS, using a similar principle as above. Take a look at the collection.
There are a few criteria by which you can search for fonts, and you can enter some sample text to see how it’ll be displayed:
Google Web Fonts.
I’m going to choose Iceland. When I click Quick Use, it gives me this HTML:
If you load http://fonts.googleapis.com/css?family=Iceland in your browser, you’ll see that it’s the same kind of thing as we wrote from scratch before:
@media screen { @font-face { font-family: 'Iceland'; font-style: normal; font-weight: 400; src: local('Iceland'), local('Iceland-Regular'), url('http://themes.googleusercontent.com/static/fonts/iceland/v1/F6LYTZLHrG9BNYXRjU7RSw.woff') format('woff'); } }It has a few more details, and the font is in WOFF format rather than TTF, but you get the idea.
You can use whichever font you like (and it doesn’t have to be a Google Web Font), but for the purpose of this tutorial I’m going to assume that you’re using Iceland. So, edit
game.htmland paste the font reference into it:Now, back in
main.js, change the font from Impact to Iceland:gameCanvas.getContext("2d").font = "18px Iceland"; gameCanvas.getContext("2d").textBaseline = "top"; gameCanvas.getContext("2d").fillText("Score: " + ticksSurvived, 5, 5);Take a look:
Click to try it out.
Cool!
Challenge: Show the Best Score
Now that the current score is on screen at all times, it’s only natural that the player will want to see what they’re trying to beat.
Using what you’ve learnt, draw their best score at the top-right of the canvas. This is a little trickier than it seems: you’ll have to decide what to do (if anything) when the current score overtakes the current top score!
Saving Scores Between Sessions
You’ve probably noticed by now that the high score is reset when you reload the page. This makes sense – after all, we run this line right at the start:
…but even without that line, the high score still wouldn’t persist. All variables get reset and unassigned when you leave the page.
However, there is an alternative: every browser sets aside 5MB of storage for each web site domain. You can store any string you like in this 5MB storage, and it’ll stay there even if the user closes their browser and restarts their computer.
It’s called local storage, and it’s really easy to use! To save something to it, you just need to give it two strings:
To retrieve it, you just need the name that you originally used.
A Quick Example
Here’s a quick example: let’s add something to the local storage right at the start of the
setUpGame()function:function setUpGame() { localStorage.setItem("exampleItem", "This is a great example.");Save the file, and load your game. Then close the tab.
Now, edit your JS file again, delete the line you just added, and replace it with a line that should retrieve the item:
function setUpGame() { alert(localStorage.getItem("exampleItem"));So, just to be clear, there is now nothing in the code that sets the value of
"exampleItem".When you load the game this time, you should see an alert with
"This is a great example."– proof that the string has been saved between sessions.We can remove this item from the local storage using
localStorage.removeItem():function setUpGame() { localStorage.removeItem("exampleItem");I recommend you do this now, and then delete the line entirely.
You can also clear everything in the local storage at once, using
localStorage.clear()(no arguments required). Okay – not quite everything. Your page can only affect the local storage space assigned to the domain on which it is hosted; I can’t clear your local storage for google.com from a page hosted at tutsplus.com, and vice-versa.Saving the Best Score
Now that we’ve seen an example, let’s put it into practice.
Whenever a new high score is set, let’s save it to the local storage. It only gets set in one place: when the avatar collides with an enemy and the current score is higher than the best score. So, add the call to
localStorage.setItem()in the appropriate place:while (currentEnemyNumber < numberOfEnemies) { if ( ( (avatarX < enemyXPositions[currentEnemyNumber] && enemyXPositions[currentEnemyNumber] < avatarX + 30) || (enemyXPositions[currentEnemyNumber] < avatarX && avatarX < enemyXPositions[currentEnemyNumber] + 30) ) && ( (avatarY < enemyYPositions[currentEnemyNumber] && enemyYPositions[currentEnemyNumber] < avatarY + 33) || (enemyYPositions[currentEnemyNumber] < avatarY && avatarY < enemyYPositions[currentEnemyNumber] + 30) ) ) { alert("You hit an enemy! You survived " + ticksSurvived + " ticks."); if (ticksSurvived > mostTicksSurvived) { alert("You beat your old high score by " + (ticksSurvived - mostTicksSurvived) + " ticks!"); mostTicksSurvived = ticksSurvived; localStorage.setItem("bestScore", mostTicksSurvived); } startNewGame(); } currentEnemyNumber = currentEnemyNumber + 1; }When the page is first loaded, we should check to see whether there already is a high score saved in the local storage, and assign
mostTicksSurvivedto that value, if so. (You don’t need to repeat this check once the page is loaded, unless you’re worried about the user playing the game in two separate tabs at once.)How do we check whether a value exists? All we have to do is put it inside an
ifcondition:if (localStorage.getItem("thisItemDoesNotExist")) { alert("This alert will never appear!"); }The
alert()above never gets called becauselocalStorage.getItem("thisItemDoesNotExist")does not exist. Easy, right? So at the start ofsetUpGame(), we can just write:function setUpGame() { if (localStorage.getItem("bestScore")) { mostTicksSurvived = localStorage.getItem("bestScore"); }Have a go. Load the page, set a high score, then beat it. Reload the page, then get a lower score – does it tell you you beat your old score?
Click to try it out.
Great! By the way, if you took the challenge earlier then you’ll be able to see your previous best score in the top-right corner of the canvas, and this will now carry over from session to session.
Sidenote: Strong and Weak Typing
We’re about done for this part of the series, but I just want to point out that, once again, we’ve been treating a string as a number and a number as a string: local storage only saves string values, yet we save a number to it when we save the best score, and use the value we retrieve from it as a number later on.
This is acceptable, because JavaScript is what’s called “weakly-typed”. Other programming languages are “strongly-typed”, which means that if you say that something is a number, then it stays a number; if you say something is a string, then it stays a string.
In a strongly-typed language, if you want to add the string
"Score: "to the number32, then you have to explicitly tell the language to treat32as a string, perhaps like so:"Score: " + (32 as String)Also, in a strongly-typed language, when you define a variable, you also specify what type it is:
But JavaScript doesn’t worry about these things. This doesn’t make it better or worse than a strongly-typed language, just different.
Wrapping Up
That’s it for this part of the tutorial. Now your game has both a game over condition and a means of keeping score. Plus, you learned about drawing text to a canvas, choosing fonts, and using the local storage.
If you’d like to challenge yourself before the next part, have a go at making these changes:
Enjoy!