Flash AS3: Swapping Parts within Movieclips - actionscript-3

I have a Movieclip (level 1, MC) with around 10 frames. There is a different movieclip (level 2, Move Jump etc animation) on each frame. Inside each of the level 2 movieclips, there are about 30 movieclip symbols (level 3, Head Arm etc) each spamming about 100 frames with around 30-50 keyframes. Each level 3 symbol has around 10 frames, on each frame I have a particular skin graphic.
I have all the level 2 and level 3 symbols instance named (same label across timeline for the same object), and I have the right frames stop(); and labelled. I also understand that everytime I enter a new frame in not just level 1 but also level 2, I have to reset all the level 3 symbols to the desired frame, because my previous settings will be destroyed upon leaving frame.
I got it working by doing level1.gotoAndStop(level2name) ---> level1.level2.level3.gotoAndStop(skintype) and then loop over a nasty nasty number of poses X bodyparts. AND this process needs to be performed in an ENTER_FRAME event since everything will be reset again. Needless to say, I really really don't want to do it this way.
One of the alternatives is breaking the graphics up and have many many level 2 poses movieclips inside my level 1 MC movieclip (mage hit by warrior 3rd attack, warrior hit by rogue 4th attack...). That's what I used to do before. But for this project, a simple calculation tells me I need to make 200+ animations that way, which is not feasible. I can also get rid of level 1 MC and have the poses saved into an array, but the bodyparts still need to be refreshed every frame.
I'm hoping that there's a relatively quick fix to this that I managed to miss, as it seems like such a basic feature, I'm sure many flash games will have to go through it (dress up, or anything with customization + animation really). Yet somehow I've been searching for days and can't find a cure. The author-time ability to simply swap out graphics within a symbol to replace every frame of every animation in the entire file also suggests that there's gotta be a more universal approach to these swapping. I hope you can prove me right!
I do have 2 things that I don't know if I should even bother trying: 1) Drag the MC onto frame1 (my only frame), where I currently have nothing but code. 2) Declare each bodypart individually AND declare MC, then have MC's parts link to these bodyparts. Basically, I just need a viable method to keep these bodyparts from resetting everytime the animation goes to a new frame, my flash knowledge is not enough to tell me whether if it's even possible to have these "global graphics bank independent of frames".
And yes I know I'm probably not doing it in the most clean way possible, but I simply animate better with visuals, so while I CAN start from shapes and animate everything using strictly code I REALLY want to move away from it. The art style is pretty important in this project.
UPDATE: For now, I went for the ugly route. Everytime the MC changes animation, I do:
MovieClip(DisplayObjectContainer(MC.getChildByName(MC.move))).Hand.gotoAndStop(MC.skinname);
And repeat that for all 35 body parts. Turns out that flash replaces all frames of hand in the MC.move, which makes life a lot easier. The alternative is to poll for every single frame, but the direct consequence is MUCH slower fps. Instead, right now I only need to switch graphics whenever there is a change to the moves.
This works, but I'm aware that it slows down performance quite a bit. In fact it can slow down performance in the same magnitude as the actual vector rendering. Limiting the swapping to only move changes is really not optional but mandatory.

