Skip to content

Commit 0cbea23

Browse files
committed
Adding easy aerials, and extracting a steering function.
1 parent f94c51b commit 0cbea23

File tree

6 files changed

+128
-52
lines changed

6 files changed

+128
-52
lines changed

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,4 +108,6 @@ ENV/
108108
/build
109109

110110
# Gradle files
111-
/.gradle
111+
/.gradle
112+
113+
.vscode/

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
# You will automatically get updates for all versions starting with "1.".
33
rlbot==1.*
44
rlbottraining
5+
RLUtilities
56

67
# This will cause pip to auto-upgrade and stop scaring people with warning messages
78
pip

run.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
logger.log(logging_utils.logging_level,
1414
'Skipping upgrade check for now since it looks like you have no internet')
1515
elif public_utils.is_safe_to_upgrade():
16-
subprocess.call([sys.executable, "-m", "pip", "install", '-r', 'requirements.txt', '--upgrade', '--upgrade-strategy=eager'])
16+
subprocess.call([sys.executable, "-m", "pip", "install", '-r', 'requirements.txt'])
17+
subprocess.call([sys.executable, "-m", "pip", "install", 'rlbot', '--upgrade'])
1718

1819
# https://stackoverflow.com/a/44401013
1920
rlbots = [module for module in sys.modules if module.startswith('rlbot')]

src/bot.py

Lines changed: 19 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
1-
import math
21
from typing import List
32

43
from rlbot.agents.base_agent import BaseAgent, SimpleControllerState
54
from rlbot.utils.structures.game_data_struct import GameTickPacket
65
from rlbot.utils.structures.quick_chats import QuickChats
76

7+
from util.aerial import AerialStep, LineUpForAerialStep
8+
from util.drive import steer_toward_target
89
from util.goal_detector import find_future_goal
9-
from util.orientation import Orientation
1010
from util.sequence import Sequence, ControlStep
1111
from util.spikes import SpikeWatcher
1212
from util.vec import Vec3
1313

14+
1415
# Would you like to use numpy utilities? Check out the np_util folder!
1516

