Lench Scripter Mod [Besiege v0.42]

Lench

Active Member
#81
Hi, THank your for your mod!!
I have a problem when I follow your guide " Getting started " . At the step with the variable v=1, the console said "MissingFieldException: Field '.AddPiece.isSimulating' not found".
What can I do?
This error happens when you use a mod not updated to the latest (v0.32) Besiege version. This probably has nothing to do with this mod.

To fix it, make sure all your mods are updated. Mine use an automatic update checker, so it must be some other mod.
 
#82
This error happens when you use a mod not updated to the latest (v0.32) Besiege version. This probably has nothing to do with this mod.

To fix it, make sure all your mods are updated. Mine use an automatic update checker, so it must be some other mod.
Ok, I will try , thank you ;)
 

Lench

Active Member
#84
This is Amazing mod! Whether will be on python 3? And why python 2 ?
Thanks!

Because the engine IronPython is pretty popular and works very well with .NET (and Mono). I love it's CLR reflection; if you're familiar with UnityEngine, you can actually just import UnityEngine and bypass my relatively limited API.

There is an IronPython3 engine on GitHub, which I have yet to try. If it works well, I might consider replacing it, but for now there's no reason to.
 
#85
I am trying to implement the PID controller for a quadcopter drone from Lenches tutorial. I never have implemented one, that one is new to me. I had some issues with identation of python, I dont use python very often. So the script runs without error messages in the console. The target altitude increases and decreases on the watchlist as I press either of two keys. However the drone seems to totally not do anything but fly without responding to the controls. The tuning of the PID, seems not to go far as it doesnt respond to changes of the pid gains. My script:

HOVER_SPEED=1.00

altitude_p_gain = 1
altitude_i_gain = 1
altitude_d_gain = 1

pitch_p_gain = 1
pitch_i_gain = 1
pitch_d_gain = 1

roll_p_gain = 1
roll_i_gain = 1
roll_d_gain = 1

last_altitude = 1
last_pitch = 1
last_roll = 1

target_altitude = 1
target_pitch = 1
target_roll = 1

altitude_i_error=1
pitch_i_error=1
roll_i_error=1


starting_block= Besiege.GetBlock("66316e3b-6874-4ef5-ab04-6aa8a9f13162")
rotor_lf=Besiege.GetBlock("0e21d12b-6a77-4123-a747-e2c6aa295395")
rotor_rf=Besiege.GetBlock("e97a7562-c728-4909-b640-d334a5a6300b")
rotor_rb=Besiege.GetBlock("d595b5ce-e6d8-4923-aba4-5f01ddc12079")
rotor_lb=Besiege.GetBlock("81dc677e-2ede-4a9e-b27a-e138d8141032")

