I have never seen this happen before. I have some very simple code to display a Sprite and move it around. I first add it to the stage with this code:
public var floor:Floor;
public var char:Character;
public function Game()
{
addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event):void{
floor = new Floor();
addChild(floor);
char = new Character();
addChild(char);
char.x = stage.stageWidth - char.width;
var item:Sprite = new Sprite();
item.graphics.beginFill(0x666600);
item.graphics.drawRect(400, 400, 50, 50);
item.addEventListener(MouseEvent.CLICK, moveEast);
addChild(item);
}
That last bit with the graphics is just for testing; basically, I want a button that I can press and have it shift everything by 50 pixels. This is the moveEast function:
public function moveEast(e:Event):void {
floor.x += 50;
}
As you can see, it is very straightforward. In theory, I should add my Floor object, then move it to the right 50 pixels whenever I push the button. However, in practice, what happens is that is both shifts and doesn't shift. Specifically, it does shift; I can move it all the way across the stage and have the complete object move. However, it ALSO retains a copy of the object at the original position. I am confident that I have not accidentally added another child somewhere. If I remove the single addChild(floor) line from my code, then nothing shows up. Any ideas on why it might be doing this?
Related
i'm building a flash desktop app, where the user needs to link two Movieclips on stage (a computer and a router) using a line (or whatever can do the job), i want to achieve this same exact effect: image1. I searched and found this solution, i tried the code and did some modifications:
link.addEventListener(MouseEvent.CLICK, linkOnClick);
function linkOnClick(e:MouseEvent){
this.addEventListener(Event.ENTER_FRAME, enterFrame);
var linkPoint:Point = new Point(link.x, link.y);
var mousePoint:Point = new Point();
var distance:Number;
var radians:Number;
function enterFrame(e:Event):void {
//Distance
mousePoint.x = stage.mouseX;
mousePoint.y = stage.mouseY;
distance = Point.distance(linkPoint, mousePoint);
link.width = distance;
//Rotation
radians = Math.atan2(stage.mouseY - link.y, stage.mouseX - link.x);
link.rotation = radians * (180/ Math.PI);
if(link.hitTestObject(router)){trace("Success");}
}
When i compiled the code i got this: image2, so as you may remark, the problems i found are:
1-the edge of the line follows the direction of the mouse, but sometimes it goes beyond the cursor, i want the cursor to drag the edge of the line.
2-the line changes it's width, if it's 90° degrees the line width is so remarkable, i want the line to have a constant width.
how can i acheive the same exact effect shown in image1 ?
// First, lets create mouse-transparent container for drawing.
var DrawingLayer:Shape = new Shape;
addChild(DrawingLayer);
// Hook the event for starting.
stage.addEventListener(MouseEvent.MOUSE_DOWN, onDown);
// Define a storage for keeping the initial coordinates.
var mouseOrigin:Point = new Point;
function onDown(e:MouseEvent):void
{
// Save the initial coordinates.
mouseOrigin.x = DrawingLayer.mouseX;
mouseOrigin.y = DrawingLayer.mouseY;
// Hook the events for drawing and finishing.
stage.addEventListener(MouseEvent.MOUSE_UP, onUp);
stage.addEventListener(MouseEvent.MOUSE_MOVE, onDraw);
}
function onDraw(e:MouseEvent):void
{
// Remove the previous line.
DrawingLayer.graphics.clear();
// Draw a new line.
DrawingLayer.graphics.lineStyle(5, 0xFF6600);
DrawingLayer.graphics.moveTo(mouseOrigin.x, mouseOrigin.y);
DrawingLayer.graphics.lineTo(DrawingLayer.mouseX, DrawingLayer.mouseY);
}
function onUp(e:MouseEvent):void
{
// Unhook the events for drawing and finishing.
stage.removeEventListener(MouseEvent.MOUSE_UP, onUp);
stage.removeEventListener(MouseEvent.MOUSE_MOVE, onDraw);
}
It's because of that the actionscript is trying to stretch the line thickness by changing its container MovieClip's scale. But you can prevent this by setting the line Scale option to None.
To do that, select your line and open the properties menu and then select None from the drop down menu of the Scale option.
But,
I recommend you to draw a line by a code: Draw line from object to Mouse (AS3)
Write below code:
this.graphic.clear ();
this.graphic.lineStyle(0x000000);
this.moveTo(startPoint.x,startPoint.y);
this.lineTo(endpoint.X,endpoint.y);
I have a gun child inside of a movieclip called "player" and "player" is inside another movieclip called "level one".
So inside the gun class, the code spawns a bullet. Which has to spawn in the parent's parent. So the bullet can shoot into the level.
private function fire(m: MouseEvent)
{
//when bullet fired
var b = new Bullet;
MovieClip(MovieClip(parent).parent).addChild(b);
}
However, the bullet never appears in the parent's parent. What could be the issue here?
UPDATED CODE:
In gun class:
function fire(e:MouseEvent):void
{
dispatchEvent(new Event('fire!', true));
}
In player class:
protected function fire(e: Event)
{
var b: Bullet = new Bullet();
// bullet must be in same position and angle as player.gun
b.rotation = player.gun.rotation;
b.x = player.gun.x; + player.gun.width * Math.cos(player.gun.rotation / 180 * Math.PI);
b.y = player.gun.y + player.gun.width * Math.sin(player.gun.rotation / 180 * Math.PI);
addChild(b);
}
You missed to strong typing the variable and add parentheses
Try
var b:Bullet = new Bullet();
Why don't you keep a reference to the parent.parent?
var b:Bullet = new Bullet(parent)
...
public function Bullet(spawnTarget:MovieClip)
{
_spawnTarget = spawnTarget;
}
private function fire(m: MouseEvent)
{
//when bullet fired
var b = new Bullet;
_spawnTarget.addChild(b);
}
Using parent.parent is risky because you'd always need to make sure that the gun class is instantiated in a child of the game class. Also, you can't create guns in any other position in the hierarchy which can be a problem if your game grows bigger.
You can solve this with listeners (as the comments state, and this is an example of passing a game reference. Each instance receives a reference to game, so each instance can call public function on game:
Game.as
myPlayer = new Player(this);
public function addBulletToWorld(){
}
Player.as
public function Player(game){
myGun = new Gun(game);
}
Gun.as
public function Gun(game){
game.addBulletToWorld();
}
If you absolutely insist on using the parent.parent thing, just set a break point at that line and see what parent.parent actually is, then adjust as needed to match your actual structure. But aside from the fact that this is just bad practice, it's going to limit you to where you can't use this Class anywhere but at that one depth.
I'd recommend just generating a custom event when you want to fire the bullet, and letting the grandparent handle it.
In Gun:
function fire(e:MoudeEvent):void {
dispatchEvent(new Event('fire!', true));//setting it true lets it bubble up to the parent
}
In Game
protected var bullets:Vector. = new Vector();
public function Game() {
addEventListener('fire!', fire);
}
//note I worked with Flex too long to use private methods very often
protected function fire(e:Event) {
var bullet:Bullet = new Bullet();
bullet.x = MoveClip(e.target).x);
bullet.y = MoveClip(e.target).y);
addChild(bullet);
bullets.push(bullet);//I assume you're going to want to move this or something, so we need to store it to manage it
}
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 :)
I got a problem with AS3 and AIR. I'm working on a side-scrolling game for smartphones with a plane and I use different backgrounds as layers.
Before all other: I use GPU and only bitmaps, quality is set to low. So Performance settings are all set for smartphone use.
I putted them into a rectangle using the drawing API and move the background with a matrix:
protected var scrollingBitmap:BitmapData;
protected var canvas:Graphics;
protected var matrix:Matrix;
public function move(dx:Number, dy:Number):void {
matrix.translate(dx, dy);
if(dx != 0) matrix.tx %= scrollingBitmap.width;
if(dy != 0) matrix.ty %= scrollingBitmap.height;
drawCanvas();
}
protected function drawCanvas():void {
canvas.clear();
canvas.beginBitmapFill(scrollingBitmap, matrix, true, true);
canvas.drawRect(0, -scrollingBitmap.height, 1404, scrollingBitmap.height);
}
UPDATE2 (
Take a look at this: http://plasticsturgeon.com/2010/06/infinite-scrolling-bitmap-backgrounds-in-as3/
I used this to create my backgrounds.
With this I can simulate that my plane is flying to the right without moving the whole background and I can use a small single graphic which repeats every time (for the foreground layer).
For the background layer I use this method, too, but with a much larger graphic and I move it only with less the speed of my plane to simulate a far background.
My move-method is on an enterframe event. So I can update the background every frame with the "movement" of my plane.
)
The plane can exceed the height of the bitmaps. Everytime the bitmap comes back into the window/screen a real long lag occurs. And when the plane flies very fast, the game start to lag, too.
My first approach was to use .PNG files (but they are very big: 1-3MB size).
My next approach was to use .GIF files (much less size).
With both it's the same. So it can't be that.
I read about draw() and copyPixels() but I don't know, how I can use those to repeat the image.
UPDATE1:
protected var scrollingBitmap:BitmapData;
protected var canvas:Bitmap;
protected function init(e:Event):void {
removeEventListener(Event.ADDED_TO_STAGE, init);
canvas = new Bitmap(new BitmapData(1404, scrollingBitmap.height, true), "auto", true);
this.addChild(canvas);
drawCanvas();
}
public function move(dx:Number, dy:Number):void {
if(dx != 0) dx %= scrollingBitmap.width;
if(dy != 0) dy %= scrollingBitmap.height;
drawCanvas(dx, dy);
}
protected function drawCanvas(xPos:Number = 0, yPos:Number = 0):void {
canvas.bitmapData.copyPixels(scrollingBitmap, new Rectangle(0, 0, 1404, scrollingBitmap.height), new Point(xPos, yPos), scrollingBitmap);
}
I think you'd be better off with a Bitmap instead of using the graphics object with fill. copyPixels is very fast. So what you'd do is simply copyPixels over the top of whatever was there before, presuming everything is opaque. If everything is not opaque, you'll need to use your source bitmap as its own alpha data so previously drawn pixels don't show through.
Let's reframe your canvas so it is a Bitmap and not a MC. your new code will look like:
protected function drawCanvas():void {
canvas.bitmapData.copyPixels(scrollingBitmap, new Rectangle(0, 0, scrollingBitmap.width, scrollingBitmap.height), new Point(0,0), scrollingBitmap);
}
Oh, and look at that! Not only is this code faster, it's only one line of code!
EDIT: Added working code
package {
import flash.display.MovieClip;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.events.Event;
import flash.geom.Rectangle;
import flash.geom.Point;
public class EndlessBG extends MovieClip{
//this one stays stationary, we're getting the pixels for the right side of the pic from here
private var _source:BitmapData;
//this is the one moving to the left (the pixels for the right side are not visible except for once a cycle);
private var _movingPixels:BitmapData;
private var _canvas:Bitmap;
private var _xOffset:int = 0;
private var _rect:Rectangle = new Rectangle();;
private var _point:Point = new Point();
public function EndlessBG() {
super();
_source = new BathroomStillLife();
_canvas = new Bitmap(new BitmapData(_source.width, _source.height));
_canvas.bitmapData.draw(_source);
_canvas.x = stage.stageWidth/2 - _canvas.width/2;
_canvas.y = 5;
addChild(_canvas);
addEventListener(Event.ENTER_FRAME, gameLoop);
setGeometryDefaults();
_movingPixels = new BitmapData(_source.width, _source.height);
_movingPixels.copyPixels(_source, _rect, _point);
//turn this on to watch red pixels be drawn where the source pixels are coming in
//_source = new BitmapData(_source.width, _source.height, false, 0xFF0000);
}
private function gameLoop(e:Event):void {
_xOffset--;//where the background is moving to
if (_xOffset < -_source.width) {
_xOffset = 0;
//this doesn't seem to work correctly:
//_movingPixels.scroll(_source.width, 0);
_movingPixels = new BitmapData(_source.width, _source.height);
_movingPixels.copyPixels(_source, _rect, _point);
}
trace(_xOffset);
setGeometryDefaults();
_movingPixels.scroll(-1, 0);
//draw the moved part of the canvas
_canvas.bitmapData.copyPixels(_movingPixels, _rect, _point);
//If we stop here, we get a smear to the right
//so, get the remaining pixels directly from the source
//1) reset our rect and point to be to the right side
_rect.x = 0;
_rect.width = -_xOffset;
_point.x = _source.width + _xOffset;
//2) copy from the source
_canvas.bitmapData.copyPixels(_source, _rect, _point);
}
private function setGeometryDefaults():void {
_rect.x=0;
_rect.y=0;
_rect.width = _source.width;
_rect.height = _source.height;
_point.x = 0;
_point.y = 0;
}
}
}
Not ideal, and not polished enough yet for a blog post, but should get you started.
Edit:
Eventually I did write that blog post.
http://www.greensock.com/blitmask
This might help although not free
public class ItemView extends MovieClip {
private var _title:TextField;
private var _extra:MovieClip;
public function ItemView( ) {
setup();
return;
}
private function setup( ):void {
trace("ItemView::setup()");
_title = new TextField();
_title.text = "Title";
addChild(_title);
_extra = new MovieClip();
_extra.width = 200;
_extra.height = 40;
_extra.graphics.beginFill(0x0000ff);
_extra.graphics.drawRect(0, 0, 20, 20);
_extra.graphics.endFill();
addChild(_extra);
return;
}
}
When I create an instance of ItemView and add it to the stage, "Title" displays but the blue square does not. However, if I make the graphics calls on this instead of _extra, I do see the blue square. This tells me that _extra itself is not displaying properly, but I can't figure out why.
What am I missing? Is there some special procedure for adding one MovieClip to another?
A little quirk.
When you set the width/height of the MovieClip object, Flash internally also adjusts the scaleX and scaleY properties. For instance, if the original width was 100, and now you've set it to 200, then the new scaleX should be 2. This means Flash will display it at 2X scale horizontally.
Now, initially the width is 0 (blank object), so when you set a new width, the new scaleX should become infinite - or 0, as Flash does it.
So even though you've drawn something onto the object, it's still at zero scale, which is why nothing gets displayed. The way to remedy this, as suggested by another poster, is to avoid setting width/height on the blank object or alternatively to reset scaleX and scaleY to 1 after your drawing is done.
...
_extra.graphics.endFill();
_extra.scaleX = _extra.scaleY = 1;
Try to delete
_extra.width = 200;
_extra.height = 40;