Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- # PID TUNING
- TURRET_HEADING_PID = {"p": 0.5, "i": 0, "d": 1, "max": 90}
- TURRET_ELEVATION_PID = {"p": 1.3, "i": 0, "d": 0.5, "max": 25}
- MISSILE_VELOCITY_PID = {"p": 1, "i": 1, "d": 1, "max": 300}
- MISSILE_PITCH_PID = {"p": 1, "i": 0.5, "d": 1, "max": 45}
- MISSILE_HEADING_PID = {"p": 1, "i": 0, "d": 1, "max": 45}
- PITCH_GAIN = 1.4
- HEADING_GAIN = 1.0
- import math
- # ANGULAR PID CONTROLLER CLASS DEFINITION
- class AngularController:
- def __init__(self, p, i, d, max):
- sum = float(p + i + d)
- self.p = p / sum
- self.i = i / sum
- self.d = d / sum
- self.err = float(0)
- self.int = float(0)
- self.der = float(0)
- self.max = float(max)
- self.first_call = True
- self.on_target = False
- self.last_time = None
- self.last_current = None
- def control(self, current, target):
- time = Besiege.GetTime()
- output = 0
- if self.first_call:
- self.first_call = False
- else:
- self.delta_time = (time - self.last_time)
- self.err = clamp(Mathf.DeltaAngle(current, target), self.p * self.max * -1, self.p * self.max)
- self.int = clamp(self.int + self.err * self.delta_time, self.i * self.max * -1, self.i * self.max)
- self.der = Mathf.DeltaAngle(current, self.last_current) * self.delta_time
- output = (self.err * self.p) + (self.int * self.i) + (self.der * self.d)
- self.on_target = abs(self.err / self.max) < 0.05 and abs(self.der / self.max) < 0.05
- self.last_current = current
- self.last_time = time
- output = clamp(output, self.max * -1, self.max) / self.max
- return output
- # LINEAR PID CONTROLLER CLASS DEFINITION
- class LinearController:
- def __init__(self, p, i, d, max):
- sum = float(p + i + d)
- self.p = p / sum
- self.i = i / sum
- self.d = d / sum
- self.err = float(0)
- self.int = float(0)
- self.der = float(0)
- self.max = float(max)
- self.first_call = True
- self.on_target = False
- self.last_time = None
- self.last_current = None
- def control(self, current, target):
- time = Besiege.GetTime()
- output = 0
- if self.first_call:
- self.first_call = False
- else:
- self.delta_time = (time - self.last_time)
- self.err = clamp(current - target, self.p * self.max * -1, self.p * self.max)
- self.int = clamp(self.int + self.err * self.delta_time, self.i * self.max * -1, self.i * self.max)
- self.der = (current - self.last_current) * self.delta_time
- output = (self.err * self.p) + (self.int * self.i) + (self.der * self.d)
- self.on_target = abs(self.err / self.max) < 0.05 and abs(self.der / self.max) < 0.05
- self.last_current = current
- self.last_time = time
- output = clamp(output, self.max * -1, self.max) / self.max
- return output
- # MISSILE CLASS DEFINITION
- class Missile:
- def __init__(self, left_motor, right_motor, top_motor, bottom_motor, decoupler_front, decoupler_rear, bomb, arm):
- self.left_motor = left_motor
- self.right_motor = right_motor
- self.top_motor = top_motor
- self.bottom_motor = bottom_motor
- self.decoupler_front = decoupler_front
- self.decoupler_rear = decoupler_rear
- self.bomb = bomb
- self.arm = arm
- self.destroyed = False
- self.launched = False
- self.velocity_controller = LinearController(MISSILE_VELOCITY_PID["p"], MISSILE_VELOCITY_PID["i"],
- MISSILE_VELOCITY_PID["d"], MISSILE_VELOCITY_PID["max"])
- self.pitch_controller = AngularController(MISSILE_PITCH_PID["p"], MISSILE_PITCH_PID["i"],
- MISSILE_PITCH_PID["d"], MISSILE_PITCH_PID["max"])
- self.heading_controller = AngularController(MISSILE_HEADING_PID["p"], MISSILE_HEADING_PID["i"],
- MISSILE_HEADING_PID["d"], MISSILE_HEADING_PID["max"])
- def launch(self, target):
- if self.destroyed:
- return
- self.target = target
- self.last_target_pos = target.Position
- self.launched = True
- self.launch_time = Besiege.GetTime()
- self.left_motor.SetSliderValue("FLIGHT DURATION", 10)
- self.right_motor.SetSliderValue("FLIGHT DURATION", 10)
- self.top_motor.SetSliderValue("FLIGHT DURATION", 10)
- self.bottom_motor.SetSliderValue("FLIGHT DURATION", 10)
- self.left_motor.SetSliderValue("THRUST", LAUNCH_THRUST)
- self.right_motor.SetSliderValue("THRUST", LAUNCH_THRUST)
- self.top_motor.SetSliderValue("THRUST", LAUNCH_THRUST)
- self.bottom_motor.SetSliderValue("THRUST", LAUNCH_THRUST * 1.5)
- self.arm.SetSliderValue("ROTATION SPEED", 3)
- self.arm.SetAngle(80)
- self.decoupler_front.Action("EXPLODE")
- self.decoupler_rear.Action("EXPLODE")
- self.left_motor.Action("LAUNCH")
- self.right_motor.Action("LAUNCH")
- self.top_motor.Action("LAUNCH")
- self.bottom_motor.Action("LAUNCH")
- def guide(self):
- if not self.bomb.Exists:
- self.destroyed = True
- if self.destroyed or not self.launched or Time.deltaTime == 0:
- return
- position = self.left_motor.Position
- rotation = self.left_motor.EulerAngles
- velocity = self.left_motor.Velocity
- horizontal_velocity = Vector3(velocity.x, 0, velocity.z)
- target_pos = self.target.Position
- target_velocity = (target_pos - self.last_target_pos) / Time.deltaTime
- target_relative = target_pos - position
- tt_target = target_relative.magnitude / Vector3.Project(velocity, target_relative.normalized).magnitude
- self.last_target_pos = target_pos
- predict_pos = target_pos + target_velocity * tt_target
- predict_relative = predict_pos - position
- tt_predict = predict_relative.magnitude / Vector3.Project(velocity, predict_relative.normalized).magnitude
- intercept_pos = predict_pos
- intercept_relative = intercept_pos - position
- velocity_pitch = math.asin(velocity.normalized.y) * Mathf.Rad2Deg
- velocity_heading = math.atan2(horizontal_velocity.normalized.z,
- horizontal_velocity.normalized.x) * Mathf.Rad2Deg
- x = Vector3(intercept_relative.x, 0, intercept_relative.z).magnitude
- y = - intercept_relative.y
- v = velocity.magnitude
- g = 8
- if v < 120 or v * v * v * v - g * (g * x * x - 2 * y * v * v) < 0:
- return
- target_pitch = math.atan(
- (v * v - math.sqrt(v * v * v * v - g * (g * x * x - 2 * y * v * v))) / (g * x)) * Mathf.Rad2Deg
- target_heading = math.atan2(intercept_relative.z, intercept_relative.x) * Mathf.Rad2Deg
- missile_pitch = velocity_pitch
- missile_heading = velocity_heading
- velocity_adjustment = self.velocity_controller.control(velocity.magnitude, MISSILE_VELOCITY)
- heading_adjustment = self.heading_controller.control(missile_heading, target_heading)
- pitch_adjustment = self.pitch_controller.control(missile_pitch, target_pitch)
- adjustment_magnitude = math.sqrt(heading_adjustment * heading_adjustment + pitch_adjustment * pitch_adjustment)
- if adjustment_magnitude > math.sqrt(2):
- k = math.sqrt(2) / adjustment_magnitude
- heading_adjustment *= k
- pitch_adjustment *= k
- heading_adjustment = heading_adjustment * HEADING_GAIN * math.sqrt(velocity.magnitude)
- pitch_adjustment = pitch_adjustment * PITCH_GAIN * math.sqrt(velocity.magnitude)
- base_thrust = MISSILE_THRUST
- thrust = base_thrust - base_thrust * velocity_adjustment
- missile_roll_sin = math.sin(rotation.x * Mathf.Deg2Rad)
- missile_roll_cos = math.cos(rotation.x * Mathf.Deg2Rad)
- left_thrust = base_thrust * (- heading_adjustment * missile_roll_cos + pitch_adjustment * missile_roll_sin)
- right_thrust = base_thrust * (heading_adjustment * missile_roll_cos - pitch_adjustment * missile_roll_sin)
- top_thrust = thrust + base_thrust * (
- heading_adjustment * missile_roll_sin - pitch_adjustment * missile_roll_cos)
- bottom_thrust = thrust + base_thrust * (
- - heading_adjustment * missile_roll_sin + pitch_adjustment * missile_roll_cos)
- self.left_motor.SetSliderValue("THRUST", left_thrust)
- self.right_motor.SetSliderValue("THRUST", right_thrust)
- self.top_motor.SetSliderValue("THRUST", top_thrust)
- self.bottom_motor.SetSliderValue("THRUST", bottom_thrust)
- if intercept_relative.magnitude < 10:
- self.destroy()
- def destroy(self):
- self.left_motor.SetSliderValue("FLIGHT DURATION", 0)
- self.right_motor.SetSliderValue("FLIGHT DURATION", 0)
- self.top_motor.SetSliderValue("FLIGHT DURATION", 0)
- self.bottom_motor.SetSliderValue("FLIGHT DURATION", 0)
- self.destroyed = True
- # CONSTANTS
- LAUNCH_THRUST = 20
- MISSILE_THRUST = 3
- MISSILE_VELOCITY = 200
- # BLOCK HANDLERS
- turret_heading_spinning = Besiege.GetBlock("87471ace-3f6d-4e9b-95a6-3cf717a62ee4")
- turret_elevation_left_steering = Besiege.GetBlock("eecc1be4-ab43-4f5f-b96e-8826ef78cdbf")
- turret_elevation_right_steering = Besiege.GetBlock("ee2cb232-70f2-457d-aa8d-fda7a18b5711")
- missiles = [
- Missile(
- Besiege.GetBlock("ea8cb52b-eb83-49ec-9791-a81f286af316"), # left motor
- Besiege.GetBlock("1cf03895-77ae-4584-bb55-086a5e39424f"), # right motor
- Besiege.GetBlock("af9885b5-94e7-4850-89cd-88f53d40980d"), # top motor
- Besiege.GetBlock("0fab5b20-ede2-45f5-9690-7bd2abd8cdad"), # bottom motor
- Besiege.GetBlock("2a17b5ce-ca20-4d4d-9b19-7b1a48496e5c"), # front decoupler
- Besiege.GetBlock("e00b947f-6fad-4874-aa5c-b19eab8597f6"), # rear decoupler
- Besiege.GetBlock("893a8bc8-1a0f-4817-97f5-b3d06bc496ab"), # bomb
- Besiege.GetBlock("3234278a-6ba7-4818-bc10-171cd7300071")), # arm
- Missile(
- Besiege.GetBlock("11a96476-880a-4887-afdf-1452663a18cf"),
- Besiege.GetBlock("e4864062-f4af-46f4-8b81-1efaab781cc0"),
- Besiege.GetBlock("324ccdb6-8270-4966-845b-4a346dc21e64"),
- Besiege.GetBlock("d8667611-96e2-432c-9655-c9cd575b51f0"),
- Besiege.GetBlock("351e66b5-3b7c-4e7d-b87b-9f76727bdcdd"),
- Besiege.GetBlock("d854aa4b-de55-4898-b7d5-ac7f6c0ce7c0"),
- Besiege.GetBlock("dbed74ee-f178-48b9-8b47-3d23e469f354"),
- Besiege.GetBlock("10985473-fd0e-41f0-85d9-91534120d29e")),
- Missile(
- Besiege.GetBlock("d5a4a8c8-1017-4f4d-a4c3-082f7541aadc"),
- Besiege.GetBlock("8bfbdf2c-95a6-47ac-b731-1148ee5522f4"),
- Besiege.GetBlock("2486c33c-8e83-403f-a398-d7f7feb18fd7"),
- Besiege.GetBlock("ba29d0e9-f635-4bb2-a3ab-a5b460b2aabb"),
- Besiege.GetBlock("e40ed68c-5a90-4250-a297-ce415801cfef"),
- Besiege.GetBlock("24aab8ca-1d61-4c2e-b735-50ca1cff6d03"),
- Besiege.GetBlock("ac8b8775-1232-449a-b482-426b6cfebe1c"),
- Besiege.GetBlock("18ea0012-499f-4c4c-abae-a246f3ea01f8")),
- Missile(
- Besiege.GetBlock("15f1ef0a-4f05-4e6a-8524-26075b2ce851"),
- Besiege.GetBlock("1b263b73-a84f-4e03-80d9-c2c8e63f051d"),
- Besiege.GetBlock("080e9a89-08be-462e-bca8-d90f3523f1f0"),
- Besiege.GetBlock("fba21339-047c-49af-8bfe-8057c990e0bd"),
- Besiege.GetBlock("58cbc2b1-bb4e-40ab-bca6-68b42fd9a2c9"),
- Besiege.GetBlock("b4ad1d9c-0723-4efc-8699-4d9e403528a5"),
- Besiege.GetBlock("60aa4136-94c9-4821-af1c-02a386591c4b"),
- Besiege.GetBlock("0898bc2e-760b-4001-9a02-3d40bc8c8e4a"))
- ]
- # CONTROLLER INITIALISATION
- heading_controller = AngularController(
- TURRET_HEADING_PID["p"],
- TURRET_HEADING_PID["i"],
- TURRET_HEADING_PID["d"],
- TURRET_HEADING_PID["max"])
- elevation_controller = AngularController(
- TURRET_ELEVATION_PID["p"],
- TURRET_ELEVATION_PID["i"],
- TURRET_ELEVATION_PID["d"],
- TURRET_ELEVATION_PID["max"])
- # GLOBAL VARIABLES
- target_tracking = None
- target_set = False
- remaining_missiles = 4
- turret_elevation_left_steering.ClearKeys("LEFT")
- turret_elevation_left_steering.ClearKeys("RIGHT")
- turret_elevation_right_steering.ClearKeys("LEFT")
- turret_elevation_right_steering.ClearKeys("RIGHT")
- def Update():
- global target_set
- global target_tracking
- global remaining_missiles
- global missiles
- for m in missiles:
- m.guide()
- if target_set and remaining_missiles > 0:
- next_missile = missiles[4 - remaining_missiles]
- missile_position = next_missile.bomb.Position
- missile_pitch = 360 - next_missile.bomb.EulerAngles.x
- missile_heading = - next_missile.bomb.EulerAngles.y + 90
- target = target_tracking.Position
- target_relative_vector = target - missile_position
- target_pitch = math.asin(target_relative_vector.normalized.y) * Mathf.Rad2Deg
- target_heading = math.atan2(target_relative_vector.z, target_relative_vector.x) * Mathf.Rad2Deg
- heading_adjustment = heading_controller.control(missile_heading, target_heading)
- elevation_adjustment = elevation_controller.control(missile_pitch, target_pitch)
- turret_heading_spinning.SetSliderValue("SPEED", heading_adjustment)
- turret_elevation_left_steering.SetInput(elevation_adjustment)
- turret_elevation_right_steering.SetInput(elevation_adjustment)
- if Input.GetKeyDown(KeyCode.O):
- try:
- target_tracking = Besiege.GetRaycastCollider()
- target_set = True
- except:
- pass
- if Input.GetKeyDown(KeyCode.L):
- missiles[4 - remaining_missiles].launch(target_tracking)
- remaining_missiles -= 1
- def clamp(value, minvalue, maxvalue):
- return max(min(value, maxvalue), minvalue)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement