formation 8
drag : select a squad
ctrl + a : select all
shift + click : move the squad to destination
left, right arrow : change formation type
/**
* Copyright codeonwort ( http://wonderfl.net/user/codeonwort )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/5jx7
*/
// forked from codeonwort's formation 7
// forked from codeonwort's formation 6
// forked from codefl's formation 5
// forked from codefl's formation 4
// forked from codefl's formation 3
package {
import flash.display.DisplayObject
import flash.display.Sprite
import flash.display.MovieClip
import flash.events.Event
import flash.events.MouseEvent
import flash.events.KeyboardEvent
import flash.ui.Keyboard
import flash.text.TextField
import com.bit101.components.RadioButton
/* 이번에 한 작업
* 일반 RTS처럼 드래그 방식으로 유닛 일부만 선택한다. (ctrl + a로 전체 선택)
* 이동을 그냥 클릭이 아니라 쉬프트 + 클릭으로 바꿨다.
* 이동 방식을 하나 추가했다 (diagonal)
*/
/* 앞으로 할 작업
* 유닛끼리 비켜가는 건 언제 구현하지
* IDestination 떡밥도 처리해야 하는디
* 동그라미가 슬슬 질리는데 스프라이트 넣으면 깊이 정렬도 해야한다 아이고 귀찮아ㅋㅋ
*/
public class FormationWorld extends Sprite {
private var formationNames:Array = ["rect", "circle", "circular crowd",
"wedge", "helix", "firework", "random"]
private var formationIndex:int = 0
private var formationView:TextField
private var numInput:TextField
private var unitContainer:Sprite
private var formCaller:FormationCaller
private var squadSelector:SquadSelector
private var moveMan:MoveManager
public function FormationWorld() {
graphics.beginFill(0x0, 1)
graphics.drawRect(0, 0, stage.stageWidth, stage.stageHeight)
graphics.endFill()
unitContainer = addChild(new Sprite) as Sprite
unitContainer.mouseEnabled = false
unitContainer.mouseChildren = false
makeUnits(67) // 인자는 유닛 개수
setKeyboardAction()
// 분대를 선택하는 역할을 한다
squadSelector = new SquadSelector(stage, this, unitContainer)
// 언제 대형 함수를 호출할 지를 결정한다
formCaller = new FormationCaller(this)
formCaller.runClickMode(stage)
moveMan = new MoveManager(unitContainer)
moveMan.setMoveType(MoveManager.MANHATTAN)
// radio buttons for click mode
var clickMode1:RadioButton = new RadioButton(this, stage.width-80, 5, "click", true, _setClickMode)
var clickMode2:RadioButton = new RadioButton(this, stage.width-80, 20, "timed click", false, _setTimedClickMode)
clickMode1.groupName = clickMode2.groupName = "anotherGroupName"
function _setClickMode():void { formCaller.runClickMode(stage) }
function _setTimedClickMode():void { formCaller.runTimedClickMode(stage) }
// radio buttons for move mode
new RadioButton(this, stage.width-160, 5, "manhattan", true, _setManhattanMove)
new RadioButton(this, stage.width-160, 20, "diagonal", false, _setDiagonalMove)
function _setManhattanMove():void { moveMan.setMoveType(MoveManager.MANHATTAN) }
function _setDiagonalMove():void { moveMan.setMoveType(MoveManager.DIAGONAL) }
}
public function makeFormation(centerX:Number, centerY:Number):void {
var squad:Vector.<Unit> = squadSelector.selectedSquad
var len:uint = squad.length
var data:FormationData
= FormationDataGenerator.gen(formationIndex, len, centerX, centerY)
for(var i:int=0 ; i<len ; i++){
squad[i].dest.x = data.dests[i].x
squad[i].dest.y = data.dests[i].y
}
}
private function makeUnits(num:uint):void {
var unit:Unit, x0:Number, y0:Number
for(var i:int=0 ; i<num ; i++){
x0 = Math.random() * stage.stageWidth
y0 = Math.random() * stage.stageHeight
unit = new Unit(x0, y0)
unitContainer.addChild(unit)
}
}
private function setKeyboardAction():void {
formationView = addChild(new TextField()) as TextField
formationView.autoSize = "left"
formationView.selectable = false
formationView.textColor = 0xffffff
formationView.text = "formation : " + formationNames[formationIndex]
formationView.mouseEnabled = false
stage.addEventListener("keyDown", changeFormation)
function changeFormation(e:KeyboardEvent):void {
switch(e.keyCode){
case Keyboard.LEFT :
formationIndex --
if(formationIndex == -1) formationIndex = FormationDataGenerator.length - 1
break
case Keyboard.RIGHT :
formationIndex ++
if(formationIndex == FormationDataGenerator.length) formationIndex = 0
break
}
formationView.text = "formation : " + formationNames[formationIndex]
}
var cap:TextField = addChild(new TextField()) as TextField
cap.selectable = false
cap.autoSize = "left"
cap.y = 20
cap.textColor = 0xffffff
cap.mouseEnabled = false
cap.text = "write number of units and enter"
numInput = addChild(new TextField) as TextField
numInput.text = "67"
numInput.type = "input"
numInput.autoSize = "left"
numInput.background = true
numInput.backgroundColor = 0xffff00
numInput.textColor = 0x000000
numInput.x = 160
numInput.y = 20
numInput.restrict = "0-9"
numInput.maxChars = 4
numInput.addEventListener("keyDown", changeNumUnits)
function changeNumUnits(e:KeyboardEvent):void {
if(e.keyCode == Keyboard.ENTER){
for(;unitContainer.numChildren;) unitContainer.removeChildAt(0)
makeUnits(int(numInput.text))
}
}
} // setKeyboardAction() 끝
}
}
//////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
// 패키지 스코프의 바깥
import flash.display.Shape
internal class Unit extends Shape {
public static const R:int = 5
public var dest:IDestination
public var vx:Number=0, vy:Number=0
public function Unit(x0:Number, y0:Number){
x = x0 ; y = y0
dest = new Location(x0, y0)
graphics.beginFill(0xff0000, 1)
graphics.drawCircle(0, 0, R)
graphics.endFill()
}
public function set selected(v:Boolean):void {
graphics.clear()
if(v){
graphics.lineStyle(1, 0x00ff00)
graphics.drawEllipse(-R, R/2, R*2, R+R/2)
graphics.lineStyle()
}
graphics.beginFill(0xff0000)
graphics.drawCircle(0, 0, R)
}
}
internal interface IDestination {
function get x():Number
function set x(val:Number):void
function get y():Number
function set y(val:Number):void
}
internal class Location implements IDestination {
private var _x:Number, _y:Number
public function Location(x0:Number, y0:Number) {
_x = x0 ; _y = y0
}
public function get x():Number { return _x }
public function set x(val:Number):void { _x = val }
public function get y():Number { return _y }
public function set y(val:Number):void { _y = val }
}
internal class UnitLocation implements IDestination {
private var _u:Unit
public function UnitLocation(u:Unit) {
_u = u
}
public function get x():Number { return _u.x }
public function get y():Number { return _u.y }
public function set x(val:Number):void {}
public function set y(val:Number):void {}
}
import flash.display.InteractiveObject
import flash.events.MouseEvent
import flash.events.Event
// manages when to call formation function
// 대형 함수를 호출하는 역할을 맡는다
internal class FormationCaller {
private var world:FormationWorld
private var clk_io:InteractiveObject
private var time_io:InteractiveObject
public function FormationCaller(formationWorld:FormationWorld) {
world = formationWorld
}
public function runClickMode(io:InteractiveObject):void {
clearMode()
io.addEventListener("mouseDown", clickMode_mouseDown)
clk_io = io
}
private function clickMode_mouseDown(e:MouseEvent):void {
if(e.target == clk_io && e.shiftKey){
world.makeFormation(e.stageX, e.stageY)
}
}
public function runTimedClickMode(io:InteractiveObject):void {
clearMode()
io.addEventListener("mouseDown", timedClickMode_mouseDown)
time_io = io
}
private function timedClickMode_mouseDown(e:MouseEvent):void {
if(e.target == time_io && e.shiftKey){
time_io.addEventListener("enterFrame", timedClickMode_enterFrame)
time_io.stage.addEventListener("mouseUp", timedClickMode_mouseUp)
if(e.target == time_io) world.makeFormation(e.stageX, e.stageY)
}
}
private function timedClickMode_enterFrame(e:Event):void {
world.makeFormation(time_io.mouseX, time_io.mouseY)
}
private function timedClickMode_mouseUp(e:MouseEvent):void {
time_io.removeEventListener("enterFrame", timedClickMode_enterFrame)
time_io.stage.removeEventListener("mouseUp", timedClickMode_mouseUp)
}
private function clearMode():void {
if(clk_io){
clk_io.removeEventListener("mouseDown", clickMode_mouseDown)
clk_io = null
}
if(time_io){
time_io.removeEventListener("mouseDown", timedClickMode_mouseDown)
time_io.removeEventListener("enterFrame", timedClickMode_enterFrame)
time_io.stage.removeEventListener("mouseUp", timedClickMode_mouseUp)
time_io = null
}
}
}
// 대형 구성 정보
import flash.geom.Point
internal class FormationData {
public var dests:Vector.<Point>
private var count:int = 0
public function FormationData(len:uint) {
dests = new Vector.<Point>(len, true)
for(var i:int=0 ; i<dests.length ; i++) dests[i] = new Point
}
public function push(dx:Number, dy:Number):void {
dests[count].x = dx
dests[count].y = dy
count ++
}
public function get full():Boolean { return count == dests.length }
public function get extra():Boolean { return count != dests.length }
}
// 대형 발생기
internal class FormationDataGenerator {
private static var list:Array = [f0, f1, f2, f3, f4, f5, f6]
private static var numUnits:uint
private static var data:FormationData
public static function get length():uint { return list.length }
public static function gen(idx:int, num:uint, dx:Number, dy:Number):FormationData {
numUnits = num
data = new FormationData(num)
list[idx](dx, dy)
return data
}
// 직사각형
public static function f0(destX:Number, destY:Number):void {
var numColumns:int = Math.sqrt(numUnits)
var numRows:int = numUnits / numColumns
var numMods:int = numUnits - numColumns*numRows
var spaceX:Number = Unit.R * 2, spaceY:Number = Unit.R * 2
var LT_x:Number = destX - numRows*spaceX/2
var LT_y:Number = destY - numColumns*spaceY/2
var count:int = 0
for(var column:int=0 ; column<numColumns ; column++){
for(var row:int=0 ; row<numRows ; row++){
data.push(LT_x + row*spaceX, LT_y + column*spaceY)
count ++
}
}
if(count < numUnits){
var numRest:uint = numUnits - numRows*numColumns
for(var i:int=0 ; i<numRest ; i++){
data.push(LT_x + i*spaceX, LT_y + column*spaceY)
count ++
}
}
}
// 원
public static function f1(destX:Number, destY:Number):void {
var R:Number = Math.min(300, numUnits*2)
var t:Number
for(var i:int=0 ; i<numUnits ; i++){
t = 2*Math.PI*i/numUnits
data.push(destX + R*Math.cos(t), destY + R*Math.sin(t))
}
}
// 다층 원
private static var circularCrowd_angle:Number = 0
public static function f2(destX:Number, destY:Number):void {
var rest:int = numUnits
var need:int = Math.min(3, rest)
var count:int = 0
var R:Number = Unit.R * 2
var t:Number = circularCrowd_angle += 1 * Math.PI/180
var k:Number
while(true){
for(var i:int=0 ; i<need ; i++){
k = 2*Math.PI * i/need
data.push(destX + R*Math.cos(k+t), destY + R*Math.sin(k+t))
count ++
}
rest -= need
if(rest <= 0) break
need = Math.min(rest, need+5)
R += Unit.R * 3
}
}
// 쐐기
public static function f3(cx:Number, cy:Number):void {
var rest:uint = numUnits
var num:uint = 1, num_inc:uint = 8
var rowSpace:Number = Unit.R * 6
var spaceX:Number = Unit.R * 2.5
var spaceY:Number = Unit.R * 2
var i:int, half:uint, other:uint
while(rest > 0){
if(rest <= num){ num=rest ; rest=0 }
else{ rest -= num }
cx -= rowSpace
half = num / 2
other = num - half
if(num & 1) { data.push(cx, cy) ; other-- }
for(i = 1 ; i <= half ; i++) data.push(cx + spaceX*i, cy - spaceY*i)
for(i = 1 ; i <= other ; i++) data.push(cx + spaceX*i, cy + spaceY*i)
num += num_inc
}
}
// 나선
public static function f4(cx:Number, cy:Number):void {
var t:Number = 0, r:Number = Unit.R * 2
var rInc:Number = Unit.R / 4
for(var i:int = 0 ; i < numUnits ; i++){
data.push( cx + r*Math.cos(t), cy + r*Math.sin(t) )
t += Math.acos((r*r + (r+rInc)*(r+rInc) - Unit.R*Unit.R*4) / (2*r*(r+rInc)))
r += rInc
}
}
// 불꽃놀이
private static var fire_angle:Number = 30 * Math.PI/180
public static function f5(cx:Number, cy:Number):void {
var rest:uint = numUnits
var t:Number = 0, r:Number = Unit.R * 2, space:Number = Unit.R * 2
var numNiddles:uint = 8, tInc:Number = 2*Math.PI / numNiddles
var limit:uint = numNiddles
var angle:Number = fire_angle += Math.PI/180
var c:int = 0
for(var i:int = 0 ; i <= limit ; i++){
if(i == numNiddles){
rest -= numNiddles
if(rest <= 0) break
else{
i = 0
r += space
t = 0
limit = Math.min(rest, numNiddles)
}
}
data.push(cx + r * Math.cos(angle + t), cy + r * Math.sin(angle + t))
t += tInc
c++
if(c == numUnits) break
}
}
// 랜덤 (더블클릭으로 하는 거였는데 시리즈에 추가함)
public static function f6(cx:Number, cy:Number):void {
for(var i:int=0 ; i<numUnits ; i++){
data.push(Math.random()*500, Math.random()*500)
}
}
}
import flash.display.DisplayObjectContainer
import flash.display.InteractiveObject
import flash.display.Sprite
import flash.display.Shape
import flash.events.Event
import flash.events.MouseEvent
import flash.events.KeyboardEvent
import flash.events.EventDispatcher
internal class SquadSelector extends EventDispatcher {
private var _io:InteractiveObject
private var _doc:DisplayObjectContainer
private var _unitContainer:Sprite
private var _x0:Number, _y0:Number // start point
private var _x1:Number, _y1:Number // end point
private var _shape:Shape // area graphic
private var _squad:Vector.<Unit>
public function SquadSelector(io:InteractiveObject, doc:DisplayObjectContainer, unitContainer:Sprite) {
_io = io
_doc = doc
_unitContainer = unitContainer
_io.addEventListener("mouseDown", startDragArea, false, 1)
_io.addEventListener("keyDown", io_keyDown)
_shape = new Shape
_squad = new <Unit>[]
}
private function io_keyDown(e:KeyboardEvent):void {
if((e.keyCode == 65 || e.keyCode == 97) && e.ctrlKey){
_squad.length = 0
var unit:Unit
for(var i:int=0 ; i<_unitContainer.numChildren ; i++){
unit = _unitContainer.getChildAt(i) as Unit
unit.selected = true
_squad.push(unit)
}
}
}
private function startDragArea(e:MouseEvent):void {
if(e.target != _io) return
if(e.shiftKey) return
_io.addEventListener("mouseMove", dragArea)
_io.addEventListener("mouseUp", stopDragArea)
_x0 = e.localX, _y0 = e.localY
_doc.addChild(_shape)
}
private function dragArea(e:MouseEvent):void {
_shape.graphics.clear()
_shape.graphics.lineStyle(1, 0x00ff00)
_shape.graphics.drawRect(_x0, _y0, e.localX - _x0, e.localY - _y0)
}
private function stopDragArea(e:MouseEvent):void {
_shape.graphics.clear()
_x1 = e.localX
_y1 = e.localY
_doc.removeChild(_shape)
_io.removeEventListener("mouseMove", dragArea)
_io.removeEventListener("mouseUp", stopDragArea)
checkSelectedUnits()
}
private function checkSelectedUnits():void {
_squad.length = 0
var unit:Unit
for(var i:int=0 ; i<_unitContainer.numChildren ; i++){
unit = _unitContainer.getChildAt(i) as Unit
if(step(_x0, unit.x, _x1) && step(_y0, unit.y, _y1)){
unit.selected = true
_squad.push(unit)
}else unit.selected = false
}
function step(a:Number, b:Number, c:Number):Boolean {
return (a <= b && b <= c) || (a >= b && b >= c)
}
}
public function get selectedSquad():Vector.<Unit> { return _squad }
}
import flash.display.Sprite
import flash.events.Event
internal class MoveManager {
public static const MANHATTAN:uint = 0
public static const DIAGONAL:uint = 1
private var types:Array = [manhattan, diagonal]
private var _container:Sprite
private var _currType:uint
public function MoveManager(unitContainer:Sprite):void {
_container = unitContainer
}
public function setMoveType(type:uint):void {
_container.removeEventListener("enterFrame", types[_currType])
_currType = type
_container.addEventListener("enterFrame", types[type])
}
private function manhattan(e:Event):void {
var speedX:Number = 5, speedY:Number = 5
var unit:Unit
for(var i:int=0 ; i<_container.numChildren ; i++){
unit = _container.getChildAt(i) as Unit
if(unit.x < unit.dest.x){
if(unit.dest.x - unit.x > speedX) unit.vx = speedX
else unit.vx = unit.dest.x - unit.x
}
else if(unit.x > unit.dest.x){
if(unit.x - unit.dest.x > speedX) unit.vx = -speedX
else unit.vx = unit.dest.x - unit.x
}
if(unit.y < unit.dest.y){
if(unit.dest.y - unit.y > speedY) unit.vy = speedY
else unit.vy = unit.dest.y - unit.y
}
else if(unit.y > unit.dest.y){
if(unit.y - unit.dest.y > speedY) unit.vy = -speedY
else unit.vy = unit.dest.y - unit.y
}
if(Math.abs(unit.x-unit.dest.x)<0.05) unit.vx = 0
if(Math.abs(unit.y-unit.dest.y)<0.05) unit.vy = 0
unit.x += unit.vx
unit.y += unit.vy
}
}
private function diagonal(e:Event):void {
var speed:Number = 5
var dx:Number, dy:Number, d:Number
var unit:Unit
for(var i:int=0 ; i<_container.numChildren ; i++){
unit = _container.getChildAt(i) as Unit
dx = unit.dest.x - unit.x
dy = unit.dest.y - unit.y
d = Math.sqrt(dx*dx + dy*dy)
if(d <= speed){
unit.x = unit.dest.x
unit.y = unit.dest.y
unit.vx = unit.vy = 0
}else{
dx /= d, dy /= d
unit.vx = dx * speed
unit.vy = dy * speed
}
unit.x += unit.vx
unit.y += unit.vy
}
}
}