A noob error :3 - actionscript-3

The supplied DisplayObject must be a child of the caller.
What a basic error right? Iv fixed many of these but maybe im just over thinking this... idk anyways...
I have a game drawing a map based off a perlin noise and a bitmap, well drawing the WHOLE map and moving EVERY tile of the map is unpractical and not smart. So iv set it to draw a section of the map and when you push the arrow keys it deletes the tiles and re draws them with the new array of tiles it needs. BUT
When i move from the first set of the map, to the next set it deletes the tiles and re draws them fine, but when i try to move to another section of the map it gives me supplied display object must be a child of the caller. Which i get the error, it says what its deleting has to be within what its deleting from (i think). But im not sure why it works the first time but not the next? My guess is it has something to do with the worldTiles sprite that im adding the tiles too. But i dont know how to go about fixing it
If you set the map width to 1600, then push the right key once, it will draw the next area tiles, but you cant go anywhere else without getting an error
http://www.fastswf.com/xl3LXbg
On the level class, which is a class that runs when it reaches a certain frame on the timeline. Aka a class connected to a MovieClip on the screen. Within that class I create a new World.class object
world = new World(this);
This just creates the worldTiles sprite onto the level class and runs the first generate tile when the class is first ran
public function World(parentMC:MovieClip)
{
worldTiles = new Sprite();
parentMC.addChild(worldTiles);
generateTile();
}
This is what sets the X and Y of the drawRect and the X and Y of the array it draws the tiles of.
if (k.keyCode == Keyboard.RIGHT)
{
world.deleteTiles();
world.X += 800/world.TILE_SIZE;
world.generateTile();
X += 800/MAP_SCALE;
}
if (k.keyCode == Keyboard.LEFT)
{
world.deleteTiles();
world.X -= 800/world.TILE_SIZE;//the actual X width of the tiles being drawn
world.generateTile();
X -= 800/MAP_SCALE;//drawRect
}
This is what im using to delete the tiles on the screen.
public function deleteTiles()
{
if (tilesInWorld.length > 0)
{
for (var i:int = 0; i < tilesInWorld.length; i++)
{
worldTiles.removeChild(tilesInWorld[i]);
}
}
}
And then when the tiles get re add'd it re runs the generateTiles() function which is this
public function generateTile()
{
for (var i=X; i < X+80 i++) //using 80,60 cause thats the screen width / 10, which is default tile size
{
for (var j=Y+60; j < grid_height; j++)
{
hm = heightmap[i][j];
if (hm >= 0.84)
{
tile = new Water();
}
else if (hm >= 0.8 && hm < 0.84)
{
tile = new Shallow();
}
else if (hm >= 0.7 && hm < 0.8)
{
tile = new Sand();
}
else if (hm >= 0.2 && hm < 0.7)
{
tile = new Tile();
}
else
{
tile = new Stone();
}
tile.width = TILE_SIZE;
tile.height = TILE_SIZE;
worldTiles.x = 0;
worldTiles.y = 0;
tile.x = TILE_SIZE * (i % grid_width);
tile.y = TILE_SIZE * (j % grid_height);
tilesInWorld.push(tile);
worldTiles.addChild(tile);
}
}
}

You're never removing the tiles from your tilesInWorld array, so the second time you call deleteTiles it's attempting to delete tiles that were already deleted. Try something like this:
public function deleteTiles()
{
while (tilesInWorld.length > 0)
{
// this will remove the tile from the array, then remove it from the display list
worldTiles.removeChild(tilesInWorld.pop());
}
}

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.

How can I code an object to spawn onto the stage in the Actions panel?

