Connecting blocks together but prevent intersection - actionscript-3

EDIT I have had a rethink: I am going to use a much easier to implement tree structure with a grid so that each block can have up to 4 neighbours.
I am trying to attach blocks on to the stalk (series of blocks) of this flower. Green blocks are the existing stalk and the blue block is the one attached to the mouse.
The block attached to the mouse will correctly snap to the nearest edges (I'm allowing diagonals for now) however the blocks will also be able to go 'inside' the stalk (image 2) by snapping to a block above or below.
My question is, how can I stop this? I considered
Iterating the list again but ignoring the block I just tried to attach to; I think this will just result in the same problem really by attaching to another block 'inside' the stalk.
Find which block I am intersecting and get another connection point from it, repeat until there are no intersections; This seems the better option but could be very messy when intersecting more than 1 block at a time
I should note that I plan on having a smoother snap, not just arbitrarily to an edge, so a grid is pretty much out of the question. I am pretty confident there must be an elegant solution, I'm just not seeing it!
Here is my snapping code as it currently stands
var mousePos:Point = new Point(mouseX, mouseY);// new Point(e.stageX, e.stageY);
var nearestPoint:Point = null;
var nearestDistance:Number = 0;
for (var i:int = 0; i < mPlant.length; ++i) {
var part:PlantPart = mPlant[i];
if (part is Stalk) {
var connectionPoint:Point = (part as Stalk).getNearestConnectionPoint(mousePos);
var distance:Number = Point.distance(mousePos, connectionPoint);
if (nearestPoint == null || distance < nearestDistance) {
nearestPoint = connectionPoint;
nearestDistance = distance;
}
}
}
if (nearestPoint != null) {
mMousePointer.x = nearestPoint.x;
mMousePointer.y = nearestPoint.y;
}

You can always pre-calculate a bounding Rectangle for the stalk, and then do the snapping check against that Rectangle.
The Rectangle can be calculated using something like this (un-tested code, though)
var _bounding:Rectangle = new Rectangle(int.MAX_VALUE, int.MAX_VALUE,0,0);
for each( var part:PlantPart in mPlant)
{
if(part is Stalk)
{
_bounding.width = part.width; // Width will always be the same
_bounding.height = Math.max( _bounding.height, part.y );
_bounding.x = Math.min( _bounding.x, part.x );
_bounding.y = Math.min( _bounding.y, part.y );
}
}

Related

Moving Object to another Objects position

Hey everyone so I am having some trouble trying to get this to work correctly. I have a MC Object called character and another called "points". I have a container object called planetContainer I add the character to the planetContainer the character is rotating around the planets that are also added to the container. The main issue I am having is when the points power up is activated I want the points to move off the other planets and to the charactercenter position. It was working perfect but had to update some code and remove the Points out of the planetContainer and attach them to the planets instead. I know I might have to use localToGlobal but not too sure.
Here is how I setup the character:
private function newCounterClockWise():void
{
planetContainer.addChild(character);
character.rotation = (Math.atan2(character.y - planetHit.y, character.x - planetHit.x) * 180 / Math.PI);
}
How the points are added to the Planets:
private function addPoints():void
{
points = new mcPoints();
var planetPosition:Point = planetContainer.parent.localToGlobal(new Point(0, 0));
points.x = planetPosition.x;
points.y = planetPosition.y;
outerPlanets.addChild(points);
aPointsArray.push(points);
}
Now this is the main function that handles the points to move to the character but it is not working correctly. The points move but they move off the screen or cause the game to kinda tweak out and do different things. Also the "magnetHandler(); is in my EnterFRame Event:
private function magnetHandler():void
{
for (var i:int = 0; i < aPointsArray.length; i++)
{
var currentPoints:mcPoints = aPointsArray[i];
var characterPosition:Point = planetContainer.parent.globalToLocal(new Point(character.x, character.y));
if (currentPoints.hitTestObject(playScreen.mcPointsHit))
{
trace("POINTS MID STAGE");
currentPoints.x -= (currentPoints.x - characterPosition.x);
currentPoints.y -= (currentPoints.y - characterPosition.y);
//currentPoints.x = character.x;
//currentPoints.y = character.y;
//TweenMax.to(currentPoints, 0.5, {x:characterGlobalPosition.x, y:characterGlobalPosition.y , ease:Power1.easeInOut } );
}
}
}
Can anyone see what I am doing wrong?
It's a hard to understand your question fully (or to understand why you're putting things that relate to each other in separate containers), but likely this line is where it's falling down:
var characterPosition:Point = planetContainer.parent.globalToLocal(new Point(character.x, character.y));
What you want to do, is get the characters x/y coordinates in the currentPoints parent space. To do that, you would do something like this:
//first, find the global position of character:
var globalCharacterPoint:Point = character.localToGlobal(new Point());
//then, convert that to the currentPoints parent local space:
var localCharacterPoint:Point = currentPoints.parent.globalToLocal(globalCharacterPoint);
Also, in this code of yours:
points = new mcPoints();
var planetPosition:Point = planetContainer.parent.localToGlobal(new Point(0, 0));
points.x = planetPosition.x;
points.y = planetPosition.y;
You are getting the global space of the planetContainer's parent, which is probably NOT what you want. You likely want:
planetContainer.localToGlobal(new Point()); //this gives you the global location of the planet container's top left corner
And, since you're adding the points object to outerPlanets, you probably want to convert to its local space (unless it's positioned at 0,0 globally - then it doesn't especially matter).
var outerPoint:Point = outerPlanets.globalToLocal(planetPosition);
points.x = outerPoint.x;
points.y = outerPoint.y;
Needless to say, for games it's best to have everything in the global coordinate space unless it's truly encapsulated assets (like smoke on a rocket etc.)

