How to spawn objects in array without repeating? - actionscript-3

I have MC's rockThrowers that are added to the stage in 3 different positions. They spawn randomly using a random generator which is working fine. The user clicks a button on the stage and the rockThrowers are added to the stage and pushed in their own array aRockThrowerArray I want to be able to check which of 3 positions they spawned on and not overlap the next rockThrowers that are added to the stage and if so then add a new one to the empty positions. I have tried different strategies mainly booleans and calling them from their own class to my main class but nothing seems to work. Here is my
rockThrowers Class:
private function startPosition():void
{
// y position
this.y = (stage.stageHeight / 2) + 200;
//Start Speed
nSpeed = randomNumber(5, 8);
leftScreenSpawn = randomNumber(1, 3);
//For Left Screen
leftNeg = (stage.stageWidth / 2) - 200;
leftMiddle = (stage.stageWidth / 2) - 150;
leftPos = (stage.stageWidth / 2) - 100;
//Left Screen
if (leftScreenSpawn == 1)
{
this.x = leftNeg;
bLeftNeg = true; // Now if the left Rock thrower is destroyed then turn back to false on main engine class
}else
if (leftScreenSpawn == 2)
{
this.x = leftMiddle;
bLeftMiddle = true;
}else
if (leftScreenSpawn == 3)
{
this.x = leftPos;
bLeftPos = true;
}
//Move
startMoving();
}
Now in my Main Class I have it setup like so for when the user clicks the left screen Btn:
private function rockThrowerSpawn(e:MouseEvent):void
{
//Instantiate screens before hand
rockThrowerSpawnScreen.x = (stage.stageWidth / 2);
rockThrowerSpawnScreen.y = (stage.stageHeight / 2) + 200;
addChild(rockThrowerSpawnScreen);
rockThrowerSpawnScreen.left.addEventListener(MouseEvent.CLICK, chooseSpawnSideRockThrowers);
}
Then the Spawn Function:
private function chooseSpawnSideRockThrowers(e:MouseEvent):void
{
if (e.currentTarget == rockThrowerSpawnScreen.left) // Spawn LEFT
{
//add new rock thrower
rockThrowers = new mcRockThrowers();
//Add object
addChild(rockThrowers);
//Add to Array
aRockThrowerArray.push(rockThrowers);
//trace("LEFT SPAWN");
}
//Subtract resources and update text
nResources -= 10;
updateResourceTextField();
//Remove Listeners
rockThrowerSpawnScreen.left.removeEventListener(MouseEvent.CLICK, chooseSpawnSideRockThrowers);
rockThrowerSpawnScreen.destroy();
}
I understand that this alone will always spawn random positions I deleted everything that wasn't working now I'm back to this square one. Any ideas on how I can accomplish this? All support is appreciate.

Easy. You need a finite Array that produces 3 values in a random order.
var L:Array =
[
(stage.stageWidth / 2) - 200,
(stage.stageWidth / 2) - 150,
(stage.stageWidth / 2) - 100,
];
function fetchPosition():Number
{
// Get a random index based on the current length of L.
var anIndex:int = Math.random() * L.length;
// Record the result.
var result:Number = L[anIndex];
// Remove the result from the list.
L.splice(anIndex, 1);
return result;
}
So, you can validly call fetchPosition() three times per application run, and each run the contents of L will be fetched in random order and, more importantly, you won't get the same value twice, because the fetched value is removed from the data set.

Related

Simple click and hit flash game

