Manipulating Obstacles

Started by science_boy, April 10, 2021, 18:40:17

Previous topic - Next topic

science_boy

I am working on a project making some changes, for educational purposes, to a Boid simulation program which I found here: https://www.syntaxbomb.com/index.php?topic=1247.0

The flocks (boids) fly around, and a user can apply obstacles on the screen for the flocks to avoid.

I have been reading the API and Docs content on the BlitzMax website, but I am very much a novice when it comes to coding.

The main thing I am trying to do with this program is limit the number of obstacles which can be created, and change the way the obstacles are cleared. For instance, currently a student could put as many obstacles on screen as they choose, and the only way to clear the obstacles is to use right click. This opens up unwanted possibilities of what can be put on the screen by placing the obstacles in certain ways. (Such as vulgar words or inappropriate images)

The way I am trying to make this work is that I would allow only a limited number of obstacles on screen, such as 5. When you click to add an obstacle the position of the newest would replace the position of the oldest, meaning there would still only be 5 obstacles on screen.

As I said, I am a total newbie so any insight is appreciated. Thanks!

Science_boy



col

#1
Hiya,

QuoteThe way I am trying to make this work is that I would allow only a limited number of obstacles on screen, such as 5. When you click to add an obstacle the position of the newest would replace the position of the oldest, meaning there would still only be 5 obstacles on screen.
When the user clicks the Left Mouse Button the code calls into the TObstacle.Create function to create a new obstacle. When a new obstacle is created it is added to the end of a TList. What you can do is modify the main loop so that when the Left Mouse button is clicked check the number of items in that TList, and if there are 5 or more then remove the first item from beginning of the list, then add the new obstacle to the end of it.

The existing code allows you to remove obstacles using the Right Mouse Button. To remove this functionality just comment out or remove the code altogether related to the Right Mouse Button being clicked.

Here are the code modifications required to achieve your goal:

The modified 'main loop' which limits the maximum numbers of obstacles to 5 and also removes the Right Mouse Button detection.
Code (BlitzMax) Select

While Not KeyHit(KEY_ESCAPE) And Not AppTerminate()
        Cls
       
        If MouseHit(1)
            ' Add this to limit the number of obstacles on screnn to 5
            Local MaximumObjects:Int = 5
            If CountList(obstacleList) >= MaximumObjects
                 ListRemove(obstacleList, obstacleList.First())
            EndIf

            TObstacle.Create(MouseX(),MouseY())
        EndIf

        ' Remove this line of code to remove the Right Mouse Button functionality.
        'If MouseHit(2) Then TObstacle.Remove()
       
        TObstacle.DrawAll()
       
        TBoid.UpdateAll()
        TBoid.DrawAll()
       
        Flip
Wend


And here is the full code with the previous modifications in place - Feel free to remove the commented code as it no longer needed for your goal:
Code (BlitzMax) Select

SuperStrict
SeedRnd MilliSecs()

AppTitle$ = "Boids ! Boids ! Boids ! Use mouse buttons to add and remove obstacles"

Global obstacleList:TList = New TList

'-----------------------------------------------------------------------------------------------------------------------------------------------'
'-----------------------------------------------------------------------------------------------------------------------------------------------'
'-----------------------------------------------------------------------------------------------------------------------------------------------'
Graphics 800,600

SetBlend ALPHABLEND


TBoid.Create(200) '---------------------------------------------------------------------- number of boids to create
                                                                                                                                                                                ' see ine the TBoid type to customize boids behaviours

SetClsColor 64,64,64

While Not KeyHit(KEY_ESCAPE) And Not AppTerminate()
        Cls
       
        If MouseHit(1)
            ' Add this to limit the number of obstacles on screnn to 5
            Local MaximumObjects:Int = 5
            If CountList(obstacleList) >= MaximumObjects
                 ListRemove(obstacleList, obstacleList.First())
            EndIf
       
            TObstacle.Create(MouseX(),MouseY())
        EndIf

        ' Remove this line of code to remove the Right Mouse Button functionality.
        'If MouseHit(2) Then TObstacle.Remove()
       
        TObstacle.DrawAll()
       
        TBoid.UpdateAll()
        TBoid.DrawAll()
       
        Flip
Wend

TBoid.RemoveAll()
TObstacle.RemoveAll()

End

