首页 > 编程问答 >平台游戏 AABB 物理 Bug

平台游戏 AABB 物理 Bug

时间:2024-07-24 03:55:51浏览次数:7  
标签:python pygame-ce

因此,我使用 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 。然后,只能在角色没有接地时应用重力。

以下是对的代码所做更改的逐步说明:

  1. HXcollider 组件中添加 grounded 标志:
class HXcollider(HXcomponent):
    def __init__(self, object, dimensions:list[int]):
        # ... 其他初始化代码 ...
        self.grounded = False  # 添加此行
        self.update()

    # ... 其他方法 ... 
  1. 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
  1. 仅在角色没有接地时应用重力:
    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

相关文章

  • Python tkinter 窗口不断关闭,我不知道为什么
    我正在尝试制作一个有趣的小程序,其中每小时左右就会有一只毛茸茸的动物走过屏幕。我有一个主窗口,它启动一个循环,每小时左右播放一次动画/声音,但是在口袋妖怪第一次完成行走后,整个程序就会结束。我认为这可能与我设置tkinter窗口的方式有关,但我无法弄清楚。我认为在这里包含......
  • 用于自动访问 MongoDB Atlas CLI 的 Python 脚本
    我想编写一个Python脚本,以便普通用户可以访问他的数据库并从他的终端执行CRUD操作。我正在查看官方文档,但我有点迷失。有人可以给我指点基本教程来开始吗?当然,以下是如何构建Python脚本来访问MongoDBAtlasCLI的基本教程:先决条件:MongoDBAtlas......
  • Python实现简单学生登陆系统
     代码:importhashlibclassStudent:def__init__(self,username,password):#初始化学生对象,存储用户名和加密后的密码self.username=usernameself.password=hashlib.sha256(password.encode()).hexdigest()defcheck_passwo......
  • python猜数字游戏
    好的,下面是一个简单的小游戏代码示例,使用Python编写。这个游戏是一个猜数字游戏,玩家需要猜一个1到100之间的随机数。importrandomdefguess_number_game():print("欢迎来到猜数字游戏!")print("我已经想好了一个1到100之间的数字,请你猜一猜。")secret_num......
  • 【Python正则表达式】:文本解析与模式匹配
    文章目录1.正则表达式2.re模块3.修饰符3.元字符3-1字符匹配元字符3-2重复次数限定元字符3-3字符集合匹配元字符3-4分组元字符3-5边界匹配元字符3-6字符类别匹配元字符4.技巧4-1贪婪与非贪婪5.案例1.正则表达式正则表达式面向什么样的问题?1、判断一个字......
  • 仅需增加2行代码,Python量化策略速度提升20+倍!
    今天分享一个Python量化策略加速的小技巧,不用修改原有代码,只需在原有代码里新增2行,策略执行速度便可能提高20+倍,正文开始~现如今,无论是入门量化投资,还是做数据分析、机器学习和深度学习,Python成为了首选编程语言,直观的原因就是容易上手和资源丰富,但Python有个根深蒂固的标签,......
  • Python基础-Anaconda,Spyder,数据类型
    1、Python与Anaconda在想使用Python之前需先安装Python,以及PythonIDE和Python的库,而用Anaconda就可以一键安装。Anaconda包含了Python,常用的python库以及IDE,还具有强大的环境和python包的管理能力。PythonIDE(IntegratedDevelopmentEnvironment,集成开发环境)是一个为开发......
  • python实现图像特征提取算法2
    python实现广义Hough变换算法、Hough变换算法1.广义Hough变换算法详解算法步骤Python实现详细解释优缺点2.Hough变换算法详解算法步骤Python实现详细解释优缺点实现广义Hough变换算法(GeneralizedHoughTransform)可以用于检测任意形状的......
  • 使用Python自动备份MC服务器存档至轻量COS
    在存档意外炸档时,备份就显得尤为重要。本文以Minecraft1.18.2为例,教你如何使用轻量搭建一个MC服务器,并定时将存档备份入轻量COS,节省本地空间。前期准备一台安装了Linux原版系统的轻量(因为目前轻量COS仅支持挂载Linux系统镜像,不支持容器或自定义镜像)一个和轻量大区相同的轻......
  • python中的数据类型-适合新手-比较完善(写了好久……)
    作者的话        首先,我先申明,以下思路仅为个人理解,如有不同,望指导,谢谢。        数据类型它是什么,有什么用,怎么用就是它的全部内容,知识框架串联起来之后就是这三部分内容,没有什么特别的东西就只是单纯的可以培养这个习惯,之后就可以很轻松的学习使用之后串联......