Flash - Adding objects to a scene randomly, at certain set points - actionscript-3

I'm in the process of creating a simple Flash game using ActionScript 3.0 and have come across an issue when spawning my obstacles onto the scene. My aim is to have approximately 10 points across the x axis (remaining at the same y axis) and when spawning the obstacles into my scene it will pick 2-4 of those points randomly and spawn them on them.
I've got the obstacles to spawn randomly but cannot figure out how to make them spawn at random set points, from a list. If anyone could help, I would be much appreciative. Thanks
EDIT:
The code I have so far:
var a:Array = new Array();
for (var count=0; count< 5; count++) {
a[count] = new asteroidOne();
a[count].x = 100 * count + (Math.floor(Math.random() * 200));
a[count].y = 100;
addChild(a[count]);
}
// Asteroid obstacle spawning 2.0
player.addEventListener(Event.ENTER_FRAME, obstacleMove);
function obstacleMove(evt:Event):void {
for (var i=0; i< 5; i++) {
a[i].y += 5;
if (a[i].y == 480) {
a[i].y = 0;
}
if (player.hitTestObject(a[i])) {
trace("HIT");
}
}
}

Assuming you have your spawn points in an array, you could do the following:
var spawnPoints:Array = [100,200,250,300,450,500,600,800]; //your list of spawn x locations
spawnPoints.sort(randomizeArray); //lets randomize the spwanPoints
function randomizeArray(a:*, b:*):int {
return ( Math.random() < .5 ) ? 1 : -1;
}
var a:Vector.<asteroidOne> = new Vector.<asteroidOne>(); //the array for your astroids - changed to vector for possible performance and code hint improvement (basically the same as Array but every object has to be of the specified type)
for (var count:int=0; count < 5; count++) {
a.push(new asteroidOne());
a[count].x = spawnPoints.pop(); //pop removes the last element from the array and returns it
a[count].y = 100;
addChild(a[count]);
}
EDIT
To address you comments, here is a decent example:
import flash.events.Event;
import flash.events.TimerEvent;
import flash.utils.Timer;
var spawnTimer:Timer = new Timer(10000); //timer will tick every 10 seconds
spawnTimer.addEventListener(TimerEvent.TIMER, spawn, false, 0, true); //let's run the spawn function every timer tick
spawnTimer.start();
var spawnPoints:Array = [100,200,250,300,450,500,600,800]; //your list of spawn x locations
var spawnAmount:int = 5; //how many asteroids to have on the screen at once (you could increase this over time to make it more difficult)
var asteroids:Vector.<asteroidOne> = new Vector.<asteroidOne>(); //the array for your asteroids - changed to vector for possible performance and code hint improvement (basically the same as Array but every object has to be of the specified type)
spawn(); //lets call it right away (otherwise it will won't be called until the first timer tick in 10 seconds)
//calling this will spawn as many new asteroids as are needed to reach the given amount
function spawn(e:Event = null):void {
if(asteroids.length >= spawnAmount) return; //let's not bother running any of the code below if no new asteroids are needed
spawnPoints.sort(randomizeArray); //lets randomize the spwanPoints
var spawnIndex:int = 0;
var a:asteroidOne; //var to hold the asteroid every loop
while (asteroids.length < spawnAmount) {
a = new asteroidOne();
a.x = spawnPoints[spawnIndex];
spawnIndex++; //incriment the spawn index
if (spawnIndex >= spawnPoints.length) spawnIndex = 0; //if the index is out of range of the amount of items in the array, go back to the start
a.y = 100;
asteroids.push(a); //add it to the array/vector
addChild(a); //add it to the display
}
}
player.addEventListener(Event.ENTER_FRAME, obstacleMove);
function obstacleMove(evt:Event):void {
//this is the same as a backwards for loop - for(var i:int=asteroids.length-1;i >= 0; i--)
var i:int = asteroids.length;
while(i--){ //since we are potentially removing items from the array/vector, we need to iterate backwards - otherwise when you remove an item, the indices will have shifted and you'll eventually get an out of range error
asteroids[i].y += 5;
if (asteroids[i].y > stage.stageHeight || asteroids[i].x > stage.stageWidth || asteroids[i].x < -asteroids[i].width || asteroids[i].y < -asteroids[i].height) {
//object is out of the bounds of the stage, let's remove it
removeChild(asteroids[i]); //remove it from the display
asteroids.splice(i, 1); //remove it from the array/vector
continue; //move on to the next iteration in the for loop
}
if (player.hitTestObject(asteroids[i])) {
trace("HIT");
}
}
}
function randomizeArray(a:*, b:*):int {
return ( Math.random() < .5 ) ? 1 : -1;
}