'-----------------------------------------------------------------------------------------------------------------------------------------------'
Type TObstacle

        Field x:Int,y:Int
        Field radius:Int
               
        Function Create(x:Int,y:Int)
                ' Add this to limit the number of obstacles on screnn to 5
                Local MaximumObjects:Int = 5
If CountList(obstacleList) >= MaximumObjects
   ListRemove(obstacleList, obstacleList.First())
EndIf

                Local ob:TObstacle = New TObstacle
                ob.x = x
                ob.y = y
                ob.radius = 30
                ListAddLast obstacleList,ob
        End Function
       
' The Remove() Function is no longer required.
'        Function Remove()
'                For Local ob:TObstacle = EachIn obstacleList
'                        If Sqr( (ob.x-MouseX())*(ob.x-MouseX()) + (ob.y-MouseY())*(ob.y-MouseY()) ) < ob.radius
'                                ListRemove obstacleList,ob
'                                Exit
'                        EndIf
'                Next   
'        End Function
       
        Function RemoveAll()
                For Local ob:TObstacle = EachIn obstacleList
                        ListRemove obstacleList,ob
                Next   
        End Function
       
        Function DrawAll()
                SetColor 0,0,0
                For Local ob:TObstacle = EachIn obstacleList
                        DrawOval ob.x-ob.radius,ob.y-ob.radius,ob.radius*2,ob.radius*2
                Next   
        End Function
       
End Type

'-----------------------------------------------------------------------------------------------------------------------------------------------'
Type TBoid
       
        Global boidList:TList = CreateList()
        Global friendList:TList = CreateList()
       
        Global speed:Float = 3 '--------------------------------------------------------------------------------------------- boids speed
        Global smoothTurn:Float = 25 '--------------------------------------------------------------------------------------- limits boids turn angle
        Global radius:Float = 10 '------------------------------------------------------------------------------------------- boids draw size

        '############################################################################################################################################
        Global friendRadius:Float = 75 '----------------------------------------------- each boid will interact with other boids within this distance
        Global friendDistance:Float = 30 '------------------------------------------------------------------------- collision distance beetween boids
       
        Global cohesionFactor:Float = 100 '--------------------------------------------- the lower this value, the strongest boids will pack together
        Global alignSpeed:Float = 8 '--------------------------------------- the lower this value, the better boids will stay aligned with each other
       
        Global obstacleMargin:Float = 2 ' ----------------------------------------------- the lower this value, the farest boids will avoid obstacles
        '############################################################################################################################################
       
        Global friendSQRradius:Float = friendRadius*friendRadius
        Global friendSQRdistance:Float = friendDistance*friendDistance
       
       
        Field x:Float,y:Float
        Field vx:Float,vy:Float
        Field angle:Float
        Field red:Int,green:Int,blue:Int
       
        Method Update()
                GetFriends()
                If CountList(friendList) > 0
                        vx = 0
                        vy = 0
                        Cohesion()
                        Obstacle()
                        Distance()
                        Align()
                Else
                        'Erratic() ' see Erratic() Method below
                        Obstacle()
                EndIf
               
                Move()
        End Method
       
        Method Cohesion()
                Local centerX:Float
                Local centerY:Float
                For Local friend:TBoid = EachIn friendList
                        centerX = centerX + friend.x
                        centerY = centerY + friend.y
                Next
                centerX = centerX / CountList(friendList)
                centerY = centerY / CountList(friendList)
                vx = vx + (centerX-x) / cohesionFactor
                vy = vy + (centerY-y) / cohesionFactor
        End Method
       
        Method Distance()
                For Local friend:TBoid = EachIn friendList
                        Local diffX:Float = x-friend.x
                        Local diffY:Float = y-friend.y
                        Local sqrDistance:Float = diffX*diffX + diffY*diffY
                        If diffX*diffX + diffY*diffY < friendSQRdistance                       
                                vx = vx - ( friend.x - x ) / Sqr(sqrDistance)
                                vy = vy - ( friend.y - y ) / Sqr(sqrDistance)
                        EndIf
                Next
        End Method
       
        Method Obstacle()
                For Local ob:TObstacle = EachIn obstacleList
                        Local diffX:Float = x-ob.x
                        Local diffY:Float = y-ob.y
                        Local sqrDistance:Float = diffX*diffX + diffY*diffY
                        If diffX*diffX + diffY*diffY < ob.radius*ob.radius*ob.radius/obstacleMargin
                                vx = vx - ( ob.x - x ) / Sqr(sqrDistance)
                                vy = vy - ( ob.y - y ) / Sqr(sqrDistance)
                        EndIf
                Next
        End Method

        Method Align()
                Local sumVx:Float
                Local sumVy:Float
                For Local friend:TBoid = EachIn friendList
                        sumVx = sumVx + friend.vx
                        sumVy = sumVy + friend.vy
                Next
                sumVx = sumVx / CountList(friendList)
                sumVy = sumVy / CountList(friendList)
                vx = vx + ( sumVx - vx ) / alignSpeed
                vy = vy + ( sumVy - vy ) / alignSpeed
        End Method
       
        Method GetFriends()
                ClearList(friendList)
                For Local friend:TBoid = EachIn boidList
                        Local diffX:Float = x-friend.x
                        Local diffY:Float = y-friend.y
                        If diffX*diffX + diffY*diffY < friendSQRradius
                                If friend <> Self Then ListAddLast friendList,friend
                        EndIf
                Next   
        End Method
       
        Method Erratic()
                ' here we can make a special behaviour when a boid doesn't have any neighbour to intereact with <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                ' for the moment nothing happens when a boid is alone so it goes in a straight path
                ' but an erratic movement to search for neighbours would be better
        End Method
       
        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 + GraphicsWidth()
                If x > GraphicsWidth() Then x = x - GraphicsWidth()
                If y < 0 Then y = y + GraphicsHeight()
                If y > GraphicsHeight() Then y = y - GraphicsHeight()
        End Method
       
        Method Draw()
                SetColor red,green,blue
                Local x1:Float = x+Cos(angle)*radius
                Local y1:Float = y+Sin(angle)*radius
                Local x2:Float = x+Cos(angle+150)*radius
                Local y2:Float = y+Sin(angle+150)*radius
                Local x3:Float = x+Cos(angle-150)*radius
                Local y3:Float = y+Sin(angle-150)*radius       
                Local tri:Float[]=[x1,y1,x2,y2,x3,y3]
                DrawPoly tri
        End Method
       
        Function Create(count:Int)
                For Local i:Int = 1 To count
                        Local boid:TBoid = New TBoid
                        boid.x = Rand(GraphicsWidth())
                        boid.y = Rand(GraphicsHeight())
                        boid.angle = Rnd(360)
                        boid.red = Rand(50,255)
                        boid.green = Rand(50,255)
                        boid.blue = Rand(50,255)
                        ListAddLast boidList,boid
                Next
        EndFunction
       
        Function UpdateAll()
                For Local boid:TBoid = EachIn boidList
                        boid.Update()
                Next
        End Function
       
        Function DrawAll()
                For Local boid:TBoid = EachIn boidList
                        boid.Draw()
                Next   
        End Function
       
        Function RemoveAll()
                For Local boid:TBoid = EachIn boidList
                        ListRemove boidList,boid
                Next   
        End Function
       
