ID:1864794
 
So, in the process of developing my game, I realize that I needed finer movement control than the standard per-pixel array that I was normally presented with in DM. I ended up coming up with a system that utilizes the ability to break movement down further than a single pixel.

Basically I hold a variable called pmove that holds the fractional float value of movement.

Its works by basically adding the movement speed onto the current pmove, then rounding it down to get the number of pixels actually moved. The pmove is subtracted by the number of pixels moved. This allows a floating pmove to build up and jump an extra pixel every few steps, depending on the speed. It's a bit difficult to explain without any visual aids, but just trust me.

The rate of these extra pixels is typically fast enough and small enough that you'll never notice in animation, but will provide a very simple method of calculating fractal pixel movements.

This is basically how my current movement system looks right now. On top of the fractal pixel movement, it also includes a simple method of turning verticle and horizontal movement commands into diagonal movement, which I find to be very odd why this isn't a default behaviour.

#define CARD 0
#define DIAG 1

mob
var
vert = 0
horz = 0
speed = 4 //Pixels per move. This can also be a floating value.
pmove = 0 //The remainder of subpixel movement
move = 0 //The amount of pixels being moved at that instance.
list
vertstack = list()
horzstack = list()

proc
moveDir(d)
switch(d)
if(CARD) pmove+=speed
if(DIAG) pmove+=(speed/ROOT2)
move = round(pmove)
pmove -= move
return move
moveStep(d)
if(vert&&horz)
if(!moveDir(DIAG)) return
else
if(!moveDir(CARD)) return
step(src,d,move)
startMove()
moving = 1
while(length(vertstack)||length(horzstack))
if(length(vertstack))
switch(vertstack[length(vertstack)]) //Checks the very last element in the list.
if("N") vert = 1
if("S") vert = 2
else vert = 0
if(length(horzstack))
switch(horzstack[length(horzstack)])
if("E") horz = 4
if("W") horz = 8
else horz = 0
moveStep(vert+horz)
sleep(world.tick_lag)
moving = 0

verb
moveNorth()
set hidden = 1
vertstack.Add("N")
if(!moving) startMove() //Only if the mob isn't already moving does it call this function.
moveSouth()
set hidden = 1
vertstack.Add("S")
if(!moving) startMove()
moveEast()
set hidden = 1
horzstack.Add("E")
if(!moving) startMove()
moveWest()
set hidden = 1
horzstack.Add("W")
if(!moving) startMove()

stopNorth()
set hidden = 1
vertstack.Remove("N")
stopSouth()
set hidden = 1
vertstack.Remove("S")
stopEast()
set hidden = 1
horzstack.Remove("E")
stopWest()
set hidden = 1
horzstack.Remove("W")


I hope that this was a somewhat informative method of creating precise, responsive pixel movement controls. If you see any way to improve this or clean it up, I'd love to hear your comments.

10.8.2015 EDIT: Due to some latency being created by the original proc, I rewrote how I handled the actual verbs. Instead of just dictating the direction, I had each verb add or remove an identifier from a makeshift call stack for each vertical and horizontal direction. The code checks for the very last element in the list, being highest on the stack.

The verbs now no longer require them to be repeating to function properly, all they do is simply update the direction of movement and only if the mob isn't already moving, does it call the startMove() proc.

The reason for the call stack was to prevent the bug where someone would be holding down the left and right buttons for example and when letting go of the button in the direction they were currently going, it would not automatically call them to start moving the other direction. Doing it this way fixes that and adds a lot more responsiveness to the code.

I hope this update serves some people's ideas well.
Well I'm done now. Lol, but that's good to know at least. I assume his works in a relatively similar manner?
Also note that I've further advanced the safety of this code by adding if(!m) return just before the step() proc in moveUpdate() as a small pmove like 8 would be rounded down to 0 and cause the character to jump their default step size instead of staying still until pmove is 10 or greater.
In response to Kats
So far I understand the basic concept, sub pixel movement currently in 10ths of a pixel. I'm having a lot of trouble understanding your description and relating the code but I've only looked for a couple of minutes so I'll get it soon I reckon.

From just quick inspection I'm noticing a couple of things that you could improve.

1. sqrt(2). Square roots are not easy calculations. You could greatly reduce the amount of processing time this loop takes if you only calculate the sqrt(2) once and store it in a variable to use later. Or... Just hard code it.

