Thursday, July 13, 2017

Monkey-X - Flow Fields Multiple Agents - rts - code example


Around each agent flow fields are placed direction agents who want to travel 
in their path around them. On the map itself random flowfields are placed.

The code does not work 100% correctly yet since sometimes agents go through 
each other.

Import mojo

Class flowfield
    Field mapwidth:Int,mapheight:Int
    Field tilewidth:Float,tileheight:Float
    Field screenwidth:Int,screenheight:Int
    Field map:Int[][]
    Field tempmap:Int[][]
    Field flowlinestartx:Int
    Field flowlinestarty:Int

    Method New(screenwidth:Int,screenheight:Int,mapwidth:Int,mapheight:Int)
        Self.screenwidth = screenwidth
        Self.screenheight = screenheight
        Self.tilewidth = Float(screenwidth)/Float(mapwidth)
        Self.tileheight = Float(screenheight)/Float(mapheight)
        Self.mapwidth = mapwidth
        Self.mapheight = mapheight
        ' make a array
        map = New Int[mapwidth][]
        tempmap = New Int[mapwidth][]        
        For Local i = 0 Until mapwidth
            map[i] = New Int[mapheight]
            tempmap[i] = New Int[mapheight]
        Next    
        ' -1 if no direction
        For Local y:=0 Until mapheight
        For Local x:=0 Until mapheight
            map[x][y] = -1
        Next
        Next

        '
        ' Here we create a number of points
        ' with which we draw the lines in between.
        '
        Seed = GetDate[5]
        Local lastx:Int=Rnd(2,mapwidth-2)
        Local lasty:Int=Rnd(2,mapheight-2)
        flowlinestartx = lastx
        flowlinestarty = lasty
        For Local i:=0 Until mapwidth*mapheight/10
            Local newx:Int=Rnd(2,mapwidth-2)
            Local newy:Int=Rnd(2,mapheight-2)
            flowline(lastx,lasty,newx,newy)
            lastx=newx
            lasty=newy
        Next
    End Method

    'copy temp into map
    Method refreshmap()
        For Local y:=0 Until mapheight
        For Local x:=0 Until mapwidth
            map[x][y] = tempmap[x][y]
        Next
        Next
    End Method
    
    ' Make a flowfield(line) between two points
    Method flowline(x1:Int,y1:Int,x2:Int,y2:Int)
        Local dx:Int, dy:Int, sx:Int, sy:Int, e:Int
          dx = Abs(x2 - x1)
          sx = -1
          If x1 < x2 Then sx = 1      
          dy = Abs(y2 - y1)
          sy = -1
          If y1 < y2 Then sy = 1
          If dx < dy Then 
             e = dx / 2 
          Else 
             e = dy / 2          
          End If
          Local exitloop:Bool=False
          While exitloop = False
            'SetColor 255,255,255
            'DrawPoint x1,y1
            If x1 = x2 
                If y1 = y2
                    exitloop = True
                End If
            End If
            For Local y:=-6 To 6
            For Local x:=-6 To 6
                If x1+x<0 Or x1+x>=mapwidth Or y1+y<0 Or y1+y>=mapheight Then continue
                map[x1+x][y1+y] = pointto(x1+x,y1+y,x1,y1)
            Next
            Next
            map[x1][y1] = pointto(x1,y1,x2,y2)
            If dx > dy Then
                x1 += sx ; e -= dy 
                  If e < 0 Then e += dx ; y1 += sy
            Else
                y1 += sy ; e -= dx 
                If e < 0 Then e += dy ; x1 += sx
            Endif
          Wend
          ' put contents in tempmap
        For Local y:=0 Until mapheight
        For Local x:=0 Until mapwidth
            tempmap[x][y] = map[x][y]
        Next
        Next
     End Method
    ' point the flow field direction to the x2,y2 from x1,y1
     Function pointto:Int(x1:Int,y1:Int,x2:Int,y2:Int)
        Local nd:Int=-1
        If x1<x2 Then nd=0
        If x1>x2 Then nd=4
        If y1<y2 Then nd=2
        If y1>y2 Then nd=6                    
        If x1<x2 And y1<y2 Then nd=1
        If x1>x2 And y1<y2 Then nd=3
        If x1<x2 And y1>y2 Then nd=7
        If x1>x2 And y1>y2 Then nd=5
        Return nd
     End Function

    Method draw()
        SetColor 255,255,255
        For Local y:=0 Until mapheight
        For Local x:=0 Until mapwidth
            Local direction:Int = map[x][y]
            If direction=-1 Then Continue
            Local x1:Float=Float(x)*tilewidth+tilewidth/2
            Local y1:Float=Float(y)*tileheight+tileheight/2
            Local ang:Int= (360/8*direction)
            Local x2:Float=x1+(Cos(ang)*tilewidth/2)
            Local y2:Float=y1+(Sin(ang)*tileheight/2)        
            Local x3:Float=x1+(Cos(ang+150)*tilewidth/4)
            Local y3:Float=y1+(Sin(ang+150)*tileheight/4)        
            Local x4:Float=x1+(Cos(ang-150)*tilewidth/4)
            Local y4:Float=y1+(Sin(ang-150)*tileheight/4)        
            
            DrawPoly([x2,y2,x3,y3,x4,y4])
        Next
        Next
    End Method
End Class

