programming character animation with AS3 - actionscript-3

I am programing a little game in flash,
and I am programing the animations of the character,
the character body is made of different objects, for example, I have an movie clip that is the head, Torso, Arms, Hands, Legs, etc,.
and I am making the animations with AS3, for example:
(only programed 1 leg movement)
public function walk(){
if(_etapa==5){
_etapa=1;
}
var etapa=_etapa;
switch(etapa){
case 1:
Animar(musloDer,22,RetomarAnimacion);
break;
case 2:
Animar(musloDer,0,RetomarAnimacion);
break;
case 3:
Animar(musloDer,-22,RetomarAnimacion);
break;
case 4:
Animar(musloDer,0,RetomarAnimacion);
break;
}
}
the walk animation haves 4 stages,
then I have the Animar function:
private function Animar(parte, valor, callback){
trace(direccion);
if(direccion=="SE" || direccion=="NO"){
valor=valor+45
valor=Math.abs(valor);
if(valor>180){
valor=360-valor;
}
if(valor<0){
}
_scaleY = (1-(valor)/90);
trace(_scaleY);
_rotation = 0;
}
else if(direccion=="N" || direccion=="S"){
_scaleY = .5;
_rotation = -valor;
}
_etapa++;
TweenLite.to(parte, 2.5, {rotation:_rotation, scaleY:_scaleY,ease:Linear.easeNone, onComplete:caminar});
}
that function moves the body part to the degrees that I want, It also decides what to do, the character can walk in 8 directions, front back, left right, and diagonals, and the character is showed from isometric view,. So the animation function decides what to do depending on the direction that the character is.
for example, if the character is walking right, viewed from lateral view, in that case rotating one leg 22 degrees is very simple, just LeftLeg.rotation = 22; great, but when the character is facing the camera, the leg rotation is not a rotation, is a change in scaleY property, for example, since the body is in isometric view, the scaleY of 1 leg in normal position is .5 (or 50%) when the leg is at 45º the scaleY is 1, and so on.
But I have a problem, because, for example if the LEG initial position is 0º (scaleY = 0.5) and I want to move it to 90º, 90º would be also scaleY = 0.5, so my function does nothing, it tweens from .5 to .5 (nothing), it should go from .5 to 0 and then to .5 again.
I hope someone understands my problem.
best,
Alvaro

Not sure if this is of any use...
http://dragonbones.github.com/index.html#.ULZvXqUq54F

Related

As3: Not move through objects? How is the logic behind this?

I have a function where I can control my character.
I also have a movieclip on the stage called assassin_table.
I want to make it so that the character can't move in the table, a.k.a make the table work like a wall.
I have this code:
if(!this.hitTestObject(_root.assassin_table))
{
if(upKeyDown)
{
gotoAndStop(4);
y-=Math.cos(rotation/-180*Math.PI)*(mainSpeed +7);
x-=Math.sin(rotation/-180*Math.PI)*(mainSpeed +7);
}
if(!upKeyDown)
{
gotoAndStop(3);
}
}
However, if I touch the table, then I can't move at all.
I know it's because if(!this.hitTestObject(_root.assassin_table)), but I don't understand the logic behind NOT moving through objects. I'd much rather have a near pixel-perfect collision detection system, but since it's so hard to find any good info online which isn't confusing, I'll stick with hitTestObject for now :)
EDIT: Tried something, didn't really work that well.
if(!_root.assassinDead && !teleporting && this.currentFrame != 5)
{
if(this.hitbox.hitTestObject(_root.assassin_table))
{
_root.assassin_table.gotoAndStop(2);
if(this.x > _root.assassin_table.x)
{
trace("Can't move right");
canMoveRight = false;
}
if(this.x <_root.assassin_table.x)
{
trace("Can't move left");
canMoveLeft = false;
}
if(this.y > _root.assassin_table.y)
{
trace("Can't move up");
canMoveUp = false;
}
if(this.y < _root.assassin_table.y)
{
trace("Can't move down");
canMoveDown = false;
}
}
else
{
canMoveRight = true;
canMoveLeft = true;
canMoveUp = true;
canMoveDown = true;
}
}
This causes me to sometimes be able to walk through the table. I figure it's because my character can move in essentially every possible angle (since he's always facing the mouse and there are no tiles/grids).
How would I make it so that it would work with the advanced movement I have?
Moving up runs this:
y-=Math.cos(rotation/-180*Math.PI)*(mainSpeed +7);
x-=Math.sin(rotation/-180*Math.PI)*(mainSpeed +7);
And the rotation is decided by this:
this.rotation = Math.atan2((stage.mouseY - this.y), (stage.mouseX - this.x)) * 180/ Math.PI + 90;
You should seperate your hittest functions for four different moving directions.
I mean, you shouldn't use this "hitTestObject" stuff, that only returns a boolean value "true" or "false", and that's not going to work for you.
You need a function like "testPoint(player.x, player.y);" and returns the object at the given position, so you can implement it for your game like that
if (upKeyDown && testPoint(player.x, player.y - mainSpeed +7) == null) y-=Math.cos(rotation/-180*Math.PI)*(mainSpeed +7);
player.y - mainSpeed +7 // that checks for plus mainSpeed+7 because of trying to stop your player before it get stack inside object
Basically your logic flow should be like this:
Sample input (key press)
Move character
Check for collisions
If collision then move character to it's "outside" the object that it's touching
In your particular case, if your character is on the left of the table, and you're moving right, then first things first, you move your character. At your new position, check for any collisions. If you have a collection, then because you were moving from the left, we want to be on the left of the object that we colliding with, in this case the table, so we position our character to the left of the table.
The first part of that (checking if the character has hit the table) is called collision detection. Moving the character so that it's outside the bounds of the table is called collision response. Collision detection is a pretty big field and really depends on the type of game you're making; you can do grid-based, hit-test based (if you don't have a ton of objects), physics-based, math-based etc. Collision response can be anything and everything, depending on how you want to react to a collision - you can destroy the object (balloon on a spike), change its speed (character running through mud), bounce off it (ball off wall), or stop any further movement (character against wall).
To make things a bit easier:
Separate your systems - your input shouldn't be dependant on your collision for example. If the up key is down, just register that fact - what you do with it later (make your character move) is up to you
Separate your objects position in memory from its position on screen - this will let you move it around, react to collisions etc, and only when everything is good, update the graphics (stops things like graphics entering a wall only to jump out the next frame)
Solve for one axis at a time - e.g. collide on the x axis first, then the y
Pixel perfect collision is rarely needed :) Non-rotated boxes and circles will work a lot more than you'd think
Somewhat related - the shape of your object doesn't have to be the shape that you're colliding with - e.g. your table collision shape could just be a box; your character collision shape could just be a circle
Update
This causes me to sometimes be able to walk through the table
Assuming that we're going to collide our character and table as boxes, you need to take into account their sizes - i.e. we don't just compare the x values, but the right side of our character box against the left side of the table box etc. Something like:
// assumes objects have their anchor point in the top left - if its
// not the case, adjust as you see fit
if( char.x + char.width > table.x ) // right side of the char is overlapping the left side of the table
canMoveRight = false;
else if( char.x < table.x + table.width ) // left side of char vs right side of table
canMoveLeft = false;
// etc
When working with collisions, it's always nice to see the actual boxes, for debugging. Something like this:
var debug:Shape = new Shape;
debug.addEventListener( Event.ENTER_FRAME, function():void
{
debug.graphics.clear();
debug.graphics.lineStyle( 2.0, 0xff0000 );
// char (again assuming anchor point is top left)
debug.graphics.drawRect( char.x, char.y, char.width, char.height );
// table
debug.graphics.drawRect( table.x, table.y, table.width, table.height );
});
// add our debug. NOTE: make sure it's above everything
stage.addChild( debug );

How do you add gravity with wall collision in actionscript 3?

