In response to Kozuma3
It's one thing to say that but lets profile it and see if that's actually the case.

I'm going to use the updateMovement procs from my earlier posts and run our code in the profiler.
I got both running as best as I could and tested them. Switch runs about 15% faster.

My code without switch:

    proc
moveUpdateNoSwitch()

// . = direction to move

if(horz != 0) updateStraightMovement()
else updateAngularMovement()

if(!m) return

// Deal with positive vertical movement
if(vert == 1)
if(horz == 1) . = 5
else if(horz == -1) . = 9
else . = 1

// Deal with negative vertical movement
else if(vert == -1)
if(horz == 1) . = 6
else if(horz == -1) . = 10
else . = 2

// Deal with no vertical movement
else if(vert == 0)
if(horz == 1) . = 4
else if(horz == -1) . = 8

// Going to skip running movement as it's not what we're testing


Your code using swtich with some go-faster edits.
        moveUpdateSwitch()

if(horz != 0) updateStraightMovement()
else updateAngularMovement()

if(!m) return

switch(vert)
if(1) switch(horz)
if(1)
. = 5
if(-1)
. = 9
else
. = 1
if(-1) switch(horz)
if(1)
. = 6
if(-1)
. = 10
else
. = 2
else if(!vert) switch(horz)
if(1)
. = 4
if(-1)
. = 8

// Going to skip running movement as it's not what we're testing


Update procs used:

        updateAngularMovement()
pmove += round(speed/sqrt2)
m = round(pmove/10)
pmove %= 10

updateStraightMovement()
pmove += speed
m = round(pmove/10)
pmove %= 10
No switch() necessary!
// horizontal direction
var dh = horz && (horz > 0 ? EAST : WEST)

// vertical direction
var dv = vert && (vert > 0 ? NORTH : SOUTH)

// possibly diagonal direction
var d = dh|dv

if(d && ((dh && dv) ? updateAngularMovement() : updateStraightMovement()) != 0)
step(src, d, m)
return d

I admit that if() is a bit messy, but I wouldn't have made two procs for that, and if I did, I wouldn't be testing if it returned 0 vs null.
In response to Kaiochao
Kaiochao wrote:
No switch() necessary!
[code]

I wonder how this compares to switch() speed wise.

Anyway, this is the important part of the fractional accumulator that I use in my library. It's plug and play: you can call movable.Move(loc, dir, step_x, step_y) with float step_x/y values, and the decimal parts will be preserved.
atom
movable
var tmp
// Accumulated decimal parts.
__dec_x
__dec_y

// This allows you to use decimal values for StepX and StepY when you call Move().
Move(Loc, Dir, StepX, StepY)
var rx = round(StepX)
var ry = round(StepY)
__dec_x += StepX - rx
__dec_y += StepY - ry
while(__dec_x < -0.5) { __dec_x ++; rx -- }
while(__dec_x >= 0.5) { __dec_x --; rx ++ }
while(__dec_y < -0.5) { __dec_y ++; ry -- }
while(__dec_y >= 0.5) { __dec_y --; ry ++ }
. = ..(Loc, Dir || dir, rx, ry)

Forum_account uses the same method in his pixel movement libraries to achieve float-precise positioning. Basically, we've had sub-pixel-perfect pixel movement since long before native pixel movement came out, which is why it's so disappointing that native pixel movement came out without it.
In response to Kaiochao
Modifying Move() is ill advised, very inefficient.
Falacy posted about this recently, would suggest avoiding this.
In response to Kozuma3
All it takes is a variable that disables this code, or you could move it to a different type. You definitely shouldn't keep it under the base movable type unless you want all of your things moving how they... should have from the start.
In response to Kaiochao
Kaiochao wrote:
All it takes is a variable that disables this code, or you could move it to a different type. You definitely shouldn't keep it under the base movable type unless you want all of your things moving how they... should have from the start.

Or you can rig it to your own movement system all together and avoid using Move() and save yourself from the trouble and inefficiency that comes with modifying Move()
In response to Kozuma3
Move() provides pixel accurate collision detection for axis aligned bounding boxes. You can fire an object that's 1×1 pixels in size, and move it maybe 500px east and 1200px north (both in a single Move, of course), and it'll perfectly collide with anything in its straight (diagonal) line path (assuming its step_size is sufficiently large for a glide instead of a jump). This is an exaggerated example that would probably be slow (for better performance, increase the size of our mover), but it would be slow without modifying Move() at all.

I agree that my code doesn't have to be under Move(). It's there for the effect of working by plug and play. For example, the same exact code will work differently if my library is included. It's not necessary for serious programmers, and I should make a note of that in the library, or make a preprocessor that puts it in a new proc.

Thanks for your input. Yut Put was having performance issues months ago due to it being defined for all movables, so I really should do something about that in the library.


This is what the movement currently looks like. Right now, I'm not too worried about optimization and innovation, more around functionality. The cursor is invisible because Fraps doesn't like recording BYOND games very well, but you can get the idea of independent movement and view direction.

I probably will move to switch statements for speed, but for now, I'm still working on ironing out the actual mechanics of the project. I love the input though, so when I do get around to dealing with bottlenecks, this information will do pretty well.
In response to Kats
Looks good
In response to Kats
Oh also, because this is code that maybe other people are going to use it's good if we can make sure they're using best practices and getting optimized code.

