File size: 3,903 Bytes
f92c150 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 |
import math
import collider
FLYING_ACCEL = (0, 0, 0)
GRAVITY_ACCEL = (0, -32, 0)
# these values all come (losely) from Minecraft, but are multiplied by 20 (since Minecraft runs at 20 TPS)
FRICTION = ( 20, 20, 20)
DRAG_FLY = ( 5, 5, 5)
DRAG_JUMP = (1.8, 0, 1.8)
DRAG_FALL = (1.8, 0.4, 1.8)
class Entity:
def __init__(self, world):
self.world = world
# physical variables
self.jump_height = 1.25
self.flying = False
self.position = [0, 80, 0]
self.rotation = [-math.tau / 4, 0]
self.velocity = [0, 0, 0]
self.accel = [0, 0, 0]
# collision variables
self.width = 0.6
self.height = 1.8
self.collider = collider.Collider()
self.grounded = False
def update_collider(self):
x, y, z = self.position
self.collider.x1 = x - self.width / 2
self.collider.x2 = x + self.width / 2
self.collider.y1 = y
self.collider.y2 = y + self.height
self.collider.z1 = z - self.width / 2
self.collider.z2 = z + self.width / 2
def teleport(self, pos):
self.position = list(pos)
self.velocity = [0, 0, 0] # to prevent collisions
def jump(self, height = None):
# obviously, we can't initiate a jump while in mid-air
if not self.grounded:
return
if height is None:
height = self.jump_height
self.velocity[1] = math.sqrt(-2 * GRAVITY_ACCEL[1] * height)
@property
def friction(self):
if self.flying:
return DRAG_FLY
elif self.grounded:
return FRICTION
elif self.velocity[1] > 0:
return DRAG_JUMP
return DRAG_FALL
def update(self, delta_time):
# apply input acceleration, and adjust for friction/drag
self.velocity = [v + a * f * delta_time for v, a, f in zip(self.velocity, self.accel, self.friction)]
self.accel = [0, 0, 0]
# compute collisions
self.update_collider()
self.grounded = False
for _ in range(3):
adjusted_velocity = [v * delta_time for v in self.velocity]
vx, vy, vz = adjusted_velocity
# find all the blocks we could potentially be colliding with
# this step is known as "broad-phasing"
step_x = 1 if vx > 0 else -1
step_y = 1 if vy > 0 else -1
step_z = 1 if vz > 0 else -1
steps_xz = int(self.width / 2)
steps_y = int(self.height)
x, y, z = map(int, self.position)
cx, cy, cz = [int(x + v) for x, v in zip(self.position, adjusted_velocity)]
potential_collisions = []
for i in range(x - step_x * (steps_xz + 1), cx + step_x * (steps_xz + 2), step_x):
for j in range(y - step_y * (steps_y + 2), cy + step_y * (steps_y + 3), step_y):
for k in range(z - step_z * (steps_xz + 1), cz + step_z * (steps_xz + 2), step_z):
pos = (i, j, k)
num = self.world.get_block_number(pos)
if not num:
continue
for _collider in self.world.block_types[num].colliders:
entry_time, normal = self.collider.collide(_collider + pos, adjusted_velocity)
if normal is None:
continue
potential_collisions.append((entry_time, normal))
# get first collision
if not potential_collisions:
break
entry_time, normal = min(potential_collisions, key = lambda x: x[0])
entry_time -= 0.001
if normal[0]:
self.velocity[0] = 0
self.position[0] += vx * entry_time
if normal[1]:
self.velocity[1] = 0
self.position[1] += vy * entry_time
if normal[2]:
self.velocity[2] = 0
self.position[2] += vz * entry_time
if normal[1] == 1:
self.grounded = True
self.position = [x + v * delta_time for x, v in zip(self.position, self.velocity)]
# apply gravity acceleration
gravity = FLYING_ACCEL if self.flying else GRAVITY_ACCEL
self.velocity = [v + a * delta_time for v, a in zip(self.velocity, gravity)]
# apply friction/drag
self.velocity = [v - min(v * f * delta_time, v, key = abs) for v, f in zip(self.velocity, self.friction)]
# make sure we can rely on the entity's collider outside of this function
self.update_collider() |