Skip to content

Commit 5777fca

Browse files
NicEastvillagetarehart
authored andcommitted
Added Vec3 with essential functionality (#25)
1 parent b65d3f6 commit 5777fca

3 files changed

Lines changed: 170 additions & 35 deletions

File tree

python_example/python_example.py

Lines changed: 25 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,29 @@
33
from rlbot.agents.base_agent import BaseAgent, SimpleControllerState
44
from rlbot.utils.structures.game_data_struct import GameTickPacket
55

6+
from util.orientation import Orientation
7+
from util.vec import Vec3
8+
69

710
class PythonExample(BaseAgent):
811

912
def initialize_agent(self):
10-
#This runs once before the bot starts up
13+
# This runs once before the bot starts up
1114
self.controller_state = SimpleControllerState()
1215

1316
def get_output(self, packet: GameTickPacket) -> SimpleControllerState:
14-
ball_location = Vector2(packet.game_ball.physics.location.x, packet.game_ball.physics.location.y)
17+
ball_location = Vec3(packet.game_ball.physics.location)
1518

1619
my_car = packet.game_cars[self.index]
17-
car_location = Vector2(my_car.physics.location.x, my_car.physics.location.y)
18-
car_direction = get_car_facing_vector(my_car)
20+
car_location = Vec3(my_car.physics.location)
21+
1922
car_to_ball = ball_location - car_location
2023

21-
steer_correction_radians = car_direction.correction_to(car_to_ball)
24+
# Find the direction of our car using the Orientation class
25+
car_orientation = Orientation(my_car.physics.rotation)
26+
car_direction = car_orientation.forward
27+
28+
steer_correction_radians = find_correction(car_direction, car_to_ball)
2229

2330
if steer_correction_radians > 0:
2431
# Positive radians in the unit circle is a turn to the left.
@@ -35,42 +42,25 @@ def get_output(self, packet: GameTickPacket) -> SimpleControllerState:
3542

3643
return self.controller_state
3744

38-
class Vector2:
39-
def __init__(self, x=0, y=0):
40-
self.x = float(x)
41-
self.y = float(y)
42-
43-
def __add__(self, val):
44-
return Vector2(self.x + val.x, self.y + val.y)
4545

46-
def __sub__(self, val):
47-
return Vector2(self.x - val.x, self.y - val.y)
46+
def find_correction(current: Vec3, ideal: Vec3) -> float:
47+
# Finds the angle from current to ideal vector in the xy-plane. Angle will be between -pi and +pi.
4848

49-
def correction_to(self, ideal):
50-
# The in-game axes are left handed, so use -x
51-
current_in_radians = math.atan2(self.y, -self.x)
52-
ideal_in_radians = math.atan2(ideal.y, -ideal.x)
49+
# The in-game axes are left handed, so use -x
50+
current_in_radians = math.atan2(current.y, -current.x)
51+
ideal_in_radians = math.atan2(ideal.y, -ideal.x)
5352

54-
correction = ideal_in_radians - current_in_radians
53+
diff = ideal_in_radians - current_in_radians
5554

56-
# Make sure we go the 'short way'
57-
if abs(correction) > math.pi:
58-
if correction < 0:
59-
correction += 2 * math.pi
60-
else:
61-
correction -= 2 * math.pi
62-
63-
return correction
64-
65-
66-
def get_car_facing_vector(car):
67-
pitch = float(car.physics.rotation.pitch)
68-
yaw = float(car.physics.rotation.yaw)
55+
# Make sure that diff is between -pi and +pi.
56+
if abs(diff) > math.pi:
57+
if diff < 0:
58+
diff += 2 * math.pi
59+
else:
60+
diff -= 2 * math.pi
6961

70-
facing_x = math.cos(pitch) * math.cos(yaw)
71-
facing_y = math.cos(pitch) * math.sin(yaw)
62+
return diff
7263

73-
return Vector2(facing_x, facing_y)
7464

7565
def draw_debug(renderer, car, ball, action_display):
7666
renderer.begin_rendering()

python_example/util/orientation.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import math
2+
3+
from util.vec import Vec3
4+
5+
6+
# This is a helper class for calculating directions relative to your car. You can extend it or delete if you want.
7+
class Orientation:
8+
"""
9+
This class describes the orientation of an object from the rotation of the object.
10+
Use this to find the direction of cars: forward, right, up.
11+
It can also be used to find relative locations.
12+
"""
13+
14+
def __init__(self, rotation):
15+
self.yaw = float(rotation.yaw)
16+
self.roll = float(rotation.roll)
17+
self.pitch = float(rotation.pitch)
18+
19+
cr = math.cos(self.roll)
20+
sr = math.sin(self.roll)
21+
cp = math.cos(self.pitch)
22+
sp = math.sin(self.pitch)
23+
cy = math.cos(self.yaw)
24+
sy = math.sin(self.yaw)
25+
26+
self.forward = Vec3(cp * cy, cp * sy, sp)
27+
self.right = Vec3(cy*sp*sr-cr*sy, sy*sp*sr+cr*cy, -cp*sr)
28+
self.up = Vec3(-cr*cy*sp-sr*sy, -cr*sy*sp+sr*cy, cp*cr)
29+
30+
31+
# Sometimes things are easier, when everything is seen from your point of view.
32+
# This function lets you make any location the center of the world.
33+
# For example, set center to your car's location and ori to your car's orientation, then the target will be
34+
# relative to your car!
35+
def relative_location(center: Vec3, ori: Orientation, target: Vec3) -> Vec3:
36+
"""
37+
Returns target as a relative location from center's point of view, using the given orientation. The components of
38+
the returned vector describes:
39+
40+
* x: how far in front
41+
* y: how far right
42+
* z: how far above
43+
"""
44+
x = (target - center).dot(ori.forward)
45+
y = (target - center).dot(ori.right)
46+
z = (target - center).dot(ori.up)
47+
return Vec3(x, y, z)

python_example/util/vec.py

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import math
2+
3+
4+
# This is a helper class for vector math. You can extend it or delete if you want.
5+
class Vec3:
6+
"""
7+
This class should provide you with all the basic vector operations that you need, but feel free to extend its
8+
functionality when needed.
9+
The vectors found in the GameTickPacket will be flatbuffer vectors. Cast them to Vec3 like this:
10+
`car_location = Vec3(car.physics.location)`.
11+
12+
Remember that the in-game axis are left-handed.
13+
14+
When in doubt visit the wiki: https://github.com/RLBot/RLBot/wiki/Useful-Game-Values
15+
"""
16+
17+
def __init__(self, x: float or 'Vec3'=0, y: float=0, z: float=0):
18+
"""
19+
Create a new Vec3. The x component can alternatively be another vector with an x, y, and z component, in which
20+
case the created vector is a copy of the given vector and the y and z parameter is ignored. Examples:
21+
22+
a = Vec3(1, 2, 3)
23+
24+
b = Vec3(a)
25+
26+
"""
27+
28+
if hasattr(x, 'x'):
29+
# We have been given a vector. Copy it
30+
self.x = float(x.x)
31+
self.y = float(x.y) if hasattr(x, 'y') else 0
32+
self.z = float(x.z) if hasattr(x, 'z') else 0
33+
else:
34+
self.x = float(x)
35+
self.y = float(y)
36+
self.z = float(z)
37+
38+
def __getitem__(self, item: int):
39+
return (self.x, self.y, self.z)[item]
40+
41+
def __add__(self, other: 'Vec3') -> 'Vec3':
42+
return Vec3(self.x + other.x, self.y + other.y, self.z + other.z)
43+
44+
def __sub__(self, other: 'Vec3') -> 'Vec3':
45+
return Vec3(self.x - other.x, self.y - other.y, self.z - other.z)
46+
47+
def __neg__(self):
48+
return Vec3(-self.x, -self.y, -self.z)
49+
50+
def __mul__(self, scale: float) -> 'Vec3':
51+
return Vec3(self.x * scale, self.y * scale, self.z * scale)
52+
53+
def __rmul__(self, scale):
54+
return self * scale
55+
56+
def __truediv__(self, scale: float) -> 'Vec3':
57+
scale = 1 / float(scale)
58+
return self * scale
59+
60+
def __str__(self):
61+
return "Vec3(" + str(self.x) + ", " + str(self.y) + ", " + str(self.z) + ")"
62+
63+
def flat(self):
64+
"""Returns a new Vec3 that equals this Vec3 but projected onto the ground plane. I.e. where z=0."""
65+
return Vec3(self.x, self.y, 0)
66+
67+
def length(self):
68+
"""Returns the length of the vector. Also called magnitude and norm."""
69+
return math.sqrt(self.x**2 + self.y**2 + self.z**2)
70+
71+
def dist(self, other: 'Vec3') -> float:
72+
"""Returns the distance between this vector and another vector using pythagoras."""
73+
return (self - other).length()
74+
75+
def normalized(self):
76+
"""Returns a vector with the same direction but a length of one."""
77+
return self / self.length()
78+
79+
def rescale(self, new_len: float) -> 'Vec3':
80+
"""Returns a vector with the same direction but a different length."""
81+
return new_len * self.normalized()
82+
83+
def dot(self, other: 'Vec3') -> float:
84+
"""Returns the dot product."""
85+
return self.x*other.x + self.y*other.y + self.z*other.z
86+
87+
def cross(self, other: 'Vec3') -> 'Vec3':
88+
"""Returns the cross product."""
89+
return Vec3(
90+
self.y * other.z - self.z * other.y,
91+
self.z * other.x - self.x * other.z,
92+
self.x * other.y - self.y * other.x
93+
)
94+
95+
def ang_to(self, ideal: 'Vec3') -> float:
96+
"""Returns the angle to the ideal vector. Angle will be between 0 and pi."""
97+
cos_ang = self.dot(ideal) / (self.length() * ideal.length())
98+
return math.acos(cos_ang)

0 commit comments

Comments
 (0)