如何使对象在相同高度上永远弹跳



我试图使球永远在相同的高度上弹跳,就像弹跳中不会损失能量一样。

我不知道为什么,具体取决于我所做的事情随着时间的流逝而弹跳,或者每次跳跃都会弹跳。我的期望是,每次反弹都应始终达到相同的高度,而不是更高或更低。

这是我的代码(代码在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个DT,一个之前和一个后反弹后的一个,然后进行2个加速更新,但这也不起作用。

你们能帮我吗?对于如此简单的方案编程游戏物理的方法是什么?

数值集成很难!由于您可以轻松地求解一维弹道方程,因此请这样做:计算

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

这是恒定加速度的微不足道情况下的速度verlet方法。如果y1<0,您可以求解二次方程式以找出它从该点弹跳并重新启动集成(否定速度(。

如果您想在数值准确的同时合并更复杂的物理,请考虑速度变量的 centering 。可以通过使其格式化来获得更好的精度 - 将其定义在定义位置的点之间的时间点上,从而提供了类似的跨越方法。

保守力量的一种截然不同的方法是定义球的总能量,并根据其高度基于它的高度进行动力学。即使那样,您仍必须将上述校正包括在dt*dt中,以避免在最大高度附近的数值问题。

因此,您的方程式无法正常工作,是因为您的更新功能缺少加速度引起的位置变化量。这应该起作用。

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()

我一直在想,我相信即使程序完全准确,对象通常也不会达到相同的高度。由于弹跳会导致对象在上升的途中采取不同的位置,因此在顶部,对象可能会显示出较低的位置,因为在游戏循环中仅显示了对象的真实运动的某些框架。这些帧可能与球的最高位置无关。抱歉,这太晚了,但这是我在JS中的尝试。您可以在任何网页的控制台中运行此操作。请注意,总能量几乎保持不变,并且没有我的草率编码,可能会更准确。

document.body.innerHTML = '<canvas id="myCanvas" width="375" height="555"></canvas> <p id ="text"></p>'
var x = 200;
var y = 105.3;
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
ctx.canvas.width = window.innerWidth
ctx.canvas.height = window.innerHeight-120

var g = .2
var ballRadius = 3;
var xsp = 0;
var ysp = 0;   
var iysp = 0; 
var p = 0;
var ip = 0;
var iy = 0;
var high = 200;
var a = 0
var b = 0
var fix = 0
var iter = 0
var fpms = 10
var gi = 0
var gii = 0
var brek = 100000000
var dt = 1
var smallt = 0
var mass = 1
var total = 0
function totale() {
    total = parseFloat(((mass*g*(500-y))+(.5*mass*(ysp*ysp))).toFixed(8))
}
function drawBall() {
    ctx.beginPath();
    ctx.arc(x, y, ballRadius, 0, Math.PI*2);
    ctx.fillStyle = "#0095DD";
    ctx.fill();
    ctx.closePath();
    ctx.beginPath();
    ctx.rect(0,0,200,105.3-ballRadius);
    ctx.fillStyle = "#0085DD";
    ctx.fill();
    ctx.closePath();
    ctx.beginPath();
    ctx.rect(0,500,200,100);
    ctx.fillStyle = "#0085DD";
    ctx.fill();
    ctx.closePath();
}
function draw() {
    if (iter==brek) {
        clearInterval(mainLoop)
        return;
    }
    iysp = ysp
    iy = y
    ysp = parseFloat((ysp + g*dt).toFixed(8))
    y = parseFloat((y + ((ysp+iysp)/2)*dt).toFixed(8))
    totale()
    if (y > 500) {
        ysp = iysp
        y = iy
        ysp = Math.sqrt(iysp*iysp+(2*g*(500-y)))
        b=ysp
        smallt = 1-((ysp-iysp)/g)
        ysp = ysp*-1+((g*dt)*smallt)
        y = parseFloat((500 + ((ysp+b*-1)/2)*dt*smallt).toFixed(8))
    }
        
    
    if (y < iy) {
        high = y
    }
    iter ++
    document.getElementById("text").innerHTML = '__y speed= '+ysp+'<br>'+'__highest y value= '+high+'<br>'+'__y pos'+(y)+'<br>'+'__smallt= '+(smallt)+'<br>'+'__iter= '+iter+'__total e= '+total
    ctx.clearRect(0,0,canvas.width,canvas.height)
    drawBall();
    
}
mainLoop = setInterval(draw,fpms)

最新更新