I created a script that allows your character to move around with wall collision for every direction. How do I add gravity to it on the y-axis and still have it work the same way (like a platform game)?
I tried something like char.y-- when it hits the platform if it were just the floor, but all that does is slowly bring char up no matter where it is in the object, and once it reaches the top, it shakes like crazy.
A preview of the stage: http://puu.sh/63Y1g.png
//WALL COLLISION
if(walls.hitTestPoint(char.x-(char.width/2),char.y,true) && Key.isDown(Key.A)){
char.x+=5
}
if(walls.hitTestPoint(char.x+(char.width/2),char.y,true) && Key.isDown(Key.D)){
char.x-=5
}
if(walls.hitTestPoint(char.x,char.y-(char.height/2),true) && Key.isDown(Key.W)){
char.y+=5
}
if(walls.hitTestPoint(char.x,char.y+(char.height/2),true) && Key.isDown(Key.S)){
char.y-=5
}
//CHARACTER CONTROLS
char.x+=0;
if(Key.isDown(Key.A)){
char.x+=-5;
speed=-5;
}
if(Key.isDown(Key.D)){
char.x+=5;
speed=5;
}
if(Key.isDown(Key.W)){
char.y+=-5;
speed=5;
}
if(Key.isDown(Key.S)){
char.y+=5;
speed=5;
}
To do this really simply, something like this would suffice:
// Check if there is a floor below the char
if (!floor.hitTestPoint(char.x, char.y + (char.height/2) + 2 , true))
{
// If not, apply Gravity
char.y += 5
}
This will only apply gravity if there is no floor 2 pixels directly below the char (this will stop the jittering, feel free to try with different pixel offsets that might work better in your case), I wasn't sure how you were differentiating between the walls and floor, so replace 'floor' with how you are doing it.
===============
If you would like to add realistic accelerating gravity, you will need to add a variable to track the current vertical speed of the char, then add to that each step
eg. Each step do this instead of just adding to y
// Apply gravity
vSpeed += 5
char.y += vSpeed
But if you are going to do that you might as well start getting into vectors and more complex things, there's a lot of materials on the net for this.

How to detect if the area was 100% painted in as3

I´m making a game that simulates an industry of pan, and one of the process is Painting.
What I want to do is to let the player paint the pan, but i don´t want it to be easy using FILL, i want that the player paint the pan with an brush and then the game detects if all the area was painted and let the player advance.
For the painting i intend to use that library: http://www.nocircleno.com/graffiti/
But i have no idea how to detect if all the area was painted. Can someone show me some way of doing that?
One of the ways would be - you make a shielding BitmapData that has transparency and is opaque in place which you need your player to paint. (Color it as needed, but make sure the color is fully opaque). Then gather histogram() then query alpha vector for 255th value, this will be the initial value for zero percent filled. These range from 0-255, so you can't use 100 or any other fixed value. Then, while the player is painting, you draw the brush over that BitmapData with blendMode parameter set to BlendMode.ERASE, this will net your BitmapData to gain transparency where the brush was drawn. After your player finishes drawing by any means (say, the paint is used up), you run another histogram() over the BitmapData, and query the 255th value of alpha channel vector. 0 means the bitmap is fully transparent (or at least, only a small amount of pixels is left opaque), thus you can count a zero as 100% fill, for anything greater use the proportion.
var bd:BitmapData=new BitmapData(w,h,true,0x0); // fully transparent initial bitmap
bd.draw(yourPaintBase); // a shape that designates area to be painted. Must be fully opaque
var bm:Bitmap=new Bitmap(bd);
// position it as needed, so the area which should be painted is aligned to wherever you need
addChild(bm);
addEventListener(Event.ENTER_FRAME,doPaint);
var baseValue:int=bd.histogram()[3][255]; // Vector #3 contains alpha, #255 contains
// percentage of those pixels that have alpha of 255 = fully opaque
function doPaint(e:Event):void {
if (!areWePainting) return;
var sh:Shape=getBrush(); // shuold return an existing Shape object reference for performance
sh.x=e.localX;
sh.y=e.localY; // we are drawing where the mouse is
bd.draw(sh,null,null,BlendMode.ERASE);
decreasePaint(); // we have used some paint
if (noMorePaint()) {
e.target.removeEventListener(Event.ENTER_FRAME,doPaint);
var endValue:int=Math.floor(100*(1-bd.histogram()[3][255]/baseValue));
// aligning to percentage. This is the value you seek
reportFilledPercentage(endValue);
}
}
You can iterate over the pixels on your BitmapData and use getPixel() to check if the color of all those pixels is not white. If a white one is found, the image is not fully painted.
Something like this:
function containsWhite(bitmapData:BitmapData):Boolean
{
for(var c:int = 0; c < bitmapData.width; c++)
{
for(var r:int = 0; r < bitmapData.height; r++)
{
// Check if pixel is white.
if(bitmapData.getPixel(c, r) >= 0xFFFFFF)
{
return true;
}
}
}
return false;
}
Your essentially dealing with a collision detection problem. From looking at their API you could try something like a for loop with getColorAtPoint and try to determine they have drawn at each pixel.
If all else fails look into collision between the objects the library generates using the .hitTestObject method of an object.
See this: http://sierakowski.eu/list-of-tips/39-collision-detection-methods-hittest-and-hittestobject-alternatives.html
And this to see how someone handles collision with pixels: http://www.emanueleferonato.com/2010/08/05/worms-like-destructible-terrain-in-flash-part-2/