1617
class MyBot(BaseAgent):
@@ -27,6 +28,8 @@ def get_output(self, packet: GameTickPacket) -> SimpleControllerState:
2728
see the motion of the ball, etc. and return controls to drive your car.
2829
"""
2930

31+
# This is good to keep at the beginning of get_output. It will allow you to continue
32+
# any sequences that you may have started during a previous call to get_output.
3033
if self.active_sequence and not self.active_sequence.done:
3134
return self.active_sequence.tick(packet)
3235

@@ -39,9 +42,7 @@ def get_output(self, packet: GameTickPacket) -> SimpleControllerState:
3942
if predicted_goal:
4043
goal_text = f"Goal in {predicted_goal.time - packet.game_info.seconds_elapsed:.2f}s"
4144

42-
ball_location = Vec3(packet.game_ball.physics.location)
4345
my_car = packet.game_cars[self.index]
44-
car_location = Vec3(my_car.physics.location)
4546
car_velocity = Vec3(my_car.physics.velocity)
4647

4748
# Example of using a sequence
@@ -57,64 +58,32 @@ def get_output(self, packet: GameTickPacket) -> SimpleControllerState:
5758

5859
# Example of using the spike watcher.
5960
# This will make the bot say I got it! when it spikes the ball,
60-
# then release it 3 seconds later.
61+
# then release it 2 seconds later.
6162
if self.spike_watcher.carrying_car == my_car:
6263
if self.spike_watcher.carry_duration == 0:
6364
self.send_quick_chat(QuickChats.CHAT_EVERYONE, QuickChats.Information_IGotIt)
64-
elif self.spike_watcher.carry_duration > 3:
65+
elif self.spike_watcher.carry_duration > 2:
6566
return SimpleControllerState(use_item=True)
6667

67-
# The rest of this code just ball chases.
68-
# Find the direction of your car using the Orientation class
69-
car_orientation = Orientation(my_car.physics.rotation)
70-
car_direction = car_orientation.forward
71-
72-
target = ball_location
73-
car_to_target = target - car_location
74-
steer_correction_radians = find_correction(car_direction, car_to_target)
68+
# Example of doing an aerial. This will cause the car to jump and fly toward the
69+
# ceiling in the middle of the field.
70+
if my_car.boost > 50 and my_car.has_wheel_contact:
71+
self.start_aerial(Vec3(0, 0, 2000), packet.game_info.seconds_elapsed + 4)
7572

73+
# If nothing else interesting happened, just chase the ball!
74+
ball_location = Vec3(packet.game_ball.physics.location)
75+
self.controller_state.steer = steer_toward_target(my_car, ball_location)
7676
self.controller_state.throttle = 1.0
7777

78-
# Change the multiplier to influence the sharpness of steering. You'll wiggle if it's too high.
79-
self.controller_state.steer = limit_to_safe_range(-steer_correction_radians * 5)
80-
78+
# Draw some text on the screen
8179
draw_debug(self.renderer, [goal_text])
8280

8381
return self.controller_state
8482

85-
86-
def limit_to_safe_range(value: float) -> float:
87-
"""
88-
Controls like throttle, steer, pitch, yaw, and roll need to be in the range of -1 to 1.
89-
This will ensure your number is in that range. Something like 0.45 will stay as it is,
90-
but a value of -5.6 would be changed to -1.
91-
"""
92-
if value < -1:
93-
return -1
94-
if value > 1:
95-
return 1
96-
return value
97-
98-
99-
def find_correction(current: Vec3, ideal: Vec3) -> float:
100-
"""
101-
Finds the angle from current to ideal vector in the xy-plane. Angle will be between -pi and +pi.
102-
"""
103-
104-
# The in-game axes are left handed, so use -x
105-
current_in_radians = math.atan2(current.y, -current.x)
106-
ideal_in_radians = math.atan2(ideal.y, -ideal.x)
107-
108-
diff = ideal_in_radians - current_in_radians
109-
110-
# Make sure that diff is between -pi and +pi.
111-
if abs(diff) > math.pi:
112-
if diff < 0:
113-
diff += 2 * math.pi
114-
else:
115-
diff -= 2 * math.pi
116-
117-
return diff
83+
def start_aerial(self, target: Vec3, arrival_time: float):
84+
self.active_sequence = Sequence([
85+
LineUpForAerialStep(target, arrival_time, self.index),
86+
AerialStep(target, arrival_time, self.index)])
11887

11988

12089
def draw_debug(renderer, text_lines: List[str]):

src/util/aerial.py

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
from RLUtilities.GameInfo import GameInfo
2+
from RLUtilities.LinearAlgebra import vec3
3+
from RLUtilities.Maneuvers import Aerial
4+
from rlbot.agents.base_agent import SimpleControllerState
5+
from rlbot.utils.structures.game_data_struct import GameTickPacket
6+
7+
from util.drive import steer_toward_target
8+
from util.sequence import Step, StepResult
9+
from util.vec import Vec3
10+
11+
12+
MAX_SPEED_WITHOUT_BOOST = 1410
13+
SECONDS_PER_TICK = 0.008 # Assume a 120Hz game. It's OK if we're wrong, aerial will still go OK
14+
15+
16+
class LineUpForAerialStep(Step):
17+
"""
18+
This will cause the car to steer toward the target until it is lined up enough and going at
19+
an appropriate speed for a successful aerial.
20+
"""
21+
def __init__(self, target: Vec3, arrival_time: float, index: int):
22+
self.target = target
23+
self.arrival_time = arrival_time
24+
self.index = index
25+
26+
def tick(self, packet: GameTickPacket) -> StepResult:
27+
car = packet.game_cars[self.index]
28+
29+
seconds_till_arrival = self.arrival_time - packet.game_info.seconds_elapsed
30+
if seconds_till_arrival <= 0:
31+
return StepResult(SimpleControllerState(), done=True)
32+
current_speed = Vec3(car.physics.velocity).length()
33+
avg_speed_needed = Vec3(car.physics.location).flat().dist(self.target.flat()) / seconds_till_arrival
34+
35+
steering = steer_toward_target(car, self.target)
36+
controls = SimpleControllerState(
37+
steer=steering,
38+
throttle=1 if avg_speed_needed > current_speed else 0,
39+
boost=avg_speed_needed > current_speed and avg_speed_needed > MAX_SPEED_WITHOUT_BOOST)
40+
41+
ready_to_jump = abs(steering) < 0.1 and current_speed / avg_speed_needed > 0.7
42+
43+
return StepResult(controls, done=ready_to_jump)
44+
45+
46+
class AerialStep(Step):
47+
"""
48+
This uses the Aerial controller provided by RLUtilities. Thanks chip!
49+
It will take care of jumping off the ground and flying toward the target.
50+
This will only work properly if you call tick repeatedly on the same instance.
51+
"""
52+
def __init__(self, target: Vec3, arrival_time: float, index: int):
53+
self.index = index
54+
self.aerial: Aerial = None
55+
self.game_info: GameInfo = None
56+
self.target = target
57+
self.arrival_time = arrival_time
58+
59+
def tick(self, packet: GameTickPacket) -> StepResult:
60+
61+
if self.game_info is None:
62+
self.game_info = GameInfo(self.index, packet.game_cars[self.index].team)
63+
self.game_info.read_packet(packet)
64+
65+
if self.aerial is None:
66+
self.aerial = Aerial(self.game_info.my_car, vec3(self.target.x, self.target.y, self.target.z),
67+
self.arrival_time)
68+
69+
self.aerial.step(SECONDS_PER_TICK)
70+
controls = SimpleControllerState()
71+
controls.boost = self.aerial.controls.boost
72+
controls.pitch = self.aerial.controls.pitch
73+
controls.yaw = self.aerial.controls.yaw
74+
controls.roll = self.aerial.controls.roll
75+
controls.jump = self.aerial.controls.jump
76+
77+
return StepResult(controls, packet.game_info.seconds_elapsed > self.arrival_time)

src/util/drive.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import math
2+
3+
from rlbot.utils.structures.game_data_struct import PlayerInfo
4+
5+
from util.orientation import Orientation, relative_location
6+
from util.vec import Vec3
7+
8+
9+
def limit_to_safe_range(value: float) -> float:
10+
"""
11+
Controls like throttle, steer, pitch, yaw, and roll need to be in the range of -1 to 1.
12+
This will ensure your number is in that range. Something like 0.45 will stay as it is,
13+
but a value of -5.6 would be changed to -1.
14+
"""
15+
if value < -1:
16+
return -1
17+
if value > 1:
18+
return 1
19+
return value
20+
21+
22+
23+
def steer_toward_target(car: PlayerInfo, target: Vec3) -> float:
24+
relative = relative_location(Vec3(car.physics.location), Orientation(car.physics.rotation), target)
25+
angle = math.atan2(relative.y, relative.x)
26+
return limit_to_safe_range(angle * 5)

0 commit comments

Comments
 (0)