I am trying to introduce a new ball into my pong game, sort of like a power up. I am writing all of my code in the Actions panel in the first frame. The new ball should appear on the stage and start moving around randomly like the original ball. Although I am using a code snippet and not .as file. So all of my code is in the Actions panel(Accessed by pressing f9).
I would also like my dynamic text box to merge with the stage colour so that you can't see the white background.
I can't show you what the fla looks like because I have less than 10 reputation, but the dynamic text box will not merge into the background and instead has a white surrounding. This hides the ball when the ball goes up.
import flash.events.Event;
import flash.ui.Mouse;
//hide mouse
Mouse.hide();
init(); //initialises everything
var bSpeedX:int = -3.5;
var bSpeedY:int = -2.5;
// assign a maximum speed to the AI
var compPaddleSpeed:int = 3.5;
var pScore:int = 0;
var cScore:int = 0;
// Updates the score
function scoreUpdate():void {
playerScore.text = ("Player Score: " + pScore);
computerScore.text = ("AI Score: " + cScore);
}
function init():void //tells flash not to return values
{
stage.addEventListener(Event.ENTER_FRAME, loop);
}
/*we want the ySpeed to be larger if there
is a greater difference between the y
positions of the ball and paddle, so I started with
(gameBallY-padY). To convert this difference
into a number between -1 and 1, I divided
this number by 25, which
is half the height of the paddle. Finally, I wanted
the ySpeed to be more powerful than
just -1 to 1, and after a bit of trial and error
I decided to times by 5 at the end
to change the total magnitude of the new ySpeed.*/
//defying the laws of Physics
function calculategameBallAngle(padY:Number, gameBallY:Number):Number
{
var ySpeed:Number = 5 * ((gameBallY-padY) / 25 );
return ySpeed;
}
//main loop
function loop(e:Event):void
{
//makes the paddle track the mouse
playerPaddle.y = mouseY;
//paddle AI
if(compPaddle.y < gameBall.y - 10){
compPaddle.y += compPaddleSpeed;//make it go up
} else if(compPaddle.y > gameBall.y + 10){
compPaddle.y -= compPaddleSpeed;//make it go down
}
//Collisions
if( playerPaddle.hitTestObject(gameBall) == true ){
if(bSpeedX < 0){
bSpeedX *= -1;
bSpeedY = calculategameBallAngle(playerPaddle.y, gameBall.y);
}
} else if(compPaddle.hitTestObject(gameBall) == true ){
if(bSpeedX > 0){
bSpeedX *= -1;
bSpeedY = calculategameBallAngle(compPaddle.y, gameBall.y);
}
}
//makes the gameBall move
gameBall.x += bSpeedX; //each frame, we add the bSpeedX to the ball's x position.
gameBall.y += bSpeedY; //same for the bSpeedY to the ball's y postion.
// checks to see if the ball misses the paddle
if(gameBall.x <= gameBall.width/2){
gameBall.x = gameBall.width/2;
bSpeedX *= -1;
cScore ++;
scoreUpdate();
//keeps the ball within the stage
} else if(gameBall.x >= stage.stageWidth-gameBall.width/2){
gameBall.x = stage.stageWidth-gameBall.width/2;
bSpeedX *= -1;
pScore ++;
scoreUpdate();
}
if(gameBall.y <= gameBall.height/2){
gameBall.y = gameBall.height/2;
bSpeedY *= -1;
}
else if(gameBall.y >= stage.stageHeight-gameBall.height/2){
gameBall.y = stage.stageHeight-gameBall.height/2;
bSpeedY *= -1;
}
//-------------------------------------------------------
//keeps the player paddle within the stage
//check if paddle is above top of the screen
if(playerPaddle.y - playerPaddle.height/2 < 0){
playerPaddle.y = playerPaddle.height/2;
} else if(playerPaddle.y + playerPaddle.hieght/2 > stage.stageHeight){
playerPaddle.y = stage.stageHeight - playerPaddle.height/2;
//check if paddle is below bottom of the screen
} else if(playerPaddle.y + playerPaddle.height/2 > stage.stageHeight){
playerPaddle.y = stage.stageHeight - playerPaddle.height/2;
}
}
If you only want your ball to be replaced with new one which has diffrent grphics and/or speed you can for example export your ball class to action script:
Library>RMB on your symbol>Properties>ActionScript Linkage>Export for ActionScript
Type your class name under Class: field like "MyBallClass" and hit OK.
Now you can construct this ball in your code and replace old one like this:
var newBall:MyBallClass = new MyBallClass();
addChild(newBall);
newBall.x = gameBall.x; newBall.y = gameBall.y;
gameBall = newBall;
Additionally you can define new variable like var speedModifier:Number = 1; to use with:
gameBall.x += bSpeedX * speedModifier;
gameBall.y += bSpeedY * speedModifier;
And change that also when you change the ball.
If You want to have multiple balls at same time You really should consider build this in OOP. For simplest example in addition to previous one
You can create MyBallClass.as file and write in it something like:
package
{
import flash.display.Sprite;
import flash.geom.Point;
public class MyBallClass extends Sprite
{
public var speedFactor:Number;
public var speed:Point = new Point(-3.5, -2.5);
public function MyBallClass(x:Number, y:Number, speedFactor:Number = 1)
{
this.x = x; this.y = y;
this.speed = speed;
}
}
}
Now you can create container for all the balls in yor game.
var balls:Vector<MyBallClass> = Vector<MyBallClass>([]);
and run your physics for all of them in a loop.
Generally main code would look something like this:
var balls:Vector.<MyBallClass> = Vector.<MyBallClass>([]);
addBall(...)//place first ball.
function loop(e:Event):void {
processBalls();
if(wantToAddNewSuperSpeedBall) addBall(x,y,3);
...
}
function processBalls() {
for (var i:int = 0; i < balls.length; i++) {
detecCollision(balls[i]);
moveBall(balls[i]);
//any code that process a single ball...
}
}
function addBall(x:Number, y:Number, speedFactor:Number = 1) {
var newBall:MyBallClass = new MyBallClass(x,y, speedFactor);
addChild(newBall);
balls.push(newBall);
}
function moveBall(ball:MyBallClass) {
ball.x += ball.speed.x * ball.speedFactor;
ball.y += ball.speed.y * ball.speedFactor;
}
So you should modify all functions which affect ball behavior to work with ball passed as argument, not only one specific instance and then use them for all balls.
There are more to cover in this topic and this isn't maybe the best approach but I've tried to make it easy to understend. There a lot of guides for OOP so you can get better idea about what is going on if you read them.
I hope that helped you somehow.

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

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

