AS3: Sprite following a Path in high speed - actionscript-3

First of all sorry for some english mistakes. Portuguese is my first language(I am from Brazil)
Im trying to make a space game from scratch in AS3 and the way to move the ship is like in the game Air Traffic Chief.
I succeed at some point. But when the ship is very fast it start to shake and its not very smooth and clean as I want.
Here is what i have done: http://megaswf.com/s/2437744
As the code is very big so I pasted in pastebin: pastebin.com/1YVZ23WX
I also wrote some english documentation.
This is my first game and my first post here. I really hope you guys can help me.
Thanks in advance.
Edit:
As the code is very big i will try to clarify here.
When the user MouseDown and MouseMove the ship every coordinate is passed to an array.
When the user MouseUP this array is passed to a function that fix the array.
For example: If the distance between two coordinates is greater than 5px, the function creates a coordinate in the middle of the two coordinates.
if I take this function off the problem seen to be solved. But if the user move the mouse very slow it still happens. It also creates a problem that i was trying to solve with that function. as the distance of the two coordinates are very big when the ship arrive in one coordinate most of the line path disappear.
I uploaded a version without the function that fixes the array. http://megaswf.com/s/2437775
I think there is 2 ways for solving this problem
1- Try to fix the noise in the array of coordinates 2- Take off the function that create an coordinate between two points and try to fix the problem of the line path disappear.
Here is the 2 important functions:
this function moves the ship
private function mover():void
{
if (caminhoCoords[0]!=null) // caminhoCoords is the array that contain the path
{
var angulo:Number = Math.atan2(this.y - caminhoCoords[0][1], this.x - caminhoCoords[0][0]);
this.rotation = angulo / (Math.PI / 180);
this.x = this.x - velocidade * (Math.cos(angulo));
this.y = this.y - velocidade * (Math.sin(angulo));
var testex:Number = Math.abs(this.x - caminhoCoords[0][0]); //test to see the distance between the ship and the position in the array
var testey:Number = Math.abs(this.y - caminhoCoords[0][1]);
if (testey<=velocidade+2 && testex<=velocidade+2) // if is velocidade+2 close then go to the next coordnate
{
caminhoCoords.shift();
}
}
}
This function draw the line:
private function desenhaCaminho():void //draw the black Path
{
if(caminhoCoords.length>=1)
{
caminho.graphics.clear();
caminho.graphics.lineStyle(1, 0x000000, 1,true);
caminho.graphics.moveTo(caminhoCoords[0][0],caminhoCoords[0][1]);
for (var i:int = 1; i < caminhoCoords.length; i++)
{
caminho.graphics.lineTo(caminhoCoords[i][0], caminhoCoords[i][1]);
}
}else
{
caminho.graphics.clear();
}
}
Every time the ship arrive in one coordinate is take that coordinate off the array and redraw the array.
Is there a better way of doing that?

I believe if you set your registration point of the plane to the centre and use .snapto(path), it will improve the action.

Judging from just the look of the stuttering, I would guess you need to smooth out the "line" a fair bit. It's probably picking up a lot of noise in the line, which is then translated into the rotation of the plane. Either smooth out the rotation/position of the plane, or the line itself.

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 );

AS3 create a trail of movieclips following each other

So, I'm trying to get a few movieclips to follow it's precursor and have the last one follow the mouse. The problem is I'm creating them from code instead of using the interface and, since I'm not an expert, I can't get them to work.
All I have in the library is a MovieClip(linkage:"LETRA") which contains a textField inside(instance name:"myTextField").
Here's what I have:
import flashx.textLayout.operations.MoveChildrenOperation;
import flash.display.MovieClip;
import flash.events.Event;
//this are the letters that will be following the mouse
var phrase:Array = ["H","a","c","e","r"," ","u","n"," ","p","u","e","n","t","e"];
//variable to spread them instead of creating them one of top of each other
var posXLetter:Number = 0;
//looping through my array
for (var i:Number = 0; i < phrase.length; i++)
{
//create an instance of the LETRA movieclip which contains a text field inside
var newLetter:MovieClip = new LETRA();
//assing a letter to that text field matching the position of the phrase array
newLetter.myTextField.text = phrase[i];
//assign X position to the letter I'm going to add
newLetter.x = posXLetter;
//add properties for storing the letter position
var distx:Number = 0;
var disty:Number = 0;
//add the listener and the function which will move each letter
newLetter.addEventListener(Event.ENTER_FRAME, moveLetter);
function moveLetter(e:Event){
distx = newLetter.x - mouseX;
disty = newLetter.y - mouseY;
newLetter.x -= distx / 10;
newLetter.y -= disty / 10;
}
//add each letter to the stage
stage.addChild(newLetter);
//increment the next letter's x position
posXLetter += 9;
}
With that code, only one letter is following the mouse (the "E") and the rest are staying where I added them using addChild and the posXLetter variable.
Also, I'm trying to get it to behave more like a trail, so if I move up, the letters will lag beneath me; if I move to the left, the letters will lag to my right but I think that with my current approach they will either A) move all together to the same spot or B) always hang to the left of the cursor.
Thanks for any possible help.
This is a kind of motion called Inverse Kinematics and it is a quite popular way to make rag dolls in games. It uses a design pattern called the Composite Pattern where one object adds another object as a child of its and then when it's update() function if called, it calls all of its (usually one) child's update() functions. The most common example of this is of a snake. The snake's head follows your mouse, and the rest of the snake's body pieces move with the snake, and it looks immensely realistic. This exact example is explained and build here although it does not include joint restrictions at all.
This example is in the middle of a book, and so may be hard to start reading, but if your somewhat familiar with design patterns and/or have some intermediate experience with programming, then i'm sure you can understand it. I advise that you, after reading and understanding the example, scratch what you have now because it is not very elegant coding. You may feel that this example uses too many classes, but trust me, its worth it as it allows you to very easily edit your code, if you decide to change it in the future, with no drawbacks.
Also, i know that this snake is not what you want, but if you understand the concept then you can apply it to your own specific needs.
I hope this helps.
I think it is a scoping issue. You might need to modify your handler
function moveLetter(e:Event){
trace(e.target); //check if this is the right movie clip
distx = e.target.x - mouseX;
disty = e.target.y - mouseY;
e.target.x -= distx / 10;
e.target.y -= disty / 10;
}

