Boids!

Jimmy

Well-known member
3rd Party Module Dev
Tutorial Author
Joined
Jan 2, 2020
Messages
1,105
Trying to use Cerberus for avoiding obstacles..

Wondering if anyone has a good idea how to create "magnet" food, something that lures the boids towards them?


Screenshot 2022-01-31 at 16.26.33.png

Code:
#MOJO_AUTO_SUSPEND_ENABLED=False
Import mojo2

Global myobstacle:List<obstacle>
Global boidlist:List<boid> = New List<boid>

Function Main()
    New MyGame()
End

Class MyGame Extends App

    Field myboid:boid
    Field canvas:Canvas
   
    Method OnCreate()
        canvas = New Canvas()
        SetSwapInterval 1 ; SetUpdateRate 0
        myobstacle = New List<obstacle>
        For Local i:Int = 0 Until 10
            myobstacle.AddLast(New obstacle(Rnd(640),Rnd(480)))
        Next
        myboid = New boid()
        myboid.create(20)
    end
   
    Method OnUpdate()
        myboid.updateall()    
        If TouchDown(0) Then myobstacle.AddLast(New obstacle(TouchX(0),TouchY(0)))
    end
   
    Method OnRender()
        canvas.Clear
        canvas.SetColor 1,1,1
        myboid.drawall(canvas)
        For Local i:=Eachin myobstacle
            i.draw(canvas)
        Next
        canvas.Flush
    end
   
End Class

' -----------------------------------------------

Class obstacle

    Field x:Float,y:Float
    Field radius:Int
   
    Method New(x:Int,y:Int)
        Self.x = x ; Self.y = y ; Self.radius = 20  
    end
   
    Method draw(canvas:Canvas)
        canvas.SetColor 0,1,0
        canvas.DrawCircle x,y,radius
    end  
End Class

Class boid

    Field friendlist:List<boid> = New List<boid>
    Field x:Float,y:Float,angle:Float
    Field vx:Float,vy:Float
    Field alignspeed:Float = 8
    Field speed:Float = 3
    Field smoothturn:Float = 25
    Field radius:Float = 10
    Field cohesionfactor:Float = 100
    Field friendradius:Float = 75
    Field frienddistance:Float = 30
    Field friendsqradius:Float
    Field friendsqrdistance:Float
    Field obstaclemargin:Float = 2
   
    Method New()
        friendsqradius = friendradius*friendradius
        friendsqrdistance = frienddistance*frienddistance
    end
   
    Method create(count:Int)
        For Local i:=0 Until count
            Local newboid:boid = New boid()
            newboid.x = Rnd(640) ; newboid.y = Rnd(480)
            newboid.angle = Rnd(360)
            boidlist.AddLast(newboid)
        Next
    end
   
    Method update()
        getfriends()
        If friendlist.Count > 0
            vx = 0 ; vy = 0
            cohesion() ; obstacle() ; distance() ; align()
        Else
            obstacle()
        End If      
        ' Clamp speed
        If vx < -2 Then vx = -2
        If vy < -2 Then vy = -2
        If vx > 2 Then vx = 2
        If vy > 2 Then vy = 2
        move()
    end
   
    Method obstacle()
        For Local i:=Eachin myobstacle
            Local diffx:Float = x - i.x
            Local diffy:Float = y - i.y
            Local sqrdistance:Float=diffx*diffx+diffy*diffy
            If diffx*diffx+diffy*diffy < i.radius*i.radius*i.radius/obstaclemargin
                vx -= (i.x - x) / Sqrt(sqrdistance)
                vy -= (i.y - y) / Sqrt(sqrdistance)
            End If
        Next
    end
   
    Method cohesion()
        Local centerx:Float
        Local centery:Float
        For Local i:= Eachin friendlist
            centerx += i.x
            centery += i.y
        Next
        centerx /= friendlist.Count
        centery /= friendlist.Count
        vx += (centerx-x) / cohesionfactor
        vy += (centery-y) / cohesionfactor
    end
   
    Method distance()
        For Local i:=Eachin friendlist
            Local diffx:Float=x-i.x,diffy:Float=y-i.y
            Local sqrdistance:Float=diffx*diffx+diffy*diffy
            If diffx*diffx+diffy*diffy < friendsqrdistance
                vx -= (i.x - x) / Sqrt(sqrdistance)
                vy -= (i.y - y) / Sqrt(sqrdistance)
            End If
        Next
    end
   
    Method align()
        Local sumvx:Float,sumvy:Float
        For Local i:=Eachin friendlist
            sumvx += i.vx ; sumvy += i.vy
        Next
        sumvx = sumvx /  friendlist.Count ; sumvy = sumvy / friendlist.Count
        vx = vx + (sumvx - vx) / alignspeed ; vy = vy + (sumvy - vy) / alignspeed
    end
   
    Method move()
        x = x + vx ; y = y + vy
        angle = smoothrotate(x,y,angle,x+vx,y+vy,smoothturn)
        x = x + Cos(angle) * speed ; y = y + Sin(angle) * speed
        If x < 0 Then x = x + 640
        If y < 0 Then y = y + 480
        If x > 640 Then x = x - 640
        If y > 480 Then y = y - 480
    end
   
    Method getfriends()
        friendlist.Clear()
            For Local i:=Eachin boidlist
            Local diffx:Float=x-i.x,diffy:Float=y-i.y          
            If diffx*diffx+diffy*diffy < friendsqradius
                If i <> Self Then
                    friendlist.AddLast(i)
                End If
            End If
        Next
    end
   
    Method updateall()
        For Local i:=Eachin boidlist
            i.update
        Next
    end
   
    Method drawall(canvas:Canvas)
        For Local i:=Eachin boidlist
            i.draw(canvas)
        Next
    end
   
    Method draw(canvas:Canvas)
        canvas.SetColor 1,1,1
        canvas.DrawCircle x,y,10
    end
   
    Function smoothrotate:Float(sourceX:Float,sourceY:Float,sourceAngle:Float,destX:Float,destY:Float,smooth:Float)
        Local targetAngle:Float = ATan2(sourceY-destY,sourceX-destX)
        Local tempAngle:Float = targetAngle - Sgn(targetAngle-sourceAngle) * 360
        If Abs(targetAngle-sourceAngle) > Abs(tempAngle-sourceAngle) Then targetAngle = tempAngle
        If sourceAngle <> targetAngle Then sourceAngle = sourceAngle - Sgn(targetAngle-sourceAngle) * (180-Abs(targetAngle-sourceAngle)) / (1+smooth)
        If sourceAngle >= 360 Then sourceAngle -= 360 Else If sourceAngle < 0 Then sourceAngle += 360
        Return sourceAngle
    end

End Class
 

MikeHart

Administrator
Joined
Jun 19, 2017
Messages
3,281
I didn't try your code, but I would measure the distance between the food and the boid. If it is below a threshold value, I would make the boid move towards the food. If the boid has a direction, I would determine the shortest angle to towards the food and then make it move forward while turning into to that direction.
 

Jimmy

Well-known member
3rd Party Module Dev
Tutorial Author
Joined
Jan 2, 2020
Messages
1,105
I didn't try your code, but I would measure the distance between the food and the boid. If it is below a threshold value, I would make the boid move towards the food. If the boid has a direction, I would determine the shortest angle to towards the food and then make it move forward while turning into to that direction.
That sounds like a good idea! I was first about to introduce some kind of gravity but what you said makes lots more sense as you never
know what's behind a corner. Some kind of distance would be good and to add eye-contact to add to the interest. Maybe some kind of trail of smell, that would make the gameplay more fun. I'll see what I can do!
 
Top Bottom