2. The next thing is that you used while(0). That's technically correct but it's not as user friendly as something like while(canMove) which allows people to stop a movement process at times when it's not needed. Sure a player character always needs this running but if you have 200 npcs on your map you really would rather only have npcs close to a player moving. Otherwise you're running 200 updates which gets insane.

I would also suggest renaming it to startMovementLoop() instead of moveUpdate() which I think is misleading as a name because it doesn't imply constant iteration.
In response to Zecronious
Zecronious wrote:
1. sqrt(2). Square roots are not easy calculations. You could greatly reduce the amount of processing time this loop takes if you only calculate the sqrt(2) once and store it in a variable to use later. Or... Just hard code it.

That's actually very good. I don't know why I didn't think about it earlier. That would save some calculation speed.

2. The next thing is that you used while(0). That's technically correct but it's not as user friendly as something like while(canMove) which allows people to stop a movement process at times when it's not needed. Sure a player character always needs this running but if you have 200 npcs on your map you really would rather only have npcs close to a player moving. Otherwise you're running 200 updates which gets insane.

Right now the movement is based on the variables vert and horz which, if both are 0, means that there's no movement to be done. This is controlled by the stopVert() and stopHorz() verbs, at least for player mobs so having a canMove variable would be a little redundant.

I would also suggest renaming it to startMovementLoop() instead of moveUpdate() which I think is misleading as a name because it doesn't imply constant iteration.

Again, as I mentioned before, it's not a loop. I used do-while statements simply so I could use break conveniently, which I've already mentioned is not optimal, and I've already changed it in my code to not use it. I just needed a quick solution while my brain was working. It's a single iteration function that updates the position of the character. It is a moveUpdate(), not a loop.
In response to Kats
Oh right but surely if you need to do that to use break then there's got to be a better way. That's not how both break and while were designed to be used. I'm sure it works but that's just kinda weird.

Wouldn't return be sufficient? All break does is skip to the end of the process right before it returns anyway.
In response to Kats
Welp, try this. I'm 99% sure it should do exactly the same thing. If-Else is an amazing thing when used correctly.