def Update():
global target_altitude, target_pitch, target_roll, last_altitude, last_pitch, last_roll, altitude_i_error, pitch_i_error, roll_i_error
if target_altitude==None:
target_altitude = 0
if Input.GetKey(KeyCode.UpArrow):
target_pitch = 10
if Input.GetKey(KeyCode.DownArrow):
target_pitch = -10
if Input.GetKey(KeyCode.LeftArrow):
target_roll = 10
if Input.GetKey(KeyCode.RightArrow):
target_roll = -10
if Input.GetKey(KeyCode.I):
target_altitude = target_altitude + 5 * Time.deltaTime
if Input.GetKey(KeyCode.K):
target_altitude = target_altitude - 5 * Time.deltaTime
position = starting_block.Position
rotation = starting_block.EulerAngles
machine_altitude = position.y
vertical_velocity = (machine_altitude - last_altitude) / Time.deltaTime
machine_pitch = rotation.x
pitch_velocity = Mathf.DeltaAngle(machine_pitch, last_pitch) / Time.deltaTime
machine_roll = rotation.z
roll_velocity = Mathf.DeltaAngle(machine_roll, last_roll) / Time.deltaTime
last_altitude = machine_altitude
last_pitch = machine_pitch
last_roll = machine_roll
altitude_p_error = target_altitude - machine_altitude
altitude_i_error = altitude_i_error + altitude_p_error * Time.deltaTime
altitude_d_error = - vertical_velocity
altitude_adjustment = ( altitude_p_error * altitude_p_gain + altitude_i_error * altitude_i_gain + altitude_d_error * altitude_d_gain) / 20
pitch_p_error = target_pitch - machine_pitch
pitch_i_error = pitch_i_error + pitch_p_error * Time.deltaTime
pitch_d_error = - vertical_velocity
pitch_adjustment = ( pitch_p_error * pitch_p_gain + pitch_i_error * pitch_i_gain + pitch_d_error * pitch_d_gain) / 20
roll_p_error = target_roll - machine_roll
roll_i_error = roll_i_error + roll_p_error * Time.deltaTime
roll_d_error = - vertical_velocity
roll_adjustment = ( roll_p_error * roll_p_gain + roll_i_error * roll_i_gain + roll_d_error * roll_d_gain) / 20
speed_lf = HOVER_SPEED + HOVER_SPEED * (altitude_adjustment - pitch_adjustment - roll_adjustment)
speed_rf = HOVER_SPEED + HOVER_SPEED * (altitude_adjustment - pitch_adjustment + roll_adjustment)
speed_lb = HOVER_SPEED + HOVER_SPEED * (altitude_adjustment + pitch_adjustment - roll_adjustment)
speed_rb = HOVER_SPEED + HOVER_SPEED * (altitude_adjustment + pitch_adjustment + roll_adjustment)
rotor_lf.SetSliderValue("SPEED", speed_lf)
rotor_rf.SetSliderValue("SPEED", speed_rf)
rotor_lb.SetSliderValue("SPEED", speed_lb)
rotor_rb.SetSliderValue("SPEED", speed_rb)
 

Lench

Active Member
#86
I am trying to implement the PID controller for a quadcopter drone from Lenches tutorial. I never have implemented one, that one is new to me. I had some issues with identation of python, I dont use python very often. So the script runs without error messages in the console. The target altitude increases and decreases on the watchlist as I press either of two keys. However the drone seems to totally not do anything but fly without responding to the controls. The tuning of the PID, seems not to go far as it doesnt respond to changes of the pid gains.
You need to reset target_pitch and target_roll in Update() to zero. Instead of defining them globally, you should just set them to zero every time before checking for inputs, like this:

Code:
target_roll = 0
if Input.GetKey(KeyCode.LeftArrow):
    target_roll = 10
if Input.GetKey(KeyCode.RightArrow):
    target_roll = -10
