Drawing "flat" waves is easy, but I want to draw the wave between two points x1,y1 x2,y2
Here is the "flat" code:
package display
{
import flash.display.Sprite;
import flash.events.Event;
public class SineWave extends Sprite
{
private var angle:Number = 0;
private var centerY:Number = 200;
private var range:Number = 50;
private var xspeed:Number = 2;
private var yspeed:Number = .1;
private var xpos:Number
private var ypos:Number
public function SineWave()
{
init()
}
protected function init():void
{
var sinWavePosition = 100;
var cosWavePosition = 200;
var sinWaveColor:uint = 0xFF0000;
var cosWaveColor:uint = 0x00FF00;
var waveMultiplier:Number = 10;
var waveStretcher:Number = 5;
var i:uint;
for(i = 1; i < 500; i++)
{
var sinPosY:Number = Math.sin(i / waveStretcher) * waveMultiplier;
var cosPosY:Number = Math.cos(i / waveStretcher) * waveMultiplier;
graphics.beginFill(sinWaveColor);
graphics.drawRect(i, sinWavePosition + sinPosY, 2, 2);
graphics.beginFill(cosWaveColor);
graphics.drawRect(i, cosWavePosition + cosPosY, 2, 2);
}
}
}
}
What about a bezier curve? This isn't a sine wave per se. But the effect is similar. With proper control points you should be able to make it look just like a sine wave.
Well quick cheat would be to get the distance between the points, draw the graphic onto a separate sprite, then just work out the angle between the two points and rotate the graphic to that angle.
Not the most 'perfect' solution, but should do the trick, otherwise I can imagine, working out the angle between the two points and then adding this as an increment to the existing values.
Hope this hack helps.
I believe this will work, it's basically a rotationmatrix that is applied to every point on the line.
There might be some errors with the order and signs of the multiplications and the parameters to atan2 but otherwhise i think this will work.
float v = Atan2(y2-y1, x2-x1);
for(blabla)
{
calculate sinPosY from i
newSinPosY = i*Cos(v) + sinPosY*Sin(v);
sinPosX = i*-Sin(v) + sinPosY*Cos(v));
add offset
render
}
Related
I'm having trouble with AS3 which I have to use for a little research project of mine.
The AS3 project would create a number of randomly placed squares which all would rotate around its center point.
I managed to figure out how to rotate it internally using this handy walkthrough.
However I am having trouble applying this method to all squares which were created in a for loop with randomly selected stage points. Only the first created will rotate
Here is the code in question:-
for(var i=0; i<10; i++)
{
var square:Shape = new Shape();
this.addChild(square);
var posX = Math.floor(Math.random() * stage.stageWidth) + 50;
var posY = Math.floor(Math.random() * stage.stageHeight) + 50;
square.x=posX;
square.y=posY;
var curSquareAng:Number=0;
var squareRotCenter:Point=new Point(0,0);
drawShapes();
var squareMat:Matrix=square.transform.matrix.clone();
}
this.addEventListener(Event.ENTER_FRAME, onEnter);
function onEnter(e:Event):void {
curSquareAng = (curSquareAng+2)%360;
rotateSquare(curSquareAng);
}
function rotateSquare(deg:Number):void {
var mat:Matrix= squareMat.clone();
MatrixTransformer.rotateAroundInternalPoint(mat,squareRotCenter.x, squareRotCenter.y, deg);
square.transform.matrix = mat;
}
I realize I likely have to create an array for each of the squares initial center points and loop through them. However I am completely lost on how to do so. As you can likely tell I am unfamiliar with AS3 and would much appreciate any help you can give this beginner programmer. :P
You need to create your own class based on a shape, then you stuff it full of properties that represent rotation center point, current angle and whatever else you'd want your squares to contain, then give the class update method which will do what you wrote in onEnter function for itself only. Then you will have easier control over what your squares are able to do. This technique is called "encapsulation".
On a side note, if you're wanting your square to rotate around internal point of (0,0), you can set their rotation property to achieve the desired effect. For other points, the walkthrough or its equivalent should be used.
public class Square extends Shape {
public var rotationCenter:Point=new Point();
private var currentAngle:Number=0;
public var rotationSpeed:Number=2; // degrees per frame
private var baseMatrix:Matrix;
public function Square() {
// draw the shape on "this.graphics"
this.graphics.beginFill(0xffff00,1);
this.graphics.moveTo(-20,-20);
this.graphics.lineTo(20,-20);
this.graphics.lineTo(20,20);
this.graphics.lineTo(20,-20);
this.graphics.lineTo(-20,-20);
this.graphics.endFill();
// if not set in declaration, set internal vars
baseMatrix=this.transform.matrix; // likely identity matrix, but let's initialize anyway
}
public function storeMatrix():void {
// you are positioning a square after you create it, so probably you want its new location to be transformed
// that one's matrix will no longer be an identity, so go capture
baseMatrix=this.transform.matrix;
}
public function update():void {
// should be called once per frame
currentAngle=(currentAngle+rotationSpeed)%360;
var mat:Matrix= baseMatrix.clone();
MatrixTransformer.rotateAroundInternalPoint(mat,rotationCenter.x, rotationCenter.y, currentAngle);
this.transform.matrix = mat;
}
}
Now, you will have to maintain an array of squares to make them rotate separately:
var squares:Array=[];
for (var i:int=0;i<10;i++) {
var square:Square=new Square();
var posX = Math.floor(Math.random() * stage.stageWidth) + 50;
var posY = Math.floor(Math.random() * stage.stageHeight) + 50;
square.x=posX;
square.y=posY;
// after you position, give it a rotation point
square.rotationCenter.x=Math.random()*40-20;
square.rotationCenter.y=Math.random()*40-20; // -20 to 20, should do for this example
// now fix the position so your square will know that it should rotate
// its *current* transform matrix
square.storeMatrix();
// even if it's actually unchanged by changing X or Y
// also, should you desire to scale some square, you should do that prior to calling this
// now add the square to an array
squares.push(square);
}
addEventListener(Event.ENTER_FRAME,onEnter);
function onEnter(e:Event):void {
for (var i:int=0;i<squares.length;i++) squares[i].update();
// simple, isn't it? Each square will know what to do.
}
Nevermind. Thank you Vesper for putting me on the right track I've managed to solve my problem thanks to your input (not necessarily through your way but your input helped me get to the destination per say).
I think I was making it a little too complicated going though the matrix route and instead used a shape array to loop through the squares and add rotation. The solution I came to is a little simple but gets the job done.
public var rotationSpeed:Number=2; // degrees per frame
public var square:Array = new Array( );
public function Square() {
for (var i:int=0;i<10;i++) {
square[i] = new Shape();
var posX = Math.floor(Math.random() * stage.stageWidth) + 50;
var posY = Math.floor(Math.random() * stage.stageHeight) + 50;
square[i].graphics.lineStyle();
var rgb = Math.random() * 0xFFFFFF;
square[i].graphics.beginFill(rgb);
// -50 determines where the spin will center from.
square[i].graphics.drawRect(-50,-50,100,100);
square[i].graphics.endFill();
square[i].x = posX;
square[i].y = posY;
addChild(square[i]);
}
addEventListener(Event.ENTER_FRAME,onEnter);
}
private function onEnter(e:Event):void {
for (var i:int=0; i < square.length; i++) {
getChildAt(i).rotation += rotationSpeed;
}
}
I'm new to Flash Actionscript 3.0 and object programming in general. I'm trying to create a simple game, which is drawing a shape based on steering.
public class Player extends Shape
{
public var X,Y,v,vX,vY,size,a,r:Number;
public var k,counter,leftKey,rightKey,_color:uint;
public var line:Shape = new Shape();
public var dot:Shape = new Shape();
/*...*/
/*constructor, giving values to variables here, not important*/
/*...*/
public function Move():void
{
a=a+0.05*k;
//player controls k parameter k=0 by default
//k=1 when right key pressed
//k=-1 when left key pressed
vX=v*Math.cos(a);
vY=v*Math.sin(a);
X=X+vX;
Y=Y+vY;
dot.x=X+vX*size/(2*v);
dot.y=Y+vY*size/(2*v);
if (counter==0)
{
line.graphics.lineTo(X,Y);
if (Math.random()<0.008) counter=12;
} else
{
line.graphics.moveTo(X, Y);
counter--;
}
}
}
Function Move is in my Player class, which is called from inifinite TimerEvent function in my Main Class
public function mainLoop(TimerEvent:Event):void
{
for (var i:uint=0; i<players; i++) player[i].Move();
}
It seems to be working well at the beginning but after some time CPU usage raises dramatically and game becomes unplayble. I belivie it's caused by my shape (line) getting more and more complex.
Is there some reasonable way to optimize it? Can I somehow draw a line in less consuming way? I tried to convert it to bitmap but that looked ugly and didn't really help.
Thanks and cheers!
You're right in assuming that your slowdown in coming from your shape code - vector data is redrawn every frame in flash, so the more complex it is, the longer it takes to draw. Some solutions, depending on what you're willing to do:
Your fidelity is way to high - you're calling your Move function every frame; you probably don't need it that high, as the difference in movement since the last frame is probably less than a pixel. Sample your position every X frames instead (where X is the level of fidelity your willing to go down to). This can be done with a simple counter in the enter frame
If you don't need to keep the entire history of the drawing, put all your points into an array/vector, culling the length as needed. Then every frame, do a line.graphics.clear() and just draw the points in the array
If you do need to keep the entire history, then keep a BitmapData under your line (e.g. the size of the stage). Every so often, draw the line to the BitmapData and call clear() on your graphics to get right of the vector data. You shouldn't notice any loss in quality (set smoothing to true when you're drawing)
I'd do the first point in any case, then choose between the second and third, depending on your use case
Expanding my comment, try something like this:
public class Player extends Shape
{
public var X,Y,v,vX,vY,size,a,r:Number;
public var k,counter,leftKey,rightKey,_color:uint;
public var line:Shape = new Shape();
public var dot:Shape = new Shape();
/*...*/
/*constructor, giving values to variables here, not important*/
/*...*/
public function Player(){
//draw shapes
graphics.lineStyle(1);
graphics.drawCircle(0,0,r);
graphics.lineTo(size,0);//can't test this now, but make sure the line is in the same direction as rotation 0 (guessing it's to the right)
//your other constructor code here
}
public function Move():void
{
a=a+0.05*k;
//player controls k parameter k=0 by default
//k=1 when right key pressed
//k=-1 when left key pressed
vX=v*Math.cos(a);
vY=v*Math.sin(a);
X=X+vX;
Y=Y+vY;
x=X+vX*size/(2*v);
y=Y+vY*size/(2*v);
rotation = a * 57.2957795;//quick'n'dirty radians to degrees
}
and if you want to draw the trails you can try something like this:
var canvas:Bitmap = new BitmapData(state.stageWidth,stage.stageHeight,true,0xFF000000);
var ct:ColorTransform = new ColorTransform(1,1,1,.1);
public function mainLoop(TimerEvent:Event):void
{
for (var i:uint=0; i<players; i++) {
player[i].Move();
canvas.draw(player[i],player[i].transform.concatenatedMatrix,ct);
}
}
Hope this makes sense.
Update
Here is a standalone code snippet to illustrate the idea above(which has untested syntax):
package {
import flash.display.Bitmap;
import flash.geom.ColorTransform;
import flash.display.BitmapData;
import flash.text.TextField;
import flash.ui.Keyboard;
import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.utils.Dictionary;
import flash.display.Sprite;
public class PlayerMoveTest extends Sprite {
private var keys:Dictionary = new Dictionary();
private var players:Vector.<Player> = new Vector.<Player>();
private var trails:BitmapData;
private var fade:ColorTransform = new ColorTransform(1,1,1,.1);
public function PlayerMoveTest() {
addEventListener(Event.ADDED_TO_STAGE,init);
}
private function init(e:Event):void{
trails = new BitmapData(stage.stageWidth,stage.stageHeight,true,0x00FFFFFF);
addChild(new Bitmap(trails));
for(var i:int = 0 ; i < 2; i++){
var p:Player = addChild(new Player(10+i*10)) as Player;
p.x = stage.stageWidth * .5;
p.y = stage.stageHeight * .5;
players.push(p);
}
stage.addEventListener(KeyboardEvent.KEY_DOWN,onKeyDown);
stage.addEventListener(KeyboardEvent.KEY_UP,onKeyUp);
stage.addEventListener(Event.ENTER_FRAME,update);
}
private function onKeyDown(e:KeyboardEvent):void{
keys[e.keyCode] = true;
}
private function onKeyUp(e:KeyboardEvent):void{
keys[e.keyCode] = null;
}
private function update(e:Event):void{
if(keys[Keyboard.LEFT] != undefined) {players[0].a -= .05;players[1].a += .05;}
if(keys[Keyboard.RIGHT] != undefined) {players[0].a += .05;players[1].a -= .05;}
if(keys[Keyboard.UP] != undefined) {players[0].s += .15;players[1].s -= .15;}
if(keys[Keyboard.DOWN] != undefined) {players[0].s -= .15;players[0].s += .15;}
for(var i:int = 0 ; i < players.length; i++) {
players[i].move();
trails.draw(players[i],players[i].transform.concatenatedMatrix,fade);
}
}
}
}
import flash.display.*;
class Player extends Shape{
public var vx:Number,vy:Number,a:Number,size:Number,r:Number,s:Number;
public function Player(size:Number){
init(size);
}
private function init(size:Number):void{
vx = vy = a = s = 0;
this.size = size;
this.r = size * .25;
graphics.lineStyle(1);
graphics.drawCircle(0,0,r);
graphics.lineTo(size,0);
}
public function move():void{
rotation = a * 57.2957795;
vx = Math.cos(a) * s;
vy = Math.sin(a) * s;
x += vx;
y += vy;
if(x < 0) x = 0;
if(y < 0) y = 0;
if(x > stage.stageWidth) x = stage.stageWidth-width;
if(y > stage.stageHeight) y = stage.stageHeight-height;
}
}
You can test this code here and here's a preview:
Use the arrow keys to drive(up arrow accelerates, left/right steer).
The first player is the smaller one, having the correct controls, the other is simply mirroring the previous controls)
This question is related to ActionScript 3.0 and Flash CS6
I am trying to make an object shake a bit in a certain for some seconds. I made it a "movieclip" and made this code:
import flash.events.TimerEvent;
var Machine_mc:Array = new Array();
var fl_machineshaking:Timer = new Timer(1000, 10);
fl_machineshaking.addEventListener (TimerEvent.TIMER, fl_shakemachine);
fl_machineshaking.start ();
function fl_shakemachine (event:TimerEvent):void {
for (var i = 0; i < 20; i++) {
Machine.x += Math.random() * 6 - 4;
Machine.y += Math.random() * 6 - 4;
}
}
When testing the movie I get multiple errors looking exactly like this one:
TypeError: Error #1009: Cannot access a property or method of a null object reference.
at Historieoppgave_fla::MainTimeline/fl_shakemachine()
at flash.utils::Timer/_timerDispatch()
at flash.utils::Timer/tick()
Also, the object doesnt shake, but it moves steadily upwards to the left a bit every tick.
To the point:
I wish to know how I stop the script after the object is not in the stage/scene anymore and also how to make it shake around, as I do not see what is wrong with my script, please help, thank you ^_^
AStupidNube brought up a great point about the original position. So adding that to shaking that should be a back and forth motion, so don't rely on random values that may or may not get you what you want. Shaking also has a dampening effect over time, so try something like this:
Link to working code
• http://wonderfl.net/c/eB1E - Event.ENTER_FRAME based
• http://wonderfl.net/c/hJJl - Timer Based
• http://wonderfl.net/c/chYC - Event.ENTER_FRAME based with extra randomness
**1 to 20 shaking items Timer Based code - see link above for ENTER_FRAME code••
package {
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.events.TimerEvent;
import flash.geom.Point;
import flash.text.TextField;
import flash.utils.Timer;
public class testing extends Sprite {
private var shakeButton:Sprite;
private var graphic:Sprite;
private var shakerPos:Array;
private var shakers:Array;
private var numShakers:int = 20;
private var dir:int = 1;
private var displacement:Number = 10;
private var shakeTimer:Timer;
public function testing() {
this.shakers = new Array();
this.shakerPos = new Array();
this.addEventListener(Event.ADDED_TO_STAGE, this.init);
}
private function init(e:Event):void {
this.stage.frameRate = 30;
this.shakeTimer = new Timer(33, 20);
this.shakeTimer.addEventListener(TimerEvent.TIMER, this.shake);
this.graphics.beginFill(0x333333);
this.graphics.drawRect(0,0,this.stage.stageWidth, this.stage.stageHeight);
this.graphics.endFill();
this.createShakers();
this.shakeButton = this.createSpriteButton("Shake ");
this.addChild(this.shakeButton);
this.shakeButton.x = 10;
this.shakeButton.y = 10;
this.shakeButton.addEventListener(MouseEvent.CLICK, this.shakeCallback);
}
private function createSpriteButton(btnName:String):Sprite {
var sBtn:Sprite = new Sprite();
sBtn.name = btnName;
sBtn.graphics.beginFill(0xFFFFFF);
sBtn.graphics.drawRoundRect(0,0,80,20,5);
var sBtnTF:TextField = new TextField();
sBtn.addChild(sBtnTF);
sBtnTF.text = btnName;
sBtnTF.x = 5;
sBtnTF.y = 3;
sBtnTF.selectable = false;
sBtn.alpha = .5;
sBtn.addEventListener(MouseEvent.MOUSE_OVER, function(e:Event):void { sBtn.alpha = 1 });
sBtn.addEventListener(MouseEvent.MOUSE_OUT, function(e:Event):void { sBtn.alpha = .5 });
return sBtn;
}
private function createShakers():void {
var graphic:Sprite;
for(var i:int = 0;i < this.numShakers;i++) {
graphic = new Sprite();
this.addChild(graphic);
graphic.graphics.beginFill(0xFFFFFF);
graphic.graphics.drawRect(0,0,10,10);
graphic.graphics.endFill();
// add a 30 pixel margin for the graphic
graphic.x = (this.stage.stageWidth-60)*Math.random()+30;
graphic.y = (this.stage.stageWidth-60)*Math.random()+30;
this.shakers[i] = graphic;
this.shakerPos[i] = new Point(graphic.x, graphic.y);
}
}
private function shakeCallback(e:Event):void {
this.shakeTimer.reset();
this.shakeTimer.start();
}
private function shake(e:TimerEvent):void {
this.dir *= -1;
var dampening:Number = (20 - e.target.currentCount)/20;
for(var i:int = 0;i < this.numShakers;i++) {
this.shakers[i].x = this.shakerPos[i].x + Math.random()*10*dir*dampening;
this.shakers[i].y = this.shakerPos[i].y + Math.random()*10*dir*dampening;
}
}
}
}
Now this is a linear dampening, you can adjust as you see fit by squaring or cubing the values.
You have to remember the original start position and calculate the shake effect from that point. This is my shake effect for MovieClips. It dynamically adds 3 variables (startPosition, shakeTime, maxShakeAmount) to it. If you use classes, you would add them to your clips.
import flash.display.MovieClip;
import flash.geom.Point;
function shake(mc:MovieClip, frames:int = 10, maxShakeAmount:int = 30) : void
{
if (!mc._shakeTime || mc._shakeTime <= 0)
{
mc.startPosition = new Point(mc.x, mc.y);
mc._shakeTime = frames;
mc._maxShakeAmount = maxShakeAmount;
mc.addEventListener(Event.ENTER_FRAME, handleShakeEnterFrame);
}
else
{
mc.startPosition = new Point(mc.x, mc.y);
mc._shakeTime += frames;
mc._maxShakeAmount = maxShakeAmount;
}
}
function handleShakeEnterFrame(event:Event):void
{
var mc:MovieClip = MovieClip(event.currentTarget);
var shakeAmount:Number = Math.min(mc._maxShakeAmount, mc._shakeTime);
mc.x = mc.startPosition.x + (-shakeAmount / 2 + Math.random() * shakeAmount);
mc.y = mc.startPosition.y + (-shakeAmount / 2 + Math.random() * shakeAmount);
mc._shakeTime--;
if (mc._shakeTime <= 0)
{
mc._shakeTime = 0;
mc.removeEventListener(Event.ENTER_FRAME, handleShakeEnterFrame);
}
}
You can use it like this:
// shake for 100 frames, with max distance of 15px
this.shake(myMc, 100, 15);
BTW: In Flash, you should enable 'permit debugging' in your 'publish settings' to have more detailed errors. This also gives back the line numbers where your code is breaking.
update:
Code now with time / maximum distance separated.
Here is a forked version of the chosen answer, but is a bit more flexible in that it allows you to set the frequency as well. It's also based on time as opposed to frames so you can think in terms of time(ms) as opposed to frames when setting the duration and interval.
The usage is similar to the chosen answer :
shake (clipToShake, durationInMilliseconds, frequencyInMilliseconds, maxShakeRange);
This is just an example of what I meant by using a TimerEvent as opposed to a ENTER_FRAME. It also doesn't require adding dynamic variables to the MovieClips you are shaking to track time, shakeAmount, and starting position.
public function shake(shakeClip:MovieClip, duration:Number = 3000, frequency:Number = 30, distance:Number = 30):void
{
var shakes:int = duration / frequency;
var shakeTimer:Timer = new Timer(frequency, shakes);
var startX:Number = shakeClip.x;
var startY:Number = shakeClip.y;
var shakeUpdate:Function = function(e:TimerEvent):void
{
shakeClip.x = startX + ( -distance / 2 + Math.random() * distance);
shakeClip.y = startY + ( -distance / 2 + Math.random() * distance);
}
var shakeComplete:Function = function(e:TimerEvent):void
{
shakeClip.x = startX;
shakeClip.y = startY;
e.target.removeEventListener(TimerEvent.TIMER, shakeUpdate);
e.target.removeEventListener(TimerEvent.TIMER_COMPLETE, shakeComplete);
}
shakeTimer.addEventListener(TimerEvent.TIMER, shakeUpdate);
shakeTimer.addEventListener(TimerEvent.TIMER_COMPLETE, shakeComplete);
shakeTimer.start();
}
-4 <= Math.random() * 6 - 4 < 2
You add this offset to Machine.x 20 times, so chances for moving to the left is greater, than to the right.
It seems that you looking for something like this:
for each (var currentMachine:MovieClip in Machine_mc)
{
currentMachine.x += Math.random() * 6 - 3;
currentMachine.y += Math.random() * 6 - 3;
}
Clouds,
ducks,
score
display
and
waves
should
each
have
a
class
to
govern
their
movement
and
behavior.
When
ducks
are
clicked
on
they
are
“shot”
and
the
duck
is
removed
from
the
array
as
well
as
from
the
stage
(use
arrayName.splice()
for
this).
The
score
display
should
count
down
as
this
occurs.
The
number
of
ducks
left
should
be
a
property
within
the
Score
Display’s
class
and
adjusted
by
Main
when
the
ducks
are
shot.
When
all
the
ducks
are
“shot”
the
game
should
animate
the
“you
win”
message.
This
can
be
done
by
adding
and
removing
event
listeners
that
associate
an
ENTER
FRAME
event
with
an
animating
function.
(This
is
worth
only,
so
leave
it
for
last).
When
the
ducks
are
“shot”
the
waves
and
clouds
should
also
be
removed
from
view
AND
from
their
respective
arrays.
Game
should
reset
after
player
has
won
or
lost
many
times.
(not
just
once)
I have most of this done, I'm just having trouble with the scoreboard. Any tips on how to reset everything, and code the you win sign would help too.
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.text.TextField;
[SWF(width="800", height="600", backgroundColor="#E6FCFF")]
public class Main extends Sprite
{
private var _sittingDucks:Array = []; //always set your arrays with [] at the top
public var _scoreDisplay:TextField
public function Main()
{
//adding the background, and positioning it
var background:Background = new Background();
this.addChild(background);
background.x = 30;
background.y = 100;
for(var i:uint = 0; i < 5; i++)
{
//adding the first cloud, and positioning it
var clouds:Clouds = new Clouds();
this.addChild(clouds);
clouds.x = 130 + Math.random() * 600; //130 to 730
clouds.y = 230;
clouds.speedX = Math.random() * 3;
clouds.width = clouds.height = 200 * Math.random()//randomly changes the clouds demensions
}
var waves:Waves = new Waves();
this.addChild(waves);
waves.x = 0;
waves.y = 510;
waves.speedX = Math.random() * 3;
for(var j:uint = 0; j < 8; j++)
{
var ducks:Ducks = new Ducks();
this.addChild(ducks);
ducks.x = 100 + j * 100;
ducks.y = 475;
_sittingDucks.push(ducks);
ducks.addEventListener(MouseEvent.CLICK, ducksDestroy);
}
var waves2:Waves = new Waves();
this.addChild(waves2);
waves2.x = 0;
waves2.y = 520;
waves2.speedX = Math.random() * 3;
var setting:ForeGround = new ForeGround();
this.addChild(setting);
setting.x = 0;
setting.y = 50;
setting.width = 920;
var board:ScoreDisplay = new ScoreDisplay();
this.addChild(board);
board.x = 570;
board.y = 35;
}
private function ducksDestroy(event:MouseEvent):void
{
//store the crow we clicked on in a new array
var clickedDuck:Ducks = Ducks(event.currentTarget);
//remove it from the crows array
//find the address of the crow we are removing
var index:uint = _sittingDucks.indexOf(clickedDuck);
//remove it from the array with splice
_sittingDucks.splice(index, 1);
//remove it from my document's display list
this.removeChild(clickedDuck);
}
}
import flash.events.Event;
import flash.events.MouseEvent;
import flash.text.TextField;
import ScoreDisplayBase; // always import the classes you are using
public class ScoreDisplay extends ScoreDisplayBase
{
private var txt:TextField; // where is it initialized?
private var score:uint = 0;
public function ScoreDisplay()
{
super(); // do you init txt here?
}
public function scoreUpdate():void
{
score += 10; // ok, so I suppose that your score does not represent the remaining ducks as you said, just only a score
txt.text = score.toString();
}
}
Aaaalrighty:
You do want to create the TextField txt in ScoreDisplay's constructor. Instantiate it, set its text to initial score (0), and addChild(txt).
In order to set the score later, we'll need a way to reference the display.
//you want a reference to the ScoreDisplay, not this
public var _scoreDisplay:TextField //no
public var _scoreDisplay:ScoreDisplay //yes
and when you create it in the Main constructor, we need to keep a reference.
_scoreDisplay = :ScoreDisplay = new ScoreDisplay();
this.addChild(_scoreDisplay );
_scoreDisplay .x = 570;
_scoreDisplay .y = 35;
If you want to be able to reset the game, I would recommend taking the duck creation and placing it in a method outside the Main class' constructor. You should also create a 'reset' function that sets the score (and the display) to 0 in ScoreDisplay.
private function spawnDucks() {
for(var j:uint = 0; j < 8; j++)
{
var ducks:Ducks = new Ducks();
this.addChild(ducks);
ducks.x = 100 + j * 100;
ducks.y = 475;
_sittingDucks.push(ducks);
ducks.addEventListener(MouseEvent.CLICK, ducksDestroy);
}
}
and then you call it in the constructor, and can call it again when you need to reset the game.
ducksDestroy(event:MouseEvent) is going to be where you want to recalculate the score, check if you've won, show a message, and reset the game. You'll need some kind of popup to display, here is a decent one if you don't know where to get started at with that.
private function ducksDestroy(event:MouseEvent):void
{
//store the crow we clicked on in a new array
var clickedDuck:Ducks = Ducks(event.currentTarget);
//remove it from the crows array
//find the address of the crow we are removing
var index:uint = _sittingDucks.indexOf(clickedDuck);
//remove it from the array with splice
_sittingDucks.splice(index, 1);
//remove it from my document's display list
this.removeChild(clickedDuck);
//update the score
_scoreDisplay.scoreUpdate();
//Check if all the ducks are gone
if (_sittingDucks.length == 0) {
//All the ducks are dead, we've won the game!
//create some kind of popup to display.
//add it to the screen, have some form
//of button (or a timer) take it away
//whatever takes the popup away, have it call 'reset'
}
}
private function reset():void
{
//write a reset method to clear the score
_scoreDisplay.reset();
//create some ducks and you're ready to go!
spawnDucks();
}
How can I make my object stop when it reaches the destination i gave it with my mouse click? The code makes the object move towards the point of a mouse click but I can't seem to find out how to make it stop, because it will almost never pass the specific destination point. :/ Somebody who knows how to accomplish this?
public function onMouseDown(evt:MouseEvent)
{
if (this._character != null)
{
_character.isMoving = false;
_character.dx = 0;
_character.dy = 0;
targetX = mouseX - _character.x;
targetY = mouseY - _character.y;
var angle:Number = Math.atan2(targetY,targetX);
var dx:Number = Math.cos(angle) * _character.speed;
var dy:Number = Math.sin(angle) * _character.speed;
_character.dx = dx;
_character.dy = dy;
_character.isMoving = true;
}
}
public function updateCharacter(e:Event):void
{
if (this._character.isMoving)
{
this._character.x += this._character.dx;
this._character.y += this._character.dy;
}
}
Easiest way to do it would be to calculate the angle to the point you want to stop at each time you move. This value should remain the same if you're moving in a straight line until you pass the point you're trying to stop at, at which point it will change drastically.
Once this happens, simply move your object back to the position it should have stopped at before you render it again.
I've created a demo with source code for you. There's a fair amount of code, so rather than posting everything here you can download the source instead:
http://martywallace.com/testing/gotoPoint.zip
Try this
package
{
import flash.display.MovieClip;
import flash.events.Event;
import flash.events.MouseEvent;
public class Guest extends MovieClip
{
var walkSpeed:Number = 5;
var oldPosX;
var oldPosY;
public function Guest()
{
stage.addEventListener(MouseEvent.CLICK, walk);
}
function walk(event:MouseEvent):void
{
oldPosX = parent.mouseX;
oldPosY = parent.mouseY;
rotation = Math.atan2(oldPosY - y,oldPosX - x) / Math.PI * 180;
addEventListener(Event.ENTER_FRAME, loop);
}
function loop(event:Event):void
{
// see if you're near the target
var dx:Number = oldPosX - x;
var dy:Number = oldPosY - y;
var distance:Number = Math.sqrt((dx*dx)+(dy*dy));
if (distance<walkSpeed)
{
// if you are near the target, snap to it
x = oldPosX;
y = oldPosY;
removeEventListener(Event.ENTER_FRAME, loop);
}
else
{
x = x+Math.cos(rotation/180*Math.PI)*walkSpeed;
y = y+Math.sin(rotation/180*Math.PI)*walkSpeed;
}
}
}
}
Similar questions have been asked many times.
However, see the code in my answer here that should explain how to move and stop.
Movement of Objects in a simulation