AS3 Change position of object in TileList

I am using a tileList in ActionScript 3 to display movieclips. However I have the problem that not all reference points of the movieclips are in the correct place. This leads to that these movieclips are shown partly outside of their cell in the tileList.
I have tried to adjust the x and y position of the movieClip before adding it to the tileList, but this did not change anything. Now I have tried to find if it is possible to change the x and y position of an object already in the tileList, but without finding any answers.
I hope that I have made my problem clear.
Thanks in advance!
EDIT:
This is the code I tried:
private function initTileList():void {
for(var i:int = 0; i < _movieClips.length; i++) {
changePos(_movieClips[i]);
tileList.addItem({label: _movieClips[i].name, source: _movieClips[i]});
}
}
private function changePos(mc:MovieClip):void {
if(MovieClip(mc).getRect(mc).x != 0) {
mc.x -= MovieClip(mc).getRect(stateMachineRef).x;
}
if(MovieClip(mc).getRect(mc).y != 0) {
mc.y -= MovieClip(mc).getRect(stateMachineRef).y;
}
}
I do not have any errors, it just doesn't affect the position of the object in the tileList.
Example of how the problem looks.
Hard to say where's the problem without knowing these things:
1. What tileList.AddItem() does exactly;
2. What is stateMachineRef
3. How MovieClips are loaded. If they are loaded from a network, that'll be a whole different story.
By the way, you don't have to cast MovieClip(mc) as mc is already a MovieClip. Also, there is no difference as to when you will correct the coordinates: before or after adding to the tileList. Should work either way.
So, given that information on your problem is not complete, I would just suggest you insure the following steps:
-We assume all tiles are displayed inside a tile container. It can be Stage or a MovieClip or any suitable DisplayObjectContainer, so let's call it just tileContainer from now on.
-We assume all tiles are of the same width and height. If you are not sure, you should check it again.
-We assume that each tile in the tileContainer is displayed at some regular grid coordinates. I.e. it conforms the following code:
for (var pos_y:int = 0; pos_y < GRID_SIZE_Y; pos_y++) {
for (var pos_x:int = 0; pos_x < GRID_SIZE_X; pos_x++) {
var tile:Tile = getNextTile(); // just get a tile from somewhere
tile.source.x = pos_x * TILE_WIDTH; // using your tile structure
tile.source.y = pos_y * TILE_HEIGHT;
tileContainer.addChild(tile.source);
}
}
Now I see your problem that some tiles are created in a way that they have their source movieclip coordinates shifted from (0,0). So they will not align with the grid.
What are you doing seems to be a proper way of aligning them but I don't know exactly what happens in your code so I'll just rewrite it:
function changePos(mc:MovieClip) {
var r:Rectangle = mc.getRect(mc);
mc.x -= r.x; // note you don't need any if's
mc.y -= r.y;
}
And in the above loop just add the changePos() AFTER setting the grid coordinates:
tile.source.x = pos_x * TILE_WIDTH;
tile.source.y = pos_y * TILE_HEIGHT;
changePos(tile.source);
tileContainer.addChild(tile.source);
If you're following all these steps, that's basically all you need and it will work for sure.

