Case Study #1: Robert the Luggage Bot
Part 2: Adding Complexity
So we have our simplistic bot, which barely qualifies under the definition. Robert hovers where we need and stays pretty stable.

Here we have Robert with a few additional features. Therefore, this lesson will be on adding complexity.
First, the new construction changes.

The ranger, E2 chip, and entity marker remain, but there is now also a Wire NoCollide linked to the border ring.
And the code: Code:
@name Robert the Luggage Bot
@inputs Alt Border:entity
@outputs NoCol
@persist Plate:entity O:entity
@persist Ang1:angle Ang2:angle Vec:vector
@persist Dist Ground R:vector PrimeAlt
@persist Cen:vector
if(first()){
PrimeAlt = 50 #How high the bot should hover
Trail = 300 #How far behind the bot should trail
Follow = 130 #How far away the drone should stay
Unlock = 150 #How close before the border unlocks
Plate = entity():isWeldedTo()
O = owner()
R = vec(Trail, Follow, Unlock)
Cen = Plate:massCenter()
Dist = PrimeAlt
Ground = (Plate:pos():z() - Border:pos():z())
}
runOnTick(1)
#OPERATION
#Angular stabiliser
Ang1 = -Border:angles()*1000
Ang2 = -Plate:angles()*1000
Border:applyAngForce(Ang1 + $Ang1*5)
Plate:applyAngForce(Ang2 + $Ang2*5)
#Directional force
Vec = (Cen - Plate:massCenter())*1000
Plate:applyForce(Vec + $Vec*5)
#TRICKS
#Owner Tracking
Own = O:shootPos():setZ(Cen:z())
#Altitude control
timer("alt_check",100)
if(clk("alt_check")){
Inc = (Dist - Alt)/4
Cen = Cen:setZ(Cen:z()+Inc)}
#Owner-Bot Distance Management
#Distance from Bot to Owner
D = Cen:distance(Own)
Z = Cen:z() #Alt elevation
Slip = Own - Cen #Owner-Bot separation
#Cen modified for Trail & Follow
#Trail = R:x()
#Follow = R:y()
if(D > R:x()){
Cen = (Own - Slip:setZ(0):normalized()*R:x()):setZ(Z)}
elseif(D < R:y()){
Cen = (Own - Slip:setZ(0):normalized()*R:y()):setZ(Z)}
#Border no collide & floor dropping
if(D < R:z() & D > R:y()){
if(clk("alt_check") & Dist > Ground){Dist -= Ground/5}
if(Alt <= Dist){NoCol = 1}}
else{NoCol = 0,Dist = PrimeAlt} As you can see, quite a few things have changed.
The new features of Robert (Hereon referred to as Robby 2.0) are:- Owner tracking, it remains within a given range of distance from them on the X/Y axises.
- It lowers to the ground and no collides the border when the owner is within a certain distance, then rises slowly back up.
Now let's look at how it achieves these features: Code:
@name Robert the Luggage Bot
@inputs Alt Border:entity
@outputs NoCol
@persist Plate:entity O:entity
@persist Ang1:angle Ang2:angle Vec:vector
@persist Dist Ground R:vector PrimeAlt
@persist Cen:vector
if(first()){
PrimeAlt = 50 #How high the bot should hover
Trail = 300 #How far behind the bot should trail
Follow = 130 #How far away the drone should stay
Unlock = 150 #How close before the border unlocks
Plate = entity():isWeldedTo()
O = owner()
R = vec(Trail, Follow, Unlock)
Cen = Plate:massCenter()
Dist = PrimeAlt
Ground = (Plate:pos():z() - Border:pos():z())} A bunch of new persists, but pivotally the following:- Altitude persists:
- Dist - To tell the bot how high to hover at any given time
- PrimeAlt - The standard altitude the bot should maintain
- Ground - The distance from the plate to the border, so the bot can comfortably land
- R - A vector to hold all three comfortably
- O - The owner's entity
- NoCol - An output for the Wire NoCollide
Following these new persists, we have the appropriate start-up allocations, setting the different persists to what they need to be during operation. Because the R vector holds the three altitude numbers, you can modify each one individually before spawning the chip, without digging deeply through the code. This isn't a feature unique to drone design but it's a helpful little feature to have for personal bots like this.
Minor alterations have been made to the pre-existing altitude and positioning code, mostly altering static numbers to variables and adding timers to slowly correct things rather than shunting them into position, so we'll skip past that and on to the meat of the alterations.
First up: The Owner-following code Code:
#TRICKS
#Owner Tracking
Own = O:shootPos():setZ(Cen:z())
#Altitude control
timer("alt_check",100)
if(clk("alt_check")){
Inc = (Dist - Alt)/4
Cen = Cen:setZ(Cen:z()+Inc)}
#Owner-Bot Distance Management
#Distance from Bot to Owner
D = Cen:distance(Own)
Z = Cen:z() #Alt elevation
Slip = Own - Cen #Owner-Bot separation
#Cen modified for Trail & Follow
#Trail = R:x()
#Follow = R:y()
if(D > R:x()){
Cen = (Own - Slip:setZ(0):normalized()*R:x()):setZ(Z)}
elseif(D < R:y()){
Cen = (Own - Slip:setZ(0):normalized()*R:y()):setZ(Z)} You can see I've divided the sections into OPERATION and TRICKS, this will tie in with the next lesson.
Firstly, both tricks need the owner's position at the same Z level as the bot, so we make a temporary variable (Own) to hold it and save us typing O
os():setZ(Cen:z()) each time we need it. We don't need delta for it or to record it, so it's temporary.
You may also notice that altitude control has been placed under TRICKS here, as it modifies the variable (Cen) used for the motion control, it does not alter the bot's actions directly and this is true of most tricks.
To keep Robby floating along behind his owner, we need to know how far apart he is from them, so we make D, the distance between them based on their vector locations. Robby's position is always made to be Cen by the motion controls, so it's a good shorthand for his position. We're also going to make a quick shorthand for Cen's Z altitude, Z, and Slip for the difference in vector position between them, their separation.
So now that we have our necessary variables, we need to see if Robby is too close or too far from his owner, by measuring D against the X and Y values of R, aka the Trail and Follow variables. They're two different numbers, so they get separate functions because the values used are different.
The code for this is slightly tricky, but it's another formula that shows up in E2 fairly often with set-distance devices. We take the owner's position, and subtract the equivalent distance in vector between them multiplied by how far away from the needed distance the bot is.
So: (Own - Slip:setZ(0):normalised()*Distance):setZ(Z)
Own = Owner's position
Slip = The difference in position between owner and Robby
:setZ(0) = Reduces the difference to a flat XY plane
:normalised() = Reduces the difference to a vector 1 Gmod unit long
*Distance = Makes the difference the needed distance, either R:x() or R:y()
:setZ(Z) = Swiftly factors in how high Robby thinks he should be at the moment
You don't need to know this kind of complex code to make drones, it's a coding trick.
Now on to the sitting down code: Code:
#Border no collide & floor dropping
if(D < R:z() & D > R:y()){
if(clk("alt_check") & Dist > Ground){Dist -= Ground/5}
if(Alt <= Dist){NoCol = 1}}
else{NoCol = 0,Dist = PrimeAlt} We still have our D variable from the distance management code, because the chip will run through this code segment afterwards before resetting (At which point, D would be lost), so we can use it here as well.
Here, as above, we are checking to see if the distance between Robby and his owner is greater than the minimum distance apart they should be, and less than the distance the owner wants him to sit down in (Unlock).
If these conditions are met, we then have a pair of if functions. The first triggers alongside the altitude management code, and if the Dist variable is greater than the Ground variable (The distance the ranger should read if Robby is sitting on the floor), it subtracts one fifth of the Ground variable from the Dist variable. This produces a smooth, slow, transition, especially when combined with the new timer of the altitude code, which corrects the altitude gradually, so that when Dist changes it doesn't shunt up or down.
The second if function checks if the altitude is correctly regulated according to the new Dist variable, if so it "unlocks" the border ring by activating it's Wire NoCollide, allowing objects to pass through it with ease. This border ring holds things inside when it isn't no collided, so now it can let them in and out.
If neither or only one of the initial comparisons is true, then the border remains solid, and the Dist is reset to the PrimeAlt value set at startup.
The result of these new code tricks is that when the owner approaches to within that distance range, Robby will slowly lower in place until the border ring sits on the ground, whilst the plate remains relative and stable. The border becomes no collided, so you can use it just like a basket. When you step too close, Robby slowly shifts back up and moves away to compensate, and moves towards you and back up when you move away again.
Brief Aside: Using E2 for construction
You may noticed whilst comparing the Robert from the first case study section and the Robby from the second that the chassis has changed slightly, with the plate more central to the border. This is because I used E2 and applyforce to precision place them.
First, I began with the raw materials: the border ring and the circular plate. I no collided them with eachother appropriately so that they could sit within eachother comfortably.

I then applied the following expression, which cause them both to hover at the map's [0,0,0] world co-ordinates and negate their angles, which fortunately stabilised them in the necessary positions.
Code:
@name PlateHover
@persist Plate:entity Vec:vector Ang:angle
if(first()){Plate = entity():isWeldedTo()}
runOnTick(1)
Vec = -Plate:massCenter()*1000
Plate:applyForce(Vec + $Vec*5)
Ang = -Plate:angles()*1000
Plate:applyAngForce(Ang + $Ang*5) 
Because I used the center of mass, they became central to one another, perfectly, and remained so when I welded them together in midair. This allowed me to achieve a level of accuracy that would be near impossible to achieve with regular physgun placement.
This once more isn't something unique for drone construction, but it's a handy tool if you need something to be precisely positioned relative to another.
Bookmarks