As for the rest of the code, you are not setting the derivative component for roll and pitch controllers right (you're setting them both to vertical velocity instead of roll and pitch velocity). I suggest you try debugging your script with either print commands or through watchlist (Ctrl+I) to find your future errors, because it's gonna be a lot of those.

When you are more familiar with Python, you may want to take a look at this: http://pastebin.com/gNd6HFt8
 
#89
I am trying my script in Besiege, the first few time was working, however after few run, the console shows message
-----------------------------------------------------------------------------------------------------
NullReferenceException
Lench.Scripter.Mark.Clear (Boolean manual_call)
Lench.Scripter.Functions.ClearMarks (Boolean manual_call)
Lench.Scripter.Internal.Scripter.DestroyScriptingEnvironment ()
Lench.Scripter.Internal.Scripter.OnSimulationToggle (Boolean isSimulating)
spaar.ModLoader.Game.OnStopSimulate ()
UnityEngine.GameObject:SendMessage(String, SendMessageOptions)
AddPiece:SendSimulateMessage(Boolean)
$:MoveNext()
UnityEngine.MonoBehaviour:StartCoroutine_Auto(IEnumerator)
AddPiece:Update()
-----------------------------------------------------------------------------------------------------
And it has nothing to do with the script itself, a simple script with only one line of print function doesn't work

It works again for few times after I restart the whole game
 

Lench

Active Member
#90
I am trying my script in Besiege, the first few time was working, however after few run, the console shows message
-----------------------------------------------------------------------------------------------------
NullReferenceException
Lench.Scripter.Mark.Clear (Boolean manual_call)
Lench.Scripter.Functions.ClearMarks (Boolean manual_call)
Lench.Scripter.Internal.Scripter.DestroyScriptingEnvironment ()
Lench.Scripter.Internal.Scripter.OnSimulationToggle (Boolean isSimulating)
spaar.ModLoader.Game.OnStopSimulate ()
UnityEngine.GameObject:SendMessage(String, SendMessageOptions)
AddPiece:SendSimulateMessage(Boolean)
$:MoveNext()
UnityEngine.MonoBehaviour:StartCoroutine_Auto(IEnumerator)
AddPiece:Update()
-----------------------------------------------------------------------------------------------------
And it has nothing to do with the script itself, a simple script with only one line of print function doesn't work

It works again for few times after I restart the whole game
I think I know where the bug may be. Would you please try the attached patch and tell me if the issue persists?
 

Attachments

#91
I think I know where the bug may be. Would you please try the attached patch and tell me if the issue persists?
Good Afternoon

Sometimes the error persists after I clicked my mouse on the sky
----------------------------------------------------------------------------------here is my script
marked=False
m = Besiege.CreateMark(Besiege.GetRaycastHit())
def Update():
global marked
global m
if (Input.GetMouseButtonDown(0)):
if(marked==True):
m.Clear()
m.Move(Besiege.GetRaycastHit())
m.SetColor(Color(1, 0, 0))
marked=True
-------------------------------------------------------------------------------here is the error report
NullReferenceException
Lench.Scripter.Mark.Clear (Boolean manual_call)
Lench.Scripter.Functions.ClearMarks (Boolean manual_call)
Lench.Scripter.Internal.Scripter.DestroyScriptingEnvironment ()
Lench.Scripter.Internal.Scripter.OnSimulationToggle (Boolean isSimulating)
spaar.ModLoader.Game.OnStopSimulate ()
UnityEngine.GameObject:SendMessage(String, SendMessageOptions)
AddPiece:SendSimulateMessage(Boolean)
$:MoveNext()
UnityEngine.MonoBehaviour:StartCoroutine_Auto(IEnumerator)
AddPiece:Update()
------------------------------------------------------------------------------

//I also saw a collide box error persists for one times, but it doesn't come up later

------------------------------------------------------------------------------
BoxColliders does not support negative scale or size.
The effective box size has been forced positive and is likely to give unexpected collision geometry.
If you absolutely need to use negative scaling you can use the convex MeshCollider. Scene hierarchy path "HUD/AlignTopLeftNoFold/TimeScale/SetPercentage"
-------------------------------------------------------------------------------
 
Last edited:

Lench

Active Member
#92
Good Afternoon

Sometimes the error persists after I clicked my mouse on the sky
----------------------------------------------------------------------------------here is my script
marked=False
m = Besiege.CreateMark(Besiege.GetRaycastHit())
def Update():
global marked
global m
if (Input.GetMouseButtonDown(0)):
if(marked==True):
m.Clear()
m.Move(Besiege.GetRaycastHit())
m.SetColor(Color(1, 0, 0))
marked=True
-------------------------------------------------------------------------------here is the error report
NullReferenceException
Lench.Scripter.Mark.Clear (Boolean manual_call)
Lench.Scripter.Functions.ClearMarks (Boolean manual_call)
Lench.Scripter.Internal.Scripter.DestroyScriptingEnvironment ()
Lench.Scripter.Internal.Scripter.OnSimulationToggle (Boolean isSimulating)
spaar.ModLoader.Game.OnStopSimulate ()
UnityEngine.GameObject:SendMessage(String, SendMessageOptions)
AddPiece:SendSimulateMessage(Boolean)
$:MoveNext()
UnityEngine.MonoBehaviour:StartCoroutine_Auto(IEnumerator)
AddPiece:Update()
------------------------------------------------------------------------------

//I also saw a collide box error persists for one times, but it doesn't come up later

------------------------------------------------------------------------------
BoxColliders does not support negative scale or size.
The effective box size has been forced positive and is likely to give unexpected collision geometry.
If you absolutely need to use negative scaling you can use the convex MeshCollider. Scene hierarchy path "HUD/AlignTopLeftNoFold/TimeScale/SetPercentage"
-------------------------------------------------------------------------------
Oh, that explains it.

There's two issues with your code.
First, Besiege.GetRaycastHit() will not work and throw an error if you raycast into the sky. You can handle this by putting it in a try block, like this:
Code:
if Input.GetMouseButtonDown(0):
    try:
        hit = Besiege.GetRaycastHit()
        m.Move(hit)
    except:
        print "The raycast didn't hit any colliders."
Secondly, clearing the mark destroys it's game object. Calling methods of a destroyed object will throw an error. You are moving the mark after it has been destroyed.
If your goal is just to move the mark to a mouse click position, I suggest you do something like this:

Code:
m = Besiege.CreateMark(Vector3(0, -10, 0)) 
# to avoid handling creating the mark on first click, you can just initialize it somewhere underground and then move it
m.SetColor(Color(1, 0, 0))
# you only need to set the color once

def Update():
    if Input.GetMouseButtonDown(0):
        try:
            hit = Besiege.GetRaycastHit()
            m.Move(hit)
        except:
            print "The raycast didn't hit any colliders."
 
#93
Oh, that explains it.

There's two issues with your code.
First, Besiege.GetRaycastHit() will not work and throw an error if you raycast into the sky. You can handle this by putting it in a try block, like this:
Code:
if Input.GetMouseButtonDown(0):
    try:
        hit = Besiege.GetRaycastHit()
        m.Move(hit)
    except:
        print "The raycast didn't hit any colliders."
Secondly, clearing the mark destroys it's game object. Calling methods of a destroyed object will throw an error. You are moving the mark after it has been destroyed.
If your goal is just to move the mark to a mouse click position, I suggest you do something like this:

Code:
m = Besiege.CreateMark(Vector3(0, -10, 0))
# to avoid handling creating the mark on first click, you can just initialize it somewhere underground and then move it
m.SetColor(Color(1, 0, 0))
# you only need to set the color once

def Update():
    if Input.GetMouseButtonDown(0):
        try:
            hit = Besiege.GetRaycastHit()
            m.Move(hit)
        except:
            print "The raycast didn't hit any colliders."
Thanks :D
I was teached in a C++ course before. Know nothing about Python.
 
#94
Oh, that explains it.

There's two issues with your code.
First, Besiege.GetRaycastHit() will not work and throw an error if you raycast into the sky. You can handle this by putting it in a try block, like this:
Code:
if Input.GetMouseButtonDown(0):
    try:
        hit = Besiege.GetRaycastHit()
        m.Move(hit)
    except:
        print "The raycast didn't hit any colliders."
Secondly, clearing the mark destroys it's game object. Calling methods of a destroyed object will throw an error. You are moving the mark after it has been destroyed.
If your goal is just to move the mark to a mouse click position, I suggest you do something like this:

Code:
m = Besiege.CreateMark(Vector3(0, -10, 0))
# to avoid handling creating the mark on first click, you can just initialize it somewhere underground and then move it
m.SetColor(Color(1, 0, 0))
# you only need to set the color once

def Update():
    if Input.GetMouseButtonDown(0):
        try:
            hit = Besiege.GetRaycastHit()
            m.Move(hit)
        except:
            print "The raycast didn't hit any colliders."
.
Also, is there some way I can draw shapes in Besiege? Other than the sphere that mark function provide.
I am working on building a RA2 liked drone that can control by the way point that set by mouse

Something like this, and attack given point that set by mouse clicking
If I can draw lines and other shape in Besiege, then visualizeing waypoint become possible.
//And easier for adding a laser sight to the cannon ╮( ̄▽ ̄")╭
 

Lench

Active Member
#95
.
Also, is there some way I can draw shapes in Besiege? Other than the sphere that mark function provide.
I am working on building a RA2 liked drone that can control by the way point that set by mouse

Something like this, and attack given point that set by mouse clicking
If I can draw lines and other shape in Besiege, then visualizeing waypoint become possible.
//And easier for adding a laser sight to the cannon ╮( ̄▽ ̄")╭
There's no API for drawing lines in the mod. It is possible to draw lines using UnityEngine.LineRenderer, but unlike with marks you would have to delete them yourself when simulation stops and you would probably have a few problems with Python reflection (UnityEngine wasn't meant to be called through Python).