Related

if a ball is hitting a box from every side, how to know which side hittest is true

i am creating a brick breaker game in which ball is hitting a square object. i want to change the direction of ball when he hit square object.
square is of 15 px. and
ball is of 10 px.
for example
if hits on the right side, Speed in x direction will reverse
if hits on the left side, Speed in x direction will reverse
if hits on the up side, Speed in y direction will reverse
if hits on the down side , Speed in y direction will reverse.
I tried it hard but found nothing. any help will be appreciated. here is the code:
import flash.events.*;
import flash.display.*;
stop();
// speed of ball in x and y
var speedx : Number = 10;
var speedy : Number = 10;
// start running
function begin_code (event:MouseEvent):void{
addEventListener(Event.ENTER_FRAME,move_ball);
stage.addEventListener(MouseEvent.MOUSE_DOWN,by_key);
addEventListener(Event.ENTER_FRAME,ht_mc);
start_game.alpha=0;
}
start_game.addEventListener(MouseEvent.CLICK, begin_code) ;
//start ball moving
function move_ball(e:Event):void{
Ball.x += speedx;
Ball.y += speedy;
Ball.rotation +=speedx;
var ballposition : Number = Ball.x -Hitbar.x;
var ballhitpercent : Number = (ballposition /(Hitbar.width-Ball.width));
if(Ball.x <= x1.x+Ball.width/2){
speedx = speedx *-1;
}
if(Ball.x >= x2.x-Ball.width/2){
speedx = speedx *-1;
}
if(Ball.y <=(55)){
speedy = speedy *-1;
}
else if (Ball.y >= stage.stageHeight-Ball.height){
speedy = speedy *-1;
trace("hit y");
}
//start ball angle
else if(Ball.hitTestObject(Hitbar)){
speedx = ballhitpercent*10;
speedy = speedy *-1;
}
}
//start hitbar moving
// code for keys!!!!!!!!!
var distance : Number = 0;
function by_key(e:MouseEvent):void{
if (mouseX>Hitbar.x){
distance = (mouseX-Hitbar.x);
Hitbar.x += distance;
}
else if (mouseX<Hitbar.x){
distance = (Hitbar.x-mouseX);
Hitbar.x -= distance;
}
}
//restart
function by_key_up(e:MouseEvent):void{
}
stage.addEventListener(MouseEvent.MOUSE_UP, by_key_up);
function pLimiter(e:Event):void{
Hitbar.y = 406.2;
if (Hitbar.hitTestObject(x1)){
Hitbar.x = 32.6;
}
if (Hitbar.hitTestObject(x2)){
Hitbar.x = 287.55;
}
}
addEventListener(Event.ENTER_FRAME, pLimiter);
// Bricks set hit
var pickup:Array =new Array();
for (var i = 0;i<numChildren;i++){
if(getChildAt(i) is abc){
pickup.push(getChildAt(i));
}
}
function hittest_box(e:Event):void{
for (var f = 0;f<pickup.length;f++){
if (Ball.hitTestObject(pickup[f])){
if(pickup[f].parent) {
pickup[f].parent.removeChild(pickup[f]);
}
}
}
}
function ht_mc(e:Event):void{
for (var j = 0;j<pickup.length;j++){
var ball_pos_x: Number = ( Ball.x - pickup[j].x);
var ball_pos_y: Number = ( Ball.y - pickup[j].y);
if( ball_pos_x < 11.5 && ball_pos_x > -11.5){
speedy = speedy *-1;
trace("box y");
addEventListener(Event.ENTER_FRAME,hittest_box);
}
else if ( ball_pos_y < 11.5 && ball_pos_y > -11.5){
speedx = speedx *-1;
trace("boxx");
addEventListener(Event.ENTER_FRAME,hittest_box);
}
}
}
edit
To turn my two point example into a one-ball-moving version, the main thing you need to do is to declare the p1 then move the ball and then declare p2.
p1.x = ball.x;
p1.y = ball.y;
moveBall();
p2.x = ball.x;
p2.y = ball.y;
Now you have p1 and p2. Run the calculations as below to find which brick is hit and which side was hit.
A few more calculations will be needed (and you'll have to loop through this whole thing for every collision that occurs in between frames). But I can't post all that here.
Original Answer
My answer is meant to be able to be used in any situation in which you have a point object that is moving and needs to detect when it collides with sides of a square. Simple hitTest solutions are inadequate because a point object moving fast enough could "jump over" a corner. Additionally, if the walls of the object are infinitely thin, there couldn't easily be a "collision", so what we really want to calculate is whether or not the point passed through a wall during the last tick. Also, if the ball is going fast enough to jump over an entire brick, you'll get collision with bricks that are surrounded by other bricks. This method fixes that. Here we go...
My illustration shows the basic method I'm using. It breaks down like this:
define end points for each line segment (corners of block and 2 positions of ball)
check first if ball bounding box and block overlap with simple hit test type of conditional statement
if they do, determine which edge the ball passed through by comparing the 3 angles as shown in the diagram (angle 1 just has to be between angle 2 and 3)
then determine which of the points that make up the lines that the ball passed through during the last frame transition and determine which of these points is nearest the where the ball was on the previous frame (this will be a point contained in the line that the ball will hit!)
lastly, determine which line contains that point
There could certainly be some optimizations added, but this works like a charm for what it is.
This code will add a bunch of LineTestBlock objects to the stage as well as 2 Point objects associated with circles. Move the Points such that 1 will represent the first frame location of the ball, and the second Point can be dragged to a place representing where the ball will be on the 2nd frame. You will see that wherever the line between these passes through, the closest edge of a block will light up.
And here is the code. It is a main document class named BlockDoc and a LineTestBlock class.
package {
import flash.display.MovieClip;
import flash.geom.Point;
import flash.events.Event;
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.text.TextField;
import flash.text.TextFormat;
import flash.geom.Rectangle;
import flash.display.Shape;
public class BlockDoc extends MovieClip {
// this holds the circle handles (the actual sprite objects)
var cArray = new Array();
// this holds all the bricks
var brickArray = new Array();
//ball position in frame 1
//these are just point objects. p1 will need to represent where the ball was on the last frame
var p1:Point = new Point();
// and p2 will need to be where the ball is "going" to be on the next frame
// although, if it hits something, the ball will not end up p2 on the next screen update, obviously
var p2:Point = new Point();
// just an array used for putting the 1 and 2 on the handles
// you won't eventually need this, of course
var pointNameArray:Array = new Array();
// this is the Sprite object that holds the rectangle
// which describes the bounding box made by the ball in the two frames
var recASprite:Sprite = new Sprite();
// rec is the Sprite that holds the bricks, or you could just put the bricks on the stage. I like having them in a container so I can remove them all or move them all or change them all just by changing the container
var rec:Sprite = new Sprite();
// lines is the Shape object that all the bricks edge lines are drawn to
var lines:Shape = new Shape();
// mainLine is the Shape object that the line connecting the two ball positions is drawn to
var mainLine:Shape = new Shape();
// you won't need either of these lines in your version
public function BlockDoc() {
// just a background so you can see the alpha better.
// you won't need this in yours
var bkgnd:Sprite = new Sprite();
bkgnd.graphics.lineStyle();
bkgnd.graphics.beginFill(0x0);
bkgnd.graphics.drawRect(0,0,stage.stageWidth,stage.stageHeight);
bkgnd.graphics.endFill();
addChild(bkgnd);
makeBricks();
createArray();
addChild(mainLine);
addChild(lines);
createHandles(); // creates the circle handles
addEventListener(Event.ENTER_FRAME,gameTick);
}
private function drawLine(container:Shape,p1:Point,p2:Point,color:uint=0xffbbee):void{
container.graphics.clear();
container.graphics.lineStyle(2,color);
container.graphics.moveTo(p1.x,p1.y);
container.graphics.lineTo(p2.x,p2.y);
}
private function makeBricks():void{
// creates rectangle object defined by frame 1 and 2 of ball position
updateBallBoundingBox(recASprite,p1,p2);
addChild(rec); // the parent for all bounding boxes
makeSimpleRectangle(recASprite); // this is the bounding box for the 2 positions of the ball
// these are the bricks
for (var i:int = 0; i < 5; i++){
for (var j: int = 0; j < 3; j++){
var b: LineTestBlock = new LineTestBlock(50,25);
rec.addChild(b);
b.alpha = .5;
brickArray.push(b);
b.x = 100 * i + 50;
b.y = 80 * j +100;
// top left corner of brick
b._p1.x = b.x;
b._p1.y = b.y;
// top right corner of brick
b._p2.x = b.x + b.width;
b._p2.y = b.y;
// bottom right corner of brick
b._p3.x = b.x + b.width;
b._p3.y = b.y + b.height;
// bottom left corner of brick
b._p4.x = b.x;
b._p4.y = b.y + b.height;
}
}
}
private function makeSimpleRectangle(rect:Sprite,color:uint = 0x0):void{
rect.graphics.lineStyle(0,0x0,0);
rect.graphics.beginFill(color,.5);
rect.graphics.drawRect(0,0,50,50);
rect.graphics.endFill();
}
private function updateBallBoundingBox(rec:Sprite,p1:Point,p2:Point):void{
rec.x = Math.min(p2.x,p1.x);
rec.y = Math.min(p2.y,p1.y);
rec.width = Math.max(p2.x,p1.x)-rec.x;
rec.height = Math.max(p2.y,p1.y)-rec.y;
}
private function checkIntersection(A1:Point,A2:Point,B1:Point,B2:Point):Boolean{
// adjusts the current "bounding box" using the position 1 and position 2 of the ball
updateBallBoundingBox(recASprite,A1,A2);
// if bounding boxes hit
// define the angles to be used (further optimization can easily be done here by
// only checking the 2 possible sides of contact, or using a look up table
// instead of using Math.atan2)
// angle between position 1 and 2 of the ball
var A1_A2_r:Number = Math.atan2(A2.y - A1.y,A2.x - A1.x);
// angle between position 1 and one corner
var A1_B1_r:Number = Math.atan2(B1.y - A1.y,B1.x - A1.x);
// angle between position 1 and the second corner
var A1_B2_r:Number = Math.atan2(B2.y - A1.y,B2.x - A1.x);
// and if angle from position 1 to position 2 is between angle from one corner to the other corner...
if (A1_A2_r > A1_B1_r && A1_A2_r < A1_B2_r){
// return true
return true;
} else {
return false;
}
}
private function gameTick(e:Event):void{
// p1 and p2 are the point objects that describe frame 1 and frame 2 of the moving ball
// for actual moving ball:
//p1.x = Ball.x;
//p1.y = Ball.y;
//move_ball();
//p2.x = Ball.x;
//p2.y = Ball.y;
//or use this for experimenting with the handles
//p1.x = cArray[0].x;
//p1.y = cArray[0].y;
//p2.x = cArray[1].x;
//p2.y = cArray[1].y;
// draw line between position 1 and 2
drawLine(mainLine,p1,p2);
checkCollision();
}
private function checkCollision():void{
// clears previously drawn lines on bricks
lines.graphics.clear();
var xDir:int;
var yDir:int;
// determine direction of ball (you probably already have this as a variable somewhere so probably don't need this
if (p1.x < p2.x){
xDir = 1;
} else if (p1.x > p2.x){
xDir = -1;
} else {
xDir = 0;
}
if (p1.y < p2.y){
yDir = 1;
} else if (p1.y > p2.y){
yDir = -1;
} else {
yDir = 0;
}
// this array will hold all the Point objects involved in true collision
var arr:Array = new Array();
var arrLines:Array = new Array();
var nearestDistance:Number = 2000; // change this to something sensible (maybe the speed of the ball...);
var nearestPoint:Point = new Point(); // change this to something sensible
// first check bounding boxes
for (var i:int = 0; i < brickArray.length; i++){
var b:LineTestBlock = brickArray[i];
var a:Sprite = recASprite;
updateBallBoundingBox(a,p2,p1);
// this is just a bounding box hittest, and hitTestObject used to work, but I broke something, so I have to do it like this now
if (a.x+a.width > b.x && a.y + a.height > b.y && a.x < b.x+b.width && a.y<b.y+b.height){
// now that we know the rectangles overlap, we check the two sides that are hittable
// instead of xDir and yDir just use your variable or property that holds the x and y speeds;
// I use xDir and yDir because my example doesn't actually use motion.
if (xDir > 0){
if (checkIntersection(p1,p2,b._p1,b._p4)){
// ball hits left wall of brick
//drawLine(lines,b._p1,b._p4,0x00ffee);
// instead of drawLine above, put in your function for collision of left wall
arr.push(b._p1,b._p4);
arrLines.push({p1:b._p1,p2:b._p4,brick:brickArray[i]});
// that last array there simply holds an object with 3 properties
// for use later in comparing what should actually receive the hit.
}
if (yDir > 0) {
if (checkIntersection(p2,p1,b._p1,b._p2)){
// ball hits top wall of brick
arr.push(b._p1,b._p2);
arrLines.push({p1:b._p1,p2:b._p2,brick:brickArray[i]});
}
} else if (yDir < 0){
if(checkIntersection(p2,p1,b._p3,b._p4)){
// ball hits bottom wall of brick
arr.push(b._p3,b._p4);
arrLines.push({p1:b._p3,p2:b._p4,brick:brickArray[i]});
}
}
} else if (xDir < 0){
if(checkIntersection(p2,p1,b._p2,b._p3)){
// ball hits right edge of brick
arr.push(b._p2,b._p3);
arrLines.push({p1:b._p2,p2:b._p3,brick:brickArray[i]});
}
if (yDir > 0) {
if (checkIntersection(p2,p1,b._p1,b._p2)){
// ball hits top edge of brick
arr.push(b._p1,b._p2);
arrLines.push({p1:b._p1,p2:b._p2,brick:brickArray[i]});
}
} else if (yDir < 0){
if(checkIntersection(p2,p1,b._p3,b._p4)){
// ball hits bottom edge of brick
arr.push(b._p3,b._p4);
arrLines.push({p1:b._p3,p2:b._p4,brick:brickArray[i]});
}
}
}
}
}
for (var i:int = 0; i < brickArray.length; i++){
brickArray[i].alpha = 0.3;
}
for (var i:int = 0; i < arr.length; i++){
var d:Number = dist(arr[i],p1);
if(d < nearestDistance){
nearestDistance = d;
nearestPoint = arr[i];
}
}
for (var j:int = 0; j < arrLines.length; j++){
if (arrLines[j].p1 == nearestPoint){
drawLine(lines,nearestPoint,arrLines[j].p2);//THIS IS THE EDGE THAT GETS HIT!!
arrLines[j].brick.alpha = 0.7; // THIS IS THE BRICK THAT GETS HIT!!
} else if (arrLines[j].p2 == nearestPoint){
drawLine(lines,nearestPoint,arrLines[j].p1); //THIS IS THE EDGE THAT GETS HIT!!
arrLines[j].brick.alpha = 0.7; // THIS IS THE BRICK THAT GETS HIT!!
}
}
}
private function dist(p1:Point, p2:Point):Number{
var dx:Number = p1.x - p2.x;
var dy:Number = p1.y - p2.y;
return Math.sqrt(dx * dx + dy * dy);
}
private function createHandles():void{
// these are the circle handles for moving the balls around
for (var i:int = 0; i < 2; i++){
var c:Sprite = new Sprite();
var tf:TextField = new TextField();
c.graphics.lineStyle();
c.graphics.beginFill(0x5555ff+0x5555*i);
c.graphics.drawCircle(0,0,10);
c.graphics.endFill();
addChild(c);
c.addChild(tf);
c.x = 50*i+50;
c.y = 50;
c.addEventListener(MouseEvent.MOUSE_DOWN,dragObject);
c.addEventListener(MouseEvent.MOUSE_UP,dragStopObject);
cArray.push(c);
tf.height = 20;
tf.width = 10;
tf.text = pointNameArray[i];
tf.x = -5;
tf.y = -10;
tf.mouseEnabled = false;
}
}
private function createArray():void{
// just used for putting the fancy 1 and 2 on the handles
pointNameArray = ["1","2"];
}
private function radsFromPoints(p1:Point,p2:Point):Number{
return Math.atan2(p1.y - p2.y, p1.x - p2.y);
}
private function dragObject(me:MouseEvent):void{
me.target.startDrag(true);
}
private function dragStopObject(me:MouseEvent):void{
me.target.stopDrag();
}
}
}
and the LineTestBlock class:
package {
import flash.display.*;
import flash.text.*;
import flash.geom.Point;
public class LineTestBlock extends Sprite{
var _p1:Point = new Point();
var _p2:Point = new Point();
var _p3:Point = new Point();
var _p4:Point = new Point();
public function LineTestBlock(w:Number,h:Number) {
var s:Sprite = new Sprite();
s.graphics.lineStyle();
s.graphics.beginFill(0xff5599,0.5);
s.graphics.drawRect(0,0,w,h);
s.graphics.endFill();
addChild(s);
}
}
}
Obviously the stuff about lines and alpha are just for display purposes. You'll be able to erase a bunch of this code and will need to replace some of it with actual bounce functions.
One thing to keep in mind is how you deal with the bouncing off of the ball. You can't simply reverse the x or y speed of the ball, as you might think because then it is inside of a brick, or on the opposite side of the brick trying to come back out, which will work, but could look funny and actually allow the ball access to bricks that should be off limits because the are surrounded by other bricks. But I've already answered the question at hand, so I'll stop there.

Creating Falling Snow in AS3/Flash

So I am trying to create random, falling snow in a Flash application. Using the following code I have my snow appearing at a random point on the screen.
var mc:snowFalling = new snowFalling();
addChild(mc);
var result:Number = Math.random() * 100;
var randomX:Number = Math.random() * stage.stageWidth;
mc.x = randomX;
snowFalling is the linkage name.
I am unsure how to have the snow both appear multiple times and respawn. Could someone give me a rundown of the code I will need to do this.
First, you need to create as many snowflakes on the screen as you'd like to have. Then, you need to move each snow flake every frame tick. Something like the following is ONE way you could do it. (there are many ways to accomplish this).
var flakes:Vector.<snowFalling> = new Vector.<snowFalling>(); //an array to store all your snow flakes
var mc:snowFalling;
var mc2:snowFalling;
var columns:int = 5;
var rows:int = 7;
var columnWidth:Number = stage.stageWidth / columns;
var rowHeight:Number = stage.stageHeight / rows;
//make 5 columns
for (var i:int = 0; i < columns; i++) {
mc = new snowFalling();
mc.x = (columnWidth * i);// + (Math.random() * (columnWidth * .25)); //to randomize the x position within the column size
addChild(mc);
flakes.push(mc);
//make 7 rows in each column
for (var j:int = 0; j < rows; j++) {
mc2 = new snowFalling();
mc2.x = mc.x;
mc2.y = (rowHeight * j);// + (Math.random() * (rowHeight * .25)); //to randomize the y within the row size
addChild(mc2);
flakes.push(mc2);
}
}
//run the enterFrame function below every frame tick of the application
this.addEventListener(Event.ENTER_FRAME, enterFrame);
function enterFrame(e:Event):void {
//loop through each snowflake in the flakes array
for (var i:int = 0; i < flakes.length; i++) {
flakes[i].y += 2; //move it down 2 pixels
//check to see if it's off screen, if so, move back to the top of the screen (less it's height so it's just off screen at the top)
if (flakes[i].y > stage.stageHeight) flakes[i].y = -flakes[i].height;
}
}
Or, if you wanted a traditional spawn type method, this:
//create a container to hold all the snowflakes
var snowContainer:Sprite = new Sprite();
addChild(snowContainer);
//this function creates a snowflake and puts it at the top of the screen in a random x spot
function spawnFlake(e:Event = null):void {
var mc:snowFalling;
snowContainer.addChild(mc);
mc.x = Math.random() * (stage.stageWidth - mc.width);
mc.y = -mc.height; //just off screen at the top
}
//create a timer that will call the spanFlake function every second
var timer:Timer = new Timer(1000);
timer.addEventListener(TimerEvent.TIMER, spawnFlake);
timer.start();
this.addEventListener(Event.ENTER_FRAME, enterFrame);
//every frame, iterate through all the children of snowContainer and move the flakes down 2 pixels
function enterFrame(e:Event):void {
var flake:snowFalling;
var i:int = snowContainer.numChildren;
//we need to iterate backwards because we are potentially removing items (which would throw our i value out of whack if iterating forwards)
while(i--){
flake = snowContainer.getChildAt(i) as snowFalling;
flake.y += 2;
//if out of bounds, remove the flake
if (flake.y > stage.stageHeight) {
snowContainer.removeChild(flake);
}
}
}

HitTest Multiple MovieClips with Array

I have a MovieClip exported to ActionScript called smallGainPoints. What i want to do is create multiple instances of this MovieClip on the stage in a linear or diagonal path. When this is accomplished i want a hitTestObject to take place between the Points array and the Player. The points are added to stage where i would want them to be, but the HitTest wont initiate.
Here is how i set up the Functions:
This Function is added in my Gameloop which is called from an onEnterFrame handler:
private function checkPlayerHitPoints():void
{
for (var j:int = 0; j < aPointsArray.length; j++)
{
//get current points in j loop
var currentPoints:smallGainPoints = aPointsArray[j];
//test if player is hitting current point
if (player.hitTestObject(currentPoints))
{
//remove point on stage
currentPoints.destroyPoints()
//remove point from array
aPointsArray.splice(j, 1);
nScore += 5;
updateHighScore();
}
}
}
Im not sure if i did this right but i want to add multiple instances of the points in a line so the player can gather as much points as possible. So i created a function and set the positions then added the function in my constructor as so addPointsToStage() so it doesn't loop every frame.
private function addPointsToStage():void
{
for (var i = 0; i < nPoints; i++)
{
points = new smallGainPoints();
stage.addChild(points);
points.x = (stage.stageWidth / 2);
points.y = (stage.stageHeight / 2);
points = new smallGainPoints();
stage.addChild(points);
points.x = (stage.stageWidth / 2) + 200;
points.y = (stage.stageHeight / 2);
}
This is how I initiated the array:
public var points:smallGainPoints;
private var nPoints:Number = 5;
private var aPointsArray:Array;
Then in my Constructor i added:
aPointsArray = new Array();
So the points are added to the stage but the hittest doesnt work. Please help!
In your addPointsToStage method, you never add your smallGainPoints object to the array.
After this line:
points = new smallGainPoints();
Push the new points object onto the aPointsArray array:
aPointsArray.push(points);
EDIT:
A better way to add your points in a row might be like this:
private function addPointsToStage():void
{
var startPoint:Point = new Point(stage.stageWidth / 2, stage.stageHeight / 2);
var spacing:Number = 50;
for (var i = 0; i < nPoints; i++)
{
points = new smallGainPoints();
aPointsArray.push(points);
addChild(points);
points.x = startPoint.x + (spacing * i);
points.y = startPoint.y;
}
}
This for loop will add a bunch of smallGainPoint objects in a row, starting from the center of the screen and going right.

AS3 which item in an array has the lower value?

I’m trying to make a game like tower defence in AS3 and currently cant find solution to check which item in an array has the lower value of distance between enemy and turret, in order to choose which enemy to attack first.
I'm really stuck with this problem and asking for your help.
Here is a short code:
var enemyArray:Array = new Array();
var turretArray:Array = new Array();
addEventListener(Event.EnterFrame, loop);
// adding enemies
for(var i:int=0; i<3; i++){
var enemy:Enemy = new Enemy();
...
...
enemyArray.push(enemy);
addChild(enemy);
}
// adding turret
for(var t:int=0; t<2; t++){
var turret:Turret = new Turret();
...
...
turret.destinationX = 0;
turret.destinationY = 0;
turret.distance = 0;
turretArray.push(turret);
addChild(turret);
}
// loop
function loop(event:Event):void{
for(var j:int=enemyArray.length-1; j>=0; j--){
for(var k:int=turretArray.length-1; k>=0; k--){
// getting destination
turretArray[k].destinationX = turretArray[k].x - enemyArray[j].x;
turretArray[k].destinationY = turretArray[k].y - enemyArray[j].y;
// getting distance between turret and enemy
turretArray[k].distance = Math.sqrt(turretArray[k].destinationX*turretArray[k].destinationX+turretArray[k].destinationY*turretArray[k].destinationY);
// here i need to get min value from all turrets distance
}
}
}
Looks like you just need to be keeping track of the lowest value you've found as you go rather than overwriting it every time (if I've understood your code, correctly).
// loop
function loop(event:Event):void{
for(var k:int=turretArray.length-1; k>=0; k--)
{
turretArray[k].distance = -1;
for(var j:int=enemyArray.length-1; j>=0; j--)
{
var dx = turretArray[k].x - enemyArray[j].x;
var dy = turretArray[k].y - enemyArray[j].y;
var dist = Math.sqrt(dx * dx + dy * dy);
if(dist < turretArray[k].distance || turretArray[k].distance < 0)
{
turretArray[k].distance = dist;
turretArray[k].destinationX = dx;
turretArray[k].destinationY = dy;
}
}
}
}
Here, we store the initial distance value found in turretArray[k].distance, and only overwrite that if we find a lower one. We set it to -1 each time so we can tell if it's been set, yet, or not.
This is the equation you want:
http://www.mathopenref.com/coorddist.html
sqrt( (turret1X - turret2x)^2 + (turret1Y - turret2Y)^2 )

Error #1009, ActionScript 3, Bullet is null

TypeError: Error #1009: Cannot access a property or method of a null object reference.
at Main_fla::MainTimeline/BulletFire()[Main_fla.MainTimeline::frame32:68]. Is occurring and I have no idea why...PLEASE HELP, it has been days of me troubleshooting this and i am completely lost. Thanks, also, for some reason when I fire the bullet it only goes at 45 degrees and 225 degrees...Thanks guys
//Create an array to hold multiple sprites
var mySpriteHolder:Array = [];
//Create a counter to keep track of the number of sprites
var lbCounter:int = 0;
//Maximum number of sprites on the canvas
var maxLB:int = 1;
//Keypress Code
stage.addEventListener(MouseEvent.CLICK, dropBullet);
//Function for the mouse event to fire bullet
function dropBullet(evt:MouseEvent):void{
var bcos:Number = Math.cos((Turret.rotation) * Math.PI / 180);
var bsin:Number = Math.sin((Turret.rotation) * Math.PI / 180);
//starting x and y
var startx:Number = Turret.x + (15 * bcos);
var starty:Number = Turret.y + (15 * bsin);
//calculates where the bullet needs to go by aiming in front of the gun
var endx:Number = Turret.x + (50 * bcos);
var endy:Number = Turret.y + (50 * bsin);
var Bullet:MovieClip = new bullet();
Bullet.x = startx;
Bullet.y = starty;
Bullet.xspeed = (endx - startx)/5;
Bullet.yspeed = (endx - startx)/5;
mySpriteHolder.push(Bullet);
stage.addChild(Bullet);
//this calls the move down function
stage.addEventListener(Event.ENTER_FRAME,BulletFire);
}
//Function to shoot bullet
function BulletFire(evt:Event):void{
var Bullet:MovieClip;
//Use a for loop to move the Bullets
for(var i:int=0; i<=mySpriteHolder.length; i++){
Bullet = mySpriteHolder[i];
//Bounds Collision
if(Bullet.hitTestObject(Up)){
Bullet.yspeed*=-1;
}
if(Bullet.hitTestObject(Lower)){
Bullet.yspeed*=-1;
}
if(Bullet.hitTestObject(Left)){
Bullet.xspeed*=-1;
}
if(Bullet.hitTestObject(Right)){
Bullet.xspeed*=-1;
}
//Blockade Collision
for(var t in myBlockadeHolder){
if(Bullet.hitTestObject(myBlockadeHolder[t])){
trace("test");
}
}
//Target Collision
for(var c in mytargetHolder){
if(Bullet.hitTestObject(mytargetHolder[c])){
stage.removeChild(Bullet);
mySpriteHolder.splice(i,1);
lbCounter --;
mytargetHolder[c].y = Math.random()*390 + 10;
mytargetHolder[c].x = Math.random()*390 + 10;
while(mytargetHolder[c].hitTestObject(Turret)){
mytargetHolder[c].y = Math.random()*390 + 10;
mytargetHolder[c].x = Math.random()*390 + 10;
}
}
for(var a in mytargetHolder){
for(var s in mytargetHolder){
while(mytargetHolder[a].hitTestObject(mytargetHolder[s])&& a!=s){
mytargetHolder[a].y = Math.random()*390 + 10;
mytargetHolder[a].x = Math.random()*390 + 10;
}
}
for(var g in myBlockadeHolder){
while(mytargetHolder[a].hitTestObject(myBlockadeHolder[g])&& a!=g){
mytargetHolder[a].y = Math.random()*390 + 10;
mytargetHolder[a].x = Math.random()*390 + 10;
}
}
}
}
Bullet.y += Bullet.yspeed;
Bullet.x += Bullet.xspeed;
}
}
mySpriteHolder.splice(i,1);
This line is probably causing some unexpected results (and possibly your error) as you're splicing objects from the array your loop is working incrementally from. It's important to understand that when you splice an object in AS3 you're effectively removing that element and all indices that follow move down the splicing amount. Try working backwards in the loop instead:
for(var i:int=mySpriteHolder.length-1; i>=0; i--){
Also, this line should be moved out of your MouseClick Event as you're adding multiple ENTER_FRAME events every time a bullet is placed. As it is right now it's going to do an additional bulletFire() pass every time you mouse click:
stage.addEventListener(Event.ENTER_FRAME,BulletFire);
For a start:
for (var i:int = 0; i < mySpriteHolder.length; i++) {
...
i.e. change <= to <. If i becomes mySpriteHolder.length then you'll get an out-of-bounds exception.
Next:
stage.removeChild(Bullet);
mySpriteHolder.splice(i--, 1);
i.e. when you remove the current element from the array, you also decrement the index i. It'll get incremented again in the next iteration, so you'll be at the same index. For example, if you delete the 5th element, you want to look at the new 5th element (previously the 6th) in the next iteration, as opposed to the new 6th element (previously the 7th). In your current code, you're inadvertently skipping one - not to mention that itself could give you an out-of-bounds exception.
By "out-of-bounds" I mean Bullet being undefined.