ID:151264
 
I know Forum_account's Pixel Movement libraries handle it very nicely, but what I want to know is if there's a very basic algorithm for fractional pixel movement. What I mean by that is, of course, moving by fractions of a pixel at a time. The importance of that to me comes from usage of math slightly more complex than addition and subtraction (mainly trig).

The current basic "native pixel movement" requires you to shift positions by integer(s) at a time. This inaccuracy is problematic in a number of cases. Mainly, at higher frames-per-second, the minimum speed limit of 1p/t(pixel per tick) really noticeable. There are also obvious rounding issues with trig functions. They simply won't work with low speeds, causing you to only be able to move one of the 8 directions at rounded speeds. Using the "simple" Move(loc, dir, step_x + dx, step_y + dy) won't use BYOND's awesome pixel-perfect collision detection, where, if an object's bounding box intersects (or possibly even overshoots?) another, it is instantly pushed back.

What I ask for is the simplest fully-functional method that best uses BYOND's native pixel movement support while providing fractional displacements. Forum_account's technique is to build up the fractional parts and add the integer parts. Is there a simpler way that replaces less of BYOND's native functionality?

edit (30 Aug 2015):
The current simplest way I've found:
atom/movable
// position relative to the current pixel, between -0.5 and 0.5
var __dx, __dy

// sub-pixel movement along a translation vector
proc/Translate(Tx, Ty)
if(!(Tx || Ty)) return

// full-pixel movements
var rx = round(__dx + Tx, 1)
var ry = round(__dy + Ty, 1)
__dx += Tx - rx
__dy += Ty - ry

// change step_size only for this movement,
// gotta make it a slide instead of a jump
var s = step_size
step_size = 1 + max(abs(rx), abs(ry))

// move if we can, return success if we tried to move but didn't move a full pixel
. = (Tx || Ty) ? (rx || ry) ? Move(loc, dir, step_x + rx, step_y + ry) : TRUE : FALSE
step_size = s

proc/Project(Distance, Angle)
. = Distance && Translate(Distance*sin(Angle), Distance*cos(Angle))
You are referring to projectiles?
In response to Kaigne
I'm referring to all movers being able to have velocities between integers. I can get projectile collisions by calling bound() on an arbitrary coordinate.
In response to Kaiochao
It'd be much more difficult to have things collide on a fractional level, you'd have to basically rewrite the whole collision detection system.