I'll maybe put together an update and enable you to draw lines. Until then I suggest you find some other way.
 
#96
Hello, thanks for this incredible mod!
I have some problems, I follow your guide and at this step the consol said : "
File "C:/Program Files (x86)/Steam/steamapps/common/Besiege/Besiege_Data/Scripts/a.py", line 7, in Update
UnboundLocalError: Local variable 'v' referenced before assignment."

Here is my code:
left_wheel = Besiege.GetBlock("69aabdaf-bb78-4e23-9ae7-9a8cebe6e151")
right_wheel = Besiege.GetBlock("2deff94d-e9e7-4a30-8f68-b4346364c859")
v = 1

def Update():
if Input.GetKey(KeyCode.I):
v += 4
if Input.GetKey(KeyCode.K):
v -= 4

left_wheel.SetSliderValue("SPEED", v)
right_wheel.SetSliderValue("SPEED", v)

Can you help me?
 

Lench

Active Member
#97
Hello, thanks for this incredible mod!
I have some problems, I follow your guide and at this step the consol said : "
File "C:/Program Files (x86)/Steam/steamapps/common/Besiege/Besiege_Data/Scripts/a.py", line 7, in Update
UnboundLocalError: Local variable 'v' referenced before assignment."

Here is my code:
left_wheel = Besiege.GetBlock("69aabdaf-bb78-4e23-9ae7-9a8cebe6e151")
right_wheel = Besiege.GetBlock("2deff94d-e9e7-4a30-8f68-b4346364c859")
v = 1