I know you're not too worried but I think it's good for the community if we try to make sure code gets checked by others for defects/optimization.
In response to Kaiochao
Kaiochao wrote:
Anyway, this is the important part of the fractional accumulator that I use in my library. It's plug and play: you can call movable.Move(loc, dir, step_x, step_y) with float step_x/y values, and the decimal parts will be preserved.
> atom
> movable
> var tmp
> // Accumulated decimal parts.
> __dec_x
> __dec_y
>
> // This allows you to use decimal values for StepX and StepY when you call Move().
> Move(Loc, Dir, StepX, StepY)
> var rx = round(StepX)
> var ry = round(StepY)
> __dec_x += StepX - rx
> __dec_y += StepY - ry
> while(__dec_x < -0.5) { __dec_x ++; rx -- }
> while(__dec_x >= 0.5) { __dec_x --; rx ++ }
> while(__dec_y < -0.5) { __dec_y ++; ry -- }
> while(__dec_y >= 0.5) { __dec_y --; ry ++ }
> . = ..(Loc, Dir || dir, rx, ry)
>

Forum_account uses the same method in his pixel movement libraries to achieve float-precise positioning. Basically, we've had sub-pixel-perfect pixel movement since long before native pixel movement came out, which is why it's so disappointing that native pixel movement came out without it.

Honestly, I always hated the while loops in your approach, Kaio.

A large part of Yut's issues with your pixel movement approach were simply that he had too many objects in the world because he was using Forum_account's habit of using objects as though they were areas. He had tens of thousands of objects in a barely 150x150 world map, which was causing all movement functions to have to filter through dozens of potential colliders every frame.

My approach to your subpixel movement approach was to do what you were doing mathematically rather than iteratively.

#define ceil(x) (-round(-(x)))
#define floor(x) round(x)
#define round_inner(x) ((x) < 0 ? ceil(x) : floor(x))
#define round_outer(x) ((x) < 0 ? floor(x) : ceil(x))
#define clamp(x,l,h) max(min(x,h),l)
#define to_places(v,p) (floor(10**p*v)/10**p)

proc
decimal(x)
. = x-round_inner(x)
atom
movable
var
rem_x = 0
rem_y = 0
Move(atom/NewLoc,Dir=0,Step_x=0,Step_y=0,Source=null)
var/turf/OldLoc = loc
var/oDir = dir
var/oSx = step_x
var/oSy = step_y
var/rx = Step_x
var/ry = Step_y
Step_x = round_outer(round_inner((Step_x + rem_x)*2)/2)
Step_y = round_outer(round_inner((Step_y + rem_y)*2)/2)
rem_x = decimal(rx-Step_x+rem_x)
rem_y = decimal(ry-Step_y+rem_y)
. = ..()
if(.)
Moved(OldLoc,oDir,oSx,oSy)
In response to Ter13
That's true, so I have to give you that argument.
So this is my current working code, not including the independent direction code. We'll call this v2. Also, pmove now floats, which does make everything quite nicer.

#define CARD 0
#define DIAG 1

merc
proc
moveDir(d)
switch(d)
if(CARD) pmove+=speed
if(DIAG) pmove+=(speed/ROOT2)
move = round(pmove/10)
pmove-=(move*10)
return move
moveUpdate() //Handle the order of movement procs.
getMove()
updateLoc() //Retrieves and updates the pixel coordinates of the mob.
setDir() //Updates the direction of the mob.
moveStep(d)
if(vert&&horz)
if(!moveDir(DIAG)) return
else
if(!moveDir(CARD)) return
step(src,d,move)
getMove()
switch(vert)
if(1)
switch(horz)
if(1) moveStep(NORTHEAST)
if(-1) moveStep(NORTHWEST)
else moveStep(NORTH)
if(-1)
switch(horz)
if(1) moveStep(SOUTHEAST)
if(-1) moveStep(SOUTHWEST)
else moveStep(SOUTH)
else
switch(horz)
if(1) moveStep(EAST)
if(-1) moveStep(WEST)

var
vert = 0
horz = 0
speed = 40
pmove = 0 //The remainder of subpixel movement
move = 0 //The amount of pixels being moved at that instance.

verb
moveVert(DIR as num) //1 for NORTH, -1 for SOUTH
set hidden = 1
vert = DIR
moveUpdate()
moveHorz(DIR as num) //1 for EAST, -1 for WEST
set hidden = 1
horz = DIR
moveUpdate()

stopVert()
set hidden = 1
vert = 0
stopHorz()
set hidden = 1
horz = 0


EDIT: Much cleaner moveStep() function.
You need to use a comma to combine things in switch, not ||. Using || is just evaluating the usual || operation. You could also use (dir & (dir-1)) to check if dir is diagonal.
In response to Kaiochao
Actually I just realized, I could just check if both of the vert and horz vars are not 0... That's so much simpler...

Aaaand... Fixed it.
At any rate, it seems like your pixel movement system only allows for movements along the 8 directions, which is very limiting. If you want to move something according to any 2D vector (vx, vy), you should be taking advantage of the built-in linear collision detection in Move(loc, dir, step_x + vx, step_y + vy) (also known as Translate(vx, vy)). If you're limited to 8 directional movement where you have to set the speed before each move, you'll have to manually break up the move into short movements along the 8 dirs to approximate the straight line movement.
In response to Kaiochao
Yes, my movement only allows along 8 vectors because you only have so many fingers. Since you're controlling the character's movement independently from their view, I haven't bothered to add a feature that I don't plan on using. All it would require simple trigonometry to compute the angle of travel as well as the speed, which isn't hard if you know how that stuff works. In fact, full movement relies on floating carry values like how my pmove works to calculate a proper trajectory anyway, so I already have a map of how it works.

My projectile system will be relying on full directional movement, so maybe when I get that completed, I'll post a snippet in a new thread. But as far as more dynamic movement in my system, it's not going to happen.
Also, I'm a dope, so I've just updated the code to accept actual built-in directions instead of vague 1's and -1's.
Page: 1 2 3 4