0
\$\begingroup\$

To start, I'm not working with an engine and am looking for some help fixing the math behind the function I created.

Say we have anchor, obj, speed, dt, and terminationDist ; function anchorPull will theoretically pull obj towards anchor at a rate of speed pixels per second until the distance from anchor to obj is less than terminationDist. My implementation is as follows:

def anchorPull(anchor, obj, speed, dt, termDist = 10):

    if dist(anchor, obj) > termDist:
        deltaX1 = obj.x - anchor.x
        deltaY1 = obj.y - anchor.y

        theta = degrees(atan(deltaX1/deltaY1))

        deltaX2 = sin(theta) * speed * dt
        deltaY2 = cos(theta) * speed * dt

        obj.x += deltaX2
        obj.y += deltaY2

Visual Representation: Visual Representation

The problem is, at low framerates (a high dt) obj wont go all the way to anchor: enter image description here

obj will also stochastically move around when anchor isn't stationary.

Any help would be great, thanks!

\$\endgroup\$
1
  • \$\begingroup\$ How about setting the position when stopped? The dt in the real world is infinitely small, and some situations cannot be simulated by the program. \$\endgroup\$
    – Mangata
    Commented Jun 11, 2022 at 7:51

1 Answer 1

0
\$\begingroup\$

It looks like you're trying to implement a MoveTowards function. Try something more like this:

def moveTowards(mover, target, maxStep):

    deltaX = target.x - mover.x
    deltaY = target.y - mover.y

    mag = deltaX * deltaX + deltaY * deltaY

    if mag < maxStep * maxStep:
        mover.x = target.x
        mover.y = target.y 
    else:
        scale = maxStep / sqrt(mag)
    
        mover.x += deltaX * scale
        mover.y += deltaY * scale

Or, to stop at a fixed distance away from the object, modify like so:

def moveTowardsOrbit(mover, target, maxStep, stopDistance):

    deltaX = target.x - mover.x
    deltaY = target.y - mover.y

    distance = sqrt(deltaX * deltaX + deltaY * deltaY)

    unitScale = 1 / distance

    deltaX *= unitScale
    deltaY *= unitScale

    onCircleX = target.x - deltaX * stopDistance
    onCircleY = target.y - deltaY * stopDistance

    fromStop = distance - stopDistance

    if abs(fromStop) <= maxStep:
        mover.x = onCircle.x
        mover.y = onCircle.y 
    else:
        if fromStop < 0:
            deltaX *= -1
            deltaY *= -1
    
        mover.x += deltaX * maxStep
        mover.y += deltaY * maxStep

Call this with moveTowards(obj, anchor, speed * dt, 10).

This version...

  • Picks a point on the circle stopDistance away from the anchor and moves toward that - whether that takes it inward or outward along the line joining obj and anchor.

  • Moves the object at most a distance of maxStep per invocation.

  • When the destination is closer than maxStep, it snaps the object directly to the stopDistance circle, with no under- or over-shooting.

  • Uses no trigonometric functions. It also handles all angles correctly, unlike the sample code (hint: you probably want atan2 when trying to find an angle from two coordinates).

You should also consider using a fixed timestep for your game simulation, so it's not subject to variation depending on the rendering framerate.

\$\endgroup\$
2
  • \$\begingroup\$ Thanks, these work great! One small modification to the moveTowardsOrbit function: if abs(fromStop) <= speed: obj.x = onCircleX obj.y = onCircleY \$\endgroup\$
    – SlavaCat
    Commented Jun 11, 2022 at 14:50
  • \$\begingroup\$ No, that is not the correct change to make. \$\endgroup\$
    – DMGregory
    Commented Jun 11, 2022 at 15:52

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .