Using createjs, preoloading and caching, why is my canvas still so slow? - html

I am trying to create a small RPG for class. I create one Bitmap image, cache it, and then every time I need that same Bitmap, I clone it, and finally add them to the stage. However, despite my efforts, my canvas is still extremely slow since I am drawing all of these bushes.
I would put them in a container, however, I need to know the X and Y position that way I know if the player is trying to step over their boundaries.
Here is my function:
parseRoom: function(mapObject, room, obj, image){
var letter = 'X';
//var object = obj;
var object = null;
var img = new createjs.Bitmap(image);
for(var m=0; m < mapObject.length; m++){
for(var j=0; j< mapObject[m].length; j++){
letter = mapObject[j][m];
switch (letter){
case 'X':
//do nothing
break;
case 'O':
//object = this.createObject();
//object.image = img.clone();
img.cache();
object = img.clone();
room.AddObstacle(object, m, j);
break;
}
}
}
}
and this is my function when I actually add them to the stage:
addObstacle: function(imgObj, x, y){
imgObj.x = x ||imgObj.x || 0;
imgObj.y = y ||imgObj.y || 0;
imgObj.setVisible = function(visible){
this.visible = visible;
};
imgObj.update = function(){
if(this.visible)this.visible= true;
else this.visible = false;
};
objects.push(imgObj);
stage.addChild(imgObj);
},
as you can see, I extend the Bitmap class and also add and update and a setVisible method to it. That way I can turn them all off or on depending on the screen. Any ideas that could help me and make my game smoother? Thank you!

I was constantly updating every object, including the bushes, and there was no need to update the bushes every tick. I simply removed logic from checking the bushes and now it runs really fast.

Related

HitTestObject Both MovieClip Actionscript 3.0

I want to make a player and 2 Circles.
When the player hit the first Circle, then the Circle will also move as like player. Continue with the second Circle, if the second circle get hit by the first circle (while the player is moving and pushing the first circle), the second circle will also move as like player's speed movement!
Can you solve the problems, please ... :)
Thank You!
Use the addChild() method.
var circle1Hit:Boolean = false;
var circle2Hit:Boolean = false;
function myHitTest(me:MouseEvent): void
{
if (player.hitTestObject(circle1) && circle1Hit == false){
circle1Hit = true;
var _x:Number = circle1.x - player.x;
var _y:Number = circle1.y - player.y;
player.addChild(circle1)
circle1.x = _x;
circle1.y = _y;
}
if (player.hitTestObject(circle2) && circle2Hit == false)
{
circle2Hit = true;
var _x:Number = circle2.x - player.x;
var _y:Number = circle2.y - player.y;
player.addChild(circle2)
circle2.x = _x;
circle2.y = _y;
}
}
For further reading check out this great tutorial that explains containers and OOP really well. Also check out the one on Arrays from the same author here. By using an array, you could add even more circles to the array and all of them would be able to stick like these two do without having to have a separate bit of code for each circle like we have here.

AS3-Having a mouse click affect object in an array's timeline

I'm going crazy with this. I have a few bears on the stage and have listed them in an array. I want them to change their animation frame when clicked as long as they are not on the "down" animation frame to begin with. Below is the code. The problem occurs below the //check if they get hit line.
//put the bears in an array
var bearsArray: Array = new Array();
for (var i=0; i<numChildren;i++) {
if (getChildAt (i) is bear_mc) {
bearsArray.push(getChildAt(i));
}
}
//move them up and down
addEventListener(Event.ENTER_FRAME,upAndDown);
function upAndDown(event:Event){
if (Math.random() < 0.02){
var randomBear = Math.floor(Math.random() * 9);
bearsArray[randomBear].gotoAndPlay("popup");
}
}
//check if they get hit
for (var j:int = 0; j < bearsArray.length; j++){
bearsArray[j].addEventListener(MouseEvent.CLICK, hitBears);
}
function hitBears(e:MouseEvent){
for (var k: int=0; k<numChildren; k++){
if (bearsArray[k].currentFrame != "down"){
trace("clicked");
bearsArray[k].gotoAndPlay("surprised");
}
}
}
currentFrame returns an integer, not string. If you want to use string, you have to use currentFrameLabel or currentLabel.
EDIT: http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/display/MovieClip.html#currentLabel
If you want to control only one bear, remember that you don't need to cycle through them (actually that would serve no purpose as you would be checking every one of them). The better (and correct) approach would be this:
function hitBears(e:MouseEvent){
var bear:MovieClip = e.currentTarget as MovieClip;
if(bear.currentLabel != "down") { //or currentFrameLabel, depends how your mc is set up
trace("clicked");
bear.gotoAndPlay("surprised");
}
}
I got it working by checking a hittest with the mouse instead of just checking the click. That works for some reason.