How to display player lives in game AS3

Hey everyone so having a little trouble trying to accomplish this. I understand how to add lives and display them through a Dynamic Text Field on the stage. My game is set up right now to where this happens and whenever the player dies a live decrements so it works fine.
But I want to display and Image of all 3 lives a custom Movie Clip that I drew in Flash CS6 to represent the lives. So All 3 lives will be displayed in the game and once the player dies one of the lives images is removed.
I have some idea on what to do. For instance I created a "for loop" to display all 3 images on the screen and created variables to place them horizontally next to each other with a space of 30 pixels.
But I'm not sure if this is the correct way to approach this. Also kind of confused on how I would remove one of the images when the player dies?
Her is my code so far:
public var playerLives:mcPlayerLives;
private var nLives:Number = 3;
private var startPoint:Point;
private var aPlayerLivesArray:Array;
In my main class Engine:
aPlayerLivesArray = new Array;
addPlayerLivesToStage();
Then the addplayerlivestostage function:
public function addPlayerLivesToStage():void
{
var startPoint:Point = new Point((stage.stageWidth / 2) - 300, (stage.stageHeight / 2) - 200);
var xSpacing:Number = 30;
for (var i = 0; i < nLives; i++)
{
trace(aPlayerLivesArray.length);
playerLives = new mcPlayerLives();
stage.addChild(playerLives);
playerLives.x = startPoint.x + (xSpacing * i);
playerLives.y = startPoint.y;
aPlayerLivesArray.push(playerLives);
}
}
So like i stated above everything works fine and it does display the 3 images that represent the lives, but would this be the correct approach or is there an easier method?
i think you're using right way to add lives.
and for removing them, you don't need to remove all and add new lives, you can remove the last element of lives array, so, it's done, i think thats already an easy method
so, you can implement this
for (var i = 0; i < nLives; i++)
to make a better usage for "adding lives in-game (earning lives)"
something like
for (var i = aPlayerLivesArray.length; i < nLives; i++)
but don't forget to decrease array length by 1 after removing last element of livesArray when player dies
Looks pretty close to a reasonable approach. I would have a blank container movieclip on stage where you want the lives icons to display. Create one life icon in your library and link it for export with actionscript. When you generate your game view, you can populate this container with the starting value of three lives. Place the first one, then place the subsequent ones based on the first location.
Some untested code:
NOTE: The following presumes your container clip for the lives is named lives_container and your link instance name for the life icon in the library is Life_icon.
var numberOfLives:Number = 3;
var iconSpacing:Number = 5;
var nextX:Number = 0;
for(var i:int = 0; i < numberOfLives; i++ )
{
var icon:MovieClip = new Life_icon();
icon.x = nextX;
lives_container.addChild( icon );
nextX += icon.width + iconSpacing;
}
This way you could add extra lives easily if the player gained any by adding new icons at the last nextX value, like so:
function addLife():void
{
var icon:MovieClip = new Life_icon();
icon.x = nextX;
lives_container.addChild( icon );
nextX += icon.width + iconSpacing;
}
or remove them as the player loses them:
function removeLife():void
{
var numberOfLivesDisplayed:Number = lives_container.numChildren();
lives_container.removeChildAt( numberOfLivesDisplayed - 1 );
nextX -= icon.width + iconSpacing;
}
Using a container clip for the lives icons makes adjusting the location of the life icons easier if it becomes necessary later.