I am trying to create a simple game like whack a mole, what I want is for instead of mole make rectangle appear and disappear quickly on screen and the player has to click it more rectangles he click more his score increases.
I think it's a fairly simple game but my problem is:
How do I make the rectangles appear and disappear on screen at random position also at increasing speeds as the timer is decreasing? i.e speed of rectangles appearing and disappearing increases as the time reduces, there is a countdown time as player gets to play for 30 sec.
import flash.utils.Timer;
import flash.events.TimerEvent;
import flash.events.Event;
var inc:Number = 1;
var gogo:Timer = new Timer(inc*1000);
var val:Number = 30;
var counter = val;
var time2:Timer = new Timer(1000+speed);
var speed:Number = 50;
timee.text = counter;
box.addEventListener(MouseEvent.CLICK, st);
function st(event:MouseEvent):void
{
gogo.start();
time2.start();
}
gogo.addEventListener(TimerEvent.TIMER, res);
function res(ev:TimerEvent):void
{
if(counter == 0){
gogo.stop();
}else{
val = val - inc;
counter = val;
timee.text = counter;
}
}
stage.addEventListener(Event.ENTER_FRAME, yea);
function yea(e:Event):void{
speed += 50;
}
It seems to me that you need a math.random() method.
explained here
I'd personally have every successful 'hit' lower a "blinkSpeed" variable by either a set fraction of a second or a percentage and use that variable as my random input "max" number. That way it would decrease the time it could be available for automatically as they play.
Hello
Well, that wouldn't be much hard! Here's a clue.
Step1 : Create a new movieclip.
Step2 : Right click on your movie clip in the library. Click properties. Check the "Export for ActionScript" checkbox. Name the class MyEnemy (for example).
Step3 : Go to the frame and pull the Actions window.
Now put this code in it :
var mc:MyEnemy= new MyEnemy() // creates a instance of the movieclip.
addChild(mc); // adds the movie clip to the stage.
Step 4 : Now that we have added a new movieclip to the stage. To add it at a random x location in the stage, you need to make use of mc's x and y fields and the Math.random() function.
Example of Math.random() :
var randomThing:Number = Math.random() * 100 // returns a number between n, where 0 <= n < 100
Now, for example, we need both x and y values relative to the stage you must multiply Math.random() with the stage's width instead. As follows :
var randomX:Number = Math.random() * stage.stageWidth // returns a number between n, where 0 <= n < stage.stageWidth
var randomY:Number = Math.random() * stage.stageWidth // returns a number between n, where 0 <= n < stage.stageWidth
And! The magic final code would be :
mc.x = randomX;
mc.y = randomY;
Edit 1 : To add multiple "mc" movieclips to stage, we would use loops, do the follow :
stage.addEventListener(Event.ENTER_FRAME, doSimple);
function doSimple (ev:Event) {
var mc:MyEnemy = new MyEnemy();
var randomX:Number = Math.random() * stage.stageWidth // returns a number between n, where 0 <= n < stage.stageWidth
var randomY:Number = Math.random() * stage.stageWidth // returns a number between n, where 0 <= n < stage.stageWidth
for (var i:Number=1; i<=10;i++){ //i is a number, you can discard the 'i<=10' if you want an infinite loop.
addChild(mc);
}
mc.x = randomX;
mc.y = randomY;
trace("yes");
}
Conclusion : I look forward for your feedback!

Properly hovering over isometric tile sprite