End Type

'-----------------------------------------------------------------------------------------------------------------------------------------------'
'-----------------------------------------------------------------------------------------------------------------------------------------------'
Function SmoothRotate:Float(sourceX:Float,sourceY:Float,sourceAngle:Float,destX:Float,destY:Float,smooth:Float)
        ' Thanks to BlackSp1der on BB forums for this piece of code ! <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
        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 = sourceAngle - 360 Else If sourceAngle < 0 Then sourceAngle = sourceAngle + 360
        Return sourceAngle
End Function
https://github.com/davecamp

"When you observe the world through social media, you lose your faith in it."

science_boy

I am so grateful for your reply! This goes a long way in helping me understand the program more clearly too! Have you any suggestions on efficiently navigating the help menu in MaxIDE in order to more easily find leads on issues such as this? It is often difficult to know where to begin looking. Thanks so much!  :)

Science_boy

col

Hiya.

I'm glad it helped :)

Stepping aside from examining other programs I can only suggest to spend some time looking through the module code relating to what you want to look into. You can also run programs in a 'debug build' and step into every call/function which may eventually take you to to see inside the modules too.

I believe there may be some online help/docs but I don't know the links off hand. Someone else may well do - there may even be some tutorial links on this website?
https://github.com/davecamp

"When you observe the world through social media, you lose your faith in it."

Henri

Hi,

you can start from here https://blitzmax.org/docs/en/setup/get_started/

API-section describes the modules.

-Henri
- Got 01100011 problems, but the bit ain't 00000001