Here is some work I did a long time ago. It was part of a project, but I have not heard from the folks on that project for quite a long time now, so I figure this is free to post. Anyway, this is some of my older work, and I am sure I could do better now. I might revist this type of work if there is interest in this snippet.
1) Select a formation type (at the top)
2) Left-click and drag to select units. You can select as many as you want.
3) Right-click to select the disired destination.
4) Right-click and drag to select the desired destination and direction.
Sync On: Sync Rate 60: AutoCam Off
Randomize Timer()
Null = Make Vector3(1)
Type Coordinates
x as Float
y as Float
z as Float
Endtype
Type Mini_AI
destination as Coordinates
movespeed as float
selected as boolean
ObjectID as Integer
Formation as Integer
Position as Integer
EndType
Type Flags
direct as boolean
click1 as boolean
sel as boolean
move as boolean
Endtype
Global Object as Coordinates
Global Target as Coordinates
Global AI As Coordinates
Global Camera as Coordinates
Global Limb as Coordinates
Global flag as flags
Global Formation as Integer
Dim Formation_Dest(20) as Coordinates
Dim Formation_Face(20) as Coordinates
Dim Formation_Age(20) as Integer
Global Selected_Formation as Integer
Global Avg_Sel as Coordinates
Global Avg_frm as Coordinates
Formation = 2
Dim Units(1000) as Mini_AI
Make Matrix 1,1000,1000,20,20
Update Matrix 1
Position Camera 500,750,-50
Point Camera 500,0,500
`Set up some test units.
Make_AI()
`Set up some random test objects
For i = 501 to 700
Make_Unit(i)
Position Object i,rnd(1000),5,rnd(1000)
Do
n=rnd(999)+1
If Units(n).ObjectID=0 Then Exit
Loop
Units(n).ObjectID=i
Return_Object(i)
Units(n).destination.x=Object.x:Units(n).Destination.y=Object.y:Units(n).Destination.z=Object.z
Units(n).movespeed=0
Units(n).selected=0
Next i
`Make a sample Sprite Menu
Restore Menu_Items
Read n
For i = 1 to n
Read Menu$
Menu_Sprite(1000+i,Menu$)
Sprite 1000+i,-100,-100,1000+i
Sprite 1000+i,50+i*(Sprite Width(1000+i)*1.5),10,1000+i
Set Sprite Alpha 1000+i,60
Next i
Make_AI_Sprite()
`Main (test) loop.Very easy to read what's going on here.
Do
Pointer()
Selector()
Check_Selected()
Direct_Selected()
Coordinate_Movement()
Sprite_Menu()
Move_Camera()
Set Cursor 0,0
Print screen fps()
Print Formation_Age(4)
Sync
Loop
Function Move_Camera()
Return_Camera()
Position Camera Camera.x,Camera.y+Upkey()-DownKey(),Camera.z
Point Camera 500,Camera.y,500
Position Object 2,500,Camera.y,500
Set Object To Camera Orientation 2
Turn Object Right 2,(LeftKey()-RightKey())*.5
Move Object 2,-600
Return_AI()
Position Camera AI.x,AI.y,AI.z
Point Camera 500,0,500
Endfunction
`High-level Sprite Menu control
Function Sprite_Menu()
m=Menu_Select()
Select m
Case 1001
Formation = 1
EndCase
Case 1002
Formation = 2
EndCase
Case 1003
Formation = 3
EndCase
Case 1004
End
EndCase
EndSelect
EndFunction
`Detail-level Sprite Menu handling. Detects which Sprite menu item was selected.
Function Menu_Select()
mi=0
If MouseClick()=1
mi=Sprite Collision(2,0)
If mi>0
Restore Menu_Items
Read n
For i = 1 to n
Set Sprite Alpha 1000+i,60
Next i
Set Sprite Alpha mi,100
Endif
Endif
EndFunction mi
`This function handles actually moving the objects to their destinations. Once close to the destination, they stop.
Function Move_Units()
For i=1+flag.move to 999+flag.move step 2` checks even/odd numbered objects on alternating cycles to save fps.
If Units(i).formation>0 and Units(i).formation<>Selected_Formation
If Object Exist(Units(i).formation)=0
Units(i).formation=0:Units(i).movespeed=0
Else
Turn_Unit(i)
Units(i).destination.x=Limb Position X(Units(i).formation,Units(i).position)
Units(i).destination.y=Limb Position Y(Units(i).formation,Units(i).position)
Units(i).destination.z=Limb Position Z(Units(i).formation,Units(i).position)
Position Object 2,Units(i).Destination.x,Units(i).Destination.y,Units(i).Destination.z
Return_AI()
d#=Return_Distance(2,Units(i).ObjectID)
If d#>Units(i).MoveSpeed then d#=Units(i).MoveSpeed
Move Object Units(i).ObjectID,d#
`Set Object To Object Orientation Units(i).ObjectID,Units(i).formation
Endif
Endif
Next i
If flag.move=0 Then flag.move=1 else flag.move=0
EndFunction
Function Turn_Unit(i)
Return_Limb(Units(i).formation,Units(i).position)
Position Object 2,Limb.x,Limb.y,Limb.z
dm#=Return_Sqrd_Distance(Units(i).ObjectID,2)+1000.0
if dm#<1000.0+Units(i).movespeed
Position Object Units(i).ObjectID,limb.x,limb.y,limb.z
Set Object To Object Orientation Units(i).ObjectID,Units(i).formation
Exitfunction
Endif
Return_AI()
L=0
For n=0 to 2
Return_Limb(Units(i).ObjectID,n)
dx#=Limb.x-AI.x:dz#=Limb.z-AI.z
d#=dx#*dx#+dz#*dz#
If d#<dm#
dm#=d#
L=n
Endif
Next n
if L=0 then ExitFunction
L=((L-1)*2)-1
Turn Object Right Units(i).ObjectID,L*Units(i).movespeed*10.0*(Rnd(50.0)/10.0)
EndFunction
Function Direct_Selected()
If Mouseclick()<>2
Flag.direct=0
Flag.click1=0
Selected_Formation=0
ExitFunction
Else
Flag.direct=1
Endif
If flag.direct=0 Then ExitFunction
n=Count_selected()
If n=0 Then ExitFunction
If Flag.click1=0
Flag.click1=1
Select Formation
Case 1
New_Obj=Make_Wedge_Formation(n)
EndCase
Case 2
New_Obj=Make_Column_Formation(n)
EndCase
Case 3
New_Obj=Make_Flank_Formation(n)
EndCase
EndSelect
Pointer()
Return_AI()
Formation_Dest(New_Obj).x=AI.x:Formation_Dest(New_Obj).y=5:Formation_Dest(New_Obj).z=AI.z
Position Object New_Obj,AI.x,5,AI.z
Formation_Age(New_Obj)=0
Selected_Formation=New_Obj
Else
Pointer()
Return_AI()
Return_Object(Selected_Formation)
Position Object 2,Object.x,Object.y,Object.z
Point Object 2,AI.x,AI.y,AI.z
Move Object 2,20
Return_AI()
Point Object Selected_Formation,AI.x,5,AI.z
Formation_Face(Selected_Formation).x=AI.x:Formation_Face(Selected_Formation).y=5:Formation_Face(Selected_Formation).z=AI.z
Formation_Age(Selected_Formation)=0
Endif
EndFunction
`This function points each selected object to their individual formation destinations and sets them moving.
Function Coordinate_Movement()
For formations=4 to 20
If Object Exist(formations)
If Mouseclick()<>2 and Formation_Age(formations)=0 Then Formation_Age(formations)=1
Endif
If Formation_Age(formations)>0 And Object Exist(formations)
Move_Formation(formations)
Endif
Move_Units()
Next formations
EndFunction
Function Move_Formation(frm)
Select Formation_Age(frm)
Case 1
Hide Object frm
Formation_Age(frm)=2
first=0
For i = 1 to 1000
If Units(i).selected>0
If first=0 then first=i
Units(i).MoveSpeed=.5
Endif
Next i
if first=0 then ExitFunction
Return_Object(Units(first).ObjectID)
Position Object frm,Object.x,Object.y,Object.z
Set Object To Object Orientation frm,Units(first).ObjectID
pos=0
For i = 1 to 1000
If Units(i).Selected Then Units(i).formation=frm:Units(i).Position=pos:pos=pos+1
Next i
EndCase
Case 2
If Object Exist(2)=0 Then Make_AI()
Return_Object(frm)
Position Object 2,Object.x,Object.y,Object.z
Set Object To Object Orientation 2,frm
Move Object 2,20
Point Object 2,Formation_Dest(frm).x,Formation_Dest(frm).y,Formation_Dest(frm).z
cs#=Count_Formation(frm)
cs#=Sqrt(cs#)
Move Object 2,2.0/cs#
Return_AI()
Point Object frm,AI.x,5,AI.z
Move Object frm,4.0/cs#
Position Object 2,Formation_Dest(frm).x,Formation_Dest(frm).y,Formation_Dest(frm).z
d#=Return_Sqrd_Distance(frm,2)
if d#<5 Then Formation_Age(frm)=Formation_Age(frm)+1
EndCase
Case Default
Return_Object(frm)
Position Object 2,Object.x,Object.y,Object.z
Set Object To Object Orientation 2,frm
Move Object 2,20
cs#=Count_Formation(frm)
cs#=Sqrt(cs#)
Point Object 2,Formation_Face(frm).x,Formation_Face(frm).y,Formation_Face(frm).z
Move Object 2,2.0/cs#
Return_AI()
Point Object frm,AI.x,AI.y,AI.z
Formation_Age(frm)=Formation_Age(frm)+1
If Formation_Age(frm)>1000 Then Delete Object frm:Formation_Age(frm)=0
Endcase
EndSelect
EndFunction
`This function checks to see which objects are selected and which are not. Does nothing if the Selector object (3) does not exist.
Function Check_Selected()
If Object Exist(3)=0 Then ExitFunction
For i = 1+flag.sel to 999+flag.sel step 2 ` checks even/odd numbered objects on alternating cycles to save fps.
obj=Units(i).ObjectID
If obj>0
p=Pick Object(Object Screen X(obj),Object Screen Y(obj),3,3)
if p=0 Then p=Pick Object(Mousex(),Mousey(),obj,obj)
If p>0
Color Object obj,rgb(255,0,0)
Units(i).Selected=1
Else
If Units(i).Selected=1 And ControlKey()=0
Color Object obj,rgb(255,255,255)
Units(i).Selected=0
Endif
Endif
Endif
Next i
If flag.sel=0 then flag.sel=1 else flag.sel=0
EndFunction
`This function creates and positions an invisble 3d pointer (AI object, 2) and a sprite pointer. The pointer is used to get 3d coordinates from the mouse cursor.
Function Pointer()
Sprite 2,Mousex(),Mousey(),2
Return_Camera()
Pick Screen Mousex(),MouseY(),20
Position Object 2,Get Pick Vector X()+Camera.x,Get Pick Vector Y()+Camera.y,Get Pick Vector Z()+Camera.z
Return_Camera()
Point Object 2,Camera.x,Camera.y,Camera.z
Turn Object Right 2,180
Do
Move Object 2,10
If Object Position Y(2)<5 Then Exit
Loop
EndFunction
`This function draws the 3d selection area by calling the Make_Selector() function and plugging in the correct variables.
Function Selector()
If MouseClick()<>1
If Object Exist(3) Then Delete Object 3
flag.direct=0
ExitFunction
Endif
If Object Exist(3)=0
Return_AI()
Make_Selector(ai.x,ai.y,ai.z,ai.x+.1,ai.y,ai.z+.1)
Else
Return_Object(3)
Return_AI()
Make_selector(Object.x,Object.y,Object.z,AI.x,AI.y,AI.z)
Endif
EndFunction
`This function creates the 3d selection area for selection objects in the 3d world.
Function Make_Selector(tx#,ty#,tz#,bx#,by#,bz#)
If Object Exist(3) Then Delete Object 3
sx#=bx#-tx#:sy#=by#-ty#:sz#=bz#-tz#
Make Object Triangle 3,0,0,0,0,sy#,sz#,sx#,sy#,sz#
Make Mesh from Object 3,3
Delete Object 3
Make Object Triangle 3,0,0,0,sx#,0,0,sx#,sy#,sz#
Add Limb 3,1,3
Position Object 3,tx#,ty#,tz#
Color Object 3,Rgb(200,200,0)
Set Alpha Mapping On 3,30
Set Object Cull 3,0
Disable Object ZDepth 3
EndFunction
`Creates a small AI (dummy) object for various uses. A real workhorse. Great as a temporary place holder.
Function Make_AI()
If Object Exist(2) Then Delete Object 2
Make Object Sphere 2,1,6,6
Set Object Collision To Boxes 2
Set Object Collision On 2
Hide Object 2
EndFunction
`Makes a small (1x1) hidden sprite. Mainly used for quick return of a selected sprite from the mouse.
Function Make_AI_Sprite()
If Sprite Exist(2) Then Delete Sprite 2
If Image Exist(2) Then Delete Image 2
If Bitmap Exist(1) Then Delete Bitmap 1
Create Bitmap 1,64,64
Set Current Bitmap 1
Box 0,0,63,63
Get Image 2,0,0,1,1,0
Sprite 2,-10,-10,2
Hide Sprite 2
Set Current Bitmap 0
Delete Bitmap 1
EndFunction
`Creates a wedge formation object.
Function Make_Wedge_Formation(size)
fObj=0
For i = 4 to 20
If Object Exist(i)=0 And fObj=0 Then fObj=i
Next i
If fObj<20 And Object Exist(fObj+1) Then Delete Object fObj+1
If fObj=0 And Object Exist(4) Then Delete Object 4
Make Object Triangle fObj,-8,0,0,0,0,13.85,8,0,0
Make Mesh From Object fObj,fObj
r#=2:c#=1
For i = 1 to size-1
Add Limb fObj,i,fObj
Offset Limb fObj,i,-20.0*(r#/2.0)+(c#-1)*20,0,(r#-1)*-20.0
c#=c#+1
If c#>r# Then r#=r#+1:c#=1
Next i
Offset Limb fObj,0,-8,0,0
Set Object Collision To Boxes fObj
Set Object Collision Off fObj
Disable Object ZDepth fObj
Color Object fObj,rgb(255,0,0)
Set Object Emissive fObj,rgb(200,0,0)
Set Alpha Mapping On fObj,60
EndFunction fObj
`Creates a column formation object.
Function Make_Column_Formation(size)
fObj=0
For i = 4 to 20
If Object Exist(i)=0 And fObj=0 Then fObj=i
Next i
If fObj<20 And Object Exist(fObj+1) Then Delete Object fObj+1
If fObj=20 And Object Exist(4) Then Delete Object 4
Make Object Triangle fObj,-8,0,0,0,0,13.85,8,0,0
Make Mesh From Object fObj,fObj
max=2
r=1:c=0
For i = 1 to size-1 Step 2
Add Limb fObj,i,fObj
Offset Limb fObj,i,r*30,0,c*20
Add Limb fObj,i+1,fObj
Offset Limb fObj,i+1,r*-30,0,c*20
r=r+1:if r>=max then r=0:c=c-1
Next i
Set Object Collision To Boxes fObj
Set Object Collision Off fObj
Disable Object ZDepth fObj
Color Object fObj,rgb(255,0,0)
Set Object Emissive fObj,rgb(200,0,0)
Set Alpha Mapping On fObj,60
EndFunction fObj
`Creates a flank formation object.
Function Make_Flank_Formation(size)
fObj=0
For i = 4 to 20
If Object Exist(i)=0 And fObj=0 Then fObj=i
Next i
If fObj<20 And Object Exist(fObj+1) Then Delete Object fObj+1
If fObj=20 And Object Exist(4) Then Delete Object 4
Make Object Triangle fObj,-8,0,0,0,0,13.85,8,0,0
Make Mesh From Object fObj,fObj
max=size/8
r=1:c=0
For i = 1 to size-1 Step 2
Add Limb fObj,i,fObj
Offset Limb fObj,i,r*30,0,c*20
Add Limb fObj,i+1,fObj
Offset Limb fObj,i+1,r*-30,0,c*20
r=r+1:if r>=max then r=0:c=c-1
Next i
Set Object Collision To Boxes fObj
Set Object Collision Off fObj
Disable Object ZDepth fObj
Color Object fObj,rgb(255,0,0)
Set Object Emissive fObj,rgb(200,0,0)
Set Alpha Mapping On fObj,60
EndFunction fObj
`Returns a count of how many units are selected
Function Count_Selected()
n=0
`Doing 4 sets of 1 to 250 instead of 1 set of 1 to 1000 saves a little bit on the for/next overhead.
For i = 1 to 250
If Units(i).Selected>0 Then n=n+1
j=i+250
If Units(j).Selected>0 Then n=n+1
j=j+250
If Units(j).Selected>0 Then n=n+1
j=j+250
If Units(j).Selected>0 Then n=n+1
Next i
EndFunction n
Function Count_Formation(frm)
n=0
`Doing 4 sets of 1 to 250 instead of 1 set of 1 to 1000 saves a little bit on the for/next overhead.
For i = 1 to 250
If Units(i).Formation=frm Then n=n+1
j=i+250
If Units(j).Formation=frm Then n=n+1
j=j+250
If Units(j).Formation=frm Then n=n+1
j=j+250
If Units(j).Formation=frm Then n=n+1
Next i
EndFunction n
Function Count_Moving()
n=0
`Doing 4 sets of 1 to 250 instead of 1 set of 1 to 1000 saves a little bit on the for/next overhead.
For i = 1 to 250
If Units(i).movespeed>0 Then n=n+1
j=i+250
If Units(j).movespeed>0 Then n=n+1
j=j+250
If Units(j).movespeed>0 Then n=n+1
j=j+250
If Units(j).movespeed>0 Then n=n+1
Next i
Set Cursor 0,25:Print n
EndFunction n
Function Average_Formation(frm)
n#=Count_Formation(frm)
If n#=0 Then ExitFunction
Avg_frm.x=0:Avg_frm.y=0:Avg_frm.z=0
For i = 1 to 250
If Units(i).Formation>0
Return_Object(i)
Avg_frm.x=Object.x:Avg_frm.y=Object.y:Avg_frm.z=Object.z
Endif
j=i+250
If Units(i).Formation>0
Return_Object(j)
Avg_frm.x=Object.x:Avg_frm.y=Object.y:Avg_frm.z=Object.z
Endif
j=j+250
If Units(i).Formation>0
Return_Object(j)
Avg_frm.x=Object.x:Avg_frm.y=Object.y:Avg_frm.z=Object.z
Endif
j=j+250
If Units(i).Formation>0
Return_Object(j)
Avg_frm.x=Object.x:Avg_frm.y=Object.y:Avg_frm.z=Object.z
Endif
Next i
Avg_frm.x=Avg_frm.x/n#:Avg_frm.y=Avg_frm.y/n#:Avg_frm.z=Avg_frm.z/n#
EndFunction
`These next four functions were created because I was lazy.
`But they also store the location in variables, which is quicker than calling the position functions.
Function Return_Object(ObjectID)
Object.x=Object Position X(ObjectID):Object.y=Object Position Y(ObjectID):Object.z=Object Position Z(ObjectID)
EndFunction
Function Return_Target(TargetID)
Target.x=Object Position X(TargetID):Target.y=Object Position Y(TargetID):Target.z=Object Position Z(TargetID)
EndFunction
Function Return_AI()
AI.x=Object Position X(2):AI.y=Object Position Y(2):AI.z=Object Position Z(2)
EndFunction
Function Return_Camera()
Camera.x=Camera Position X():Camera.y=Camera Position Y():Camera.z=Camera Position Z()
EndFunction
Function Return_Limb(ObjectID,LimbID)
Limb.x=Limb Position X(ObjectID,LimbID):Limb.y=Limb Position Y(ObjectID,LimbID):Limb.z=Limb Position Z(ObjectID,LimbID)
EndFunction
Function Return_Distance(obj1,obj2)
If Object Exist(obj1)=0 Then ExitFunction 0.0
If Object Exist(obj2)=0 Then ExitFunction 0.0
Return_Object(obj1)
Return_target(obj2)
Set Vector3 1,Object.x-Target.x,Object.y-Target.y,Object.z-Target.z
dist#=Length Vector3(1)
EndFunction dist#
Function Return_Sqrd_Distance(obj1,obj2)
If Object Exist(obj1)=0 Then ExitFunction 0.0
If Object Exist(obj2)=0 Then ExitFunction 0.0
Return_Object(obj1)
Return_target(obj2)
Set Vector3 1,Object.x-Target.x,Object.y-Target.y,Object.z-Target.z
dist#=Squared Length Vector3(1)
EndFunction dist#
`Create the Sprite Images for Menu items.
Function Menu_Sprite(SpriteID,text$)
If Sprite Exist(SpriteID) Then Delete Sprite SpriteID
If Image Exist(SpriteID) Then Delete Image SpriteID
Set Text Size 16
w=Text Width(text$)*1.2
h=Text Height(text$)*1.2
If Bitmap Exist(1) Then Delete Bitmap 1
Create Bitmap 1,512,512
Set Current Bitmap 1
Center Text w/2,2,text$
Get Image SpriteID,0,0,w,h,1
Set Current Bitmap 0
Delete Bitmap 1
EndFunction
Function Make_Unit(ObjectID)
If Object Exist(ObjectID) then Delete Object ObjectID
Make Object Triangle ObjectID,0,0,0,0,.01,0,0,0,0
Make Mesh From Object ObjectID,ObjectID
Delete Object ObjectID
Make Object Cube ObjectID,10
Add Limb ObjectID,1,ObjectID
Offset Limb ObjectID,1,-1,0,-.2
Add Limb ObjectID,2,ObjectID
Offset Limb ObjectID,2,1,0,-.2
Delete Mesh ObjectID
EndFunction
Menu_Items:
Data 4
Data "Wedge","Column","Flank","Quit "
Open MMORPG: It's your game!