Tuesday, November 9, 2010

Actionscript 3 RTS game tutorial - part 5

Today we continue making our selection rectangle feature!

Here is what we will end up with today - hold CTRL, hold mouse and move mouse to select units:


Here's the commented code. I added a piece of code in the addUnit() function in the end of the loop function.

// We turn all the code that handles movement and behavior of one unit
// into a function, through which we can apply the same code to
// multiple units.
function addUnit(mc):void
{
var iswalking = false;
var movespeed = 5;
var dir = "down";
var goX;
var goY;
// the selectedUnit variable is toggled when the unit is being clicked
var selectedUnit = false;

stage.addEventListener(Event.ENTER_FRAME, loop);

function loop(Event)
{
if (iswalking == true)
{
mc.w.play();
}
else
{
mc.w.gotoAndStop(1);
}

mc.gotoAndStop(dir);
if ((goY-movespeed*2)>mc.y)
{
mc.y +=  movespeed;
dir = "down";
}
else if ((goY+movespeed*2)<mc.y)
{
mc.y -=  movespeed;
dir = "up";
}
if ((goX-movespeed*2)>mc.x)
{
mc.x +=  movespeed;
dir = "right";
}
else if ((goX+movespeed*2)<mc.x)
{
mc.x -=  movespeed;
dir = "left";
}

if ((goY-movespeed*2)>mc.y || (goY+movespeed*2)<mc.y || (goX-movespeed*2)>mc.x || (goX+movespeed*2)<mc.x)
{
iswalking = true;
}
else
{
iswalking = false;
}

// Check for space key (deselect)
if (isSpace)
{
selectedUnit = false;
mc.selectedGfx.gotoAndStop(1);
}

// Checking for selection rectangle

// If we're using the selection rectangle right now and if the current unit is not selected
// and if the rectangle hits the unit, set the selection graphic inside the movie clip (the border)
// visible. If we're using the rectangle, the unit is not selected and the rectangle DOESN'T
// hit this unit, the border shouldn't be visible.
if (selectingRectNow && selectedUnit == false && selectRect.hitTestObject(mc.selectArea))
{
mc.selectedGfx.gotoAndStop(2);
}
else if (selectingRectNow && selectedUnit==false && !selectRect.hitTestObject(mc.selectArea))
{
mc.selectedGfx.gotoAndStop(1);
}

// Once we release mouse, we aren't selecting rectangle anymore. So, when we're not
// using the selection rectangle and this unit is not selectet, but its border is visible
// (meaning that it was selected with the rectangle before), then make the unit selected for real
// and add to totalSelected. 
if (! selectingRectNow && selectedUnit == false && mc.selectedGfx.currentFrame == 2)
{
selectedUnit = true;
totalSelected++;
}

}

// When we click the unit, toggle the selectUnit variable

mc.selectArea.addEventListener(MouseEvent.CLICK, select);

function select(MouseEvent):void
{
if (selectedUnit == true)
{
selectedUnit = false;
// mc.selectedGfx is a movieclip with a green border graphic
// it is visible on frame 2 and invisible on frame 1
mc.selectedGfx.gotoAndStop(1);
totalSelected--;
}
else
{
selectedUnit = true;
mc.selectedGfx.gotoAndStop(2);
totalSelected++;
}
}

// When we click the stage area, move there

stage.addEventListener(MouseEvent.CLICK, setposition);

function setposition(evt:MouseEvent):void
{
// The conditional checks if we click stage (not a unit) and set the position
// of the move marker

if (evt.target == stage && selectedUnit == true)
{
goX = mouseX;
goY = mouseY;
}

// Now we check if we clicked on a unit, but not this one. If we did it without holding
// CTRL, then this unit should be deselected.
// We use the selectArea movieclip (which is invisible) located in each unit to trace clicking

for (var l:int=0; l<units.length; l++)
{
if (evt.target == units[l].selectArea && evt.target != mc.selectArea && isCtrl == false && selectedUnit)
{
selectedUnit = false;
mc.selectedGfx.gotoAndStop(1);
totalSelected--;
}
}

}
}

// All units are stored in an array
// char, char2 and char3 - instance names of unit mcs on stage

var units:Array = [char,char2,char3];

for (var i:int=0; i<units.length; i++)
{
addUnit(units[i]);
}



// Here we manage depths

stage.addEventListener(Event.ENTER_FRAME, depthManager);