turn based movement on a tile grid, isolated tile(s)

This is written in as3 and FlashPunk.
My problem? I have a tank that can move 4 tiles in the 4 directions; north,south,east and west.
Image can be found at http://www.aroderick.com/isolatedTiles.jpg
I've started with a square grid (Lt gray on the image) and used an array to mark out a diamond pattern (in red on the image) from the square grid, which are all the possible movement choices given the tank can only move 4 spaces in the 4 directions.
The tiles with numbers on the image are the actual tiles you see in the game, the numbers are the "col-row" numbers.
Where is gets a little more complex is that I've also removed tiles from the diamond where there are obstacles (water,trees,mountains) which in turn increases the movement cost to get to tiles beyond the obstacle(s).
I should mention at this point this is based on the A* algorithm and uses A* for movement but these are the movement choices that need to be established before a destination tile is chosen.
My problem is isolated tiles that are beyond the move ability of the tank and are isolated on their own from the main movement area where tiles are joined and make a complete path for A* and the tank can be moved from one tile to the next.
Is there a simple, elegant way to deal(get rid of) with these rogue tiles?
I've tried a system of rules i.e.;
//same row going east
if(ob.row == co.row && ob.row == startNode.row && ob.col < co.col && ob.col > startNode.col && ob.c < co.c )
{
extraCost = co.c;
reason = 1;
break;
}
//same row going west
else if(ob.row == co.row && ob.row == startNode.row && ob.col > co.col && ob.col < startNode.col && ob.c < co.c )
{
extraCost = co.c;
reason = 2;
break;
}
Where "c" a a property representing the tile movement "cost" as the crow flies.
But these seem to create as many problems as they solve.
//reusable tile grid
public static function makeTileGrid(entityLoc:Point,moveGrid:Array,travelMax:int,tsize:int = 64):Array
{
//node list
var nodeLst:Array = [];
//counter
var tileCount:int = 0;
//for tile naming
var co_ordX:String = "";
var co_ordY:String = "";
if(moveNode == null) var moveNode:Object;
//subtract the tile range from the current location
//tile range times two because you can go forewards
//or backwards tRange spaces
for (var col:int = travelMax * 2; col >=0;col--)
{
//placeX is an x value so you must multiply both row and tRange by the tile width
var placeX:Number = entityLoc.x - (travelMax*64 - col*64);
//trace(placeX);
for(var row:int = travelMax * 2; row >=0;row--)
{
var placeY:Number = entityLoc.y - (travelMax*64 - row*64);
//trace(moveGrid[col]);
//use tile grid map array
if(moveGrid[tileCount] == 1)
{
//use coordinates for the name value e.g.
co_ordX = col.toString();
co_ordY = row.toString();
moveNode = {col:col,row:row,obst:false,node:co_ordX+"-"+co_ordY,nX:placeX,nY:placeY,ph:0,h:0,g:0,f:0,c:0};
nodeLst.push(moveNode);
}
tileCount ++;
}
}
return nodeLst;
}
My grid code. In case.
Thanks guys,
james-
I think you should use a limited (distance-wise) breadth-first A* search right over that grid, with obstacles playing as walls in the algorithm. This will generate a set of reachable nodes each with distance_left attribute available, in a way that your "isolated tiles" will not get listed. Your code seems to just get the diamond pattern out of starting position and passage matrix, without actual check of the path distance.
public static function getReachableTiles(startTile:Point,distance:int):Array {
var d:Dictionary=new Dictionary(); // will hold visited tiles point by point
var o:Object={d:distance,px:startTile.x,py:startTile.y}; // a simple object info
// add fields as necessary
var a:Array=new Array(); // output array
a.push(o);
d[startTile.y*256+startTile.x]=o; // the simplest hash. You have to ensure
// these never overlap for different pairs of [x,y], and the same hash
// function is used across this method
while (distance>0) {
for each (o in a) {
if (o.d!=distance) continue; // already parsed
var to:Object={d:distance-1,px:o.x-1,py:o.y};
if (Game.isPassable(to.x,to.y)&&!d[to.y*256+to.x]) {
// a new cell, and is valid for the tank to pass
// "isPassable" returns true if (x,y) corresponds to a passable and
// valid position. For example, (-1,2) might return false as it's off borders
d[to.y*256+to.x]=to; // add to dictionary - a parsed cell
a.push(to); // and to output
}
// doing the same for 3 other possible moves
to={d:distance-1,px:o.x,py:o.y-1};
if (Game.isPassable(to.x,to.y)&&!d[to.y*256+to.x]) {
d[to.y*256+to.x]=to;
a.push(to);
}
to={d:distance-1,px:o.x+1,py:o.y};
if (Game.isPassable(to.x,to.y)&&!d[to.y*256+to.x]) {
d[to.y*256+to.x]=to;
a.push(to);
}
to={d:distance-1,px:o.x,py:o.y+1};
if (Game.isPassable(to.x,to.y)&&!d[to.y*256+to.x]) {
d[to.y*256+to.x]=to;
a.push(to);
}
} // okay, entire array was parsed for new cells
distance--; // we've iterated A* once more, lessen the distance
}
// at this point, we've iterated A* "distance" times, return
return a;
}
You need to adjust this snippet for your variable structure, so that Game.isPassable() will be a valid call, and there would be the actual grid under your drawn map. Note, startTile is a grid-based set of coordinates, not pixel-based. Hope this helps.