(Of course I don't have your program so I can't test it so I'm not 100% sure.)

#define sqrt2 1.41421356237

mob
var
vert = 0
horz = 0
speed = 40
pmove = 0
m = 0

proc
moveUpdate()

if(vert==1)

if(horz==1)
if(updateAngularMovement()==0) return
step(src,NORTHEAST,m)

else if(horz==-1)
if(updateAngularMovement()==0) return
step(src,NORTHWEST,m)

else
if(updateStraightMovement()==0) return
step(src,NORTH,m)

else if(vert==-1)

if(horz==1)
if(updateAngularMovement()==0) return
step(src,SOUTHEAST,m)

else if(horz==-1)
if(updateAngularMovement()==0) return
step(src,SOUTHWEST,m)

else
if(updateStraightMovement()==0) return
step(src,SOUTH,m)

else if(horz==1&&!vert)
if(updateStraightMovement()==0) return
step(src,EAST,m)


else if(horz==-1&&!vert)
if(updateStraightMovement()==0) return
step(src,WEST,m)


proc
updateAngularMovement()
pmove += round(speed/sqrt2)
m = round(pmove/10)
pmove %= 10
if(!m) return 0

updateStraightMovement()
pmove += speed
m = round(pmove/10)
pmove %= 10
if(!m) return 0
Thanks for the post, but like I said, I've already updated my code before to break it down into simpler functions to remove the redundancy. Still a useful post for the thread, however.

EDIT: In your updateAngularMovement(), you still calculate sqrt(2) instead of using the sqrt2 var.
DM performs the calculation of constant expressions like sqrt(2) on compilation, so you don't even have to worry about that.

I don't see why it's necessary to limit your decimals to 0.1 when you could use actual floating point accuracy.

You should totally take a look at my absolute positions library. I haven't really had any feedback on it but I'll update it if anyone asks.
In response to Kaiochao
True, I should update it to use floating point accuracy. I'm still hashing out how I want to do this exactly and I think the feedback I've been getting is fantastic! Thank you guys. Hopefully I should have this section of the engine done within the next day or two and can finally start on other interesting things like calculating collision between rotated rectangles.
In response to Kats
Kats wrote:
Thanks for the post, but like I said, I've already updated my code before to break it down nto simpler functions to remove the redundancy.

When did you say that and why did you post this if you've got a better version?

In response to Zecronious
Zecronious wrote:
When did you say that and why did you post this if you've got a better version?

I thought I did in an earlier post. I guess that was just in my head. And it's been a work in progress. This was the original code. I'll update it as soon as I'm done making all of my tweaks and am actually happy with the final result.

The code above works, but it needs the optimizations I've been doing, so I'll get on that soon.
In response to Zecronious
Zecronious wrote:
Welp, try this. I'm 99% sure it should do exactly the same thing. If-Else is an amazing thing when used correctly.

Switch() ftw! I'm against if-else abuse Q.Q

mob/proc/moveUpdate()
switch(vert)
if(1) switch(horz)
if(1)
if(updateAngularMovement()!=0) . = 5
if(-1)
if(updateAngularMovement()!=0) . = 9
else
if(updateStraightMovement()!=0) . = 1
if(-1) switch(horz)
if(1)
if(updateAngularMovement()!=0) . = 6
if(-1)
if(updateAngularMovement()!=0) . = 10
else
if(updateStraightMovement()!=0) . = 2
else if(!vert) switch(horz)
if(1)
if(updateStraightMovement()!=0) . = 4
if(-1)
if(updateStraightMovement()!=0) . = 8
if(.) step(src,., m)
In response to Kozuma3
Looks better. I think it could still use a couple of changes. Mainly that double ifs are redundant. You can use && unless linespace is an issue.

Also the final else statement is redundant because we're expecting 3 possible values. vert==1 or vert==0 or vert==-1.

(edit: don't use this code, it's broken)
    proc/moveUpdate()

// . = direction to move

switch(vert)

// Deal with positive vertical movement
if(1)
switch(horz)
if(1 && updateAngularMovement()!=0) . = 5
if(-1 && updateAngularMovement()!=0) . = 9
else if(updateStraightMovement()!=0) . = 1

// Deal with negative vertical movement
if(-1)
switch(horz)
if(1 && updateAngularMovement()!=0) . = 6
if(-1 && updateAngularMovement()!=0) . = 10
else if(updateStraightMovement()!=0) . = 2

// Deal with no vertical movement
if(0)
switch(horz)
if(1 && updateStraightMovement()!=0) . = 4
if(-1 && updateStraightMovement()!=0) . = 8

// Initiate movement in . direction of m magnitude.
if(.) step(src,., m)
In response to Zecronious
Zecronious wrote:
Looks better. I think it could still use a couple of changes. Mainly that double ifs are redundant. You can use && unless linespace is an issue.

Also the final else statement is redundant because we're expecting 3 possible values. vert==1 or vert==0 or vert==-1.

[code]


Expected a constant expression, you can't do that with switch().

Also, you're not checking for [ if(!vert) ] now because of the removal of the if statement :P

Also no need for if(0) since it'll default to that with the else.
In response to Kozuma3
Haha, true. My bad. I forgot because I never use switch. I think you'd agree that with only 2 or 3 things switch isn't needed anyway. Switch is for more in the range of 5 or 10 ish things (in my opinion as there's no set standard).
In response to Kozuma3
Anyway, the reason to use switch was to clean up the code. I think it looks more cleaned up when you then take out switch.

    proc/moveUpdate()

// . = direction to move

// Deal with positive vertical movement
if(vert == 1)
if(horz == 1 && updateAngularMovement()!=0) . = 5
if(horz == -1 && updateAngularMovement()!=0) . = 9
else if(updateStraightMovement()!=0) . = 1

// Deal with negative vertical movement
if(vert == -1)
if(horz == 1 && updateAngularMovement()!=0) . = 6
if(horz == -1 && updateAngularMovement()!=0) . = 10
else if(updateStraightMovement()!=0) . = 2

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

// Initiate movement in . direction of m magnitude.
if(.) step(src,., m)
It runs faster with switch(), efficiency should be the goal.
In response to Kozuma3
Hmm, lets run some tests then. I'd be keen to see that. Fun little project.
switch() using 2 arguments(3 in this case) is faster than using a single if and else if statement.

[Edit] Also your newer code is less efficient than before since you're checking multiple if's for different values even tho it can only be one or the other.
Page: 1 2 3 4