Как заставить объект отскакивать навсегда на одной высоте - PullRequest
1 голос
/ 25 мая 2019

Я пытаюсь заставить шар отскочить навсегда, на одной и той же высоте всегда, как это было бы, если бы энергия отскока не была потеряна.

Я не знаю почему, в зависимости от того, что я делаюсо временем подпрыгивает выше или падает при каждом прыжке.Я ожидаю, что в каждом скачке он всегда должен достигать одинаковой высоты, а не выше или ниже.

Это мой код (код написан на pyglet, но его действительно легко прочитать, даже если вы этого не сделаете)знаю библиотеку).Здесь с середины экрана выпадает квадрат, отмеченный стрелкой, и я ожидаю, что куб отскочит в полу и точно вернется к исходной высоте при возвращении:

import pyglet


class Polygon(object):
    def __init__(self, vertices, color, velocity=0, acceleration=-600):
        self.vertices = vertices
        self.y_idx = 1
        self.num_vertices = int(len(self.vertices) // 2)
        self.colors = color * self.num_vertices
        self.velocity = velocity
        self.acceleration = acceleration
        self.bottom_edge = 0

    def draw(self):
        self.vertex_list = pyglet.graphics.vertex_list(self.num_vertices,
                                                       ("v2f", self.vertices),
                                                       ("c3B", self.colors))
        self.vertex_list.draw(pyglet.gl.GL_POLYGON)

    def move_by_offset(self, offset):
        for i in range(1, len(self.vertices), 2):
            self.vertices[i] += offset  # only modify y values

    def bounce(self, dt):
        if self.vertices[self.y_idx] < self.bottom_edge:
            self.velocity = abs(self.velocity)
            return True
        return False

    def update(self, dt):
        # move
        self.move_by_offset(self.velocity * dt)
        # check if bounce
        self.bounce(dt)
        # accelerate
        self.velocity += self.acceleration * dt


class GameWindow(pyglet.window.Window):
    def __init__(self, objects=None, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.objects = objects

    def update(self, dt):
        for obj in self.objects:
            obj.update(dt)

    def on_draw(self):
        self.clear()
        for obj in self.objects:
            obj.draw()


class Game(object):
    def __init__(self, w=400, h=400, title="My game", resizable=False):
        self.w = w
        self.h = h
        objects = [
            # square
            Polygon(vertices=[w/2-20, h/2, w/2-20, h/2+40, w/2+20, h/2+40, w/2+20, h/2],
                    color=[0, 128, 32],  # green
                    velocity=0,
                    acceleration=-6000),
            # arrow, marks exactly how high the square should bounce
            Polygon(vertices=[w/2, h/2, w/2+40, h/2+20, w/2+30, h/2, w/2+40, h/2-20],
                    color=[255, 255, 0], # yellow
                    velocity=0,
                    acceleration=0)
        ]
        self.window = GameWindow(objects, self.w, self.h, title, resizable)

    def update(self, dt):
        self.window.update(dt)


if __name__ == "__main__":
    game = Game(resizable=False)
    pyglet.clock.schedule_interval(game.update, 1/120)
    pyglet.app.run()

Я пыталсяразличные порядки обновления, такие как изменение скорости в соответствии с ускорением до изменения скорости после отскока, или даже не ускорение вообще после отскока (что, кажется, работает лучше), но все же отскок не является точным и постоянно изменяет высоту:

    def update2(self, dt):
        # move
        self.move_by_offset(self.velocity * dt)
        # accelerate
        self.velocity += self.acceleration * dt
        # check if bounce
        self.bounce(dt)

    def update3(self, dt):
        # move
        self.move_by_offset(self.velocity * dt)
        # check if bounce
        bounced = self.bounce(dt)
        if not bounced:
            # accelerate (only if no bounce happened)
            self.velocity += self.acceleration * dt

Я даже попробовал что-то более сложное: создать 2 точки, одну до и одну после отскока, и сделать 2 обновления ускорения, но это тоже не сработало.

Можете ли вы, ребята, помочь мне?Как программировать игровую физику для такого простого сценария?

Ответы [ 2 ]

1 голос
/ 25 мая 2019

Численное интегрирование сложно!Поскольку вы легко можете точно решить одномерное баллистическое уравнение, сделайте так: вычислите

y1=y0+v0*dt+g*dt*dt/2
v1=v0+g*dt

Это метод Верле скорости в тривиальном случае постоянного ускорения.Если y1<0, вы можете решить квадратное уравнение, чтобы выяснить , когда оно отскочило, и перезапустить интегрирование с этой точки (с отрицательной скоростью).

Если вы хотите включить более сложноефизика, все еще будучи численно точной, рассмотрите центрирование вашей переменной скорости.Лучшей точности можно добиться, ошеломив ее: определение ее в моменты времени, находящиеся посередине между точками, в которых определяется позиция, дает аналогичный метод скачкообразной перестройки .

Совсем другой подход для консерваторовсила состоит в том, чтобы определить полную энергию для шара и переместить его в зависимости от того, сколько его должно быть кинетическим в зависимости от его высоты.Даже в этом случае вы должны включить вышеуказанную поправку в dt*dt, чтобы избежать численных проблем вблизи максимальной высоты.

0 голосов
/ 25 мая 2019

Причина, по которой ваше уравнение не работает, заключается в том, что в вашей функции обновления отсутствует величина изменения положения, вызванная ускорением.Это должно работать.

import pyglet
import math

class Polygon(object):
    def __init__(self, vertices, color, velocity=0, acceleration=-600):
        self.vertices = vertices
        self.y_idx = 1
        self.num_vertices = int(len(self.vertices) // 2)
        self.colors = color * self.num_vertices
        self.velocity = velocity
        self.acceleration = acceleration
        self.bottom_edge = 0

    def draw(self):
        self.vertex_list = pyglet.graphics.vertex_list(self.num_vertices,
                                                       ("v2f", self.vertices),
                                                       ("c3B", self.colors))
        self.vertex_list.draw(pyglet.gl.GL_POLYGON)

    def move_by_offset(self, offset):
        for i in range(1, len(self.vertices), 2):
            self.vertices[i] += offset  # only modify y values

    def bounce(self, dt):
        if self.vertices[self.y_idx] < self.bottom_edge:
            self.velocity = abs(self.velocity)

            dropped_height = (self.velocity**2) / (-self.acceleration * 2) 
            drop_time = math.sqrt(2 * dropped_height / -self.acceleration)
            print("dropped height:", dropped_height)
            print("drop time:", drop_time) 

            return True
        return False

    def update(self, dt):
        # move
        move_by_velocity = self.velocity * dt
        move_by_acceleration = 1/2 * -self.acceleration * dt * dt
        self.move_by_offset(move_by_velocity + move_by_acceleration)
        # check if bounce
        self.bounce(dt)
        # accelerate
        self.velocity += self.acceleration * dt


class GameWindow(pyglet.window.Window):
    def __init__(self, objects=None, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.objects = objects

    def update(self, dt):
        for obj in self.objects:
            obj.update(dt)

    def on_draw(self):
        self.clear()
        for obj in self.objects:
            obj.draw()


class Game(object):
    def __init__(self, w=400, h=400, title="My game", resizable=False):
        self.w = w
        self.h = h
        objects = [
            # square
            Polygon(vertices=[w/2-20, h/2, w/2-20, h/2+40, w/2+20, h/2+40, w/2+20, h/2],
                    color=[0, 128, 32],  # green
                    velocity=0,
                    acceleration=-6000),
            # arrow, marks exactly how high the square should bounce
            Polygon(vertices=[w/2, h/2, w/2+40, h/2+20, w/2+30, h/2, w/2+40, h/2-20],
                    color=[255, 255, 0], # yellow
                    velocity=0,
                    acceleration=0)
        ]
        self.window = GameWindow(objects, self.w, self.h, title, resizable)

    def update(self, dt):
        self.window.update(dt)


if __name__ == "__main__":
    game = Game(resizable=False)
    pyglet.clock.schedule_interval(game.update, 1/120)
    pyglet.app.run()
...