But for fractional movement, just divide the velocities into two components (x and y) and add those components to the fractional position of the object. (Fractional position isn't actual pixel position.) Then have the movement procs "check" to see if after adding the velocity to the fractional position that the new object would be better positioned in the next pixel over. That would be the case if the object should be overlaying more than half of it's pixel neighbor. If that is true, then move the object a step closer to this neighbor and subtract 1 from the total saved fractional position.
Move(), step_x, and step_y don't support fractional values. If you want actual fractional positions, you'll have to handle movement yourself. If you want to use BYOND's native movement, you'll have to use a similar method as mine (accumulating fractional parts). I can't think of any other way.

It's not that hard to split that part out of the library - it used to use this method with BYOND's native feature.
you could scale all your graphics up 2:1 and display them at half that. then you'd gain 0.5 of a pixel.
In response to FIREking
FIREking wrote:
you could scale all your graphics up 2:1 and display them at half that. then you'd gain 0.5 of a pixel.

You wouldn't truly move half of a pixel by doing that. It would only appear that way, which if the isometric support hasn't taught us, nobody is interested in making things appear to be something they're not.
In response to Kumorii
Kumorii wrote:
FIREking wrote:
you could scale all your graphics up 2:1 and display them at half that. then you'd gain 0.5 of a pixel.

You wouldn't truly move half of a pixel by doing that. It would only appear that way, which if the isometric support hasn't taught us, nobody is interested in making things appear to be something they're not.

well just so you know, there is no such thing as half a pixel. only a visual effect.

even open gl has to map something to a pixel. its the smallest increment you have in graphics.
In response to FIREking
While this is all an interesting point to make...
You wouldn't just be gaining half-a-pixel, you'd also be doubling the amount of pixels that the collision calculations would need to be done for.

It's clear the issue-at-hand is not a graphical one, but a physical one. (The fact that pixels are the smallest thing in graphics is irrelevant to the physics engine. Or, at least, it should be.)
In response to Complex Robot
True. But I don't think you'll ever see floating point accuracy in byond.
In response to FIREking
All numbers in BYOND are floating point, I'm pretty sure.
In response to FIREking
FIREking wrote:
True. But I don't think you'll ever see floating point accuracy in byond.

What makes you think that?
Kaiochao wrote:
What I ask for is the simplest fully-functional method that best uses BYOND's native pixel movement support while providing fractional displacements. Forum_account's technique is to build up the fractional parts and add the integer parts. Is there a simpler way that replaces less of BYOND's native functionality?

I thought I had replied to this, but I guess not. Maybe it was a similar post somewhere else.

Accumulating the fractional parts doesn't replace any of BYOND's native functionality. That method can work just fine with calling mob.Move() - that's how the library used to work.

You can do something like this:

mob
proc
MyMove(dx, dy)

// accumulate fractional parts and truncate dx and dy

// if the fractional parts are greater than 0.5 or less
// than -0.5, adjust dx and dy by an integer amount

Move(loc, dir, step_x + dx, step_y + dy)

You're still using the built-in Move() proc to handle the pixel movement, you're simply adjusting the parameters you're passing into it based on the fractional parts of moves that you've accumulated.
At first, I didn't want to use "fractional part accumulation" to handle this because it seemed cheap. After making use of that extremely simple method, I have a much better understanding of its simplicity. It's really not that complicated and it provides very decent results.

If this topic was in Code Problems, I'd vote up your post, F_a.

Aaand... this is my implementation.
#include <forum_account/keyboard/keyboard.dme>

// Main implementation
mob
// accumulating fractional parts
var step_xf, step_yf

proc/move(dx, dy)
if(dx)
// horizontal movement
var ipart_x = round(dx) // integer part
step_xf += dx - ipart_x // fractional part
while(step_xf > 0.5) {ipart_x ++; step_xf --}
while(step_xf < -0.5) {ipart_x --; step_xf ++}

// horizontal movement failure
if(ipart_x && !Move(loc, dir, step_x + ipart_x, step_y))
vel_x = 0

if(dy)
// vertical movement
var ipart_y = round(dy) // integer part
step_yf += dy - ipart_y // fractional part
while(step_yf > 0.5) {ipart_y ++; step_yf --}
while(step_yf < -0.5) {ipart_y --; step_yf ++}

// vertical movement failure
if(ipart_y && !Move(loc, dir, step_x, step_y + ipart_y))
vel_y = 0

// Velocity and Acceleration properties
var accel = 1, decel = 1
var vel_x = 0, vel_y = 0

// Initial setup for per-tick, velocity-based movement
Login()
..()
client.focus = src

spawn for()
sleep(world.tick_lag)
move_tick()

// Event called every tick to process inputs into outputs
proc/move_tick()
// horizontal movement
var pre_vel_x = vel_x

// accelerate if input is given
if(client.keys["east" ]) vel_x += accel
if(client.keys["west" ]) vel_x -= accel

// when there's no acceleration, slow down
if(pre_vel_x == vel_x)
if(vel_x > 0) vel_x = max(0, vel_x - decel)
if(vel_x < 0) vel_x = min(0, vel_x + decel)

// vertical movement
var pre_vel_y = vel_y

// accelerate if input is given
if(client.keys["north"]) vel_y += accel
if(client.keys["south"]) vel_y -= accel

// when there's no acceleration, slow down
if(pre_vel_y == vel_y)
if(vel_y > 0) vel_y = max(0, vel_y - decel)
if(vel_y < 0) vel_y = min(0, vel_y + decel)

// perform the movement (handled independently in the proc)
move(vel_x, vel_y)
If density isn't an issue, this is what I use in BYONDfall:
Move(locate(1, 1, z), 0,
px,
py)

px and py are absolute coordinates and are stored/adjusted on the mover. The only problem with this that I've encountered is that if the Move() fails, px and py are still adjusted, so the object will just stay still until it gets to a position that it can actually get to. The larger the distance moves, the harder it is to deal with collision detection.

I just wish there was a built-in translate(dx, dy) proc that moves the object in a straight line, taking obstacles into account for each discrete step in between. I can do this by cutting the displacement vector down to a unit vector and performing multiple moves, but it's just not practical when you're moving lots of things at arbitrary velocities. And, of course, when moving tiny amounts per tick, fractional moves should be conserved!
What I did in one of my game was create a proc that gets a heading for movement (basically, the slope of a line for it to travel), then using the formula for a circle it takes the x and y coordinate and then makes the atom take 2 separate steps, one to the east/west and one to the north/south. It allows for easy pixel movement in any direction, which I find to be significantly better than being limited to 8 directions. Since it still uses the built-in step() functions, it handles obstacle collisions just fine.


If you need to see example code, let me know.

EDIT: The movement also handles tracking the remainder from any non-integer values... initially it was rounding for every step, but after 20-30 steps it was ending up way off from where it was intended to be.
Knick-knack, this thread is back!
I haven't used BYOND for awhile, but you need to overwrite proc which handles collision and add own code to take in mind floating point values.

Second possible solution it to make all atoms bigger by 1 pixel in each direction, then when Cross() is called you could make extra check. Though this somewhat prevents Uncross() from working correctly.
I still don't understand the original post here. You can't move a fraction of a pixel. That's not how pixels work. If the value is non-integer, then how would you conceivably display a move of "half a pixel"?
In response to Gunbuddy13
In an HTML5 canvas, it's shown using anti-aliasing.

What I'm looking for isn't a new display mode; I'm looking for the ability to manipulate position using decimals. I don't actually care (nor does the program) how the output is drawn.
Page: 1 2