Correct formula to "invert" rotation (in degrees) in AS3?

I have my player movieclip on the stage, and when the mouse is clicked a bullet is fired and projected at the correct angle to point itself at the mouse location. I also want a "mirroring" enemy, that fires at the complete opposite direction when the player does.
For example, when the player shoots upwards, the enemy should shoot down. Likewise, shooting to the right will cause the enemy to shoot to the left.
Is there a formula to convert the rotation in degrees to it's complete opposite?
Using Matrix is a very simple and exact solution. just multiple a or c with -1 ( to flip vertical and horizontal ).
Sample code:
var _tmpMatrix:Matrix = sprite.transform.matrix;
_tmpMatrix.a *= -1;
if ( _tmpMatrix.a < 0 ) {
_tmpMatrix.tx = sprite.width + sprite.x;
} else {
_tmpMatrix.tx = 0;
}
sprite.transform.matrix = _tmpMatrix;
Wouldn't adding or subtracting 180 degrees point in the opposite direction?
or
obj.scaleX = -1;
will do the same thing. =)

Actionscript 3 smooth Sprite movement

I move a Sprite across the screen using the keyboard, at a rate of 10 pixels/ENTER_FRAME event fire. The issue is that, when it moves, you can see it being "redrawn" every 10 pixels, which makes it hard to look at. I haven't seen this in other Flash games.
If you look closely, you can also see this here (although at a much lower scale): http://kirill-poletaev.blogspot.com/2010/07/smooth-character-movement-using-as3.html
I want to get rid of that effect, any ideas?
If the player is at a distance of ... from the screen edge, it stops moving (by moving in the opposite direction), and the BG starts scrolling (the same visual effect can be seen).
Music in playing in the background, a minimap is updated with the player's position.
private function updater(e:Event):void
{
if(up && GlobalVars.vars.upPossible)
{
cont.y-=unit;
setu(); // Player graphics state
}
else if(down && GlobalVars.vars.downPossible)
{
cont.y+=unit;
setd(); // Player graphics state
}
else if(left && GlobalVars.vars.leftPossible)
{
cont.x-=unit;
setl(); // Player graphics state
}
else if(right && GlobalVars.vars.rightPossible)
{
cont.x+=unit;
setr(); // Player graphics state
}
else
{
ups.visible=false; downs.visible=false; rights.visible=false;
lefts.visible=false; normals.visible=true; // Player graphics state
GlobalVars.vars.scXr=0; GlobalVars.vars.scYu=0; GlobalVars.vars.scXl=0;
GlobalVars.vars.scYd=0; cont.x=int(cont.x); cont.y=int(cont.y); //Someone from the Kongregate.com forums suggested this, no visible effect
}
if((cont.x=GlobalVars.vars.maxX))
{
if(cont.x=GlobalVars.vars.maxX && right && GlobalVars.vars.canScrollR) GlobalVars.vars.scXr=1, cont.x-=unit, setr();
}
else GlobalVars.vars.scXl=0, GlobalVars.vars.scXr=0; //BG Scrolling
if((cont.y=stage.stageHeight*7.3/10))
{
if(cont.y=stage.stageHeight*7.3/10 && down && GlobalVars.vars.canScrollD) GlobalVars.vars.scYd=1, cont.y-=unit, setd();
}
else GlobalVars.vars.scYu=0, GlobalVars.vars.scYd=0; //BG Scrolling
if(cont.y>=stage.stageHeight*7.3/10 && cont.x>=GlobalVars.vars.maxX) GlobalVars.vars.minimapTr=1;
else GlobalVars.vars.minimapTr=0;
if(cont.y-unitGlobalVars.vars.sH-cont.height-3.1) GlobalVars.vars.downPossible=false;
else GlobalVars.vars.downPossible=true;
if(cont.x-unitGlobalVars.vars.sW-cont.width-3.5) GlobalVars.vars.rightPossible=false;
else GlobalVars.vars.rightPossible=true;
GlobalVars.vars.plX=cont.x; //for minimap
GlobalVars.vars.plY=cont.y;
Also, key listener functions:
stage.addEventListener(KeyboardEvent.KEY_DOWN, keyD, false, 0, true);
stage.addEventListener(KeyboardEvent.KEY_UP, keyU, false, 0, true);
private function keyD(e:KeyboardEvent):void
{
if(e.keyCode==37 || e.keyCode==65) left=true;
if(e.keyCode==38 || e.keyCode==87) up=true;
if(e.keyCode==39 || e.keyCode==68) right=true;
if(e.keyCode==40 || e.keyCode==83) down=true;
}
private function keyU(e:KeyboardEvent):void
{
if(e.keyCode==37 || e.keyCode==65) left=false;
if(e.keyCode==38 || e.keyCode==87) up=false;
if(e.keyCode==39 || e.keyCode==68) right=false;
if(e.keyCode==40 || e.keyCode==83) down=false;
}
I've encountered some improvement by increasing the FPS to 120, and decreasing the step to 4, but it's still there. I'm fairly sure it's not a performance issue, but, rather, a movement method fault.
A couple of suggestions:
Increase the frame rate
Use a tween library (e.g. GTween) with some easing effect.
Basically, if you want the object to jump 10px to the right, don't just move it right away, let it animate to its new position with some easing effect. Additionally, if the object is still moving and the key is pressed again, you probably want to accelerate the movement a bit (but only up to a point!).
I think you're asking how to make it more smooth. Well:
If you change the movement 2px per frame, then you'll have a much smoother experience. However, the movement will see very slow. To combat this, you just increase the frame rate of your swf. I believe the maximum is 120 frames, but that should be more than enough.
I love Tweenlite, it's great for pre-defined effects. It isn't for the core of your game though so I'd use Tweenlite for UI effects but barely for ingame movements responding to user controls.
I think around 24-30 fps for games is great, that's how I set all my games. I think abit you're picky, but perhaps what you're missing is a bit of edge: a smooth start and end for the movement. In most platformers I have a similar mechanic:
You want the start of the movement to start slow, reach the normal (max) speed you define and when the player lets go of a button, it slows down back to 0. This means you need to check the speed on enter frame regardless of the button triggers (they increment the speed but aren't the only condition to change speed and move the object)
enterframe loop:
//check for each direction:
if up, y_speed++
else if down, y_speed--
else if right, x_speed++
else if left, x_speed--
else // perform a decrease:
y_speed *= .8
x_speed *= .8
// make sure we aren't past the max speed
if x_speed>max_x_speed
x_speed = max_x_speed
etc. else other direction and y_speed
// now apply the movement
object.x += x_speed
object.y += y_speed
// remove the enterframe for better performance
if math.abs(x_speed)<.1 && math.abs(y_speed)<.1
// remove enterframe for movement here, add it again next time we know we have movement (button downs related to the movement, etc.)
I would implement this in your code but your code is too crowded. Anyway otherwise, most games with these simple controls are working just like your application does.