supplied DisplayObject must be a child of the caller fix

Im confused as to why my codes saying this,
My game creates a procedural generated map and the map is split into chuncks. The map works fine, i can switch areas and stuff no problem but when i delete a tree tile and re-add a grass tile then try to switch areas it tells me "The supplied DisplayObject must be a child of the caller." Iv gottn and fixed and slighty understand this problem, but i feel as if it IS a child of the calleer. idk :c
How my code is set up it creates a world class on my level class, then in that world a worldTiles sprite is created to place the tiles of the world into. This is where the tiles are originally added and deleted
This is where im pretty sure my problem is, the fucntion that deletes a tree tile and adds a grass tile
protected function mouseOnTile()
{
for (var i:int; i < world.tilesInWorld.length; i++)
{
if (mouse.hitTestObject(world.tilesInWorld[i]))
{
trace(world.tilesInWorld[i].Name);
if (world.tilesInWorld[i].Name == "tree")
{
var tx:int = world.tilesInWorld[i].x;
var ty:int = world.tilesInWorld[i].y;
world.worldTiles.removeChild(world.tilesInWorld[i]);
world.treePool.returnSprite(world.tilesInWorld[i]);
world.tilesInWorld.pop();
world.tile = world.tilePool.getSprite();
world.tile.width = world.TILE_SIZE;
world.tile.height = world.TILE_SIZE;
world.tile.x = tx;
world.tile.y = ty;
world.tilesInWorld.push(world.tile);
world.worldTiles.addChild(world.tile);
}
}
}
}
Im sure it has something to do with how im re-adding the grass tile into the worldTiles, but im confused on how else i could do it?
This is the function that deletes the tiles when you walk to a different screen section
public function deleteTiles()
{
if (tilesInWorld.length > 0)
{
for (var i:int = tilesInWorld.length - 1; i >= 0; i--)
{
worldTiles.removeChild(tilesInWorld[i]);
switch (tilesInWorld[i].Name)
{
case "water" :
waterPool.returnSprite(tilesInWorld[i]);
break;
case "shallow" :
shallowPool.returnSprite(tilesInWorld[i]);
break;
case "shell" :
shellPool.returnSprite(tilesInWorld[i]);
break;
case "sand" :
sandPool.returnSprite(tilesInWorld[i]);
break;
case "tree" :
treePool.returnSprite(tilesInWorld[i]);
break;
case "grass" :
tilePool.returnSprite(tilesInWorld[i]);
break;
case "rock" :
rockPool.returnSprite(tilesInWorld[i]);
break;
case "stone" :
stonePool.returnSprite(tilesInWorld[i]);
break;
}
}
tilesInWorld.length = 0;//empty array
generateTile();
}
}
This is where the tiles are generated onto the screen after being deleted
public function generateTile()
{
var Xcalc:int = (X + (800 / TILE_SIZE) / GlobalCode.MAP_SCALE);
var Ycalc:int = (Y + (600 / TILE_SIZE) / GlobalCode.MAP_SCALE);
for (var i:int = X; i < Xcalc; i++)
{
for (var j:int = Y; j < Ycalc; j++)
{
hm = heightmap[i][j];
if ((hm >= 0.84))
{
tile = waterPool.getSprite();
}
else if (((hm >= 0.8) && hm < 0.84))
{
tile = shallowPool.getSprite();
}
else if (((hm >= 0.79) && hm < 0.799))
{
tile = shellPool.getSprite();
}
else if (((hm >= 0.7) && hm < 0.8))
{
tile = sandPool.getSprite();
}
else if (((hm >= 0.35) && hm < 0.4))
{
tile = treePool.getSprite();
}
else if (((hm >= 0.2) && hm < 0.7))
{
tile = tilePool.getSprite();
}
else if (((hm >= 0.09) && hm < 0.2))
{
tile = stonePool.getSprite();
}
else
{
tile = rockPool.getSprite();
}
tile.width = TILE_SIZE;
tile.height = TILE_SIZE;
worldTiles.x = 0;
worldTiles.y = 0;
tile.x = TILE_SIZE * (i % 800);
tile.y = TILE_SIZE * (j % 600);
tilesInWorld.push(tile);
worldTiles.addChild(tile);
}
}
}
I believe your problem is here:
for (var i:int; i < world.tilesInWorld.length; i++)
{
...
world.worldTiles.removeChild(world.tilesInWorld[i]);
world.treePool.returnSprite(world.tilesInWorld[i]);
world.tilesInWorld.pop();
...
}
You are lopping forward through the array, using removeChild on an element in the array, and using "pop" which removes the last item of the array, not the item that was actually removed. You eventually will hit an item that was already removed. Additionally, your i pointer changes each time you pop, which means the loop will never hit every item and is fundamentally flawed.
The DisplayObject used as the argument in dO.removeChild() must be defined, non-null, and a child of dO (i.e. added with dO.addChild(). If it does not meet all of those requirements, it will error out.
To fix this, use splice() instead of pop() (which will allow you to remove a specific element in an array) and go backwards through the array (which will handle the i pointer issues)
for (var i:int = world.tilesInWorld.length - 1; i >= 0; --i)
{
...
world.worldTiles.removeChild(world.tilesInWorld[i]);
world.treePool.returnSprite(world.tilesInWorld[i]);
world.tilesInWorld.splice(i, 1);
...
}
You can also loop forward through the array, but you need to modify the pointer. This is slower and more error prone than going backwards, but can work just as well (when I say slower, we're talking microseconds difference unless you are doing massive computations).
for (var i:int; i < world.tilesInWorld.length; i++)
{
...
world.worldTiles.removeChild(world.tilesInWorld[i]);
world.treePool.returnSprite(world.tilesInWorld[i]);
world.tilesInWorld.splice(i, 1);
--i;
...
}
Additionally, and this is just a syntax/readability thing, you should never rely on a datatype's default value. int will default to 0, but you should still declare it as var i:int = 0 to make it easy to change in the future, standardized and easy to read, and so that you could easily change it to a Number, which has a much, much higher max value than int but defaults to NaN.
This does not make any sense. Since you are removing all element from the array you can jusr forget about pop or slice and do
world.tilesInWorld.length = 0;
after the loop.
Since you are removing all object from world.worldTiles don't bother removing them one by one and do:
world.worldTiles.removeChildren()
after the loop.
Finally you are left with a simple loop where you only do:
world.treePool.returnSprite(world.tilesInWorld[i]);
It's a case (in your case and in the answer given) where you both try very hard to make a simple code as complicated as possible.

Resetting a players x position (Flash As3)

private function scrollStage():void
{
if (lastPosX != lastPosX)
{
canScrollStage = false;
}
else
if (lastPosX == lastPosX)
{
canScrollStage = true;
}
if (canScrollStage)
{
if (rightKey)
{
//move background left
//something.x +=(stage.stageWidth * 0.5) - character.x * 2;
}
else
if (leftKey)
{
//move backgrounf Roight
}
for (var b:int = 0; b < childrenOnStage; b++)
{
if (getChildAt(b).name == "enemy")
{
getChildAt(b).x +=(stage.stageWidth * 0.5) - character.x
}
}
ground.x += (stage.stageWidth * 0.5) - character.x
}
else
{
//move the background
}
// do this last, everything moves around object
character.x = stage.stageWidth * 0.5;
lastPosX = character.x;
}
Someone told me to move the objects around the player and then update the players x position.
This is what I've done by looking at a tutorial ("Cartoon Smart");
In my player class I have a reset function.
private var xSpeed:int;
private var resetPos:Point;
public function player()
{
addEventListener(Event.ADDED_TO_STAGE, onAdd)
}
private function onAdd(e: Event): void
{
xSpeed = 3;
resetPos = new Point(x, y);
}
public function reset():void
{
x = resetPos.x;
y = resetPos.y;
//trace(resetPos);
//trace(resetPos + "player");
}
When the player falls to death this function is called
private function playerFalls():void
{
if (character.y > stage.stageHeight)
{
//take life away and go back to check point or back to reset
character.x = stage.stageWidth * 0.5;
character.y = 20;
//goblin1.reset();
//goblin2.reset();
//goblin3.reset();
//stage.x = stage.stageWidth * 0.5;
//canScrollStage = false;
}
}
if I use
character.x = stage.stageWidth * 0.5;
Then my character ends up in the middle, but it will end up in the middle since the scroll function dictates the player to be in the center always.
character.x = (stage.stageWidth * 0.5) - 400;// moves him back
but if character falls off left of the screen then he is moved back.
Any one have a solution for this please?
My question is, I want to reset the player's x position to 300 and y position to 10;
But I can't do this because the stage shifts and the co ordinate system changes.
In order for the player to go back to the original coordinate of the stage, the stage must scroll.
That's my idea, or perhaps the ground and enemies must do the opposite?
I sincerely apologize for the late answer. Okay, so your saying that when the character falls you don't want the screen to scroll all the way back to the character's start position. What you need to do is find the start positions of your character and ground (as well as any other layers such as backgrounds etc). Then wherever your fall function comes into place simply set the character and background positions to their starting coordinates.
function fall(){
char.x=charStartX;
char.y=charStartY;
ground.x=groundStartX;
ground.y=groundStartY;
//etc
}
For example, let's say you have a check point. If player hits check point charResetPoint = 100. If player falls then set it's x pos to charResetPoint
But I can't move the players x position because it will always be stage.stageWidth/2 due to my code.
So in theory it will be difficult to make a resetPoint, because when I add goblin 1.x , it's x pos is 900, if I scroll right goblon1 x pos will change. Everythings x pos changes and so does the stages.
I can't get a grasp on the concept, sortlry