find closest point to mouse position

I've got a grid of sprites.
Now I would like to drag an image on a grid-element.
Therefore I need to know which x/y of the grid-element is the closest point to the mouse-position.
All the grid-elements are stored in an array.
How can I achieve that?
You must loop through all the elements and find the smallest distance to the mouse. Then store the array index of the element. Try something like this:
// Setup variables outside of loop.
var mousePoint:Point = new Point(mouseX, mouseY);
var elementPoint:Point = new Point();
var element:Sprite;
var closestIndex:uint = 0;
var closestDist:Number;
// Loop through elements
for (var i:int = 0; i < gridElements.length; i++)
{
element = gridElements[i] as Sprite;
// Set the elementPoint's x and y rather than creating a new Point object.
elementPoint.x = element.x;
elementPoint.y = element.y;
// Find distance from mouse to element.
var dist:Number = Point.distance(mousePoint, elementPoint);
// Update closestIndex and closestDist if it's the closest.
if (i == 0 || dist < closestDist)
{
closestDist = dist;
closestIndex = i;
}
}
// Can now use closestIndex to get the element from the array.
trace('The closest element is at index', closestIndex, ', with a distance of', closestDist);
Of course, this will only get you the first closest element, so you must decide what you want to happen if there are two elements an equal distance away.
You will also need to allow for the origins of your elements. The will probably have there origins set to there top left, so you need to allow for this in your distance calculation.
You could also check for a minimum distance. So if the user is dragging too far away from any of the elements then do nothing.
If all you need is the x,y of the closest grid then all you have to do is.
var gridX:int = Math.floor(mouseX / NumberOfColumns);
var gridY:int = Math.floor(mouseY / NumberOfRows);
This will convert your mouse coordinates to your grid coordinates.
Now comes the problem. If your storing them in a 2d array then you have your x/y if your storing them in a flat array (1d) you need to look it up just like you created it.
var myObject:Object = my2dArray[gridX, gridY];
var myObject:Object = myFlatArray[(gridX * NumberOfRows) + gridY];
If you have taken care of how you create your array and push the items in it, it should be no problem to retrieve stuff without searching it.