Please look at this: http://zdg.ru/tmp/animation.swf
The source can be downloaded here: http://zdg.ru/tmp/animation.fla
If I got your description correct, I did the animation in the same way. The main timeline has a single frame with a character symbol. A character symbol has a timeline within it with two animation points "stand" and "jump". The character consists of symbols "head", "body", "left_hand", "right_hand", "left_leg", "right_leg". Each of these symbols is animated independently. Charater animation timeline contains both keyframes and tweens. All symbols are named consistently in all frames.
Each character part, in turn, has a timeline of 2 frames, corresponding to skin 1 and skin 2.
As you can see, the skin is not destroyed during the animation and there is no need to correct it in every frame.
The code on the main timeline is:
var char_body_parts:Array = new Array(
mv_char.body, mv_char.head, mv_char.left_hand, mv_char.right_hand,
mv_char.left_leg, mv_char.right_leg
);
var skin_num:int = 1;
mv_char.gotoAndStop("stand");
setSkin(char_body_parts, skin_num);
btn_jump.addEventListener(MouseEvent.CLICK, doJump);
btn_skin.addEventListener(MouseEvent.CLICK, doSkin);
function doJump(evt:MouseEvent):void {
mv_char.gotoAndPlay("jump");
}
function doSkin(evt:MouseEvent):void {
skin_num++;
if (skin_num > 2) skin_num = 1;
setSkin(char_body_parts, skin_num);
}
function setSkin(parts:Array, skin_num:int):void {
for (var i:int = 0; i < parts.length; i++) {
(parts[i] as MovieClip).gotoAndStop(skin_num);
}
}
================== UPDATE ====================
This is the updated animation: http://zdg.ru/tmp/animation3.swf
The source can be downloaded here: http://zdg.ru/tmp/animation3.fla
Now my setup is identical to yours.
I have a 2-frame char, one frame contains "stand" movieclip and the other frame contains "jump" movieclip.
Indeed, when you gotoAndStop for any of animations, skins get lost. But you don't have to update them every frame. You need to update them only in the first frame of animation, i.e. right after gotoAndStop("animation"). I still have a list of body parts to bulk assign the skin but now they are accessed by name. So the main code changes are:
var char_body_parts:Array = new Array(
"body", "head", "left_hand", "right_hand",
"left_leg", "right_leg"
);
function setSkin(char:MovieClip, parts:Array, skin_num:int):void {
for (var i:int = 0; i < parts.length; i++) {
char.mv_animation.getChildByName(parts[i]).gotoAndStop(skin_num);
}
}
function setAnimation(char:MovieClip, anim_name:String):void {
char.gotoAndStop(anim_name);
setSkin(char, char_body_parts, skin_num);
}
There are also two more solutions, I didn't code them but they are easily described.
In frame 1 of each skin movieclip, add gotoAndStop((root as MovieClip).skin_num). It works as follows: whenever a skin gets reset, it starts from frame 1. In frame 1 it will gotoAndStop to the current skin automatically. So you dont need to do anything else.
Put your animation movieclips not in the timeline, but in the same single frame. Name each animation lke "stand", "jump" etc. Switch animations not by gotoAndStop, but by making selected animation visible and others invisible. The skin would have to be set once for each skin change.

Related

Cocos2D-X nodes loading in wrong positions have RemoveAllChildren()

I'm only using one scene object for my entire 2d sidescrolling platformer with two layers: There is a hudLayer for the controller, and the gameLayer for everything in the game.
When I control my character to walk into a door (sprite with a physicsBody that has a callback that takes me to the next level), the next level loads by these instructions:
remove all event listeners
stop all actions for both layers (the gameLayer typically has been doing a Follow action that follows the player, and is limited to the size of the backgroundSpriteNode)
remove all children from the gameLayer
set gameLayer position to Vec2(0,0)
load level 2 (a complete copy of what level 1 should look like... load the background sprite to gameLayer, playerSprite, add event listeners)
add event listeners back to the hudLayer
The only problem is that for whatever reason, the level only partially loads the way it should... For instance, coins appear in the spots they should, but platforms don't seem to. Neither that or the player, or other characters. Not sure really why these objects are specifically not loading in the correct positions, but maybe someone who has experienced this sort of problem before can help?
Most nodes (such as platforms) are appearing below the bottom of the screen when they should be fully shown at the bottom of the screen. Characters are appearing down there too when they should be appearing above the platforms.
P.S. After testing a bit more, I've come to realize that SOMETIMES some of the platforms are loading in the correct positions, but sometimes they aren't. Not sure why. A fix for this may be to load things one at a time with a delay between them. But I'd rather not have to put delays and just figure out what the deal is.
Figured it out: for whatever reason, I needed to add a delay in there. I think for whatever reason, by calling removeAllChildren(), something got messed up with the positions. It was my suspicion since SOME of the time, parts of the next level would load, but only some parts, and only sometimes.
Here's the code I added to run an action on the gameLayer once all children were removed, etc...
runAction(Sequence::createWithTwoActions(DelayTime::create(0.1), CallFunc::create( [&] ( ) {
loadLevel2(this);
})));

