因此,我使用 python 和 pygame-ce 制作了一个平台游戏,并且通过简单的 AABB 碰撞检测来实现物理,并且遇到了一个相当烦人的问题,在我的 aabby() 方法中检测到并解决了向下碰撞后,我设置了变换组件的纠正对象位置重叠后,velocity.y 为 0。但我注意到,由于我也施加重力,因此当我接地时,速度实际上永远不会保持为零,这确实有意义,因为我不断施加向下的力。然而,当我引入依赖于物理状态(如动画)的组件时,问题就出现了。如果我静止地站在地板上,我的空闲状态和跳跃动画就会来回闪烁,因为我的速度。y 一帧为 0 会触发空闲状态,然后我会在其余的时间内跳跃/掉落。任何人都可以提供帮助或者指出我做错了什么?我不确定除了我的物理处理器及其处理的两个组件之外,您还需要什么其他代码,但如果是这样,请告诉我。
# --------------------------- #
class HXtransform:
def __init__(self, object, size:list[int]=[32, 32], location:list[int]=[100, 100]) -> None :
self.object = object
self.dynamic:bool=False
self.negx:bool=False
self.mass:float=10.0
self.scale:float=1.0
self.speed:float=120.0
self.rotation:float=0.0
self.size:pg.math.Vector2=vec2(size)
self.velocity:pg.math.Vector2=vec2()
self.location:pg.math.Vector2=vec2(location)
self.center:pg.math.Vector2=vec2(self.location[0]+(self.size[0]//2), self.location[1]+(self.size[1]//2))
self.sgrid = object.sgrid
self.spatial_ids = set()
if self.sgrid:
self.sgrid.add_object(self.object)
def get_spatial_ids(self) -> set:
return self.spatial_ids
def get_rect(self, offset:pg.math.Vector2=vec2(0,0)) -> pg.Rect:
return pg.Rect(self.location - offset, self.size)
def set_scale(self, scale:float=1.0) -> None :
self.scale = scale
def set_speed(self, speed:float=120.0) -> None :
self.speed = speed
def set_velocity(self, x:float|int=0.0, y:float|int=0.0, vec:pg.math.Vector2=None) -> None :
if vec: self.velocity = vec
else:
self.velocity.x = x
self.velocity.y = y
def move(self, delta_time:float, up:bool=False, down:bool=False, left:bool=False, right:bool=False):
if up: self.velocity[1] = -self.speed
if down: self.velocity[1] = self.speed
if left: self.velocity[0] = -self.speed
if right: self.velocity[0] = self.speed
def update_spatial_ids(self) -> None:
if self.sgrid:
new_spatial_ids = self.sgrid.get_node_ids(self.get_rect())
if new_spatial_ids != self.spatial_ids:
self.sgrid.rem_object(self.object)
self.spatial_ids = new_spatial_ids
self.sgrid.add_object(self.object)
def update(self, delta_time:float, *args, **kwargs) -> None:
self.center = vec2(self.location[0] + (self.size[0] // 2), self.location[1] + (self.size[1] // 2))
self.update_spatial_ids()
def free(self):
self.velocity = None
self.location = None
self.size = None
self.center = None
if self.sgrid:
self.sgrid.rem_object(self.object)
self.sgrid = None
self.spatial_ids.clear()
# --------------------------- #
# --------------------------- #
class HXcollider(HXcomponent):
def __init__(self, object, dimensions:list[int]):
super().__init__(object, [])
self.dimensions:list[int]=dimensions
self.rect:pg.Rect = pg.Rect([0, 0], dimensions)
self.transform:HXtransform=self.object.get_component(HXtransform)
self.colliding = {
"top":False,
"left":False,
"right":False,
"bottom":False
}
self.grounded:bool=False
self.update()
def update(self, *args, **kwargs):
self.rect.topleft = self.transform.location
# --------------------------- #
# --------------------------- #
class HXphysics:
GRAVITY:float|int = 500
FRICTION:float|int = 250
def __init__(self) -> None: ...
def set_gravity(self, g:float|int) -> None: self.GRAVITY = g
def set_friction(self, f:float|int) -> None: self.FRICTION = f
def gravity(self, ent, dt):
t1 = ent.get_component(HXtransform)
if t1.dynamic:
t1.velocity[1] += self.GRAVITY * dt
def friction(self, ent, dt) -> None:
t1 = ent.get_component(HXtransform)
if t1.velocity.x > 0:
t1.velocity.x -= (self.FRICTION) * dt
if t1.velocity.x < 0:
t1.velocity.x = 0
if t1.velocity.x < 0:
t1.velocity.x += (self.FRICTION) * dt
if t1.velocity.x > 0:
t1.velocity.x = 0
def aabby(self, check, against) -> bool:
res:bool=False
c1 = check.get_component(HXcollider)
t1 = check.get_component(HXtransform)
for ent in against:
if ent != check:
c2 = ent.get_component(HXcollider)
if c1.rect.colliderect(c2.rect):
if t1.velocity[1] > 0:
t1.velocity[1] = 0
t1.location.y = c2.rect.top - c1.rect.height
elif t1.velocity[1] < 0:
t1.velocity[1] = 0
t1.location.y = c2.rect.bottom
res = True
return res
def aabbx(self, check, against) -> bool:
res:bool=False
c1:HXcollider = check.get_component(HXcollider)
t1 = check.get_component(HXtransform)
for ent in against:
if ent != check:
c2:HXcollider = ent.get_component(HXcollider)
if c1.rect.colliderect(c2.rect):
if t1.velocity[0] > 0:
t1.velocity[0] = 0
t1.location.x = c2.rect.left - c1.rect.width
elif t1.velocity[0] < 0:
t1.velocity[0] = 0
t1.location.x = c2.rect.right
res = True
return res
def bind_transform(self, transform:HXtransform, sgrid) -> None:
""" binds an object's location to the spatial grid it was created in"""
if transform.location.x <= 0:
transform.velocity.x = 0
transform.location.x = 1
elif transform.location.x >= sgrid.size.x-transform.size.x:
transform.velocity.x = 0
transform.location.x = sgrid.size.x-transform.size.x
if transform.location.y <= 0:
transform.velocity.y = 0
transform.location.y = 1
elif transform.location.y >= sgrid.size.y-transform.size.y:
transform.velocity.y = 0
transform.location.y = sgrid.size.y-transform.size.y
def update(self, snodes, dt, *args, **kwargs) -> None:
try:
snode = snodes.pop()
for obj in snode.objects:
if obj == None: continue
c = obj.get_component(HXcollider)
t = obj.get_component(HXtransform)
if not t or not c:continue
t.location.y += t.velocity.y * dt
self.gravity(obj, dt)
c.update(t.location)
self.aabby(obj, snode.objects)
t.location.x += t.velocity.x * dt
self.friction(obj, dt)
c.update(t.location)
self.aabbx(obj, snode.objects)
self.bind_transform(transform=t, sgrid=obj.sgrid)
c.update(t.location)
except (AttributeError, IndexError) as err: hxLogger.log(hxLogger.HLX_LOG_ERROR, f"HXphysics: {err}") # off grid query
# --------------------------- #
我已经尝试引入一个固定的更新循环,我可能应该早点完成,我也尝试过仅使用整数值,但这会消除很多运动精度,正如您所猜测的那样。
遇到的问题是因为在每一帧都对重力进行计算,即使角色位于地面上也是如此。这会导致角色的速度在零附近振荡,导致的动画状态发生闪烁。
需要做的是在的
aabby
函数中引入一个“grounded”标志。当检测到碰撞时,此标志将设置为
True
,并在没有碰撞时设置为
False
。然后,只能在角色没有接地时应用重力。
以下是对的代码所做更改的逐步说明:
-
在
HXcollider
组件中添加grounded
标志:
class HXcollider(HXcomponent):
def __init__(self, object, dimensions:list[int]):
# ... 其他初始化代码 ...
self.grounded = False # 添加此行
self.update()
# ... 其他方法 ...
-
在
aabby
函数中更新grounded
标志:
def aabby(self, check, against) -> bool:
res:bool=False
c1 = check.get_component(HXcollider)
t1 = check.get_component(HXtransform)
c1.grounded = False # 将 grounded 标志初始化为 False
for ent in against:
if ent != check:
c2 = ent.get_component(HXcollider)
if c1.rect.colliderect(c2.rect):
if t1.velocity[1] > 0:
t1.velocity[1] = 0
t1.location.y = c2.rect.top - c1.rect.height
c1.grounded = True # 如果有向下碰撞,则将 grounded 设置为 True
elif t1.velocity[1] < 0:
t1.velocity[1] = 0
t1.location.y = c2.rect.bottom
c1.grounded = True # 如果有向上碰撞,则将 grounded 设置为 True
res = True
return res
- 仅在角色没有接地时应用重力:
def gravity(self, ent, dt):
t1 = ent.get_component(HXtransform)
c1 = ent.get_component(HXcollider) # 获取 collider 组件
if t1.dynamic and not c1.grounded: # 仅在没有接地时应用重力
t1.velocity[1] += self.GRAVITY * dt
通过这些更改,的角色在落地后其垂直速度将不再振荡,并且的动画应该会按预期运行,而不会出现闪烁。
标签:python,pygame-ce From: 78785367