Splicing elements out of a Vector in a loop which goes through the same Vector (ActionScript 3)

I'm making a simple game and have a Vector full of enemies in order to do hit-checking on them from my "laser" object (it's a space shmup). Every laser loops through the Vector and checks if it's occluding the hit circle. The problem lies in when one laser destroys an enemy, the rest of the lasers try to also check the same Vector, only to go out of bounds since the enemy's already been spliced out and it's changed the size of the Vector.
for each (var enemy:Enemy in enemies){
var distanceX = this.x - enemy.x;
var distanceY = this.y - enemy.y;
var distance = Math.sqrt( (distanceX*distanceX) + (distanceY*distanceY) );
if (distance <= enemy.hitRadius) {
enemy.removeEnemy();
enemies.splice(enemies.indexOf(enemy),enemies.indexOf(enemy));
}
}
How would I go about collecting the index of individual elements in the Vector to be deleted, then only deleting them when every Laser object is finished its checking?
edit: here's my removeEnemy() function from my Enemy class, just in case:
public function removeEnemy(){
removeEventListener(Event.ENTER_FRAME, move);
parent.removeChild(this);
trace("removed Enemy", enemies.indexOf(this));
}
edit edit: I'm also getting a null reference pointer error to "parent" in my removeEnemy() function... but only sometimes. I have a feeling that if I fix one of these two problems, the other will also be fixed but I'm not sure.
I fixed it! The problem was actually in how I used the "splice()" method. Turns out that the second parameter isn't the end index of where to stop splicing, it's the number of elements to be spliced. So when I was trying to splice element 0, I wasn't splicing anything, and when I was trying to splice element 3, I was also splicing 4 and 5. I feel like such a dunce for not reading the API right and wasting a couple hours on this. Thanks to everyone who commented-- you guys helped me rule out what I thought the problem was.

Collision Check

I'm trying to test for collision between bullets within a vector and enemies within another vector. I can access the data fine, but the problem lies in the actual detection of the collision. Funny thing is, it works perfectly when I use hitTestObject, so I don't see why this shouldn't be working. I might be overlooking something, but I'm having a hard time finding it.
Code:
for each(var i in eManager.enemyArray)
{
for each(var j in gManager.gunVector)
{
for each (var k in j.bManager.bulletVector)
{
// Basically using Pythagorean's theorem but with both sides squared
// to minimize any process-heavy operations
if(((i.x - k.x)*(i.x - k.x))+((i.y - k.y)*(i.y - k.y)) <= 4)
{
// Note that when this happens, the enemy dies
i.kill = true;
}
}
}
}
Whoops. Turns out that I was testing the collision between 2 points, not between a point and a shape. My mistake.

Actionscript 3.0 problem - Why is it giving an error even when I've re-checked the code?