I have four classes: Room, TileGrid, HoverTile, and Tile.
Room is composed of walls and a TileGrid. TileGrid is made out of Tile. Currently, I use this code to generate a TileGrid out of Tiles:
this.mapArray = [[1,1,1,1,1,1,1],
[1,1,1,1,1,1,1],
[1,1,1,1,1,1,1],
[1,1,1,1,1,1,1],
[1,1,1,1,1,1,1],
[1,1,1,1,1,1,1],
[1, 1, 1, 1, 1, 1, 1]];
this._mapHeight = this.mapArray.length;
this._mapWidth = this.mapArray[0].length;
this._tileHeight = 23;
this._tileWidth = 46;
var initialX:Number = 260;
var initialY:Number = 150;
for (var isoY:int = 0; isoY < mapArray.length; isoY++)
{
for (var isoX:int = 0; isoX < mapArray[isoY].length; isoX++)
{
if (isoX == 0 && isoY == 0)
{
var _tile:Tile = new Tile();
_tile.x = initialX;
_tile.y = initialY;
this.addChild(_tile);
}
if (this.mapArray[isoY][isoX] == 1)
{
var _tile:Tile = new Tile();
_tile.x = initialX - (isoX * 20) - (isoY * 20);
_tile.y = initialY - (isoX * 10) + (isoY * 10);
addChild(_tile);
_tile.addEventListener(MouseEvent.MOUSE_OVER, updateHover);
}
}
}
My current issue is that I want to add a white square around the tile that a mouse is hovering over. The code I used to use wasn't sufficient, because transparent parts of the Tile sprite are still counted as part of it. So even if I'm pointing at another Tile2 (which is next to Tile1), for example, if I'm not far enough onto Tile2, it'll highlight Tile1.
So, here's the current code I'm using:
public function updateHover(e:MouseEvent):void
{
var mX:int = e.stageX - (_tileWidth / 2);
var tPoint:Point = pointToXY(mX, e.stageY);
var isoX = tPoint.x;
var isoY = tPoint.y;
if (isoX >= 0 && isoY >= 0)
{
if (isoY < mapArray.length)
{
if (isoX < mapArray[0].length)
{
tPoint = xyToPoint(isoX, isoY);
_tileHover.x = tPoint.x;
_tileHover.y = tPoint.y;
_tileHover.visible = true;
return;
}
}
}
_tileHover.visible = false;
}
public function pointToXY(x:int, y:int):Point
{
x -= 260;
y -= 150;
var pRatio:int = (_tileWidth / 2) / (_tileHeight / 2);
var tX:int = (y + x / pRatio) * (pRatio / 2) / (_tileWidth / 2);
var tY:int = (y - x / pRatio) * (pRatio / 2) / (_tileWidth / 2);
return new Point(tX, tY);
}
public function xyToPoint(x:int, y:int):Point
{
x -= 1;
var worldPoint:Point = new Point(0, 0);
worldPoint.x = (x * (_tileWidth / 2)) - (y * (_tileWidth / 2));
worldPoint.y = (x * (_tileHeight / 2)) + (y * (_tileHeight / 2));
worldPoint.x = worldPoint.x + (_tileWidth / 2);
worldPoint.y = worldPoint.y + (_tileHeight / 2);
worldPoint.x += 260;
worldPoint.y += 150;
return worldPoint;
}
Sorry I have to post so many code blocks. Now, 260 and 150 are the default starting point for the entire room. That said, I'm really confused on how to get the last two functions in particular to work so that they'll give me the correct answer. This is what I expected from using this code:
That would be perfect. But, again, I don't know why the code isn't working. The sizes are all correct and I believe the offset is, too. So, I'm
First, you should add the listener to this, not to _tile, because then you are locked to stage coordinates to determine the tile that's selected, which is not good. Second, your listener should be against MouseEvent.MOUSE_MOVE event, not over, this way you'll constantly get updated mouse coords to properly move your rectangle over tiles. And you have a minor error out there, you have a (0,0) tile created two times, one being inactive.
for (var isoY:int = 0; isoY < mapArray.length; isoY++)
{
for (var isoX:int = 0; isoX < mapArray[isoY].length; isoX++)
{
if (this.mapArray[isoY][isoX] == 1)
{
var _tile:Tile = new Tile();
_tile.x = initialX - (isoX * 20) - (isoY * 20);
_tile.y = initialY - (isoX * 10) + (isoY * 10);
addChild(_tile);
}
}
}
this.addEventListener(MouseEvent.MOUSE_MOVE, updateHover);
Also, it'll be better that you'd store (x,y) pairs on the array (as tiles, most likely), so that your initial array of zeroes and ones would transform into an array of Tile objects. To do that, you first do this:
this.tileArray=[];
for (var i:int=0;i<this.mapArray.length;i++)
this.tileArray.push(new Array(this.mapArray[i].length));
This will create an array of nulls that matches your mapArray by dimensions, that will serve as placeholder for created Tile objects. After you do this, you call this.tileArray[isoY][isoX]=_tile; to place the newly created tile to its place. After that, you can rewrite your listener to this:
public function updateHover(e:MouseEvent):void
{
var p:Point=pointToXY(e.localX,e.localY);
_tileHover.visible = false; // hide hover for now
if ((p.y<0) || (p.y>=tileArray.length)) return; // range error on Y
if ((p.x<0)||(p.x>=tileArray[p.y].length)) return; // range error on X
if (!tileArray[p.y][p.x]) return; // no tile
var _tile:Tile=tileArray[p.y][p.x];
_tileHover.x=_tile.x;
_tileHover.y=_tile.y; // no need to convert xyToPoint() we have coords stored in tile
_tileHover.visible=true;
}

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.

