ID:1038028
 
Resolved
When Move() was called with a high speed, small object, and shallow angle of movement, collisions were improperly calculated.
BYOND Version:493
Operating System:Windows XP Pro
Web Browser:Firefox 21.0
Applies to:Dream Daemon
Status: Resolved (499.1194)

This issue has been resolved.
Shift an object's position along a straight line with proper collision detection. Regardless of how large the shift is, this movement will always be considered a "slide" as opposed to a "jump."

It's beyond my experience to do this properly, accurately, and efficiently. I don't even care if it doesn't take decimals. (edit: decimal moves are extremely necessary but they go in a different request)
atom/movable/proc
translate(px,py)
step(src,px>0 ? EAST : WEST,abs(px))
step(src,py>0 ? NORTH : SOUTH,abs(py))
translate_polar(magnitude,angle)
src.translate(cos(angle)*magnitude,sin(angle)*magnitude)

atom/movable/step_size=1


That should do it.
Unless you need things to be really precise step-wise, but even then, just break things into smaller and smaller jumps until the difference is marginal enough for you.
In response to Oasiscircle
Oasiscircle wrote:
That should do it.
Unfortunately not. ("along a straight line")

Unless you need things to be really precise step-wise...
Yes. ("with proper collision detection")

...but even then, just break things into smaller and smaller jumps until the difference is marginal enough for you.
Very difficult to do. ("efficiently")

I've been at this since long before native pixel movement came out.
There simply isn't a way that works perfectly for what I've been trying to do.
atom/movable
var
decx
decy
proc
translate_polar(magnitude,angle)
var/px=magnitude*cos(angle)
var/py=magnitude*sin(angle)
var/int_x=cos(angle)
var/int_y=sin(angle)
var/signx=sign(x)
var/signy=sign(y)
while(px||py)
if(px)
px-=int_x
decx+=int_x
if(abs(decx)>=1)
if(!step(src,decx>0 ? EAST : WEST,roundd(abs(decx)))) break
decx-=roundd(abs(decx))*sign(decx)
if((signx>0&&px<0)||(signx<0&&px>0)) px=0
if(py)
py-=int_y
decy+=int_y
if(abs(decy)>=1)
if(!step(src,decy>0 ? NORTH : SOUTH,roundd(abs(decy)))) break
decy-=roundd(abs(decy))*sign(decy)
if((signy>0&&py<0)||(signy<0&&py>0)) py=0

proc/roundd(n) return round(n)>n ? round(n)-1 : round(n)
proc/sign(n) return n>0 ? 1 : -1


How's that?
In response to Oasiscircle
The entire point of smooth movement is to generally have a lot of small movements at a high framerate. This doesn't really require decimal movements unless you want to go slower than 1 pixel per tick, which gets faster at higher framerates.

However, bullets with a constant velocity (i.e. not projectiles) only need relatively few moves at very high speed along a straight line with precise collision detection. The decimal part here isn't as important larger moves appear less rounded. It's also incredibly hard to efficiently get precise collision detection between moves using DM.

Everything in between, such as vehicles, projectiles, missiles (slower than bullets but faster than players), etc. require decimal moves to get around properly.

The bottom line is that I simply won't settle for 8-directional movement with rounded positions. Some might think, "Why could you possibly need to move half a pixel?" but they are just being completely ignorant of the math behind the scenes. It simply doesn't make sense to me that "smooth movement" only works well in 8 directions.

Also, bump.
This is the obvious problem with moving fast speeds along only cardinal axes:

The projectile dodges the obstacle because it moved along the two axes individually. I can see why it would do that, but it's not useful.

My attempts to split movements up into smaller steps just end up not being efficient.

//  my current syntax
var vx, vy
move(vx, vy)

// it also takes a vector, or a list with len == 2
#define vec2(x, y) list(x, y)
#define vec2p(r, t) vec2(r * sin(t), r * cos(t))

var velocity[] = vec2(x, y)
// or
var velocity[] = vec2p(speed, angle)
move(velocity)
Suggestion: Modify the object's step_size temporarily to the size of your jump. The internal code already breaks down a large step (bigger than the object bounds) into smaller steps so it can handle collision detection properly using large velocities.
In response to Lummox JR
Huh, I was sure that it only did this in the 8 BYOND directions. ...

It's not perfect, though, with a 2x2 object:

The last one has it going through the block AND impacting in the wrong spot.

It's using Move(loc, dir, step_x + dx, step_y + dy).
I consider the behavior you're seeing in #3 to be a bug. The routine is not properly keeping the angle intact. Because the incremental step size (2px) is so low, the shallow angle means the x step isn't being accounted for at all until later in the projectile's path.
Actually if you can send me the test project for that, I can get it in with the next round of fixes. I could make a project myself but it would take too long, and it seems you have something together already.
Lummox JR resolved issue with message:
When Move() was called with a high speed, small object, and shallow angle of movement, collisions were improperly calculated.