function depthManager(Event):void
{
// Two loops for two objects (object one hits object 2)
for (var v:int=0; v<units.length; v++)
{
for (var o:int=0; o<units.length; o++)
{
// First we check if object1 and object2 aren't the same unit
// Then we check if they collide
if (v != o && units[v].hitTestObject(units[o]))
{
// Swap depths if needed
if (getChildIndex(units[v]) > getChildIndex(units[o]) && units[v].y < units[o].y)
{
swapChildren(units[v], units[o]);
}
if (getChildIndex(units[v]) < getChildIndex(units[o]) && units[v].y > units[o].y)
{
swapChildren(units[v], units[o]);
}
}
}
}
}

// Creating a keyboard manager

stage.addEventListener(KeyboardEvent.KEY_DOWN, kDown);
stage.addEventListener(KeyboardEvent.KEY_UP, kUp);

var isCtrl:Boolean = false;
var isSpace:Boolean = false;
msgText.text = "Hold CTRL to select multiple units. Space to deselect all.";

function kDown(evt:KeyboardEvent):void
{
if (evt.keyCode == 17)
{
isCtrl = true;
msgText.text = "Multiple unit select";
}
if (evt.keyCode == 32)
{
isSpace = true;
msgText.text = "Deselected all";
totalSelected = 0;
}
}

function kUp(evt:KeyboardEvent):void
{
if (evt.keyCode == 17)
{
isCtrl = false;
msgText.text = "Single unit select";
}
if (evt.keyCode == 32)
{
isSpace = false;
}
}

// Selection checker

var totalSelected:Number = 0;

stage.addEventListener(Event.ENTER_FRAME, selectionChecker);

function selectionChecker(Event):void
{
// When no units are selected, totalSelected is set to 0.
// Once a unit is clicked, this number increases by 1 and decreases when unselected.
// This var is changed through the addUnit() function and also Space key event.
msgText2.text = "Total units selected: " + totalSelected.toString();
}

// Mouse holding rectangle selection managment:

var selectStartPosX:Number;
var selectStartPosY:Number;
var selectingRectNow:Boolean = false;

// What happens when we release mouse

stage.addEventListener(MouseEvent.MOUSE_UP, selectRectangleOff);

function selectRectangleOff(MouseEvent):void
{
selectingRectNow = false;
selectRect.width = 0;
selectRect.height = 0;
}

// What happens when we press mouse

stage.addEventListener(MouseEvent.MOUSE_DOWN, selectRectangleOn);

function selectRectangleOn(MouseEvent):void
{
if (isCtrl)
{
selectRect.x = mouseX;
selectRect.y = mouseY;
selectRect.width = 0;
selectRect.height = 0;
selectingRectNow = true;
selectStartPosX = stage.mouseX;
selectStartPosY = stage.mouseY;
}
}

// What happens when we hold and move our mouse

stage.addEventListener(MouseEvent.MOUSE_MOVE, selectRectangleScale);

function selectRectangleScale(MouseEvent):void
{
if (selectingRectNow)
{
// There are many if's here to manage correct display of the selection field
// It all depends in which direction are you dragging the selection
if (selectStartPosX <= mouseX && selectStartPosY <= mouseY)
{
selectRect.width = mouseX - selectStartPosX;
selectRect.height = mouseY - selectStartPosY;
}
else if (selectStartPosX>mouseX && selectStartPosY<=mouseY)
{
selectRect.width = selectStartPosX - mouseX;
selectRect.x = mouseX;
selectRect.height = mouseY - selectStartPosY;
}
else if (selectStartPosX>mouseX && selectStartPosY>mouseY)
{
selectRect.width = selectStartPosX - mouseX;
selectRect.x = mouseX;
selectRect.y = mouseY;
selectRect.height = selectStartPosY - mouseY;
}
else if (selectStartPosX<mouseX && selectStartPosY>mouseY)
{
selectRect.width = mouseX - selectStartPosX;
selectRect.y = mouseY;
selectRect.height = selectStartPosY - mouseY;
}
}
}

Thanks for reading!

Related:

Actionscript 3 RTS game tutorial part 1
Actionscript 3 RTS game tutorial part 2
Actionscript 3 RTS game tutorial part 3
Actionscript 3 RTS game tutorial part 4

3 comments:

Anonymous said...

Very nice tut! thank you :D could you maybe try making the characters solid so they collide instead of overlapping? thanks :D

Anonymous said...

Yeah collision detection isn't fun... With the arrays and all

Anonymous said...

hi

Post a Comment