def Update():
if Input.GetKey(KeyCode.I):
v += 4
if Input.GetKey(KeyCode.K):
v -= 4

left_wheel.SetSliderValue("SPEED", v)
right_wheel.SetSliderValue("SPEED", v)

Can you help me?
Just add `global v` at the start of the Update function, like this:
Code:
def Update():
    global v
    if Input.GetKey(KeyCode.I):
        v += 4
    if Input.GetKey(KeyCode.K):
        v -= 4
Functions in Python have their own private scopes. When `v += 4` executes, it looks for a variable `v` in the function's local scope, where it doesn't exist. By including `global v`, you tell it to look for `v` in global scope.
 
#98
Just add `global v` at the start of the Update function, like this:
Code:
def Update():
    global v
    if Input.GetKey(KeyCode.I):
        v += 4
    if Input.GetKey(KeyCode.K):
        v -= 4
Functions in Python have their own private scopes. When `v += 4` executes, it looks for a variable `v` in the function's local scope, where it doesn't exist. By including `global v`, you tell it to look for `v` in global scope.
Thank you, it fix the problem, console didn't say anything but it apparently nothing happen.
Any idea?
 

Lench

Active Member
#99
Thank you, it fix the problem, console didn't say anything but it apparently nothing happen.
Any idea?
I can't tell from your post as it lacks indentation, but make sure that the `SetSliderValue` calls are inside the `Update` function. That way they get updated after key presses, instead of just set once at the start.
 
Last edited:
Top