Dragging with Actionscript 3.0, like moving camera in RTS games

I am trying to build a RTS game with Flash and doing some basic testing. I come across this site teaching me dragging objects. I am modified the code to simulate moving the game world of the game while clicking on it. The center circle is the focus point / center of camera. The rectangle board represents the game world.
I tried to change the function boardMove to click and move according to mouseX and mouseY. But every time I click, the mouseX and mouseY becomes the center of the board, which is not what I wanted. I want to make it relative to the mouse position, but I could only make the board flickering, or moves with its top left corner.
Any suggestions would be appreciated.
// Part 1 -- Setting up the objects
var board:Sprite = new Sprite();
var myPoint:Sprite = new Sprite();
var stageWidth = 550;
var stageHeight = 400;
var boardWidth = 400;
var boardHeight = 300;
var pointWidth = 10;
this.addChild(board);
this.addChild(myPoint);
board.graphics.lineStyle(1,0);
board.graphics.beginFill(0xCCCCCC);
board.graphics.drawRect(0,0,boardWidth,boardHeight);
board.graphics.endFill();
board.x = (stageWidth - boardWidth) / 2;
board.y = (stageHeight - boardHeight) / 2;
myPoint.graphics.lineStyle(1,0);
myPoint.graphics.beginFill(0x0000FF,0.7);
myPoint.graphics.drawCircle(0,0,pointWidth);
myPoint.graphics.endFill();
myPoint.x = (stageWidth - pointWidth) / 2;
myPoint.y = (stageHeight - pointWidth) / 2;
// Part 2 -- Add drag-and-drop functionality - Better Attempt
stage.addEventListener(MouseEvent.MOUSE_DOWN, startMove);
function startMove(evt:MouseEvent):void {
stage.addEventListener(MouseEvent.MOUSE_MOVE, boardMove);
}
// Revised definition of pointMove in Part II of our script
function boardMove(e:MouseEvent):void {
board.x = checkEdgeX(board.mouseX);
board.y = checkEdgeY(board.mouseY);
e.updateAfterEvent();
}
stage.addEventListener(MouseEvent.MOUSE_UP, stopMove);
function stopMove(e:MouseEvent):void {
stage.removeEventListener(MouseEvent.MOUSE_MOVE, boardMove);
}
// Part III -- Check for boundaries
function checkEdgeX(inX:Number):Number {
var x = stageWidth / 2 - boardWidth;
if (inX < x) {
return x;
}
x = stageWidth / 2;
if (inX > x) {
return x;
}
return inX;
}
function checkEdgeY(inY:Number):Number {
var y = stageHeight / 2 - boardHeight;
if (inY < y) {
return y;
}
y = stageHeight / 2;
if (inY > y) {
return y;
}
return inY;
}
One option is to determine the relative movement of the mouse and move the board accordingly; something like:
private Point lastPosition;
function startMove(...) {
lastPosition = null;
...
}
function boardMove(e:MouseEvent):void {
Point position = new Point(stageX, stageY);
if (lastPosition != null) {
Point delta = position.subtract(lastPosition);
board.x += delta.x; // NOTE: also try -= instead of +=
board.y += delta.y; // NOTE: also try -= instead of +=
e.updateAfterEvent();
}
lastPosition = position;
}

How to have an object hover back and forth constrained within a specific radius?