I'm using Adobe Flash CS4 professional for this Actionscript 3.0 project
(http://tutorials.flashmymind.com/2009/02/rotating-menu-via-actionscript-3/)
I even tried following the suggestions in the comments as well but this error always shows up:
TypeError: Error #1010: A term is undefined and has no properties.
at rotating_menu_fla::MainTimeline/frame1()
(for complete details, kindly click the link - http://i429.photobucket.com/albums/qq19/tsujzpie/screenshot_03.jpg)
I've been following every step of the tutorial but I'm stumped over the coding...
Here's the code by the way...
//Save the center coordinates of the stage
var centerX:Number=stage.stageWidth/2;
var centerY:Number=stage.stageHeight/2;
//The number of items we will have (feel free to change!)
var NUMBER_OF_ITEMS:uint=5;
//Radius of the menu circle (horizontal and vertical)
var radiusX:Number=200;
var radiusY:Number=100;
//Angle difference between the items (in radians)
var angleDifference:Number = Math.PI * (360 / NUMBER_OF_ITEMS) / 180;
//How fast a single circle moves (we calculate the speed
//according to the mouse position later on...)
var angleSpeed:Number=0;
//Scaling speed of a single circle
var scaleSpeed:Number=0.0002;
//This vector holds all the items
//(this could also be an array...)
var itemVector:Array = new Array ('1', '2', '3', '4','5');
//This loop creates the items and positions them
//on the stage
for (var i:uint = 0; i < NUMBER_OF_ITEMS; i++) {
//Create a new menu item
var item:Item = new Item();
//Get the angle for the item (we space the items evenly)
var startingAngle:Number=angleDifference*i;
//Set the x and y coordinates
item.x=centerX+radiusX*Math.cos(startingAngle);
item.y=centerY+radiusY*Math.sin(startingAngle);
//Save the starting angle of the item.
//(We have declared the Item class to be dynamic. Therefore,
//we can create new properties dynamically.)
item.angle=startingAngle;
//Add an item number to the item's text field
item.itemText.text=i.toString();
//Allow no mouse children
item.mouseChildren=false;
//Add the item to the vector
itemVector.push(item);
//Add the item to the stage
addChild(item);
}
//We use ENTER_FRAME to animate the items
addEventListener(Event.ENTER_FRAME, enterFrameHandler);
//This function is called in each frame
function enterFrameHandler(e:Event):void {
//Calculate the angle speed according to mouse position
angleSpeed = -(mouseX - centerX) / 5000;
//Loop through the vector
for (var i:uint = 0; i < NUMBER_OF_ITEMS; i++) {
//Save the item to a local variable
var item:Item=itemVector[i];
//Update the angle
item.angle+=angleSpeed;
//Set the new coordinates
item.x=centerX+radiusX*Math.cos(item.angle);
item.y=centerY+radiusY*Math.sin(item.angle);
//Calculate the vertical distance from centerY to the item
var dy:Number=centerY-item.y;
//Scale the item according to vertical distance
item.scaleY = (dy / radiusY)+2;
//Set the x scale to be the same as y scale
item.scaleX=item.scaleY;
//Adjust the alpha according to y scale
item.alpha=item.scaleY+1.1;
}
}
I find it odd - it may be that the code is right but I don't know if these steps has messed up the project...
3 - Convert the circle into a movie clip...
4 - In the “Item” movie clip, create a dynamic text field in the center of the circle (in a new layer).
5 - Set the text to align center. Type some number in the text field. Give the text field an instance name of “itemText”. Embed numerals...
6 - Remove the Item movie clip from the stage. We will create all the items dynamically via ActionScript 3.
I could've given more screenshots but since I'm a new user, I'm only allowed a max of two - and contrary to this tag, I'm not doing an Android app.
(I'll give you added info once any of you would reply to this question...)
I have to admit that the steps 3 to 6 is confusing and didn't made sense to me - especially with step six, when you have to remove the movie clip from the stage. For me, if I were to do that, what would the script work on then?
Any idea what I did wrong?
EDIT:
Thanks, I realized my mistake - thanks for the tip :-)
But now, I tried to modify the code of this tutorial a bit to have words appear inside the circles (like "Home", "About", etc....) like what I've typed in the line in the screenschot - http://i429.photobucket.com/albums/qq19/tsujzpie/modifiedlineincode_00.jpg
But inspite of what I believe are the appropriate changes I've done, an error showed up still - (kindly see here - http://i429.photobucket.com/albums/qq19/tsujzpie/newerrorincode_00.jpg)
Why is that happening? Before I forget, in what section of the code must I insert a line that will make a clicked button display the info corresponding to it? (that is, if I click on the "Contact" or any of the buttons a window will appear beneath the menu on the stage...)
That errors means ActionScript has no clue what an Item is. Make sure you've ticked Export for ActionScript on your Item MovieClip in library and that the class is named Item also.
Update:
The tutorial you're following mentions itemVector is a Vector of Item instances, not an array of Strings like you're code suggests. This why you're getting the error displayed in your screenshot. It means ActionScript can not convert an Item to a String.
An easy fix is to create another array for the menu item labels:
var itemVector:Array = [];// = new Array ('1', '2', '3', '4','5');
var itemLabels:Array = ["Home","About","Contact","Gallery"];
NUMBER_OF_ITEMS = itemLabels.length;
and in the for loop swap this item.itemText.text=i.toString(); for this tem.itemText.text=itemLabels[i];
It seems that you don't fully grasp the difference between variable types. I would suggest getting familiar with the basics of as3 before moving forward. Also paying more attention to your code and fully understanding code you use written by others will keep you out of trouble. However you will still encounter errors. You might find explanations for those errors on the Flash Error Database.
Regarding the Carosel tutorial, also have a look at this video. It might help explain things better.
The compiler can not find the Item class.
Go into your library and open up the properties for the "Item" MovieClip.
Verify that is is set for export to actionscript.