Drag-and-Drop - Limiting duplicate MCs for single use in targets?

I've been working on a Flash drag-and-drop scene using AS3. The scene is broken into 4 "zones", and each zone has 5 targets. I've used arrays to allow the mc's to be dropped in their respective zones, without any particular order. All of this works as it should.
The issue I have it that there are 2 "puzzle" pieces (MCs) that are identical to one-another in terms of how they visually display on the screen. I've set up the arrays to allow for both to be dropped in zone 1 or zone 2. However, I'd like to set it up so that if "identical piece 1" gets dropped into zone 1, then "identical piece 2" cannot also be dropped into that same zone.
Any suggestions for how to go about doing this?
How about setting a flag in every zone that tells if a piece has already been attached to it?
If your zones are MovieClips you can simply add variables to them, and set mcTarget.isTaken = true when a piece gets attached to it. If they are simply coordinates, you can use a global array like this
private mZoneTaken:Array = new Array();
for (var i:int=0; i<4; i++)
{
mZoneTaken[i] = new Array();
for (var j:int = 0; j<5; j++)
{
mZoneTaken[i][j] = false;
}
}
Then whenever a piece is dropped on a zone, you can set its flag to true, allowing you to prevent any further piece from getting attached to it.
Personally, I would have an array/Vector (Vectors are much, much faster. Though they can be a royal pain to use) of objects within each individual zone. You keep this updated at all times. If you remove one, you use splice(indexOf(obj),1) to remove it from the array.
For your problem, I would do this:
var p1:Sprite = new Sprite();
var p2:Sprite = new Sprite();
var zoneArray:Array = new Array();//there would be one for each zone, simplified it here
//this code would run whenever an object enters the zone
function zoneEnter(e:Event = null):void{
var currentZone:Sprite; //set this equal to the zone the sprite just entered
var currentObject:Sprite = e.currentTarget as Sprite; //this way we know which object just entered currentZone
if ( currentObject == p1 && zoneArray.indexOf(p2) >= 0 ) {
//prevent it from entering the zone
}
if ( currentObject == p2 && zoneArray.indexOf(p1) >= 0 ) {
//prevent it from entering the zone
}
}
There are obviously some limitations here if you are looking at a lot of objects. But if you know, for sure, there will only be two, I think this would be the way to go. If there will be more, you would need to create some kind of control structure that would allow you to check an object against other objects. I could elaborate and give a sample of what I am talking of here if it's needed. It's a tad bit complex, but not hard to implement.
EDIT 10-17-12: Added in example code of how the more advanced logic would look.
var puzzleMap:Array = new Array();
var piece1:Piece = new Piece(); //this is the puzzle piece we'll be checking
var piece2:Piece = new Piece();
var piece3:Piece = new Piece();
var piece1Objs:Array = [piece2,piece3];
var piece2Objs:Array = [piece1,piece3];
puzzleMap.push({piece:piece1,others:piece1Objs});
puzzleMap.push({piece:piece2,others:puzzle2Objs);
private function stopDragHandler(e:MouseEvent = null):void{
var space:Space = space; //this is the space the object was dropped into (I don't know how you identify them, so this is pseudo code)
var valid:Boolean = true; //we'll check for a bad drop since it's easiest
if (event.target.dropTarget != null && MovieClip(event.target.dropTarget.parent).allowed.indexOf(event.target) > -1){
for ( var i:Number = 0; i < puzzleMap.length; i++ ) {
var current:Object = puzzleMap[i];
if ( current == e.currentTarget ) {
for ( var j:Number = 0; j < current.others.length; j++ ) {
var checkAgainst:Piece = current.others[i] as Piece;
if ( space.currentObjects.indexOf(checkAgainst) < 0 ) {
valid = false;
break;
}
}
}
}
if ( !valid ) {
//prevent the drop here
}
}
}

Flex mobile - increase list scrolling performance

Imagine a tablet app that displays two content areas side by side. They completely fill the display, so are 100% in height and 50% in width.
Lets assume we add a list to one container. Naturally this list will consume half the space of the whole display.
Now to my problem, is it possible that high framerate scrolling is kind of impossible with lists of this size? I've got the most basic AS3 ItemRenderer and still can't get anything higher than 30fps during scrolling. Now the odd part, if I add stuff the other container, lets say another list or other components, the list scrolling performance drops to the low 20s.
So nowhere near the 40+ fps you see Adobe advertising in their MAX shows.
I'm testing on a iPad2 and 3 and even with static values, scrolling isn't really good. Now if I enable streaming values so that the ItemRenderer's set data method is called, the framerate drops another 2 to 3 frames.
My (almost) complete renderer looks like this, but even if I strip it to just display a single textfield, disable the stuff going on in the set data and also set only the size of the single textfield in the layoutContents, performance is as described, about 30 if the list is displayed alone, low 20s if other stuff is displayed as well.
//FCStyleableTextField is just a StyleableTextField with an additional ID
private var _textFields:Vector.<FCStyleableTextField>;
private var _oldValues:Dictionary;
private var _sym:Symbol;
public function GridRenderer() {
super();
_textFields = new Vector.<FCStyleableTextField>();
_oldValues = new Dictionary();
}
override protected function createChildren():void {
var _symLabel:FCStyleableTextField = new FCStyleableTextField();
_symLabel.editable = false;
_symLabel.selectable = false;
_symLabel.multiline = false;
_symLabel.id="sym";
_symLabel.setStyle("fontSize", fontSize);
_symLabel.textColor = 0xc0c0c0;
_textFields.push(_symLabel);
addChild(_symLabel);
var fidLen:int = fids.length;
for (var i:int = 0; i<fidLen; i++) {
var _fid_lbl:FCStyleableTextField = new FCStyleableTextField();
_fid_lbl.selectable = false;
_fid_lbl.editable = false;
_fid_lbl.multiline = false;
_fid_lbl.id = String(fids[i]);
_fid_lbl.textColor = 0xc0c0c0;
_fid_lbl.setStyle("textAlign", "right");
_fid_lbl.setStyle("fontSize", fontSize);
_fid_lbl.text = " ";
_textFields.push(_fid_lbl);
addChild(_fid_lbl);
if(i>visibleColumns) {
_fid_lbl.includeInLayout = false;
_fid_lbl.visible = false;
}
}
}
override public function set data(value:Object):void {
if(!value) return;
if(data) {
// check if the value's symbolName is different than the current
// data's symbolName, if so, the itemRenderer has been
// recycled, thus we need to reset the old fid values
if((value as Symbol).symbolName != (data as Symbol).symbolName)
_oldValues = new Dictionary();
}
super.data = value;
_sym = data as Symbol;
try {
var textLen:int = _textFields.length;
for (var i:int = 0; i<textLen;i++) {
var lbl:FCStyleableTextField = _textFields[i];
if(lbl.id == "sym") {
lbl.text = _sym.symbolName;
lbl.truncateToFit();
} else {
if(lbl.id == _sym.fidList.fidMap[lbl.id].fidId && lbl.text != _sym.fidList.fidMap[lbl.id].fieldValue) {
var time:int = new Date().time;
var timerName:String = _sym.symbolName+","+lbl.id+","+grid;
globalTimer.addTimer(timerName, time, "reset", lbl, null, null);
var _oldVal:* = _oldValues[lbl.id];
var _newVal:* = _sym.fidList.fidMap[lbl.id].fieldValue;
// basic color formatting
if(Number(_newVal) > Number(_oldVal))
lbl.textColor = 0x40c040;
else if(Number(_newVal) < Number(_oldVal))
lbl.textColor = 0xf05050;
// add + to change and changePercent fids if value is positive
if(lbl.id == "56") {
if(_newVal >0)
lbl.text = "+" + _newVal;
else
lbl.text = String(_newVal);
} else if(lbl.id == "11") {
if(_newVal >0)
lbl.text = "+" + _newVal;
else
lbl.text = String(_newVal);
} else
lbl.text = String(_newVal);
if(!_sym.fidList.fidMap[lbl.id].fieldValue)
lbl.text =" ";
_oldValues[lbl.id] = _newVal;
}
}
lbl.truncateToFit();
}
} catch (e:Error) { /* nothing to do here -> try/catch required due to async symbolassembly */ }
}
override protected function layoutContents(unscaledWidth:Number, unscaledHeight:Number):void {
var viewWidth:Number = unscaledWidth - paddingLeft - paddingRight;
var viewHeight:Number = unscaledHeight - paddingTop - paddingBottom;
var _previousLabel:FCStyleableTextField;
var textLen:int = _textFields.length;
for(var i:int =0; i<textLen;i++) {
var lbl:FCStyleableTextField = _textFields[i];
graphics.beginFill(0x808080, .3);
lbl.height = viewHeight;
lbl.y = paddingTop;
if(lbl.id=="sym") {
lbl.width = 95;
} else if (lbl.id == "35000") {
lbl.width = 24;
} else {
lbl.width = optimalColWidth;
}
_previousLabel ? lbl.x = (_previousLabel.x + _previousLabel.width): lbl.x = paddingLeft;
graphics.drawRect(lbl.x+lbl.width, 1, 1, unscaledHeight-1);
lbl.commitStyles();
_previousLabel = lbl;
graphics.endFill();
}
}
Still, I'm pretty sure that it is not the item renderer that causes the slowdown, cause as I said, it costs 2, maybe 3 frames compared to a renderer that displays just a single textfield.
I rather think that Flex somehow can't handle the amount of vectors being displayed at once, is something like that possible? And is there any way to boost performance?
I already disabled live streaming values as soon as the user scrolls the list, so that flex basically just has to scroll bitmaps (since LabelItemRenderer automatically enables cacheasbitmap), but that gained maybe 4 frames.
What are your guys tricks to make scrolling a little smoother?
Figured out that using setElementSize() and setElementPosition() instead of using width/height and x/y makes quite a difference. Gained 3fps in initial scrolling performance and 8fps once every item has been rendered.
So I'm pretty close to 30fps now, still not close to what you can do with a native app, but I figure that's as good as it gets with Flex and such a massive renderer.
Also disabled live updates so that the renderer doesn't need to be cached as a bitmap again when updates come in.

AS3: Stalling a for loop at each pass

So I've worked out a great way to create a pixelized wipe effect, with movie clips of pixels nested in rows. I've created a loop that goes through each row, and then another loop inside that loop for the pixels in each row. I then use a random number between 1-0 for for the delay before it alpha-ups the pixel. I used Greensock for the tween and it works great.
One problem, I can't stall the for loop for the rows, and every row comes up at once. Still, the pixels are staggered, and this is a nice effect, but I want it to move from left to right. So my code looks like this: (BTW, yes, AS3 does have setTimeout and it works fine)
function stripeWipeUp(stripe:MovieClip):void {
var total = stripe.numChildren;
for (var i:int = 0; i<total; i++) { // the rows
trace(i);
setTimeout(function() {
trace("setTimeout: "+i);
var row:MovieClip = stripe.getChildAt(i) as MovieClip;
var pixTotal = row.numChildren;
for (var j:int = 0; j<pixTotal; j++) { the pixels
var pix:MovieClip = row.getChildAt(j) as MovieClip;
var num = Math.floor(Math.random()*100)/100; // the delay
pixUp(pix, num); // my greensock function
}
}, 500);
}
}
What happens, with the setTimeout in there, is that the "i" for loop hits every row (there are 27) before one setTimeout gets finished. So, it doesn't work as it should - the next loop should not execute until the setTimeout is finished. Any idea how to accomplish this so each row gets stalled by about 500 ms? Thanks.
Your problem is with this.
trace("setTimeout: "+i);
"i" is not what you think it is.
The scope of "i" is in stripeWipeUp not the anonymous function.
With that being said "i" is always the value of the last itteration of the loop that was run across all of your anonymous functions.
So as you can see when the anon functions trigger "i" will most-likely be equal to "total".
This is why we avoid using anonymous functions as scope is very hard determine.
Create an array and store a custom class in it for each "pixel" that will control what you want to do.
In this class use a Timer Object not setTimeOut. setTimeOut is a garbage function left over from AS2.
drop the setTimeout and just add a delay multiplied with i
var num = ( i * 500 ) + Math.floor(Math.random()*100)/100; // the delay
I would encapsulate i and j, a Timer, and an event handler for the Timer, into a class. In the event handler I would:
Do your effect for the pixel corresponding to j and i
Update j (and i)
If there are more pixels, schedule the next event
I was able to get it to work by taking the inner part out and making it a separate function. I would prefer a much cleaner solution like Creynders', but for some reason that didn't work. Here is the code:
// the row of pixels
function row(stripe:MovieClip, i:int, up:Boolean, del:Number):void {
setTimeout(function() {
var row:MovieClip = stripe.getChildAt(i) as MovieClip;
var pixTotal = row.numChildren;
for (var j:int = 0; j<pixTotal; j++) {
var pix:MovieClip = row.getChildAt(j) as MovieClip;
var num = Math.floor(Math.random()*100)/100;
(up) ? pixUp(pix, num) : pixDown(pix, num);
}
}, del);
}
// the rows in the "stripe"
function stripeWipe(stripe:MovieClip, up:Boolean):void {
var total = stripe.numChildren;
var del:Number = 0;
for (var i:int = 0; i<total; i++) {
row(stripe, i, up, del);
del = del+100;
}
}