How can I send control of the playhead back to a parent timeline from a movieclip using createJS?

I'm making a multi-part introductory animation for a game. It works when I preview in Flash, but not when I export using the CreateJS toolkit to get HTML5 output.
On my scene's timeline, I have this.stop(); on the first frame, as well as a movie clip that contains several seconds of animation (and which may have to change in length as we develop).
My second frame is labeled "sc2" and I would like the animation to pick up there when the first movieclip is finished. To accomplish this, I went into the first movie clip and put this on the last frame:
_parent.gotoAndPlay("sc2");
This works fine in Flash, but perhaps I can't use the _parent object in JS? Is there an alternative way to access the flow of control or another way to accomplish this? My goal is to avoid doing animation on the main timeline and to control the flow by scripting there so that if the length of the individual sections of animation changes, I don't have to change the start and end frames of the various sections in several places.

Actions are assigned to wrong keyframe in Flash CS5

I am working with Flash CS5, and haven't been using Flash since the age of AS2.
I have a movieclip symbol in which I have an Actions layer, in which I have a keyframe in frame 1 and one in frame 20. Each of these should have a stop(); action assigned, but when i select the one in frame 20 and start writing, the little "a" appears on frame 1 of another layer. When I make a keyframe in frame 20 of that layer and try to assign actions it yields the same result. They appear on frame 1.
Is this a bug or am I missing some vital information about AS3?
AS3 is in fact more picky (for good reason) about how the code is implemented.
You need to select the keyframe you are intending to put code into, and then use your actions window. And as Markus commented, make sure you're using keyframes, and not empty frames. If you try to write code into an empty frame, it will default it to the most recent keyframe.
Furthermore, If you have multiple layers and say, your code is on layer2 and your objects on layer1, if you have keyframes on layer1, and intend to enter code at frame 20, but your layer selection is layer2, it will yield the same result described above. You'll have to make sure the correct layer is active. The actions window will only display the code from the current layer's most recent keyframe.
Hope that clears some things up!

Getting started with a simple Flash game

I'd like to make a simple game where "discs" fall from the top of the screen and the user must catch them. I have a MovieClip which I want to resize to one of three randomly chosen sizes.
As I see it, there are four things that must be done.
Create and size the MovieClip
Position the MovieClip
Make the MovieClip fall
Determine when it is finished "falling" and see if the user has "caught" it.
My question is: How do I create, size and position the MovieClip? I've given it an identifier of "disc". Now what? Do I make an ENTER_FRAME event and do my creation there? How do I move the disc downwards? Do I use tweens, something else?
I'm primarily asking this as a sanity check.
I would use some kind of factory class that would be responsible for dropping random discs from the top of the stage.
Beside what you correctly mentioned, you will also need to:
define if the speed for falling is constant or not, you may need some acceleration tween. To move the objects downwards, you could use a native tween method, you will need to apply it to every disk that gets dropped.
define where the disk will start falling, it can be random or always from the same place.
you can find out if two objects are colliding using the AS3 hitTestObject method which belongs to the DisplayObject class.
you factory class could have a start() and stop() method. Once start() is fired, an infinite or ENTER_FRAME loop is started and disks start falling. If you want to create disks at a specific rate you could combine your loop with a timer to run code on a defined interval. For instance, every 3 seconds create 10 disks (using main loop) and drop them onto the stage.
Assuming that you have MovieClips named 'disc' & 'userHand' exported into actionscript, I'll summarize it in the foll way:
Generate the no of discs & Randomize their location. Start with something like:
var n:int = 30; //Total no of Discs
for(i:int=0;i<n;i++)
{
var mc:disc = new disc();
mc.x=Math.random()*stage.width(); //to scatter the discs across the stage
mc.y=-mc.height; //initially hide out a disc
addchild(mc);
}
Also add the Movie clip 'userhand' to stage.
Add enterframe handler function.
Fill in the enterframe handler to update (keep increasing) the y position of each disc.
Use hitTestObject() in the enterframe handler to determine hit among each 'disc' & 'userHand'.
Reset position of all the 'disc' clips which fall off the screen to random positions as initially.
You might want to look at programming particles.
http://r3dux.org/2010/01/actionscript-3-0-particle-systems-3-rain-effect/
At a very high level what you would do is.
You need to create a disc class.
You could give this class some variable properties like width, height,x etc.
In an enter frame in your main class you would add an enterframe function that creates new instances of disc passing it random values for each property.
Each instance of disc could also have its own entframe which increases its y position until it reaches the bottom of the screen. The disc would then remove its self from the stage. You could use an easing function passing it a random number to determine the rate at which its falls.
Say if its y position is greater than the stage height, remove the disc. If the user catches it (using a hit test maybe) also remove it.
It really recommend having a look at that link I posted.

