How does one track and use the coordinates of an object that is rotated on initialization?
Let's say I have a sword that is put on the stage in Main init(); and rotated (adjusted) so that it would look ok together with the character perspective. In another class however, I am making the sword rotate some more on a keypress timer event so to create a 'swing' animation.
All this is done through flashdevelop. I only used CS6 to create the symbols. And as this 'swing' is happening, I want to add another symbol onto the tip of the sword which is a collision point object. It's being added to the stage when the swing starts and removed after every swing. I want this object to follow the very tip of the sword, yet it seems like I can only achieve that it follows the coordinates of the original sword object, as if I hadn't initially modified the rotation of the said sword. I tried to implement GlobalToLocal() and LocalToGlobal() methods, but I don't think I fully understand what is happening with that.
I hope I'm being clear enough of what I'm trying to do. Thank you. This is the relevant code in question. The code is as was before I tried the two mentioned methods and the issue currently is exactly as described before that. Do I want any of those methods or am I just doing something else wrong?
Main initialization:
sword = new Sword();
sword.x = 53;
sword.y = 90;
addChild(sword);
sword.rotationZ = -150;
sword.rotationY = 25;
sword.rotationX = -15;
Coll_Point = new coll_point();
The class that deals with the swing has a method like this:
private function SwingTime(event:Event):void
{
Main.Coll_Point.x = Main.sword.x + Main.sword.width;
Main.Coll_Point.y = Main.sword.y + Main.sword.height;
Main.MazeNr1.addChild(Main.Coll_Point);
if (Main.sword.rotationZ > -330)
Main.sword.rotationZ -= 20;
if (Main.sword.rotationX < 15)
Main.sword.rotationX += 10;
if ((Main.sword.rotationZ == -330) && (Main.sword.rotationX == 15))
{
SwingTimer.stop();
SwingBckTimer.start();
}
}
Edit:
A more holistic version of the code:
public class Main extends MovieClip
{
public static var from_point:Point = null;
public static var to_point:Point = new Point();
public function Main():void
{
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
// Puts everything on the stage here.
private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
PlayerInst = new Dorf();
PlayerInst.x = 45;
PlayerInst.y = 51;
addChild(PlayerInst);
sword = new Sword();
sword.x = 53;
sword.y = 90;
sword.rotationZ = -150;
sword.rotationY = 25;
sword.rotationX = -15;
from_point = new Point (Main.sword.width, Main.sword.height);
to_point = sword.localToGlobal(from_point);
addChild(sword);
swordBD = new BitmapData(32, 32, true, 0x0000000000);
swordBD.draw(sword);
Coll_Point = new coll_point();
Coll_PointBD = new BitmapData(2, 2, true, 0x0000000000);
Coll_PointBD.draw(Coll_Point);
}
}
This is how the Main looks like and literally every single object instantiation is added onto the stage this way. Including collision points, background, characters, gradient fills of line of sight radius, etc. And the relevant symbol class goes somewhat like this:
public class Creature extends MovieClip
{
protected var Swing:Boolean;
private var SwingTimer:Timer = new Timer (5, 0);
private var SwingBckTimer:Timer = new Timer (150, 1);
// Constructor.
public function Creature()
{
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
// Initializer.
private function init(event:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
SwingTimer.addEventListener(TimerEvent.TIMER, SwingTime);
SwingBckTimer.addEventListener(TimerEvent.TIMER, SwingBack);
}
private function SwingAction():void
{
if (Swing == true)
{
SwingTimer.start();
}
}
private function SwingTime(event:Event):void
{
Main.Coll_Point.x = Main.sword.localToGlobal(Main.from_point).x;
Main.Coll_Point.y = Main.sword.localToGlobal(Main.from_point).y;
Main.sword.addChild(Main.Coll_Point);
trace(Main.Coll_Point.x);
trace(Main.Coll_Point.y);
if (Main.sword.rotationZ > -330)
Main.sword.rotationZ -= 20;
if (Main.sword.rotationX < 15)
Main.sword.rotationX += 10;
if ((Main.sword.rotationZ == -330) && (Main.sword.rotationX == 15))
{
SwingTimer.stop();
SwingBckTimer.start();
}
}
private function SwingBack(event:Event):void
{
Main.sword.rotationZ = -150;
Main.sword.rotationX = -15;
//Main.MazeNr1.removeChild(Main.Coll_Point);
}
There is also a rather long update(); function that animates and moves every single object that needs moving.
I think your problem might be in
Main.Coll_Point.x = Main.sword.x + Main.sword.width;
Main.Coll_Point.y = Main.sword.y + Main.sword.height;
Coll_Point expects global coordinates.
The parts + Main.sword.width and + Main.sword.height only work as expected if the sword is not rotated so that height is aligned with the y-axis and width with the x-axis.
You should use localToGlobal() on the position that is local to Main.sword (
Main.sword.width, Main.sword.height) to get the global position that represents the swords rotated tip before you add it as a child.
There are two ways you can approach this (you seem to have somewhat combined both). You can either
Add the Coll_Point as a child to something above the sword in hierarchy (Stage, MazeNr1, ...) and update the position manually every timer callback. You would have to recalculate the position everytime, so take the localToGlobal() from init to your timer function. It won't update if it doesn't get called.
For that you should have this kind of code in the timer callback:
var local:Point = new Point(Main.sword.width, Main.sword.height);
var global:Point = Main.sword.localToGlobal(local);
Main.Coll_Point.x = global.x;
Main.Coll_Point.y = global.y;
Add the point as a child to the sword. This might be a better approach as then the position will be updated automatically. What I did not remember before was that you then give the coordinates in "local" form, so do not use localToGlobal()
Run this once where you create the Collision_Point:
Coll_Point.x = <your x offset>;
Coll_Point.y = <your y offset>;
Main.sword.attachChild(Coll_Point);
Instead of sword height and width you might want to try something like -height and width/2.
Here is a quick (and not the prettiest) picture to demonstrate the problem. Local space is rotated with the object:
The only thing I can imagine to help you with this problem is to have your collision object have the same registration point as the sword. What I mean is that the orientation points should match, and the collision graphic should be moved inside of the sprite so that it matches the position of the top of the sword.
This way you can put the collision object at the very same location of the sword and apply the very same rotation. This way it will move along with the top of the sword and still have hitTest working properly.
I cannot imagine any other way to figure this out as any code will get bounds and positions. But the real thing that matters is the registration point and the top of the sword, which is a graphic thing and cannot be dealt with coding.
I hope you can imagine what I mean - if now, just say and I will provide an image for you :)
Student doing AS3, I use FlashDevelop and I am wanting to follow and use this tutorial to help me create a tower defence game: http://www.flashgametuts.com/tutorials/as3/how-to-create-a-tower-defense-game-in-as3-part-1/
I have started a new project > ActionScript 3 > ActionScript 3 Project and all I have is the Main.as which contains:
package
{
import flash.display.Sprite;
import flash.events.Event;
/**
* ...
* #author Duncan John Bunting
*/
public class Main extends Sprite
{
public function Main():void
{
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
// entry point
}
}
}
It mentions saving files to the same place the source.fla file is (I do not have this) and just before the 3rd block of code, it says "Now, we must return back to the main .fla file. Create a new layer to place actions in, and add the following code:"
How do I do this in FlashDevelop, since I do not have a source.fla? or is it not possible in FlashDevelop?
If it is not possible, can someone please point me in the direction of a tutorial that creates a game in FlashDevelop using ActionScript3.
Thanks.
Yes, in FlashDevelop you don't have access to the .fla, but you still can port timeline code into FlashDevelop. To do this, put all var X:Y statements that are outside functions above the line public function Main(), regardless of where do they appear. Then, put all functions below the finish of init() function declaration. Any other code should be put into init(). In your case, the file should look like this:
package
{
import flash.display.Sprite;
import flash.events.Event;
/**
* ...
* #author Duncan John Bunting
*/
public class Main extends Sprite
{
//setting vars to step in for turns and special blocks
var S:String = 'START';
var F:String = 'FINISH';
var U:String = 'UP';
var R:String = 'RIGHT';
var D:String = 'DOWN';
var L:String = 'LEFT';
var startDir:String;//the direction the enemies go when they enter
var finDir:String;//the direction the enemies go when they exit
var startCoord:int;//the coordinates of the beginning of the road
var lvlArray:Array = new Array();//this array will hold the formatting of the roads
//the names of these variables explain what they do
var currentLvl:int = 1;
var gameOver:Boolean = false;
var roadHolder:Sprite = new Sprite();//create an object that will hold all parts of the road
public function Main():void
{
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
// entry point
// stop(); MovieClip functions are not supported for Main
lvlArray = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,R,1,1,D,0,0,R,1,1,D,0,0,R,1,1,D,0,0,
0,0,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,
0,0,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,
S,D,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,R,1,F,
0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,0,0,0,
0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,0,0,0,
0,R,1,1,U,0,0,R,1,1,U,0,0,R,1,1,U,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
];
addChild(roadHolder);//add it to the stage
//run these functions at the start
makeRoad();
startGame();
}
function makeRoad():void{
var row:int = 0;//the current row we're working on
var block;//this will act as the block that we're placing down
for(var i:int=0;i<lvlArray.length;i++){//creating a loop that'll go through the level array
if(lvlArray[i] == 0){//if the current index is set to 0
block = new EmptyBlock();//create a gray empty block
block.graphics.beginFill(0x333333);
block.graphics.drawRect(0,0,25,25);
block.graphics.endFill();
addChild(block);
//and set the coordinates to be relative to the place in the array
block.x= (i-row*22)*25;
block.y = row*25;
} else if(lvlArray[i] == 1){//if there is supposed to be a row
//just add a box that will be a darker color and won't have any actions
block = new Shape();
block.graphics.beginFill(0x111111);
block.graphics.drawRect(0,0,25,25);
block.graphics.endFill();
block.x= (i-row*22)*25;
block.y = row*25;
roadHolder.addChild(block);//add it to the roadHolder
} else if(lvlArray[i] is String){//if it's a string, meaning a special block
//then create a special block
block = new DirectBlock(lvlArray[i],(i-row*22)*25,row*25);
addChild(block);
}
for(var c:int = 1;c<=16;c++){
if(i == c*22-1){
//if 22 columns have gone by, then we move onto the next row
row++;
}
}
}
}
function startGame():void{//we'll run this function every time a new level begins
//right now we don't have any code
}
}
}
The harder part will come when you'll be told to draw something into your .fla and assign names for it. This is still portable into FD, but instead of drawing, you'll have to code all the draw routines into FD using a class that extends Sprite if layered, or Shape if not, and use graphics property to draw simple primitives. Note that you can't directly set anchor point in FD, so your coordinates should be aligned the preset anchor point of (0,0).
I'm trying to make a simple animation with Flash CS4 and Action Script 3.0 to make a number of Symbols fly by from right to left constantly. What I want is that once a symbol has reached the end of the screen it is destroyed and another one is placed at the start position.
I intend to give each symbol a random speed and create a random symbol each time one is 'destroyed'. Any clues where I can start?
As you seem new to flash as a platform I would think writing classes shouldn't be your first port of call when learning ActionScript. Definitely just play about on the timeline for now and learn the basics. As very simple solution to this, I would suggest creating a MovieClip in the library with a class name like 'MyBall'... then paste this onto the first frame of the main timeline et voila.
// Create some variables to store data
var numberOfBalls : int = 20;
var myBalls : Array = [];
var xVelocities : Array = [];
var maxXVelocitySpeed : Number = 5;
var minXVelocitySpeed : Number = 2;
// Add your orginal balls to the stage
for (var i : int = 0; i < numberOfBalls; i++)
{
var myBall : MyBall = new MyBall();
myBall.x = -(Math.random() * stage.stageWidth);
myBall.y = Math.random() * stage.stageHeight;
var xVelocity : Number = minXVelocitySpeed + (Math.random() * (maxXVelocitySpeed - minXVelocitySpeed));
myBalls.push(myBall);
xVelocities.push(xVelocity);
addChild(myBall);
}
// Add a listener for enter frame events
addEventListener(Event.ENTER_FRAME, enterFrameHandler);
//Run this code on every frame to move the balls and reposition them if they are off the stage
function enterFrameHandler(event : Event) : void
{
for each( var myBall : MyBall in myBalls)
{
var ballIndex : int = myBalls.indexOf(myBall);
myBall.x += xVelocity[ballIndex];
if (myBall.x > stage.stageWidth)
{
myBall.x = -(Math.random() * stage.stageWidth);
myBall.y = Math.random() * stage.stageHeight;
}
}
}
First, turn your symbols into MovieClips. Then create a base class MySymbol.as for your symbols, something like:
package {
import flash.display.MovieClip;
import flash.events.Event;
import flash.geom.Point;
public class MySymbol extends MovieClip
{
public var speed:Number; // Pixels moved per frame
public function MySymbol(speed:Number, startPosition:Point)
{
this.speed = speed;
this.addEventListener(Event.ENTER_FRAME, update);
this.x = startPosition.x;
this.y = startPosition.y;
}
private function update():void
{
this.x -= this.speed;
if (this.x < 0 - this.width) { // We're at the left edge
this.removeEventListener(Event.ENTER_FRAME, update);
this.dispatchEvent(new Event(Event.COMPLETE));
}
}
}
}
Then make sure your movie clips are exported for AS3 (the "linkage" option on the item in the library). Make the class name for each item unique (e.g. MySymbol1, MySymbol2), and set the base class to MySymbol.
Your document class might look something like this:
package {
import flash.display.MovieClip;
import flash.events.Event;
import MySymbol; // Not strictly needed
public class DocumentClass extends flash.display.MovieClip
{
private static var SYMBOLS:Array = new Array(MySymbol1, MySymbol2);
public function DocumentClass()
{
// Create five symbols:
for (var i:int = 0; i < 5; i++) {
makeSymbol();
}
}
private function makeSymbol():void
{
// Pick a random symbol from the array:
var symType:Class = SYMBOLS[Math.random() * SYMBOLS.length];
// Construct the new symbol:
var loc:Point = new Point(stage.stageWidth, Math.random() * stage.stageHeight);
var sym:MySymbol = new symType(1 + Math.random() * 30, loc);
// Listen for the object hitting the left edge:
sym.addEventListener(Event.COMPLETE, remakeObject);
this.addChild(sym);
}
private function remakeObject(e:Event):void
{
e.target.removeEventListener(Event.COMPLETE, remakeObject);
this.removeChild(e.target);
// Replace the dead symbol:
makeSymbol();
}
}
}
It is a lot more efficient if instead of destroying and re-creating an object that flies off-stage you re-use the existing one and move it back to the right. But this is an optimization you can implement later, if things become slow.
Note that all the code above is UNTESTED and I have not coded AS3 in a while, so there's likely at least a few bugs in it. Hopefully it will serve as a good enough starting point.
Define a Circle (symbol) class that extends Sprite/Shape and has a velocity variable
Draw a circle (or whatever) with a random color
Math.floor(Math.random() * 0xffffff)
Assign a random value to velocity
minVelocity + Math.floor(Math.random() * velocityRange)
Create a start() method inside the Circle class that registers an enter frame handler
Increment this.y inside the enter frame handler, and dispatch a 'recycleMe' event if y is more than the max value.
Create N instances of Circle, addChild them, and call their start() methods.
listen to 'recycleMe' events on each of them, and reset the value of y from the handler.
Here's a few prompts to get you started.
MovieClips have an x and y property. If you were to add to these numbers over time you would see the MovieClip move along the x and/or y axis of the stage. Look into doing this using the Event.ENTER_FRAME which will allow you to change the values every time the screen is going to update.
Your stage will have a given width (a stageWidth property). You probably want to monitor when your MovieClip's x property is greater than the width of your stage. If it is remove (removeChild) it and add a new one (addChild) and place it back at the start x/y position.