It took me more then 6 hours to get this example working. It still has a small bug sometimes that I have not been able to find yet. But for the most it works good.
Here in this example you can move a red unit with the mouse through a huge group of units and they will move out of the way. Handy for rts games and such.
It works like this: When moving into a direction it checks if the destination is taken. If so then this unit is moved into a random position. When this is not possible then the next location in that direction is handled this way. The units in that line are pushed into that direction until a free position is found.
Code below :
Import mojo
Const screenwidth:Int=640
Const screenheight:Int=480
Const tilewidth:Int=16
Const tileheight:Int=16
Class player
Field x:Int
Field y:Int
Field dx:Int
Field dy:Int
Field ox:Int
Field oy:Int
Field w:Int=16
Field h:Int=16
Method New(_x:Int,_y:Int)
x=_x
y=_y
dx=x
dy=y
End Method
Method update()
Local x1:Int=MouseX()/tilewidth
Local y1:Int=MouseY()/tileheight
Local moved:Bool=False
If MouseHit(MOUSE_LEFT)
If rectsoverlap(x1,y1,1,1,x-1,y-1,3,3) = True Then moved=True
End If
If moved=True
moveunit(x1,y1)
dx = x1
dy = y1
End If
If x<> dx Or y<>dy
If x<dx Then ox+=1
If x>dx Then ox-=1
If y<dy Then oy+=1
If y>dy Then oy-=1
If ox=16 Then x+=1 ; dx=x ; ox=0
If oy=16 Then y+=1 ; dy=y ; oy=0
If ox=-16 Then x-=1 ; dx=x ; ox = 0
If oy=-16 Then y-=1 ; dy=y ; oy = 0
End If
End Method
Method moveunit(_x,_y)
Local occupied:Bool=False
For Local i:=Eachin u
If i.x = _x And i.y=_y Then occupied = True
Next
If occupied = False Then
dx=_x
dy=_y
Else
dx=_x
dy=_y
moveunitoutoftheway(_x,_y)
End If
End Method
Method moveunitoutoftheway(x1:Int,y1:Int)
Local xdir:Int = x1-x
Local ydir:Int = y1-y
Local eloop:Bool=False
Local mx:Int=0
Local my:Int=0
While eloop=False
eloop=True
For Local i:=Eachin u
If i.x = x1+mx And i.y=y1+my
If moveintofreepos(i.x,i.y) = False Then
i.dx+=xdir
i.dy+=ydir
eloop=False
Else
Return True
End If
End If
Next
mx+=xdir
my+=ydir
Wend
End Method
Method moveintofreepos:Bool(x1:Int,y1:Int)
Local eloop:Bool=False
Local mx:Int
Local my:Int
Local cnt:Int=0
For Local i:=Eachin u
If i.x = x1 And i.y=y1
While eloop = False
eloop=True
mx=Rnd(-2,2)
my=Rnd(-2,2)
If x1+mx = p.x And y1+my = p.y Then eloop = False
If x1+mx = p.dx And y1+my = p.dy Then eloop = False
If x1+mx = x1 And y1+my = y1 Then eloop = False
For Local ii:=Eachin u
If ii.x = x1+mx And ii.y = y1+my Then eloop = False
If ii.dx = x1+mx And ii.dy = y1+my Then eloop = False
Next
cnt+=1
If cnt>100 Then Return False
Wend
i.dx = x1+mx
i.dy = y1+my
End If
Next
Return True
End Method
Method draw()
SetColor 255,0,0
DrawOval x*tilewidth+ox,y*tileheight+oy,tilewidth,tileheight
End Method
End Class
Class unit
Field x:Int
Field y:Int
Field w:Int=16
Field h:Int=16
Field dx:Float
Field dy:Float
Field ox:Int
Field oy:Int
Field moved:Bool=False
Field hasmoved:Bool=False
Method New(_x:Int,_y:Int)
x=_x
y=_y
dx=x
dy=y
End Method
Method update()
If x<>dx Or y<>dy
If x<dx Then ox+=1
If x>dx Then ox-=1
If y<dy Then oy+=1
If y>dy Then oy-=1
If ox=16 Then x=dx ; ox=0
If oy=16 Then y=dy ; oy=0
If ox=-16 Then x=dx ; ox = 0
If oy=-16 Then y=dy ; oy = 0
End If
If hasmoved = True And x=dx And y=dy Then hasmoved=False
End Method
Method draw()
SetColor 255,255,0
DrawOval (x*tilewidth)+ox,(y*tileheight)+oy,tilewidth,tileheight
End Method
End Class
Global u:List<unit> = New List<unit>
Global p:player = New player(10,10)
Class MyGame Extends App
Method OnCreate()
SetUpdateRate(60)
Local x1:Int=20
Local y1:Int=15
Local x3:Int
Local y3:Int
For Local i=0 Until 300
Local taken:Bool=True
While taken=True
Local x2:Int=(Rnd(-10,10))
Local y2:Int=(Rnd(-10,10))
x3=x1+x2
y3=y1+y2
taken=False
For Local ii:=Eachin u
If ii.x = x3 And ii.y = y3 Then taken = True ; Exit
Next
Wend
u.AddLast(New unit(x3,y3))
Next
End Method
Method OnUpdate()
p.update
For Local i:=Eachin u
i.update
Next
End Method
Method OnRender()
Cls 0,0,0
SetColor 255,255,255
DrawText "Press the mouse near the red oval to move it into that direction.",0,0
DrawText "The yellow ovals move out of the way.",0,16
For Local i:=Eachin u
i.draw
Next
p.draw
End Method
End Class
Function distance:Int(x1:Int,y1:Int,x2:Int,y2:Int)
Return Abs(x2-x1) + Abs(y2-y1)
End Function
Function getangle:Int(x1:Int,y1:Int,x2:Int,y2:Int)
Return ATan2(y1-y2, x1-x2)
End Function
Function rectsoverlap:Bool(x1:Int, y1:Int, w1:Int, h1:Int, x2:Int, y2:Int, w2:Int, h2:Int)
If x1 >= (x2 + w2) Or (x1 + w1) <= x2 Then Return False
If y1 >= (y2 + h2) Or (y1 + h1) <= y2 Then Return False
Return True
End Function
Function Main()
New MyGame()
End Function
No comments:
Post a Comment
Note: Only a member of this blog may post a comment.