symbols placed on the timeline become undefined if stepping backwards

I am using the frames in the timeline of a .swf as pages in a flash app. The user can advance to the next page by clicking a button that takes her to the next frame. Similarly, it is possible to navigate to the previous frame/page as well.
Most of the content is placed on the stage (i.e. created by dragging an instance of a library symbol to the stage) but properties of those instances, such as .visible might be changed via actionscript. Also, some objects are loaded from external flash files and displayed programmatically with addChild / addChildAt.
The problem is, if I am on Frame N+1 and there is an object displayed on the stage programmatically (i.e. with addChild, not by having it placed on the stage) and navigate to Frame N where there is an object that is placed on the stage (i.e. dragged from the library),
then the instance of that object is undefined/null and throws an error if I try to set its properties (like .visible).
The error does not occur if I am moving to the NEXT frame, only if I am moving to the PREVIOUS one. Therefore I assume that some kind of initialization is not getting called while going one frame back.
I was also thinking that the objects would just not "live" to the next timeframe, that is, their value would be lost and re-initialized because of scope, but if there is no dynamically created object on the stage, I can navigate back and forth just fine.
Is there a way to ensure that the objects created on the stage do not disappear while navigating back to the previous frame?
The first, and more useful, part of the answer is this: timeline keyframes and scripts can give conflicting information about display objects - whether they should exist, where they should be, and so on. For example, when you add an item by playing into its frame, and then delete it with script, and then play into its frame again. When this happens, there's no unambiguously correct thing for Flash to do, so it tends to be unpredictable. I believe what generally happens is that once you fiddle with a given object via script, it's considered to no longer pay attention to the timeline - but your mileage will vary.
Having said that, the reason things are different when you play backwards is the second and more arcane part of the answer. Internally Flash functions differently when seeking forward and backwards on the timeline. Flash internally treats keyframes as changes to be applied in the forward direction, so as you play forward, it applies those changes in sequence. When you move backwards, however, from frame N+X to frame N, it doesn't scan through the intervening X frames reversing those changes - it jumps back to frame 1 and fast-forwards along to frame N. Normally, it amounts to the same thing and you don't need to worry about it, but when you get into the twitchy area where scripts and the timeline have a different idea of what should be on the stage, you're liable to see things behave differently depending on which way you jump (as you are now).
The super-short version is, for things to work predictably, try to ensure that any given object gets added, updated, and removed the same way - either all via script, or all via the timeline. When that seems impossible, fiddle with your content structure - usually, the best solution is to change your object into two nested ones, so that the things you want to do with script occur one level higher or lower than the things you want to do with the timeline.
I'm not sure I got your question right, but as3 does not instantiate elements on the timeline as soon as you gotoAndSomething, but later that frame.
That is, you can't
this.gotoAndPlay(10)
this.elementOnTimelineFrame10.DoSomething()
without errors.
I remember using this chunk of code in the past to work around this problem. It uses the Stage.Invalidate() function to wait for an Event.RENDER before trying to access and children, more info (although vague as hell) is here
private function init():void
{
stage.addEventListener(Event.RENDER, stage_renderHandler);
}
private function stage_renderHandler(evt:Event):void
{
// Run your code here
updateChildren();
}
private function enterFrameHandler(evt:Event):void
{
// triggers the RENDER event
stage.invalidate();
}
This also might me very costly (performance wise). I would strongly advise against dynamically adding/removing objects to an existing timeline, is there any way in which you can place an empty Sprite above the timeline animation and use that for all your dynamic content?
Hope this helps