Class alien
    Field alienx:Float,alieny:Float
    Field speed:Float
    Method New(x:Int,y:Int)
        Self.alienx = x
        Self.alieny = y
        Self.speed = Rnd(0.1,1)
    End Method
    '
    ' here we modify the flowfield so that other units can move around the
    ' unit. We do this by placing arrows around the unit based on his direction.
    Method modifyflowmap()
        Local x:Int=alienx / myflowfield.tilewidth
        Local y:Int=alieny / myflowfield.tileheight
        If x-1<0 Or x+1>=myflowfield.mapwidth Or y-1<0 Or y+1>=myflowfield.mapheight Then Return
        Local d:Int=myflowfield.map[x][y]
        Local a:Int[8]
        Select d
            Case 2 'moving down
            a[0]=2;a[1]=1;a[2]=2;a[3]=7
            a[4]=0;a[5]=5;a[6]=2;a[7]=3
            Case 3 'moving left down
            a[0]=4;a[1]=3;a[2]=2;a[3]=3
            a[4]=4;a[5]=1;a[6]=2;a[7]=3
            Case 4 'moving left
            a[0]=4;a[1]=5;a[2]=4;a[3]=3
            a[4]=4;a[5]=5;a[6]=6;a[7]=3
            Case 5 'moving left up
            a[0]=4;a[1]=5;a[2]=6;a[3]=5
            a[4]=4;a[5]=5;a[6]=6;a[7]=3
            Case 6 ' moving up
            a[0]=4;a[1]=5;a[2]=6;a[3]=7
            a[4]=6;a[5]=5;a[6]=6;a[7]=7
            Case 7 ' moving right up
            a[0]=0;a[1]=5;a[2]=6;a[3]=7
            a[4]=0;a[5]=7;a[6]=6;a[7]=7
            Case 0 ' moving right
            a[0]=0;a[1]=1;a[2]=6;a[3]=7
            a[4]=0;a[5]=1;a[6]=0;a[7]=7
            Case 1 ' moving right down
            a[0]=0;a[1]=1;a[2]=2;a[3]=3
            a[4]=0;a[5]=1;a[6]=2;a[7]=7
            
            
            
            
            
        End Select
        
        myflowfield.map[x][y+1] = a[0]
        myflowfield.map[x-1][y+1] = a[1]
        myflowfield.map[x-1][y] = a[2]
        myflowfield.map[x-1][y-1] = a[3]
        myflowfield.map[x][y-1] = a[4]
        myflowfield.map[x+1][y-1] = a[5]
        myflowfield.map[x+1][y] = a[6]
        myflowfield.map[x+1][y+1] = a[7]
        
    End Method
    Method move()
        Local x2:Int=alienx/myflowfield.tilewidth
        Local y2:Int=alieny/myflowfield.tileheight
        
        Local d:Int=myflowfield.map[x2][y2]

        ' Move the alien based on the flowfield array's direction 0=right 1=rightdown 7=rightup
        Select d
            Case 0
            alienx+=speed
            Case 1
            alienx+=speed;alieny+=speed
            Case 2
            alieny+=speed
            Case 3
            alienx-=speed
            alieny+=speed
            Case 4
            alienx-=speed
            Case 5
            alienx-=speed
            alieny-=speed
            Case 6
            alieny-=speed
            Case 7
            alieny-=speed
            alienx+=speed
        End Select
        ' stay inside array(screen)
        If alienx+10>myflowfield.screenwidth Then alienx = myflowfield.screenwidth-10
        If alienx-10<0 Then alienx = 10
        If alieny+10>myflowfield.screenheight Then alieny = myflowfield.screenheight-10
        If alieny-10<0 Then alieny = 10
        
    End Method
    Method draw()
        SetColor 255,0,0
        DrawCircle(alienx,alieny,myflowfield.tilewidth/2)
    End Method
End Class

Global myflowfield:flowfield
Global myalien:List<alien> = New List<alien>


Class MyGame Extends App
    Field mapwidth:Int,mapheight:Int
    Method OnCreate()
        Seed = GetDate[5] + GetDate[4]
        SetUpdateRate(60)        
        myflowfield = New flowfield(DeviceWidth(),DeviceHeight(),20,20)
        For Local i:=0 Until 10
            myalien.AddLast(New alien(Rnd(DeviceWidth),Rnd(DeviceHeight)))
        Next
    End Method
    Method OnUpdate()    

        myflowfield.refreshmap()        
        For Local i:=Eachin myalien
            i.modifyflowmap
            i.move()
        Next            
           
        ' if pressed space or no move by alien then new flowfield
        If KeyHit(KEY_SPACE) Or MouseHit(MOUSE_LEFT)
            Seed = Millisecs()
            mapwidth  = Rnd(20,80)
            mapheight = mapwidth
            myflowfield = New flowfield(DeviceWidth(),DeviceHeight(),mapwidth,mapheight)
            myalien.Clear()
            For Local i:=0 Until mapwidth*mapheight/20
                Local x:Int=Rnd(DeviceWidth)
                Local y:Int=Rnd(DeviceHeight)
                myalien.AddLast(New alien(x,y))
            Next
        End If    
        
    End Method
    Method OnRender()
        Cls 0,0,0 
        SetColor 255,255,255
        
        myflowfield.draw()
        
        For Local i:=Eachin myalien
            i.draw
        Next

        SetColor 255,255,255
        DrawText("Flow Fields multiple agents - space(touch/lmb) new map.",0,0)
    End Method
End Class

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 Main()
    New MyGame()
End Function

No comments:

Post a Comment