I have a sprite in a movie symbol that I would like to hover back and forth within a 360 radius. I was hoping to make it smooth and random. Never really venturing from its original xy cordinates.
I've tried to create some stipulations with if statements and a starting momentum. Like this:
var num = 2;
stage.addEventListener(Event.ENTER_FRAME, hover);
function hover(evt:Event):void{
//start it moving
cloudWhite.y += num;
cloudWhite.x += num;
//declare these variables
var cX = cloudWhite.x;
var cY = cloudWhite.y;
// object travels 10 pixels
var cXP = cX + 10;
var cXN = cX - 10;
var cYP = cY + 10;
var cYN = cY - 10;
// if object goes 10 pixels reverse direction of momentum (maybe)
if (cX >= cXP) {
num = -2;
}
if (cX <= cXN){
num = 2;
}
if (cY >= cYP) {
num = 2;
}
if (cY <= cYN){
num = 2;
}
Clearly this is super wrong because when it runs the object just either goes to 0,0 or to some place that only the math gods know of.
I am clearly a noob at this kind of math so i apologize but I am very excited to learn the trig behind this.
Thank you for your help and thank you for reading.
You are setting all your variables inside the ENTER_FRAME loop, so none of your conditions ever evaluates to true. On every single frame you are doing this:
cloudWhite.x += 2;
cX = cloudWhite.x;
cXP = cX + 10; // Must == cloudWhite's previous x + 10 + 2;
cXN = cX - 10; // Must == cloudWite's previous x -10 + 2;
if(cX > cXP)... // Can never be true.
if(cX < cXN)... // Can never be true.
What you need to do is:
1) Store the original position of cloudWhite somewhere outside the loop, and store it before the loop begins.
2) Define your bounds relative to the original position of cloudWhite, again before your loop begins. Also define the amount you are going to change the position with each iteration.
3) Start your loop.
4) Increment the current position of cloudWhite on each iteration. Add a little random in here if you want the shape to move in a random manner.
5) Check if the new position of cW is outside your bounds and adjust the direction if it is.
The sample below is crude and jerky but I don't know exactly what effect you're looking for. If you want smoother, longer movements in each direction, consider using the Tween class or a Tween library such as the popular Greensock one, instead of incrementing / decrementing the position manually. There's a useful discussion of this here: http://www.actionscript.org/forums/archive/index.php3/t-163836.html
import flash.display.MovieClip;
import flash.events.Event;
// Set up your variables
var original_x:Number = 100; // Original x
var original_y:Number = 100; // Original y
var x_inc:Number = 5; // X Movement
var y_inc:Number = 5; // Y Movenent
var bounds:Number = 50; // Distance from origin allowed
// Doesn't take into account width of object so is distance to nearest point.
// Create an MC to show the bounds:
var display:MovieClip = addChild(new MovieClip()) as MovieClip;
display.graphics.lineStyle(1, 0x0000FF);
display.graphics.beginFill(0x0000FF, 0.5);
display.graphics.drawRect(0-bounds, 0-bounds, bounds * 2, bounds *2);
display.x = original_x;
display.y = original_y;
addChild(display);
// Create our moving mc:
var mc:MovieClip = addChild(new MovieClip()) as MovieClip;
mc.graphics.beginFill(0xFF0000, 1);
mc.graphics.drawCircle(-10, -10, 20);
// Position it:
mc.x = original_x;
mc.y = original_y;
addChild(mc);
// Loop:
function iterate($e:Event = null):void
{
// Move the mc by a random amount related to x/y inc
mc.x += (Math.random() * (2 * x_inc))/2;
mc.y += (Math.random() * (2 * y_inc))/2;
// If the distance from the origin is greater than bounds:
if((Math.abs(mc.x - original_x)) > bounds)
{
// Reverse the direction of travel:
x_inc == 5 ? x_inc = -5 : x_inc = 5;
}
// Ditto on the y axis:
if((Math.abs(mc.y - original_y)) > bounds)
{
y_inc == 5 ? y_inc = -5 : y_inc = 5;
}
}
// Start the loop:
addEventListener(Event.ENTER_FRAME, iterate);
This should get you started. I'm sure there are any number of other ways to do this with formal trig, but this has the benefit of being very simple, and just an extension of your existing method.